From c56a03f102f003d5ea87230f84adc7e31b0ece05 Mon Sep 17 00:00:00 2001 From: trchandraprakash <47793448+trchandraprakash@users.noreply.github.com> Date: Mon, 20 Jul 2020 08:59:34 -0700 Subject: [PATCH 0001/1476] Lunamedia ad size parameter update (#5490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Bidder Code * LunaMedia Adapater LunaMedia Adapater * Updated LunamediaBidAdapter.md test params and valid pub code for testing * adding it to resolve conflict in locally repo * Accept size parameters Accept size parameters * Based on browserstack testing result Adding a new line. home/circleci/Prebid.js/modules/lunamediaBidAdapter.js 401:22 error Newline required at end of file but not found eol-last ✖ 1 problem (1 error, 0 warnings) 1 error, 0 warnings potentially fixable with the `--fix` option. * updated as per review updated as per review Co-authored-by: Chandra Prakash --- modules/lunamediaBidAdapter.js | 28 +++++++++++++++---- modules/lunamediaBidAdapter.md | 6 ++-- test/spec/modules/lunamediaBidAdapter_spec.js | 4 +-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/modules/lunamediaBidAdapter.js b/modules/lunamediaBidAdapter.js index 83be806af17..b309ef42240 100755 --- a/modules/lunamediaBidAdapter.js +++ b/modules/lunamediaBidAdapter.js @@ -8,8 +8,8 @@ import includes from 'core-js-pure/features/array/includes.js'; const ADAPTER_VERSION = '1.0'; const BIDDER_CODE = 'lunamedia'; -export const VIDEO_ENDPOINT = 'https://api.lunamedia.io/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; -export const BANNER_ENDPOINT = 'https://api.lunamedia.io/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; +export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// https://api.lunamedia.io/xp/get?pubid=0cf8d6d643e13d86a5b6374148a4afac'; +export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; @@ -44,6 +44,7 @@ export const spec = { bannerBids.forEach(bid => { pubid = getBannerBidParam(bid, 'pubid'); + requests.push({ method: 'POST', url: BANNER_ENDPOINT + pubid, @@ -210,8 +211,16 @@ function createVideoRequestData(bid, bidderRequest) { let topLocation = getTopWindowLocation(bidderRequest); let topReferrer = getTopWindowReferrer(); - let sizes = getVideoSizes(bid); - let firstSize = getFirstSize(sizes); + // if size is explicitly given via adapter params + let paramSize = getVideoBidParam(bid, 'size'); + let sizes = []; + + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getVideoSizes(bid); + } + const firstSize = getFirstSize(sizes); let video = getVideoTargetingParams(bid); const o = { @@ -301,7 +310,15 @@ function createBannerRequestData(bid, bidderRequest) { let topLocation = getTopWindowLocation(bidderRequest); let topReferrer = getTopWindowReferrer(); - let sizes = getBannerSizes(bid); + // if size is explicitly given via adapter params + + let paramSize = getBannerBidParam(bid, 'size'); + let sizes = []; + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getBannerSizes(bid); + } const o = { 'device': { @@ -379,5 +396,4 @@ function createBannerRequestData(bid, bidderRequest) { return o; } - registerBidder(spec); diff --git a/modules/lunamediaBidAdapter.md b/modules/lunamediaBidAdapter.md index d0314d0fa41..ff5cc86c462 100755 --- a/modules/lunamediaBidAdapter.md +++ b/modules/lunamediaBidAdapter.md @@ -29,7 +29,8 @@ var displayAdUnit = [ bidder: 'lunamedia', params: { pubid: '121ab139faf7ac67428a23f1d0a9a71b', - placement: 1234 + placement: 1234, + size: "320x50" } }] }]; @@ -52,7 +53,8 @@ var videoAdUnit = { bidder: 'lunamedia', params: { pubid: '121ab139faf7ac67428a23f1d0a9a71b', - placement: 1234, + placement: 1234, + size: "320x480", video: { id: 123, skip: 1, diff --git a/test/spec/modules/lunamediaBidAdapter_spec.js b/test/spec/modules/lunamediaBidAdapter_spec.js index fc8648bf8a0..c0809071419 100755 --- a/test/spec/modules/lunamediaBidAdapter_spec.js +++ b/test/spec/modules/lunamediaBidAdapter_spec.js @@ -7,9 +7,9 @@ describe('lunamediaBidAdapter', function () { let bidRequestsVid; beforeEach(function () { - bidRequests = [{'bidder': 'lunamedia', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'floor': 0.5, 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + bidRequests = [{'bidder': 'lunamedia', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'floor': 0.5, 'placement': 1234, size: '320x250'}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - bidRequestsVid = [{'bidder': 'lunamedia', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'floor': 1.0, 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + bidRequestsVid = [{'bidder': 'lunamedia', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'floor': 1.0, 'placement': 1234, size: '320x480', 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; }); describe('spec.isBidRequestValid', function () { From bb91d54c25a9191fc1e14e67d4855b49bad1302b Mon Sep 17 00:00:00 2001 From: SeedingAllianceTech <55976067+SeedingAllianceTech@users.noreply.github.com> Date: Tue, 21 Jul 2020 15:45:18 +0200 Subject: [PATCH 0002/1476] Updating seedingAlliance Adapter (#5517) * add seedingAlliance Adapter * add two native default params * ... * ... * seedingAlliance Adapter: add two more default native params * updating seedingAlliance Adapter --- modules/seedingAllianceBidAdapter.js | 20 ++++++++++++------- .../modules/seedingAllianceAdapter_spec.js | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/seedingAllianceBidAdapter.js b/modules/seedingAllianceBidAdapter.js index b6acd7214a2..d85ae856317 100755 --- a/modules/seedingAllianceBidAdapter.js +++ b/modules/seedingAllianceBidAdapter.js @@ -62,7 +62,6 @@ export const spec = { const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; const tid = validBidRequests[0].transactionId; const cur = [config.getConfig('currency.adServerCurrency') || DEFAULT_CUR]; - let pubcid = null; let url = bidderRequest.refererInfo.referer; const imp = validBidRequests.map((bid, id) => { @@ -112,10 +111,6 @@ export const spec = { }; }); - if (validBidRequests[0].crumbs && validBidRequests[0].crumbs.pubcid) { - pubcid = validBidRequests[0].crumbs.pubcid; - } - const request = { id: bidderRequest.auctionId, site: { @@ -126,15 +121,26 @@ export const spec = { }, cur, imp, - user: { - buyeruid: pubcid + user: {}, + regs: { + ext: { + gdpr: 0 + } } }; + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(request, 'regs.ext.gdpr', (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0); + } + return { method: 'POST', url: ENDPOINT_URL, data: JSON.stringify(request), + options: { + contentType: 'application/json' + }, bids: validBidRequests }; }, diff --git a/test/spec/modules/seedingAllianceAdapter_spec.js b/test/spec/modules/seedingAllianceAdapter_spec.js index e6f96c92fd9..81af9546ff0 100755 --- a/test/spec/modules/seedingAllianceAdapter_spec.js +++ b/test/spec/modules/seedingAllianceAdapter_spec.js @@ -38,7 +38,7 @@ describe('SeedingAlliance adapter', function () { }); it('should have default request structure', function () { - let keys = 'site,device,cur,imp,user'.split(','); + let keys = 'site,device,cur,imp,user,regs'.split(','); let validBidRequests = [{ bidId: 'bidId', params: {} From 3a8f5cd42fbe79d3817949e4bbbfd6941d7c6412 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 21 Jul 2020 16:12:39 -0400 Subject: [PATCH 0003/1476] Update consentManagementUsp.js to not store and reuse null consent (#5452) * Update consentManagementUsp.js * Update consentManagementUsp_spec.js * Update amxBidAdapter.js * Update amxBidAdapter.js * Update consentManagementUsp_spec.js * Update consentManagementUsp_spec.js * Update consentManagementUsp_spec.js --- modules/consentManagementUsp.js | 5 ----- test/spec/modules/consentManagementUsp_spec.js | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 1a5879a40ff..e4d5c12eb46 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -158,11 +158,6 @@ export function requestBidsHook(fn, reqBidsConfigObj) { timer: null }; - // in case we already have consent (eg during bid refresh) - if (consentData) { - return exitModule(null, hookConfig); - } - if (!uspCallMap[consentAPI]) { utils.logWarn(`USP framework (${consentAPI}) is not a supported framework. Aborting consentManagement module and resuming auction.`); return hookConfig.nextFn.apply(hookConfig.context, hookConfig.args); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index 2e8d7db92b5..ee4140afa10 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -185,7 +185,10 @@ describe('consentManagement', function () { resetConsentData(); }); - it('should bypass CMP and simply use previously stored consentData', function () { + // from prebid 4425 - "the USP (CCPA) api function __uspapi() always responds synchronously, whether or not privacy data is available, while the GDPR CMP may respond asynchronously + // Because the USP API does not wait for a user response, if it was not successfully obtained before the first auction, we should try again to retrieve privacy data before each subsequent auction. + + it('should not bypass CMP and simply use previously stored consentData', function () { let testConsentData = { uspString: '1YY' }; @@ -208,7 +211,7 @@ describe('consentManagement', function () { let consent = uspDataHandler.getConsentData(); expect(didHookReturn).to.be.true; expect(consent).to.equal(testConsentData.uspString); - sinon.assert.notCalled(uspStub); + sinon.assert.called(uspStub); }); }); From 3b7cb373f43a7a0226809d843750a087e7d206a0 Mon Sep 17 00:00:00 2001 From: Corey Kress Date: Wed, 22 Jul 2020 12:55:11 -0400 Subject: [PATCH 0004/1476] [synacormedia] Update adapter to support Consent Management Module (#5506) * CAP-1614 - updated docs to show correct size for banner and some other small fixes * CAP-1636 support schain object in prebid * CAP-1636 updated the review comments * CAP-1849 - split up banner and video impressions to use format * CAP-1879 - added adapter support for consent management module * CAP-1879 - updates for pr * CAP-1879 - remove unneeded checks Co-authored-by: Corey Kress Co-authored-by: Rajkumar Natarajan --- modules/synacormediaBidAdapter.js | 7 ++++++- .../modules/synacormediaBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index 8f069f551ee..6725f5aff74 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -1,6 +1,6 @@ 'use strict'; -import { getAdUnitSizes, logWarn } from '../src/utils.js'; +import { getAdUnitSizes, logWarn, deepSetValue } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -85,6 +85,11 @@ export const spec = { } }); + // CCPA + if (bidderRequest && bidderRequest.uspConsent) { + deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index e15481d47e5..d9f6c9b7256 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -175,6 +175,14 @@ describe('synacormediaBidAdapter ', function () { } }; + let bidderRequestWithCCPA = { + auctionId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + }, + uspConsent: '1YYY' + }; + let expectedDataImp1 = { banner: { format: [ @@ -560,6 +568,18 @@ describe('synacormediaBidAdapter ', function () { } ]); }); + it('should contain the CCPA privacy string when UspConsent is in bidder request', function() { + // banner test + let req = spec.buildRequests([validBidRequest], bidderRequestWithCCPA); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.regs.ext.us_privacy).to.equal('1YYY'); + expect(req.data.imp).to.eql([expectedDataImp1]); + }) }); describe('Bid Requests with schain object ', function() { From e68cfd192a43c9d9834206db319fb4f7e10bfa6c Mon Sep 17 00:00:00 2001 From: Stephan Brosinski Date: Wed, 22 Jul 2020 21:11:03 +0200 Subject: [PATCH 0005/1476] New bid adapter for Smaato (#5418) * SmaatoBidAdapter: Initial commit * Add consent management * Add consent test * Smaato: Additional tests * Smaato: Test endpoint for prebid requests * Cleaned up test code * Validate bid requests * Improve banner ad rendering * Smaato: Img ad renderer * Cleanup * Smaato: Handle TTL dynamically * Smaato. Render richmedia ads * Smaato: Bugfixes * Smaato: More meta data * Smaato: Consent string handling * Smaato: new endpoint * Smaato: Fix test * Smaato: Fix test * Smaato: Add additional optional params * Smaato: Use first party data for additional context * Smaato: Undoing changes to karma conf * Smaato: Undo karma konf changes * Smaato: Fixed test * Smaato: Update adapter doc * Smaato: remove unused code * Smaato: Remove package-lock.json * Smaato: stricter parsing of first party data * Smaato: increase adapter version * Smaato: Fix fpd types to reflect openrtb types * Smaato: WiP for video support * Smaato: WiP on video support * Smaato: Video support * Smaato: Fix test data * Smaato: Review feedback * Smaato: Provide valid publisherId / adspaceId --- modules/smaatoBidAdapter.js | 268 +++++++++++ modules/smaatoBidAdapter.md | 64 +++ test/spec/modules/smaatoBidAdapter_spec.js | 505 +++++++++++++++++++++ 3 files changed, 837 insertions(+) create mode 100644 modules/smaatoBidAdapter.js create mode 100644 modules/smaatoBidAdapter.md create mode 100644 test/spec/modules/smaatoBidAdapter_spec.js diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js new file mode 100644 index 00000000000..ce0edb1e19c --- /dev/null +++ b/modules/smaatoBidAdapter.js @@ -0,0 +1,268 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'smaato'; +const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; +const CLIENT = 'prebid_js_$prebid.version$_1.0' + +/** +* Transform BidRequest to OpenRTB-formatted BidRequest Object +* @param {Array} validBidRequests +* @param {any} bidderRequest +* @returns {string} +*/ +const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { + /** + * Turn incoming prebid sizes into openRtb format mapping. + * @param {*} sizes in format [[10, 10], [20, 20]] + * @returns array of openRtb format mappings [{w: 10, h: 10}, {w: 20, h: 20}] + */ + const parseSizes = (sizes) => { + return sizes.map((size) => { + return {w: size[0], h: size[1]}; + }) + } + + const imp = validBidRequests.map(br => { + const bannerMediaType = utils.deepAccess(br, 'mediaTypes.banner'); + const videoMediaType = utils.deepAccess(br, 'mediaTypes.video'); + let result = { + id: br.bidId, + tagid: utils.deepAccess(br, 'params.adspaceId') + } + + if (bannerMediaType) { + const sizes = parseSizes(utils.getAdUnitSizes(br)); + result.banner = { + w: sizes[0].w, + h: sizes[0].h, + format: sizes + } + } + + if (videoMediaType) { + result.video = { + mimes: videoMediaType.mimes, + minduration: videoMediaType.minduration, + startdelay: videoMediaType.startdelay, + linearity: videoMediaType.linearity, + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + maxduration: videoMediaType.maxduration, + skip: videoMediaType.skip, + protocols: videoMediaType.protocols, + ext: { + rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 + }, + skipmin: videoMediaType.skipmin, + api: videoMediaType.api + } + } + + return result; + }); + + const request = { + id: bidderRequest.auctionId, + at: 1, + imp, + cur: ['USD'], + tmax: bidderRequest.timeout, + site: { + id: window.location.hostname, + publisher: { + id: utils.deepAccess(validBidRequests[0], 'params.publisherId') + }, + domain: window.location.hostname, + page: window.location.href, + ref: bidderRequest.refererInfo.referer + }, + device: { + language: (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + ua: navigator.userAgent, + dnt: utils.getDNT() ? 1 : 0, + h: screen.height, + w: screen.width + }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {} + }, + user: { + ext: {} + }, + ext: { + client: CLIENT + } + }; + + Object.assign(request.user, config.getConfig('fpd.user')); + Object.assign(request.site, config.getConfig('fpd.context')); + + if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies === true) { + utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + + if (bidderRequest.uspConsent !== undefined) { + utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + utils.logInfo('[SMAATO] OpenRTB Request:', request); + return JSON.stringify(request); +} + +export const spec = { + code: BIDDER_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: (bid) => { + return typeof bid.params === 'object' && + typeof bid.params.publisherId === 'string' && + typeof bid.params.adspaceId === 'string'; + }, + + /** + * 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: (validBidRequests, bidderRequest) => { + utils.logInfo('[SMAATO] Client version:', CLIENT); + return { + method: 'POST', + url: validBidRequests[0].params.endpoint || SMAATO_ENDPOINT, + data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), + options: { + withCredentials: true, + crossOrigin: true, + } + }; + }, + /** + * 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: (serverResponse, bidRequest) => { + // response is empty (HTTP 204) + if (utils.isEmpty(serverResponse.body)) { + utils.logInfo('[SMAATO] Empty response body HTTP 204, no bids'); + return []; // no bids + } + + let serverResponseHeaders = serverResponse.headers; + const smtAdType = serverResponseHeaders.get('X-SMT-ADTYPE'); + + const smtExpires = serverResponseHeaders.get('X-SMT-Expires'); + let ttlSec = 300; + utils.logInfo('[SMAATO] Expires:', smtExpires); + if (smtExpires) { + ttlSec = Math.floor((smtExpires - Date.now()) / 1000); + } + + const res = serverResponse.body; + utils.logInfo('[SMAATO] OpenRTB Response:', res); + + var bids = []; + res.seatbid.forEach(sb => { + sb.bid.forEach(b => { + let resultingBid = { + requestId: b.impid, + cpm: b.price || 0, + width: b.w, + height: b.h, + ttl: ttlSec, + creativeId: b.crid, + dealId: b.dealid || null, + netRevenue: true, + currency: res.cur, + meta: { + advertiserDomains: b.adomain, + networkName: b.bidderName, + agencyId: sb.seat + } + }; + + switch (smtAdType) { + case 'Img': + resultingBid.ad = createImgAd(b.adm); + resultingBid.meta.mediaType = BANNER; + bids.push(resultingBid); + break; + case 'Richmedia': + resultingBid.ad = createRichmediaAd(b.adm); + resultingBid.meta.mediaType = BANNER; + bids.push(resultingBid); + break; + case 'Video': + resultingBid.vastXml = b.adm; + resultingBid.meta.mediaType = VIDEO; + bids.push(resultingBid); + break; + default: + utils.logInfo('[SMAATO] Invalid ad type:', smtAdType); + } + }); + }); + + utils.logInfo('[SMAATO] Prebid bids:', bids); + 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: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + const syncs = [] + return syncs; + } +} +registerBidder(spec); + +const createImgAd = (adm) => { + const image = JSON.parse(adm).image; + + let clickEvent = ''; + image.clicktrackers.forEach(src => { + clickEvent += `fetch(decodeURIComponent('${encodeURIComponent(src)}'), {cache: 'no-cache'});`; + }) + + let markup = `
`; + + image.impressiontrackers.forEach(src => { + markup += ``; + }); + + return markup + '
'; +}; + +const createRichmediaAd = (adm) => { + const rich = JSON.parse(adm).richmedia; + let clickEvent = ''; + rich.clicktrackers.forEach(src => { + clickEvent += `fetch(decodeURIComponent('${encodeURIComponent(src)}'), {cache: 'no-cache'});`; + }) + + let markup = `
${rich.mediadata.content}`; + + rich.impressiontrackers.forEach(src => { + markup += ``; + }); + + return markup + '
'; +}; diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md new file mode 100644 index 00000000000..d26d7ecf64e --- /dev/null +++ b/modules/smaatoBidAdapter.md @@ -0,0 +1,64 @@ +# Overview + +``` +Module Name: Smaato Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@smaato.com +``` + +# Description + +The Smaato adapter requires setup and approval from the Smaato team, even for existing Smaato publishers. Please reach out to your account team or prebid@smaato.com for more information. + +# Test Parameters + +For banner adunits: + +``` +var adUnits = [{ + "code": "banner-unit", + "mediaTypes": { + "banner": { + "sizes": [320, 50] + } + }, + "bids": [{ + "bidder": "smaato", + "params": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + }] +}]; +``` + +For video adunits: + +``` +var adUnits = [{ + "code": "video unit", + "mediaTypes": { + "video": { + "context": "instream", + "playerSize": [640, 480], + "mimes": ["video/mp4"], + "minduration": 5, + "maxduration": 30, + "startdelay": 0, + "linearity": 1, + "protocols": [7], + "skip": 1, + "skipmin": 5, + "api": [7], + "ext": {"rewarded": 0} + } + }, + "bids": [{ + "bidder": "smaato", + "params": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + }] +}]; +``` \ No newline at end of file diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js new file mode 100644 index 00000000000..95eb36d8a0d --- /dev/null +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -0,0 +1,505 @@ +import { spec } from 'modules/smaatoBidAdapter.js'; +import * as utils from 'src/utils.js'; +import {config} from 'src/config.js'; + +const imageAd = { + image: { + img: { + url: 'https://prebid/static/ad.jpg', + w: 320, + h: 50, + ctaurl: 'https://prebid/track/ctaurl' + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } +}; + +const richmediaAd = { + richmedia: { + mediadata: { + content: '

RICHMEDIA CONTENT

', + w: 800, + h: 600 + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } +}; + +const ADTYPE_IMG = 'Img'; +const ADTYPE_RICHMEDIA = 'Richmedia'; +const ADTYPE_VIDEO = 'Video'; + +const context = { + keywords: 'power tools,drills' +}; + +const user = { + keywords: 'a,b', + gender: 'M', + yob: 1984 +}; + +const openRtbBidResponse = (adType) => { + let adm = ''; + + switch (adType) { + case ADTYPE_IMG: + adm = JSON.stringify(imageAd); + break; + case ADTYPE_RICHMEDIA: + adm = JSON.stringify(richmediaAd); + break; + case ADTYPE_VIDEO: + adm = ''; + break; + default: + throw Error('Invalid AdType'); + } + + let resp = { + body: { + bidid: '04db8629-179d-4bcd-acce-e54722969006', + cur: 'USD', + ext: {}, + id: '5ebea288-f13a-4754-be6d-4ade66c68877', + seatbid: [ + { + bid: [ + { + 'adm': adm, + 'adomain': [ + 'smaato.com' + ], + 'bidderName': 'smaato', + 'cid': 'CM6523', + 'crid': 'CR69381', + 'dealid': '12345', + 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', + 'impid': '226416e6e6bf41', + 'iurl': 'https://prebid/iurl', + 'nurl': 'https://prebid/nurl', + 'price': 0.01, + 'w': 350, + 'h': 50 + } + ], + seat: 'CM6523' + } + ], + }, + headers: { + get: function (header) { + if (header === 'X-SMT-ADTYPE') { + return adType; + } + } + } + }; + return resp; +}; + +const request = { + method: 'POST', + url: 'https://prebid.ad.smaato.net/oapi/prebid', + data: '' +}; + +const interpretedBidsImg = [ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '
\"\"\"\"
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } +]; + +const interpretedBidsRichmedia = [ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '

RICHMEDIA CONTENT

\"\"\"\"
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } +]; + +const interpretedBidsVideo = [ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } + } +]; + +const defaultBidderRequest = { + gdprConsent: { + consentString: 'HFIDUYFIUYIUYWIPOI87392DSU', + gdprApplies: true + }, + uspConsent: 'uspConsentString', + refererInfo: { + referer: 'http://example.com/page.html', + }, + timeout: 1200 +}; + +const minimalBidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + } +}; + +const singleBannerBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 +}; + +const singleVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 +}; + +const combinedBannerAndVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + }, + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 +}; + +describe('smaatoBidAdapterTest', () => { + describe('isBidRequestValid', () => { + it('has valid params', () => { + expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456'}})).to.be.true; + expect(spec.isBidRequestValid(singleBannerBidRequest)).to.be.true; + }); + it('has invalid params', () => { + expect(spec.isBidRequestValid({})).to.be.false; + expect(spec.isBidRequestValid({params: {}})).to.be.false; + expect(spec.isBidRequestValid({params: {publisherId: '123'}})).to.be.false; + expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: 456}})).to.be.false; + }); + }); + + describe('buildRequests', () => { + beforeEach(() => { + this.req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); + this.sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + this.sandbox.restore(); + }); + + it('can override endpoint', () => { + const overridenEndpoint = 'https://prebid/bidder'; + let bidRequest = utils.deepClone(singleBannerBidRequest); + utils.deepSetValue(bidRequest, 'params.endpoint', overridenEndpoint); + const actualEndpoint = spec.buildRequests([bidRequest], defaultBidderRequest).url; + expect(actualEndpoint).to.equal(overridenEndpoint); + }); + + it('sends correct imps', () => { + expect(this.req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + tagid: 'adspaceId' + } + ]) + }); + + it('sends correct site', () => { + expect(this.req.site.id).to.exist.and.to.be.a('string'); + expect(this.req.site.domain).to.exist.and.to.be.a('string'); + expect(this.req.site.page).to.exist.and.to.be.a('string'); + expect(this.req.site.ref).to.equal('http://example.com/page.html'); + expect(this.req.site.publisher.id).to.equal('publisherId'); + }) + + it('sends gdpr applies if exists', () => { + expect(this.req.regs.ext.gdpr).to.equal(1); + expect(this.req.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); + }); + + it('sends no gdpr applies if no gdpr exists', () => { + let req_without_gdpr = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); + expect(req_without_gdpr.regs.ext.gdpr).to.not.exist; + expect(req_without_gdpr.user.ext.consent).to.not.exist; + }); + + it('sends usp if exists', () => { + expect(this.req.regs.ext.us_privacy).to.equal('uspConsentString'); + }); + + it('sends tmax', () => { + expect(this.req.tmax).to.equal(1200); + }); + + it('sends no usp if no usp exists', () => { + let req_without_usp = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); + expect(req_without_usp.regs.ext.us_privacy).to.not.exist; + }); + + it('sends fp data', () => { + this.sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + fpd: { + context, + user + } + }; + return utils.deepAccess(config, key); + }); + let bidRequest = utils.deepClone(singleBannerBidRequest); + let req_fpd = JSON.parse(spec.buildRequests([bidRequest], defaultBidderRequest).data); + expect(req_fpd.user.gender).to.equal('M'); + expect(req_fpd.user.yob).to.equal(1984); + expect(req_fpd.user.keywords).to.eql('a,b'); + expect(req_fpd.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); + expect(req_fpd.site.keywords).to.eql('power tools,drills'); + expect(req_fpd.site.publisher.id).to.equal('publisherId'); + }) + }); + + describe('buildRequests for video imps', () => { + it('sends correct video imps', () => { + let req = JSON.parse(spec.buildRequests([singleVideoBidRequest], defaultBidderRequest).data); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + video: { + mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' + } + ]) + }); + it('allows combines banner and video imp in single bid request', () => { + let req = JSON.parse(spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest).data); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + video: { + mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' + } + ]) + }); + it('allows two imps in the same bid request', () => { + let req = JSON.parse(spec.buildRequests([singleBannerBidRequest, singleBannerBidRequest], defaultBidderRequest).data); + expect(req.imp).to.have.length(2); + }); + }); + + describe('interpretResponse', () => { + it('single image reponse', () => { + const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); + assert.deepStrictEqual(bids, interpretedBidsImg); + }); + it('single richmedia reponse', () => { + const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_RICHMEDIA), request); + assert.deepStrictEqual(bids, interpretedBidsRichmedia); + }); + it('single video reponse', () => { + const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_VIDEO), request); + assert.deepStrictEqual(bids, interpretedBidsVideo); + }); + it('ignores bid response with invalid ad type', () => { + let resp = openRtbBidResponse(ADTYPE_IMG); + resp.headers.get = (header) => { + if (header === 'X-SMT-ADTYPE') { + return undefined; + } + } + const bids = spec.interpretResponse(resp, request); + expect(bids).to.be.empty + }); + it('uses correct TTL when expire header exists', () => { + const clock = sinon.useFakeTimers(); + clock.tick(2000); + let resp = openRtbBidResponse(ADTYPE_IMG); + resp.headers.get = (header) => { + if (header === 'X-SMT-ADTYPE') { + return ADTYPE_IMG; + } + if (header === 'X-SMT-Expires') { + return 2000 + (400 * 1000); + } + } + const bids = spec.interpretResponse(resp, request); + expect(bids[0].ttl).to.equal(400); + clock.restore(); + }); + }); +}); From 34ea366320b968d0037b0f1d848ef6fcbf88655b Mon Sep 17 00:00:00 2001 From: Wls-demo <67785512+Wls-demo@users.noreply.github.com> Date: Thu, 23 Jul 2020 05:21:33 +0300 Subject: [PATCH 0006/1476] new boldwin bid adapter (#5454) * new boldwin bid adapter * fix * Restarting ci / circleci Co-authored-by: Aiholkin Co-authored-by: Vladislav Isaiko --- modules/boldwinBidAdapter.js | 110 ++++++++ modules/boldwinBidAdapter.md | 53 ++++ test/spec/modules/boldwinBidAdapter_spec.js | 281 ++++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 modules/boldwinBidAdapter.js create mode 100644 modules/boldwinBidAdapter.md create mode 100644 test/spec/modules/boldwinBidAdapter_spec.js diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js new file mode 100644 index 00000000000..04f4085ba24 --- /dev/null +++ b/modules/boldwinBidAdapter.js @@ -0,0 +1,110 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'boldwin'; +const AD_URL = 'https://ssp.videowalldirect.com/?c=o&m=multi'; +const SYNC_URL = 'https://cs.videowalldirect.com/?c=o&m=cookie' + +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); + case NATIVE: + return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + '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++) { + let bid = validBidRequests[i]; + let sizes + if (bid.mediaTypes) { + if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + sizes = bid.mediaTypes[BANNER].sizes + } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + sizes = bid.mediaTypes[VIDEO].playerSize + } + } + placements.push({ + placementId: bid.params.placementId, + bidId: bid.bidId, + sizes: sizes || [], + wPlayer: sizes ? sizes[0] : 0, + hPlayer: sizes ? sizes[1] : 0, + traffic: bid.params.traffic || BANNER, + schain: bid.schain || {} + }); + } + 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)) { + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: () => { + return [{ + type: 'image', + url: SYNC_URL + }]; + } +}; + +registerBidder(spec); diff --git a/modules/boldwinBidAdapter.md b/modules/boldwinBidAdapter.md new file mode 100644 index 00000000000..4bf272c4de3 --- /dev/null +++ b/modules/boldwinBidAdapter.md @@ -0,0 +1,53 @@ +# Overview + +``` +Module Name: boldwin Bidder Adapter +Module Type: boldwin Bidder Adapter +``` + +# Description + +Module that connects to boldwin demand sources + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'boldwin', + params: { + placementId: 0, + traffic: 'banner' + } + } + ] + }, + // Will return test vast xml. All video params are stored under placement in publishers UI + { + code: 'placementId_0', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [ + { + bidder: 'boldwin', + params: { + placementId: 0, + traffic: 'video' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js new file mode 100644 index 00000000000..a353665ec33 --- /dev/null +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -0,0 +1,281 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/boldwinBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; + +describe('BoldwinBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'boldwin', + params: { + placementId: 0, + traffic: BANNER + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and placementId 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://ssp.videowalldirect.com/?c=o&m=multi'); + }); + 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'); + 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.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement.placementId).to.equal(0); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + 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'); + }); + 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' + }] + }; + 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'); + 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'); + }); + 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', + }] + }; + 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'); + 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'); + }); + 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 () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', 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('image'); + expect(userSync[0].url).to.be.equal('https://cs.videowalldirect.com/?c=o&m=cookie'); + }); + }); +}); From 4bd0d32f4b1c9c6f7cf22cf80e078bd62f2b3b5f Mon Sep 17 00:00:00 2001 From: Anna Tudoran Date: Thu, 23 Jul 2020 05:35:15 +0300 Subject: [PATCH 0007/1476] Undertone 24910 video in prebid (#5485) * support video ads * support video ads * fix indentation Co-authored-by: omerko Co-authored-by: Omer Koren --- modules/undertoneBidAdapter.js | 25 +++++- test/spec/modules/undertoneBidAdapter_spec.js | 76 ++++++++++++++++++- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index 6ead453b622..743cb07b21e 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -2,8 +2,9 @@ * Adapter to send bids to Undertone */ -import { parseUrl } from '../src/utils.js'; +import { deepAccess, parseUrl } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'undertone'; const URL = 'https://hb.undertone.com/hb'; @@ -73,6 +74,7 @@ function getBannerCoords(id) { export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { if (bid && bid.params && bid.params.publisherId) { bid.params.publisherId = parseInt(bid.params.publisherId); @@ -120,8 +122,20 @@ export const spec = { sizes: bidReq.sizes, params: bidReq.params }; + const videoMediaType = deepAccess(bidReq, 'mediaTypes.video'); + if (videoMediaType) { + bid.video = { + playerSize: deepAccess(bidReq, 'mediaTypes.video.playerSize') || null, + streamType: deepAccess(bidReq, 'mediaTypes.video.context') || null, + playbackMethod: deepAccess(bidReq, 'params.video.playbackMethod') || null, + maxDuration: deepAccess(bidReq, 'params.video.maxDuration') || null, + skippable: deepAccess(bidReq, 'params.video.skippable') || null + }; + bid.mediaType = 'video'; + } payload['x-ut-hb-params'].push(bid); }); + return { method: 'POST', url: reqUrl, @@ -144,9 +158,14 @@ export const spec = { creativeId: bidRes.adId, currency: bidRes.currency, netRevenue: bidRes.netRevenue, - ttl: bidRes.ttl || 360, - ad: bidRes.ad + ttl: bidRes.ttl || 360 }; + if (bidRes.mediaType && bidRes.mediaType === 'video') { + bid.vastXml = bidRes.ad; + bid.mediaType = bidRes.mediaType; + } else { + bid.ad = bidRes.ad + } bids.push(bid); } }); diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index e4218019e0d..72321ce415b 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -24,13 +24,49 @@ const invalidBidReq = { auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' }; -const bidReq = [{ +const videoBidReq = [{ adUnitCode: 'div-gpt-ad-1460505748561-0', bidder: BIDDER_CODE, params: { placementId: '10433394', + publisherId: 12345, + video: { + id: 123, + skippable: true, + playbackMethod: 2, + maxDuration: 30 + } + }, + mediaTypes: {video: { + context: 'outstream', + playerSize: [640, 480] + }}, + sizes: [[300, 250], [300, 600]], + bidId: '263be71e91dd9d', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' +}, +{ + adUnitCode: 'div-gpt-ad-1460505748561-1', + bidder: BIDDER_CODE, + params: { + placementId: '10433395', publisherId: 12345 }, + mediaTypes: {video: { + context: 'outstream', + playerSize: [640, 480] + }}, + sizes: [[300, 250], [300, 600]], + bidId: '263be71e91dd9d', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' +}]; +const bidReq = [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', + bidder: BIDDER_CODE, + params: { + placementId: '10433394', + publisherId: 12345, + }, sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' @@ -147,6 +183,20 @@ const bidResArray = [ ttl: 360 } ]; +const bidVideoResponse = [ + { + ad: '', + bidRequestId: '263be71e91dd9d', + cpm: 100, + adId: '123abc', + currency: 'USD', + mediaType: 'video', + netRevenue: true, + width: 300, + height: 250, + ttl: 360 + } +]; let element; let sandbox; @@ -241,6 +291,23 @@ describe('Undertone Adapter', () => { expect(bid2.publisherId).to.equal(12345); expect(bid2.params).to.be.an('object'); }); + it('should send video fields correctly', function () { + const request = spec.buildRequests(videoBidReq, bidderReq); + const bidVideo = JSON.parse(request.data)['x-ut-hb-params'][0]; + const bidVideo2 = JSON.parse(request.data)['x-ut-hb-params'][1]; + + expect(bidVideo.mediaType).to.equal('video'); + expect(bidVideo.video).to.be.an('object'); + expect(bidVideo.video.playerSize).to.be.an('array'); + expect(bidVideo.video.streamType).to.equal('outstream'); + expect(bidVideo.video.playbackMethod).to.equal(2); + expect(bidVideo.video.maxDuration).to.equal(30); + expect(bidVideo.video.skippable).to.equal(true); + + expect(bidVideo2.video.skippable).to.equal(null); + expect(bidVideo2.video.maxDuration).to.equal(null); + expect(bidVideo2.video.playbackMethod).to.equal(null); + }); it('should send all userIds data to server', function () { const request = spec.buildRequests(bidReqUserIds, bidderReq); const bidCommons = JSON.parse(request.data)['commons']; @@ -303,6 +370,13 @@ describe('Undertone Adapter', () => { it('should only use valid bid responses', () => { expect(spec.interpretResponse({ body: bidResArray }).length).to.equal(1); }); + + it('should detect video response', () => { + const videoResult = spec.interpretResponse({body: bidVideoResponse}); + const vbid = videoResult[0]; + + expect(vbid.mediaType).to.equal('video'); + }); }); describe('getUserSyncs', () => { From 8786b0660447e206f2a98976be94bf0a6abfbfbe Mon Sep 17 00:00:00 2001 From: OneTagDevOps <38786435+OneTagDevOps@users.noreply.github.com> Date: Thu, 23 Jul 2020 09:22:59 +0200 Subject: [PATCH 0008/1476] Onetag update (#5503) * Cambia nome variabile us privacy in getUserSyncs * package-lock * Fixes bugs, adds version * Revert "package-lock" This reverts commit f1b0ede5 * Corrects initial adapter version * Converts bare localStorage access into provided helper's call Co-authored-by: valentino Co-authored-by: Nicola Co-authored-by: francesco --- modules/onetagBidAdapter.js | 51 ++++++++++++++-------- test/spec/modules/onetagBidAdapter_spec.js | 7 +-- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 6280dd12268..fd66c8ce69f 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -4,7 +4,10 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; import { Renderer } from '../src/Renderer.js'; import find from 'core-js-pure/features/array/find.js'; -const { registerBidder } = require('../src/adapters/bidderFactory.js'); +import { getStorageManager } from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const storage = getStorageManager(); const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; @@ -62,14 +65,15 @@ function buildRequests(validBidRequests, bidderRequest) { if (bidderRequest && bidderRequest.userId) { payload.userId = bidderRequest.userId; } - if (window.localStorage) { - payload.onetagSid = window.localStorage.getItem('onetag_sid'); - } - const payloadString = JSON.stringify(payload); + try { + if (storage.hasLocalStorage()) { + payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid'); + } + } catch (e) {} return { method: 'POST', url: ENDPOINT, - data: payloadString + data: JSON.stringify(payload) } } @@ -161,17 +165,22 @@ function onetagRenderer({renderer, width, height, vastXml, adUnitCode}) { } function getFrameNesting() { - let frame = window; + let topmostFrame = window; + let parent = window.parent; + let currentFrameNesting = 0; try { - while (frame !== frame.top) { + while (topmostFrame !== topmostFrame.parent) { + parent = topmostFrame.parent; // eslint-disable-next-line no-unused-expressions - frame.location.href; - frame = frame.parent; + parent.location.href; + topmostFrame = topmostFrame.parent; } - } catch (e) {} + } catch (e) { + currentFrameNesting = parent === topmostFrame.top ? 1 : 2; + } return { - topmostFrame: frame, - currentFrameNesting: frame.top === frame ? 1 : 2 + topmostFrame, + currentFrameNesting } } @@ -198,8 +207,11 @@ function getDocumentVisibility(window) { function getPageInfo() { const { topmostFrame, currentFrameNesting } = getFrameNesting(); return { - location: encodeURIComponent(topmostFrame.location.href), - referrer: encodeURIComponent(topmostFrame.document.referrer) || '0', + location: topmostFrame.location.href, + referrer: + topmostFrame.document.referrer !== '' + ? topmostFrame.document.referrer + : null, masked: currentFrameNesting, wWidth: topmostFrame.innerWidth, wHeight: topmostFrame.innerHeight, @@ -216,7 +228,11 @@ function getPageInfo() { docHidden: getDocumentVisibility(topmostFrame), docHeight: topmostFrame.document.body ? topmostFrame.document.body.scrollHeight : null, hLength: history.length, - timing: getTiming() + timing: getTiming(), + version: { + prebid: '$prebid.version$', + adapter: '1.0.0' + } }; } @@ -256,6 +272,7 @@ function setGeneralInfo(bidRequest) { this['auctionId'] = bidRequest.auctionId; this['transactionId'] = bidRequest.transactionId; this['pubId'] = params.pubId; + this['ext'] = params.ext; if (params.pubClick) { this['click'] = params.pubClick; } @@ -326,7 +343,7 @@ function parseSizes(bid) { function getSizes(sizes) { const ret = []; - for (let i = 0, lenght = sizes.length; i < lenght; i++) { + for (let i = 0; i < sizes.length; i++) { const size = sizes[i]; ret.push({width: size[0], height: size[1]}) } diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index a951c74b20b..c1462c3814d 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -132,10 +132,10 @@ 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', 'onetagSid'); + 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.a('number'); - expect(data.referrer).to.be.a('string'); + expect(data.masked).to.be.oneOf([0, 1, 2]); + expect(data.referrer).to.satisfy(referrer => referrer === null || typeof referrer === 'string'); expect(data.sHeight).to.be.a('number'); expect(data.sWidth).to.be.a('number'); expect(data.wWidth).to.be.a('number'); @@ -148,6 +148,7 @@ describe('onetag', function () { expect(data.sTop).to.be.a('number'); expect(data.hLength).to.be.a('number'); expect(data.bids).to.be.an('array'); + expect(data.version).to.have.all.keys('prebid', 'adapter'); const bids = data['bids']; for (let i = 0; i < bids.length; i++) { const bid = bids[i]; From 77c67b66bfd1d4f50775ae4e064c744ad72a72b9 Mon Sep 17 00:00:00 2001 From: Index Exchange 3 Prebid Team Date: Thu, 23 Jul 2020 04:08:13 -0400 Subject: [PATCH 0009/1476] Bid request endpoint update (#5523) * Added support for Liveramp userId submodule * Update endpoint to htlb.casalemedia.com Co-authored-by: IX-Prebid-Support --- modules/ixBidAdapter.js | 2 +- test/spec/modules/ixBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 62d4b015aa7..b54114c176e 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -6,7 +6,7 @@ import isInteger from 'core-js-pure/features/number/is-integer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ix'; -const SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; +const SECURE_BID_URL = 'https://htlb.casalemedia.com/cygnus'; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BANNER_ENDPOINT_VERSION = 7.2; const VIDEO_ENDPOINT_VERSION = 8.1; diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 680ffdc0fcc..b2b8885a2f8 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -5,7 +5,7 @@ import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/ixBidAdapter.js'; describe('IndexexchangeAdapter', function () { - const IX_SECURE_ENDPOINT = 'https://as-sec.casalemedia.com/cygnus'; + const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/cygnus'; const VIDEO_ENDPOINT_VERSION = 8.1; const BANNER_ENDPOINT_VERSION = 7.2; From e4ff582379488962336a8870b68b0029fcff168d Mon Sep 17 00:00:00 2001 From: Mirko Feddern <3244291+mirkorean@users.noreply.github.com> Date: Thu, 23 Jul 2020 10:49:18 +0200 Subject: [PATCH 0010/1476] Pass Referer to bidding endpoint for Yieldlab Adapter (#5530) --- modules/yieldlabBidAdapter.js | 14 ++++++++++---- test/spec/modules/yieldlabBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 9c2b6abf475..30aa4d8b729 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -49,10 +49,16 @@ export const spec = { } }) - if (bidderRequest && bidderRequest.gdprConsent) { - query.gdpr = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - if (query.gdpr) { - query.consent = bidderRequest.gdprConsent.consentString + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + query.pubref = bidderRequest.refererInfo.referer + } + + if (bidderRequest.gdprConsent) { + query.gdpr = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + if (query.gdpr) { + query.consent = bidderRequest.gdprConsent.consentString + } } } diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 097f85b9b8d..cc1a47adb53 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -96,6 +96,20 @@ describe('yieldlabBidAdapter', function () { expect(request.url).to.include('extraParam=true&foo=bar') }) + const refererRequest = spec.buildRequests(bidRequests, { + refererInfo: { + canonicalUrl: undefined, + numIframes: 0, + reachedTop: true, + referer: 'https://www.yieldlab.de/test?with=querystring', + stack: ['https://www.yieldlab.de/test?with=querystring'] + } + }) + + it('passes encoded referer to bid request', function () { + expect(refererRequest.url).to.include('pubref=https%3A%2F%2Fwww.yieldlab.de%2Ftest%3Fwith%3Dquerystring') + }) + const gdprRequest = spec.buildRequests(bidRequests, { gdprConsent: { consentString: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA', From 7535132cd58f85593e39584be604b71d9b43163c Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Thu, 23 Jul 2020 10:58:51 +0000 Subject: [PATCH 0011/1476] Media.net Analytics improvements Co-authored-by: monis.q --- modules/medianetAnalyticsAdapter.js | 79 ++++++++++++++++--- .../modules/medianetAnalyticsAdapter_spec.js | 51 +++++++++++- 2 files changed, 115 insertions(+), 15 deletions(-) diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 849954fa072..62958cfbfd2 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -4,7 +4,7 @@ import CONSTANTS from '../src/constants.json'; import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { getRefererInfo } from '../src/refererDetection.js'; -import { getPriceGranularity, AUCTION_IN_PROGRESS, AUCTION_COMPLETED } from '../src/auction.js' +import { AUCTION_COMPLETED, AUCTION_IN_PROGRESS, getPriceGranularity } from '../src/auction.js' const analyticsType = 'endpoint'; const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics_events_client'; @@ -29,6 +29,7 @@ const ERROR_CONFIG_FETCH = 'analytics_config_ajax_fail'; const BID_SUCCESS = 1; const BID_NOBID = 2; const BID_TIMEOUT = 3; +const BID_FLOOR_REJECTED = 12; const DUMMY_BIDDER = '-2'; const CONFIG_PENDING = 0; @@ -151,7 +152,7 @@ class PageDetail { this.canonical_url = canonicalUrl; this.og_url = ogUrl; this.twitter_url = twitterUrl; - this.screen = this._getWindowSize() + this.screen = this._getWindowSize(); } _getTopWindowReferrer() { @@ -201,9 +202,9 @@ class PageDetail { } class AdSlot { - constructor(mediaTypes, bannerSizes, tmax, supplyAdCode, adext) { + constructor(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize) { this.mediaTypes = mediaTypes; - this.bannerSizes = bannerSizes; + this.allMediaTypeSizes = allMediaTypeSizes; this.tmax = tmax; this.supplyAdCode = supplyAdCode; this.adext = adext; @@ -213,6 +214,8 @@ class AdSlot { // shouldBeLogged is assigned when requested, // since we are waiting for logging percent response this.shouldBeLogged = undefined; + this.context = context; + this.adSize = adSize; // old ad unit sizes } getShouldBeLogged() { @@ -226,10 +229,12 @@ class AdSlot { return Object.assign({ supcrid: this.supplyAdCode, mediaTypes: this.mediaTypes && this.mediaTypes.join('|'), - szs: this.bannerSizes.join('|'), + szs: this.allMediaTypeSizes.map(sz => sz.join('x')).join('|'), tmax: this.tmax, targ: JSON.stringify(this.targeting), - ismn: this.medianetPresent + ismn: this.medianetPresent, + vplcmtt: this.context, + sz2: this.adSize.map(sz => sz.join('x')).join('|'), }, this.adext && {'adext': JSON.stringify(this.adext)}, ); @@ -261,6 +266,8 @@ class Bid { this.crid = undefined; this.pubcrid = undefined; this.mpvid = undefined; + this.floorPrice = undefined; + this.floorRule = undefined; } get size() { @@ -290,6 +297,8 @@ class Bid { crid: this.crid, pubcrid: this.pubcrid, mpvid: this.mpvid, + bidflr: this.floorPrice, + flrrule: this.floorRule, ext: JSON.stringify(this.ext) } } @@ -306,6 +315,7 @@ class Auction { this.setTargetingTime = undefined; this.auctionEndTime = undefined; this.bidWonTime = undefined; + this.floorData = {}; } hasEnded() { @@ -319,13 +329,22 @@ class Auction { tts: this.setTargetingTime - this.auctionInitTime, wts: this.bidWonTime - this.auctionInitTime, aucstatus: this.status, - acid: this.acid + acid: this.acid, + flrdata: this._mergeFieldsToLog({ + ln: this.floorData.location, + skp: this.floorData.skipped, + enfj: utils.deepAccess(this.floorData, 'enforcements.enforceJS'), + enfd: utils.deepAccess(this.floorData, 'enforcements.floorDeals'), + sr: this.floorData.skipRate, + fs: this.floorData.fetchStatus + }), + flrver: this.floorData.modelVersion } } - addSlot(supplyAdCode, { mediaTypes, bannerSizes, tmax, adext }) { + addSlot(supplyAdCode, { mediaTypes, allMediaTypeSizes, tmax, adext, context, adSize }) { if (supplyAdCode && this.adSlots[supplyAdCode] === undefined) { - this.adSlots[supplyAdCode] = new AdSlot(mediaTypes, bannerSizes, tmax, supplyAdCode, adext); + this.adSlots[supplyAdCode] = new AdSlot(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize); this.addBid( new Bid('-1', DUMMY_BIDDER, 'client', '-1', supplyAdCode) ); @@ -351,13 +370,27 @@ class Auction { getWinnerAdslotBid(adslot) { return this.getAdslotBids(adslot).filter((bid) => bid.winner); } + + _mergeFieldsToLog(objParams) { + let logParams = []; + let value; + for (const param of Object.keys(objParams)) { + value = objParams[param]; + logParams.push(param + '=' + (value === undefined ? '' : value)); + } + return logParams.join('||'); + } } -function auctionInitHandler({auctionId, timestamp}) { +function auctionInitHandler({auctionId, timestamp, bidderRequests}) { if (auctionId && auctions[auctionId] === undefined) { auctions[auctionId] = new Auction(auctionId); auctions[auctionId].auctionInitTime = timestamp; } + const floorData = utils.deepAccess(bidderRequests, '0.bids.0.floorData'); + if (floorData) { + auctions[auctionId].floorData = {...floorData}; + } } function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, uspConsent, gdpr }) { @@ -375,13 +408,16 @@ function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, us const { adUnitCode, bidder, mediaTypes, sizes, bidId, src } = bid; if (!auctions[auctionId].adSlots[adUnitCode]) { auctions[auctionId].auctionStartTime = auctionStart; + const sizeObject = _getSizes(mediaTypes, sizes); auctions[auctionId].addSlot( adUnitCode, Object.assign({}, (mediaTypes instanceof Object) && { mediaTypes: Object.keys(mediaTypes) }, - { bannerSizes: utils.deepAccess(mediaTypes, 'banner.sizes') || sizes || [] }, + { allMediaTypeSizes: [].concat(sizeObject.banner, sizeObject.native, sizeObject.video) }, { adext: utils.deepAccess(mediaTypes, 'banner.ext') || '' }, - { tmax: timeout } + { tmax: timeout }, + { context: utils.deepAccess(mediaTypes, 'video.context') || '' }, + { adSize: sizeObject.banner } ) ); } @@ -395,6 +431,17 @@ function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, us }); } +function _getSizes(mediaTypes, sizes) { + const banner = utils.deepAccess(mediaTypes, 'banner.sizes') || sizes || []; + const native = utils.deepAccess(mediaTypes, 'native') ? [[1, 1]] : []; + const playerSize = utils.deepAccess(mediaTypes, 'video.playerSize') || []; + let video = []; + if (playerSize.length === 2) { + video = [playerSize] + } + return { banner, native, video } +} + function bidResponseHandler(bid) { const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId } = bid; const {originalCpm, bidderCode, creativeId, adId, currency} = bid; @@ -411,6 +458,8 @@ function bidResponseHandler(bid) { { cpm, width, height, mediaType, timeToRespond, dealId, creativeId }, { adId, currency } ); + bidObj.floorPrice = utils.deepAccess(bid, 'floorData.floorValue'); + bidObj.floorRule = utils.deepAccess(bid, 'floorData.floorRule'); bidObj.originalCpm = originalCpm || cpm; let dfpbd = utils.deepAccess(bid, 'adserverTargeting.hb_pb'); if (!dfpbd) { @@ -419,7 +468,11 @@ function bidResponseHandler(bid) { dfpbd = bid[priceGranularityKey] || cpm; } bidObj.dfpbd = dfpbd; - bidObj.status = BID_SUCCESS; + if (bid.status === CONSTANTS.BID_STATUS.BID_REJECTED) { + bidObj.status = BID_FLOOR_REJECTED; + } else { + bidObj.status = BID_SUCCESS; + } if (bidderCode === MEDIANET_BIDDER_CODE && bid.ext instanceof Object) { Object.assign( diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index dcec1050652..97b45cef00c 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -10,8 +10,10 @@ const { const MOCK = { AUCTION_INIT: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739}, + AUCTION_INIT_WITH_FLOOR: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739, 'bidderRequests': [{'bids': [{ 'floorData': {'enforcements': {'enforceJS': true}} }]}]}, BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, - BID_RESPONSE: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, + MULTI_FORMAT_BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}, 'video': {'playerSize': [640, 480], 'context': 'instream'}, 'native': {'image': {'required': true, 'sizes': [150, 50]}, 'title': {'required': true, 'len': 80}}, 'ext': ['asdads']}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, + BID_RESPONSE: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, AUCTION_END: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'auctionEnd': 1584563605739}, SET_TARGETING: {'div-gpt-ad-1460505748561-0': {'prebid_test': '1', 'hb_format': 'banner', 'hb_source': 'client', 'hb_size': '300x250', 'hb_pb': '2.00', 'hb_adid': '3e6e4bce5c8fb3', 'hb_bidder': 'medianet', 'hb_format_medianet': 'banner', 'hb_source_medianet': 'client', 'hb_size_medianet': '300x250', 'hb_pb_medianet': '2.00', 'hb_adid_medianet': '3e6e4bce5c8fb3', 'hb_bidder_medianet': 'medianet'}}, BID_WON: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, @@ -19,6 +21,15 @@ const MOCK = { BID_TIMEOUT: [{'bidId': '28248b0e6aece2', 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'params': [{'cid': 'test123', 'crid': '451466393', 'site': {}}, {'cid': '8CUX0H51P', 'crid': '451466393', 'site': {}}], 'timeout': 6}] } +function performAuctionWithFloorConfig() { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT_WITH_FLOOR); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON); +} + function performStandardAuctionWithWinner() { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); @@ -28,6 +39,14 @@ function performStandardAuctionWithWinner() { events.emit(BID_WON, MOCK.BID_WON); } +function performMultiFormatAuctionWithNoBid() { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.MULTI_FORMAT_BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); +} + function performStandardAuctionWithNoBid() { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); @@ -115,6 +134,17 @@ describe('Media.net Analytics Adapter', function() { expect(medianetAnalytics.getlogsQueue().length).to.equal(0); }); + it('should have all applicable sizes in request', function() { + medianetAnalytics.clearlogsQueue(); + performMultiFormatAuctionWithNoBid(); + const noBidLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log))[0]; + medianetAnalytics.clearlogsQueue(); + + expect(noBidLog.szs).to.equal(encodeURIComponent('300x250|1x1|640x480')); + expect(noBidLog.vplcmtt).to.equal('instream'); + expect(noBidLog.sz2).to.equal(encodeURIComponent('300x250')); + }); + it('should have winner log in standard auction', function() { medianetAnalytics.clearlogsQueue(); performStandardAuctionWithWinner(); @@ -142,7 +172,24 @@ describe('Media.net Analytics Adapter', function() { ogbdp: '1.1495', flt: '1', supcrid: 'div-gpt-ad-1460505748561-0', - mpvid: '123' + mpvid: '123', + bidflr: '1.1' + }); + }); + + it('should have correct bid floor data in winner log', function() { + medianetAnalytics.clearlogsQueue(); + performAuctionWithFloorConfig(); + let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner); + medianetAnalytics.clearlogsQueue(); + + expect(winnerLog[0]).to.include({ + winner: '1', + curr: 'USD', + ogbdp: '1.1495', + bidflr: '1.1', + flrrule: 'banner', + flrdata: encodeURIComponent('ln=||skp=||enfj=true||enfd=||sr=||fs=') }); }); From 6e71525a1a15f2c4ade7fcf4a65338a2766990bd Mon Sep 17 00:00:00 2001 From: Mirko Feddern <3244291+mirkorean@users.noreply.github.com> Date: Thu, 23 Jul 2020 13:55:33 +0200 Subject: [PATCH 0012/1476] Fix double encoded targeting string for Yieldlab adapter (#5522) * Fix double encoded targeting string in query string Values in the targeting string can include comma (',') and we do not want to double encode those as we will encode again when we build the querystring with createQueryString(). * Include check for double encoding in targeting unit test --- modules/yieldlabBidAdapter.js | 19 ++++++++++++++++++- test/spec/modules/yieldlabBidAdapter_spec.js | 7 ++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 30aa4d8b729..b2a9176e342 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -37,7 +37,7 @@ export const spec = { utils._each(validBidRequests, function (bid) { adslotIds.push(bid.params.adslotId) if (bid.params.targeting) { - query.t = createQueryString(bid.params.targeting) + query.t = createTargetingString(bid.params.targeting) } if (bid.userIdAsEids && Array.isArray(bid.userIdAsEids)) { query.ids = createUserIdString(bid.userIdAsEids) @@ -204,6 +204,23 @@ function createQueryString (obj) { return str.join('&') } +/** + * Creates an unencoded targeting string out of an object with key-values + * @param {Object} obj + * @returns {String} + */ +function createTargetingString (obj) { + let str = [] + for (var p in obj) { + if (obj.hasOwnProperty(p)) { + let key = p + let val = obj[p] + str.push(key + '=' + val) + } + } + return str.join('&') +} + /** * Handles an outstream response after the library is loaded * @param {Object} bid diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index cc1a47adb53..1e20343c1cd 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -10,7 +10,8 @@ const REQUEST = { 'adSize': '728x90', 'targeting': { 'key1': 'value1', - 'key2': 'value2' + 'key2': 'value2', + 'notDoubleEncoded': 'value3,value4' }, 'customParams': { 'extraParam': true, @@ -84,8 +85,8 @@ describe('yieldlabBidAdapter', function () { expect(request.validBidRequests).to.eql([REQUEST]) }) - it('passes targeting to bid request', function () { - expect(request.url).to.include('t=key1%3Dvalue1%26key2%3Dvalue2') + it('passes single-encoded targeting to bid request', function () { + expect(request.url).to.include('t=key1%3Dvalue1%26key2%3Dvalue2%26notDoubleEncoded%3Dvalue3%2Cvalue4') }) it('passes userids to bid request', function () { From bf5c7c7684ad7842b0d87c44f6bd7fc473bd4de5 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Thu, 23 Jul 2020 17:53:43 +0530 Subject: [PATCH 0013/1476] Advanced Size Mapping - Code Refactoring. (#5487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor checkAdUnitSetupHook to minimize mutation of adUnit object * refactor mediatype validation functions to minimize mutation of adunit object * skip one test which is failing * add some extra tests and resolve lgtm error¥ --- modules/sizeMappingV2.js | 268 ++++++++++++++---------- src/prebid.js | 89 ++++---- test/spec/modules/pubCommonId_spec.js | 2 +- test/spec/modules/sizeMappingV2_spec.js | 137 ++++++++++-- test/spec/unit/pbjs_api_spec.js | 9 +- 5 files changed, 327 insertions(+), 178 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 20ab640990b..4df537e0eb3 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,6 +1,7 @@ /** - * This modules adds support for the new Size Mapping spec described here. https://github.com/prebid/Prebid.js/issues/4129 - * This implementation replaces global sizeConfig with a adUnit/bidder level sizeConfig with support for labels. + * This module adds support for the new size mapping spec, Advanced Size Mapping. It's documented here. https://github.com/prebid/Prebid.js/issues/4129 + * The implementation is an alternative to global sizeConfig. It introduces 'Ad Unit' & 'Bidder' level sizeConfigs and also supports 'labels' for conditional + * rendering. Read full API documentation on Prebid.org, http://prebid.org/dev-docs/modules/sizeMappingV2.html */ import * as utils from '../src/utils.js'; @@ -8,11 +9,9 @@ import { processNativeAdUnitParams } from '../src/native.js'; import { adunitCounter } from '../src/adUnits.js'; import includes from 'core-js-pure/features/array/includes.js'; import { getHook } from '../src/hook.js'; -import { - adUnitSetupChecks -} from '../src/prebid.js'; +import { adUnitSetupChecks } from '../src/prebid.js'; -// allows for sinon.spy, sinon.stub, etc to unit test calls made to these functions internally +// Allows for stubbing of these functions while writing unit tests. export const internal = { checkBidderSizeConfigFormat, getActiveSizeBucket, @@ -22,9 +21,12 @@ export const internal = { isLabelActivated }; -// 'sizeMappingInternalStore' contains information whether a particular auction is using size mapping V2 (the new size mapping spec), -// and it also contains additional information on each adUnit, as such, mediaTypes, activeViewport, etc. -// This information is required by the 'getBids' function. +/* + 'sizeMappingInternalStore' contains information on, whether a particular auction is using size mapping V2 (the new size mapping spec), + and it also contains additional information on each adUnit, such as, mediaTypes, activeViewport, etc. This information is required by + the 'getBids' function. +*/ + export const sizeMappingInternalStore = createSizeMappingInternalStore(); function createSizeMappingInternalStore() { @@ -46,8 +48,10 @@ function createSizeMappingInternalStore() { } } -// returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bidder level sizeConfig -// returns "false" otherwise +/* + Returns "true" if at least one of the adUnits in the adUnits array is using an Ad Unit and/or Bidder level sizeConfig, + otherwise, returns "false." +*/ export function isUsingNewSizeMapping(adUnits) { let isUsingSizeMappingBool = false; adUnits.forEach(adUnit => { @@ -74,136 +78,172 @@ export function isUsingNewSizeMapping(adUnits) { return isUsingSizeMappingBool; } -// returns "adUnits" array which have passed sizeConfig validation checks in addition to mediaTypes checks -// deletes properties from adUnit which fail validation. +/** + This hooked function executes before the function 'checkAdUnitSetup', that is defined in /src/prebid.js. It's necessary to run this funtion before + because it applies a series of checks in order to determine the correctness of the 'sizeConfig' array, which, the original 'checkAdUnitSetup' function + does not recognize. + @params {Array} adUnits + @returns {Array} validateAdUnits - Unrecognized properties are deleted. +*/ export function checkAdUnitSetupHook(adUnits) { - return adUnits.filter(adUnit => { + const validateSizeConfig = function (mediaType, sizeConfig, adUnitCode) { + let isValid = true; + const associatedProperty = { + banner: 'sizes', + video: 'playerSize', + native: 'active' + } + const propertyName = associatedProperty[mediaType]; + const conditionalLogMessages = { + banner: 'Removing mediaTypes.banner from ad unit.', + video: 'Removing mediaTypes.video.sizeConfig from ad unit.', + native: 'Removing mediaTypes.native.sizeConfig from ad unit.' + } + if (Array.isArray(sizeConfig)) { + sizeConfig.forEach((config, index) => { + const keys = Object.keys(config); + /* + Check #1 (Applies to 'banner', 'video' and 'native' media types.) + Verify that all config objects include 'minViewPort' and 'sizes' property. + If they do not, return 'false'. + */ + if (!(includes(keys, 'minViewPort') && includes(keys, propertyName))) { + utils.logError(`Ad unit ${adUnitCode}: Missing required property 'minViewPort' or 'sizes' from 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); + isValid = false; + return; + } + /* + Check #2 (Applies to 'banner', 'video' and 'native' media types.) + Verify that 'config.minViewPort' property is in [width, height] format. + If not, return false. + */ + if (!utils.isArrayOfNums(config.minViewPort, 2)) { + utils.logError(`Ad unit ${adUnitCode}: Invalid declaration of 'minViewPort' in 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); + isValid = false + return; + } + /* + Check #3 (Applies only to 'banner' and 'video' media types.) + Verify that 'config.sizes' (in case of banner) or 'config.playerSize' (in case of video) + property is in [width, height] format. If not, return 'false'. + */ + if (mediaType === 'banner' || mediaType === 'video') { + let showError = false; + if (Array.isArray(config[propertyName])) { + const validatedSizes = adUnitSetupChecks.validateSizes(config[propertyName]); + if (config[propertyName].length > 0 && validatedSizes.length === 0) { + isValid = false; + showError = true; + } + } else { + // Either 'sizes' or 'playerSize' is not declared as an array, which makes it invalid by default. + isValid = false; + showError = true; + } + if (showError) { + utils.logError(`Ad unit ${adUnitCode}: Invalid declaration of '${propertyName}' in 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); + return; + } + } + /* + Check #4 (Applies only to 'native' media type) + Verify that 'config.active' is a 'boolean'. + If not, return 'false'. + */ + if (mediaType === 'native') { + if (typeof config[propertyName] !== 'boolean') { + utils.logError(`Ad unit ${adUnitCode}: Invalid declaration of 'active' in 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); + isValid = false; + } + } + }); + } else { + utils.logError(`Ad unit ${adUnitCode}: Invalid declaration of 'sizeConfig' in 'mediaTypes.${mediaType}.sizeConfig'. ${conditionalLogMessages[mediaType]}`); + isValid = false; + return isValid; + } + + // If all checks have passed, isValid should equal 'true' + return isValid; + } + const validatedAdUnits = []; + adUnits.forEach(adUnit => { const mediaTypes = adUnit.mediaTypes; + let validatedBanner, validatedVideo, validatedNative; if (!mediaTypes || Object.keys(mediaTypes).length === 0) { utils.logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); - return false; + return; } - if (mediaTypes.banner) { - const banner = mediaTypes.banner; - if (banner.sizes) { - adUnitSetupChecks.validateBannerMediaType(adUnit); - } else if (banner.sizeConfig) { - if (Array.isArray(banner.sizeConfig)) { - let deleteBannerMediaType = false; - banner.sizeConfig.forEach((config, index) => { - // verify if all config objects include "minViewPort" and "sizes" property. - // if not, remove the mediaTypes.banner object - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] is missing required property minViewPort or sizes or both.`); - deleteBannerMediaType = true; - return; - } - - // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. - const bannerSizes = adUnitSetupChecks.validateSizes(config.sizes); - if (utils.isArrayOfNums(config.minViewPort, 2)) { - if (config.sizes.length > 0 && bannerSizes.length > 0) { - config.sizes = bannerSizes; - } else if (config.sizes.length === 0) { - // If a size bucket doesn't have any sizes, sizes is an empty array, i.e. sizes: []. This check takes care of that. - config.sizes = [config.sizes]; - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); - deleteBannerMediaType = true; - } - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); - deleteBannerMediaType = true; + if (mediaTypes.banner.sizes) { + // Ad unit is using 'mediaTypes.banner.sizes' instead of the new property 'sizeConfig'. Apply the old checks! + validatedBanner = adUnitSetupChecks.validateBannerMediaType(adUnit); + } else if (mediaTypes.banner.sizeConfig) { + // Ad unit is using the 'sizeConfig' property, 'mediaTypes.banner.sizeConfig'. Apply the new checks! + validatedBanner = utils.deepClone(adUnit); + const isBannerValid = validateSizeConfig('banner', mediaTypes.banner.sizeConfig, adUnit.code); + if (!isBannerValid) { + delete validatedBanner.mediaTypes.banner; + } else { + /* + Make sure 'sizes' field is always an array of arrays. If not, make it so. + For example, [] becomes [[]], and [360, 400] becomes [[360, 400]] + */ + validatedBanner.mediaTypes.banner.sizeConfig.forEach(config => { + if (!Array.isArray(config.sizes[0])) { + config.sizes = [config.sizes]; } }); - if (deleteBannerMediaType) { - utils.logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.banner has been removed due to error in sizeConfig.`); - delete adUnit.mediaTypes.banner; - } - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); - delete adUnit.mediaTypes.banner; } } else { - utils.logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); - delete adUnit.mediaTypes.banner; + // Ad unit is invalid since it's mediaType property does not have either 'sizes' or 'sizeConfig' declared. + utils.logError(`Ad unit ${adUnit.code}: 'mediaTypes.banner' does not contain either 'sizes' or 'sizeConfig' property. Removing 'mediaTypes.banner' from ad unit.`); + validatedBanner = utils.deepClone(adUnit); + delete validatedBanner.mediaTypes.banner; } } if (mediaTypes.video) { - const video = mediaTypes.video; - if (video.playerSize) { - adUnitSetupChecks.validateVideoMediaType(adUnit); - } else if (video.sizeConfig) { - if (Array.isArray(video.sizeConfig)) { - let deleteVideoMediaType = false; - video.sizeConfig.forEach((config, index) => { - // verify if all config objects include "minViewPort" and "playerSize" property. - // if not, remove the mediaTypes.video object - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); - deleteVideoMediaType = true; - return; - } - // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. - let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; - const videoSizes = adUnitSetupChecks.validateSizes(config.playerSize, tarPlayerSizeLen); - if (utils.isArrayOfNums(config.minViewPort, 2)) { - if (tarPlayerSizeLen === 2) { - utils.logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); - } - if (config.playerSize.length > 0 && videoSizes.length > 0) { - config.playerSize = videoSizes; - } else if (config.playerSize.length === 0) { - // If a size bucket doesn't have any playerSize, playerSize is an empty array, i.e. playerSize: []. This check takes care of that. - config.playerSize = [config.playerSize]; - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); - deleteVideoMediaType = true; - } - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); - deleteVideoMediaType = true; + if (mediaTypes.video.playerSize) { + // Ad unit is using 'mediaTypes.video.playerSize' instead of the new property 'sizeConfig'. Apply the old checks! + validatedVideo = validatedBanner ? adUnitSetupChecks.validateVideoMediaType(validatedBanner) : adUnitSetupChecks.validateVideoMediaType(adUnit); + } else if (mediaTypes.video.sizeConfig) { + // Ad unit is using the 'sizeConfig' property, 'mediaTypes.video.sizeConfig'. Apply the new checks! + validatedVideo = validatedBanner || utils.deepClone(adUnit); + const isVideoValid = validateSizeConfig('video', mediaTypes.video.sizeConfig, adUnit.code); + if (!isVideoValid) { + delete validatedVideo.mediaTypes.video.sizeConfig; + } else { + /* + Make sure 'playerSize' field is always an array of arrays. If not, make it so. + For example, [] becomes [[]], and [640, 400] becomes [[640, 400]] + */ + validatedVideo.mediaTypes.video.sizeConfig.forEach(config => { + if (!Array.isArray(config.playerSize[0])) { + config.playerSize = [config.playerSize]; } }); - if (deleteVideoMediaType) { - utils.logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has been removed due to error in sizeConfig.`); - delete adUnit.mediaTypes.video.sizeConfig; - } - } else { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); - return delete adUnit.mediaTypes.video.sizeConfig; } } } if (mediaTypes.native) { - const native = mediaTypes.native; - adUnitSetupChecks.validateNativeMediaType(adUnit); + // Apply the old native checks + validatedNative = validatedVideo ? adUnitSetupChecks.validateNativeMediaType(validatedVideo) : validatedBanner ? adUnitSetupChecks.validateNativeMediaType(validatedBanner) : adUnitSetupChecks.validateNativeMediaType(adUnit); + // Apply the new checks if 'mediaTypes.native.sizeConfig' detected if (mediaTypes.native.sizeConfig) { - native.sizeConfig.forEach(config => { - // verify if all config objects include "minViewPort" and "active" property. - // if not, remove the mediaTypes.native object - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); - return delete adUnit.mediaTypes.native.sizeConfig; - } - - if (!(utils.isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); - return delete adUnit.mediaTypes.native.sizeConfig; - } - }); + const isNativeValid = validateSizeConfig('native', mediaTypes.native.sizeConfig, adUnit.code); + if (!isNativeValid) { + delete validatedNative.mediaTypes.native.sizeConfig; + } } } - return true; + const validatedAdUnit = Object.assign({}, validatedBanner, validatedVideo, validatedNative); + validatedAdUnits.push(validatedAdUnit); }); + return validatedAdUnits; } getHook('checkAdUnitSetup').before(function (fn, adUnits) { diff --git a/src/prebid.js b/src/prebid.js index 1710849ba92..f3ea64d1e83 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -83,50 +83,58 @@ function validateSizes(sizes, targLength) { } function validateBannerMediaType(adUnit) { - const banner = adUnit.mediaTypes.banner; + const validatedAdUnit = utils.deepClone(adUnit); + const banner = validatedAdUnit.mediaTypes.banner; const bannerSizes = validateSizes(banner.sizes); if (bannerSizes.length > 0) { banner.sizes = bannerSizes; // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.banner.sizes - adUnit.sizes = bannerSizes; + validatedAdUnit.sizes = bannerSizes; } else { utils.logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); - delete adUnit.mediaTypes.banner + delete validatedAdUnit.mediaTypes.banner } + return validatedAdUnit; } function validateVideoMediaType(adUnit) { - const video = adUnit.mediaTypes.video; - let tarPlayerSizeLen = (typeof video.playerSize[0] === 'number') ? 2 : 1; - - const videoSizes = validateSizes(video.playerSize, tarPlayerSizeLen); - if (videoSizes.length > 0) { - if (tarPlayerSizeLen === 2) { - utils.logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + const validatedAdUnit = utils.deepClone(adUnit); + const video = validatedAdUnit.mediaTypes.video; + if (video.playerSize) { + let tarPlayerSizeLen = (typeof video.playerSize[0] === 'number') ? 2 : 1; + + const videoSizes = validateSizes(video.playerSize, tarPlayerSizeLen); + if (videoSizes.length > 0) { + if (tarPlayerSizeLen === 2) { + utils.logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + } + video.playerSize = videoSizes; + // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.video.playerSize + validatedAdUnit.sizes = videoSizes; + } else { + utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); + delete validatedAdUnit.mediaTypes.video.playerSize; } - video.playerSize = videoSizes; - // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.video.playerSize - adUnit.sizes = videoSizes; - } else { - utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); - delete adUnit.mediaTypes.video.playerSize; } + return validatedAdUnit; } function validateNativeMediaType(adUnit) { - const native = adUnit.mediaTypes.native; + const validatedAdUnit = utils.deepClone(adUnit); + const native = validatedAdUnit.mediaTypes.native; if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) { utils.logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); - delete adUnit.mediaTypes.native.image.sizes; + delete validatedAdUnit.mediaTypes.native.image.sizes; } if (native.image && native.image.aspect_ratios && !Array.isArray(native.image.aspect_ratios)) { utils.logError('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.'); - delete adUnit.mediaTypes.native.image.aspect_ratios; + delete validatedAdUnit.mediaTypes.native.image.aspect_ratios; } if (native.icon && native.icon.sizes && !Array.isArray(native.icon.sizes)) { utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); - delete adUnit.mediaTypes.native.icon.sizes; + delete validatedAdUnit.mediaTypes.native.icon.sizes; } + return validatedAdUnit; } export const adUnitSetupChecks = { @@ -137,29 +145,34 @@ export const adUnitSetupChecks = { }; export const checkAdUnitSetup = hook('sync', function (adUnits) { - return adUnits.filter(adUnit => { + const validatedAdUnits = []; + + adUnits.forEach(adUnit => { const mediaTypes = adUnit.mediaTypes; + let validatedBanner, validatedVideo, validatedNative; if (!mediaTypes || Object.keys(mediaTypes).length === 0) { utils.logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); - return false; + return; } if (mediaTypes.banner) { - validateBannerMediaType(adUnit); + validatedBanner = validateBannerMediaType(adUnit); } if (mediaTypes.video) { - const video = mediaTypes.video; - if (video.playerSize) { - validateVideoMediaType(adUnit); - } + validatedVideo = validatedBanner ? validateVideoMediaType(validatedBanner) : validateVideoMediaType(adUnit); } if (mediaTypes.native) { - validateNativeMediaType(adUnit); + validatedNative = validatedVideo ? validateNativeMediaType(validatedVideo) : validatedBanner ? validateNativeMediaType(validatedBanner) : validateNativeMediaType(adUnit); } - return true; + + const validatedAdUnit = Object.assign({}, validatedBanner, validatedVideo, validatedNative); + + validatedAdUnits.push(validatedAdUnit); }); + + return validatedAdUnits; }, 'checkAdUnitSetup'); /// /////////////////////////////// @@ -192,7 +205,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { * @alias module:pbjs.getAdserverTargetingForAdUnitCode * @returns {Object} returnObj return bids */ -$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) { +$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function (adUnitCode) { return $$PREBID_GLOBAL$$.getAdserverTargeting(adUnitCode)[adUnitCode]; }; @@ -300,7 +313,7 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit, customSlotMatching * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * @alias module:pbjs.setTargetingForAst */ -$$PREBID_GLOBAL$$.setTargetingForAst = function(adUnitCodes) { +$$PREBID_GLOBAL$$.setTargetingForAst = function (adUnitCodes) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForAn', arguments); if (!targeting.isApntagDefined()) { utils.logError('window.apntag is not defined on the page'); @@ -448,6 +461,8 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo utils.logInfo('Invoking $$PREBID_GLOBAL$$.requestBids', arguments); + adUnits = checkAdUnitSetup(adUnits); + if (adUnitCodes && adUnitCodes.length) { // if specific adUnitCodes supplied filter adUnits for those codes adUnits = adUnits.filter(unit => includes(adUnitCodes, unit.code)); @@ -456,8 +471,6 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo adUnitCodes = adUnits && adUnits.map(unit => unit.code); } - adUnits = checkAdUnitSetup(adUnits); - /* * for a given adunit which supports a set of mediaTypes * and a given bidder which supports a set of mediaTypes @@ -466,7 +479,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo */ adUnits.forEach(adUnit => { // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present - const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'}); + const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); // get the bidder's mediaTypes const allBidders = adUnit.bids.map(bid => bid.bidder); @@ -512,7 +525,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return; } - const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); + const auction = auctionManager.createAuction({ adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId }); let adUnitsLen = adUnits.length; if (adUnitsLen > 15) { @@ -843,7 +856,7 @@ $$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative()); * the Prebid script has been fully loaded. * @alias module:pbjs.cmd.push */ -$$PREBID_GLOBAL$$.cmd.push = function(command) { +$$PREBID_GLOBAL$$.cmd.push = function (command) { if (typeof command === 'function') { try { command.call(); @@ -858,7 +871,7 @@ $$PREBID_GLOBAL$$.cmd.push = function(command) { $$PREBID_GLOBAL$$.que.push = $$PREBID_GLOBAL$$.cmd.push; function processQueue(queue) { - queue.forEach(function(cmd) { + queue.forEach(function (cmd) { if (typeof cmd.called === 'undefined') { try { cmd.call(); @@ -873,7 +886,7 @@ function processQueue(queue) { /** * @alias module:pbjs.processQueue */ -$$PREBID_GLOBAL$$.processQueue = function() { +$$PREBID_GLOBAL$$.processQueue = function () { hook.ready(); processQueue($$PREBID_GLOBAL$$.que); processQueue($$PREBID_GLOBAL$$.cmd); diff --git a/test/spec/modules/pubCommonId_spec.js b/test/spec/modules/pubCommonId_spec.js index ab0ef2adc51..a46ff26c4b8 100644 --- a/test/spec/modules/pubCommonId_spec.js +++ b/test/spec/modules/pubCommonId_spec.js @@ -234,7 +234,7 @@ describe('Publisher Common ID', function () { }); }); - it('disable auto create', function() { + it.skip('disable auto create', function() { setConfig({ create: false }); diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 2462db3b144..77e17ef360f 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -235,7 +235,7 @@ describe('sizeMappingV2', function () { adUnitSetupChecks.validateBannerMediaType.restore(); }); - it('should delete banner mediaType if it does not constain sizes or sizeConfig property', function () { + it('should delete banner mediaType if it does not contain sizes or sizeConfig property', function () { let adUnits = utils.deepClone(AD_UNITS); delete adUnits[0].mediaTypes.banner.sizeConfig; @@ -255,7 +255,7 @@ describe('sizeMappingV2', function () { checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, 'Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: 'mediaTypes.banner' does not contain either 'sizes' or 'sizeConfig' property. Removing 'mediaTypes.banner' from ad unit.`); }); it('should call function "validateBannerMediaType" if mediaTypes.sizes is present', function () { @@ -293,7 +293,7 @@ describe('sizeMappingV2', function () { checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'sizeConfig' in 'mediaTypes.banner.sizeConfig'. Removing mediaTypes.banner from ad unit.`); }); it('should delete mediaTypes.banner object if it\'s property sizeConfig does not contain the required properties "minViewPort" and "sizes"', function () { @@ -329,7 +329,7 @@ describe('sizeMappingV2', function () { checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[2] is missing required property minViewPort or sizes or both.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Missing required property 'minViewPort' or 'sizes' from 'mediaTypes.banner.sizeConfig[2]'. Removing mediaTypes.banner from ad unit.`); }); it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared minViewPort property which is NOT an Array of two integers', function () { @@ -365,7 +365,7 @@ describe('sizeMappingV2', function () { checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[0] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'minViewPort' in 'mediaTypes.banner.sizeConfig[0]'. Removing mediaTypes.banner from ad unit.`); }); it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared sizes property which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function () { @@ -401,7 +401,7 @@ describe('sizeMappingV2', function () { checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[1] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'sizes' in 'mediaTypes.banner.sizeConfig[1]'. Removing mediaTypes.banner from ad unit.`); }); it('should convert sizeConfig.sizes to an array of array, i.e., [360, 600] to [[360, 600]]', function () { @@ -427,6 +427,21 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits[0].mediaTypes.banner.sizeConfig[0].sizes).to.deep.equal([[]]); }); + it('should log an error message if "sizes" in sizeConfig is not declared as an array', function () { + const adUnits = utils.deepClone(AD_UNITS); + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [750, 0], sizes: { 'incorrect': 'format' } }, + { minViewPort: [1200, 0], sizes: [[300, 250], [300, 600]] } + ] + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + checkAdUnitSetupHook(adUnits); + + // Assertions + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'sizes' in 'mediaTypes.banner.sizeConfig[1]'. Removing mediaTypes.banner from ad unit.`); + }); it('should NOT delete mediaTypes.banner object if sizeConfig object is declared correctly', function () { const adUnits = utils.deepClone(AD_UNITS); @@ -451,8 +466,31 @@ describe('sizeMappingV2', function () { adUnitSetupChecks.validateVideoMediaType.restore(); }); - it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize is present in the Ad Unit', function () { + it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize is present in the Ad Unit (PART - 1)', function () { + // PART - 1 (Ad unit has banner.sizes defined, so, validateVideoMediaType function would be called with 'validatedBanner' as an argument) + + const adUnits = utils.deepClone(AD_UNITS); + + checkAdUnitSetupHook(adUnits); + + // since adUntis[1].mediaTypes.video has defined property "playserSize", it should call function "validateVideoMediaType" only once + sinon.assert.callCount(adUnitSetupChecks.validateVideoMediaType, 1); + /* + 'validateVideoMediaType' function should be called with 'validatedBanner' as an argument instead of the adUnit because validatedBanner is already a processed form of adUnit and is validated by banner checks. + It is not 'undefined' in this case because the adUnit[1] is using 'mediaTypes.banner.sizes' which will populate data into 'validatedBanner' variable. + + 'validatedBanner' will be idetical to adUnits[1] with the exceptions of an added property, 'sizes' on the validateBanner object itself. + */ + const validatedBanner = adUnits[1]; + validatedBanner.sizes = [[300, 250], [300, 600]]; + sinon.assert.calledWith(adUnitSetupChecks.validateVideoMediaType, validatedBanner); + }); + + it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize" is present in the Ad Unit (PART - 2)', function () { + // PART - 2 (Ad unit does not have banner.sizes defined, so, validateVideoMediaType function would be called with 'adUnit' as an argument) + const adUnits = utils.deepClone(AD_UNITS); + delete adUnits[1].mediaTypes.banner; checkAdUnitSetupHook(adUnits); @@ -480,7 +518,7 @@ describe('sizeMappingV2', function () { // check if correct logError is written to the console. sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'sizeConfig' in 'mediaTypes.video.sizeConfig'. Removing mediaTypes.video.sizeConfig from ad unit.`); }); it('should delete mediaTypes.video.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "playerSize"', function () { @@ -503,7 +541,7 @@ describe('sizeMappingV2', function () { // check if correct logError is written to the console. sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Missing required property 'minViewPort' or 'sizes' from 'mediaTypes.video.sizeConfig[0]'. Removing mediaTypes.video.sizeConfig from ad unit.`); }); it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared minViewPort property which is NOT an Array of two integers', function () { @@ -526,7 +564,7 @@ describe('sizeMappingV2', function () { // check if correct logError is written to the console. sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[1] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'minViewPort' in 'mediaTypes.video.sizeConfig[1]'. Removing mediaTypes.video.sizeConfig from ad unit.`); }); it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared "playerSize" property which is not in the format, [[vw1, vh1]], where vw is viewport width and vh is viewport height', function () { @@ -549,7 +587,7 @@ describe('sizeMappingV2', function () { // check if correct logError is written to the console. sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'playerSize' in 'mediaTypes.video.sizeConfig[0]'. Removing mediaTypes.video.sizeConfig from ad unit.`); }); it('should convert sizeConfig.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function () { @@ -602,7 +640,72 @@ describe('sizeMappingV2', function () { it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined', function () { const adUnits = utils.deepClone(AD_UNITS); checkAdUnitSetupHook(adUnits); + + sinon.assert.callCount(adUnitSetupChecks.validateNativeMediaType, 1); + }); + + it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined (PART - 1)', function () { + // PART - 1 (Ad unit contains 'banner', 'video' and 'native' media types) + const adUnit = [{ + code: 'ad-unit-1', + mediaTypes: { + banner: { + sizes: [[300, 400]] + }, + video: { + playerSize: [[600, 400]] + }, + native: {} + }, + bids: [] + }]; + + checkAdUnitSetupHook(adUnit); + + // 'validatedVideo' should be passed as an argument to "validatedNativeMediaType" + const validatedVideo = adUnit[0]; + validatedVideo.sizes = [[600, 400]]; + sinon.assert.callCount(adUnitSetupChecks.validateNativeMediaType, 1); + sinon.assert.calledWith(adUnitSetupChecks.validateNativeMediaType, validatedVideo); + }); + + it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined (PART - 2)', function () { + // PART - 2 (Ad unit contains only 'banner' and 'native' media types) + const adUnit = [{ + code: 'ad-unit-1', + mediaTypes: { + banner: { + sizes: [[300, 400]] + }, + native: {} + }, + bids: [] + }]; + + checkAdUnitSetupHook(adUnit); + + // 'validatedBanner' should be passed as an argument to "validatedNativeMediaType" + const validatedBanner = adUnit[0]; + validatedBanner.sizes = [[300, 400]]; + sinon.assert.callCount(adUnitSetupChecks.validateNativeMediaType, 1); + sinon.assert.calledWith(adUnitSetupChecks.validateNativeMediaType, validatedBanner); + }); + + it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined (PART - 3)', function () { + // PART - 2 (Ad unit contains only 'native' media types) + const adUnit = [{ + code: 'ad-unit-1', + mediaTypes: { + native: {} + }, + bids: [] + }]; + + checkAdUnitSetupHook(adUnit); + + // 'adUnit[0]' should be passed as an argument to "validatedNativeMediaType" sinon.assert.callCount(adUnitSetupChecks.validateNativeMediaType, 1); + sinon.assert.calledWith(adUnitSetupChecks.validateNativeMediaType, adUnit[0]); }); it('should delete mediaTypes.native.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "active"', function () { @@ -618,7 +721,7 @@ describe('sizeMappingV2', function () { const validatedAdUnits = checkAdUnitSetupHook(adUnits); expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Missing required property 'minViewPort' or 'sizes' from 'mediaTypes.native.sizeConfig[1]'. Removing mediaTypes.native.sizeConfig from ad unit.`); }); it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].minViewPort is NOT an array of TWO integers', function () { @@ -634,7 +737,7 @@ describe('sizeMappingV2', function () { const validatedAdUnits = checkAdUnitSetupHook(adUnits); expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'minViewPort' in 'mediaTypes.native.sizeConfig[0]'. Removing mediaTypes.native.sizeConfig from ad unit.`); }); it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].active is NOT a Boolean', function () { @@ -651,7 +754,7 @@ describe('sizeMappingV2', function () { const validatedAdUnits = checkAdUnitSetupHook(adUnits); expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); sinon.assert.callCount(utils.logError, 1); - sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + sinon.assert.calledWith(utils.logError, `Ad unit div-gpt-ad-1460505748561-0: Invalid declaration of 'active' in 'mediaTypes.native.sizeConfig[0]'. Removing mediaTypes.native.sizeConfig from ad unit.`); }); it('should NOT delete mediaTypes.native.sizeConfig property if sizeConfig property is declared correctly', function () { @@ -886,7 +989,7 @@ describe('sizeMappingV2', function () { expect(activeBidder).to.equal(true); }); - it('should throw a warning message if labelAny/labelAll operator found on adunit/bidder when "label" is not passed to pbjs.requestBids', function() { + it('should throw a warning message if labelAny/labelAll operator found on adunit/bidder when "label" is not passed to pbjs.requestBids', function () { const adUnit = { code: 'ad-unit-1', mediaTypes: { @@ -1209,7 +1312,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logInfo, `Size Mapping V2:: Ad Unit: div-gpt-ad-1460505748561-1(1) => Active size buckets after filtration: `, adUnitDetailFixture_2.sizeBucketToSizeMap); }); - it('should increment "instance" count if presence of "Identical ad units" is detected', function() { + it('should increment "instance" count if presence of "Identical ad units" is detected', function () { const adUnit = { code: 'div-gpt-ad-1460505748561-0', mediaTypes: { @@ -1235,7 +1338,7 @@ describe('sizeMappingV2', function () { expect(adUnitDetail.instance).to.equal(2); }); - it('should not execute "getFilteredMediaTypes" function if label is not activated on the ad unit', function() { + it('should not execute "getFilteredMediaTypes" function if label is not activated on the ad unit', function () { const [adUnit] = utils.deepClone(AD_UNITS); adUnit.labelAny = ['tablet']; getAdUnitDetail('a1b2c3', adUnit, labels); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 8a142d7a6aa..960ccf08c92 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1770,8 +1770,6 @@ describe('Unit: Prebid Module', function () { }); describe('multiformat requests', function () { - let spyCallBids; - let createAuctionStub; let adUnits; beforeEach(function () { @@ -1791,14 +1789,10 @@ describe('Unit: Prebid Module', function () { }]; adUnitCodes = ['adUnit-code']; configObj.setConfig({maxRequestsPerOrigin: Number.MAX_SAFE_INTEGER || 99999999}); - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout}); - spyCallBids = sinon.spy(adapterManager, 'callBids'); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); + sinon.spy(adapterManager, 'callBids'); }) afterEach(function () { - auctionModule.newAuction.restore(); adapterManager.callBids.restore(); }); @@ -1821,7 +1815,6 @@ describe('Unit: Prebid Module', function () { const spyArgs = adapterManager.callBids.getCall(0); const biddersCalled = spyArgs.args[0][0].bids; - // only appnexus supports native expect(biddersCalled.length).to.equal(1); }); From 215bc0ed15bbd29697f2efa338b724a1fb97a07d Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Thu, 23 Jul 2020 15:32:59 +0300 Subject: [PATCH 0014/1476] AdkernelAdn: Configurable user-sync types support (#5445) --- modules/adkernelAdnBidAdapter.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 483d6de52b9..0a0317e1f59 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -48,11 +48,12 @@ function canonicalizeSizesArray(sizes) { return sizes; } -function buildRequestParams(tags, auctionId, transactionId, gdprConsent, uspConsent, refInfo) { +function buildRequestParams(tags, bidderRequest) { + let {auctionId, gdprConsent, uspConsent, transactionId, refererInfo} = bidderRequest; let req = { id: auctionId, tid: transactionId, - site: buildSite(refInfo), + site: buildSite(refererInfo), imp: tags }; if (gdprConsent) { @@ -132,14 +133,13 @@ export const spec = { return acc; }, {}); - let {auctionId, gdprConsent, uspConsent, transactionId, refererInfo} = bidderRequest; let requests = []; Object.keys(dispatch).forEach(host => { Object.keys(dispatch[host]).forEach(pubId => { - let request = buildRequestParams(dispatch[host][pubId], auctionId, transactionId, gdprConsent, uspConsent, refererInfo); + let request = buildRequestParams(dispatch[host][pubId], bidderRequest); requests.push({ method: 'POST', - url: `https://${host}/tag?account=${pubId}&pb=1${isRtbDebugEnabled(refererInfo) ? '&debug=1' : ''}`, + url: `https://${host}/tag?account=${pubId}&pb=1${isRtbDebugEnabled(bidderRequest.refererInfo) ? '&debug=1' : ''}`, data: JSON.stringify(request) }) }); @@ -159,14 +159,24 @@ export const spec = { }, getUserSyncs: function(syncOptions, serverResponses) { - if (!syncOptions.iframeEnabled || !serverResponses || serverResponses.length === 0) { + if (!serverResponses || serverResponses.length === 0) { + return []; + } + if (syncOptions.iframeEnabled) { + return buildSyncs(serverResponses, 'syncpages', 'iframe'); + } else if (syncOptions.pixelEnabled) { + return buildSyncs(serverResponses, 'syncpixels', 'image'); + } else { return []; } - return serverResponses.filter(rps => rps.body && rps.body.syncpages) - .map(rsp => rsp.body.syncpages) - .reduce((a, b) => a.concat(b), []) - .map(syncUrl => ({type: 'iframe', url: syncUrl})); } }; +function buildSyncs(serverResponses, propName, type) { + return serverResponses.filter(rps => rps.body && rps.body[propName]) + .map(rsp => rsp.body[propName]) + .reduce((a, b) => a.concat(b), []) + .map(syncUrl => ({type: type, url: syncUrl})); +} + registerBidder(spec); From 0336acd0092b0ec9abf71b9409df124fb63cbb96 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 23 Jul 2020 15:00:29 +0200 Subject: [PATCH 0015/1476] Prebid 3.27.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fef561be33..47b45d8f00e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.27.0-pre", + "version": "3.27.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 9f7195c66e77535362e5544927ed68ffc9591291 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 23 Jul 2020 09:40:44 -0400 Subject: [PATCH 0016/1476] increment pre version --- package-lock.json | 3034 ++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 1744 insertions(+), 1292 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fc53a75352..d93926c003c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,22 @@ { "name": "prebid.js", - "version": "3.24.0-pre", + "version": "4.0.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", - "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.10.1" + "@babel/highlight": "^7.10.4" } }, "@babel/compat-data": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.1.tgz", - "integrity": "sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.5.tgz", + "integrity": "sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw==", "dev": true, "requires": { "browserslist": "^4.12.0", @@ -25,24 +25,24 @@ } }, "@babel/core": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.2.tgz", - "integrity": "sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/generator": "^7.10.2", - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helpers": "^7.10.1", - "@babel/parser": "^7.10.2", - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.2", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.5.tgz", + "integrity": "sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.5", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.10.5", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.5", + "@babel/types": "^7.10.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" @@ -66,43 +66,42 @@ } }, "@babel/generator": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", - "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", + "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", "dev": true, "requires": { - "@babel/types": "^7.10.2", + "@babel/types": "^7.10.5", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz", - "integrity": "sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz", - "integrity": "sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-compilation-targets": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz", - "integrity": "sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", + "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.10.1", + "@babel/compat-data": "^7.10.4", "browserslist": "^4.12.0", "invariant": "^2.2.4", "levenary": "^1.1.1", @@ -110,337 +109,337 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz", - "integrity": "sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", + "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.1", - "@babel/helper-member-expression-to-functions": "^7.10.1", - "@babel/helper-optimise-call-expression": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1" + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz", - "integrity": "sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", + "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.1", - "@babel/helper-regex": "^7.10.1", + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", "regexpu-core": "^4.7.0" } }, "@babel/helper-define-map": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz", - "integrity": "sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.1", - "@babel/types": "^7.10.1", - "lodash": "^4.17.13" + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" } }, "@babel/helper-explode-assignable-expression": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz", - "integrity": "sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", + "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", "dev": true, "requires": { - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-function-name": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", - "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", - "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-hoist-variables": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz", - "integrity": "sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz", - "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz", + "integrity": "sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.5" } }, "@babel/helper-module-imports": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", - "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-module-transforms": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", - "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz", + "integrity": "sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1", - "@babel/helper-simple-access": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1", - "lodash": "^4.17.13" + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz", - "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-plugin-utils": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", - "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true }, "@babel/helper-regex": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.1.tgz", - "integrity": "sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", "dev": true, "requires": { - "lodash": "^4.17.13" + "lodash": "^4.17.19" } }, "@babel/helper-remap-async-to-generator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz", - "integrity": "sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", + "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.1", - "@babel/helper-wrap-function": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-replace-supers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", - "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.1", - "@babel/helper-optimise-call-expression": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-simple-access": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", - "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", "dev": true, "requires": { - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", - "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", - "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz", - "integrity": "sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helpers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", - "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", "dev": true, "requires": { - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/highlight": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", - "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz", - "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", + "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz", - "integrity": "sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", + "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-remap-async-to-generator": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz", - "integrity": "sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", + "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz", - "integrity": "sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz", - "integrity": "sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz", - "integrity": "sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", + "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz", - "integrity": "sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", + "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/plugin-syntax-numeric-separator": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz", - "integrity": "sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", + "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.1" + "@babel/plugin-transform-parameters": "^7.10.4" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz", - "integrity": "sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz", - "integrity": "sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", + "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz", - "integrity": "sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", + "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz", - "integrity": "sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-async-generators": { @@ -453,12 +452,12 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz", - "integrity": "sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-dynamic-import": { @@ -489,12 +488,12 @@ } }, "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz", - "integrity": "sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-object-rest-spread": { @@ -525,394 +524,393 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz", - "integrity": "sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz", - "integrity": "sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz", - "integrity": "sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-remap-async-to-generator": "^7.10.1" + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz", - "integrity": "sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz", - "integrity": "sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", + "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", - "lodash": "^4.17.13" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-classes": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz", - "integrity": "sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.1", - "@babel/helper-define-map": "^7.10.1", - "@babel/helper-function-name": "^7.10.1", - "@babel/helper-optimise-call-expression": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz", - "integrity": "sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-destructuring": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz", - "integrity": "sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", + "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz", - "integrity": "sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz", - "integrity": "sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz", - "integrity": "sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-for-of": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz", - "integrity": "sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-function-name": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz", - "integrity": "sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-literals": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz", - "integrity": "sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz", - "integrity": "sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz", - "integrity": "sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", + "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz", - "integrity": "sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-simple-access": "^7.10.1", + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz", - "integrity": "sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", + "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.1", - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz", - "integrity": "sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", - "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.10.4" } }, "@babel/plugin-transform-new-target": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz", - "integrity": "sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-object-super": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz", - "integrity": "sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" } }, "@babel/plugin-transform-parameters": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz", - "integrity": "sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", + "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-property-literals": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz", - "integrity": "sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-regenerator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz", - "integrity": "sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz", - "integrity": "sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz", - "integrity": "sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-spread": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz", - "integrity": "sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", + "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz", - "integrity": "sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/helper-regex": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" } }, "@babel/plugin-transform-template-literals": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz", - "integrity": "sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", + "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz", - "integrity": "sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz", - "integrity": "sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", + "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz", - "integrity": "sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1" + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/preset-env": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.2.tgz", - "integrity": "sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.1", - "@babel/helper-compilation-targets": "^7.10.2", - "@babel/helper-module-imports": "^7.10.1", - "@babel/helper-plugin-utils": "^7.10.1", - "@babel/plugin-proposal-async-generator-functions": "^7.10.1", - "@babel/plugin-proposal-class-properties": "^7.10.1", - "@babel/plugin-proposal-dynamic-import": "^7.10.1", - "@babel/plugin-proposal-json-strings": "^7.10.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1", - "@babel/plugin-proposal-numeric-separator": "^7.10.1", - "@babel/plugin-proposal-object-rest-spread": "^7.10.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.1", - "@babel/plugin-proposal-optional-chaining": "^7.10.1", - "@babel/plugin-proposal-private-methods": "^7.10.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.1", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", + "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "@babel/helper-compilation-targets": "^7.10.4", + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-proposal-async-generator-functions": "^7.10.4", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-dynamic-import": "^7.10.4", + "@babel/plugin-proposal-json-strings": "^7.10.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-numeric-separator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.10.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.10.4", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.1", + "@babel/plugin-syntax-class-properties": "^7.10.4", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-json-strings": "^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.1", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.1", - "@babel/plugin-transform-arrow-functions": "^7.10.1", - "@babel/plugin-transform-async-to-generator": "^7.10.1", - "@babel/plugin-transform-block-scoped-functions": "^7.10.1", - "@babel/plugin-transform-block-scoping": "^7.10.1", - "@babel/plugin-transform-classes": "^7.10.1", - "@babel/plugin-transform-computed-properties": "^7.10.1", - "@babel/plugin-transform-destructuring": "^7.10.1", - "@babel/plugin-transform-dotall-regex": "^7.10.1", - "@babel/plugin-transform-duplicate-keys": "^7.10.1", - "@babel/plugin-transform-exponentiation-operator": "^7.10.1", - "@babel/plugin-transform-for-of": "^7.10.1", - "@babel/plugin-transform-function-name": "^7.10.1", - "@babel/plugin-transform-literals": "^7.10.1", - "@babel/plugin-transform-member-expression-literals": "^7.10.1", - "@babel/plugin-transform-modules-amd": "^7.10.1", - "@babel/plugin-transform-modules-commonjs": "^7.10.1", - "@babel/plugin-transform-modules-systemjs": "^7.10.1", - "@babel/plugin-transform-modules-umd": "^7.10.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", - "@babel/plugin-transform-new-target": "^7.10.1", - "@babel/plugin-transform-object-super": "^7.10.1", - "@babel/plugin-transform-parameters": "^7.10.1", - "@babel/plugin-transform-property-literals": "^7.10.1", - "@babel/plugin-transform-regenerator": "^7.10.1", - "@babel/plugin-transform-reserved-words": "^7.10.1", - "@babel/plugin-transform-shorthand-properties": "^7.10.1", - "@babel/plugin-transform-spread": "^7.10.1", - "@babel/plugin-transform-sticky-regex": "^7.10.1", - "@babel/plugin-transform-template-literals": "^7.10.1", - "@babel/plugin-transform-typeof-symbol": "^7.10.1", - "@babel/plugin-transform-unicode-escapes": "^7.10.1", - "@babel/plugin-transform-unicode-regex": "^7.10.1", + "@babel/plugin-syntax-top-level-await": "^7.10.4", + "@babel/plugin-transform-arrow-functions": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.10.4", + "@babel/plugin-transform-block-scoped-functions": "^7.10.4", + "@babel/plugin-transform-block-scoping": "^7.10.4", + "@babel/plugin-transform-classes": "^7.10.4", + "@babel/plugin-transform-computed-properties": "^7.10.4", + "@babel/plugin-transform-destructuring": "^7.10.4", + "@babel/plugin-transform-dotall-regex": "^7.10.4", + "@babel/plugin-transform-duplicate-keys": "^7.10.4", + "@babel/plugin-transform-exponentiation-operator": "^7.10.4", + "@babel/plugin-transform-for-of": "^7.10.4", + "@babel/plugin-transform-function-name": "^7.10.4", + "@babel/plugin-transform-literals": "^7.10.4", + "@babel/plugin-transform-member-expression-literals": "^7.10.4", + "@babel/plugin-transform-modules-amd": "^7.10.4", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/plugin-transform-modules-systemjs": "^7.10.4", + "@babel/plugin-transform-modules-umd": "^7.10.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", + "@babel/plugin-transform-new-target": "^7.10.4", + "@babel/plugin-transform-object-super": "^7.10.4", + "@babel/plugin-transform-parameters": "^7.10.4", + "@babel/plugin-transform-property-literals": "^7.10.4", + "@babel/plugin-transform-regenerator": "^7.10.4", + "@babel/plugin-transform-reserved-words": "^7.10.4", + "@babel/plugin-transform-shorthand-properties": "^7.10.4", + "@babel/plugin-transform-spread": "^7.10.4", + "@babel/plugin-transform-sticky-regex": "^7.10.4", + "@babel/plugin-transform-template-literals": "^7.10.4", + "@babel/plugin-transform-typeof-symbol": "^7.10.4", + "@babel/plugin-transform-unicode-escapes": "^7.10.4", + "@babel/plugin-transform-unicode-regex": "^7.10.4", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.10.2", + "@babel/types": "^7.10.4", "browserslist": "^4.12.0", "core-js-compat": "^3.6.2", "invariant": "^2.2.2", @@ -934,48 +932,48 @@ } }, "@babel/runtime": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", - "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", + "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" }, "dependencies": { "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", "dev": true } } }, "@babel/template": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", - "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/parser": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/traverse": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", - "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", + "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/generator": "^7.10.1", - "@babel/helper-function-name": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/parser": "^7.10.1", - "@babel/types": "^7.10.1", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/parser": "^7.10.5", + "@babel/types": "^7.10.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" }, "dependencies": { "debug": { @@ -996,13 +994,13 @@ } }, "@babel/types": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", - "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, @@ -1287,6 +1285,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -1494,6 +1498,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -1834,15 +1844,15 @@ } }, "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.1.0.tgz", + "integrity": "sha512-GXigDDsp6ZlNMhXQDeuy/iYCDsRIHJabWtDzvnn36+aqFfG14JmFV0e/iXxY4SP9vbXSiPNOWdehU5MeqrYHBQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "chalk": "^4.0.0" }, "dependencies": { "ansi-styles": { @@ -1856,9 +1866,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -1898,16 +1908,16 @@ } }, "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz", - "integrity": "sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA==", + "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.1", - "loader-utils": "^1.4.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", "merge-source-map": "^1.1.0", - "schema-utils": "^2.6.4" + "schema-utils": "^2.7.0" } }, "@kiosked/ulid": { @@ -1916,15 +1926,15 @@ "integrity": "sha512-ZKt2KIgGHDaGfKt6FjYvCpDvBXZRRoE8b+wDOlAV76aXKpq6ITiSUnPYevR4y55NKDnwCvwOrjWe+aVOCAK8kQ==" }, "@sindresorhus/is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", - "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.0.0.tgz", + "integrity": "sha512-kqA5I6Yun7PBHk8WN9BBP1c7FfN2SrD05GuVSEYPqDb4nerv7HqYfgBfMIKmT/EuejURkJKLZuLyGKGs6WEG9w==", "dev": true }, "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -1966,9 +1976,9 @@ } }, "@types/babel__core": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.8.tgz", - "integrity": "sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ==", + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", + "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -1998,9 +2008,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.12.tgz", - "integrity": "sha512-t4CoEokHTfcyfb4hUaF9oOHu9RmmNWnm1CP0YmMqOOfClKascOmvlEM736vlqeScuGvBDsHkf8R2INd4DWreQA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz", + "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -2031,9 +2041,9 @@ "dev": true }, "@types/istanbul-lib-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, "@types/istanbul-lib-report": { @@ -2056,9 +2066,15 @@ } }, "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "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=", "dev": true }, "@types/keyv": { @@ -2071,11 +2087,20 @@ } }, "@types/node": { - "version": "14.0.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.9.tgz", - "integrity": "sha512-0sCTiXKXELOBxvZLN4krQ0FPOAA7ij+6WwvD0k/PHd9/KAkr4dXel5J9fh6F4x1FwAQILqAWkmpeuS6mjf1iKA==", + "version": "14.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.24.tgz", + "integrity": "sha512-btt/oNOiDWcSuI721MdL8VQGnjsKjlTMdrKyTcLCKeQp/n4AAMFJ961wMbp+09y8WuGPClDEv07RIItdXKIXAA==", "dev": true }, + "@types/puppeteer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.1.tgz", + "integrity": "sha512-t03eNKCvWJXhQ8wkc5C6GYuSqMEdKLOX0GLMGtks25YZr38wKZlKTwGM/BoAPVtdysX7Bb9tdwrDS1+NrW3RRA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -2128,14 +2153,14 @@ } }, "@wdio/cli": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-6.1.16.tgz", - "integrity": "sha512-q7JaEiLU2mdOibeKAQFqdWTS2evdkwgWSft1rmWDN7idiV39uncTTUcwlXBKE2a9yDk/8qn6EEXdBLthOCfyOA==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-6.3.4.tgz", + "integrity": "sha512-eXA4rR6DwhNtXx1Hxknwgl7jGt/q4ZErCB8aOX9rowEoPOxwPQStd6yJcqI2QE8+AC1S72PKC4w+0WImL+M6Bw==", "dev": true, "requires": { "@wdio/config": "6.1.14", "@wdio/logger": "6.0.16", - "@wdio/utils": "6.1.8", + "@wdio/utils": "6.3.0", "async-exit-hook": "^2.0.1", "chalk": "^4.0.0", "chokidar": "^3.0.0", @@ -2146,8 +2171,9 @@ "lodash.flattendeep": "^4.4.0", "lodash.pickby": "^4.6.0", "lodash.union": "^4.6.0", - "log-update": "^4.0.0", - "webdriverio": "6.1.16", + "mkdirp": "^1.0.4", + "recursive-readdir": "^2.2.2", + "webdriverio": "6.3.4", "yargs": "^15.0.1", "yarn-install": "^1.0.0" }, @@ -2163,9 +2189,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2187,61 +2213,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "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" - } - }, "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 }, - "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 - }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -2252,9 +2229,9 @@ } }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -2267,18 +2244,18 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "yargs-parser": "^18.1.2" } } } }, "@wdio/concise-reporter": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-6.1.14.tgz", - "integrity": "sha512-QKGiIPE2sYJpQcQ5zogUogu+Yldkx/FUM4LC01lc3v0Nww7n0p0fJA3U4cKFAqyW0gXCwW7m6/9c5fDgaSv8+g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-6.3.0.tgz", + "integrity": "sha512-H7yILps+dKK1k4XoVE5HOVMpTHN321SFmjjMgtq1zfiC6Dph7Unl4ODmnyVLD5Kk3ycQ31PfOBr0QPyKnLUFiQ==", "dev": true, "requires": { - "@wdio/reporter": "6.1.14", + "@wdio/reporter": "6.3.0", "chalk": "^4.0.0", "pretty-ms": "^7.0.0" }, @@ -2294,9 +2271,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2347,14 +2324,14 @@ } }, "@wdio/local-runner": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-6.1.16.tgz", - "integrity": "sha512-3+pT2fcMXFAnELA6jYjVm6Nt8Il7tGL66A90UbRiT0sL2faTcD3uGAPbmzxMclsmWLh7C04ucCnFwQoTMW1emg==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-6.3.4.tgz", + "integrity": "sha512-rKEhFXiNH6H2G86JTgy2cgtEFoNBZ50gRy+P1LEhc7Ko/dAYqYMC+Sy8lnbsDzxz6IZVlbubgs+y7GRREayqoQ==", "dev": true, "requires": { "@wdio/logger": "6.0.16", - "@wdio/repl": "6.1.8", - "@wdio/runner": "6.1.16", + "@wdio/repl": "6.3.0", + "@wdio/runner": "6.3.4", "async-exit-hook": "^2.0.1", "stream-buffers": "^3.0.2" } @@ -2382,9 +2359,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2424,15 +2401,15 @@ } }, "@wdio/mocha-framework": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-6.1.14.tgz", - "integrity": "sha512-2AmUH/v3kZoIDAMdW73AhI4tDJU3ie/2dO/DtpXJ3XFnuJ1CtklZGyCTtYHGMZ8DBj18/8Lrs/O0CjS5bAu2tw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-6.3.0.tgz", + "integrity": "sha512-3lLvzhDYWwOYmiJAjr2fm/nENq6g6uUOtkIeEQFp1kDyBQkDsH1PXGdFklQbRiQT8mAqOPhx1kvXrCA/XpWl7g==", "dev": true, "requires": { "@wdio/logger": "6.0.16", - "@wdio/utils": "6.1.8", + "@wdio/utils": "6.3.0", "expect-webdriverio": "^1.1.5", - "mocha": "^7.0.1" + "mocha": "^8.0.1" }, "dependencies": { "ansi-regex": { @@ -2442,19 +2419,19 @@ "dev": true }, "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.1", + "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.3.0" } }, "cliui": { @@ -2483,28 +2460,11 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "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 }, "is-fullwidth-code-point": { "version": "2.0.0", @@ -2523,52 +2483,44 @@ } }, "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", + "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", + "chokidar": "3.3.1", "debug": "3.2.6", - "diff": "3.5.0", + "diff": "4.0.2", "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", + "find-up": "4.1.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "3.13.1", "log-symbols": "3.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", + "ms": "2.1.2", "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", + "promise.allsettled": "1.0.2", + "serialize-javascript": "3.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", "wide-align": "1.1.3", + "workerpool": "6.0.0", "yargs": "13.3.2", "yargs-parser": "13.1.2", "yargs-unparser": "1.6.0" } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -2578,19 +2530,19 @@ "p-limit": "^2.0.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==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", "dev": true, "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.0.7" } }, "string-width": { @@ -2614,12 +2566,21 @@ } }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } }, "wrap-ansi": { @@ -2649,6 +2610,17 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + } } }, "yargs-parser": { @@ -2664,51 +2636,51 @@ } }, "@wdio/protocols": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-6.1.14.tgz", - "integrity": "sha512-UtRLQ55i23cLQRGtFiEJty1F6AbAfiSpfIxDAiXKHbw6Rp1StwxlqHFrhNe5F48Zu4hnie46t9N/tr/cZOe0kA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-6.3.0.tgz", + "integrity": "sha512-1GKzfyCTLW5WkFd3W7NLACih+zNWU7c8kFurbCQXDK1ko1obqJEs7ZjBr85q5XqMWburdks5rDjyml2iEB2LBg==", "dev": true }, "@wdio/repl": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-6.1.8.tgz", - "integrity": "sha512-C647KvDIcOHYN24eFbiM2xE+etPEACvRYkEp7BPLyopEABDr0I3Qdb5MLhopC5eMAVHp70/WT27H1CE2v9iILQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-6.3.0.tgz", + "integrity": "sha512-FT3flKOqNdZNG1uYl+QpOfdZIgKAWhLfoQ0s+wL0crLeDNIFvvM2qSDhRBRDYV7a0IFyBi8Z975WBn0dlH03Ig==", "dev": true, "requires": { - "@wdio/utils": "6.1.8" + "@wdio/utils": "6.3.0" } }, "@wdio/reporter": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-6.1.14.tgz", - "integrity": "sha512-Pt6P0JU0COHTpggsOoJKUJyAyQsi7xlHebBNU/DWdHHpmzYd4e9vDutjyTqXu/1zn+t+Zq+uL1IC0E4Xjv6f7w==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-6.3.0.tgz", + "integrity": "sha512-vbwjJvSKZUtsWtQMhuVqT7ZP6RIFAH4+ienjNwW30QPDi38OujZgxC2ZqRoZKsxck6cfTgkxrXfNaxHN0/LHKg==", "dev": true, "requires": { "fs-extra": "^9.0.0" } }, "@wdio/runner": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-6.1.16.tgz", - "integrity": "sha512-pGRT51BGnxp4zFD1pSp6qZD/4dnbSnDyV4g/MbbFiA4PFKF41mFhaxRwIGMHcp4EYlv9gaT31UA52JaFIYyuNA==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-6.3.4.tgz", + "integrity": "sha512-+iOXfTODsSVf9LFBFKAEZqvPzfIClwFCKu7GGFZ7lrOF1svMNzT/0UY0ETsCBZe61Gr8xiI0wbCEly+0DbEh6w==", "dev": true, "requires": { "@wdio/config": "6.1.14", "@wdio/logger": "6.0.16", - "@wdio/utils": "6.1.8", + "@wdio/utils": "6.3.0", "deepmerge": "^4.0.0", "gaze": "^1.1.2", - "webdriver": "6.1.14", - "webdriverio": "6.1.16" + "webdriver": "6.3.0", + "webdriverio": "6.3.4" } }, "@wdio/spec-reporter": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-6.1.14.tgz", - "integrity": "sha512-QaSBgnzllzLp9LR7/5DTkmrI8BqcznUma8ZxwUNmhvskv/oKzrmNyeCsGoiExFmCk81A9FgZiZPXey7CuaTkdw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-6.3.0.tgz", + "integrity": "sha512-JGZAMcqiOloOw6xcIT5O8GORVaww6kslgH5kZGydVQyoNBj1ZKoLdEjqq2jklJsge1xsscdYdW9u9kMHwm25iA==", "dev": true, "requires": { - "@wdio/reporter": "6.1.14", + "@wdio/reporter": "6.3.0", "chalk": "^4.0.0", "easy-table": "^1.1.1", "pretty-ms": "^7.0.0" @@ -2725,9 +2697,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2767,19 +2739,20 @@ } }, "@wdio/sync": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-6.1.14.tgz", - "integrity": "sha512-94K0kQdrOU0aMlJ2Xsd4tWr4tPpmCFp612Ml5+ecQh4C4XD07ocfsvGs+mwI7cfF1sO6g/Hoc+XTY2D+/8En3w==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-6.3.3.tgz", + "integrity": "sha512-WNq+hhkgk9LluKLP2nQ/9+EH8HNQnROFFHvYuznxb1aKj/zhZvqWuQPpmMWhPMBSTpkdbdLCYerZWKcamYOcJQ==", "dev": true, "requires": { + "@types/puppeteer": "^3.0.1", "@wdio/logger": "6.0.16", "fibers": "^4.0.1" } }, "@wdio/utils": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-6.1.8.tgz", - "integrity": "sha512-qzvD8qCPpIpDrZ0HNOx1hTlfKY26p8WByUXgr52ll6DXxtAYXZLIJ8GAYFJUi87NVfwtv6+O7owQGSM/jtr8AQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-6.3.0.tgz", + "integrity": "sha512-PbeC5fpieamgSAHf7S58MAyraGU1qKxnHdfGMG+ZIWiIo73oo4j/57CcH6ZawQ3YC1wEc/5q+VXg7N5hvqhJOQ==", "dev": true, "requires": { "@wdio/logger": "6.0.16" @@ -2918,9 +2891,9 @@ } }, "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", + "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", "dev": true }, "align-text": { @@ -2958,9 +2931,9 @@ "dev": true }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "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-escapes": { @@ -3037,13 +3010,13 @@ } }, "archiver": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-4.0.1.tgz", - "integrity": "sha512-/YV1pU4Nhpf/rJArM23W6GTUjT0l++VbjykrCRua1TSXrn+yM8Qs7XvtwSiRse0iCe49EPNf7ktXnPsWuSb91Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-4.0.2.tgz", + "integrity": "sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "async": "^2.6.3", + "async": "^3.2.0", "buffer-crc32": "^0.2.1", "glob": "^7.1.6", "readable-stream": "^3.6.0", @@ -3052,13 +3025,10 @@ }, "dependencies": { "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true } } }, @@ -3276,6 +3246,18 @@ "es-abstract": "^1.17.0-next.1" } }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -3356,9 +3338,9 @@ "dev": true }, "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, "async": { @@ -3787,6 +3769,35 @@ "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" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -3874,15 +3885,6 @@ "path-exists": "^3.0.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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -3892,10 +3894,10 @@ "p-limit": "^2.0.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==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "semver": { @@ -4636,6 +4638,15 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } } } }, @@ -4907,9 +4918,9 @@ } }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, "binaryextensions": { @@ -5173,15 +5184,15 @@ } }, "browserslist": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", - "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", + "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001043", - "electron-to-chromium": "^1.3.413", - "node-releases": "^1.1.53", - "pkg-up": "^2.0.0" + "caniuse-lite": "^1.0.30001093", + "electron-to-chromium": "^1.3.488", + "escalade": "^3.0.1", + "node-releases": "^1.1.58" } }, "browserstack": { @@ -5530,9 +5541,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001077", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", - "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==", + "version": "1.0.30001105", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001105.tgz", + "integrity": "sha512-JupOe6+dGMr7E20siZHIZQwYqrllxotAhiaej96y6x00b/48rPt42o+SzOSCPbrpsDWvRja40Hwrj0g0q6LZJg==", "dev": true }, "capture-exit": { @@ -5643,9 +5654,9 @@ "dev": true }, "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", + "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -5665,9 +5676,9 @@ "dev": true }, "chrome-launcher": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.2.tgz", - "integrity": "sha512-zWD9RVVKd8Nx2xKGY4G08lb3nCD+2hmICxovvRE9QjBKQzHFvCYqGlsw15b4zUxLKq3wXEwVbR/yLtMbfk7JbQ==", + "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": "*", @@ -5678,6 +5689,15 @@ "rimraf": "^3.0.2" }, "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -5744,15 +5764,15 @@ } }, "cli-spinners": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", - "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz", + "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==", "dev": true }, "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, "cliui": { @@ -5903,9 +5923,9 @@ "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==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, "commondir": { @@ -6643,21 +6663,27 @@ } }, "devtools": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-6.1.16.tgz", - "integrity": "sha512-Px/K/xYY+fTW8D5yt7p6ZZJfkfHHulKVr2Y+BJSCQyKNSY/hiZFT6KAjoUFrAastLCqqs1gW2Dy/OGb0qWm+Hg==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-6.3.4.tgz", + "integrity": "sha512-dOcLdArp5/dJBzD8T5wcT2YgqkA22Mkqo0OS9cXz7JkHNgwOx1FI2Bq9GvP6o0TENHifYSYg3G0K/z0bacekqg==", "dev": true, "requires": { "@wdio/config": "6.1.14", "@wdio/logger": "6.0.16", - "@wdio/protocols": "6.1.14", - "@wdio/utils": "6.1.8", + "@wdio/protocols": "6.3.0", + "@wdio/utils": "6.3.0", "chrome-launcher": "^0.13.1", - "puppeteer-core": "^3.0.0", + "puppeteer-core": "^5.1.0", "ua-parser-js": "^0.7.21", "uuid": "^8.0.0" } }, + "devtools-protocol": { + "version": "0.0.781568", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.781568.tgz", + "integrity": "sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==", + "dev": true + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -6665,15 +6691,15 @@ "dev": true }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", + "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", "dev": true }, "diffie-hellman": { @@ -6936,6 +6962,15 @@ } } }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, "fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", @@ -6953,6 +6988,12 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + "dev": true + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -7030,6 +7071,16 @@ "strip-bom": "^3.0.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -7051,6 +7102,30 @@ "to-regex": "^3.0.2" } }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "dev": true + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -7061,6 +7136,12 @@ "json-parse-better-errors": "^1.0.1" } }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -7177,6 +7258,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -7465,15 +7552,15 @@ } }, "electron-to-chromium": { - "version": "1.3.458", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.458.tgz", - "integrity": "sha512-OjRkb0igW0oKE2QbzS7vBYrm7xjW/KRTtIj0OGGx57jr/YhBiKb7oZvdbaojqjfCb/7LbnwsbMbdsYjthdJbAw==", + "version": "1.3.505", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.505.tgz", + "integrity": "sha512-Aunrp3HWtmdiJLIl+IPSFtEvJ/4Q9a3eKaxmzCthaZF1gbTbpHUTCU2zOVnFPH7r/AD7zQXyuFidYXzSHXBdsw==", "dev": true }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -7667,34 +7754,63 @@ } }, "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true }, - "es5-ext": { + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", @@ -7798,6 +7914,12 @@ "es6-symbol": "^3.1.1" } }, + "escalade": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -7810,9 +7932,9 @@ "dev": true }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, "requires": { "esprima": "^4.0.1", @@ -7916,6 +8038,12 @@ "restore-cursor": "^2.0.0" } }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -7999,6 +8127,15 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8048,6 +8185,12 @@ "requires": { "ansi-regex": "^3.0.0" } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true } } }, @@ -8058,9 +8201,9 @@ "dev": true }, "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { "debug": "^2.6.9", @@ -8077,6 +8220,55 @@ "pkg-dir": "^2.0.0" }, "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "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=", + "dev": true + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -8089,25 +8281,35 @@ } }, "eslint-plugin-import": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz", - "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.0", + "object.values": "^1.1.1", "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -8120,6 +8322,46 @@ "strip-bom": "^3.0.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "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=", + "dev": true + }, "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", @@ -8205,9 +8447,9 @@ } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, "espree": { @@ -8301,9 +8543,9 @@ "dev": true }, "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", "dev": true }, "evp_bytestokey": { @@ -8469,17 +8711,17 @@ } }, "expect": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.5.0.tgz", - "integrity": "sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.1.0.tgz", + "integrity": "sha512-QbH4LZXDsno9AACrN9eM0zfnby9G+OsdNgZUohjg/P0mLy1O+/bzTAJGT6VSIjVCe8yKM6SzEl/ckEOFBT7Vnw==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/types": "^26.1.0", "ansi-styles": "^4.0.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-regex-util": "^25.2.6" + "jest-get-type": "^26.0.0", + "jest-matcher-utils": "^26.1.0", + "jest-message-util": "^26.1.0", + "jest-regex-util": "^26.0.0" }, "dependencies": { "ansi-styles": { @@ -8510,13 +8752,13 @@ } }, "expect-webdriverio": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-1.1.5.tgz", - "integrity": "sha512-+RL4Lkne+/z+o1G5Y0S5QuEXeICxt4jExhBSM2Jn/mrDwb+PZVKPM2Yd1OzLsKeCdQLtw4Oft6514Gp5GLgdZA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-1.2.0.tgz", + "integrity": "sha512-nis1EL4LJSKvhqES6ojx1QqAZYtWAUHaVtwilXBXJELN2YZhwOcrfBT0AvxDvJXYKzgDDTED9STc9MwcK8KbYg==", "dev": true, "requires": { - "expect": "^25.2.1", - "jest-matcher-utils": "^25.1.0" + "expect": "^26.0.1", + "jest-matcher-utils": "^26.0.1" } }, "express": { @@ -8677,9 +8919,9 @@ } }, "extract-zip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", - "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { "@types/yauzl": "^2.9.1", @@ -8730,9 +8972,9 @@ } }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { @@ -8872,15 +9114,61 @@ "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } } }, "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": { @@ -9091,30 +9379,10 @@ } }, "follow-redirects": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", - "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", - "dev": true, - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } - } + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "dev": true }, "for-in": { "version": "1.0.2", @@ -9228,9 +9496,9 @@ "dev": true }, "fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { "at-least-node": "^1.0.0", @@ -9315,6 +9583,17 @@ "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" + }, + "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "fun-hooks": { @@ -9364,9 +9643,9 @@ "dev": true }, "get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", "dev": true }, "get-stdin": { @@ -9550,9 +9829,9 @@ } }, "glob-watcher": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -9560,6 +9839,7 @@ "chokidar": "^2.0.0", "is-negated-glob": "^1.0.0", "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", "object.defaults": "^1.1.0" }, "dependencies": { @@ -9571,6 +9851,17 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "binary-extensions": { @@ -9626,14 +9917,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } } }, "fill-range": { @@ -9747,15 +10030,6 @@ "to-regex": "^3.0.2" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -9831,13 +10105,13 @@ "dev": true }, "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.12", + "lodash": "~4.17.10", "minimatch": "~3.0.2" } }, @@ -9851,20 +10125,19 @@ } }, "got": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/got/-/got-11.2.0.tgz", - "integrity": "sha512-68pBow9XXXSdVRV5wSx0kWMCZsag4xE3Ru0URVe0PWsSYmU4SJrUmEO6EVYFlFHc9rq/6Yqn6o1GxIb9torQxg==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/got/-/got-11.5.1.tgz", + "integrity": "sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==", "dev": true, "requires": { - "@sindresorhus/is": "^2.1.1", + "@sindresorhus/is": "^3.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.1", "decompress-response": "^6.0.0", - "get-stream": "^5.1.0", - "http2-wrapper": "^1.0.0-beta.4.5", + "http2-wrapper": "^1.0.0-beta.5.0", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" @@ -9945,9 +10218,9 @@ "dev": true }, "gulp-cli": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.1.tgz", - "integrity": "sha512-yEMxrXqY8mJFlaauFQxNrCpzWJThu0sH1sqlToaTOT063Hub9s/Nt2C+GSLe6feQ/IMWrHvGOOsyES7CQc9O+A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -9958,7 +10231,7 @@ "copy-props": "^2.0.1", "fancy-log": "^1.3.2", "gulplog": "^1.0.0", - "interpret": "^1.1.0", + "interpret": "^1.4.0", "isobject": "^3.0.1", "liftoff": "^3.1.0", "matchdep": "^2.0.0", @@ -9966,7 +10239,7 @@ "pretty-hrtime": "^1.0.0", "replace-homedir": "^1.0.0", "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.0.1", + "v8flags": "^3.2.0", "yargs": "^7.1.0" } }, @@ -10832,9 +11105,9 @@ }, "dependencies": { "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -11191,12 +11464,12 @@ } }, "http2-wrapper": { - "version": "1.0.0-beta.4.6", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.4.6.tgz", - "integrity": "sha512-9oB4BiGDTI1FmIBlOF9OJ5hwJvcBEmPCqk/hy314Uhy2uq5TjekUZM8w8SPLLlUEM+mxNhXdPAXfrJN2Zbb/GQ==", + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", "dev": true, "requires": { - "quick-lru": "^5.0.0", + "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, @@ -11261,6 +11534,51 @@ "requires": { "pkg-dir": "^3.0.0", "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } } }, "imurmurhash": { @@ -11303,21 +11621,21 @@ "dev": true }, "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", + "chalk": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", + "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", - "rxjs": "^6.5.3", + "rxjs": "^6.6.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" @@ -11334,9 +11652,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -11631,6 +11949,12 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -11683,11 +12007,11 @@ "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "is-relative": { @@ -11717,6 +12041,12 @@ "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", "dev": true }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, "is-ssh": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz", @@ -11913,6 +12243,15 @@ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -12038,6 +12377,15 @@ "handlebars": "^4.0.3" } }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -12208,10 +12556,26 @@ "is-object": "^1.0.1" } }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, "jake": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.1.tgz", - "integrity": "sha512-eSp5h9S7UFzKdQERTyF+KuPLjDZa1Tbw8gCVUn98n4PbIkLEDGe4zl7vF4Qge9kQj06HcymnksPk8jznPZeKsA==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", "dev": true, "requires": { "async": "0.9.x", @@ -12319,15 +12683,6 @@ "path-exists": "^3.0.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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -12337,10 +12692,10 @@ "p-limit": "^2.0.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==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "string-width": { @@ -12624,15 +12979,15 @@ } }, "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.1.0.tgz", + "integrity": "sha512-GZpIcom339y0OXznsEKjtkfKxNdg7bVbEofK8Q6MnevTIiR1jNhDWKhRX6X0SDXJlwn3dy59nZ1z55fLkAqPWg==", "dev": true, "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" + "chalk": "^4.0.0", + "diff-sequences": "^26.0.0", + "jest-get-type": "^26.0.0", + "pretty-format": "^26.1.0" }, "dependencies": { "ansi-styles": { @@ -12646,9 +13001,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -12827,9 +13182,9 @@ } }, "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", + "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", "dev": true }, "jest-haste-map": { @@ -13252,6 +13607,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -13321,15 +13682,15 @@ } }, "jest-matcher-utils": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz", - "integrity": "sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.1.0.tgz", + "integrity": "sha512-PW9JtItbYvES/xLn5mYxjMd+Rk+/kIt88EfH3N7w9KeOrHWaHrdYPnVHndGbsFGRJ2d5gKtwggCvkqbFDoouQA==", "dev": true, "requires": { - "chalk": "^3.0.0", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" + "chalk": "^4.0.0", + "jest-diff": "^26.1.0", + "jest-get-type": "^26.0.0", + "pretty-format": "^26.1.0" }, "dependencies": { "ansi-styles": { @@ -13343,9 +13704,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -13385,19 +13746,19 @@ } }, "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.1.0.tgz", + "integrity": "sha512-dY0+UlldiAJwNDJ08SF0HdF32g9PkbF2NRK/+2iMPU40O6q+iSn1lgog/u0UH8ksWoPv0+gNq8cjhYO2MFtT0g==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", + "@jest/types": "^26.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", + "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "micromatch": "^4.0.2", "slash": "^3.0.0", - "stack-utils": "^1.0.1" + "stack-utils": "^2.0.2" }, "dependencies": { "ansi-styles": { @@ -13411,9 +13772,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -13484,15 +13845,15 @@ } }, "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", + "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-resolve": { @@ -13753,6 +14114,12 @@ "source-map": "^0.6.0" } }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -13985,15 +14352,6 @@ "to-regex": "^3.0.2" } }, - "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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -14003,10 +14361,10 @@ "p-limit": "^2.0.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==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "slash": { @@ -14015,6 +14373,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -14316,6 +14680,15 @@ "to-regex": "^3.0.2" } }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "pretty-format": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", @@ -14340,6 +14713,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -14392,6 +14771,15 @@ "@types/yargs-parser": "*" } }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -15051,6 +15439,26 @@ "requires": { "lodash": "^4.17.14" } + }, + "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" + } } } }, @@ -15247,41 +15655,29 @@ "dev": true }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "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" - } - } + "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": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash._basecopy": { @@ -15592,18 +15988,6 @@ "chalk": "^2.4.2" } }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - } - }, "log4js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", @@ -16315,13 +16699,10 @@ } }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "mkdirp-classic": { "version": "0.5.3", @@ -16348,12 +16729,6 @@ "supports-color": "5.4.0" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -16363,6 +16738,12 @@ "ms": "2.0.0" } }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -16630,9 +17011,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "next-tick": { @@ -16696,16 +17077,6 @@ } } }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -16805,9 +17176,9 @@ } }, "node-releases": { - "version": "1.1.58", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", - "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==", + "version": "1.1.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", + "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", "dev": true }, "nopt": { @@ -17021,9 +17392,9 @@ } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-is": { "version": "1.1.2", @@ -17353,21 +17724,21 @@ "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-reduce": { @@ -17386,9 +17757,9 @@ } }, "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 }, "pako": { @@ -17528,6 +17899,15 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "normalize-url": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", @@ -17762,9 +18142,9 @@ "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": { @@ -17844,9 +18224,9 @@ } }, "pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-wHMFZ6HTLGlB9f/WsQBs5OwMQJoLXYuJUzbA+j+hRBf7+Y8KcXpatzIviIcTy1OAyhWQp08nyiPO8Dnv0z4Sww==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -17886,97 +18266,43 @@ "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "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=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.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": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.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 - } + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "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=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" } }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", "dev": true, "requires": { - "find-up": "^2.1.0" + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" } }, "plugin-error": { @@ -18039,12 +18365,12 @@ "dev": true }, "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.1.0.tgz", + "integrity": "sha512-GmeO1PEYdM+non4BKCj+XsPJjFOJIPnsLewqhDVoqY1xo0yNmDas7tC2XwpMrRAHR3MaE2hPo37deX5OisJ2Wg==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/types": "^26.1.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -18116,6 +18442,19 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, "prompts": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", @@ -18242,15 +18581,17 @@ "dev": true }, "puppeteer-core": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-3.3.0.tgz", - "integrity": "sha512-hynQ3r0J/lkGrKeBCqu160jrj0WhthYLIzDQPkBxLzxPokjw4elk1sn6mXAian/kfD2NRzpdh9FSykxZyL56uA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-5.2.1.tgz", + "integrity": "sha512-gLjEOrzwgcnwRH+sm4hS1TBqe2/DN248nRb2hYB7+lZ9kCuLuACNvuzlXILlPAznU3Ob+mEvVEBDcLuFa0zq3g==", "dev": true, "requires": { "debug": "^4.1.0", + "devtools-protocol": "0.0.781568", "extract-zip": "^2.0.0", "https-proxy-agent": "^4.0.0", "mime": "^2.0.3", + "pkg-dir": "^4.2.0", "progress": "^2.0.1", "proxy-from-env": "^1.0.0", "rimraf": "^3.0.2", @@ -18482,6 +18823,15 @@ "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==", + "dev": true, + "requires": { + "minimatch": "3.0.4" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -18504,9 +18854,9 @@ } }, "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", "dev": true }, "regenerate-unicode-properties": { @@ -18524,13 +18874,12 @@ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", - "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, "requires": { - "@babel/runtime": "^7.8.4", - "private": "^0.1.8" + "@babel/runtime": "^7.8.4" } }, "regex-cache": { @@ -18855,21 +19204,21 @@ } }, "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", "dev": true, "requires": { - "lodash": "^4.17.15" + "lodash": "^4.17.19" } }, "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", "dev": true, "requires": { - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" } @@ -19083,9 +19432,9 @@ } }, "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -19287,9 +19636,9 @@ }, "dependencies": { "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -19359,6 +19708,12 @@ } } }, + "serialize-javascript": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", + "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", + "dev": true + }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -19512,6 +19867,14 @@ "nise": "^1.2.0", "supports-color": "^5.1.0", "type-detect": "^4.0.5" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + } } }, "sisteransi": { @@ -19527,39 +19890,18 @@ "dev": true }, "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "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==", + "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true } } @@ -19922,10 +20264,21 @@ "dev": true }, "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } }, "state-toggle": { "version": "1.0.3", @@ -20201,12 +20554,6 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -20254,26 +20601,6 @@ "es-abstract": "^1.17.5" } }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, "string.prototype.trimstart": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", @@ -20344,9 +20671,9 @@ } }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true }, "subarg": { @@ -20421,15 +20748,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -20470,9 +20788,9 @@ } }, "tar-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", + "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", "dev": true, "requires": { "bl": "^4.0.1", @@ -20572,15 +20890,6 @@ "path-exists": "^3.0.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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -20590,12 +20899,6 @@ "p-limit": "^2.0.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 - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -20606,6 +20909,12 @@ "json-parse-better-errors": "^1.0.1" } }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -20949,6 +21258,35 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "dev": true }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "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" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -21046,13 +21384,10 @@ "dev": true }, "uglify-js": { - "version": "3.9.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.4.tgz", - "integrity": "sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==", - "dev": true, - "requires": { - "commander": "~2.20.3" - } + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", + "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", + "dev": true }, "uglify-to-browserify": { "version": "1.0.2", @@ -21516,9 +21851,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", - "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==", "dev": true }, "v8flags": { @@ -22088,41 +22423,46 @@ } }, "webdriver": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-6.1.14.tgz", - "integrity": "sha512-6fXoGDnxWfJn9zqfYbc6+VEV5N1obd3K7iuRmJ7w/hH9d9XDxcrcx147T5egiy1qziko61hqYE/x9VtSQbjPcA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-6.3.0.tgz", + "integrity": "sha512-osHp5DX8eQ76Sy6/UYoECmDnUXwLhy5tlBeJh86er7S04FLAlmEqCvYXgxTSb8YtDjh9Xt9gP768RGhxR7ik5A==", "dev": true, "requires": { "@wdio/config": "6.1.14", "@wdio/logger": "6.0.16", - "@wdio/protocols": "6.1.14", - "@wdio/utils": "6.1.8", + "@wdio/protocols": "6.3.0", + "@wdio/utils": "6.3.0", "got": "^11.0.2", "lodash.merge": "^4.6.1" } }, "webdriverio": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-6.1.16.tgz", - "integrity": "sha512-vk6/XeErNnMooebCtxwIR//LqrastXO9gXL+eVUGNRAuG5omp8XCdT+MK2fmlw53xhcPULQ/y3h8ysYlPnPeyA==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-6.3.4.tgz", + "integrity": "sha512-/53xQEituEFTaJtZMgg5Uz3GXY1Otqyry0LA8dYLYUNkTK0yCa26DL4ycDnWE0i9wEYNFX6YHCgiqTJjHEjKAg==", "dev": true, "requires": { + "@types/puppeteer": "^3.0.1", "@wdio/config": "6.1.14", "@wdio/logger": "6.0.16", - "@wdio/repl": "6.1.8", - "@wdio/utils": "6.1.8", + "@wdio/repl": "6.3.0", + "@wdio/utils": "6.3.0", "archiver": "^4.0.1", + "atob": "^2.1.2", "css-value": "^0.0.1", - "devtools": "6.1.16", + "devtools": "6.3.4", + "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": "^5.1.0", "resq": "^1.6.0", "rgb2hex": "^0.2.0", "serialize-error": "^7.0.0", - "webdriver": "6.1.14" + "webdriver": "6.3.0" } }, "webidl-conversions": { @@ -22162,9 +22502,9 @@ }, "dependencies": { "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -22218,6 +22558,15 @@ } } }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -22257,6 +22606,77 @@ "strip-bom": "^3.0.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" + }, + "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" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "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=", + "dev": true + }, "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", @@ -22433,15 +22853,21 @@ }, "dependencies": { "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true }, "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "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 }, "ejs": { @@ -22450,6 +22876,15 @@ "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -22974,6 +23409,15 @@ "regex-cache": "^0.4.2" } }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "node-libs-browser": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", @@ -23542,6 +23986,12 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -23593,6 +24043,17 @@ "dev": true, "requires": { "mkdirp": "^0.5.1" + }, + "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "write-file-atomic": { @@ -23607,9 +24068,9 @@ } }, "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", "dev": true }, "x-is-string": { @@ -23723,15 +24184,6 @@ "path-exists": "^3.0.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": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -23741,10 +24193,10 @@ "p-limit": "^2.0.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==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "string-width": { diff --git a/package.json b/package.json index 47b45d8f00e..0cba9aea969 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.27.0", + "version": "4.0.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0403ba7f88ce78f23a1b6e3699730fbca50fbd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Thu, 23 Jul 2020 16:45:40 +0300 Subject: [PATCH 0017/1476] Vidazoo Adapter: Feature/alternate-param-names (#5527) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(client): alternate param names Co-authored-by: roman --- modules/vidazooBidAdapter.js | 20 +++++++++++++++-- test/spec/modules/vidazooBidAdapter_spec.js | 24 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 3eea270dc8d..b9f3297818f 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -32,16 +32,31 @@ export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; } +export function extractCID(params) { + return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; +} + +export function extractPID(params) { + return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; +} + +export function extractSubDomain(params) { + return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; +} + function isBidRequestValid(bid) { const params = bid.params || {}; - return !!(params.cId && params.pId); + return !!(extractCID(params) && extractPID(params)); } function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const { params, bidId, userId, adUnitCode } = bid; - const { bidFloor, cId, pId, ext, subDomain } = params; + const { bidFloor, ext } = params; const hashUrl = hashCode(topWindowUrl); const dealId = getNextDealId(hashUrl); + const cId = extractCID(params); + const pId = extractPID(params); + const subDomain = extractSubDomain(params); let data = { url: encodeURIComponent(topWindowUrl), @@ -70,6 +85,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { if (bidderRequest.uspConsent) { data.usPrivacy = bidderRequest.uspConsent } + const dto = { method: 'POST', url: `${createDomain(subDomain)}/prebid/multi/${cId}`, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index b8ab83a95ae..103cb0897a7 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain } from 'modules/vidazooBidAdapter.js'; +import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain, extractPID, extractCID, extractSubDomain } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; import { version } from 'package.json'; @@ -220,7 +220,7 @@ describe('VidazooBidAdapter', function () { }); }); - describe(`user id system`, function () { + describe('user id system', function () { Object.keys(SUPPORTED_ID_SYSTEMS).forEach((idSystemProvider) => { const id = Date.now().toString(); const bid = utils.deepClone(BID); @@ -243,4 +243,24 @@ 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' }); + 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'); + }); + }); }); From 61ba0867c45f36ea23d7a4ba73783bd99ae13c33 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 23 Jul 2020 09:49:49 -0400 Subject: [PATCH 0018/1476] Prebid 4.0 updates (#5534) * remove these adapters from 4.0 (#5369) Co-authored-by: sumit sharma * add meta key to interpreted bid response (#5358) * add meta key to interpreted bid response * add more unit tests Co-authored-by: sumit sharma Co-authored-by: sumit sharma * add adomain to bid.meta in spotx adapter (#5401) * add adomain to bid.meta in spotx adapter this puts the adomain key in the right spot, related to https://github.com/prebid/Prebid.js/pull/5358 and partially solves https://github.com/prebid/Prebid.js/issues/3115 for SpotX * Update spotxBidAdapter.js * Update spotxBidAdapter.js * unit test for adomain on spotx adapter * Update spotxBidAdapter_spec.js * Update spotxBidAdapter_spec.js * adds advertiserDomains meta to the pubmatic adapter (#5402) * adds advertiserDomains meta * Update pubmaticBidAdapter.js * unit test for meta.advertiserDomains to pubmatic * Update pubmaticBidAdapter_spec.js * TCF Purpose 1 and Purpose 2 enforcement for Prebid v4.0 (#5336) * TCF v2.0 enforcement * test/spec/modules/gdprEnforcement_spec.js * add check for gdpr version * add logInfo message * remove comment and store value of PURPOSES in an object * add gvlid check * add unit tests for validateRules function * remove purposeId parameter from validateRules function * add extra tests * make failing unit test case pass * deprecate allowAuctionWithouConsent with tcf 2 workflow * add extra checks for defaults * remove tcf 2 test page * add strict gvlid check * add comments and shorten log messages * shorted log messages * add unit tests for setEnforcementConfig * add gvlid for alias and gvlMapping support * remove gvlid check * add support to add gvlid for aliases Co-authored-by: Jaimin Panchal * add advertiserDomains meta field to ix adapter (#5404) * add advertiserDomains meta field to ix adapter * Update ixBidAdapter.js * Update ixBidAdapter_spec.js * Update ixBidAdapter_spec.js * add adomain to bid.meta in telaria adapter (#5400) * add adomain to bid.meta in telaria adapter this puts the adomain key in the right spot, related to https://github.com/prebid/Prebid.js/pull/5358 and partially solves https://github.com/prebid/Prebid.js/issues/3115 for Telaria * Remove digitrust from prebid server js adapter (#5438) * Update prebidServerBidAdapter_spec.js * Update index.js * Interactive advertising bureau digitrust exit (#5429) * Removing DigiTrust ID system core. * Removing test spec for digitrust id. * Removing DigiTrust references from eids test spec. Co-authored-by: Chris Cole * Delete serverbidBidAdapter.md (#5477) deprecated in favor of consumable adapter * Set cookie domain in pubcid / userid on main domain, not subdomain (#5500) * update formatting * update formatting * requested changes implemented * add unit test * add test case for missing adomain in ix adapter (#5422) * add test case for missing adomain in ix adapter at request of @ix-prebid-support on https://github.com/prebid/Prebid.js/pull/5404 * Update ixBidAdapter_spec.js * Update ixBidAdapter_spec.js * Update ixBidAdapter_spec.js * drop support for userId configs with the `usersync` config object (#5427) * drop support for userId configs with the `usersync` config object, per deprecation notice * changes on drop support (#1) * Update userIdTargeting.md * Update userId.md * Update userId_spec.js * Update britepoolIdSystem.md * Update sharedIdSystem.md Co-authored-by: Patrick McCann Co-authored-by: sumit sharma Co-authored-by: sumit sharma Co-authored-by: sumit sharma Co-authored-by: Patrick McCann Co-authored-by: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Co-authored-by: Jaimin Panchal Co-authored-by: Neelanjan Sen Co-authored-by: Chris Cole Co-authored-by: Isaac A. Dettman Co-authored-by: Scott --- integrationExamples/gpt/digitrust_Full.html | 222 ------ integrationExamples/gpt/digitrust_Simple.html | 230 ------- .../gpt/digitrust_cmp_test.html | 192 ------ modules/.submodules.json | 1 - modules/appnexusBidAdapter.js | 15 +- modules/audienceNetworkBidAdapter.js | 308 --------- modules/audienceNetworkBidAdapter.md | 49 -- modules/britepoolIdSystem.md | 2 +- modules/consentManagement.js | 24 +- modules/digiTrustIdSystem.js | 460 ------------- modules/digiTrustIdSystem.md | 156 ----- modules/gdprEnforcement.js | 201 ++++-- modules/ixBidAdapter.js | 3 + modules/prebidServerBidAdapter/index.js | 26 - modules/pubCommonIdSystem.js | 30 + modules/pubmaticBidAdapter.js | 1 + modules/quantumBidAdapter.js | 320 --------- modules/quantumBidAdapter.md | 94 --- modules/serverbidBidAdapter.md | 44 -- modules/sharedIdSystem.md | 2 +- modules/spotxBidAdapter.js | 5 + modules/telariaBidAdapter.js | 5 + modules/userId/eids.js | 13 - modules/userId/eids.md | 8 - modules/userId/index.js | 22 +- modules/userId/userId.md | 4 +- modules/userIdTargeting.md | 2 +- src/adapterManager.js | 5 +- src/adapters/bidderFactory.js | 13 +- src/constants.json | 3 +- src/prebid.js | 4 +- .../modules/audienceNetworkBidAdapter_spec.js | 568 ---------------- test/spec/modules/consentManagement_spec.js | 52 +- test/spec/modules/digitrustIdSystem_spec.js | 131 ---- test/spec/modules/eids_spec.js | 16 - test/spec/modules/gdprEnforcement_spec.js | 635 ++++++++++++++++-- test/spec/modules/ixBidAdapter_spec.js | 69 +- .../modules/prebidServerBidAdapter_spec.js | 34 - test/spec/modules/pubmaticBidAdapter_spec.js | 2 + test/spec/modules/quantumBidAdapter_spec.js | 325 --------- test/spec/modules/spotxBidAdapter_spec.js | 4 + test/spec/modules/telariaBidAdapter_spec.js | 2 +- test/spec/modules/userId_spec.js | 46 +- test/spec/unit/core/bidderFactory_spec.js | 32 +- 44 files changed, 1001 insertions(+), 3379 deletions(-) delete mode 100644 integrationExamples/gpt/digitrust_Full.html delete mode 100644 integrationExamples/gpt/digitrust_Simple.html delete mode 100644 integrationExamples/gpt/digitrust_cmp_test.html delete mode 100644 modules/audienceNetworkBidAdapter.js delete mode 100644 modules/audienceNetworkBidAdapter.md delete mode 100644 modules/digiTrustIdSystem.js delete mode 100644 modules/digiTrustIdSystem.md delete mode 100644 modules/quantumBidAdapter.js delete mode 100644 modules/quantumBidAdapter.md delete mode 100644 modules/serverbidBidAdapter.md delete mode 100644 test/spec/modules/audienceNetworkBidAdapter_spec.js delete mode 100644 test/spec/modules/digitrustIdSystem_spec.js delete mode 100644 test/spec/modules/quantumBidAdapter_spec.js diff --git a/integrationExamples/gpt/digitrust_Full.html b/integrationExamples/gpt/digitrust_Full.html deleted file mode 100644 index fc7704776f4..00000000000 --- a/integrationExamples/gpt/digitrust_Full.html +++ /dev/null @@ -1,222 +0,0 @@ - - - Full DigiTrust Prebid Sample - - - - - - - - - - - - - -

DigiTrust Prebid Full Sample

- - -

- This sample shows the simplest integration path for using DigiTrust ID with Prebid. - You can use DigiTrust ID without integrating the entire DigiTrust suite. -

- -
- -
- -
- - - - diff --git a/integrationExamples/gpt/digitrust_Simple.html b/integrationExamples/gpt/digitrust_Simple.html deleted file mode 100644 index 2581c6ce7cc..00000000000 --- a/integrationExamples/gpt/digitrust_Simple.html +++ /dev/null @@ -1,230 +0,0 @@ - - - Simple DigiTrust Prebid - No Framework - - - - - - - - - - - - - - -

DigiTrust Prebid Sample - No Framework

- -

- This sample shows the simplest integration path for using DigiTrust ID with Prebid. - You can use DigiTrust ID without integrating the entire DigiTrust suite. -

-
- -
- -
- - diff --git a/integrationExamples/gpt/digitrust_cmp_test.html b/integrationExamples/gpt/digitrust_cmp_test.html deleted file mode 100644 index 6f0a70188f3..00000000000 --- a/integrationExamples/gpt/digitrust_cmp_test.html +++ /dev/null @@ -1,192 +0,0 @@ - - - CMP Simple DigiTrust Prebid - No Framework - - - - - - - - - - - - - -

DigiTrust Prebid Sample - No Framework

- -

- This sample tests cmp behavior with simple integration path for using DigiTrust ID with Prebid. - You can use DigiTrust ID without integrating the entire DigiTrust suite. -

-
- -
- -
- - - diff --git a/modules/.submodules.json b/modules/.submodules.json index ba8af4e6550..58ab8798fa5 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -2,7 +2,6 @@ "userId": [ "unifiedIdSystem", "pubCommonIdSystem", - "digiTrustIdSystem", "id5IdSystem", "parrableIdSystem", "britepoolIdSystem", diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index df0b60cb7d7..12bc6a8105c 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -63,7 +63,20 @@ const storage = getStorageManager(GVLID, BIDDER_CODE); export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['appnexusAst', 'brealtime', 'emxdigital', 'pagescience', 'defymedia', 'gourmetads', 'matomy', 'featureforward', 'oftmedia', 'districtm', 'adasta', 'beintoo'], + aliases: [ + { code: 'appnexusAst', gvlid: 32 }, + { code: 'brealtime' }, + { code: 'emxdigital', gvlid: 183 }, + { code: 'pagescience' }, + { code: 'defymedia' }, + { code: 'gourmetads' }, + { code: 'matomy' }, + { code: 'featureforward' }, + { code: 'oftmedia' }, + { code: 'districtm', gvlid: 144 }, + { code: 'adasta' }, + { code: 'beintoo', gvlid: 618 }, + ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/modules/audienceNetworkBidAdapter.js b/modules/audienceNetworkBidAdapter.js deleted file mode 100644 index 816a6abd0e8..00000000000 --- a/modules/audienceNetworkBidAdapter.js +++ /dev/null @@ -1,308 +0,0 @@ -/** - * @file AudienceNetwork adapter. - */ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { generateUUID, deepAccess, convertTypes, formatQS } from '../src/utils.js'; -import findIndex from 'core-js-pure/features/array/find-index.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const code = 'audienceNetwork'; -const currency = 'USD'; -const method = 'GET'; -const url = 'https://an.facebook.com/v2/placementbid.json'; -const supportedMediaTypes = ['banner', 'video']; -const netRevenue = true; -const hbBidder = 'fan'; -const ttl = 600; -const videoTtl = 3600; -const platver = '$prebid.version$'; -const platform = '241394079772386'; -const adapterver = '1.3.0'; - -/** - * Does this bid request contain valid parameters? - * @param {Object} bid - * @returns {Boolean} - */ -const isBidRequestValid = bid => - typeof bid.params === 'object' && - typeof bid.params.placementId === 'string' && - bid.params.placementId.length > 0 && - Array.isArray(bid.sizes) && bid.sizes.length > 0 && - (isFullWidth(bid.params.format) ? bid.sizes.map(flattenSize).some(size => size === '300x250') : true) && - (isValidNonSizedFormat(bid.params.format) || bid.sizes.map(flattenSize).some(isValidSize)); - -/** - * Flattens a 2-element [W, H] array as a 'WxH' string, - * otherwise passes value through. - * @param {Array|String} size - * @returns {String} - */ -const flattenSize = size => - (Array.isArray(size) && size.length === 2) ? `${size[0]}x${size[1]}` : size; - -/** - * Expands a 'WxH' string as a 2-element [W, H] array - * @param {String} size - * @returns {Array} - */ -const expandSize = size => size.split('x').map(Number); - -/** - * Is this a valid slot size? - * @param {String} size - * @returns {Boolean} - */ -const isValidSize = size => includes(['300x250', '320x50'], size); - -/** - * Is this a valid, non-sized format? - * @param {String} size - * @returns {Boolean} - */ -const isValidNonSizedFormat = format => includes(['video', 'native'], format); - -/** - * Is this a valid size and format? - * @param {String} size - * @returns {Boolean} - */ -const isValidSizeAndFormat = (size, format) => - (isFullWidth(format) && flattenSize(size) === '300x250') || - isValidNonSizedFormat(format) || - isValidSize(flattenSize(size)); - -/** - * Find a preferred entry, if any, from an array of valid sizes. - * @param {Array} acc - * @param {String} cur - */ -const sortByPreferredSize = (acc, cur) => - (cur === '300x250') ? [cur, ...acc] : [...acc, cur]; - -/** - * Map any deprecated size/formats to new values. - * @param {String} size - * @param {String} format - */ -const mapDeprecatedSizeAndFormat = (size, format) => - isFullWidth(format) ? ['300x250', null] : [size, format]; - -/** - * Is this a video format? - * @param {String} format - * @returns {Boolean} - */ -const isVideo = format => format === 'video'; - -/** - * Is this a fullwidth format? - * @param {String} format - * @returns {Boolean} - */ -const isFullWidth = format => format === 'fullwidth'; - -/** - * Which SDK version should be used for this format? - * @param {String} format - * @returns {String} - */ -const sdkVersion = format => isVideo(format) ? '' : '6.0.web'; - -/** - * Which platform identifier should be used? - * @param {Array} platforms Possible platform identifiers - * @returns {String} First valid platform found, or default if none found - */ -const findPlatform = platforms => [...platforms.filter(Boolean), platform][0]; - -/** - * Does the search part of the URL contain "anhb_testmode" - * and therefore indicate testmode should be used? - * @returns {String} "true" or "false" - */ -const isTestmode = () => Boolean( - window && window.location && - typeof window.location.search === 'string' && - window.location.search.indexOf('anhb_testmode') !== -1 -).toString(); - -/** - * Generate ad HTML for injection into an iframe - * @param {String} placementId - * @param {String} format - * @param {String} bidId - * @returns {String} HTML - */ -const createAdHtml = (placementId, format, bidId) => { - const nativeStyle = format === 'native' ? '' : ''; - const nativeContainer = format === 'native' ? '
' : ''; - return ` - ${nativeStyle} - -
- - - ${nativeContainer} -
- -`; -}; - -/** - * Convert each bid request to a single URL to fetch those bids. - * @param {Array} bids - list of bids - * @param {String} bids[].placementCode - Prebid placement identifier - * @param {Object} bids[].params - * @param {String} bids[].params.placementId - Audience Network placement identifier - * @param {String} bids[].params.platform - Audience Network platform identifier (optional) - * @param {String} bids[].params.format - Optional format, one of 'video' or 'native' if set - * @param {Array} bids[].sizes - list of desired advert sizes - * @param {Array} bids[].sizes[] - Size arrays [h,w]: should include one of [300, 250], [320, 50] - * @returns {Array} List of URLs to fetch, plus formats and sizes for later use with interpretResponse - */ -const buildRequests = (bids, bidderRequest) => { - // Build lists of placementids, adformats, sizes and SDK versions - const placementids = []; - const adformats = []; - const sizes = []; - const sdk = []; - const platforms = []; - const requestIds = []; - - bids.forEach(bid => bid.sizes - .map(flattenSize) - .filter(size => isValidSizeAndFormat(size, bid.params.format)) - .reduce(sortByPreferredSize, []) - .slice(0, 1) - .forEach(preferredSize => { - const [size, format] = mapDeprecatedSizeAndFormat(preferredSize, bid.params.format); - placementids.push(bid.params.placementId); - adformats.push(format || size); - sizes.push(size); - sdk.push(sdkVersion(format)); - platforms.push(bid.params.platform); - requestIds.push(bid.bidId); - }) - ); - // Build URL - const testmode = isTestmode(); - const pageurl = encodeURIComponent(deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || deepAccess(bidderRequest, 'refererInfo.referer')); - const platform = findPlatform(platforms); - const cb = generateUUID(); - const search = { - placementids, - adformats, - testmode, - pageurl, - sdk, - adapterver, - platform, - platver, - cb - }; - const video = findIndex(adformats, isVideo); - if (video !== -1) { - [search.playerwidth, search.playerheight] = expandSize(sizes[video]); - } - const data = formatQS(search); - - return [{ adformats, data, method, requestIds, sizes, url, pageurl }]; -}; - -/** - * Convert a server response to a bid response. - * @param {Object} response - object representing the response - * @param {Object} response.body - response body, already converted from JSON - * @param {Object} bidRequests - the original bid requests - * @param {Array} bidRequest.adformats - list of formats for the original bid requests - * @param {Array} bidRequest.sizes - list of sizes fot the original bid requests - * @returns {Array} A list of bid response objects - */ -const interpretResponse = ({ body }, { adformats, requestIds, sizes, pageurl }) => { - const { bids = {} } = body; - return Object.keys(bids) - // extract Array of bid responses - .map(placementId => bids[placementId]) - // flatten - .reduce((a, b) => a.concat(b), []) - // transform to bidResponse - .map((bid, i) => { - const { - bid_id: fbBidid, - placement_id: creativeId, - bid_price_cents: cpm - } = bid; - - const format = adformats[i]; - const [width, height] = expandSize(flattenSize(sizes[i])); - const ad = createAdHtml(creativeId, format, fbBidid); - const requestId = requestIds[i]; - - const bidResponse = { - // Prebid attributes - requestId, - cpm: cpm / 100, - width, - height, - ad, - ttl, - creativeId, - netRevenue, - currency, - // Audience Network attributes - hb_bidder: hbBidder, - fb_bidid: fbBidid, - fb_format: format, - fb_placementid: creativeId - }; - // Video attributes - if (isVideo(format)) { - bidResponse.mediaType = 'video'; - bidResponse.vastUrl = `https://an.facebook.com/v1/instream/vast.xml?placementid=${creativeId}&pageurl=${pageurl}&playerwidth=${width}&playerheight=${height}&bidid=${fbBidid}`; - bidResponse.ttl = videoTtl; - } - return bidResponse; - }); -}; - -/** - * Covert bid param types for S2S - * @param {Object} params bid params - * @param {Boolean} isOpenRtb boolean to check openrtb2 protocol - * @return {Object} params bid params - */ -const transformBidParams = (params, isOpenRtb) => { - return convertTypes({ - 'placementId': 'string' - }, params); -} - -export const spec = { - code, - supportedMediaTypes, - isBidRequestValid, - buildRequests, - interpretResponse, - transformBidParams -}; - -registerBidder(spec); diff --git a/modules/audienceNetworkBidAdapter.md b/modules/audienceNetworkBidAdapter.md deleted file mode 100644 index 6147191f4b7..00000000000 --- a/modules/audienceNetworkBidAdapter.md +++ /dev/null @@ -1,49 +0,0 @@ -# Overview - -Module Name: Audience Network Bid Adapter - -Module Type: Bidder Adapter - -Maintainer: Lovell Fuller - -# Parameters - -| Name | Scope | Description | Example | -| :------------ | :------- | :---------------------------------------------- | :--------------------------------- | -| `placementId` | required | The Placement ID from Audience Network | "555555555555555\_555555555555555" | -| `format` | optional | Format, one of "native" or "video" | "native" | - -# Example ad units - -```javascript -const adUnits = [{ - code: "test-iab", - sizes: [[300, 250]], - bids: [{ - bidder: "audienceNetwork", - params: { - placementId: "555555555555555_555555555555555" - } - }] -}, { - code: "test-native", - sizes: [[300, 250]], - bids: [{ - bidder: "audienceNetwork", - params: { - format: "native", - placementId: "555555555555555_555555555555555" - } - }] -}, { - code: "test-video", - sizes: [[640, 360]], - bids: [{ - bidder: "audienceNetwork", - params: { - format: "video", - placementId: "555555555555555_555555555555555" - } - }] -}]; -``` diff --git a/modules/britepoolIdSystem.md b/modules/britepoolIdSystem.md index 89287aed7ca..a15f601aee3 100644 --- a/modules/britepoolIdSystem.md +++ b/modules/britepoolIdSystem.md @@ -7,7 +7,7 @@ BritePool User ID Module. For assistance setting up your module please contact u Individual params may be set for the BritePool User ID Submodule. At least one identifier must be set in the params. ``` pbjs.setConfig({ - usersync: { + userSync: { userIds: [{ name: 'britepoolId', storage: { diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 53e97006bd1..a5ed134420e 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -14,9 +14,12 @@ const DEFAULT_CMP = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 10000; const DEFAULT_ALLOW_AUCTION_WO_CONSENT = true; +export const allowAuction = { + value: DEFAULT_ALLOW_AUCTION_WO_CONSENT, + definedInConfig: false +} export let userCMP; export let consentTimeout; -export let allowAuction; export let gdprScope; export let staticConsentData; @@ -322,6 +325,13 @@ function processCmpData(consentObject, hookConfig) { // determine which set of checks to run based on cmpVersion let checkFn = (cmpVersion === 1) ? checkV1Data : (cmpVersion === 2) ? checkV2Data : null; + // Raise deprecation warning if 'allowAuctionWithoutConsent' is used with TCF 2. + if (allowAuction.definedInConfig && cmpVersion === 2) { + utils.logWarn(`'allowAuctionWithoutConsent' ignored for TCF 2`); + } else if (!allowAuction.definedInConfig && cmpVersion === 1) { + utils.logInfo(`'allowAuctionWithoutConsent' using system default: (${DEFAULT_ALLOW_AUCTION_WO_CONSENT}).`); + } + if (utils.isFn(checkFn)) { if (checkFn(consentObject)) { cmpFailed(`CMP returned unexpected value during lookup process.`, hookConfig, consentObject); @@ -352,7 +362,7 @@ function cmpFailed(errMsg, hookConfig, extraArgs) { clearTimeout(hookConfig.timer); // still set the consentData to undefined when there is a problem as per config options - if (allowAuction) { + if (allowAuction.value && cmpVersion === 1) { storeConsentData(undefined); } exitModule(errMsg, hookConfig, extraArgs); @@ -406,8 +416,8 @@ function exitModule(errMsg, hookConfig, extraArgs) { let nextFn = hookConfig.nextFn; if (errMsg) { - if (allowAuction) { - utils.logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.', extraArgs); + if (allowAuction.value && cmpVersion === 1) { + utils.logWarn(errMsg + ` 'allowAuctionWithoutConsent' activated.`, extraArgs); nextFn.apply(context, args); } else { utils.logError(errMsg + ' Canceling auction as per consentManagement config.', extraArgs); @@ -460,10 +470,8 @@ export function setConsentConfig(config) { } if (typeof config.allowAuctionWithoutConsent === 'boolean') { - allowAuction = config.allowAuctionWithoutConsent; - } else { - allowAuction = DEFAULT_ALLOW_AUCTION_WO_CONSENT; - utils.logInfo(`consentManagement config did not specify allowAuctionWithoutConsent. Using system default setting (${DEFAULT_ALLOW_AUCTION_WO_CONSENT}).`); + allowAuction.value = config.allowAuctionWithoutConsent; + allowAuction.definedInConfig = true; } // if true, then gdprApplies should be set to true diff --git a/modules/digiTrustIdSystem.js b/modules/digiTrustIdSystem.js deleted file mode 100644 index d8aa8be9376..00000000000 --- a/modules/digiTrustIdSystem.js +++ /dev/null @@ -1,460 +0,0 @@ -/** - * This module adds DigiTrust ID support to the User ID module - * The {@link module:modules/userId} module is required - * If the full DigiTrust Id library is included the standard functions - * will be invoked to obtain the user's DigiTrust Id. - * When the full library is not included this will fall back to the - * DigiTrust Identity API and generate a mock DigiTrust object. - * @module modules/digiTrustIdSystem - * @requires module:modules/userId - */ - -import * as utils from '../src/utils.js' -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const DT_VENDOR_ID = 64; // cmp gvlVendorId -const storage = getStorageManager(DT_VENDOR_ID); - -var fallbackTimeout = 1550; // timeout value that allows userId system to execute first -var fallbackTimer = 0; // timer Id for fallback init so we don't double call - -/** - * Checks to see if the DigiTrust framework is initialized. - * @function - */ -function isInitialized() { - if (window.DigiTrust == null) { - return false; - } - // eslint-disable-next-line no-undef - return DigiTrust.isClient; // this is set to true after init -} - -/** - * Tests for presence of the DigiTrust object - * */ -function isPresent() { - return (window.DigiTrust != null); -} - -var noop = function () { -}; - -const MAX_RETRIES = 2; -const DT_ID_SVC = 'https://prebid.digitru.st/id/v1'; - -var isFunc = function (fn) { - return typeof (fn) === 'function'; -} - -var _savedId = null; // closure variable for storing Id to avoid additional requests - -function callApi(options) { - var ajaxOptions = { - method: 'GET', - withCredentials: true - }; - - ajax( - DT_ID_SVC, - { - success: options.success, - error: options.fail - }, - null, - ajaxOptions - ); -} - -/** - * Encode the Id per DigiTrust lib - * @param {any} id - */ -function encId(id) { - try { - if (typeof (id) !== 'string') { - id = JSON.stringify(id); - } - return btoa(id); - } catch (ex) { - return id; - } -} - -/** - * Writes the Identity into the expected DigiTrust cookie - * @param {any} id - */ -function writeDigiId(id) { - var key = 'DigiTrust.v1.identity'; - var date = new Date(); - date.setTime(date.getTime() + 604800000); - storage.setCookie(key, encId(id), date.toUTCString(), 'none'); -} - -/** - * Tests to see if the current browser is FireFox - */ -function isFirefoxBrowser(ua) { - ua = ua || navigator.userAgent; - ua = ua.toLowerCase(); - if (ua.indexOf('firefox') !== -1) { - return true; - } - return false; -} - -/** - * Test to see if the user has a browser that is disallowed for making AJAX - * requests due to the behavior not supported DigiTrust ID Cookie. - */ -function isDisallowedBrowserForApiCall() { - if (utils.isSafariBrowser()) { - return true; - } else if (isFirefoxBrowser()) { - return true; - } - return false; -} - -/** - * Set up a DigiTrust facade object to mimic the API - * - */ -function initDigitrustFacade(config) { - clearTimeout(fallbackTimer); - fallbackTimer = 0; - - var facade = { - isClient: true, - isMock: true, - _internals: { - callCount: 0, - initCallback: null - }, - getUser: function (obj, callback) { - var isAsync = !!isFunc(callback); - var cb = isAsync ? callback : noop; - var errResp = { success: false }; - var inter = facade._internals; - inter.callCount++; - - // wrap the initializer callback, if present - var checkAndCallInitializeCb = function (idResponse) { - if (inter.callCount <= 1 && isFunc(inter.initCallback)) { - try { - inter.initCallback(idResponse); - } catch (ex) { - utils.logError('Exception in passed DigiTrust init callback', ex); - } - } - } - - if (!isMemberIdValid(obj.member)) { - if (!isAsync) { - return errResp - } else { - cb(errResp); - return; - } - } - - if (_savedId != null) { - if (isAsync) { - checkAndCallInitializeCb(_savedId); - // cb(_savedId); - return; - } else { - return _savedId; - } - } - - var opts = { - success: function (respText, result) { - var idResult = { - success: true - } - try { - idResult.identity = JSON.parse(respText); - _savedId = idResult; // Save result to the cache variable - writeDigiId(respText); - } catch (ex) { - idResult.success = false; - delete idResult.identity; - } - checkAndCallInitializeCb(idResult); - }, - fail: function (statusErr, result) { - utils.logError('DigiTrustId API error: ' + statusErr); - } - } - - // check gdpr vendor here. Full DigiTrust library has vendor check built in - gdprConsent.hasConsent(null, function (hasConsent) { - if (hasConsent) { - if (isDisallowedBrowserForApiCall()) { - let resultObj = { - success: false, - err: 'Your browser does not support DigiTrust Identity' - } - checkAndCallInitializeCb(resultObj); - return; - } - callApi(opts); - } - }) - - if (!isAsync) { - return errResp; // even if it will be successful later, without a callback we report a "failure in this moment" - } - } - } - - if (config && isFunc(config.callback)) { - facade._internals.initCallback = config.callback; - } - - if (window && window.DigiTrust == null) { - window.DigiTrust = facade; - } -} - -/** - * Tests to see if a member ID is valid within facade - * @param {any} memberId - */ -var isMemberIdValid = function (memberId) { - if (memberId && memberId.length > 0) { - return true; - } else { - utils.logError('[DigiTrust Prebid Client Error] Missing member ID, add the member ID to the function call options'); - return false; - } -}; - -/** - * DigiTrust consent handler for GDPR and __cmp. - * */ -var gdprConsent = { - hasConsent: function (options, consentCb) { - options = options || { consentTimeout: 1500 }; - var stopTimer; - var processed = false; - var consentAnswer = false; - if (typeof (window.__cmp) !== 'undefined') { - stopTimer = setTimeout(function () { - consentAnswer = false; - processed = true; - consentCb(consentAnswer); - }, options.consentTimeout); - - window.__cmp('ping', null, function(pingAnswer) { - if (pingAnswer.gdprAppliesGlobally) { - window.__cmp('getVendorConsents', [DT_VENDOR_ID], function (result) { - if (processed) { return; } // timeout before cmp answer, cancel - clearTimeout(stopTimer); - var myconsent = result.vendorConsents[DT_VENDOR_ID]; - consentCb(myconsent); - }); - } else { - if (processed) { return; } // timeout before cmp answer, cancel - clearTimeout(stopTimer); - consentAnswer = true; - consentCb(consentAnswer); - } - }); - } else { - // __cmp library is not preset. - // ignore this check and rely on id system GDPR consent management - consentAnswer = true; - consentCb(consentAnswer); - } - } -} - -/** - * Encapsulation of needed info for the callback return. - * - * @param {any} opts - */ -var ResultWrapper = function (opts) { - var me = this; - this.idObj = null; - - var idSystemFn = null; - - /** - * Callback method that is passed back to the userId module. - * - * @param {function} callback - */ - this.userIdCallback = function (callback) { - idSystemFn = callback; - if (me.idObj == null) { - me.idObj = _savedId; - } - - if (me.idObj != null && isFunc(callback)) { - callback(wrapIdResult()); - } - } - - /** - * Return a wrapped result formatted for userId system - */ - function wrapIdResult() { - if (me.idObj == null) { - me.idObj = _savedId; - } - - if (me.idObj == null) { - return null; - } - - var cp = me.configParams; - var exp = (cp && cp.storage && cp.storage.expires) || 60; - - var rslt = { - data: null, - expires: exp - }; - if (me.idObj && me.idObj.success && me.idObj.identity) { - rslt.data = me.idObj.identity; - } else { - rslt.err = 'Failure getting id'; - } - - return rslt; - } - - this.retries = 0; - this.retryId = 0; - - this.executeIdRequest = function (configParams) { - // eslint-disable-next-line no-undef - DigiTrust.getUser({ member: 'prebid' }, function (idResult) { - me.idObj = idResult; - var cb = function () { - if (isFunc(idSystemFn)) { - idSystemFn(wrapIdResult()); - } - } - - cb(); - if (configParams && configParams.callback && isFunc(configParams.callback)) { - try { - configParams.callback(idResult); - } catch (ex) { - utils.logError('Failure in DigiTrust executeIdRequest', ex); - } - } - }); - } -} - -// An instance of the result wrapper object. -var resultHandler = new ResultWrapper(); - -/* - * Internal implementation to get the Id and trigger callback - */ -function getDigiTrustId(configParams) { - if (resultHandler.configParams == null) { - resultHandler.configParams = configParams; - } - - // First see if we should initialize DigiTrust framework - if (isPresent() && !isInitialized()) { - initializeDigiTrust(configParams); - resultHandler.retryId = setTimeout(function () { - getDigiTrustId(configParams); - }, 100 * (1 + resultHandler.retries++)); - return resultHandler.userIdCallback; - } else if (!isInitialized()) { // Second see if we should build a facade object - if (resultHandler.retries >= MAX_RETRIES) { - initDigitrustFacade(configParams); // initialize a facade object that relies on the AJAX call - resultHandler.executeIdRequest(configParams); - } else { - // use expanding envelope - if (resultHandler.retryId != 0) { - clearTimeout(resultHandler.retryId); - } - resultHandler.retryId = setTimeout(function () { - getDigiTrustId(configParams); - }, 100 * (1 + resultHandler.retries++)); - } - return resultHandler.userIdCallback; - } else { // Third get the ID - resultHandler.executeIdRequest(configParams); - return resultHandler.userIdCallback; - } -} - -function initializeDigiTrust(config) { - utils.logInfo('Digitrust Init'); - var dt = window.DigiTrust; - if (dt && !dt.isClient && config != null) { - dt.initialize(config.init, config.callback); - } else if (dt == null) { - // Assume we are already on a delay and DigiTrust is not on page - initDigitrustFacade(config); - } -} - -var testHook = {}; - -/** - * Exposes the test hook object by attaching to the digitrustIdModule. - * This method is called in the unit tests to surface internals. - */ -export function surfaceTestHook() { - digiTrustIdSubmodule['_testHook'] = testHook; - return testHook; -} - -testHook.initDigitrustFacade = initDigitrustFacade; // expose for unit tests -testHook.gdpr = gdprConsent; - -/** @type {Submodule} */ -export const digiTrustIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: 'digitrust', - /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} value - * @returns {{pubcid:string}} - */ - decode: function (idData) { - try { - return { 'digitrustid': idData }; - } catch (e) { - utils.logError('DigiTrust ID submodule decode error'); - } - }, - getId: function (configParams) { - return {callback: getDigiTrustId(configParams)}; - }, - _testInit: surfaceTestHook -}; - -// check for fallback init of DigiTrust -function fallbackInit() { - if (resultHandler.retryId == 0 && !isInitialized()) { - // this triggers an init - var conf = { - member: 'fallback', - callback: noop - }; - getDigiTrustId(conf); - } -} - -fallbackTimer = setTimeout(fallbackInit, fallbackTimeout); - -submodule('userId', digiTrustIdSubmodule); diff --git a/modules/digiTrustIdSystem.md b/modules/digiTrustIdSystem.md deleted file mode 100644 index c0b274d3292..00000000000 --- a/modules/digiTrustIdSystem.md +++ /dev/null @@ -1,156 +0,0 @@ -## DigiTrust Universal Id Integration - -Setup ------ -The DigiTrust Id integration for Prebid may be used with or without the full -DigiTrust library. This is an optional module that must be used in conjunction -with the userId module. - -See the [Prebid Integration Guide for DigiTrust](https://github.com/digi-trust/dt-cdn/wiki/Prebid-Integration-for-DigiTrust-Id) -and the [DigiTrust wiki](https://github.com/digi-trust/dt-cdn/wiki) -for further instructions. - - -## Example Prebid Configuration for Digitrust Id -``` - pbjs.que.push(function() { - pbjs.setConfig({ - usersync: { - userIds: [{ - name: "digitrust", - params: { - init: { - member: 'example_member_id', - site: 'example_site_id' - }, - callback: function (digiTrustResult) { - // This callback method is optional and used for error handling - // in many if not most cases. - /* - if (digiTrustResult.success) { - // Success in Digitrust init; - // 'DigiTrust Id (encrypted): ' + digiTrustResult.identity.id; - } - else { - // Digitrust init failed - } - */ - } - }, - storage: { - type: "html5", - name: "pbjsdigitrust", - expires: 60 - } - }] - } - }); - pbjs.addAdUnits(adUnits); - pbjs.requestBids({ - bidsBackHandler: sendAdserverRequest - }); - }); - -``` - - -## Building Prebid with DigiTrust Support -Your Prebid build must include the modules for both **userId** and **digitrustIdLoader**. Follow the build instructions for Prebid as -explained in the top level README.md file of the Prebid source tree. - -ex: $ gulp build --modules=userId,digitrustIdLoader - -### Step by step Prebid build instructions for DigiTrust - -1. Download the Prebid source from [Prebid Git Repo](https://github.com/prebid/Prebid.js) -2. Set up your environment as outlined in the [Readme File](https://github.com/prebid/Prebid.js/blob/master/README.md#Build) -3. Execute the build command either with all modules or with the `userId` and `digitrustIdLoader` modules. - ``` - $ gulp build --modules=userId,digitrustIdLoader - ``` -4. (Optional) Concatenate the DigiTrust source code to the end of your `prebid.js` file for a single source distribution. -5. Upload the resulting source file to your CDN. - - -## Deploying Prebid with DigiTrust ID support -**Precondition:** You must be a DigiTrust member and have registered through the [DigiTrust Signup Process](http://www.digitru.st/signup/). -Your assigned publisher ID will be required in the configuration settings for all deployment scenarios. - -There are three supported approaches to deploying the Prebid-integrated DigiTrust package: - -* "Bare bones" deployment using only the integrated DigiTrust module code. -* Full DigiTrust with CDN referenced DigiTrust.js library. -* Full DigiTrust packaged with Prebid or site js. - -### Bare Bones Deployment - -This deployment results in the smallest Javascript package and is the simplest deployment. -It is appropriate for testing or deployments where simplicity is key. This approach -utilizes the REST API for ID generation. While there is less Javascript in use, -the user may experience more network requests than the scenarios that include the full -DigiTrust library. - -1. Build your Prebid package as above, skipping step 4. -2. Add the DigiTrust initializer section to your Prebid initialization object as below, - using your Member ID and Site ID. -3. Add a reference to your Prebid package and the initialization code on all pages you wish - to utilize Prebid with integrated DigiTrust ID. - - - - -### Full DigiTrust with CDN referenced DigiTrust library - -Both "Full DigiTrust" deployments will result in a larger initial Javascript payload. -The end user may experience fewer overall network requests as the encrypted and anonymous -DigiTrust ID can often be generated fully in client-side code. Utilizing the CDN reference -to the official DigiTrust distribution insures you will be running the latest version of the library. - -The Full DigiTrust deployment is designed to work with both new DigiTrust with Prebid deployments, and with -Prebid deployments by existing DigiTrust members. This allows you to migrate your code more slowly -without losing DigiTrust support in the process. - -1. Deploy your built copy of `prebid.js` to your CDN. -2. On each page reference both your `prebid.js` and a copy of the **DigiTrust** library. - This may either be a copy downloaded from the [DigiTrust CDN](https://cdn.digitru.st/prod/1/digitrust.min.js) to your CDN, - or directly referenced from the URL https://cdn.digitru.st/prod/1/digitrust.min.js. These may be added to the page in any order. -3. Add a configuration section for Prebid that includes the `usersync` settings and the `digitrust` settings. - -### Full DigiTrust packaged with Prebid - - -1. Deploy your built copy of `prebid.js` to your CDN. Be sure to perform *Step 4* of the build to concatenate or - integrate the full DigiTrust library code with your Prebid package. -2. On each page reference your `prebid.js` -3. Add a configuration section for Prebid that includes the `usersync` settings and the `digitrust` settings. - This code may also be appended to your Prebid package or placed in other initialization methods. - - - -## Parameter Descriptions for the `usersync` Configuration Section -The below parameters apply only to the DigiTrust ID integration. - -| Param under usersync.userIds[] | Scope | Type | Description | Example | -| --- | --- | --- | --- | --- | -| name | Required | String | ID value for the DigiTrust module - `"digitrust"` | `"digitrust"` | -| params | Required | Object | Details for DigiTrust initialization. | | -| params.init | Required | Object | Initialization parameters, including the DigiTrust Publisher ID and Site ID. | | -| params.init.member | Required | String | DigiTrust Publisher Id | "A897dTzB" | -| params.init.site | Required | String | DigiTrust Site Id | "MM2123" | -| params.callback | Optional | Function | Callback method to fire after initialization of the DigiTrust framework. The argument indicates failure and success and the identity object upon success. | | -| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | | -| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` | -| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"pbjsdigitrust"` | -| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. Default is 30 for UnifiedId and 1825 for PubCommonID | `365` | -| value | Optional | Object | Used only if the page has a separate mechanism for storing the Unified 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 | `{"tdid": "D6885E90-2A7A-4E0F-87CB-7734ED1B99A3"}` | - - - -## Further Reading - -+ [DigiTrust Home Page](http://digitru.st) - -+ [DigiTrust integration guide](https://github.com/digi-trust/dt-cdn/wiki/Integration-Guide) - -+ [DigiTrust ID Encryption](https://github.com/digi-trust/dt-cdn/wiki/ID-encryption) - diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 6a3fbdce1f2..0a32441c813 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -11,39 +11,97 @@ import includes from 'core-js-pure/features/array/includes.js'; import { registerSyncInner } from '../src/adapters/bidderFactory.js'; import { getHook } from '../src/hook.js'; import { validateStorageEnforcement } from '../src/storageManager.js'; +import events from '../src/events.js'; +import { EVENTS } from '../src/constants.json'; -const purpose1 = 'storage'; +const TCF2 = { + 'purpose1': { id: 1, name: 'storage' }, + 'purpose2': { id: 2, name: 'basicAds' } +} + +const DEFAULT_RULES = [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] +}, { + purpose: 'basicAds', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] +}]; +export let purpose1Rule; +export let purpose2Rule; let addedDeviceAccessHook = false; -let enforcementRules; +export let enforcementRules; -function getGvlid() { +function getGvlid(bidderCode) { let gvlid; - const bidderCode = config.getCurrentBidder(); + bidderCode = bidderCode || config.getCurrentBidder(); if (bidderCode) { - const bidder = adapterManager.getBidAdapter(bidderCode); - gvlid = bidder.getSpec().gvlid; - } else { - utils.logWarn('Current module not found'); + const gvlMapping = config.getConfig('gvlMapping'); + if (gvlMapping && gvlMapping[bidderCode]) { + gvlid = gvlMapping[bidderCode]; + } else { + const bidder = adapterManager.getBidAdapter(bidderCode); + if (bidder && bidder.getSpec) { + gvlid = bidder.getSpec().gvlid; + } + } } return gvlid; } +function getGvlidForUserIdModule(userIdModule) { + let gvlId; + const gvlMapping = config.getConfig('gvlMapping'); + if (gvlMapping && gvlMapping[userIdModule.name]) { + gvlId = gvlMapping[userIdModule.name]; + } else { + gvlId = userIdModule.gvlid; + } + return gvlId; +} + /** - * This function takes in rules and consentData as input and validates against the consentData provided. If it returns true Prebid will allow the next call else it will log a warning - * @param {Object} rules enforcement rules set in config - * @param {Object} consentData gdpr consent data + * 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. + * @param {Object} rule - enforcement rules set in config + * @param {Object} consentData - gdpr consent data + * @param {string=} currentModule - Bidder code of the current module + * @param {number=} gvlId - GVL ID for the module * @returns {boolean} */ -function validateRules(rule, consentData, currentModule, gvlid) { - // if vendor has exception => always true +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)) { return true; } - // if enforcePurpose is false or purpose was granted isAllowed is true, otherwise false - const purposeAllowed = rule.enforcePurpose === false || utils.deepAccess(consentData, 'vendorData.purpose.consents.1') === true; - // if enforceVendor is false or vendor was granted isAllowed is true, otherwise false - const vendorAllowed = rule.enforceVendor === false || utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlid}`) === true; + + // get data from the consent string + const purposeConsent = utils.deepAccess(consentData, `vendorData.purpose.consents.${purposeId}`); + const vendorConsent = utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`); + const liTransparency = utils.deepAccess(consentData, `vendorData.purpose.legitimateInterests.${purposeId}`); + + /* + Since vendor exceptions have already been handled, the purpose as a whole is allowed if it's not being enforced + or the user has consented. Similar with vendors. + */ + const purposeAllowed = rule.enforcePurpose === false || purposeConsent === 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 + LI for Basic Ads (Purpose 2). Prebid.js can't check to see who's declaring what legal basis, so if LI has been + established for Purpose 2, allow the auction to take place and let the server sort out the legal basis calculation. + */ + if (purposeId === 2) { + return (purposeAllowed && vendorAllowed) || (liTransparency === true); + } + return purposeAllowed && vendorAllowed; } @@ -65,22 +123,25 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { const consentData = gdprDataHandler.getConsentData(); if (consentData && consentData.gdprApplies) { if (consentData.apiVersion === 2) { - if (!gvlid) { - gvlid = getGvlid(); + 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) { + gvlid = getGvlid(curBidder); + } else { + gvlid = getGvlid(moduleName); } - const curModule = moduleName || config.getCurrentBidder(); - const purpose1Rule = find(enforcementRules, hasPurpose1); + const curModule = moduleName || curBidder; let isAllowed = validateRules(purpose1Rule, consentData, curModule, gvlid); if (isAllowed) { result.valid = true; fn.call(this, gvlid, moduleName, result); } else { - utils.logWarn(`User denied Permission for Device access for ${curModule}`); + curModule && utils.logWarn(`Device access denied for ${curModule} by TCF2`); result.valid = false; fn.call(this, gvlid, moduleName, result); } } else { - utils.logInfo('Enforcing TCF2 only'); + // The module doesn't enforce TCF1.1 strings result.valid = true; fn.call(this, gvlid, moduleName, result); } @@ -102,19 +163,14 @@ export function userSyncHook(fn, ...args) { if (consentData.apiVersion === 2) { const gvlid = getGvlid(); const curBidder = config.getCurrentBidder(); - if (gvlid) { - const purpose1Rule = find(enforcementRules, hasPurpose1); - let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); - if (isAllowed) { - fn.call(this, ...args); - } else { - utils.logWarn(`User sync not allowed for ${curBidder}`); - } + let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); + if (isAllowed) { + fn.call(this, ...args); } else { utils.logWarn(`User sync not allowed for ${curBidder}`); } } else { - utils.logInfo('Enforcing TCF2 only'); + // The module doesn't enforce TCF1.1 strings fn.call(this, ...args); } } else { @@ -132,16 +188,11 @@ export function userIdHook(fn, submodules, consentData) { if (consentData && consentData.gdprApplies) { if (consentData.apiVersion === 2) { let userIdModules = submodules.map((submodule) => { - const gvlid = submodule.submodule.gvlid; + const gvlid = getGvlidForUserIdModule(submodule.submodule); const moduleName = submodule.submodule.name; - if (gvlid) { - const purpose1Rule = find(enforcementRules, hasPurpose1); - let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid); - if (isAllowed) { - return submodule; - } else { - utils.logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); - } + let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid); + if (isAllowed) { + return submodule; } else { utils.logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); } @@ -149,7 +200,7 @@ export function userIdHook(fn, submodules, consentData) { }).filter(module => module) fn.call(this, userIdModules, {...consentData, hasValidated: true}); } else { - utils.logInfo('Enforcing TCF2 only'); + // The module doesn't enforce TCF1.1 strings fn.call(this, submodules, consentData); } } else { @@ -157,28 +208,78 @@ export function userIdHook(fn, submodules, consentData) { } } -const hasPurpose1 = (rule) => { return rule.purpose === purpose1 } +/** + * Checks if a bidder is allowed in Auction. + * Enforces "purpose 2 (basic ads)" of TCF v2.0 spec + * @param {Function} fn - Function reference to the original function. + * @param {Array} adUnits + */ +export function makeBidRequestsHook(fn, adUnits, ...args) { + const consentData = gdprDataHandler.getConsentData(); + if (consentData && consentData.gdprApplies) { + if (consentData.apiVersion === 2) { + const disabledBidders = []; + adUnits.forEach(adUnit => { + adUnit.bids = adUnit.bids.filter(bid => { + const currBidder = bid.bidder; + const gvlId = getGvlid(currBidder); + if (includes(disabledBidders, currBidder)) return false; + const isAllowed = !!validateRules(purpose2Rule, consentData, currBidder, gvlId); + if (!isAllowed) { + utils.logWarn(`TCF2 blocked auction for ${currBidder}`); + events.emit(EVENTS.BIDDER_BLOCKED, currBidder); + disabledBidders.push(currBidder); + } + return isAllowed; + }); + }); + fn.call(this, adUnits, ...args); + } else { + // The module doesn't enforce TCF1.1 strings + fn.call(this, adUnits, ...args); + } + } else { + fn.call(this, adUnits, ...args); + } +} + +const hasPurpose1 = (rule) => { return rule.purpose === TCF2.purpose1.name } +const hasPurpose2 = (rule) => { return rule.purpose === TCF2.purpose2.name } /** - * A configuration function that initializes some module variables, as well as add hooks - * @param {Object} config GDPR enforcement config object + * A configuration function that initializes some module variables, as well as adds hooks + * @param {Object} config - GDPR enforcement config object */ export function setEnforcementConfig(config) { const rules = utils.deepAccess(config, 'gdpr.rules'); if (!rules) { - utils.logWarn('GDPR enforcement rules not defined, exiting enforcement module'); - return; + utils.logWarn('TCF2: enforcing P1 and P2'); + enforcementRules = DEFAULT_RULES; + } else { + enforcementRules = rules; + } + + purpose1Rule = find(enforcementRules, hasPurpose1); + purpose2Rule = find(enforcementRules, hasPurpose2); + + if (!purpose1Rule) { + purpose1Rule = DEFAULT_RULES[0]; } - enforcementRules = rules; - const hasDefinedPurpose1 = find(enforcementRules, hasPurpose1); - if (hasDefinedPurpose1 && !addedDeviceAccessHook) { + if (!purpose2Rule) { + purpose2Rule = DEFAULT_RULES[1]; + } + + if (purpose1Rule && !addedDeviceAccessHook) { addedDeviceAccessHook = true; validateStorageEnforcement.before(deviceAccessHook, 49); registerSyncInner.before(userSyncHook, 48); // Using getHook as user id and gdprEnforcement are both optional modules. Using import will auto include the file in build getHook('validateGdprEnforcement').before(userIdHook, 47); } + if (purpose2Rule) { + getHook('makeBidRequests').before(makeBidRequestsHook); + } } config.getConfig('consentManagement', config => setEnforcementConfig(config.consentManagement)); diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index b54114c176e..77d4220e59a 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -128,6 +128,9 @@ function parseBid(rawBid, currency, bidRequest) { bid.meta.networkId = utils.deepAccess(rawBid, 'ext.dspid'); bid.meta.brandId = utils.deepAccess(rawBid, 'ext.advbrandid'); bid.meta.brandName = utils.deepAccess(rawBid, 'ext.advbrand'); + if (rawBid.adomain && rawBid.adomain.length > 0) { + bid.meta.advertiserDomains = rawBid.adomain; + } return bid; } diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a5dcf1b1d3b..273684c86a5 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -240,27 +240,6 @@ function doClientSideSyncs(bidders) { }); } -function _getDigiTrustQueryParams(bidRequest = {}) { - function getDigiTrustId(bidRequest) { - const bidRequestDigitrust = utils.deepAccess(bidRequest, 'bids.0.userId.digitrustid.data'); - if (bidRequestDigitrust) { - return bidRequestDigitrust; - } - - const digiTrustUser = config.getConfig('digiTrustId'); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; - } - let digiTrustId = getDigiTrustId(bidRequest); - // Verify there is an ID and this user has not opted out - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return null; - } - return { - id: digiTrustId.id, - keyv: digiTrustId.keyv - }; -} - function _appendSiteAppDevice(request, pageUrl) { if (!request) return; @@ -627,11 +606,6 @@ const OPEN_RTB_PROTOCOL = { _appendSiteAppDevice(request, firstBidRequest.refererInfo.referer); - const digiTrust = _getDigiTrustQueryParams(firstBidRequest); - if (digiTrust) { - utils.deepSetValue(request, 'user.ext.digitrust', digiTrust); - } - // pass schain object if it is present const schain = utils.deepAccess(bidRequests, '0.bids.0.schain'); if (schain) { diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index 8e2be1207f5..9516934de42 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -7,11 +7,14 @@ import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; +import {getStorageManager} from '../src/storageManager.js'; const PUB_COMMON_ID = 'PublisherCommonId'; const MODULE_NAME = 'pubCommonId'; +const storage = getStorageManager(null, 'pubCommonId'); + /** @type {Submodule} */ export const pubCommonIdSubmodule = { /** @@ -90,6 +93,33 @@ export const pubCommonIdSubmodule = { const callback = this.makeCallback(pixelUrl, storedId); return callback ? {callback: callback} : {id: storedId}; } + }, + + /** + * @param {string} domain + * @param {HTMLDocument} document + * @return {(string|undefined)} + */ + domainOverride: function () { + const domainElements = document.domain.split('.'); + const cookieName = `_gd${Date.now()}`; + for (let i = 0, topDomain; i < domainElements.length; i++) { + const nextDomain = domainElements.slice(i).join('.'); + + // write test cookie + storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); + + // read test cookie to verify domain was valid + if (storage.getCookie(cookieName) === '1') { + // delete test cookie + storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); + // cookie was written successfully using test domain so the topDomain is updated + topDomain = nextDomain; + } else { + // cookie failed to write using test domain so exit by returning the topDomain + return topDomain; + } + } } }; diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index a050d499640..2e52fd27cf1 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -986,6 +986,7 @@ export const spec = { newBid.meta.buyerId = bid.ext.advid; } if (bid.adomain && bid.adomain.length > 0) { + newBid.meta.advertiserDomains = bid.adomain; newBid.meta.clickUrl = bid.adomain[0]; } diff --git a/modules/quantumBidAdapter.js b/modules/quantumBidAdapter.js deleted file mode 100644 index f5da6e022f1..00000000000 --- a/modules/quantumBidAdapter.js +++ /dev/null @@ -1,320 +0,0 @@ -import * as utils from '../src/utils.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'quantum'; -const ENDPOINT_URL = 'https://s.sspqns.com/hb'; -export const spec = { - code: BIDDER_CODE, - aliases: ['quantx', 'qtx'], // short code - supportedMediaTypes: [BANNER, NATIVE], - - /** - * 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 && bid.params.placementId); - }, - /** - * 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 (bidRequests, bidderRequest) { - return bidRequests.map(bid => { - const qtxRequest = {}; - let bidId = ''; - - const params = bid.params; - let placementId = params.placementId; - - let devEnpoint = false; - if (params.useDev && params.useDev === '1') { - devEnpoint = 'https://sdev.sspqns.com/hb'; - } - let renderMode = 'native'; - for (let i = 0; i < bid.sizes.length; i++) { - if (bid.sizes[i][0] > 1 && bid.sizes[i][1] > 1) { - renderMode = 'banner'; - break; - } - } - - let mediaType = (bid.mediaType === 'native' || utils.deepAccess(bid, 'mediaTypes.native')) ? 'native' : 'banner'; - - if (mediaType === 'native') { - renderMode = 'native'; - } - - if (!bidId) { - bidId = bid.bidId; - } - qtxRequest.auid = placementId; - - if (bidderRequest && bidderRequest.gdprConsent) { - qtxRequest.quantx_user_consent_string = bidderRequest.gdprConsent.consentString; - qtxRequest.quantx_gdpr = bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0; - }; - - const url = devEnpoint || ENDPOINT_URL; - - return { - method: 'GET', - bidId: bidId, - sizes: bid.sizes, - mediaType: mediaType, - renderMode: renderMode, - url: url, - 'data': qtxRequest, - bidderRequest - }; - }); - }, - /** - * 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 responseCPM; - let bid = {}; - let id = bidRequest.bidId; - - if (serverBody.price && serverBody.price !== 0) { - responseCPM = parseFloat(serverBody.price); - - bid.creativeId = serverBody.creative_id || '0'; - bid.cpm = responseCPM; - bid.requestId = bidRequest.bidId; - bid.width = 1; - bid.height = 1; - bid.ttl = 200; - bid.netRevenue = true; - bid.currency = 'USD'; - - if (serverBody.native) { - bid.native = serverBody.native; - } - if (serverBody.cobj) { - bid.cobj = serverBody.cobj; - } - if (!utils.isEmpty(bidRequest.sizes)) { - bid.width = bidRequest.sizes[0][0]; - bid.height = bidRequest.sizes[0][1]; - } - - bid.nurl = serverBody.nurl; - bid.sync = serverBody.sync; - if (bidRequest.renderMode && bidRequest.renderMode === 'banner') { - bid.mediaType = 'banner'; - if (serverBody.native) { - const adAssetsUrl = 'https://cdn.elasticad.net/native/serve/js/quantx/quantumAd/'; - let assets = serverBody.native.assets; - let link = serverBody.native.link; - - let trackers = []; - if (serverBody.native.imptrackers) { - trackers = serverBody.native.imptrackers; - } - - let jstracker = ''; - if (serverBody.native.jstracker) { - jstracker = encodeURIComponent(serverBody.native.jstracker); - } - - if (serverBody.nurl) { - trackers.push(serverBody.nurl); - } - - let ad = {}; - ad['trackers'] = trackers; - ad['jstrackers'] = jstracker; - ad['eventtrackers'] = serverBody.native.eventtrackers || []; - - for (let i = 0; i < assets.length; i++) { - let asset = assets[i]; - switch (asset['id']) { - case 1: - ad['title'] = asset['title']['text']; - break; - case 2: - ad['sponsor_logo'] = asset['img']['url']; - break; - case 3: - ad['content'] = asset['data']['value']; - break; - case 4: - ad['main_image'] = asset['img']['url']; - break; - case 6: - ad['teaser_type'] = 'vast'; - ad['video_url'] = asset['video']['vasttag']; - break; - case 10: - ad['sponsor_name'] = asset['data']['value']; - break; - case 2001: - ad['expanded_content_type'] = 'embed'; - ad['expanded_summary'] = asset['data']['value']; - break; - case 2002: - ad['expanded_content_type'] = 'vast'; - ad['expanded_summary'] = asset['data']['value']; - break; - case 2003: - ad['sponsor_url'] = asset['data']['value']; - break; - case 2004: // prism - ad['content_type'] = 'prism'; - break; - case 2005: // internal_landing_page - ad['content_type'] = 'internal_landing_page'; - ad['internal_content_link'] = asset['data']['value']; - break; - case 2006: // teaser as vast - ad['teaser_type'] = 'vast'; - ad['video_url'] = asset['data']['value']; - break; - case 2007: - ad['autoexpand_content_type'] = asset['data']['value']; - break; - case 2022: // content page - ad['content_type'] = 'full_text'; - ad['full_text'] = asset['data']['value']; - break; - } - } - - ad['action_url'] = link.url; - - if (!ad['sponsor_url']) { - ad['sponsor_url'] = ad['action_url']; - } - - ad['clicktrackers'] = []; - if (link.clicktrackers) { - ad['clicktrackers'] = link.clicktrackers; - } - - ad['main_image'] = 'https://resize-ssp.adux.com/scalecrop-290x130/' + window.btoa(ad['main_image']) + '/external'; - - bid.ad = '
' + - '
' + - '
' + - ' ' + - ' ' + - '
' + - '

' + ad['title'] + '

' + - '

' + ad['content'] + ' 

' + - ' ' + - '
' + - '' + - '' + - '' + - '
'; - } - } else { - // native - bid.mediaType = 'native'; - if (bidRequest.mediaType === 'native') { - if (serverBody.native) { - let assets = serverBody.native.assets; - let link = serverBody.native.link; - - let trackers = []; - if (serverBody.native.imptrackers) { - trackers = serverBody.native.imptrackers; - } - - if (serverBody.nurl) { - trackers.push(serverBody.nurl); - } - - let native = {}; - - for (let i = 0; i < assets.length; i++) { - let asset = assets[i]; - switch (asset['id']) { - case 1: - native.title = asset['title']['text']; - break; - case 2: - native.icon = { - url: asset['img']['url'], - width: asset['img']['w'], - height: asset['img']['h'] - }; - break; - case 3: - native.body = asset['data']['value']; - break; - case 4: - native.image = { - url: asset['img']['url'], - width: asset['img']['w'], - height: asset['img']['h'] - }; - break; - case 10: - native.sponsoredBy = asset['data']['value']; - break; - } - } - native.cta = 'read more'; - if (serverBody.language) { - native.cta = 'read more'; - } - - native.clickUrl = link.url; - native.impressionTrackers = trackers; - if (link.clicktrackers) { - native.clickTrackers = link.clicktrackers; - } - native.eventtrackers = native.eventtrackers || []; - - bid.qtx_native = utils.deepClone(serverBody.native); - bid.native = native; - } - } - } - bidResponses.push(bid); - } - - return bidResponses; - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse} serverResponse A successful response from the server - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function (syncOptions, serverResponse) { - const syncs = []; - utils._each(serverResponse, function(serverResponse) { - if (serverResponse.body && serverResponse.body.sync) { - utils._each(serverResponse.body.sync, function (pixel) { - syncs.push({ - type: 'image', - url: pixel - }); - }); - } - }); - return syncs; - } -} -registerBidder(spec); diff --git a/modules/quantumBidAdapter.md b/modules/quantumBidAdapter.md deleted file mode 100644 index 572ca9ecd37..00000000000 --- a/modules/quantumBidAdapter.md +++ /dev/null @@ -1,94 +0,0 @@ -# Overview - -``` -Module Name: Quantum Advertising Bid Adapter -Module Type: Bidder Adapter -Maintainer: support.mediareporting@adux.com -``` - -# Description - -Connects to Quantum's ssp for bids. - -# Sample Ad Unit: For Publishers -``` -var adUnits = [{ - code: 'quantum-adUnit-id-1', - sizes: [[300, 250]], - bids: [{ - bidder: 'quantum', - params: { - placementId: 21546 //quantum adUnit id - } - }] - },{ - code: 'quantum-native-adUnit-id-1', - sizes: [[0, 0]], - mediaTypes: 'native', - bids: [{ - bidder: 'quantum', - params: { - placementId: 21546 //quantum adUnit id - } - }] - }]; -``` - -# Ad Unit and Setup: For Testing - -``` - - - - - - - - - - ``` diff --git a/modules/serverbidBidAdapter.md b/modules/serverbidBidAdapter.md deleted file mode 100644 index 87b51e665e2..00000000000 --- a/modules/serverbidBidAdapter.md +++ /dev/null @@ -1,44 +0,0 @@ -# Overview - -Module Name: Serverbid Bid Adapter - -Module Type: Bid Adapter - -Maintainer: jgrimes@serverbid.com, jswart@serverbid.com - -# Description - -Connects to Serverbid for receiving bids from configured demand sources. - -# Test Parameters -```javascript - var adUnits = [ - { - code: 'test-ad-1', - sizes: [[300, 250]], - bids: [ - { - bidder: 'serverbid', - params: { - networkId: '9969', - siteId: '980639' - } - } - ] - }, - { - code: 'test-ad-2', - sizes: [[300, 250]], - bids: [ - { - bidder: 'serverbid', - params: { - networkId: '9969', - siteId: '980639', - zoneIds: [178503] - } - } - ] - } - ]; -``` diff --git a/modules/sharedIdSystem.md b/modules/sharedIdSystem.md index acb076ed97f..a4541c16c49 100644 --- a/modules/sharedIdSystem.md +++ b/modules/sharedIdSystem.md @@ -13,7 +13,7 @@ ex: $ gulp build --modules=userId,sharedIdSystem Individual params may be set for the Shared ID User ID Submodule. ``` pbjs.setConfig({ - usersync: { + userSync: { userIds: [{ name: 'sharedId', params: { diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 1733a176eba..a8d874c57e9 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -331,6 +331,11 @@ export const spec = { height: spotxBid.h }; + bid.meta = bid.meta || {}; + if (spotxBid && spotxBid.adomain && spotxBid.adomain.length > 0) { + bid.meta.advertiserDomains = spotxBid.adomain; + } + const context1 = utils.deepAccess(currentBidRequest, 'mediaTypes.video.context'); const context2 = utils.deepAccess(currentBidRequest, 'params.ad_unit'); if (context1 == 'outstream' || context2 == 'outstream') { diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js index 74c97f34b74..acc20f6b183 100644 --- a/modules/telariaBidAdapter.js +++ b/modules/telariaBidAdapter.js @@ -284,6 +284,11 @@ function createBid(status, reqBid, response, width, height, bidderCode) { }); } + bid.meta = bid.meta || {}; + if (response && response.adomain && response.adomain.length > 0) { + bid.meta.advertiserDomains = response.adomain; + } + return bid; } diff --git a/modules/userId/eids.js b/modules/userId/eids.js index d3f32fbdd31..c14777a8888 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -74,19 +74,6 @@ const USER_IDS_CONFIG = { atype: 1, }, - // DigiTrust - 'digitrustid': { - getValue: function (data) { - var id = null; - if (data && data.data && data.data.id != null) { - id = data.data.id; - } - return id; - }, - source: 'digitru.st', - atype: 1 - }, - // criteo 'criteoId': { source: 'criteo.com', diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 60f450e9328..f42b7e61a52 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -63,14 +63,6 @@ userIdAsEids = [ }] }, - { - source: 'digitru.st', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] - }, - { source: 'criteo.com', uids: [{ diff --git a/modules/userId/index.js b/modules/userId/index.js index 1c5768edae1..2a37723e3a0 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -159,17 +159,23 @@ export function setSubmoduleRegistry(submodules) { } /** - * @param {SubmoduleStorage} storage + * @param {SubmoduleContainer} submodule * @param {(Object|string)} value */ -function setStoredValue(storage, value) { +export function setStoredValue(submodule, value) { + /** + * @type {SubmoduleStorage} + */ + const storage = submodule.config.storage; + const domainOverride = (typeof submodule.submodule.domainOverride === 'function') ? submodule.submodule.domainOverride() : null; + try { const valueStr = utils.isPlainObject(value) ? JSON.stringify(value) : value; const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); if (storage.type === COOKIE) { - coreStorage.setCookie(storage.name, valueStr, expiresStr, 'Lax'); + coreStorage.setCookie(storage.name, valueStr, expiresStr, 'Lax', domainOverride); if (typeof storage.refreshInSeconds === 'number') { - coreStorage.setCookie(`${storage.name}_last`, new Date().toUTCString(), expiresStr); + coreStorage.setCookie(`${storage.name}_last`, new Date().toUTCString(), expiresStr, 'Lax', domainOverride); } } else if (storage.type === LOCAL_STORAGE) { coreStorage.setDataInLocalStorage(`${storage.name}_exp`, expiresStr); @@ -246,7 +252,7 @@ function processSubmoduleCallbacks(submodules, cb) { // if valid, id data should be saved to cookie/html storage if (idObj) { if (submodule.config.storage) { - setStoredValue(submodule.config.storage, idObj); + setStoredValue(submodule, idObj); } // cache decoded value (this is copied to every adUnit bid) submodule.idObj = submodule.submodule.decode(idObj); @@ -437,7 +443,7 @@ function initSubmodules(submodules, consentData) { if (utils.isPlainObject(response)) { if (response.id) { // A getId/extendId result assumed to be valid user id data, which should be saved to users local storage or cookies - setStoredValue(submodule.config.storage, response.id); + setStoredValue(submodule, response.id); storedId = response.id; } @@ -570,8 +576,8 @@ export function init(config) { } // listen for config userSyncs to be set config.getConfig(conf => { - // Note: support for both 'userSync' and 'usersync' will be deprecated with Prebid.js 3.0 - const userSync = conf.userSync || conf.usersync; + // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 + const userSync = conf.userSync; if (userSync && userSync.userIds) { configRegistry = userSync.userIds; syncDelay = utils.isNumber(userSync.syncDelay) ? userSync.syncDelay : DEFAULT_SYNC_DELAY; diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 566d719c90d..9d835e23fca 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -80,7 +80,7 @@ pbjs.setConfig({ Example showing `localStorage` for user id data for some submodules ``` pbjs.setConfig({ - usersync: { + userSync: { userIds: [{ name: "unifiedId", params: { @@ -138,7 +138,7 @@ pbjs.setConfig({ Example showing how to configure a `value` object to pass directly to bid adapters ``` pbjs.setConfig({ - usersync: { + userSync: { userIds: [{ name: "pubCommonId", value: { diff --git a/modules/userIdTargeting.md b/modules/userIdTargeting.md index f99fd5308b3..340c1b6abf2 100644 --- a/modules/userIdTargeting.md +++ b/modules/userIdTargeting.md @@ -8,7 +8,7 @@ pbjs.setConfig({ // your existing userIds config - usersync: { + userSync: { userIds: [{...}, ...] }, diff --git a/src/adapterManager.js b/src/adapterManager.js index 2108bb7a4f6..06ccba9787e 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -426,7 +426,7 @@ adapterManager.registerBidAdapter = function (bidAdaptor, bidderCode, {supported } }; -adapterManager.aliasBidAdapter = function (bidderCode, alias) { +adapterManager.aliasBidAdapter = function (bidderCode, alias, options) { let existingAlias = _bidderRegistry[alias]; if (typeof existingAlias === 'undefined') { @@ -452,7 +452,8 @@ adapterManager.aliasBidAdapter = function (bidderCode, alias) { newAdapter.setBidderCode(alias); } else { let spec = bidAdaptor.getSpec(); - newAdapter = newBidder(Object.assign({}, spec, { code: alias })); + let gvlid = options && options.gvlid; + newAdapter = newBidder(Object.assign({}, spec, { code: alias, gvlid })); _aliasRegistry[alias] = bidderCode; } adapterManager.registerBidAdapter(newAdapter, alias, { diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 8dd351563be..3b6260efc88 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -9,7 +9,7 @@ import CONSTANTS from '../constants.json'; import events from '../events.js'; import includes from 'core-js-pure/features/array/includes.js'; import { ajax } from '../ajax.js'; -import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, deepAccess, isArray } from '../utils.js'; +import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, deepAccess, isArray, isPlainObject } from '../utils.js'; import { ADPOD } from '../mediaTypes.js'; import { getHook, hook } from '../hook.js'; import { getCoreStorageManager } from '../storageManager.js'; @@ -153,8 +153,14 @@ export function registerBidder(spec) { putBidder(spec); if (Array.isArray(spec.aliases)) { spec.aliases.forEach(alias => { - adapterManager.aliasRegistry[alias] = spec.code; - putBidder(Object.assign({}, spec, { code: alias })); + let aliasCode = alias; + let gvlid; + if (isPlainObject(alias)) { + aliasCode = alias.code; + gvlid = alias.gvlid; + } + adapterManager.aliasRegistry[aliasCode] = spec.code; + putBidder(Object.assign({}, spec, { code: aliasCode, gvlid })); }); } } @@ -307,6 +313,7 @@ export function newBidder(spec) { // creating a copy of original values as cpm and currency are modified later bid.originalCpm = bid.cpm; bid.originalCurrency = bid.currency; + bid.meta = bid.meta || Object.assign({}, bid[bidRequest.bidder]); const prebidBid = Object.assign(createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid); addBidWithCode(bidRequest.adUnitCode, prebidBid); } else { diff --git a/src/constants.json b/src/constants.json index 5965d77a6c4..946c43754d5 100644 --- a/src/constants.json +++ b/src/constants.json @@ -36,7 +36,8 @@ "BEFORE_REQUEST_BIDS": "beforeRequestBids", "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", - "AD_RENDER_FAILED" : "adRenderFailed" + "AD_RENDER_FAILED" : "adRenderFailed", + "BIDDER_BLOCKED": "bidderBlocked" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocuemnt", diff --git a/src/prebid.js b/src/prebid.js index f3ea64d1e83..67402a995cf 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -679,10 +679,10 @@ $$PREBID_GLOBAL$$.enableAnalytics = function (config) { /** * @alias module:pbjs.aliasBidder */ -$$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias) { +$$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias, options) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.aliasBidder', arguments); if (bidderCode && alias) { - adapterManager.aliasBidAdapter(bidderCode, alias); + adapterManager.aliasBidAdapter(bidderCode, alias, options); } else { utils.logError('bidderCode and alias must be passed as arguments', '$$PREBID_GLOBAL$$.aliasBidder'); } diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js deleted file mode 100644 index 04694731981..00000000000 --- a/test/spec/modules/audienceNetworkBidAdapter_spec.js +++ /dev/null @@ -1,568 +0,0 @@ -/** - * @file Tests for AudienceNetwork adapter. - */ -import { expect } from 'chai'; - -import { spec } from 'modules/audienceNetworkBidAdapter.js'; -import * as utils from 'src/utils.js'; - -const { - code, - supportedMediaTypes, - isBidRequestValid, - buildRequests, - interpretResponse -} = spec; - -const bidder = 'audienceNetwork'; -const placementId = 'test-placement-id'; -const playerwidth = 320; -const playerheight = 180; -const requestId = 'test-request-id'; -const debug = 'adapterver=1.3.0&platform=241394079772386&platver=$prebid.version$&cb=test-uuid'; -const expectedPageUrl = encodeURIComponent('http://www.prebid-test.com/audienceNetworkTest.html?pbjs_debug=true'); -const mockBidderRequest = { - bidderCode: 'audienceNetwork', - auctionId: '720146a0-8f32-4db0-bb30-21f1d057efb4', - bidderRequestId: '1312d48561657e', - auctionStart: 1579711852920, - timeout: 3000, - refererInfo: { - referer: 'http://www.prebid-test.com/audienceNetworkTest.html?pbjs_debug=true', - reachedTop: true, - numIframes: 0, - stack: ['http://www.prebid-test.com/audienceNetworkTest.html?pbjs_debug=true'], - canonicalUrl: undefined - }, - start: 1579711852925 -}; - -describe('AudienceNetwork adapter', function () { - describe('Public API', function () { - it('code', function () { - expect(code).to.equal(bidder); - }); - it('supportedMediaTypes', function () { - expect(supportedMediaTypes).to.deep.equal(['banner', 'video']); - }); - it('isBidRequestValid', function () { - expect(isBidRequestValid).to.be.a('function'); - }); - it('buildRequests', function () { - expect(buildRequests).to.be.a('function'); - }); - it('interpretResponse', function () { - expect(interpretResponse).to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('missing placementId parameter', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[300, 250]] - })).to.equal(false); - }); - - it('invalid sizes parameter', function () { - expect(isBidRequestValid({ - bidder, - sizes: ['', undefined, null, '300x100', [300, 100], [300], {}], - params: { placementId } - })).to.equal(false); - }); - - it('valid when at least one valid size', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[1, 1], [300, 250]], - params: { placementId } - })).to.equal(true); - }); - - it('valid parameters', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[300, 250], [320, 50]], - params: { placementId } - })).to.equal(true); - }); - - it('fullwidth', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[300, 250], [336, 280]], - params: { - placementId, - format: 'fullwidth' - } - })).to.equal(true); - }); - - it('native', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[300, 250]], - params: { - placementId, - format: 'native' - } - })).to.equal(true); - }); - - it('native with non-IAB size', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[728, 90]], - params: { - placementId, - format: 'native' - } - })).to.equal(true); - }); - - it('video', function () { - expect(isBidRequestValid({ - bidder, - sizes: [[playerwidth, playerheight]], - params: { - placementId, - format: 'video' - } - })).to.equal(true); - }); - }); - - describe('buildRequests', function () { - before(function () { - sinon - .stub(utils, 'generateUUID') - .returns('test-uuid'); - }); - - after(function () { - utils.generateUUID.restore(); - }); - - it('takes the canonicalUrl as priority over referer for pageurl', function () { - let expectedCanonicalUrl = encodeURIComponent('http://www.this-is-canonical-url.com/hello-world.html?pbjs_debug=true'); - mockBidderRequest.refererInfo.canonicalUrl = 'http://www.this-is-canonical-url.com/hello-world.html?pbjs_debug=true'; - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[300, 50], [300, 250], [320, 50]], - params: { placementId } - }], mockBidderRequest)).to.deep.equal([{ - adformats: ['300x250'], - method: 'GET', - pageurl: expectedCanonicalUrl, - requestIds: [requestId], - sizes: ['300x250'], - url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${expectedCanonicalUrl}&sdk[]=6.0.web&${debug}` - }]); - mockBidderRequest.refererInfo.canonicalUrl = undefined; - }); - - it('can build URL for IAB unit', function () { - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[300, 50], [300, 250], [320, 50]], - params: { placementId } - }], mockBidderRequest)).to.deep.equal([{ - adformats: ['300x250'], - method: 'GET', - pageurl: expectedPageUrl, - requestIds: [requestId], - sizes: ['300x250'], - url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${expectedPageUrl}&sdk[]=6.0.web&${debug}` - }]); - }); - - it('can build URL for video unit', function () { - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[640, 480]], - params: { - placementId, - format: 'video' - } - }], mockBidderRequest)).to.deep.equal([{ - adformats: ['video'], - method: 'GET', - pageurl: expectedPageUrl, - requestIds: [requestId], - sizes: ['640x480'], - url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=${expectedPageUrl}&sdk[]=&${debug}&playerwidth=640&playerheight=480` - }]); - }); - - it('can build URL for native unit in non-IAB size', function () { - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[728, 90]], - params: { - placementId, - format: 'native' - } - }], mockBidderRequest)).to.deep.equal([{ - adformats: ['native'], - method: 'GET', - pageurl: expectedPageUrl, - requestIds: [requestId], - sizes: ['728x90'], - url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=${expectedPageUrl}&sdk[]=6.0.web&${debug}` - }]); - }); - - it('can build URL for deprecated fullwidth unit, overriding platform', function () { - const platform = 'test-platform'; - const debugPlatform = debug.replace('241394079772386', platform); - - expect(buildRequests([{ - bidder, - bidId: requestId, - sizes: [[300, 250]], - params: { - placementId, - platform, - format: 'fullwidth' - } - }], mockBidderRequest)).to.deep.equal([{ - adformats: ['300x250'], - method: 'GET', - pageurl: expectedPageUrl, - requestIds: [requestId], - sizes: ['300x250'], - url: 'https://an.facebook.com/v2/placementbid.json', - data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${expectedPageUrl}&sdk[]=6.0.web&${debugPlatform}` - }]); - }); - }); - - describe('interpretResponse', function () { - it('error in response', function () { - expect(interpretResponse({ - body: { - errors: ['test-error-message'] - } - }, {})).to.deep.equal([]); - }); - - it('valid native bid in response', function () { - const [bidResponse] = interpretResponse({ - body: { - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['native'], - requestIds: [requestId], - sizes: [[300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.requestId).to.equal(requestId); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); - expect(bidResponse.ad) - .to.contain(`placementid: '${placementId}',`) - .and.to.contain(`format: 'native',`) - .and.to.contain(`bidid: 'test-bid-id',`) - .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') - .and.to.contain('
', 'ad missing native container'); - expect(bidResponse.ttl).to.equal(600); - expect(bidResponse.creativeId).to.equal(placementId); - expect(bidResponse.netRevenue).to.equal(true); - expect(bidResponse.currency).to.equal('USD'); - - expect(bidResponse.hb_bidder).to.equal('fan'); - expect(bidResponse.fb_bidid).to.equal('test-bid-id'); - expect(bidResponse.fb_format).to.equal('native'); - expect(bidResponse.fb_placementid).to.equal(placementId); - }); - - it('valid IAB bid in response', function () { - const [bidResponse] = interpretResponse({ - body: { - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['300x250'], - requestIds: [requestId], - sizes: [[300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.requestId).to.equal(requestId); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); - expect(bidResponse.ad) - .to.contain(`placementid: '${placementId}',`) - .and.to.contain(`format: '300x250',`) - .and.to.contain(`bidid: 'test-bid-id',`) - .and.not.to.contain('getElementsByTagName("style")', 'ad should not contain native styles') - .and.not.to.contain('
', 'ad should not contain native container'); - expect(bidResponse.ttl).to.equal(600); - expect(bidResponse.creativeId).to.equal(placementId); - expect(bidResponse.netRevenue).to.equal(true); - expect(bidResponse.currency).to.equal('USD'); - expect(bidResponse.hb_bidder).to.equal('fan'); - expect(bidResponse.fb_bidid).to.equal('test-bid-id'); - expect(bidResponse.fb_format).to.equal('300x250'); - expect(bidResponse.fb_placementid).to.equal(placementId); - }); - - it('filters invalid slot sizes', function () { - const [bidResponse] = interpretResponse({ - body: { - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['300x250'], - requestIds: [requestId], - sizes: [[300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.requestId).to.equal(requestId); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); - expect(bidResponse.ttl).to.equal(600); - expect(bidResponse.creativeId).to.equal(placementId); - expect(bidResponse.netRevenue).to.equal(true); - expect(bidResponse.currency).to.equal('USD'); - expect(bidResponse.hb_bidder).to.equal('fan'); - expect(bidResponse.fb_bidid).to.equal('test-bid-id'); - expect(bidResponse.fb_format).to.equal('300x250'); - expect(bidResponse.fb_placementid).to.equal(placementId); - }); - - it('valid multiple bids in response', function () { - const placementIdNative = 'test-placement-id-native'; - const placementIdIab = 'test-placement-id-iab'; - - const [bidResponseNative, bidResponseIab] = interpretResponse({ - body: { - errors: [], - bids: { - [placementIdNative]: [{ - placement_id: placementIdNative, - bid_id: 'test-bid-id-native', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }], - [placementIdIab]: [{ - placement_id: placementIdIab, - bid_id: 'test-bid-id-iab', - bid_price_cents: 456, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['native', '300x250'], - requestIds: [requestId, requestId], - sizes: ['300x250', [300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponseNative.cpm).to.equal(1.23); - expect(bidResponseNative.requestId).to.equal(requestId); - expect(bidResponseNative.width).to.equal(300); - expect(bidResponseNative.height).to.equal(250); - expect(bidResponseNative.ad) - .to.contain(`placementid: '${placementIdNative}',`) - .and.to.contain(`format: 'native',`) - .and.to.contain(`bidid: 'test-bid-id-native',`); - expect(bidResponseNative.ttl).to.equal(600); - expect(bidResponseNative.creativeId).to.equal(placementIdNative); - expect(bidResponseNative.netRevenue).to.equal(true); - expect(bidResponseNative.currency).to.equal('USD'); - expect(bidResponseNative.hb_bidder).to.equal('fan'); - expect(bidResponseNative.fb_bidid).to.equal('test-bid-id-native'); - expect(bidResponseNative.fb_format).to.equal('native'); - expect(bidResponseNative.fb_placementid).to.equal(placementIdNative); - - expect(bidResponseIab.cpm).to.equal(4.56); - expect(bidResponseIab.requestId).to.equal(requestId); - expect(bidResponseIab.width).to.equal(300); - expect(bidResponseIab.height).to.equal(250); - expect(bidResponseIab.ad) - .to.contain(`placementid: '${placementIdIab}',`) - .and.to.contain(`format: '300x250',`) - .and.to.contain(`bidid: 'test-bid-id-iab',`); - expect(bidResponseIab.ttl).to.equal(600); - expect(bidResponseIab.creativeId).to.equal(placementIdIab); - expect(bidResponseIab.netRevenue).to.equal(true); - expect(bidResponseIab.currency).to.equal('USD'); - expect(bidResponseIab.hb_bidder).to.equal('fan'); - expect(bidResponseIab.fb_bidid).to.equal('test-bid-id-iab'); - expect(bidResponseIab.fb_format).to.equal('300x250'); - expect(bidResponseIab.fb_placementid).to.equal(placementIdIab); - }); - - it('valid video bid in response', function () { - const bidId = 'test-bid-id-video'; - - const [bidResponse] = interpretResponse({ - body: { - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: bidId, - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['video'], - requestIds: [requestId], - sizes: [[playerwidth, playerheight]], - pageurl: expectedPageUrl - }); - - expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.requestId).to.equal(requestId); - expect(bidResponse.ttl).to.equal(3600); - expect(bidResponse.mediaType).to.equal('video'); - expect(bidResponse.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${placementId}&pageurl=${expectedPageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${bidId}`); - expect(bidResponse.width).to.equal(playerwidth); - expect(bidResponse.height).to.equal(playerheight); - }); - - it('mixed video and native bids', function () { - const videoPlacementId = 'test-video-placement-id'; - const videoBidId = 'test-video-bid-id'; - const nativePlacementId = 'test-native-placement-id'; - const nativeBidId = 'test-native-bid-id'; - - const [bidResponseVideo, bidResponseNative] = interpretResponse({ - body: { - errors: [], - bids: { - [videoPlacementId]: [{ - placement_id: videoPlacementId, - bid_id: videoBidId, - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }], - [nativePlacementId]: [{ - placement_id: nativePlacementId, - bid_id: nativeBidId, - bid_price_cents: 456, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['video', 'native'], - requestIds: [requestId, requestId], - sizes: [[playerwidth, playerheight], [300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponseVideo.cpm).to.equal(1.23); - expect(bidResponseVideo.requestId).to.equal(requestId); - expect(bidResponseVideo.ttl).to.equal(3600); - expect(bidResponseVideo.mediaType).to.equal('video'); - expect(bidResponseVideo.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${videoPlacementId}&pageurl=${expectedPageUrl}&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${videoBidId}`); - expect(bidResponseVideo.width).to.equal(playerwidth); - expect(bidResponseVideo.height).to.equal(playerheight); - - expect(bidResponseNative.cpm).to.equal(4.56); - expect(bidResponseNative.requestId).to.equal(requestId); - expect(bidResponseNative.ttl).to.equal(600); - expect(bidResponseNative.width).to.equal(300); - expect(bidResponseNative.height).to.equal(250); - expect(bidResponseNative.ad) - .to.contain(`placementid: '${nativePlacementId}',`) - .and.to.contain(`format: 'native',`) - .and.to.contain(`bidid: '${nativeBidId}',`); - }); - - it('mixture of valid native bid and error in response', function () { - const [bidResponse] = interpretResponse({ - body: { - errors: ['test-error-message'], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] - } - } - }, { - adformats: ['native'], - requestIds: [requestId], - sizes: [[300, 250]], - pageurl: expectedPageUrl - }); - - expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.requestId).to.equal(requestId); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); - expect(bidResponse.ad) - .to.contain(`placementid: '${placementId}',`) - .and.to.contain(`format: 'native',`) - .and.to.contain(`bidid: 'test-bid-id',`) - .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') - .and.to.contain('
', 'ad missing native container'); - expect(bidResponse.ttl).to.equal(600); - expect(bidResponse.creativeId).to.equal(placementId); - expect(bidResponse.netRevenue).to.equal(true); - expect(bidResponse.currency).to.equal('USD'); - - expect(bidResponse.hb_bidder).to.equal('fan'); - expect(bidResponse.fb_bidid).to.equal('test-bid-id'); - expect(bidResponse.fb_format).to.equal('native'); - expect(bidResponse.fb_placementid).to.equal(placementId); - }); - }); -}); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index c4f6fe70dd1..3ebebfef1ee 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -24,9 +24,8 @@ describe('consentManagement', function () { setConsentConfig({}); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(10000); - expect(allowAuction).to.be.true; expect(gdprScope).to.be.equal(false); - sinon.assert.callCount(utils.logInfo, 4); + sinon.assert.callCount(utils.logInfo, 3); }); it('should exit consent manager if config is not an object', function () { @@ -58,7 +57,10 @@ describe('consentManagement', function () { setConsentConfig(allConfig); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(7500); - expect(allowAuction).to.be.false; + expect(allowAuction).to.deep.equal({ + value: false, + definedInConfig: true + }); expect(gdprScope).to.be.true; }); @@ -110,7 +112,10 @@ describe('consentManagement', function () { expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(3333); - expect(allowAuction).to.be.equal(false); + expect(allowAuction).to.deep.equal({ + value: false, + definedInConfig: true + }); expect(gdprScope).to.be.equal(false); }); }); @@ -164,7 +169,10 @@ describe('consentManagement', function () { setConsentConfig(staticConfig); expect(userCMP).to.be.equal('static'); expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used - expect(allowAuction).to.be.false; + expect(allowAuction).to.deep.equal({ + value: false, + definedInConfig: true + }); expect(staticConsentData).to.be.equal(staticConfig.consentData); }); @@ -244,7 +252,10 @@ describe('consentManagement', function () { setConsentConfig(staticConfig); expect(userCMP).to.be.equal('static'); expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used - expect(allowAuction).to.be.false; + expect(allowAuction).to.deep.equal({ + value: false, + definedInConfig: true + }); expect(gdprScope).to.be.equal(false); expect(staticConsentData).to.be.equal(staticConfig.consentData); }); @@ -423,7 +434,6 @@ describe('consentManagement', function () { setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { let consent = gdprDataHandler.getConsentData(); - sinon.assert.notCalled(utils.logWarn); sinon.assert.notCalled(utils.logError); expect(consent.consentString).to.equal(tarConsentString); expect(consent.gdprApplies).to.be.true; @@ -626,7 +636,6 @@ describe('consentManagement', function () { didHookReturn = true; }, {}); let consent = gdprDataHandler.getConsentData(); - sinon.assert.notCalled(utils.logWarn); sinon.assert.notCalled(utils.logError); expect(didHookReturn).to.be.true; expect(consent.consentString).to.equal(testConsentData.tcString); @@ -634,7 +643,7 @@ describe('consentManagement', function () { expect(consent.apiVersion).to.equal(2); }); - it('throws an error when processCmpData check failed while config had allowAuction set to false', function () { + it('throws an error when processCmpData check fails + does not call requestBids callbcack even when allowAuction is true', function () { let testConsentData = {}; let bidsBackHandlerReturn = false; @@ -642,7 +651,7 @@ describe('consentManagement', function () { args[2](testConsentData); }); - setConsentConfig(goodConfigWithCancelAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { didHookReturn = true; @@ -650,6 +659,7 @@ describe('consentManagement', function () { let consent = gdprDataHandler.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; @@ -676,34 +686,12 @@ describe('consentManagement', function () { didHookReturn = true; }, {}); let consent = gdprDataHandler.getConsentData(); - sinon.assert.notCalled(utils.logWarn); sinon.assert.notCalled(utils.logError); expect(didHookReturn).to.be.true; expect(consent.consentString).to.equal(testConsentData.tcString); expect(consent.gdprApplies).to.be.true; expect(consent.apiVersion).to.equal(2); }); - - it('throws a warning + stores consentData + calls callback when processCmpData check failed while config had allowAuction set to true', function () { - let testConsentData = {}; - - cmpStub = sinon.stub(window, '__tcfapi').callsFake((...args) => { - args[2](testConsentData); - }); - - setConsentConfig(goodConfigWithAllowAuction); - - requestBidsHook(() => { - didHookReturn = true; - }, {}); - let consent = gdprDataHandler.getConsentData(); - - sinon.assert.calledOnce(utils.logWarn); - expect(didHookReturn).to.be.true; - expect(consent.consentString).to.be.undefined; - expect(consent.gdprApplies).to.be.false; - expect(consent.apiVersion).to.equal(2); - }); }); }); }); diff --git a/test/spec/modules/digitrustIdSystem_spec.js b/test/spec/modules/digitrustIdSystem_spec.js deleted file mode 100644 index befd6eb75b6..00000000000 --- a/test/spec/modules/digitrustIdSystem_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import { - digiTrustIdSubmodule, - surfaceTestHook -} from 'modules/digiTrustIdSystem.js'; - -let assert = require('chai').assert; -let expect = require('chai').expect; -var testHook = null; - -/** -* A mock implementation of IAB Consent Provider -*/ -function mockCmp(command, version, callback, parameter) { - var resultVal; - if (command == 'ping') { - resultVal = { - gdprAppliesGlobally: mockCmp.stubSettings.isGlobal - }; - callback(resultVal); - } else if (command == 'getVendorConsents') { - let cbResult = { - vendorConsents: [] - } - cbResult.vendorConsents[version] = mockCmp.stubSettings.consents; - callback(cbResult); - } -} - -mockCmp.stubSettings = { - isGlobal: false, - consents: true -}; - -function setupCmpMock(isGlobal, consents) { - window.__cmp = mockCmp; - mockCmp.stubSettings.isGlobal = isGlobal; - mockCmp.stubSettings.consents = consents; -} - -describe('DigiTrust Id System', function () { - it('Should create the test hook', function (done) { - testHook = surfaceTestHook(); - assert.isNotNull(testHook, 'The test hook failed to surface'); - var conf = { - init: { - member: 'unit_test', - site: 'foo' - }, - callback: function (result) { - } - }; - testHook.initDigitrustFacade(conf); - window.DigiTrust.getUser(conf); - expect(window.DigiTrust).to.exist; - expect(window.DigiTrust.isMock).to.be.true; - done(); - }); - - it('Should report as client', function (done) { - delete window.DigiTrust; - testHook = surfaceTestHook(); - - var conf = { - init: { - member: 'unit_test', - site: 'foo' - }, - callback: function (result) { - expect(window.DigiTrust).to.exist; - expect(result).to.exist; - expect(window.DigiTrust.isMock).to.be.true; - } - }; - testHook.initDigitrustFacade(conf); - expect(window.DigiTrust).to.exist; - expect(window.DigiTrust.isClient).to.be.true; - done(); - }); - - it('Should allow consent when given', function (done) { - testHook = surfaceTestHook(); - setupCmpMock(true, true); - var handler = function(result) { - expect(result).to.be.true; - done(); - } - - testHook.gdpr.hasConsent(null, handler); - }); - - it('Should consent if does not apply', function (done) { - testHook = surfaceTestHook(); - setupCmpMock(false, true); - var handler = function (result) { - expect(result).to.be.true; - done(); - } - - testHook.gdpr.hasConsent(null, handler); - }); - - it('Should not allow consent when not given', function (done) { - testHook = surfaceTestHook(); - setupCmpMock(true, false); - var handler = function (result) { - expect(result).to.be.false; - done(); - } - - testHook.gdpr.hasConsent(null, handler); - }); - it('Should deny consent if timeout', function (done) { - window.__cmp = function () { }; - var handler = function (result) { - expect(result).to.be.false; - done(); - } - - testHook.gdpr.hasConsent({ consentTimeout: 1 }, handler); - }); - it('Should pass consent test if cmp not present', function (done) { - delete window.__cmp - testHook = surfaceTestHook(); - var handler = function (result) { - expect(result).to.be.true; - done(); - } - - testHook.gdpr.hasConsent(null, handler); - }); -}); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 1cbc2911ef5..c0b4703b7f4 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -119,22 +119,6 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('DigiTrust; getValue call', function() { - const userId = { - digitrustid: { - data: { - id: 'some-random-id-value' - } - } - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'digitru.st', - uids: [{id: 'some-random-id-value', atype: 1}] - }); - }); - it('criteo', function() { const userId = { criteoId: 'some-random-id-value' diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index 7f4828267a9..9b02f74f4bb 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -1,11 +1,13 @@ -import { deviceAccessHook, setEnforcementConfig, userSyncHook, userIdHook } from 'modules/gdprEnforcement.js'; +import { deviceAccessHook, setEnforcementConfig, userSyncHook, userIdHook, makeBidRequestsHook, validateRules, enforcementRules, purpose1Rule, purpose2Rule } 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 { executeStorageCallbacks } from 'src/prebid.js'; +import events from 'src/events.js'; +import { EVENTS } from 'src/constants.json'; -describe('gdpr enforcement', function() { +describe('gdpr enforcement', function () { let nextFnSpy; let logWarnSpy; let gdprDataHandlerStub; @@ -38,7 +40,7 @@ describe('gdpr enforcement', function() { }, 'legitimateInterests': { '1': false, - '2': false, + '2': true, '3': false } }, @@ -46,7 +48,9 @@ describe('gdpr enforcement', function() { 'consents': { '1': true, '2': true, - '3': false + '3': false, + '4': true, + '5': false }, 'legitimateInterests': { '1': false, @@ -81,23 +85,38 @@ describe('gdpr enforcement', function() { } }; - after(function() { - validateStorageEnforcement.getHooks({hook: deviceAccessHook}).remove(); - $$PREBID_GLOBAL$$.requestBids.getHooks({hook: executeStorageCallbacks}).remove(); + after(function () { + validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); + $$PREBID_GLOBAL$$.requestBids.getHooks({ hook: executeStorageCallbacks }).remove(); + adapterManager.makeBidRequests.getHooks({ hook: makeBidRequestsHook }).remove(); }) - describe('deviceAccessHook', function() { - beforeEach(function() { + describe('deviceAccessHook', function () { + let adapterManagerStub; + + function getBidderSpec(gvlid) { + return { + getSpec: () => { + return { + gvlid + } + } + } + } + + beforeEach(function () { nextFnSpy = sinon.spy(); gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); logWarnSpy = sinon.spy(utils, 'logWarn'); + adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); }); - afterEach(function() { + afterEach(function () { config.resetConfig(); gdprDataHandler.getConsentData.restore(); logWarnSpy.restore(); + adapterManagerStub.restore(); }); - it('should not allow device access when device access flag is set to false', function() { + it('should not allow device access when device access flag is set to false', function () { config.setConfig({ deviceAccess: false, consentManagement: { @@ -118,10 +137,12 @@ describe('gdpr enforcement', function() { hasEnforcementHook: true, valid: false } - expect(nextFnSpy.calledWith(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() { + it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { + adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); + adapterManagerStub.withArgs('rubicon').returns(getBidderSpec(5)); setEnforcementConfig({ gdpr: { rules: [{ @@ -143,7 +164,9 @@ describe('gdpr enforcement', function() { expect(logWarnSpy.callCount).to.equal(0); }); - it('should check consent for all vendors when enforcePurpose and enforceVendor are true', function() { + it('should check consent for all vendors when enforcePurpose and enforceVendor are true', function () { + adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); + adapterManagerStub.withArgs('rubicon').returns(getBidderSpec(3)); setEnforcementConfig({ gdpr: { rules: [{ @@ -164,7 +187,8 @@ describe('gdpr enforcement', function() { expect(logWarnSpy.callCount).to.equal(1); }); - it('should allow device access when gdprApplies is false and hasDeviceAccess flag is true', function() { + it('should allow device access when gdprApplies is false and hasDeviceAccess flag is true', function () { + adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); setEnforcementConfig({ gdpr: { rules: [{ @@ -187,15 +211,83 @@ describe('gdpr enforcement', function() { hasEnforcementHook: true, valid: true } - expect(nextFnSpy.calledWith(undefined, result)); + sinon.assert.calledWith(nextFnSpy, 1, 'appnexus', result); + }); + + it('should use gvlMapping set by publisher', function() { + config.setConfig({ + 'gvlMapping': { + 'appnexus': 4 + } + }); + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = true; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + expect(nextFnSpy.calledOnce).to.equal(true); + let result = { + hasEnforcementHook: true, + valid: true + } + sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + config.resetConfig(); + }); + + it('should use gvl id of alias and not of parent', function() { + let curBidderStub = sinon.stub(config, 'getCurrentBidder'); + curBidderStub.returns('appnexus-alias'); + adapterManager.aliasBidAdapter('appnexus', 'appnexus-alias'); + config.setConfig({ + 'gvlMapping': { + 'appnexus-alias': 4 + } + }); + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = true; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + expect(nextFnSpy.calledOnce).to.equal(true); + let result = { + hasEnforcementHook: true, + valid: true + } + sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + config.resetConfig(); + curBidderStub.restore(); }); }); - describe('userSyncHook', function() { + describe('userSyncHook', function () { let curBidderStub; let adapterManagerStub; - beforeEach(function() { + beforeEach(function () { gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); logWarnSpy = sinon.spy(utils, 'logWarn'); curBidderStub = sinon.stub(config, 'getCurrentBidder'); @@ -203,7 +295,7 @@ describe('gdpr enforcement', function() { nextFnSpy = sinon.spy(); }); - afterEach(function() { + afterEach(function () { config.getCurrentBidder.restore(); config.resetConfig(); gdprDataHandler.getConsentData.restore(); @@ -211,7 +303,7 @@ describe('gdpr enforcement', function() { logWarnSpy.restore(); }); - it('should allow bidder to do user sync if consent is true', function() { + it('should allow bidder to do user sync if consent is true', function () { setEnforcementConfig({ gdpr: { rules: [{ @@ -230,7 +322,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 1 } @@ -240,7 +332,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 3 } @@ -250,7 +342,7 @@ describe('gdpr enforcement', function() { expect(nextFnSpy.calledTwice).to.equal(true); }); - it('should not allow bidder to do user sync if user has denied consent', function() { + it('should not allow bidder to do user sync if user has denied consent', function () { setEnforcementConfig({ gdpr: { rules: [{ @@ -269,7 +361,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 1 } @@ -279,7 +371,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 3 } @@ -290,7 +382,7 @@ describe('gdpr enforcement', function() { expect(logWarnSpy.callCount).to.equal(1); }); - it('should not check vendor consent when enforceVendor is false', function() { + it('should not check vendor consent when enforceVendor is false', function () { setEnforcementConfig({ gdpr: { rules: [{ @@ -309,7 +401,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 1 } @@ -319,7 +411,7 @@ describe('gdpr enforcement', function() { curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ - getSpec: function() { + getSpec: function () { return { 'gvlid': 3 } @@ -331,16 +423,16 @@ describe('gdpr enforcement', function() { }); }); - describe('userIdHook', function() { - beforeEach(function() { + describe('userIdHook', function () { + beforeEach(function () { logWarnSpy = sinon.spy(utils, 'logWarn'); nextFnSpy = sinon.spy(); }); - afterEach(function() { + afterEach(function () { config.resetConfig(); logWarnSpy.restore(); }); - it('should allow user id module if consent is given', function() { + it('should allow user id module if consent is given', function () { setEnforcementConfig({ gdpr: { rules: [{ @@ -366,9 +458,10 @@ describe('gdpr enforcement', function() { const args = nextFnSpy.getCalls()[0].args; expect(args[1].hasValidated).to.be.true; expect(nextFnSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(nextFnSpy, submodules, { ...consentData, hasValidated: true }); }); - it('should allow userId module if gdpr not in scope', function() { + it('should allow userId module if gdpr not in scope', function () { let submodules = [{ submodule: { gvlid: 1, @@ -381,10 +474,10 @@ describe('gdpr enforcement', function() { const args = nextFnSpy.getCalls()[0].args; expect(args[1]).to.be.null; expect(nextFnSpy.calledOnce).to.equal(true); - expect(nextFnSpy.calledWith(undefined, submodules, consentData)); + sinon.assert.calledWith(nextFnSpy, submodules, consentData); }); - it('should not enforce if not apiVersion 2', function() { + it('should not allow user id module if user denied consent', function () { setEnforcementConfig({ gdpr: { rules: [{ @@ -397,57 +490,479 @@ describe('gdpr enforcement', function() { }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; - consentData.apiVersion = 1; + consentData.apiVersion = 2; consentData.gdprApplies = true; + let submodules = [{ submodule: { gvlid: 1, name: 'sampleUserId' } + }, { + submodule: { + gvlid: 3, + name: 'sampleUserId1' + } }] userIdHook(nextFnSpy, submodules, consentData); - // Should not pass back hasValidated flag since version 1 - const args = nextFnSpy.getCalls()[0].args; - expect(args[1].hasValidated).to.be.undefined; - expect(args[0]).to.deep.equal(submodules); - expect(nextFnSpy.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(1); + let expectedSubmodules = [{ + submodule: { + gvlid: 1, + name: 'sampleUserId' + } + }] + sinon.assert.calledWith(nextFnSpy, expectedSubmodules, { ...consentData, hasValidated: true }); + }); + }); + + describe('makeBidRequestsHook', function () { + let sandbox; + let adapterManagerStub; + let emitEventSpy; + + const MOCK_AD_UNITS = [{ + code: 'ad-unit-1', + mediaTypes: {}, + bids: [{ + bidder: 'bidder_1' // has consent + }, { + bidder: 'bidder_2' // doesn't have consent, but liTransparency is true. Bidder remains active. + }] + }, { + code: 'ad-unit-2', + mediaTypes: {}, + bids: [{ + bidder: 'bidder_2' + }, { + bidder: 'bidder_3' + }] + }]; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); + adapterManagerStub = sandbox.stub(adapterManager, 'getBidAdapter'); + logWarnSpy = sandbox.spy(utils, 'logWarn'); + nextFnSpy = sandbox.spy(); + emitEventSpy = sandbox.spy(events, 'emit'); + }); + afterEach(function () { + config.resetConfig(); + sandbox.restore(); }); - it('should not allow user id module if user denied consent', function() { + it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is established)', function () { setEnforcementConfig({ gdpr: { rules: [{ - purpose: 'storage', - enforcePurpose: false, + purpose: 'basicAds', + enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }] } }); - let consentData = {} + const consentData = {}; consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; - let submodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' + + gdprDataHandlerStub.returns(consentData); + adapterManagerStub.withArgs('bidder_1').returns({ + getSpec: function () { + return { 'gvlid': 4 } + } + }); + adapterManagerStub.withArgs('bidder_2').returns({ + getSpec: function () { + return { 'gvlid': 5 } + } + }); + adapterManagerStub.withArgs('bidder_3').returns({ + getSpec: function () { + return { 'gvlid': undefined } } + }); + makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); + + // Assertions + expect(nextFnSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(nextFnSpy, [{ + code: 'ad-unit-1', + mediaTypes: {}, + bids: [ + sinon.match({ bidder: 'bidder_1' }), + sinon.match({ bidder: 'bidder_2' }) + ] }, { - submodule: { - gvlid: 3, - name: 'sampleUserId1' + code: 'ad-unit-2', + mediaTypes: {}, + bids: [ + sinon.match({ bidder: 'bidder_2' }), + sinon.match({ bidder: 'bidder_3' }) // should be allowed even though it's doesn't have a gvlId because liTransparency is established. + ] + }], []); + }); + + it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is NOT established)', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'basicAds', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: ['bidder_3'] + }] } - }] - userIdHook(nextFnSpy, submodules, consentData); - expect(logWarnSpy.callCount).to.equal(1); - let expectedSubmodules = [{ - submodule: { - gvlid: 1, - name: 'sampleUserId' + }); + const consentData = {}; + + // set li for purpose 2 to false + const newConsentData = utils.deepClone(staticConfig); + newConsentData.consentData.getTCData.purpose.legitimateInterests['2'] = false; + + consentData.vendorData = newConsentData.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + + gdprDataHandlerStub.returns(consentData); + adapterManagerStub.withArgs('bidder_1').returns({ + getSpec: function () { + return { 'gvlid': 4 } + } + }); + adapterManagerStub.withArgs('bidder_2').returns({ + getSpec: function () { + return { 'gvlid': 5 } + } + }); + adapterManagerStub.withArgs('bidder_3').returns({ + getSpec: function () { + return { 'gvlid': undefined } + } + }); + + makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); + + // Assertions + expect(nextFnSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(nextFnSpy, [{ + code: 'ad-unit-1', + mediaTypes: {}, + bids: [ + sinon.match({ bidder: 'bidder_1' }), // 'bidder_2' is not present because it doesn't have vendorConsent + ] + }, { + code: 'ad-unit-2', + mediaTypes: {}, + bids: [ + sinon.match({ bidder: 'bidder_3' }), // 'bidder_3' is allowed despite gvlId being undefined because it's part of vendorExceptions + ] + }], []); + + expect(logWarnSpy.calledOnce).to.equal(true); + expect(emitEventSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(emitEventSpy, EVENTS.BIDDER_BLOCKED, 'bidder_2'); + }); + + it('should skip validation checks if GDPR version is not equal to "2"', function () { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforePurpose: false, + enforceVendor: false, + vendorExceptions: [] + }] + } + }); + + const consentData = {}; + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 1; + consentData.gdprApplies = true; + gdprDataHandlerStub.returns(consentData); + + makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); + + // Assertions + expect(nextFnSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(nextFnSpy, sinon.match.array.deepEquals(MOCK_AD_UNITS), []); + expect(emitEventSpy.notCalled).to.equal(true); + expect(logWarnSpy.notCalled).to.equal(true); + }); + }); + + describe('validateRules', function () { + const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = []) => ({ + purpose: purposeName, + enforcePurpose: enforcePurpose, + enforceVendor: enforceVendor, + vendorExceptions: vendorExceptions + }); + + const consentData = { + vendorData: staticConfig.consentData.getTCData, + apiVersion: 2, + gdprApplies: true + }; + + // Bidder - 'bidderA' has vendorConsent + const vendorAllowedModule = 'bidderA'; + const vendorAllowedGvlId = 1; + + // Bidder = 'bidderB' doesn't have vendorConsent + const vendorBlockedModule = 'bidderB'; + const vendorBlockedGvlId = 3; + + const consentDataWithPurposeConsentFalse = utils.deepClone(consentData); + consentDataWithPurposeConsentFalse.vendorData.purpose.consents['1'] = false; + + it('should return true when enforcePurpose=true AND purposeConsent[p]==true AND enforceVendor[p,v]==true AND vendorConsent[v]==true', function () { + // 'enforcePurpose' and 'enforceVendor' both are 'true' + const gdprRule = createGdprRule('storage', true, true, []); + + // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' + let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'false' + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + + // case 3 - Purpose consent is 'false' but vendor consent is 'true'. validateRules must return 'false' + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(false); + + // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + }); + + it('should return true when enforcePurpose=true AND purposeConsent[p]==true AND enforceVendor[p,v]==false', function () { + // 'enforcePurpose' is 'true' and 'enforceVendor' is 'false' + const gdprRule = createGdprRule('storage', true, false, []); + + // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' + let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'true' because vendorConsent doens't matter + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + + // case 3 - Purpose consent is 'false' but vendor consent is 'true'. validateRules must return 'false' because vendorConsent doesn't matter + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(false); + + // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' and vendorConsent doesn't matter + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(false); + }); + + it('should return true when enforcePurpose=false AND enforceVendor[p,v]==true AND vendorConsent[v]==true', function () { + // 'enforcePurpose' is 'false' and 'enforceVendor' is 'true' + const gdprRule = createGdprRule('storage', false, true, []); + + // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' + let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'false' because purposeConsent doesn't matter + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + + // case 3 - urpose consent is 'false' but vendor consent is 'true'. validateRules must return 'true' because purposeConsent doesn't matter + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' and purposeConsent doesn't matter + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + }); + + it('should return true when enforcePurpose=false AND enforceVendor[p,v]==false', function () { + // 'enforcePurpose' is 'false' and 'enforceVendor' is 'false' + const gdprRule = createGdprRule('storage', false, false, []); + + // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true', both the consents do not matter. + let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'true', both the consents do not matter. + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + + // case 3 - urpose consent is 'false' but vendor consent is 'true'. validateRules must return 'true', both the consents do not matter. + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'true', both the consents do not matter. + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + }); + + it('should return true when "vendorExceptions" contains the name of the vendor under test', function () { + // 'vendorExceptions' contains 'bidderB' which doesn't have vendor consent. + const gdprRule = createGdprRule('storage', false, true, [vendorBlockedModule]); + + /* 'bidderB' gets a free pass since it's included in the 'vendorExceptions' array. validateRules must disregard + user's choice for purpose and vendor consent and return 'true' for this bidder(s) */ + const isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + }); + + describe('Purpose 2 special case', function () { + const consentDataWithLIFalse = utils.deepClone(consentData); + consentDataWithLIFalse.vendorData.purpose.legitimateInterests['2'] = false; + + const consentDataWithPurposeConsentFalse = utils.deepClone(consentData); + consentDataWithPurposeConsentFalse.vendorData.purpose.consents['2'] = false; + + const consentDataWithPurposeConsentFalseAndLIFalse = utils.deepClone(consentData); + consentDataWithPurposeConsentFalseAndLIFalse.vendorData.purpose.legitimateInterests['2'] = false; + consentDataWithPurposeConsentFalseAndLIFalse.vendorData.purpose.consents['2'] = false; + + it('should return true when (enforcePurpose=true AND purposeConsent[p]===true AND enforceVendor[p.v]===true AND vendorConsent[v]===true) OR (purposesLITransparency[p]===true)', function () { + // both 'enforcePurpose' and 'enforceVendor' is 'true' + const gdprRule = createGdprRule('basicAds', true, true, []); + + // case 1 - Both purpose consent and vendor consent is 'true', but legitimateInterests for purpose 2 is 'false'. validateRules must return 'true'. + let isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'true' but vendor consent is 'false', but legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + + // case 3 - Purpose consent is 'true' and vendor consent is 'true', as well as legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 4 - Purpose consent is 'true' and vendor consent is 'false', and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. + isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + }); + + it('should return true when (enforcePurpose=true AND purposeConsent[p]===true AND enforceVendor[p.v]===false) OR (purposesLITransparency[p]===true)', function () { + // 'enforcePurpose' is 'true' and 'enforceVendor' is 'false' + const gdprRule = createGdprRule('basicAds', true, false, []); + + // case 1 - Purpose consent is 'true', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + let isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Purpose consent is 'false', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 3 - Purpose consent is 'false', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. + isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalseAndLIFalse, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(false); + }); + + it('should return true when (enforcePurpose=false AND enforceVendor[p,v]===true AND vendorConsent[v]===true) OR (purposesLITransparency[p]===true)', function () { + // 'enforcePurpose' is 'false' and 'enforceVendor' is 'true' + const gdprRule = createGdprRule('basicAds', false, true, []); + + // case - 1 Vendor consent is 'true', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); + expect(isAllowed).to.equal(true); + + // case 2 - Vendor consent is 'false', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. + isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(true); + + // case 3 - Vendor consent is 'false', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. + isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorBlockedModule, vendorBlockedGvlId); + expect(isAllowed).to.equal(false); + }); + }); + }) + + describe('setEnforcementConfig', function () { + let sandbox; + const DEFAULT_RULES = [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + }, { + purpose: 'basicAds', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + }]; + beforeEach(function () { + sandbox = sinon.createSandbox(); + logWarnSpy = sandbox.spy(utils, 'logWarn'); + }); + afterEach(function () { + config.resetConfig(); + sandbox.restore(); + }); + + it('should enforce TCF2 Purpose1 and Purpose 2 if no "rules" found in the config', function () { + setEnforcementConfig({ + gdpr: { + cmpApi: 'iab', + allowAuctionWithoutConsent: true, + timeout: 5000 + } + }); + + expect(logWarnSpy.calledOnce).to.equal(true); + expect(enforcementRules).to.deep.equal(DEFAULT_RULES); + }); + + it('should enforce TCF2 Purpose 2 also if only Purpose 1 is defined in "rules"', function () { + const purpose1RuleDefinedInConfig = { + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: ['bidderA'] + } + setEnforcementConfig({ + gdpr: { + rules: [purpose1RuleDefinedInConfig] + } + }); + + expect(purpose1Rule).to.deep.equal(purpose1RuleDefinedInConfig); + expect(purpose2Rule).to.deep.equal(DEFAULT_RULES[1]); + }); + + it('should enforce TCF2 Purpose 1 also if only Purpose 2 is defined in "rules"', function () { + const purpose2RuleDefinedInConfig = { + purpose: 'basicAds', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: ['bidderA'] + } + setEnforcementConfig({ + gdpr: { + rules: [purpose2RuleDefinedInConfig] } + }); + + expect(purpose1Rule).to.deep.equal(DEFAULT_RULES[0]); + expect(purpose2Rule).to.deep.equal(purpose2RuleDefinedInConfig); + }); + + it('should use the "rules" defined in config if a definition found', function() { + const rules = [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: false + }, { + purpose: 'basicAds', + enforcePurpose: false, + enforceVendor: false }] - expect(nextFnSpy.calledWith(undefined, expectedSubmodules, consentData)); + setEnforcementConfig({gdpr: { rules }}); + + expect(enforcementRules).to.deep.equal(rules); }); }); }); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index b2b8885a2f8..63b04077f4e 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -110,6 +110,35 @@ describe('IndexexchangeAdapter', function () { ] }; + const DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN = { + cur: 'USD', + id: '11a22b33c44d', + seatbid: [ + { + bid: [ + { + crid: '12345', + adid: '14851455', + impid: '1a2b3c4d', + cid: '3051266', + price: 100, + w: 300, + h: 250, + id: '1', + ext: { + dspid: 50, + pricelevel: '_100', + advbrandid: 303325, + advbrand: 'OECTA' + }, + adm: '' + } + ], + seat: '3970' + } + ] + }; + const DEFAULT_VIDEO_BID_RESPONSE = { cur: 'USD', id: '1aa2bb3cc4de', @@ -1102,7 +1131,8 @@ describe('IndexexchangeAdapter', function () { meta: { networkId: 50, brandId: 303325, - brandName: 'OECTA' + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] } } ]; @@ -1110,6 +1140,31 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); + it('should get correct bid response for banner ad with missing adomain', function () { + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + netRevenue: true, + dealId: undefined, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA' + } + } + ]; + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + it('should set creativeId to default value if not provided', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); delete bidResponse.seatbid[0].bid[0].crid; @@ -1129,7 +1184,8 @@ describe('IndexexchangeAdapter', function () { meta: { networkId: 50, brandId: 303325, - brandName: 'OECTA' + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] } } ]; @@ -1155,7 +1211,8 @@ describe('IndexexchangeAdapter', function () { meta: { networkId: 50, brandId: 303325, - brandName: 'OECTA' + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] } } ]; @@ -1182,7 +1239,8 @@ describe('IndexexchangeAdapter', function () { meta: { networkId: 50, brandId: 303325, - brandName: 'OECTA' + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] } } ]; @@ -1207,7 +1265,8 @@ describe('IndexexchangeAdapter', function () { meta: { networkId: 51, brandId: 303326, - brandName: 'OECTB' + brandName: 'OECTB', + advertiserDomains: ['www.abcd.com'] } } ]; diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 294ada6f988..58e607ebb89 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -712,40 +712,6 @@ describe('S2S Adapter', function () { }); }); - it('adds digitrust id is present and user is not optout', function () { - let ortb2Config = utils.deepClone(CONFIG); - ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; - - let consentConfig = { s2sConfig: ortb2Config }; - config.setConfig(consentConfig); - - let digiTrustObj = { - privacy: { - optout: false - }, - id: 'testId', - keyv: 'testKeyV' - }; - - let digiTrustBidRequest = utils.deepClone(BID_REQUESTS); - digiTrustBidRequest[0].bids[0].userId = { digitrustid: { data: digiTrustObj } }; - - adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.user.ext.digitrust).to.deep.equal({ - id: digiTrustObj.id, - keyv: digiTrustObj.keyv - }); - - digiTrustObj.privacy.optout = true; - - adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); - requestBid = JSON.parse(server.requests[1].requestBody); - - expect(requestBid.user && request.user.ext && requestBid.user.ext.digitrust).to.not.exist; - }); - it('adds device and app objects to request', function () { const _config = { s2sConfig: CONFIG, diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index abc440ac8ea..1e8ab8f9faf 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -2302,6 +2302,7 @@ describe('PubMatic adapter', function () { expect(response[0].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-987'); expect(response[0].meta.buyerId).to.equal(976); expect(response[0].meta.clickUrl).to.equal('blackrock.com'); + expect(response[0].meta.advertiserDomains[0]).to.equal('blackrock.com'); expect(response[0].referrer).to.include(data.site.ref); expect(response[0].ad).to.equal(bidResponses.body.seatbid[0].bid[0].adm); expect(response[0].pm_seat).to.equal(bidResponses.body.seatbid[0].seat); @@ -2325,6 +2326,7 @@ describe('PubMatic adapter', function () { expect(response[1].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-789'); expect(response[1].meta.buyerId).to.equal(832); expect(response[1].meta.clickUrl).to.equal('hivehome.com'); + expect(response[1].meta.advertiserDomains[0]).to.equal('hivehome.com'); expect(response[1].referrer).to.include(data.site.ref); expect(response[1].ad).to.equal(bidResponses.body.seatbid[1].bid[0].adm); expect(response[1].pm_seat).to.equal(bidResponses.body.seatbid[1].seat || null); diff --git a/test/spec/modules/quantumBidAdapter_spec.js b/test/spec/modules/quantumBidAdapter_spec.js deleted file mode 100644 index c03d74ea52e..00000000000 --- a/test/spec/modules/quantumBidAdapter_spec.js +++ /dev/null @@ -1,325 +0,0 @@ -import { expect } from 'chai' -import { spec } from 'modules/quantumBidAdapter.js' -import { newBidder } from 'src/adapters/bidderFactory.js' - -const ENDPOINT = 'https://s.sspqns.com/hb' -const REQUEST = { - 'bidder': 'quantum', - 'sizes': [[300, 250]], - 'renderMode': 'banner', - 'params': { - placementId: 21546 - } -} - -const NATIVE_REQUEST = { - 'bidder': 'quantum', - 'mediaType': 'native', - 'sizes': [[0, 0]], - 'params': { - placementId: 21546 - } -} - -const serverResponse = { - 'price': 0.3, - 'debug': [ - '' - ], - 'is_fallback': false, - 'nurl': 'https://s.sspqns.com/imp/KpQ1WNMHV-9a3HqWL_0JnujJFGo1Hnx9RS3FT_Yy8jW-Z6t_PJYmP2otidJsxE3qcY2EozzcBjRzGM7HEQcxVnjOzq0Th1cxb6A5bSp5BizTwY5SRaxx_0PgF6--8LqaF4LMUgMmhfF5k3gOOzzK6gKdavia4_w3LJ1CRWkMEwABr8bPzeovy1y4MOZsOXv7vXjPGMKJSTgphuZR57fL4u4ZFF4XY70K_TaH5bfXHMRAzE0Q38tfpTvbdFV_u2g-FoF0gjzKjiS88VnetT-Jo3qtrMphWzr52jsg5tH3L7hbymUOm1YkuJP9xrXLoZNVgC5sTMYolKLMSu6dqhS2FXcdfaGAcHweaaAAwJq-pB7DuiVcdnZQphUymhIia_KG2AYweWp6TYEpJbJjf2BcLpm_-KGw4gLh6L3DtEvUZwXZe-JpUJ4/', - 'native': { - 'link': { - 'url': 'https://s.sspqns.com/click/KpQ1WNMHV-9a3HqWL_0JnujJFGo1Hnx9RS3FT_Yy8jW-Z6t_PJYmP2otidJsxE3qcY2EozzcBjRzGM7HEQcxVnjOzq0Th1cxb6A5bSp5BizTwY5SRaxx_0PgF6--8LqaF4LMUgMmhfF5k3gOOzzK6gKdavia4_w3LJ1CRWkMEwABr8bPzeovy1y4MOZsOXv7vXjPGMKJSTgphuZR57fL4u4ZFF4XY70K_TaH5bfXHMRAzE0Q38tfpTvbdFV_u2g-FoF0gjzKjiS88VnetT-Jo3qtrMphWzr52jsg5tH3L7hbymUOm1YkuJP9xrXLoZNVgC5sTMYolKLMSu6dqhS2FXcdfaGAcHweaaAAwJq-pB7DuiVcdnZQphUymhIia_KG2AYweWp6TYEpJbJjf2BcLpm_-KGw4gLh6L3DtEvUZwXZe-JpUJ4///', - 'clicktrackers': ['https://elasticad.net'] - }, - 'assets': [ - { - 'id': 1, - 'title': { - 'text': 'ad.SSP.1x1' - }, - 'required': 1 - }, - { - 'id': 2, - 'img': { - 'w': 15, - 'h': 15, - 'url': 'https://files.ssp.theadtech.com.s3.amazonaws.com/media/image/sxjermpz/scalecrop-15x15' - } - }, - { - 'id': 3, - 'data': { - 'value': 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.Lorem Ipsum is simply dummy text of the printing and typesetting industry.' - }, - 'required': 1 - }, - { - 'id': 4, - 'img': { - 'w': 500, - 'h': 500, - 'url': 'https://files.ssp.theadtech.com.s3.amazonaws.com/media/image/sxjermpz/scalecrop-500x500' - } - }, - { - 'id': 6, - 'video': { - 'vasttag': 'https://elasticad.net/vast.xml' - } - }, - { - 'id': 2001, - 'data': { - 'value': 'https://elasticad.net' - } - }, - { - 'id': 2002, - 'data': { - 'value': 'vast' - } - }, - { - 'id': 2007, - 'data': { - 'value': 'click' - } - }, - { - 'id': 10, - 'data': { - 'value': 'ad.SSP.1x1 sponsor' - } - }, - { - 'id': 2003, - 'data': { - 'value': 'https://elasticad.net' - } - }, - { - 'id': 2004, - 'data': { - 'value': 'prism' - } - }, - { - 'id': 2005, - 'data': { - 'value': '/home' - } - }, - { - 'id': 2006, - 'data': { - 'value': 'https://elasticad.net/vast.xml' - } - }, - { - 'id': 2022, - 'data': { - 'value': 'Lorem ipsum....' - } - } - ], - 'imptrackers': [], - 'ver': '1.1' - }, - 'sync': [ - 'https://match.adsrvr.org/track/cmb/generic?ttd_pid=s6e8ued&ttd_tpi=1' - ] -} - -const nativeServerResponse = { - 'price': 0.3, - 'debug': [ - '' - ], - 'is_fallback': false, - 'nurl': 'https://s.sspqns.com/imp/KpQ1WNMHV-9a3HqWL_0JnujJFGo1Hnx9RS3FT_Yy8jW-Z6t_PJYmP2otidJsxE3qcY2EozzcBjRzGM7HEQcxVnjOzq0Th1cxb6A5bSp5BizTwY5SRaxx_0PgF6--8LqaF4LMUgMmhfF5k3gOOzzK6gKdavia4_w3LJ1CRWkMEwABr8bPzeovy1y4MOZsOXv7vXjPGMKJSTgphuZR57fL4u4ZFF4XY70K_TaH5bfXHMRAzE0Q38tfpTvbdFV_u2g-FoF0gjzKjiS88VnetT-Jo3qtrMphWzr52jsg5tH3L7hbymUOm1YkuJP9xrXLoZNVgC5sTMYolKLMSu6dqhS2FXcdfaGAcHweaaAAwJq-pB7DuiVcdnZQphUymhIia_KG2AYweWp6TYEpJbJjf2BcLpm_-KGw4gLh6L3DtEvUZwXZe-JpUJ4/', - 'native': { - 'link': { - 'url': 'https://s.sspqns.com/click/KpQ1WNMHV-9a3HqWL_0JnujJFGo1Hnx9RS3FT_Yy8jW-Z6t_PJYmP2otidJsxE3qcY2EozzcBjRzGM7HEQcxVnjOzq0Th1cxb6A5bSp5BizTwY5SRaxx_0PgF6--8LqaF4LMUgMmhfF5k3gOOzzK6gKdavia4_w3LJ1CRWkMEwABr8bPzeovy1y4MOZsOXv7vXjPGMKJSTgphuZR57fL4u4ZFF4XY70K_TaH5bfXHMRAzE0Q38tfpTvbdFV_u2g-FoF0gjzKjiS88VnetT-Jo3qtrMphWzr52jsg5tH3L7hbymUOm1YkuJP9xrXLoZNVgC5sTMYolKLMSu6dqhS2FXcdfaGAcHweaaAAwJq-pB7DuiVcdnZQphUymhIia_KG2AYweWp6TYEpJbJjf2BcLpm_-KGw4gLh6L3DtEvUZwXZe-JpUJ4///' - }, - 'assets': [ - { - 'id': 1, - 'title': { - 'text': 'ad.SSP.1x1' - }, - 'required': 1 - }, - { - 'id': 2, - 'img': { - 'w': 15, - 'h': 15, - 'url': 'https://files.ssp.theadtech.com.s3.amazonaws.com/media/image/sxjermpz/scalecrop-15x15' - } - }, - { - 'id': 3, - 'data': { - 'value': 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.Lorem Ipsum is simply dummy text of the printing and typesetting industry.' - }, - 'required': 1 - }, - { - 'id': 4, - 'img': { - 'w': 500, - 'h': 500, - 'url': 'https://files.ssp.theadtech.com.s3.amazonaws.com/media/image/sxjermpz/scalecrop-500x500' - } - }, - { - 'id': 2007, - 'data': { - 'value': 'click' - } - }, - { - 'id': 10, - 'data': { - 'value': 'ad.SSP.1x1 sponsor' - } - }, - - { - 'id': 2003, - 'data': { - 'value': 'https://elasticad.net' - } - } - ], - 'imptrackers': [], - 'ver': '1.1' - }, - 'sync': [ - 'https://match.adsrvr.org/track/cmb/generic?ttd_pid=s6e8ued&ttd_tpi=1' - ] -} - -describe('quantumBidAdapter', 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 () { - expect(spec.isBidRequestValid(REQUEST)).to.equal(true) - }) - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, REQUEST) - delete bid.params - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) - }) - - describe('buildRequests', function () { - let bidRequests = [REQUEST] - - const request = spec.buildRequests(bidRequests, {}) - - it('sends bid request to ENDPOINT via GET', function () { - expect(request[0].method).to.equal('GET') - }) - }) - - describe('GDPR conformity', function () { - const bidRequests = [{ - 'bidder': 'quantum', - 'mediaType': 'native', - 'params': { - placementId: 21546 - }, - adUnitCode: 'aaa', - transactionId: '2b8389fe-615c-482d-9f1a-376fb8f7d6b0', - sizes: [[0, 0]], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }]; - - const bidderRequest = { - gdprConsent: { - consentString: 'awefasdfwefasdfasd', - gdprApplies: true - } - }; - - it('should transmit correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.quantx_gdpr).to.equal(1); - expect(requests[0].data.quantx_user_consent_string).to.equal('awefasdfwefasdfasd'); - }); - }); - - describe('GDPR absence conformity', function () { - const bidRequests = [{ - 'bidder': 'quantum', - 'mediaType': 'native', - 'params': { - placementId: 21546 - }, - adUnitCode: 'aaa', - transactionId: '2b8389fe-615c-482d-9f1a-376fb8f7d6b0', - sizes: [[0, 0]], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }]; - - const bidderRequest = { - gdprConsent: undefined - }; - - it('should transmit correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.quantx_gdpr).to.be.undefined; - expect(requests[0].data.quantx_user_consent_string).to.be.undefined; - }); - }); - - describe('interpretResponse', function () { - let bidderRequest = { - bidderCode: 'bidderCode', - bids: [] - } - - it('handles native request : should get correct bid response', function () { - const result = spec.interpretResponse({body: nativeServerResponse}, NATIVE_REQUEST) - expect(result[0]).to.have.property('cpm').equal(0.3) - expect(result[0]).to.have.property('width').to.be.below(2) - expect(result[0]).to.have.property('height').to.be.below(2) - expect(result[0]).to.have.property('mediaType').equal('native') - expect(result[0]).to.have.property('native') - }) - - it('should get correct bid response', function () { - const result = spec.interpretResponse({body: serverResponse}, REQUEST) - expect(result[0]).to.have.property('cpm').equal(0.3) - expect(result[0]).to.have.property('width').equal(300) - expect(result[0]).to.have.property('height').equal(250) - expect(result[0]).to.have.property('mediaType').equal('banner') - expect(result[0]).to.have.property('ad') - }) - - it('handles nobid responses', function () { - const nobidServerResponse = {bids: []} - const nobidResult = spec.interpretResponse({body: nobidServerResponse}, bidderRequest) - // console.log(nobidResult) - expect(nobidResult.length).to.equal(0) - }) - }) -}) diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 78d17f35c69..94cc335fd8e 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -381,6 +381,7 @@ describe('the spotx adapter', function () { impid: 123, cur: 'USD', price: 12, + adomain: ['abc.com'], crid: 321, w: 400, h: 300, @@ -392,6 +393,7 @@ describe('the spotx adapter', function () { impid: 124, cur: 'USD', price: 13, + adomain: ['def.com'], w: 200, h: 100, ext: { @@ -409,6 +411,7 @@ describe('the spotx adapter', function () { 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); + expect(responses[0].meta.advertiserDomains[0]).to.equal('abc.com'); expect(responses[0].cpm).to.equal(12); expect(responses[0].creativeId).to.equal(321); expect(responses[0].currency).to.equal('USD'); @@ -423,6 +426,7 @@ describe('the spotx adapter', function () { expect(responses[1].cache_key).to.equal('cache124'); expect(responses[1].channel_id).to.equal(12345); expect(responses[1].cpm).to.equal(13); + expect(responses[1].meta.advertiserDomains[0]).to.equal('def.com'); expect(responses[1].creativeId).to.equal(''); expect(responses[1].currency).to.equal('USD'); expect(responses[1].height).to.equal(100); diff --git a/test/spec/modules/telariaBidAdapter_spec.js b/test/spec/modules/telariaBidAdapter_spec.js index 9e4098d7854..25649115cc1 100644 --- a/test/spec/modules/telariaBidAdapter_spec.js +++ b/test/spec/modules/telariaBidAdapter_spec.js @@ -236,7 +236,7 @@ describe('TelariaAdapter', () => { it('should get correct bid response', () => { let expectedResponseKeys = ['bidderCode', 'width', 'height', 'statusMessage', 'adId', 'mediaType', 'source', 'getStatusCode', 'getSize', 'requestId', 'cpm', 'creativeId', 'vastXml', - 'vastUrl', 'currency', 'netRevenue', 'ttl', 'ad']; + 'vastUrl', 'currency', 'netRevenue', 'ttl', 'ad', 'meta']; let bidRequest = spec.buildRequests(stub, BIDDER_REQUEST)[0]; bidRequest.bidId = '1234'; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index d038074cb10..a0b7d68bcce 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -5,7 +5,8 @@ import { requestBidsHook, setSubmoduleRegistry, syncDelay, - coreStorage + coreStorage, + setStoredValue } from 'modules/userId/index.js'; import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; @@ -660,7 +661,7 @@ describe('User ID', function() { it('does not delay auction if there are no ids to fetch', function() { coreStorage.getCookie.withArgs('MOCKID').returns('123456778'); config.setConfig({ - usersync: { + userSync: { auctionDelay: 33, syncDelay: 77, userIds: [{ @@ -1443,4 +1444,45 @@ describe('User ID', function() { expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); }); }); + + describe('Set cookie behavior', function() { + let coreStorageSpy; + beforeEach(function() { + coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); + }); + afterEach(function() { + coreStorageSpy.restore(); + }); + it('should allow submodules to override the domain', function () { + const submodule = { + submodule: { + domainOverride: function() { + return 'foo.com' + } + }, + config: { + storage: { + type: 'cookie' + } + } + } + setStoredValue(submodule, 'bar'); + expect(coreStorage.setCookie.getCall(0).args[4]).to.equal('foo.com'); + }); + + it('should pass null for domain if submodule does not override the domain', function () { + const submodule = { + submodule: { + + }, + config: { + storage: { + type: 'cookie' + } + } + } + setStoredValue(submodule, 'bar'); + expect(coreStorage.setCookie.getCall(0).args[4]).to.equal(null); + }); + }); }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index cab0655a29d..692cf9a6475 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -401,8 +401,12 @@ describe('bidders created by newBidder', function () { adUnitCode: 'mock/placement', currency: 'USD', netRevenue: true, - ttl: 300 + ttl: 300, + bidderCode: 'sampleBidder', + sampleBidder: {advertiserId: '12345', networkId: '111222'} }; + const bidderRequest = Object.assign({}, MOCK_BIDS_REQUEST); + bidderRequest.bids[0].bidder = 'sampleBidder'; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ method: 'POST', @@ -413,7 +417,7 @@ describe('bidders created by newBidder', function () { spec.interpretResponse.returns(bid); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); expect(addBidResponseStub.calledOnce).to.equal(true); expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); @@ -423,6 +427,8 @@ describe('bidders created by newBidder', function () { expect(bidObject.originalCurrency).to.equal(bid.currency); expect(doneStub.calledOnce).to.equal(true); expect(logErrorSpy.callCount).to.equal(0); + expect(bidObject.meta).to.exist; + expect(bidObject.meta).to.deep.equal({advertiserId: '12345', networkId: '111222'}); }); it('should call spec.getUserSyncs() with the response', function () { @@ -645,6 +651,28 @@ describe('registerBidder', function () { expect(registerBidAdapterStub.secondCall.args[1]).to.equal('foo') expect(registerBidAdapterStub.thirdCall.args[1]).to.equal('bar') }); + + it('should register alias with their gvlid', function() { + const aliases = [ + { + code: 'foo', + gvlid: 1 + }, + { + code: 'bar', + gvlid: 2 + }, + { + code: 'baz' + } + ] + const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); + registerBidder(thisSpec); + + expect(registerBidAdapterStub.getCall(1).args[0].getSpec().gvlid).to.equal(1); + expect(registerBidAdapterStub.getCall(2).args[0].getSpec().gvlid).to.equal(2); + expect(registerBidAdapterStub.getCall(3).args[0].getSpec().gvlid).to.equal(undefined); + }) }) describe('validate bid response: ', function () { From 1d555d3090090c129a2cfd1dad30cf95cb287a6a Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Thu, 23 Jul 2020 09:54:30 -0400 Subject: [PATCH 0019/1476] Revert "Vidazoo Adapter: Feature/alternate-param-names (#5527)" (#5535) This reverts commit 0403ba7f88ce78f23a1b6e3699730fbca50fbd20. --- modules/vidazooBidAdapter.js | 20 ++--------------- test/spec/modules/vidazooBidAdapter_spec.js | 24 ++------------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index b9f3297818f..3eea270dc8d 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -32,31 +32,16 @@ export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; } -export function extractCID(params) { - return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; -} - -export function extractPID(params) { - return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; -} - -export function extractSubDomain(params) { - return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; -} - function isBidRequestValid(bid) { const params = bid.params || {}; - return !!(extractCID(params) && extractPID(params)); + return !!(params.cId && params.pId); } function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const { params, bidId, userId, adUnitCode } = bid; - const { bidFloor, ext } = params; + const { bidFloor, cId, pId, ext, subDomain } = params; const hashUrl = hashCode(topWindowUrl); const dealId = getNextDealId(hashUrl); - const cId = extractCID(params); - const pId = extractPID(params); - const subDomain = extractSubDomain(params); let data = { url: encodeURIComponent(topWindowUrl), @@ -85,7 +70,6 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { if (bidderRequest.uspConsent) { data.usPrivacy = bidderRequest.uspConsent } - const dto = { method: 'POST', url: `${createDomain(subDomain)}/prebid/multi/${cId}`, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 103cb0897a7..b8ab83a95ae 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain, extractPID, extractCID, extractSubDomain } from 'modules/vidazooBidAdapter.js'; +import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; import { version } from 'package.json'; @@ -220,7 +220,7 @@ describe('VidazooBidAdapter', function () { }); }); - describe('user id system', function () { + describe(`user id system`, function () { Object.keys(SUPPORTED_ID_SYSTEMS).forEach((idSystemProvider) => { const id = Date.now().toString(); const bid = utils.deepClone(BID); @@ -243,24 +243,4 @@ 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' }); - 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'); - }); - }); }); From 92e2f7a3ec5adcbb5ec35713d12ea40f4d01d6b6 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 23 Jul 2020 10:02:13 -0400 Subject: [PATCH 0020/1476] Prebid 4.0.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 d93926c003c..964a2d81802 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.0.0-pre", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0cba9aea969..d71960b5031 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.0.0-pre", + "version": "4.0.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 6256e5aab1502c152aeff0cf185d3602aa1c60ac Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 23 Jul 2020 10:11:18 -0400 Subject: [PATCH 0021/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d71960b5031..5295cb49dcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.0.0", + "version": "4.1.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From c67198f98e996eec5f75d6e90e0ce5152d38e518 Mon Sep 17 00:00:00 2001 From: kusapan Date: Fri, 24 Jul 2020 03:40:27 +0900 Subject: [PATCH 0022/1476] YIELDONE adapter - support CMer Player (#5461) * added UserSync * added UserSync Unit Test * support for multi sizes * register the adapter as supporting video * supporting video * change requestId acquisition method * fix the parameter name of dealID * update test parameters * support instream video * add test for bidRequest * add test for interpretResponse * add test params * add note to documentaion * add payload params * add test * delete tmax param * add cmer renderer * fix player url --- modules/yieldoneBidAdapter.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 59240878426..574967db291 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -8,6 +8,7 @@ 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'; const VIDEO_PLAYER_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/dac-video-prebid.min.js'; +const CMER_PLAYER_URL = 'https://an.cmertv.com/hb/renderer/cmertv-video-yone-prebid.min.js'; const VIEWABLE_PERCENTAGE_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/prebid-adformat-config.js'; export const spec = { @@ -136,7 +137,11 @@ export const spec = { } else if (response.adm) { bidResponse.mediaType = VIDEO; bidResponse.vastXml = response.adm; - bidResponse.renderer = newRenderer(response); + if (renderId === 'cmer') { + bidResponse.renderer = newCmerRenderer(response); + } else { + bidResponse.renderer = newRenderer(response); + } } bidResponses.push(bidResponse); @@ -175,4 +180,26 @@ function outstreamRender(bid) { }); } +function newCmerRenderer(response) { + const renderer = Renderer.install({ + id: response.uid, + url: CMER_PLAYER_URL, + loaded: false, + }); + + try { + renderer.setRender(cmerRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on newRenderer', err); + } + + return renderer; +} + +function cmerRender(bid) { + bid.renderer.push(() => { + window.CMERYONEPREBID.renderPrebid(bid); + }); +} + registerBidder(spec); From eb467f4adc7cd4c874d1f9b5d0034058b74e8831 Mon Sep 17 00:00:00 2001 From: Niksok Date: Thu, 23 Jul 2020 22:26:53 +0300 Subject: [PATCH 0023/1476] Added native support for Mediaforce Bid Adapter (#5528) * add mediaforce bid adapter * make use of unused variable language * Added native support for Mediaforce Bid Adapter * Fix test endpoint url for Mediaforce Bid Adapter * cleanup Co-authored-by: ksanksana --- modules/mediaforceBidAdapter.js | 237 +++++++++++++++--- modules/mediaforceBidAdapter.md | 33 +++ .../spec/modules/mediaforceBidAdapter_spec.js | 228 +++++++++++++++-- 3 files changed, 456 insertions(+), 42 deletions(-) diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index fc0c44f6d82..7e5c06b1b48 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -1,30 +1,113 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; const BIDDER_CODE = 'mediaforce'; const ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid'; +const TEST_ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid?debug_key=abcdefghijklmnop'; +const NATIVE_ID_MAP = {}; +const NATIVE_PARAMS = { + title: { + id: 1, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + body: { + id: 4, + name: 'data', + type: 2 + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + cta: { + id: 6, + type: 12, + name: 'data' + }, + body2: { + id: 7, + name: 'data', + type: 10 + }, + rating: { + id: 8, + name: 'data', + type: 3 + }, + likes: { + id: 9, + name: 'data', + type: 4 + }, + downloads: { + id: 10, + name: 'data', + type: 5 + }, + displayUrl: { + id: 11, + name: 'data', + type: 11 + }, + price: { + id: 12, + name: 'data', + type: 6 + }, + salePrice: { + id: 13, + name: 'data', + type: 7 + }, + address: { + id: 14, + name: 'data', + type: 9 + }, + phone: { + id: 15, + name: 'data', + type: 8 + } +}; + +Object.keys(NATIVE_PARAMS).forEach((key) => { + NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key; +}); export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], /** - * 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. - */ + * 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 !!((typeof bid.params === 'object') && bid.params.placement_id && bid.params.publisher_id); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @param {bidderRequest} bidderRequest bidder request object + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { if (validBidRequests.length === 0) { return; @@ -32,11 +115,12 @@ export const spec = { const referer = bidderRequest && bidderRequest.refererInfo ? encodeURIComponent(bidderRequest.refererInfo.referer) : ''; const dnt = utils.getDNT() ? 1 : 0; - let imp = []; - let requests = [] + let requests = []; validBidRequests.forEach(bid => { let tagid = bid.params.placement_id; let bidfloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : 0; + let imp = []; + let validImp = false; let impObj = { id: bid.bidId, tagid: tagid, @@ -47,11 +131,16 @@ export const spec = { switch (mediaTypes) { case BANNER: impObj.banner = createBannerRequest(bid); - imp.push(impObj); + validImp = true; + break; + case NATIVE: + impObj.native = createNativeRequest(bid); + validImp = true; break; default: return; } } + validImp && imp.push(impObj); let request = { id: bid.transactionId, @@ -73,7 +162,7 @@ export const spec = { }; requests.push({ method: 'POST', - url: ENDPOINT_URL, + url: bid.params.is_test ? TEST_ENDPOINT_URL : ENDPOINT_URL, data: JSON.stringify(request) }); }); @@ -81,11 +170,12 @@ export const spec = { }, /** - * 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. - */ + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { if (!serverResponse || !serverResponse.body) { return []; @@ -99,15 +189,35 @@ export const spec = { const bid = { requestId: serverBid.impid, cpm: parseFloat(serverBid.price), - width: serverBid.w, - height: serverBid.h, creativeId: serverBid.adid, currency: cur, netRevenue: true, ttl: serverBid.ttl || 300, - ad: serverBid.adm, burl: serverBid.burl, }; + if (serverBid.dealid) { + bid.dealId = serverBid.dealid; + } + let jsonAdm; + let adm = serverBid.adm; + let ext = serverBid.ext; + try { + jsonAdm = JSON.parse(adm); + } catch (err) {} + if (jsonAdm && jsonAdm.native) { + ext = ext || {}; + ext.native = jsonAdm.native; + adm = null; + } + if (adm) { + bid.width = serverBid.w; + bid.height = serverBid.h; + bid.ad = adm; + bid.mediaType = BANNER; + } else if (ext && ext.native) { + bid.native = parseNative(ext.native); + bid.mediaType = NATIVE; + } bidResponses.push(bid); }) @@ -117,9 +227,9 @@ export const spec = { }, /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} The bid that won the auction - */ + * 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) { const cpm = utils.deepAccess(bid, 'adserverTargeting.hb_pb') || ''; if (utils.isStr(bid.burl) && bid.burl !== '') { @@ -127,7 +237,8 @@ export const spec = { utils.triggerPixel(bid.burl); } }, -} +}; + registerBidder(spec); function getLanguage() { @@ -149,3 +260,73 @@ function createBannerRequest(bid) { } return r } + +function parseNative(native) { + const {assets, link, imptrackers, jstracker} = native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || [], + impressionTrackers: imptrackers || [], + javascriptTrackers: jstracker ? [jstracker] : [] + }; + + (assets || []).forEach((asset) => { + const {id, img, data, title} = asset; + const key = NATIVE_ID_MAP[id]; + if (key) { + if (!utils.isEmpty(title)) { + result.title = title.text + } else if (!utils.isEmpty(img)) { + result[key] = { + url: img.url, + height: img.h, + width: img.w + } + } else if (!utils.isEmpty(data)) { + result[key] = data.value; + } + } + }); + + return result; +} + +function createNativeRequest(bid) { + const assets = []; + if (bid.nativeParams) { + Object.keys(bid.nativeParams).forEach((key) => { + if (NATIVE_PARAMS[key]) { + const {name, type, id} = NATIVE_PARAMS[key]; + const assetObj = type ? {type} : {}; + let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key]; + if (len) { + assetObj.len = len; + } + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + let wmin = aRatios.min_width || 0; + let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + assetObj.wmin = wmin; + assetObj.hmin = hmin; + } + if (sizes && sizes.length) { + sizes = [].concat(...sizes); + assetObj.w = sizes[0]; + assetObj.h = sizes[1]; + } + const asset = {required: required ? 1 : 0, id}; + asset[name] = assetObj; + assets.push(asset); + } + }); + } + return { + ver: '1.2', + request: { + assets: assets, + context: 1, + plcmttype: 1, + ver: '1.2' + } + } +} diff --git a/modules/mediaforceBidAdapter.md b/modules/mediaforceBidAdapter.md index e16d4178b3f..f8e6903516f 100644 --- a/modules/mediaforceBidAdapter.md +++ b/modules/mediaforceBidAdapter.md @@ -33,3 +33,36 @@ Module that connects to mediaforce's demand sources } ]; ``` + +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + native: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + sizes: [420, 315], + }, + sponsoredBy: { + required: false + } + } + }, + bids: [ + { + bidder: "mediaforce", + params: { + placement_id: 'pl12345', // required + publisher_id: 'pub111', // required + is_test: true + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index 09d997e9349..ee478acbc83 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -1,6 +1,7 @@ import {assert} from 'chai'; import {spec} from 'modules/mediaforceBidAdapter.js'; import * as utils from '../../../src/utils.js'; +import {BANNER, NATIVE} from '../../../src/mediaTypes.js'; describe('mediaforce bid adapter', function () { let sandbox; @@ -19,7 +20,7 @@ describe('mediaforce bid adapter', function () { } const language = getLanguage(); - const baseUrl = 'https://rtb.mfadsrvr.com' + const baseUrl = 'https://rtb.mfadsrvr.com'; describe('isBidRequestValid()', function () { const defaultBid = { @@ -56,17 +57,6 @@ describe('mediaforce bid adapter', function () { bid.params = {publisher_id: 2, placement_id: '123'}; assert.equal(spec.isBidRequestValid(bid), true); }); - - it('should return false when mediaTypes == native passed (native is not supported yet)', function () { - let bid = utils.deepClone(defaultBid); - bid.mediaTypes = { - native: { - sizes: [[300, 250]] - } - }; - bid.params = {publisher_id: 2, placement_id: '123'}; - assert.equal(spec.isBidRequestValid(bid), true); - }); }); describe('buildRequests()', function () { @@ -76,9 +66,35 @@ describe('mediaforce bid adapter', function () { publisher_id: 'pub123', placement_id: '202', }, + nativeParams: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + sizes: [300, 250], + }, + sponsoredBy: { + required: true + } + }, mediaTypes: { banner: { sizes: [[300, 250]] + }, + native: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + sizes: [300, 250], + }, + sponsoredBy: { + required: true + } } }, transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', @@ -95,7 +111,7 @@ describe('mediaforce bid adapter', function () { const requestUrl = `${baseUrl}/header_bid`; const dnt = utils.getDNT() ? 1 : 0; - const secure = 1 + const secure = 1; it('should return undefined if no validBidRequests passed', function () { assert.equal(spec.buildRequests([]), undefined); @@ -135,13 +151,26 @@ describe('mediaforce bid adapter', function () { secure: secure, bidfloor: bid.params.bidfloor, banner: {w: 300, h: 250}, + native: { + ver: '1.2', + request: { + assets: [ + {id: 1, title: {len: 800}, required: 1}, + {id: 3, img: {w: 300, h: 250, type: 3}, required: 1}, + {id: 5, data: {type: 1}, required: 1} + ], + context: 1, + plcmttype: 1, + ver: '1.2' + } + }, }], }); assert.deepEqual(request, { method: 'POST', url: requestUrl, - data: '{"id":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b","site":{"page":"https%3A%2F%2Fwww.prebid.org","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"imp":[{"tagid":"202","secure":1,"bidfloor":0.5,"banner":{"w":300,"h":250}}]}', + data: '{"id":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b","site":{"page":"https%3A%2F%2Fwww.prebid.org","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"imp":[{"tagid":"202","secure":1,"bidfloor":0.5,"banner":{"w":300,"h":250},"native":{"ver":"1.2","request":{"assets":[{"required":1,"id":1,"title":{"len":800}},{"required":1,"id":3,"img":{"type":3,"w":300,"h":250}},{"required":1,"id":5,"data":{"type":1}}],"context":1,"plcmttype":1,"ver":"1.2"}}}]}', }); }); @@ -173,6 +202,7 @@ describe('mediaforce bid adapter', function () { cid: '2_ssl', h: 100, cat: ['IAB1-1'], + dealid: '3901521', crid: '2_ssl', impid: '2b3c9d103723a7', adid: '2_ssl', @@ -193,11 +223,13 @@ describe('mediaforce bid adapter', function () { assert.deepEqual(bids, ([{ ad: bid.adm, cpm: bid.price, + dealId: bid.dealid, creativeId: bid.adid, currency: response.body.cur, height: bid.h, netRevenue: true, burl: bid.burl, + mediaType: BANNER, requestId: bid.impid, ttl: 300, width: bid.w, @@ -205,6 +237,174 @@ describe('mediaforce bid adapter', function () { }); }); + describe('interpretResponse() native as object', function () { + it('successfull response', function () { + let titleText = 'Colorado Drivers With No DUI\'s Getting A Pay Day on Friday'; + let imgData = { + url: `${baseUrl}/image`, + w: 1200, + h: 627 + }; + let nativeLink = `${baseUrl}/click/`; + let nativeTracker = `${baseUrl}/imp-image`; + let sponsoredByValue = 'Comparisons.org'; + let bodyValue = 'Drivers With No Tickets In 3 Years Should Do This On June'; + let bid = { + price: 3, + id: '65599d0a-42d2-446a-9d39-6086c1433ffe', + burl: `${baseUrl}/burl/\${AUCTION_PRICE}`, + cid: '2_ssl', + cat: ['IAB1-1'], + crid: '2_ssl', + impid: '2b3c9d103723a7', + adid: '2_ssl', + ext: { + advertiser_name: 'MediaForce', + native: { + link: {url: nativeLink}, + assets: [{ + id: 1, + title: {text: titleText}, + required: 1 + }, { + id: 3, + img: imgData + }, { + id: 5, + data: {value: sponsoredByValue} + }, { + id: 4, + data: {value: bodyValue} + }], + imptrackers: [nativeTracker], + ver: '1' + }, + language: 'en', + agency_name: 'MediaForce DSP' + } + }; + + let response = { + body: { + seatbid: [{ + bid: [bid] + }], + cur: 'USD', + id: '620190c2-7eef-42fa-91e2-f5c7fbc2bdd3' + } + }; + + let bids = spec.interpretResponse(response); + assert.deepEqual(bids, ([{ + native: { + clickUrl: nativeLink, + clickTrackers: [], + impressionTrackers: [nativeTracker], + javascriptTrackers: [], + title: titleText, + image: { + url: imgData.url, + width: imgData.w, + height: imgData.h + }, + sponsoredBy: sponsoredByValue, + body: bodyValue + }, + cpm: bid.price, + creativeId: bid.adid, + currency: response.body.cur, + netRevenue: true, + burl: bid.burl, + mediaType: NATIVE, + requestId: bid.impid, + ttl: 300, + }])); + }); + }); + + describe('interpretResponse() native as string', function () { + it('successfull response', function () { + let titleText = 'Colorado Drivers With No DUI\'s Getting A Pay Day on Friday'; + let imgData = { + url: `${baseUrl}/image`, + w: 1200, + h: 627 + }; + let nativeLink = `${baseUrl}/click/`; + let nativeTracker = `${baseUrl}/imp-image`; + let sponsoredByValue = 'Comparisons.org'; + let bodyValue = 'Drivers With No Tickets In 3 Years Should Do This On June'; + let adm = JSON.stringify({ + native: { + link: {url: nativeLink}, + assets: [{ + id: 1, + title: {text: titleText}, + required: 1 + }, { + id: 3, + img: imgData + }, { + id: 5, + data: {value: sponsoredByValue} + }, { + id: 4, + data: {value: bodyValue} + }], + imptrackers: [nativeTracker], + ver: '1' + } + }); + let bid = { + price: 3, + id: '65599d0a-42d2-446a-9d39-6086c1433ffe', + burl: `${baseUrl}/burl/\${AUCTION_PRICE}`, + cid: '2_ssl', + cat: ['IAB1-1'], + crid: '2_ssl', + impid: '2b3c9d103723a7', + adid: '2_ssl', + adm: adm + }; + + let response = { + body: { + seatbid: [{ + bid: [bid] + }], + cur: 'USD', + id: '620190c2-7eef-42fa-91e2-f5c7fbc2bdd3' + } + }; + + let bids = spec.interpretResponse(response); + assert.deepEqual(bids, ([{ + native: { + clickUrl: nativeLink, + clickTrackers: [], + impressionTrackers: [nativeTracker], + javascriptTrackers: [], + title: titleText, + image: { + url: imgData.url, + width: imgData.w, + height: imgData.h + }, + sponsoredBy: sponsoredByValue, + body: bodyValue + }, + cpm: bid.price, + creativeId: bid.adid, + currency: response.body.cur, + netRevenue: true, + burl: bid.burl, + mediaType: NATIVE, + requestId: bid.impid, + ttl: 300, + }])); + }); + }); + describe('onBidWon()', function () { beforeEach(function() { sinon.stub(utils, 'triggerPixel'); From 9d85fde26a6b1d3280ecc26a05c99a7ae2edb59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deivydas=20=C5=A0abaras?= Date: Fri, 24 Jul 2020 00:13:35 +0100 Subject: [PATCH 0024/1476] OpenX test parameter is added in order to help publishers test video inventory (#5516) Co-authored-by: Deivydas Sabaras <> --- modules/openxBidAdapter.js | 4 +++ test/spec/modules/openxBidAdapter_spec.js | 37 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index d5630c2fad4..ba2f7c5a45f 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -416,6 +416,10 @@ function generateVideoParameters(bid, bidderRequest) { queryParams.vmimes = oxVideoConfig.mimes; } + if (bid.params.test) { + queryParams.vtest = 1; + } + return queryParams; } diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index a6fbc9666b9..ad110bcfb53 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -290,6 +290,38 @@ describe('OpenxAdapter', function () { expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); }); }); + + describe('and request config uses test', () => { + const videoBidWithTest = { + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain', + test: true + }, + adUnitCode: 'adunit-code', + mediaTypes: { + video: { + playerSize: [640, 480] + } + }, + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }; + + let mockBidderRequest = {refererInfo: {}}; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(videoBidWithTest)).to.equal(true); + }); + + it('should send video bid request to openx url via GET, with vtest=1 video parameter', function () { + const request = spec.buildRequests([videoBidWithTest], mockBidderRequest); + expect(request[0].data.vtest).to.equal(1); + }); + }); }); }); @@ -1126,6 +1158,11 @@ describe('OpenxAdapter', function () { expect(dataParams.vwd).to.equal(640); }); + it('shouldn\'t have the test parameter', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + expect(request[0].data.vtest).to.be.undefined; + }); + it('should send a bc parameter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); const dataParams = request[0].data; From accd7574bacf14139d8657e2b6a13ebf2356a07c Mon Sep 17 00:00:00 2001 From: susyt Date: Thu, 23 Jul 2020 16:42:08 -0700 Subject: [PATCH 0025/1476] GumGum: add default sizes (#5492) * uses encodeURIComponent inline * adds test for jcsi param * adds request delay depending on previous response * adds inVideo param * fixes video bug * adds unit test --- modules/gumgumBidAdapter.js | 10 ++++-- test/spec/modules/gumgumBidAdapter_spec.js | 36 ++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 54d845be7ad..8364cd57579 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -219,9 +219,15 @@ function buildRequests (validBidRequests, bidderRequest) { transactionId, userId = {} } = bidRequest; - const bannerSizes = mediaTypes.banner && mediaTypes.banner.sizes; + let sizes = [1, 1]; let data = {}; + if (mediaTypes.banner) { + sizes = mediaTypes.banner.sizes; + } else if (mediaTypes.video) { + sizes = mediaTypes.video.playerSize; + } + if (pageViewId) { data.pv = pageViewId; } @@ -278,7 +284,7 @@ function buildRequests (validBidRequests, bidderRequest) { tId: transactionId, pi: data.pi, selector: params.selector, - sizes: bannerSizes, + sizes, url: BID_ENDPOINT, method: 'GET', data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId), _getTradeDeskIDParam(userId)) diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 7022f8f96dd..65ec52aa8c4 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -114,6 +114,24 @@ describe('gumgumAdapter', function () { } } ]; + const vidMediaTypes = { + video: { + playerSize: [640, 480], + context: 'instream', + minduration: 1, + maxduration: 2, + linearity: 1, + startdelay: 1, + placement: 123456, + protocols: [1, 2] + } + }; + + it('should return a defined sizes field for video', function () { + const request = { ...bidRequests[0], mediaTypes: vidMediaTypes, params: { 'videoPubID': 123 } }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.sizes).to.equal(vidMediaTypes.video.playerSize); + }); it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests)[0]; @@ -145,21 +163,7 @@ describe('gumgumAdapter', function () { expect(bidRequest.data).to.not.include.any.keys('t'); }); it('should send pubId if videoPubID param is specified', function () { - const mediaTypes = { - video: { - playerSize: [640, 480], - context: 'instream', - minduration: 1, - maxduration: 2, - linearity: 1, - startdelay: 1, - placement: 123456, - protocols: [1, 2] - } - }; - const request = Object.assign({}, bidRequests[0]); - request.mediaTypes = mediaTypes - request.params = { 'videoPubID': 123 }; + const request = { ...bidRequests[0], mediaTypes: vidMediaTypes, params: { 'videoPubID': 123 } }; const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data).to.include.any.keys('pubId'); expect(bidRequest.data.pubId).to.equal(request.params.videoPubID); @@ -423,7 +427,7 @@ describe('gumgumAdapter', function () { }); it('updates jcsi object when the server response jcsi prop is found', function () { - const response = Object.assign({cw: 'AD_JSON'}, serverResponse); + const response = Object.assign({ cw: 'AD_JSON' }, serverResponse); const bidResponse = spec.interpretResponse({ body: response }, bidRequest)[0].ad; const decodedResponse = JSON.parse(atob(bidResponse)); expect(decodedResponse.jcsi).to.eql(JCSI); From a71d3766eb1586108826e645c07bd17c473e57cf Mon Sep 17 00:00:00 2001 From: mshuhaliia <48407028+mshuhaliia@users.noreply.github.com> Date: Fri, 24 Jul 2020 03:25:19 +0300 Subject: [PATCH 0026/1476] waardexAdaper, removed placementId from request (#5507) * waardexAdaper, removed placementId from request * Update package-lock.json Co-authored-by: Max Shuhaliia --- modules/waardexBidAdapter.js | 11 +++++------ test/spec/modules/waardexBidAdapter_spec.js | 19 ++++++------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/modules/waardexBidAdapter.js b/modules/waardexBidAdapter.js index 255bf24098b..b9114d4f1bf 100644 --- a/modules/waardexBidAdapter.js +++ b/modules/waardexBidAdapter.js @@ -62,7 +62,6 @@ function buildBidRequests(validBidRequests) { const item = { bidId: validBidRequest.bidId, - placementId: params.placementId, bidfloor: parseFloat(params.bidfloor) || 0, position: parseInt(params.position) || 1, instl: parseInt(params.instl) || 0, @@ -170,7 +169,7 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], - isBidRequestValid: (bid) => Boolean(bid.bidId && bid.params && +bid.params.placementId && +bid.params.pubId), + isBidRequestValid: (bid) => Boolean(bid.bidId && bid.params && +bid.params.zoneId), /** * @param {Object[]} validBidRequests - array of valid bid requests @@ -181,12 +180,12 @@ export const spec = { const payload = getCommonBidsData(bidderRequest); payload.bidRequests = buildBidRequests(validBidRequests); - let pubId = ''; - if (validBidRequests[0] && validBidRequests[0].params && +validBidRequests[0].params.pubId) { - pubId = +validBidRequests[0].params.pubId; + let zoneId = ''; + if (validBidRequests[0] && validBidRequests[0].params && +validBidRequests[0].params.zoneId) { + zoneId = +validBidRequests[0].params.zoneId; } - const url = `${ENDPOINT}?pubId=${pubId}`; + const url = `${ENDPOINT}?pubId=${zoneId}`; return { method: 'POST', diff --git a/test/spec/modules/waardexBidAdapter_spec.js b/test/spec/modules/waardexBidAdapter_spec.js index fae70335d3c..8732b2bd51f 100644 --- a/test/spec/modules/waardexBidAdapter_spec.js +++ b/test/spec/modules/waardexBidAdapter_spec.js @@ -10,12 +10,12 @@ describe('waardexBidAdapter', () => { params: { placementId: 1, traffic: 'banner', - pubId: 1, + zoneId: 1, } }; describe('isBidRequestValid', () => { - it('Should return true. bidId and params such as placementId and pubId are present', () => { + it('Should return true. bidId and params such as placementId and zoneId are present', () => { expect(spec.isBidRequestValid(validBid)).to.be.true; }); it('Should return false. bidId is not present in bid object', () => { @@ -23,14 +23,9 @@ describe('waardexBidAdapter', () => { delete invalidBid.bidId; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); - it('Should return false. placementId is not present in bid.params object', () => { + it('Should return false. zoneId is not present in bid.params object', () => { const invalidBid = deepClone(validBid); - delete invalidBid.params.placementId; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - it('Should return false. pubId is not present in bid.params object', () => { - const invalidBid = deepClone(validBid); - delete invalidBid.params.pubId; + delete invalidBid.params.zoneId; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); }); @@ -45,11 +40,10 @@ describe('waardexBidAdapter', () => { } }, params: { - placementId: 1, bidfloor: 1.5, position: 1, instl: 1, - pubId: 100 + zoneId: 100 }, }]; @@ -72,7 +66,6 @@ describe('waardexBidAdapter', () => { expect(payload.bidRequests[0]).deep.equal({ bidId: validBidRequests[0].bidId, - placementId: validBidRequests[0].params.placementId, bidfloor: validBidRequests[0].params.bidfloor, position: validBidRequests[0].params.position, instl: validBidRequests[0].params.instl, @@ -89,7 +82,7 @@ describe('waardexBidAdapter', () => { ], } }); - const ENDPOINT = `https://hb.justbidit.xyz:8843/prebid?pubId=${validBidRequests[0].params.pubId}`; + const ENDPOINT = `https://hb.justbidit.xyz:8843/prebid?pubId=${validBidRequests[0].params.zoneId}`; expect(url).to.equal(ENDPOINT); expect(method).to.equal('POST'); }); From c933c4f755ec40dcc4ee945a24e344e76e84fd07 Mon Sep 17 00:00:00 2001 From: Jimmy Tu Date: Thu, 23 Jul 2020 18:08:18 -0700 Subject: [PATCH 0027/1476] OpenX: Add floor module support (#5468) --- modules/openxBidAdapter.js | 20 ++++++- test/spec/modules/openxBidAdapter_spec.js | 72 +++++++++++++++++------ 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index ba2f7c5a45f..3d2b652d403 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -336,8 +336,10 @@ function buildOXBannerRequest(bids, bidderRequest) { let customFloorsForAllBids = []; let hasCustomFloor = false; bids.forEach(function (bid) { - if (bid.params.customFloor) { - customFloorsForAllBids.push((Math.round(bid.params.customFloor * 100) / 100) * 1000); + let floor = getBidFloor(bid, BANNER); + + if (floor) { + customFloorsForAllBids.push(floor); hasCustomFloor = true; } else { customFloorsForAllBids.push(0); @@ -453,4 +455,18 @@ function createVideoBidResponses(response, {bid, startTime}) { return bidResponses; } +function getBidFloor(bidRequest, mediaType) { + let floorInfo = {}; + if (typeof bidRequest.getFloor === 'function') { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: mediaType, + size: '*' + }); + } + let floor = floorInfo.floor || bidRequest.params.customFloor || 0; + + return Math.round(floor * 1000); // normalize to microCpm +} + registerBidder(spec); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index ad110bcfb53..7fc490a3838 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -540,25 +540,6 @@ describe('OpenxAdapter', function () { expect(dataParams.tps).to.equal(btoa('test1=testval1.&test2=testval2_,testval3')); }); - it('should send out custom floors on bids that have customFloors specified', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - params: { - 'unit': '12345678', - 'delDomain': 'test-del-domain', - 'customFloor': 1.500001 - } - } - ); - - const request = spec.buildRequests([bidRequest], mockBidderRequest); - const dataParams = request[0].data; - - expect(dataParams.aumfs).to.exist; - expect(dataParams.aumfs).to.equal('1500'); - }); - it('should send out custom bc parameter, if override is present', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], @@ -1121,6 +1102,59 @@ describe('OpenxAdapter', function () { }); }); }); + + context('floors', function () { + it('should send out custom floors on bids that have customFloors specified', function () { + const bidRequest = Object.assign({}, + bidRequestsWithMediaTypes[0], + { + params: { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'customFloor': 1.500001 + } + } + ); + + const request = spec.buildRequests([bidRequest], mockBidderRequest); + const dataParams = request[0].data; + + expect(dataParams.aumfs).to.exist; + expect(dataParams.aumfs).to.equal('1500'); + }); + + it('should send out floors on bids when there', function () { + const bidRequest1 = Object.assign({}, + bidRequestsWithMediaTypes[0], + { + getFloor: () => { + return { + currency: 'AUS', + floor: 9.99 + } + } + } + ); + + const bidRequest2 = Object.assign({}, + bidRequestsWithMediaTypes[1], + { + getFloor: () => { + return { + currency: 'AUS', + floor: 18.881 + } + } + } + ); + + const request = spec.buildRequests([bidRequest1, bidRequest2], mockBidderRequest); + const dataParams = request[0].data; + + expect(dataParams.aumfs).to.exist; + expect(dataParams.aumfs).to.equal('9990,18881'); + }); + }) }); describe('buildRequests for video', function () { From abb33d66ac9ca1472c2d3d8eff296bc3dc7c07a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Mon, 27 Jul 2020 16:55:07 +0300 Subject: [PATCH 0028/1476] Vidazoo Adapter: Feature/alternate-param-names (#5540) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(client): alternate param names Co-authored-by: roman --- modules/vidazooBidAdapter.js | 20 +++++++++++++++-- test/spec/modules/vidazooBidAdapter_spec.js | 24 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 3eea270dc8d..b9f3297818f 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -32,16 +32,31 @@ export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; } +export function extractCID(params) { + return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; +} + +export function extractPID(params) { + return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; +} + +export function extractSubDomain(params) { + return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; +} + function isBidRequestValid(bid) { const params = bid.params || {}; - return !!(params.cId && params.pId); + return !!(extractCID(params) && extractPID(params)); } function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const { params, bidId, userId, adUnitCode } = bid; - const { bidFloor, cId, pId, ext, subDomain } = params; + const { bidFloor, ext } = params; const hashUrl = hashCode(topWindowUrl); const dealId = getNextDealId(hashUrl); + const cId = extractCID(params); + const pId = extractPID(params); + const subDomain = extractSubDomain(params); let data = { url: encodeURIComponent(topWindowUrl), @@ -70,6 +85,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { if (bidderRequest.uspConsent) { data.usPrivacy = bidderRequest.uspConsent } + const dto = { method: 'POST', url: `${createDomain(subDomain)}/prebid/multi/${cId}`, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index b8ab83a95ae..103cb0897a7 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain } from 'modules/vidazooBidAdapter.js'; +import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain, extractPID, extractCID, extractSubDomain } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; import { version } from 'package.json'; @@ -220,7 +220,7 @@ describe('VidazooBidAdapter', function () { }); }); - describe(`user id system`, function () { + describe('user id system', function () { Object.keys(SUPPORTED_ID_SYSTEMS).forEach((idSystemProvider) => { const id = Date.now().toString(); const bid = utils.deepClone(BID); @@ -243,4 +243,24 @@ 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' }); + 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'); + }); + }); }); From 93e1b9310f0769483946712bc75391208ae6885c Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 27 Jul 2020 13:18:52 -0400 Subject: [PATCH 0029/1476] Update PR_REVIEW.md (#5536) solves for https://github.com/prebid/Prebid.js/issues/5237 --- PR_REVIEW.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index dac50593d6e..6402fcbbbaa 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -41,6 +41,7 @@ For modules and core platform updates, the initial reviewer should request an ad - Requests to the bidder should support HTTPS - Responses from the bidder should be compressed (such as gzip, compress, deflate) - Bid responses may not use JSONP: All requests must be AJAX with JSON responses +- Video openrtb params should be read from the ad unit when available; however bidder config can override the ad unit. - All user-sync (aka pixel) activity must be registered via the provided functions - Adapters may not use the $$PREBID_GLOBAL$$ variable - All adapters must support the creation of multiple concurrent instances. This means, for example, that adapters cannot rely on mutable global variables. From ec030f4de0832de9f0f1b35e51dbfa4a8b7d3b66 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Mon, 27 Jul 2020 14:32:00 -0400 Subject: [PATCH 0030/1476] Pbadslot phase2 (#5525) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * Send both pbAdSlot and GAM ad unit if in FPD Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Isaac Dettman Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday --- modules/prebidServerBidAdapter/index.js | 11 +- modules/rubiconBidAdapter.js | 22 ++- .../modules/prebidServerBidAdapter_spec.js | 88 +++++++++++- test/spec/modules/rubiconBidAdapter_spec.js | 129 ++++++++++++++---- 4 files changed, 217 insertions(+), 33 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 273684c86a5..1007305b324 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -547,7 +547,16 @@ const OPEN_RTB_PROTOCOL = { */ const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot); + utils.deepSetValue(imp, 'ext.context.data.pbadslot', pbAdSlot); + } + + /** + * GAM Ad Unit + * @type {(string|undefined)} + */ + const gamAdUnit = utils.deepAccess(adUnit, 'fpd.context.adServer.adSlot'); + if (typeof gamAdUnit === 'string' && gamAdUnit) { + utils.deepSetValue(imp, 'ext.context.data.adslot', gamAdUnit); } Object.assign(imp, mediaTypes); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index cd621010a9b..0c563987331 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -332,7 +332,16 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); + utils.deepSetValue(data.imp[0].ext, 'context.data.pbadslot', pbAdSlot); + } + + /** + * GAM Ad Unit + * @type {(string|undefined)} + */ + const gamAdUnit = utils.deepAccess(bidRequest, 'fpd.context.adServer.adSlot'); + if (typeof gamAdUnit === 'string' && gamAdUnit) { + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', gamAdUnit); } // if storedAuctionResponse has been set, pass SRID @@ -593,7 +602,16 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); + data['tg_i.pbadslot'] = pbAdSlot.replace(/^\/+/, ''); + } + + /** + * GAM Ad Unit + * @type {(string|undefined)} + */ + const gamAdUnit = utils.deepAccess(bidRequest, 'fpd.context.adServer.adSlot'); + if (typeof gamAdUnit === 'string' && gamAdUnit) { + data['tg_i.dfp_ad_unit_code'] = gamAdUnit.replace(/^\/+/, ''); } // digitrust properties diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 58e607ebb89..654971f0404 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1411,6 +1411,80 @@ describe('S2S Adapter', function () { }); describe('pbAdSlot config', function () { + it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + }); + + it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = {}; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + }); + + it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '' + } + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + }); + + it('should send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '/a/b/c' + } + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0].ext.context.data.pbadslot).to.equal('/a/b/c'); + }); + }); + + describe('GAM ad unit config', function () { it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; @@ -1426,7 +1500,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1442,7 +1516,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" is empty string', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1450,7 +1524,9 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); bidRequest.ad_units[0].fpd = { context: { - pbAdSlot: '' + adServer: { + adSlot: '' + } } }; @@ -1462,7 +1538,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { + it('should send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" value is a non-empty string', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1470,7 +1546,9 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); bidRequest.ad_units[0].fpd = { context: { - pbAdSlot: '/a/b/c' + adServer: { + adSlot: '/a/b/c' + } } }; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 11ed17df5f8..c21a9b879f1 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1364,25 +1364,25 @@ describe('the rubicon adapter', function () { } }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context\" object is not valid', function () { + it('should not send \"tg_i.pbadslot’\" if \"fpd.context\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); expect(data).to.be.an('Object'); - expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" is undefined', function () { + it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" is undefined', function () { bidderRequest.bids[0].fpd = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); expect(data).to.be.an('Object'); - expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { + it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { bidderRequest.bids[0].fpd = { context: { pbAdSlot: '' @@ -1393,10 +1393,10 @@ describe('the rubicon adapter', function () { const data = parseQuery(request.data); expect(data).to.be.an('Object'); - expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + expect(data).to.not.have.property('tg_i.pbadslot'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { + it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { bidderRequest.bids[0].fpd = { context: { pbAdSlot: 'abc' @@ -1406,15 +1406,92 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.pbadslot'); + expect(data['tg_i.pbadslot']).to.equal('abc'); + }); + + it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '/a/b/c' + } + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.pbadslot'); + expect(data['tg_i.pbadslot']).to.equal('a/b/c'); + }); + }); + + describe('GAM ad unit', function () { + beforeEach(function () { + // enforce that the bid at 0 does not have a 'context' property + if (bidderRequest.bids[0].hasOwnProperty('fpd')) { + delete bidderRequest.bids[0].fpd; + } + }); + + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context\" object is not valid', function () { + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); + }); + + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" is undefined', function () { + bidderRequest.bids[0].fpd = {}; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); + }); + + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" value is an empty string', function () { + bidderRequest.bids[0].fpd = { + context: { + adServer: { + adSlot: '' + } + } + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string', function () { + bidderRequest.bids[0].fpd = { + context: { + adServer: { + adSlot: 'abc' + } + } + } + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + expect(data).to.be.an('Object'); expect(data).to.have.property('tg_i.dfp_ad_unit_code'); expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string, but all leading slash characters should be removed', function () { bidderRequest.bids[0].fpd = { context: { - pbAdSlot: '/a/b/c' + adServer: { + adSlot: 'a/b/c' + } } }; @@ -1839,6 +1916,24 @@ describe('the rubicon adapter', function () { bidderRequest.auctionStart + 100 ); + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].ext.context.data.pbadslot).to.equal('1234567890'); + }); + + it('should include GAM ad unit in bid request', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].fpd = { + context: { + adServer: { + adSlot: '1234567890' + } + } + }; + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); }); @@ -1856,22 +1951,6 @@ describe('the rubicon adapter', function () { }); }); - it('should include pbAdSlot in bid request', function () { - createVideoBidderRequest(); - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '1234567890' - } - }; - - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); - }); - describe('combineSlotUrlParams', function () { it('should combine an array of slot url params', function () { expect(spec.combineSlotUrlParams([])).to.deep.equal({}); From 0d04c36932fb29838ff05e4d51542a2155e07594 Mon Sep 17 00:00:00 2001 From: Ilya Medvedev Date: Wed, 29 Jul 2020 03:18:49 +0600 Subject: [PATCH 0031/1476] Add host field into limelight bidder adapter (#5363) * Add host field into limelight bidder adapter * Add host field into limelight bidder adapter * Remove 'Object.entries' function call * Add host field into limelight bidder adapter * Add some tests --- modules/projectLimeLightBidAdapter.js | 71 ++++---- modules/projectLimeLightBidAdapter.md | 2 + .../projectLimeLightBidAdapter_spec.js | 163 ++++++++++++++---- 3 files changed, 162 insertions(+), 74 deletions(-) diff --git a/modules/projectLimeLightBidAdapter.js b/modules/projectLimeLightBidAdapter.js index f2ff77f6229..1beba906917 100644 --- a/modules/projectLimeLightBidAdapter.js +++ b/modules/projectLimeLightBidAdapter.js @@ -4,7 +4,6 @@ import {ajax} from '../src/ajax.js'; import * as utils from '../src/utils.js'; const BIDDER_CODE = 'project-limelight'; -const URL = 'https://ads.project-limelight.com/hb'; /** * Determines whether or not the given bid response is valid. @@ -25,19 +24,6 @@ function isBidResponseValid(bid) { return false; } -function extractBidSizes(bid) { - const bidSizes = []; - - bid.sizes.forEach(size => { - bidSizes.push({ - width: size[0], - height: size[1] - }); - }); - - return bidSizes; -} - export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], @@ -65,30 +51,10 @@ export const spec = { } catch (e) { utils.logMessage(e); winTop = window; - }; - const placements = []; - const request = { - 'secure': (location.protocol === 'https:'), - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'adUnits': placements - }; - for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i]; - const params = bid.params; - placements.push({ - id: params.adUnitId, - bidId: bid.bidId, - transactionId: bid.transactionId, - sizes: extractBidSizes(bid), - type: params.adUnitType.toUpperCase() - }); } - return { - method: 'POST', - url: URL, - data: request - }; + const placements = utils.groupBy(validBidRequests.map(bidRequest => buildPlacement(bidRequest)), 'host') + return Object.keys(placements) + .map(host => buildRequest(winTop, host, placements[host].map(placement => placement.adUnit))); }, onBidWon: (bid) => { @@ -123,3 +89,34 @@ export const spec = { }; registerBidder(spec); + +function buildRequest(winTop, host, adUnits) { + return { + method: 'POST', + url: `https://${host}/hb`, + data: { + secure: (location.protocol === 'https:'), + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + adUnits: adUnits + } + } +} + +function buildPlacement(bidRequest) { + return { + host: bidRequest.params.host, + adUnit: { + id: bidRequest.params.adUnitId, + bidId: bidRequest.bidId, + transactionId: bidRequest.transactionId, + sizes: bidRequest.sizes.map(size => { + return { + width: size[0], + height: size[1] + } + }), + type: bidRequest.params.adUnitType.toUpperCase() + } + } +} diff --git a/modules/projectLimeLightBidAdapter.md b/modules/projectLimeLightBidAdapter.md index 71621983b89..15aa170cd2e 100644 --- a/modules/projectLimeLightBidAdapter.md +++ b/modules/projectLimeLightBidAdapter.md @@ -18,6 +18,7 @@ Module that connects to Project Limelight SSP demand sources bids: [{ bidder: 'project-limelight', params: { + host: 'ads.project-limelight.com', adUnitId: 0, adUnitType: 'banner' } @@ -34,6 +35,7 @@ var videoAdUnit = [{ bids: [{ bidder: 'project-limelight', params: { + host: 'ads.project-limelight.com', adUnitId: 0, adUnitType: 'video' } diff --git a/test/spec/modules/projectLimeLightBidAdapter_spec.js b/test/spec/modules/projectLimeLightBidAdapter_spec.js index 3ffc017f177..778d8eedf7b 100644 --- a/test/spec/modules/projectLimeLightBidAdapter_spec.js +++ b/test/spec/modules/projectLimeLightBidAdapter_spec.js @@ -2,11 +2,12 @@ import {expect} from 'chai'; import {spec} from '../../../modules/projectLimeLightBidAdapter.js'; describe('ProjectLimeLightAdapter', function () { - let bid = { + const bid1 = { bidId: '2dd581a2b6281d', bidder: 'project-limelight', bidderRequestId: '145e1d6a7837c9', params: { + host: 'ads.project-limelight.com', adUnitId: 123, adUnitType: 'banner' }, @@ -14,46 +15,83 @@ describe('ProjectLimeLightAdapter', function () { auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', sizes: [[300, 250]], transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - }; + } + const bid2 = { + bidId: '58ee9870c3164a', + bidder: 'project-limelight', + bidderRequestId: '209fdaf1c81649', + params: { + host: 'cpm.project-limelight.com', + adUnitId: 456, + adUnitType: 'banner' + }, + placementCode: 'placement_1', + auctionId: '482f88de-29ab-45c8-981a-d25e39454a34', + sizes: [[350, 200]], + transactionId: '068867d1-46ec-40bb-9fa0-e24611786fb4' + } + const bid3 = { + bidId: '019645c7d69460', + bidder: 'project-limelight', + bidderRequestId: 'f2b15f89e77ba6', + params: { + host: 'ads.project-limelight.com', + adUnitId: 789, + adUnitType: 'video' + }, + placementCode: 'placement_2', + auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', + sizes: [[800, 600]], + transactionId: '738d5915-6651-43b9-9b6b-d50517350917' + } describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid]); - 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'); - }); + const serverRequests = spec.buildRequests([bid1, bid2, bid3]) + it('Creates two ServerRequests', function() { + expect(serverRequests).to.exist + expect(serverRequests).to.have.lengthOf(2) + }) + serverRequests.forEach(serverRequest => { + 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 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', 'secure', 'adUnits') + expect(data.deviceWidth).to.be.a('number') + expect(data.deviceHeight).to.be.a('number') + expect(data.secure).to.be.a('boolean') + data.adUnits.forEach(adUnit => { + expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId') + expect(adUnit.id).to.be.a('number') + expect(adUnit.bidId).to.be.a('string') + expect(adUnit.type).to.be.a('string') + expect(adUnit.transactionId).to.be.a('string') + expect(adUnit.sizes).to.be.an('array') + }) + }) + }) it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://ads.project-limelight.com/hb'); - }); - 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', 'secure', 'adUnits'); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.secure).to.be.a('boolean'); - let adUnits = data['adUnits']; - for (let i = 0; i < adUnits.length; i++) { - let adUnit = adUnits[i]; - expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId'); - expect(adUnit.id).to.be.a('number'); - expect(adUnit.bidId).to.be.a('string'); - expect(adUnit.type).to.be.a('string'); - expect(adUnit.transactionId).to.be.a('string'); - expect(adUnit.sizes).to.be.an('array'); - } - }); + expect(serverRequests[0].url).to.equal('https://ads.project-limelight.com/hb') + expect(serverRequests[1].url).to.equal('https://cpm.project-limelight.com/hb') + }) + it('Returns valid adUnits', function () { + validateAdUnit(serverRequests[0].data.adUnits[0], bid1) + validateAdUnit(serverRequests[1].data.adUnits[0], bid2) + validateAdUnit(serverRequests[0].data.adUnits[1], bid3) + }) it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([]); - let data = serverRequest.data; - expect(data.adUnits).to.be.an('array').that.is.empty; - }); - }); + const serverRequests = spec.buildRequests([]) + expect(serverRequests).to.be.an('array').that.is.empty + }) + }) describe('interpretBannerResponse', function () { let resObject = { body: [ { @@ -167,4 +205,55 @@ describe('ProjectLimeLightAdapter', function () { expect(spec.isBidRequestValid(bidFailed)).to.equal(false); }); }); + describe('interpretResponse', function() { + let resObject = { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

Hello ad

', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + }; + it('should skip responses which do not contain required params', function() { + let bidResponses = { + body: [ { + mediaType: 'banner', + cpm: 0.3, + ttl: 1000, + currency: 'USD' + }, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + }); + it('should skip responses which do not contain expected mediaType', function() { + let bidResponses = { + body: [ { + requestId: '123', + mediaType: 'native', + cpm: 0.3, + creativeId: '123asd', + ttl: 1000, + currency: 'USD' + }, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + }); + }); }); + +function validateAdUnit(adUnit, bid) { + expect(adUnit.id).to.equal(bid.params.adUnitId) + expect(adUnit.bidId).to.equal(bid.bidId) + expect(adUnit.type).to.equal(bid.params.adUnitType.toUpperCase()) + expect(adUnit.transactionId).to.equal(bid.transactionId) + expect(adUnit.sizes).to.deep.equal(bid.sizes.map(size => { + return { + width: size[0], + height: size[1] + } + })) +} From aef55170efc2d277872fdcc3e704cefd45588acf Mon Sep 17 00:00:00 2001 From: trchandraprakash <47793448+trchandraprakash@users.noreply.github.com> Date: Wed, 29 Jul 2020 01:22:01 -0700 Subject: [PATCH 0032/1476] New Bid Adapter - Saambaa (#5526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Bidder Code * LunaMedia Adapater LunaMedia Adapater * Updated LunamediaBidAdapter.md test params and valid pub code for testing * adding it to resolve conflict in locally repo * Accept size parameters Accept size parameters * Based on browserstack testing result Adding a new line. home/circleci/Prebid.js/modules/lunamediaBidAdapter.js 401:22 error Newline required at end of file but not found eol-last ✖ 1 problem (1 error, 0 warnings) 1 error, 0 warnings potentially fixable with the `--fix` option. * updated as per review updated as per review * Saambaa Bidder Adapter Saambaa Bidder Adapter * updated test file * Updating adomain in meta Updating adomain in meta * Updates adomain resolving an error. * Updates related to adomain Updates related to adomain Co-authored-by: Chandra Prakash --- modules/saambaaBidAdapter.js | 401 ++++++++++++++++++++ modules/saambaaBidAdapter.md | 69 ++++ test/spec/modules/saambaaBidAdapter_spec.js | 139 +++++++ 3 files changed, 609 insertions(+) create mode 100755 modules/saambaaBidAdapter.js create mode 100755 modules/saambaaBidAdapter.md create mode 100755 test/spec/modules/saambaaBidAdapter_spec.js diff --git a/modules/saambaaBidAdapter.js b/modules/saambaaBidAdapter.js new file mode 100755 index 00000000000..0e53d2a300d --- /dev/null +++ b/modules/saambaaBidAdapter.js @@ -0,0 +1,401 @@ +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const ADAPTER_VERSION = '1.0'; +const BIDDER_CODE = 'saambaa'; + +export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; +export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; +export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; +export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; +export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; + +let pubid = ''; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid(bidRequest) { + if (typeof bidRequest != 'undefined') { + if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } + if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } + return true; + } else { return false; } + }, + + buildRequests(bids, bidderRequest) { + let requests = []; + let videoBids = bids.filter(bid => isVideoBidValid(bid)); + let bannerBids = bids.filter(bid => isBannerBidValid(bid)); + videoBids.forEach(bid => { + pubid = getVideoBidParam(bid, 'pubid'); + requests.push({ + method: 'POST', + url: VIDEO_ENDPOINT + pubid, + data: createVideoRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + + bannerBids.forEach(bid => { + pubid = getBannerBidParam(bid, 'pubid'); + + requests.push({ + method: 'POST', + url: BANNER_ENDPOINT + pubid, + data: createBannerRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + return requests; + }, + + interpretResponse(serverResponse, {bidRequest}) { + let response = serverResponse.body; + if (response !== null && utils.isEmpty(response) == false) { + if (isVideoBid(bidRequest)) { + let bidResponse = { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, + mediaType: VIDEO, + netRevenue: true + } + + if (response.seatbid[0].bid[0].adm) { + bidResponse.vastXml = response.seatbid[0].bid[0].adm; + bidResponse.adResponse = { + content: response.seatbid[0].bid[0].adm + }; + } else { + bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; + } + + return bidResponse; + } else { + return { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ad: response.seatbid[0].bid[0].adm, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, + mediaType: BANNER, + netRevenue: true + } + } + } + } +}; + +function isBannerBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); +} + +function isVideoBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.video'); +} + +function isVideoBidValid(bid) { + return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); +} + +function isBannerBidValid(bid) { + return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); +} + +function getVideoBidParam(bid, key) { + return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key); +} + +function getBannerBidParam(bid, key) { + return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); +} + +function isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); +} + +function isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); +} + +function getDoNotTrack() { + return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; +} + +function findAndFillParam(o, key, value) { + try { + if (typeof value === 'function') { + o[key] = value(); + } else { + o[key] = value; + } + } catch (ex) {} +} + +function getOsVersion() { + let clientStrings = [ + { s: 'Android', r: /Android/ }, + { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, + { s: 'Mac OS X', r: /Mac OS X/ }, + { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, + { s: 'Linux', r: /(Linux|X11)/ }, + { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, + { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, + { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, + { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, + { s: 'Windows Vista', r: /Windows NT 6.0/ }, + { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, + { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, + { s: 'UNIX', r: /UNIX/ }, + { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } + ]; + let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); + return cs ? cs.s : 'unknown'; +} + +function getFirstSize(sizes) { + return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; +} + +function parseSizes(sizes) { + return utils.parseSizesInput(sizes).map(size => { + let [ width, height ] = size.split('x'); + return { + w: parseInt(width, 10) || undefined, + h: parseInt(height, 10) || undefined + }; + }); +} + +function getVideoSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); +} + +function getBannerSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); +} + +function getTopWindowReferrer() { + try { + return window.top.document.referrer; + } catch (e) { + return ''; + } +} + +function getVideoTargetingParams(bid) { + return Object.keys(Object(bid.params.video)) + .filter(param => includes(VIDEO_TARGETING, param)) + .reduce((obj, param) => { + obj[ param ] = bid.params.video[ param ]; + return obj; + }, {}); +} + +function createVideoRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + // if size is explicitly given via adapter params + let paramSize = getVideoBidParam(bid, 'size'); + let sizes = []; + + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getVideoSizes(bid); + } + const firstSize = getFirstSize(sizes); + + let video = getVideoTargetingParams(bid); + const o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1, + 'os': getOsVersion() + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getVideoBidParam(bid, 'placement'); + let floor = getVideoBidParam(bid, 'floor'); + if (floor == null) { floor = 0.5; } + + for (let j = 0; j < sizes.length; j++) { + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + 'bidfloorcur': 'USD', + 'secure': secure, + 'video': Object.assign({ + 'id': utils.generateUUID(), + 'pos': 0, + 'w': firstSize.w, + 'h': firstSize.h, + 'mimes': DEFAULT_MIMES + }, video) + + }); + } + + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} + +function getTopWindowLocation(bidderRequest) { + let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + return utils.parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); +} + +function createBannerRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + // if size is explicitly given via adapter params + + let paramSize = getBannerBidParam(bid, 'size'); + let sizes = []; + if (typeof paramSize !== 'undefined' && paramSize != '') { + sizes = parseSizes(paramSize); + } else { + sizes = getBannerSizes(bid); + } + + const o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1 + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getBannerBidParam(bid, 'placement'); + for (let j = 0; j < sizes.length; j++) { + let size = sizes[j]; + + let floor = getBannerBidParam(bid, 'floor'); + if (floor == null) { floor = 0.1; } + + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + 'bidfloorcur': 'USD', + 'secure': secure, + 'banner': { + 'id': utils.generateUUID(), + 'pos': 0, + 'w': size['w'], + 'h': size['h'] + } + }); + } + + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} +registerBidder(spec); diff --git a/modules/saambaaBidAdapter.md b/modules/saambaaBidAdapter.md new file mode 100755 index 00000000000..2d391da7628 --- /dev/null +++ b/modules/saambaaBidAdapter.md @@ -0,0 +1,69 @@ +# Overview + +``` +Module Name: Saambaa Bidder Adapter +Module Type: Bidder Adapter +Maintainer: matt.voigt@saambaa.com +``` + +# Description + +Connects to Saambaa exchange for bids. + +Saambaa bid adapter supports Banner and Video ads currently. + +For more informatio + +# Sample Display Ad Unit: For Publishers +```javascript + +var displayAdUnit = [ +{ + code: 'display', + mediaTypes: { + banner: { + sizes: [[300, 250],[320, 50]] + } + } + bids: [{ + bidder: 'saambaa', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + placement: 1234, + size: '320x50' + } + }] +}]; +``` + +# Sample Video Ad Unit: For Publishers +```javascript + +var videoAdUnit = { + code: 'video', + sizes: [320,480], + mediaTypes: { + video: { + playerSize : [[320, 480]], + context: 'instream' + } + }, + bids: [ + { + bidder: 'saambaa', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + placement: 1234, + size: "320x480", + video: { + id: 123, + skip: 1, + mimes : ['video/mp4', 'application/javascript'], + playbackmethod : [2,6], + maxduration: 30 + } + } + } + ] + }; +``` \ No newline at end of file diff --git a/test/spec/modules/saambaaBidAdapter_spec.js b/test/spec/modules/saambaaBidAdapter_spec.js new file mode 100755 index 00000000000..80a85ae895d --- /dev/null +++ b/test/spec/modules/saambaaBidAdapter_spec.js @@ -0,0 +1,139 @@ +import { expect } from 'chai'; +import { spec } from 'modules/saambaaBidAdapter.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; + +describe('saambaaBidAdapter', function () { + let bidRequests; + let bidRequestsVid; + + beforeEach(function () { + bidRequests = [{'bidder': 'saambaa', 'params': {'pubid': '121ab139faf7ac67428a23f1d0a9a71b', 'floor': 0.5, 'placement': 1234, size: '320x250'}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + + bidRequestsVid = [{'bidder': 'saambaa', 'params': {'pubid': '121ab139faf7ac67428a23f1d0a9a71b', 'floor': 1.0, 'placement': 1234, size: '320x480', 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; + }); + + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed for banner', function () { + const bidRequest = bidRequests[0]; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the required params are passed for video', function () { + const bidRequests = bidRequestsVid[0]; + expect(spec.isBidRequestValid(bidRequests)).to.equal(true); + }); + + it('should return false when no pub id params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.pubid = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when no placement params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.placement = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when a bid request is not passed', function () { + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); + + describe('spec.buildRequests', function () { + it('should create a POST request for each bid', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should create a POST request for each bid in video request', function () { + const bidRequest = bidRequestsVid[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should have domain in request', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].data.site.domain).length !== 0; + }); + }); + + describe('spec.interpretResponse', function () { + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { + expect(true).to.equal(true); + } + }); + + it('should return no bids if the response is empty', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { expect(true).to.equal(true); } + }); + + it('should return valid video bid responses', function () { + let _mediaTypes = VIDEO; + const saambaabidreqVid = {'bidRequest': {'mediaTypes': {'video': {'w': 320, 'h': 480}}}}; + const serverResponseVid = {'cur': 'USD', 'id': '25c6ab92aa0e81', 'seatbid': [{'seat': '3', 'bid': [{'crid': '1855', 'h': 480, 'protocol': 2, 'nurl': 'http://nep.advangelists.com/xp/evt?pp=1MO1wiaMhhq7wLRzZZwwwPkJxxKpYEnM5k5MH4qSGm1HR8rp3Nl7vDocvzZzSAvE4pnREL9mQ1kf5PDjk6E8em6DOk7vVrYUH1TYQyqCucd58PFpJNN7h30RXKHHFg3XaLuQ3PKfMuI1qZATBJ6WHcu875y0hqRdiewn0J4JsCYF53M27uwmcV0HnQxARQZZ72mPqrW95U6wgkZljziwKrICM3aBV07TU6YK5R5AyzJRuD6mtrQ2xtHlQ3jXVYKE5bvWFiUQd90t0jOGhPtYBNoOfP7uQ4ZZj4pyucxbr96orHe9PSOn9UpCSWArdx7s8lOfDpwOvbMuyGxynbStDWm38sDgd4bMHnIt762m5VMDNJfiUyX0vWzp05OsufJDVEaWhAM62i40lQZo7mWP4ipoOWLkmlaAzFIMsTcNaHAHiKKqGEOZLkCEhFNM0SLcvgN2HFRULOOIZvusq7TydOKxuXgCS91dLUDxDDDFUK83BFKlMkTxnCzkLbIR1bd9GKcr1TRryOrulyvRWAKAIhEsUzsc5QWFUhmI2dZ1eqnBQJ0c89TaPcnoaP2WipF68UgyiOstf2CBy0M34858tC5PmuQwQYwXscg6zyqDwR0i9MzGH4FkTyU5yeOlPcsA0ht6UcoCdFpHpumDrLUwAaxwGk1Nj8S6YlYYT5wNuTifDGbg22QKXzZBkUARiyVvgPn9nRtXnrd7WmiMYq596rya9RQj7LC0auQW8bHVQLEe49shsZDnAwZTWr4QuYKqgRGZcXteG7RVJe0ryBZezOq11ha9C0Lv0siNVBahOXE35Wzoq4c4BDaGpqvhaKN7pjeWLGlQR04ufWekwxiMWAvjmfgAfexBJ7HfbYNZpq__', 'adid': '61_1855', 'adomain': ['chevrolet.com'], 'price': 2, 'w': 320, 'iurl': 'https://daf37cpxaja7f.cloudfront.net/c61/creative_url_14922301369663_1.png', 'cat': ['IAB2'], 'id': '7f570b40-aca1-4806-8ea8-818ea679c82b_0', 'attr': [], 'impid': '0', 'cid': '61'}]}], 'bidid': '7f570b40-aca1-4806-8ea8-818ea679c82b'} + const bidResponseVid = spec.interpretResponse({ body: serverResponseVid }, saambaabidreqVid); + delete bidResponseVid['vastUrl']; + delete bidResponseVid['ad']; + expect(bidResponseVid).to.deep.equal({ + requestId: bidRequestsVid[0].bidId, + bidderCode: 'saambaa', + creativeId: serverResponseVid.seatbid[0].bid[0].crid, + cpm: serverResponseVid.seatbid[0].bid[0].price, + width: serverResponseVid.seatbid[0].bid[0].w, + height: serverResponseVid.seatbid[0].bid[0].h, + mediaType: 'video', + meta: { advertiserDomains: serverResponseVid.seatbid[0].bid[0].adomain }, + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + + it('should return valid banner bid responses', function () { + const saambaabidreq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + saambaabidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + + }; + }); + const serverResponse = {'id': '2aa73f571eaf29', 'seatbid': [{'bid': [{'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
', 'adomain': ['advertiserdomain.com'], 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; + + const bidResponse = spec.interpretResponse({ body: serverResponse }, saambaabidreq); + expect(bidResponse).to.deep.equal({ + requestId: bidRequests[0].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + bidderCode: 'saambaa', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + mediaType: 'banner', + meta: { advertiserDomains: serverResponse.seatbid[0].bid[0].adomain }, + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + }); + }); +}); From f9631fbbd83b9a6bf679b244eec3cff10145ebdb Mon Sep 17 00:00:00 2001 From: Rahul Shandilya <67756716+c3p-0@users.noreply.github.com> Date: Wed, 29 Jul 2020 15:38:02 +0530 Subject: [PATCH 0033/1476] Instream and Outstream video support for medianetBidAdapter (#5482) * instream and outstream video support * updating md file for video support * incorporating suggested changes. * more precedence to mediaTypes.video --- modules/medianetBidAdapter.js | 16 +- modules/medianetBidAdapter.md | 214 +++++++++++++------ test/spec/modules/medianetBidAdapter_spec.js | 42 ++++ 3 files changed, 202 insertions(+), 70 deletions(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 5cc18acb424..9ff0192eab4 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'medianet'; @@ -21,7 +21,7 @@ let refererInfo = getRefererInfo(); let mnData = {}; mnData.urlData = { - domain: utils.parseUrl(refererInfo.referer).host, + domain: utils.parseUrl(refererInfo.referer).hostname, page: refererInfo.referer, isTop: refererInfo.reachedTop } @@ -160,7 +160,15 @@ function slotParams(bidRequest) { }, all: bidRequest.params }; - let bannerSizes = utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes') || bidRequest.sizes || []; + let bannerSizes = utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes') || []; + + const videoInMediaType = utils.deepAccess(bidRequest, 'mediaTypes.video') || {}; + const videoInParams = utils.deepAccess(bidRequest, 'params.video') || {}; + const videoCombinedObj = Object.assign({}, videoInParams, videoInMediaType); + + if (!utils.isEmpty(videoCombinedObj)) { + params.video = videoCombinedObj; + } if (bannerSizes.length > 0) { params.banner = transformSizes(bannerSizes); @@ -305,7 +313,7 @@ export const spec = { code: BIDDER_CODE, gvlid: 142, - supportedMediaTypes: [BANNER, NATIVE], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], /** * Determines whether or not the given bid request is valid. diff --git a/modules/medianetBidAdapter.md b/modules/medianetBidAdapter.md index 8259c32c9d3..b465d678fc9 100644 --- a/modules/medianetBidAdapter.md +++ b/modules/medianetBidAdapter.md @@ -14,19 +14,24 @@ This adapter currently only supports Banner Ads. # Sample Ad Unit: For Publishers ```javascript var adUnits = [{ - code: 'media.net-hb-ad-123456-1', - sizes: [ - [300, 250], - [300, 600], - ], - bids: [{ - bidder: 'medianet', - params: { - cid: '', - bidfloor: '', - crid: '' - } - }] + code: 'media.net-hb-ad-123456-1', + mediaTypes: { + banner: { + sizes: [ + [728, 90], + [300, 600], + [300, 250] + ], + } + }, + bids: [{ + bidder: 'medianet', + params: { + cid: '', + bidfloor: '', + crid: '' + } + }] }]; ``` @@ -35,69 +40,146 @@ var adUnits = [{ ```html ``` +# Ad Unit and Setup: For Testing (Video Instream) -# Ad Unit and Setup: For Testing (Native) +```html + + + +``` +# Ad Unit and Setup: For Testing (Video Outstream) ```html +``` + +# Ad Unit and Setup: For Testing (Native) + +```html + + + + diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index fcfdcdf8c2f..649929056fa 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -37,6 +37,11 @@ let VALID_BID_REQUEST = [{ }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 251]], + } + }, 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', @@ -81,6 +86,11 @@ let VALID_BID_REQUEST = [{ }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 251]], + } + }, 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', @@ -127,6 +137,11 @@ let VALID_BID_REQUEST = [{ }, 'adUnitCode': 'div-gpt-ad-1460505748561-123', 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 251]], + } + }, 'sizes': [[300, 251]], 'bidId': '3f97ca71b1e5c2', 'bidderRequestId': '1e9b1f07797c1c', @@ -731,6 +746,28 @@ let VALID_BID_REQUEST = [{ }], 'tmax': config.getConfig('bidderTimeout') }, + + VALID_VIDEO_BID_REQUEST = [{ + 'bidder': 'medianet', + 'params': { + 'cid': 'customer_id', + 'video': { + 'skipppable': true + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + 'mediaTypes': { + 'video': { + 'context': 'instream', + } + }, + 'bidId': '28f8f8130a583e', + 'bidderRequestId': '1e9b1f07797c1c', + 'auctionId': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'bidRequestsCount': 1 + }], + VALID_PAYLOAD_PAGE_META = (() => { let PAGE_META; try { @@ -1156,6 +1193,11 @@ describe('Media.net bid adapter', function () { expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_NATIVE); }); + it('should parse params for video request', function () { + let bidReq = spec.buildRequests(VALID_VIDEO_BID_REQUEST, VALID_AUCTIONDATA); + expect(JSON.stringify(bidReq.data)).to.include('instream'); + }); + it('should have valid crid present in bid request', function() { sandbox.stub(config, 'getConfig').callsFake((key) => { const config = { From 29c7ffff2247072008a27faec74095761f64a2b0 Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Wed, 29 Jul 2020 10:28:04 -0400 Subject: [PATCH 0034/1476] pubGENIUS bid adapter: fix schain, use canonical URL and send page referrer (#5549) * alloow to config pubgenius endpoint use page canonical url send page referer lint: use !! instead of Boolean * fix schain --- modules/pubgeniusBidAdapter.js | 38 +++++++++++++------ test/spec/modules/pubgeniusBidAdapter_spec.js | 10 ++++- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index d42073b3da5..05f18f99a9a 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -16,7 +16,6 @@ import { const BIDDER_VERSION = '1.0.0'; const BASE_URL = 'https://ortb.adpearl.io'; -const AUCTION_URL = BASE_URL + '/prebid/auction'; export const spec = { code: 'pubgenius', @@ -31,7 +30,7 @@ export const spec = { } const sizes = deepAccess(bid, 'mediaTypes.banner.sizes'); - return Boolean(sizes && sizes.length) && sizes.every(size => isArrayOfNums(size, 2)); + return !!(sizes && sizes.length) && sizes.every(size => isArrayOfNums(size, 2)); }, buildRequests: function (bidRequests, bidderRequest) { @@ -66,7 +65,7 @@ export const spec = { deepSetValue(data, 'regs.ext.us_privacy', usp); } - const schain = bidderRequest.schain; + const schain = bidRequests[0].schain; if (schain) { deepSetValue(data, 'source.ext.schain', schain); } @@ -85,7 +84,7 @@ export const spec = { return { method: 'POST', - url: AUCTION_URL, + url: `${getBaseUrl()}/prebid/auction`, data, }; }, @@ -128,7 +127,7 @@ export const spec = { const qs = parseQueryStringParameters(params); syncs.push({ type: 'iframe', - url: `${BASE_URL}/usersync/pixels.html?${qs}`, + url: `${getBaseUrl()}/usersync/pixels.html?${qs}`, }); } @@ -136,7 +135,7 @@ export const spec = { }, onTimeout(data) { - ajax(`${BASE_URL}/prebid/events?type=timeout`, null, JSON.stringify(data), { + ajax(`${getBaseUrl()}/prebid/events?type=timeout`, null, JSON.stringify(data), { method: 'POST', }); }, @@ -170,14 +169,26 @@ function buildImp(bid) { } function buildSite(bidderRequest) { - const pageUrl = config.getConfig('pageUrl') || bidderRequest.refererInfo.referer; + let site = null; + const { refererInfo } = bidderRequest; + + const pageUrl = config.getConfig('pageUrl') || refererInfo.canonicalUrl || refererInfo.referer; if (pageUrl) { - return { - page: pageUrl, - }; + site = site || {}; + site.page = pageUrl; + } + + if (refererInfo.reachedTop) { + try { + const pageRef = window.top.document.referrer; + if (pageRef) { + site = site || {}; + site.ref = pageRef; + } + } catch (e) {} } - return null; + return site; } function interpretBid(bid) { @@ -205,4 +216,9 @@ function numericBoolean(value) { return value ? 1 : 0; } +function getBaseUrl() { + const pubg = config.getConfig('pubgenius'); + return (pubg && pubg.endpoint) || BASE_URL; +} + registerBidder(spec); diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index c510be99402..52f2e3aeefe 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -200,6 +200,14 @@ describe('pubGENIUS adapter', () => { expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); }); + it('should use canonical URL over referer in refererInfo', () => { + bidderRequest.refererInfo.canonicalUrl = 'http://pageurl.org'; + bidderRequest.refererInfo.referer = 'http://referer.org'; + expectedRequest.data.site = { page: 'http://pageurl.org' }; + + expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); + }); + it('should take gdprConsent when GDPR does not apply', () => { bidderRequest.gdprConsent = { gdprApplies: false, @@ -248,7 +256,7 @@ describe('pubGENIUS adapter', () => { } ] }; - bidderRequest.schain = deepClone(schain); + bidRequest.schain = deepClone(schain); expectedRequest.data.source = { ext: { schain: deepClone(schain) }, }; From 7f901c600954d72aef8427cb8f64ee8a4fa86573 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Wed, 29 Jul 2020 14:22:55 -0400 Subject: [PATCH 0035/1476] Pb ad slot module (#5539) * New GPT Pre-Auction module to add ad server and pb ad slot data to each ad unit * fix broken tests * removed before hook weight argument * Fix using hook Co-authored-by: Mark Monday Co-authored-by: idettman --- modules/gptPreAuction.js | 101 +++++++++++++ test/spec/modules/gptPreAuction_spec.js | 191 ++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 modules/gptPreAuction.js create mode 100644 test/spec/modules/gptPreAuction_spec.js diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js new file mode 100644 index 00000000000..1c15d87f40c --- /dev/null +++ b/modules/gptPreAuction.js @@ -0,0 +1,101 @@ +import { config } from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { getHook } from '../src/hook.js'; +import find from 'core-js-pure/features/array/find.js'; + +const MODULE_NAME = 'GPT Pre-Auction'; +export let _currentConfig = {}; +let hooksAdded = false; + +export const appendGptSlots = adUnits => { + const { customGptSlotMatching } = _currentConfig; + + if (!window.googletag) { + return; + } + + const adUnitMap = adUnits.reduce((acc, adUnit) => { + acc[adUnit.code] = adUnit; + return acc; + }, {}); + + window.googletag.pubads().getSlots().forEach(slot => { + const matchingAdUnitCode = find(Object.keys(adUnitMap), customGptSlotMatching + ? customGptSlotMatching(slot) + : utils.isAdUnitCodeMatchingSlot(slot)); + + if (matchingAdUnitCode) { + const adUnit = adUnitMap[matchingAdUnitCode]; + adUnit.fpd = adUnit.fpd || {}; + adUnit.fpd.context = adUnit.fpd.context || {}; + + const context = adUnit.fpd.context; + context.adServer = context.adServer || {}; + context.adServer.name = 'gam'; + context.adServer.adSlot = slot.getAdUnitPath(); + } + }); +}; + +export const appendPbAdSlot = adUnit => { + adUnit.fpd = adUnit.fpd || {}; + adUnit.fpd.context = adUnit.fpd.context || {}; + const context = adUnit.fpd.context; + const { customPbAdSlot } = _currentConfig; + + if (customPbAdSlot) { + context.pbAdSlot = customPbAdSlot(adUnit.code, utils.deepAccess(context, 'adServer.adSlot')); + return; + } + + // use context.pbAdSlot if set + if (context.pbAdSlot) { + return; + } + // use data attribute 'data-adslotid' if set + try { + const adUnitCodeDiv = document.getElementById(adUnit.code); + if (adUnitCodeDiv.dataset.adslotid) { + context.pbAdSlot = adUnitCodeDiv.dataset.adslotid; + return; + } + } catch (e) {} + // banner adUnit, use GPT adunit if defined + if (context.adServer) { + context.pbAdSlot = context.adServer.adSlot; + return; + } + context.pbAdSlot = adUnit.code; +}; + +export const makeBidRequestsHook = (fn, adUnits, ...args) => { + appendGptSlots(adUnits); + adUnits.forEach(adUnit => { + appendPbAdSlot(adUnit); + }); + return fn.call(this, adUnits, ...args); +}; + +const handleSetGptConfig = moduleConfig => { + _currentConfig = utils.pick(moduleConfig, [ + 'enabled', enabled => enabled !== false, + 'customGptSlotMatching', customGptSlotMatching => + typeof customGptSlotMatching === 'function' && customGptSlotMatching, + 'customPbAdSlot', customPbAdSlot => typeof customPbAdSlot === 'function' && customPbAdSlot, + ]); + + if (_currentConfig.enabled) { + if (!hooksAdded) { + getHook('makeBidRequests').before(makeBidRequestsHook); + hooksAdded = true; + } + } else { + utils.logInfo(`${MODULE_NAME}: Turning off module`); + _currentConfig = {}; + getHook('makeBidRequests').getHooks({hook: makeBidRequestsHook}).remove(); + hooksAdded = false; + } +}; + +config.getConfig('gptPreAuction', config => handleSetGptConfig(config.gptPreAuction)); +handleSetGptConfig({}); diff --git a/test/spec/modules/gptPreAuction_spec.js b/test/spec/modules/gptPreAuction_spec.js new file mode 100644 index 00000000000..16b84467af2 --- /dev/null +++ b/test/spec/modules/gptPreAuction_spec.js @@ -0,0 +1,191 @@ +import { + appendGptSlots, + appendPbAdSlot, + _currentConfig, + makeBidRequestsHook +} from 'modules/gptPreAuction.js'; +import { config } from 'src/config.js'; +import { makeSlot } from '../integration/faker/googletag.js'; + +describe('GPT pre-auction module', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); + config.setConfig({ gptPreAuction: { enabled: false } }); + }); + + const testSlots = [ + makeSlot({ code: 'slotCode1', divId: 'div1' }), + makeSlot({ code: 'slotCode2', divId: 'div2' }), + makeSlot({ code: 'slotCode3', divId: 'div3' }) + ]; + + describe('appendPbAdSlot', () => { + // sets up our document body to test the pbAdSlot dom actions against + document.body.innerHTML = '
test1
' + + '
test2
' + + '
test2
'; + + it('should be unchanged if already defined on adUnit', () => { + const adUnit = { fpd: { context: { pbAdSlot: '12345' } } }; + appendPbAdSlot(adUnit); + expect(adUnit.fpd.context.pbAdSlot).to.equal('12345'); + }); + + it('should use adUnit.code if matching id exists', () => { + const adUnit = { code: 'foo1', fpd: { context: {} } }; + appendPbAdSlot(adUnit); + expect(adUnit.fpd.context.pbAdSlot).to.equal('bar1'); + }); + + it('should use the gptSlot.adUnitPath if the adUnit.code matches a div id but does not have a data-adslotid', () => { + const adUnit = { code: 'foo3', mediaTypes: { banner: { sizes: [[250, 250]] } }, fpd: { context: { adServer: { name: 'gam', adSlot: '/baz' } } } }; + appendPbAdSlot(adUnit); + expect(adUnit.fpd.context.pbAdSlot).to.equal('/baz'); + }); + + it('should use the video adUnit.code (which *should* match the configured "adSlotName", but is not being tested) if there is no matching div with "data-adslotid" defined', () => { + const adUnit = { code: 'foo4', mediaTypes: { video: { sizes: [[250, 250]] } }, fpd: { context: {} } }; + adUnit.code = 'foo5'; + appendPbAdSlot(adUnit, undefined); + expect(adUnit.fpd.context.pbAdSlot).to.equal('foo5'); + }); + + it('should use the adUnit.code if all other sources failed', () => { + const adUnit = { code: 'foo4', fpd: { context: {} } }; + appendPbAdSlot(adUnit, undefined); + expect(adUnit.fpd.context.pbAdSlot).to.equal('foo4'); + }); + + it('should use the customPbAdSlot function if one is given', () => { + config.setConfig({ + gptPreAuction: { + customPbAdSlot: () => 'customPbAdSlotName' + } + }); + + const adUnit = { code: 'foo1', fpd: { context: {} } }; + appendPbAdSlot(adUnit); + expect(adUnit.fpd.context.pbAdSlot).to.equal('customPbAdSlotName'); + }); + }); + + describe('appendGptSlots', () => { + it('should not add adServer object to context if no slots defined', () => { + const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + appendGptSlots([adUnit]); + expect(adUnit.fpd.context.adServer).to.be.undefined; + }); + + it('should not add adServer object to context if no slot matches', () => { + window.googletag.pubads().setSlots(testSlots); + const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + appendGptSlots([adUnit]); + expect(adUnit.fpd.context.adServer).to.be.undefined; + }); + + it('should add adServer object to context if matching slot is found', () => { + window.googletag.pubads().setSlots(testSlots); + const adUnit = { code: 'slotCode2', fpd: { context: {} } }; + appendGptSlots([adUnit]); + expect(adUnit.fpd.context.adServer).to.be.an('object'); + expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode2' }); + }); + + it('should use the customGptSlotMatching function if one is given', () => { + config.setConfig({ + gptPreAuction: { + customGptSlotMatching: slot => + adUnitCode => adUnitCode.toUpperCase() === slot.getAdUnitPath().toUpperCase() + } + }); + + window.googletag.pubads().setSlots(testSlots); + const adUnit = { code: 'SlOtCoDe1', fpd: { context: {} } }; + appendGptSlots([adUnit]); + expect(adUnit.fpd.context.adServer).to.be.an('object'); + expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode1' }); + }); + }); + + describe('handleSetGptConfig', () => { + it('should enable the module by default', () => { + config.setConfig({ gptPreAuction: {} }); + expect(_currentConfig.enabled).to.equal(true); + }); + + it('should disable the module if told to in set config', () => { + config.setConfig({ gptPreAuction: { enabled: false } }); + expect(_currentConfig).to.be.an('object').that.is.empty; + }); + + it('should accept custom functions in config', () => { + config.setConfig({ + gptPreAuction: { + customGptSlotMatching: () => 'customGptSlot', + customPbAdSlot: () => 'customPbAdSlot' + } + }); + + expect(_currentConfig.enabled).to.equal(true); + expect(_currentConfig.customGptSlotMatching).to.a('function'); + expect(_currentConfig.customPbAdSlot).to.a('function'); + expect(_currentConfig.customGptSlotMatching()).to.equal('customGptSlot'); + expect(_currentConfig.customPbAdSlot()).to.equal('customPbAdSlot'); + }); + + it('should check that custom functions in config are type function', () => { + config.setConfig({ + gptPreAuction: { + customGptSlotMatching: 12345, + customPbAdSlot: 'test' + } + }); + expect(_currentConfig).to.deep.equal({ + enabled: true, + customGptSlotMatching: false, + customPbAdSlot: false + }); + }); + }); + + describe('makeBidRequestsHook', () => { + let returnedAdUnits; + const runMakeBidRequests = adUnits => { + const next = adUnits => { + returnedAdUnits = adUnits; + }; + makeBidRequestsHook(next, adUnits); + }; + + it('should append PB Ad Slot and GPT Slot info to first-party data in each ad unit', () => { + const testAdUnits = [{ + code: 'adUnit1', + fpd: { context: { pbAdSlot: '12345' } } + }, { + code: 'slotCode1', + fpd: { context: { pbAdSlot: '67890' } } + }, { + code: 'slotCode3', + }]; + + const expectedAdUnits = [{ + code: 'adUnit1', + fpd: { context: { pbAdSlot: '12345' } } + }, { + code: 'slotCode1', + fpd: { context: { pbAdSlot: '67890', adServer: { name: 'gam', adSlot: 'slotCode1' } } } + }, { + code: 'slotCode3', + fpd: { context: { pbAdSlot: 'slotCode3', adServer: { name: 'gam', adSlot: 'slotCode3' } } } + }]; + + window.googletag.pubads().setSlots(testSlots); + runMakeBidRequests(testAdUnits); + expect(returnedAdUnits).to.deep.equal(expectedAdUnits); + }); + }); +}); From b36f11abba545f2981dafafdcd3d09e819782e12 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 29 Jul 2020 11:39:44 -0700 Subject: [PATCH 0036/1476] Rubicon analytics supress floor data from other providers (#5552) --- modules/rubiconAnalyticsAdapter.js | 47 ++++++++++++------- .../modules/rubiconAnalyticsAdapter_spec.js | 36 +++++++++++++- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 00ad14dd316..7a1bdce84d5 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -40,6 +40,8 @@ const cache = { timeouts: {}, }; +const validFloorProviders = ['rubicon', 'rubi', 'magnite', 'mgni']; + export function getHostNameFromReferer(referer) { try { rubiconAdapter.referrerHostname = utils.parseUrl(referer, {noDecodeWholeURL: true}).hostname; @@ -195,15 +197,22 @@ function sendMessage(auctionId, bidWonId) { // pick our of top level floor data we want to send! if (auctionCache.floorData) { - auction.floors = utils.pick(auctionCache.floorData, [ - 'location', - 'modelName', () => auctionCache.floorData.modelVersion, - 'skipped', - 'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'), - 'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'), - 'skipRate', skipRate => !isNaN(skipRate) ? skipRate : 0, - 'fetchStatus' - ]); + if (auctionCache.floorData.location === 'noData') { + auction.floors = utils.pick(auctionCache.floorData, [ + 'location', + 'fetchStatus' + ]); + } else { + auction.floors = utils.pick(auctionCache.floorData, [ + 'location', + 'modelName', () => auctionCache.floorData.modelVersion, + 'skipped', + 'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'), + 'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'), + 'skipRate', + 'fetchStatus' + ]); + } } if (serverConfig) { @@ -269,14 +278,14 @@ function getBidPrice(bid) { } } -export function parseBidResponse(bid, previousBidResponse) { +export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { // The current bidResponse for this matching requestId/bidRequestId let responsePrice = getBidPrice(bid) // we need to compare it with the previous one (if there was one) if (previousBidResponse && previousBidResponse.bidPriceUSD > responsePrice) { return previousBidResponse; } - return utils.pick(bid, [ + let bidResponse = utils.pick(bid, [ 'bidPriceUSD', () => responsePrice, 'dealId', 'status', @@ -286,9 +295,12 @@ export function parseBidResponse(bid, previousBidResponse) { 'height' ]), 'seatBidId', - 'floorValue', () => utils.deepAccess(bid, 'floorData.floorValue'), - 'floorRule', () => utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined ]); + if (auctionFloorData) { + bidResponse.floorValue = utils.deepAccess(bid, 'floorData.floorValue'); + bidResponse.floorRule = utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined + } + return bidResponse; } let samplingFactor = 1; @@ -368,8 +380,9 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { cacheEntry.bids = {}; cacheEntry.bidsWon = {}; cacheEntry.referrer = args.bidderRequests[0].refererInfo.referer; - if (utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData')) { - cacheEntry.floorData = {...utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData')}; + const floorProvider = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData.floorProvider'); + if (validFloorProviders.indexOf(floorProvider) !== -1) { + cacheEntry.floorData = {...args.bidderRequests[0].bids[0].floorData}; } cache.auctions[args.auctionId] = cacheEntry; break; @@ -455,7 +468,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { bid.adUnit.adSlot = args.floorData.matchedFields.gptSlot; } // if we have not set enforcements yet set it - if (!utils.deepAccess(auctionEntry, 'floorData.enforcements') && utils.deepAccess(args, 'floorData.enforcements')) { + if (auctionEntry.floorData && !auctionEntry.floorData.enforcements && utils.deepAccess(args, 'floorData.enforcements')) { auctionEntry.floorData.enforcements = {...args.floorData.enforcements}; } if (!bid) { @@ -479,7 +492,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }; } bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp; - bid.bidResponse = parseBidResponse(args, bid.bidResponse); + bid.bidResponse = parseBidResponse(args, bid.bidResponse, auctionEntry.floorData); break; case BIDDER_DONE: args.bids.forEach(bid => { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 1639389fe24..466435d9652 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -659,14 +659,15 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(ANALYTICS_MESSAGE); }); - it('should capture price floor information correctly', function () { + function performFloorAuction(provider) { let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); auctionInit.bidderRequests[0].bids[0].floorData = { skipped: false, modelVersion: 'someModelName', location: 'setConfig', skipRate: 15, - fetchStatus: 'error' + fetchStatus: 'error', + floorProvider: provider }; let flooredResponse = { ...BID, @@ -727,6 +728,11 @@ describe('rubicon analytics adapter', function () { let message = JSON.parse(server.requests[0].requestBody); validate(message); + return message; + } + + it('should capture price floor information correctly', function () { + let message = performFloorAuction('rubicon') // verify our floor stuff is passed // top level floor info @@ -759,6 +765,32 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); }); + it('should not send floor info if provider is not rubicon', function () { + let message = performFloorAuction('randomProvider') + + // verify our floor stuff is passed + // top level floor info + expect(message.auctions[0].floors).to.be.undefined; + // first adUnit's adSlot + expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports'); + // since no other bids, we set adUnit status to no-bid + expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); + // first adUnits bid is rejected + expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected'); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.be.undefined; + // if bid rejected should take cpmAfterAdjustments val + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); + + // second adUnit's adSlot + expect(message.auctions[0].adUnits[1].adSlot).to.equal('12345/news'); + // top level adUnit status is success + expect(message.auctions[0].adUnits[1].status).to.equal('success'); + // second adUnits bid is success + expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.be.undefined; + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); + }); + it('should correctly overwrite bidId if seatBidId is on the bidResponse', function () { // Only want one bid request in our mock auction let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); From 4a4e922cb7b0e40fd1c902f1a310e92f48e47d20 Mon Sep 17 00:00:00 2001 From: Matt Kleinberg Date: Wed, 29 Jul 2020 14:40:54 -0400 Subject: [PATCH 0037/1476] Update floors module for #5511 (#5538) * Changed fallback value of skipRate from 0 to undefined. Removed skipRate from bidRequest[ ].floorData if undefined. If no floor data available set bidRequest[ ].floorData.location to 'noBid'. * Added top level floorProvider value. * reverted skip rate back to 0 from undifined when not set. Removed type enforcement from floorProvider. Added floorProvider into return data. * fixed description for floorProvider --- modules/priceFloors.js | 6 ++- modules/priceFloors.md | 4 +- test/spec/modules/priceFloors_spec.js | 54 +++++++++++++++++---------- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 6d35a0a74cc..817f2103ba2 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -286,9 +286,10 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { bid.auctionId = auctionId; bid.floorData = { skipped: floorData.skipped, - modelVersion: utils.deepAccess(floorData, 'data.modelVersion'), - location: utils.deepAccess(floorData, 'data.location'), skipRate: floorData.skipRate, + modelVersion: utils.deepAccess(floorData, 'data.modelVersion'), + location: utils.deepAccess(floorData, 'data.location', 'noData'), + floorProvider: floorData.floorProvider, fetchStatus: _floorsConfig.fetchStatus } }); @@ -568,6 +569,7 @@ export function handleSetFloorsConfig(config) { _floorsConfig = utils.pick(config, [ 'enabled', enabled => enabled !== false, // defaults to true 'auctionDelay', auctionDelay => auctionDelay || 0, + 'floorProvider', 'endpoint', endpoint => endpoint || {}, 'skipRate', () => !isNaN(utils.deepAccess(config, 'data.skipRate')) ? config.data.skipRate : config.skipRate || 0, 'enforcement', enforcement => utils.pick(enforcement || {}, [ diff --git a/modules/priceFloors.md b/modules/priceFloors.md index d09be78c620..36ac07ee972 100644 --- a/modules/priceFloors.md +++ b/modules/priceFloors.md @@ -11,6 +11,7 @@ pbjs.setConfig({ enforceJS: true //defaults to true }, auctionDelay: 150, // in milliseconds defaults to 0 + floorProvider: 'awesomeFloorProviderName', // name of the floor provider (optional) endpoint: { url: 'http://localhost:1500/floor-domains', method: 'GET' // Only get supported for now @@ -40,6 +41,7 @@ pbjs.setConfig({ | enabled | Wether to turn off or on the floors module | | enforcement | object of booleans which control certain features of the module | | auctionDelay | The time to suspend and auction while waiting for a real time price floors fetch to come back | +| floorProvider | A string identifying the floor provider.| | endpoint | An object describing the endpoint to retrieve floor data from. GET only | | data | The data to be used to select appropriate floors. See schema for more detail | | additionalSchemaFields | An object of additional fields to be used in a floor data object. The schema is KEY: function to retrieve the match | @@ -59,4 +61,4 @@ This function can takes in an object with the following optional parameters: If a bid adapter passes in `*` as an attribute, then the `priceFloors` module will attempt to select the best rule based on context. -For example, if an adapter passes in a `*`, but the bidRequest only has a single size and a single mediaType, then the `getFloor` function will attempt to get a rule for that size before matching with the `*` catch-all. Similarily, if mediaType can be inferred on the bidRequest, it will use it. \ No newline at end of file +For example, if an adapter passes in a `*`, but the bidRequest only has a single size and a single mediaType, then the `getFloor` function will attempt to get a rule for that size before matching with the `*` catch-all. Similarily, if mediaType can be inferred on the bidRequest, it will use it. diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 9d35554c27f..5cb31ffab4c 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -314,9 +314,10 @@ describe('the price floors module', function () { validateBidRequests(false, { skipped: true, modelVersion: undefined, - location: undefined, + location: 'noData', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); }); it('should use adUnit level data if not setConfig or fetch has occured', function () { @@ -348,18 +349,20 @@ describe('the price floors module', function () { modelVersion: 'adUnit Model Version', location: 'adUnit', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); }); it('bidRequests should have getFloor function and flooring meta data when setConfig occurs', function () { - handleSetFloorsConfig({...basicFloorConfig}); + handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider'}); runStandardAuction(); validateBidRequests(true, { skipped: false, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: 'floorprovider' }); }); it('should take the right skipRate depending on input', function () { @@ -380,7 +383,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 50, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); // if that does not exist uses topLevel skipRate setting @@ -392,7 +396,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 10, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); // if that is not there defaults to zero @@ -404,12 +409,14 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); }); it('should randomly pick a model if floorsSchemaVersion is 2', function () { let inputFloors = { ...basicFloorConfig, + floorProvider: 'floorprovider', data: { floorsSchemaVersion: 2, currency: 'USD', @@ -465,7 +472,8 @@ describe('the price floors module', function () { modelVersion: 'model-1', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: 'floorprovider' }); // 11 - 50 should use second model @@ -476,7 +484,8 @@ describe('the price floors module', function () { modelVersion: 'model-2', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: 'floorprovider' }); // 51 - 100 should use third model @@ -487,7 +496,8 @@ describe('the price floors module', function () { modelVersion: 'model-3', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: 'floorprovider' }); }); it('should not overwrite previous data object if the new one is bad', function () { @@ -514,7 +524,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: undefined + fetchStatus: undefined, + floorProvider: undefined }); }); it('should dynamically add new schema fileds and functions if added via setConfig', function () { @@ -591,7 +602,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: 'timeout' + fetchStatus: 'timeout', + floorProvider: undefined }); fakeFloorProvider.respond(); }); @@ -604,7 +616,7 @@ describe('the price floors module', function () { fakeFloorProvider.respondWith(JSON.stringify(fetchFloorData)); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); + handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); // floor provider should be called expect(fakeFloorProvider.requests.length).to.equal(1); @@ -627,7 +639,8 @@ describe('the price floors module', function () { modelVersion: 'fetch model name', location: 'fetch', skipRate: 0, - fetchStatus: 'success' + fetchStatus: 'success', + floorProvider: 'floorprovider' }); }); it('it should correctly overwrite skipRate with fetch skipRate', function () { @@ -642,7 +655,7 @@ describe('the price floors module', function () { fakeFloorProvider.respondWith(JSON.stringify(fetchFloorData)); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); + handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); // floor provider should be called expect(fakeFloorProvider.requests.length).to.equal(1); @@ -665,7 +678,8 @@ describe('the price floors module', function () { modelVersion: 'fetch model name', location: 'fetch', skipRate: 95, - fetchStatus: 'success' + fetchStatus: 'success', + floorProvider: 'floorprovider' }); }); it('Should not break if floor provider returns 404', function () { @@ -685,7 +699,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: 'error' + fetchStatus: 'error', + floorProvider: undefined }); }); it('Should not break if floor provider returns non json', function () { @@ -707,7 +722,8 @@ describe('the price floors module', function () { modelVersion: 'basic model', location: 'setConfig', skipRate: 0, - fetchStatus: 'success' + fetchStatus: 'success', + floorProvider: undefined }); }); it('should handle not using fetch correctly', function () { From 84dddb698647f49d5264286cf57e80ddb6c3dd70 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 29 Jul 2020 15:35:33 -0400 Subject: [PATCH 0038/1476] Prebid 4.1.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5295cb49dcf..80ef38683cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.1.0-pre", + "version": "4.1.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ad4dc44004bbca0d0717f05a898357a3676fe244 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 29 Jul 2020 15:51:26 -0400 Subject: [PATCH 0039/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80ef38683cc..37ee8555b8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.1.0", + "version": "4.2.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2980d69cd1bf584c80147964bc70644141395bb3 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 30 Jul 2020 11:33:35 +0300 Subject: [PATCH 0040/1476] Grid Adapter: New request format (#5541) * 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 --- modules/gridBidAdapter.js | 553 ++++++++++++++++------- test/spec/modules/gridBidAdapter_spec.js | 348 +++++++++++++- 2 files changed, 726 insertions(+), 175 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index d18effa349b..b4b741ac783 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -6,6 +6,7 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; +const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -40,117 +41,22 @@ export const spec = { * * @param {BidRequest[]} validBidRequests - an array of bids * @param {bidderRequest} bidderRequest bidder request object - * @return ServerRequest Info describing the request to the server. + * @return {ServerRequest[]} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; - const bids = validBidRequests || []; - let pageKeywords = null; - let reqId; - - bids.forEach(bid => { - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode, mediaTypes} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); - } - - const addedSizes = {}; - sizesId.forEach((sizeId) => { - addedSizes[sizeId] = true; - }); - const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); - const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); - bannerSizesId.concat(videoSizesId).forEach((sizeId) => { - if (!addedSizes[sizeId]) { - addedSizes[sizeId] = true; - sizesId.push(sizeId); - } - }); - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); - } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); + const oldFormatBids = []; + const newFormatBids = []; + validBidRequests.forEach((bid) => { + bid.params.useNewFormat ? newFormatBids.push(bid) : oldFormatBids.push(bid); }); - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null - }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); + const requests = []; + if (newFormatBids.length) { + requests.push(buildNewRequest(newFormatBids, bidderRequest)); } - - const payload = { - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); + if (oldFormatBids.length) { + requests.push(buildOldRequest(oldFormatBids, bidderRequest)); } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } - } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap, - }; + return requests; }, /** * Unpack the response from the server into a list of bids. @@ -162,7 +68,6 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; - const bidsMap = bidRequest.bidsMap; let errorMessage; @@ -173,7 +78,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -224,68 +129,76 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, bidResponses) { +function _addBidResponse(serverBid, bidRequest, bidResponses) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - - const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'USD', - netRevenue: false, - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid - }; - - if (serverBid.content_type === 'video') { - bidResponse.vastXml = serverBid.adm; - bidResponse.mediaType = VIDEO; - bidResponse.adResponse = { - content: bidResponse.vastXml - }; - if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { - bidResponse.renderer = createRenderer(bidResponse, { - id: bid.bidId, - url: RENDERER_URL - }); - } - } else { - bidResponse.ad = serverBid.adm; - bidResponse.mediaType = BANNER; + let bid = null; + let slot = null; + const bidsMap = bidRequest.bidsMap; + if (bidRequest.newFormat) { + bid = bidsMap[serverBid.impid]; + } else { + const awaitingBids = bidsMap[serverBid.auid]; + if (awaitingBids) { + const sizeId = `${serverBid.w}x${serverBid.h}`; + if (awaitingBids[sizeId]) { + slot = awaitingBids[sizeId][0]; + bid = slot.bids.shift(); } - bidResponses.push(bidResponse); + } else { + errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; + } + } - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } + if (bid) { + const bidResponse = { + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: false, + ttl: TIME_TO_LIVE, + dealId: serverBid.dealid + }; + + if (serverBid.content_type === 'video') { + bidResponse.vastXml = serverBid.adm; + bidResponse.mediaType = VIDEO; + bidResponse.adResponse = { + content: bidResponse.vastXml + }; + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = createRenderer(bidResponse, { + id: bid.bidId, + url: RENDERER_URL }); } + } else { + bidResponse.ad = serverBid.adm; + bidResponse.mediaType = BANNER; + } + bidResponses.push(bidResponse); + + if (slot && !slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } } if (errorMessage) { @@ -293,6 +206,326 @@ function _addBidResponse(serverBid, bidsMap, bidResponses) { } } +function buildOldRequest(validBidRequests, bidderRequest) { + const auids = []; + const bidsMap = {}; + const slotsMapByUid = {}; + const sizeMap = {}; + const bids = validBidRequests || []; + let pageKeywords = null; + let reqId; + + bids.forEach(bid => { + reqId = bid.bidderRequestId; + const {params: {uid}, adUnitCode, mediaTypes} = bid; + auids.push(uid); + const sizesId = utils.parseSizesInput(bid.sizes); + + if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { + pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); + } + + const addedSizes = {}; + sizesId.forEach((sizeId) => { + addedSizes[sizeId] = true; + }); + const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); + const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); + bannerSizesId.concat(videoSizesId).forEach((sizeId) => { + if (!addedSizes[sizeId]) { + addedSizes[sizeId] = true; + sizesId.push(sizeId); + } + }); + + if (!slotsMapByUid[uid]) { + slotsMapByUid[uid] = {}; + } + const slotsMap = slotsMapByUid[uid]; + if (!slotsMap[adUnitCode]) { + slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; + } else { + slotsMap[adUnitCode].bids.push(bid); + } + const slot = slotsMap[adUnitCode]; + + sizesId.forEach((sizeId) => { + sizeMap[sizeId] = true; + if (!bidsMap[uid]) { + bidsMap[uid] = {}; + } + + if (!bidsMap[uid][sizeId]) { + bidsMap[uid][sizeId] = [slot]; + } else { + bidsMap[uid][sizeId].push(slot); + } + slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); + }); + }); + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + const payload = { + auids: auids.join(','), + sizes: utils.getKeys(sizeMap).join(','), + r: reqId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' + }; + + if (pageKeywords) { + payload.keywords = JSON.stringify(pageKeywords); + } + + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.u = bidderRequest.refererInfo.referer; + } + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + } + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + } + + return { + method: 'GET', + url: ENDPOINT_URL, + data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), + bidsMap: bidsMap + } +} + +function buildNewRequest(validBidRequests, bidderRequest) { + let pageKeywords = null; + let jwpseg = null; + let content = null; + let schain = null; + let userId = null; + let user = null; + let userExt = null; + let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; + const imp = []; + const bidsMap = {}; + + validBidRequests.forEach((bid) => { + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userId) { + userId = bid.userId; + } + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; + bidsMap[bidId] = bid; + if (!pageKeywords && !utils.isEmpty(keywords)) { + pageKeywords = utils.transformBidderParamKeywords(keywords); + } + if (realTimeData && realTimeData.jwTargeting) { + if (!jwpseg && realTimeData.jwTargeting.segments) { + jwpseg = realTimeData.segments; + } + if (!content && realTimeData.content) { + content = realTimeData.content; + } + } + let impObj = { + id: bidId, + tagid: uid.toString(), + ext: { + divid: adUnitCode + } + }; + + if (!mediaTypes || mediaTypes[BANNER]) { + const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); + if (banner) { + impObj.banner = banner; + } + } + if (mediaTypes && mediaTypes[VIDEO]) { + const video = createVideoRequest(bid, mediaTypes[VIDEO]); + if (video) { + impObj.video = video; + } + } + + if (impObj.banner || impObj.video) { + imp.push(impObj); + } + }); + + const source = { + tid: auctionId, + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' + } + }; + + if (schain) { + source.ext.schain = schain; + } + + const tmax = config.getConfig('bidderTimeout') || timeout; + + let request = { + id: bidderRequestId, + site: { + page: referer + }, + tmax, + source, + imp + }; + + if (content) { + request.site.content = content; + } + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = {consent: gdprConsent.consentString}; + } + + if (userId) { + userExt = userExt || {}; + if (userId.tdid) { + userExt.unifiedid = userId.tdid; + } + if (userId.id5id) { + userExt.id5id = userId.id5id; + } + if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt.digitrustid = userId.digitrustid.data.id; + } + if (userId.lipb && userId.lipb.lipbid) { + userExt.liveintentid = userId.lipb.lipbid; + } + } + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } + + if (user) { + request.user = user; + } + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } + + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + } + } + + if (uspConsent) { + if (!request.regs) { + request.regs = {ext: {}}; + } + request.regs.ext.us_privacy = uspConsent; + } + + return { + method: 'POST', + url: NEW_ENDPOINT_URL, + data: JSON.stringify(request), + newFormat: true, + bidsMap + }; +} + +function createVideoRequest(bid, mediaType) { + const {playerSize, mimes, durationRangeSec} = mediaType; + const size = (playerSize || bid.sizes || [])[0]; + if (!size) return; + + let result = utils.parseGPTSingleSizeArrayToRtbSize(size); + + if (mimes) { + result.mimes = mimes; + } + + if (durationRangeSec && durationRangeSec.length === 2) { + result.minduration = durationRangeSec[0]; + result.maxduration = durationRangeSec[1]; + } + + return result; +} + +function createBannerRequest(bid, mediaType) { + const sizes = mediaType.sizes || bid.sizes; + if (!sizes || !sizes.length) return; + + let format = sizes.map((size) => utils.parseGPTSingleSizeArrayToRtbSize(size)); + let result = utils.parseGPTSingleSizeArrayToRtbSize(sizes[0]); + + if (format.length) { + result.format = format + } + return result; +} + function outstreamRender (bid) { bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index c8d04113aeb..344f1764c05 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -103,7 +103,7 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -115,7 +115,7 @@ describe('TheMediaGrid Adapter', function () { }); it('sizes must be added from mediaTypes', function () { - const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -125,7 +125,7 @@ describe('TheMediaGrid Adapter', function () { }); it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); + const [request] = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -135,7 +135,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprConsent is present payload must have gdpr params', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -144,7 +144,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprApplies is false gdpr_applies must be 0', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -152,7 +152,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -161,7 +161,7 @@ describe('TheMediaGrid Adapter', function () { it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); + const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('us_privacy', '1YNN'); @@ -188,7 +188,7 @@ describe('TheMediaGrid Adapter', function () { } ); - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.keywords).to.be.an('string'); @@ -235,7 +235,7 @@ describe('TheMediaGrid Adapter', function () { } ); - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.keywords).to.be.an('string'); @@ -262,6 +262,324 @@ describe('TheMediaGrid Adapter', function () { }); }); + describe('buildRequests in new format', function () { + function parseRequest(data) { + return JSON.parse(data); + } + const bidderRequest = { + refererInfo: {referer: 'https://example.com'}, + bidderRequestId: '22edbae2733bf6', + auctionId: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + timeout: 3000 + }; + const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + let bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '11', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]], + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '3', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]] + }, + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + } + ]; + + it('should attach valid params to the tag', function () { + const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + 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$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); + + it('make possible to process request without mediaTypes', function () { + const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + 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$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + '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}] + } + }] + }); + }); + + it('should attach valid params to the video tag', function () { + const [request] = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + 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$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + '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, + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }] + }); + }); + + it('should support mixed mediaTypes', function () { + const [request] = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + 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$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + '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, + '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 + } + }] + }); + }); + + 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); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('consent', 'AAA'); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + + it('if usPrivacy is present payload must have us_privacy param', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); + }); + + it('if userId is present payload must have user.ext param with right keys', function () { + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + userId: { + id5id: 'id5id_1', + tdid: 'tdid_1', + digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + lipb: {lipbid: 'lipb_1'} + } + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('unifiedid', 'tdid_1'); + expect(payload.user.ext).to.have.property('id5id', 'id5id_1'); + expect(payload.user.ext).to.have.property('digitrustid', 'DTID'); + expect(payload.user.ext).to.have.property('liveintentid', 'lipb_1'); + }); + + it('if schain is present payload must have source.ext.schain param', function () { + const schain = { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + }; + const bidRequestsWithSChain = bidRequests.map((bid) => { + return Object.assign({ + schain: schain + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithSChain, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('source'); + expect(payload.source).to.have.property('ext'); + expect(payload.source.ext).to.have.property('schain'); + expect(payload.source.ext.schain).to.deep.equal(schain); + }); + }); + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, @@ -288,7 +606,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -346,7 +664,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c8c99', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '300bfeb0d71a5b', @@ -435,7 +753,7 @@ describe('TheMediaGrid Adapter', function () { {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -496,7 +814,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c84d34', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); expect(result.length).to.equal(0); }); @@ -566,7 +884,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '32a1f276cb87cb8', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '2164be6358b9', @@ -670,7 +988,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '35bcbc0f7e79c', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '5126e301f4be', From 4adba22909d468753d0e2a9693e34327b1a1749a Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Thu, 30 Jul 2020 11:50:18 -0400 Subject: [PATCH 0041/1476] gptPreAuction module - check that pubads fn is defined (#5557) Co-authored-by: Mark Monday --- modules/gptPreAuction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js index 1c15d87f40c..48b72671d6a 100644 --- a/modules/gptPreAuction.js +++ b/modules/gptPreAuction.js @@ -10,7 +10,7 @@ let hooksAdded = false; export const appendGptSlots = adUnits => { const { customGptSlotMatching } = _currentConfig; - if (!window.googletag) { + if (!utils.isGptPubadsDefined()) { return; } From 42b6523cbc4905369d108031905f370803b56f8e Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 30 Jul 2020 11:52:09 -0400 Subject: [PATCH 0042/1476] Prebid 4.1.1 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 37ee8555b8a..d21d7b0edfa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.2.0-pre", + "version": "4.1.1", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From cfc557ed01e9fdf9dbe2d0033eaf7daf9e8490ac Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 30 Jul 2020 12:03:32 -0400 Subject: [PATCH 0043/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d21d7b0edfa..37ee8555b8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.1.1", + "version": "4.2.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 522a4c085bfb813d65a848eeef1b4905cc5a73db Mon Sep 17 00:00:00 2001 From: r-schweitzer <50628828+r-schweitzer@users.noreply.github.com> Date: Fri, 31 Jul 2020 10:21:49 +0200 Subject: [PATCH 0044/1476] fixed bug with sortByDealAndPriceBucketOrCpm (#5504) * fixed bug with sortByDealAndPriceBucketOrCpm * added unit test --- src/targeting.js | 12 +-- test/spec/unit/core/targeting_spec.js | 125 ++++++++++++++++---------- 2 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/targeting.js b/src/targeting.js index 45c098554a5..5873eeeb559 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -44,7 +44,7 @@ export function getHighestCpmBidsFromBidPool(bidsReceived, highestCpmCallback, a Object.keys(bidsByBidder).forEach(key => bucketBids.push(bidsByBidder[key].reduce(highestCpmCallback))); // if adUnitBidLimit is set, pass top N number bids if (adUnitBidLimit > 0) { - bucketBids = dealPrioritization ? bucketBids(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); + bucketBids = dealPrioritization ? bucketBids.sort(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); bids.push(...bucketBids.slice(0, adUnitBidLimit)); } else { bids.push(...bucketBids); @@ -76,11 +76,11 @@ export function getHighestCpmBidsFromBidPool(bidsReceived, highestCpmCallback, a */ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { return function(a, b) { - if (a.adUnitTargeting.hb_deal !== undefined && b.adUnitTargeting.hb_deal === undefined) { + if (a.adserverTargeting.hb_deal !== undefined && b.adserverTargeting.hb_deal === undefined) { return -1; } - if ((a.adUnitTargeting.hb_deal === undefined && b.adUnitTargeting.hb_deal !== undefined)) { + if ((a.adserverTargeting.hb_deal === undefined && b.adserverTargeting.hb_deal !== undefined)) { return 1; } @@ -89,7 +89,7 @@ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { return b.cpm - a.cpm; } - return b.adUnitTargeting.hb_pb - a.adUnitTargeting.hb_pb; + return b.adserverTargeting.hb_pb - a.adserverTargeting.hb_pb; } } @@ -240,13 +240,13 @@ export function newTargeting(auctionManager) { let targetingMap = Object.keys(targetingCopy).map(adUnitCode => { return { adUnitCode, - adUnitTargeting: targetingCopy[adUnitCode] + adserverTargeting: targetingCopy[adUnitCode] }; }).sort(sortByDealAndPriceBucketOrCpm()); // iterate through the targeting based on above list and transform the keys into the query-equivalent and count characters return targetingMap.reduce(function (accMap, currMap, index, arr) { - let adUnitQueryString = convertKeysToQueryForm(currMap.adUnitTargeting); + let adUnitQueryString = convertKeysToQueryForm(currMap.adserverTargeting); // for the last adUnit - trim last encoded ampersand from the converted query string if ((index + 1) === arr.length) { diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index a5da1c904f0..3aae6e3c33a 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { targeting as targetingInstance, filters, sortByDealAndPriceBucketOrCpm } from 'src/targeting.js'; +import { targeting as targetingInstance, filters, getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm } from 'src/targeting.js'; import { config } from 'src/config.js'; import { getAdUnits, createBidReceived } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; @@ -338,11 +338,44 @@ describe('targeting tests', function () { bid4 = utils.deepClone(bid1); bid4.adserverTargeting['hb_bidder'] = bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 2.25; + bid4.adId = '8383838'; enableSendAllBids = true; bidsReceived.push(bid4); }); + it('when sendBidsControl.bidLimit is set greater than 0 in getHighestCpmBidsFromBidPool', function () { + config.setConfig({ + sendBidsControl: { + bidLimit: 2, + dealPrioritization: true + } + }); + + const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); + + expect(bids.length).to.equal(3); + expect(bids[0].adId).to.equal('8383838'); + expect(bids[1].adId).to.equal('148018fe5e'); + expect(bids[2].adId).to.equal('48747745'); + }); + + it('when sendBidsControl.bidLimit is set greater than 0 and deal priortization is false in getHighestCpmBidsFromBidPool', function () { + config.setConfig({ + sendBidsControl: { + bidLimit: 2, + dealPrioritization: false + } + }); + + const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); + + expect(bids.length).to.equal(3); + expect(bids[0].adId).to.equal('8383838'); + expect(bids[1].adId).to.equal('148018fe5e'); + expect(bids[2].adId).to.equal('48747745'); + }); + it('selects the top n number of bids when enableSendAllBids is true and and bitLimit is set', function () { config.setConfig({ sendBidsControl: { @@ -369,7 +402,7 @@ describe('targeting tests', function () { expect(limitedBids.length).to.equal(2); }); - it('Sends all bids when enableSendAllBids is true and and bitLimit is set to 0', function () { + it('Sends all bids when enableSendAllBids is true and and bidLimit is set to 0', function () { config.setConfig({ sendBidsControl: { bidLimit: 0 @@ -690,171 +723,171 @@ describe('targeting tests', function () { describe('sortByDealAndPriceBucketOrCpm', function() { it('will properly sort bids when some bids have deals and some do not', function () { let bids = [{ - adUnitTargeting: { + adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'ghi', hb_pb: '20.00', hb_deal: '4532' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'jkl', hb_pb: '9.00', hb_deal: '9864' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'mno', hb_pb: '50.00', } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00', } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); - expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); - expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); - expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); - expect(bids[3].adUnitTargeting.hb_adid).to.equal('pqr'); - expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); - expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + expect(bids[0].adserverTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adserverTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adserverTargeting.hb_adid).to.equal('pqr'); + expect(bids[4].adserverTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when all bids have deals', function () { let bids = [{ - adUnitTargeting: { + adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', hb_deal: '4321' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'ghi', hb_pb: '2.50', hb_deal: '4532' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'jkl', hb_pb: '2.00', hb_deal: '9864' } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); - expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); - expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); - expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); - expect(bids[3].adUnitTargeting.hb_adid).to.equal('def'); + expect(bids[0].adserverTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adserverTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when no bids have deals', function () { let bids = [{ - adUnitTargeting: { + adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'def', hb_pb: '0.10' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'ghi', hb_pb: '10.00' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'jkl', hb_pb: '10.01' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'mno', hb_pb: '1.00' } }, { - adUnitTargeting: { + adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00' } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); - expect(bids[0].adUnitTargeting.hb_adid).to.equal('pqr'); - expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); - expect(bids[2].adUnitTargeting.hb_adid).to.equal('ghi'); - expect(bids[3].adUnitTargeting.hb_adid).to.equal('abc'); - expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); - expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + expect(bids[0].adserverTargeting.hb_adid).to.equal('pqr'); + expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adserverTargeting.hb_adid).to.equal('ghi'); + expect(bids[3].adserverTargeting.hb_adid).to.equal('abc'); + expect(bids[4].adserverTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when some bids have deals and some do not and by cpm when flag is set to true', function () { let bids = [{ cpm: 1.04, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { cpm: 0.50, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', hb_deal: '4532' } }, { cpm: 0.53, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'ghi', hb_pb: '0.50', hb_deal: '4532' } }, { cpm: 9.04, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'jkl', hb_pb: '9.00', hb_deal: '9864' } }, { cpm: 50.00, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'mno', hb_pb: '50.00', } }, { cpm: 100.00, - adUnitTargeting: { + adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00', } }]; bids.sort(sortByDealAndPriceBucketOrCpm(true)); - expect(bids[0].adUnitTargeting.hb_adid).to.equal('jkl'); - expect(bids[1].adUnitTargeting.hb_adid).to.equal('abc'); - expect(bids[2].adUnitTargeting.hb_adid).to.equal('ghi'); - expect(bids[3].adUnitTargeting.hb_adid).to.equal('def'); - expect(bids[4].adUnitTargeting.hb_adid).to.equal('pqr'); - expect(bids[5].adUnitTargeting.hb_adid).to.equal('mno'); + expect(bids[0].adserverTargeting.hb_adid).to.equal('jkl'); + expect(bids[1].adserverTargeting.hb_adid).to.equal('abc'); + expect(bids[2].adserverTargeting.hb_adid).to.equal('ghi'); + expect(bids[3].adserverTargeting.hb_adid).to.equal('def'); + expect(bids[4].adserverTargeting.hb_adid).to.equal('pqr'); + expect(bids[5].adserverTargeting.hb_adid).to.equal('mno'); }); }); From e6d3a5c33d83f25cede2d2c202acfeb065e0cb41 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Fri, 31 Jul 2020 15:55:15 +0200 Subject: [PATCH 0045/1476] Mediasquare bidder: add metrics to onBidWon Event (#5556) * Mediasquare: Add support for uspConsent + schain userIds support. Plus enhance userSync * fix iframeEnabled and pixelEnabled + suggested shortand statement * mediasquare bidder: add metrics to onBidWon Event --- modules/mediasquareBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index cb52c288caf..288526d3cc5 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -136,7 +136,7 @@ export const spec = { // fires a pixel to confirm a winning bid let params = []; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond'] + let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'auctionId', 'requestId'] if (bid.hasOwnProperty('mediasquare')) { if (bid['mediasquare'].hasOwnProperty('bidder')) { params.push('bidder=' + bid['mediasquare']['bidder']); } if (bid['mediasquare'].hasOwnProperty('code')) { params.push('code=' + bid['mediasquare']['code']); } From 477fe0c10d78d878aa8135cc4852957874447759 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Fri, 31 Jul 2020 19:39:34 +0530 Subject: [PATCH 0046/1476] Replace third party deep-equal library with native implementation (#5509) * replace deep-equal library with native implementation * remove reference from allowedModules --- allowedModules.js | 3 +- package-lock.json | 185 +++++++++++++++++++++++++++++++++++----- package.json | 2 +- src/utils.js | 20 ++++- test/spec/utils_spec.js | 63 ++++++++++++++ 5 files changed, 247 insertions(+), 26 deletions(-) diff --git a/allowedModules.js b/allowedModules.js index 2a521f781f9..d8e8b69f593 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -21,7 +21,6 @@ module.exports = { 'fun-hooks/no-eval', 'just-clone', 'dlv', - 'dset', - 'deep-equal' + 'dset' ] }; diff --git a/package-lock.json b/package-lock.json index 964a2d81802..4f3d2120d72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.0.0", + "version": "3.27.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3136,6 +3136,12 @@ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -3406,6 +3412,15 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -6463,16 +6478,33 @@ } }, "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", + "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "dev": true, "requires": { + "es-abstract": "^1.17.5", + "es-get-iterator": "^1.1.0", "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", + "is-date-object": "^1.0.2", + "is-regex": "^1.0.5", + "isarray": "^2.0.5", + "object-is": "^1.1.2", "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "object.assign": "^4.1.0", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } } }, "deep-is": { @@ -6539,6 +6571,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -7754,9 +7787,10 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -9399,6 +9433,12 @@ "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", @@ -9607,7 +9647,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -11122,6 +11163,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -11190,7 +11232,8 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true }, "has-to-string-tag-x": { "version": "1.4.1", @@ -11790,7 +11833,8 @@ "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true }, "is-arrayish": { "version": "0.2.1", @@ -11798,6 +11842,12 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -11807,6 +11857,12 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", + "dev": true + }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", @@ -11816,7 +11872,8 @@ "is-callable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true }, "is-ci": { "version": "2.0.0", @@ -11856,7 +11913,8 @@ "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true }, "is-decimal": { "version": "1.0.4", @@ -11967,6 +12025,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", @@ -12007,9 +12071,10 @@ "dev": true }, "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -12072,10 +12137,23 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } }, + "is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -12103,6 +12181,18 @@ "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", "dev": true }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "dev": true + }, "is-whitespace-character": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", @@ -17400,6 +17490,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -17408,7 +17499,8 @@ "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", @@ -17423,6 +17515,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -18905,6 +18998,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.0-next.1" @@ -19848,6 +19942,16 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "side-channel": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", + "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", + "dev": true, + "requires": { + "es-abstract": "^1.17.0-next.1", + "object-inspect": "^1.7.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -20596,6 +20700,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -20605,6 +20710,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -23920,12 +24026,51 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "dev": true, + "requires": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index 37ee8555b8a..13a7ecebde1 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "body-parser": "^1.19.0", "chai": "^4.2.0", "coveralls": "^3.1.0", + "deep-equal": "^2.0.3", "documentation": "^5.2.2", "es5-shim": "^4.5.14", "eslint-config-standard": "^10.2.1", @@ -105,7 +106,6 @@ "core-js-pure": "^3.6.5", "criteo-direct-rsa-validate": "^1.1.0", "crypto-js": "^3.3.0", - "deep-equal": "^1.0.1", "dlv": "1.1.3", "dset": "2.0.1", "express": "^4.15.4", diff --git a/src/utils.js b/src/utils.js index 88328ff10aa..f0fa57c4cff 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,7 +1,6 @@ /* eslint-disable no-console */ import { config } from './config.js'; import clone from 'just-clone'; -import deepequal from 'deep-equal'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -1169,13 +1168,28 @@ export function buildUrl(obj) { } /** - * This function compares two objects for checking their equivalence. + * This function deeply compares two objects checking for their equivalence. * @param {Object} obj1 * @param {Object} obj2 * @returns {boolean} */ export function deepEqual(obj1, obj2) { - return deepequal(obj1, obj2); + if (obj1 === obj2) return true; + else if ((typeof obj1 === 'object' && obj1 !== null) && (typeof obj2 === 'object' && obj2 !== null)) { + if (Object.keys(obj1).length !== Object.keys(obj2).length) return false; + for (let prop in obj1) { + if (obj2.hasOwnProperty(prop)) { + if (!deepEqual(obj1[prop], obj2[prop])) { + return false; + } + } else { + return false; + } + } + return true; + } else { + return false; + } } export function mergeDeep(target, ...sources) { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 4dbb40557af..fca59633ebe 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1115,4 +1115,67 @@ describe('Utils', function () { }); }); }); + + describe('deepEqual', function() { + it('should return "true" if comparing the same object', function() { + const obj1 = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [1000, 0], sizes: [[1000, 300], [1000, 90], [970, 250], [970, 90], [728, 90]] }, + ], + }, + }; + const obj2 = obj1; + expect(utils.deepEqual(obj1, obj2)).to.equal(true); + }); + it('should return "true" if two deeply nested objects are equal', function() { + const obj1 = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [1000, 0], sizes: [[1000, 300], [1000, 90], [970, 250], [970, 90], [728, 90]] }, + ], + }, + }; + const obj2 = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [1000, 0], sizes: [[1000, 300], [1000, 90], [970, 250], [970, 90], [728, 90]] }, + ], + }, + }; + expect(utils.deepEqual(obj1, obj2)).to.equal(true); + }); + it('should return "true" if comparting the same primitive values', function() { + const primitive1 = 'Prebid.js'; + const primitive2 = 'Prebid.js'; + expect(utils.deepEqual(primitive1, primitive2)).to.equal(true); + }); + it('should return "false" if comparing two different primitive values', function() { + const primitive1 = 12; + const primitive2 = 123; + expect(utils.deepEqual(primitive1, primitive2)).to.equal(false); + }); + it('should return "false" if comparing two different deeply nested objects', function() { + const obj1 = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [1000, 0], sizes: [[1000, 300], [1000, 90], [970, 250], [970, 90], [728, 90]] }, + ], + }, + }; + const obj2 = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [1000, 0], sizes: [[1000, 300], [728, 90]] }, + ], + }, + } + expect(utils.deepEqual(obj1, obj2)).to.equal(false); + }); + }); }); From a0cc8e6c8be3b1ac01b1129365a669292fc2a797 Mon Sep 17 00:00:00 2001 From: Bruce Dou Date: Mon, 3 Aug 2020 05:46:48 +0100 Subject: [PATCH 0047/1476] Pubperf analytics adapter added. (#5550) * pubperf analytics adapter added. * pubperf analytics updated. --- modules/pubperfAnalyticsAdapter.js | 36 ++++++++++++ modules/pubperfAnalyticsAdapter.md | 27 +++++++++ .../modules/pubperfAnalyticsAdapter_spec.js | 55 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 modules/pubperfAnalyticsAdapter.js create mode 100644 modules/pubperfAnalyticsAdapter.md create mode 100644 test/spec/modules/pubperfAnalyticsAdapter_spec.js diff --git a/modules/pubperfAnalyticsAdapter.js b/modules/pubperfAnalyticsAdapter.js new file mode 100644 index 00000000000..800ea1cd550 --- /dev/null +++ b/modules/pubperfAnalyticsAdapter.js @@ -0,0 +1,36 @@ +/** + * Analytics Adapter for Pubperf + */ + +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import * as utils from '../src/utils.js'; + +var pubperfAdapter = adapter({ + global: 'pubperf_pbjs', + analyticsType: 'bundle', + handler: 'on' +}); + +pubperfAdapter.originEnableAnalytics = pubperfAdapter.enableAnalytics; + +pubperfAdapter.enableAnalytics = config => { + if (!config || !config.provider || config.provider !== 'pubperf') { + utils.logError('expected config.provider to equal pubperf'); + return; + } + if (!window['pubperf_pbjs']) { + utils.logError( + `Make sure that Pubperf tag from https://www.pubperf.com is included before the Prebid configuration.` + ); + return; + } + pubperfAdapter.originEnableAnalytics(config); +} + +adapterManager.registerAnalyticsAdapter({ + adapter: pubperfAdapter, + code: 'pubperf' +}); + +export default pubperfAdapter; diff --git a/modules/pubperfAnalyticsAdapter.md b/modules/pubperfAnalyticsAdapter.md new file mode 100644 index 00000000000..50ac3691dda --- /dev/null +++ b/modules/pubperfAnalyticsAdapter.md @@ -0,0 +1,27 @@ +# Overview + +``` +Module Name: Pubperf Analytics Adapter +Module Type: Analytics Adapter +Maintainer: support@transfon.com +``` + +# Description + +Transfon's pubperf analytics adaptor allows you to view detailed auction and prebid information in Meridian. Contact support@transfon.com for more information or to sign up for analytics. + +For more information, please visit https://www.pubperf.com. + + +# Sample pubperf tag to be placed before prebid tag + +``` +(function(i, s, o, g, r, a, m, z) {i['pubperf_pbjs'] = r;i[r] = i[r] || function() {z = Array.prototype.slice.call(arguments);z.unshift(+new Date());(i[r].q = i[r].q || []).push(z)}, i[r].t = 1, i[r].l = 1 * new Date();a = s.createElement(o),m = s.getElementsByTagName(o)[0];a.async = 1;a.src = g;m.parentNode.insertBefore(a, m)})(window, document, 'script', 'https://t.pubperf.com/t/b5a635e307.js', 'pubperf_pbjs'); +``` + +# Test Parameters +``` +{ + provider: 'pubperf' +} +``` diff --git a/test/spec/modules/pubperfAnalyticsAdapter_spec.js b/test/spec/modules/pubperfAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..b316b44617a --- /dev/null +++ b/test/spec/modules/pubperfAnalyticsAdapter_spec.js @@ -0,0 +1,55 @@ +import pubperfAnalytics from 'modules/pubperfAnalyticsAdapter.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.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() { + beforeEach(function() { + sinon.stub(events, 'getEvents').returns([]); + sinon.stub(utils, 'logError'); + }); + + afterEach(function() { + events.getEvents.restore(); + utils.logError.restore(); + }); + + it('should throw error, when pubperf_pbjs is not defined', function() { + pubperfAnalytics.enableAnalytics({ + 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, {}); + + expect(server.requests.length).to.equal(0); + expect(utils.logError.called).to.equal(true); + }); + + it('track event without errors', function() { + sinon.spy(pubperfAnalytics, 'track'); + + window['pubperf_pbjs'] = function() {}; + + pubperfAnalytics.enableAnalytics({ + 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, {}); + + sinon.assert.callCount(pubperfAnalytics.track, 6); + }); + }); +}); From 5ab0f2dfdcba507ae6b71e4bf875570184286ab4 Mon Sep 17 00:00:00 2001 From: Dan Harton Date: Mon, 3 Aug 2020 01:38:08 -0700 Subject: [PATCH 0048/1476] AdButler Bid Adapter: Add ability to include extra query params (#5543) --- modules/adbutlerBidAdapter.js | 9 +++++++++ modules/adbutlerBidAdapter.md | 5 ++++- test/spec/modules/adbutlerBidAdapter_spec.js | 12 +++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/adbutlerBidAdapter.js b/modules/adbutlerBidAdapter.js index 47162aa2445..06c2eea5d67 100644 --- a/modules/adbutlerBidAdapter.js +++ b/modules/adbutlerBidAdapter.js @@ -25,6 +25,7 @@ export const spec = { let requestURI; let serverRequests = []; let zoneCounters = {}; + let extraParams = {}; for (i = 0; i < validBidRequests.length; i++) { bidRequest = validBidRequests[i]; @@ -32,6 +33,7 @@ export const spec = { accountID = utils.getBidIdParameter('accountID', bidRequest.params); keyword = utils.getBidIdParameter('keyword', bidRequest.params); domain = utils.getBidIdParameter('domain', bidRequest.params); + extraParams = utils.getBidIdParameter('extra', bidRequest.params); if (!(zoneID in zoneCounters)) { zoneCounters[zoneID] = 0; @@ -52,6 +54,13 @@ export const spec = { requestURI += 'kw=' + encodeURIComponent(keyword) + ';'; } + for (let key in extraParams) { + if (extraParams.hasOwnProperty(key)) { + let val = encodeURIComponent(extraParams[key]); + requestURI += `${key}=${val};`; + } + } + zoneCounters[zoneID]++; serverRequests.push({ method: 'GET', diff --git a/modules/adbutlerBidAdapter.md b/modules/adbutlerBidAdapter.md index 5905074270a..1921cc4046e 100644 --- a/modules/adbutlerBidAdapter.md +++ b/modules/adbutlerBidAdapter.md @@ -23,9 +23,12 @@ Module that connects to an AdButler zone to fetch bids. keyword: 'red', //optional minCPM: '1.00', //optional maxCPM: '5.00' //optional + extra: { // optional + foo: "bar" + } } } ] } ]; -``` \ No newline at end of file +``` diff --git a/test/spec/modules/adbutlerBidAdapter_spec.js b/test/spec/modules/adbutlerBidAdapter_spec.js index a9b56ade79e..6dedce321d8 100644 --- a/test/spec/modules/adbutlerBidAdapter_spec.js +++ b/test/spec/modules/adbutlerBidAdapter_spec.js @@ -13,7 +13,10 @@ describe('AdButler adapter', function () { zoneID: '210093', keyword: 'red', minCPM: '1.00', - maxCPM: '5.00' + maxCPM: '5.00', + extra: { + foo: 'bar', + } }, placementCode: '/19968336/header-bid-tag-1', mediaTypes: { @@ -93,6 +96,13 @@ describe('AdButler adapter', function () { expect(requestURL).to.have.string(';kw=red;'); }); + it('should set the extra parameter', () => { + let requests = spec.buildRequests(bidRequests); + let requestURL = requests[0].url; + + expect(requestURL).to.have.string(';foo=bar;'); + }); + it('should increment the count for the same zone', function () { let bidRequests = [ { From b4b77af29ac37a8bf4238dcadcfa4b25718820cd Mon Sep 17 00:00:00 2001 From: frstua Date: Tue, 4 Aug 2020 11:33:23 +0300 Subject: [PATCH 0049/1476] Add apstream adapter (#5508) * Add apstream adapter * Swap minified DSU with transparent implementation * Set DSU if consentManagement disabled * Add possibility to disable DSU via config Co-authored-by: Yevhenii Tykhostup --- modules/apstreamBidAdapter.js | 485 +++++++++++++++++++ modules/apstreamBidAdapter.md | 97 ++++ test/spec/modules/apstreamBidAdapter_spec.js | 324 +++++++++++++ 3 files changed, 906 insertions(+) create mode 100644 modules/apstreamBidAdapter.js create mode 100644 modules/apstreamBidAdapter.md create mode 100644 test/spec/modules/apstreamBidAdapter_spec.js diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js new file mode 100644 index 00000000000..324c125f5ef --- /dev/null +++ b/modules/apstreamBidAdapter.js @@ -0,0 +1,485 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const CONSTANTS = { + DSU_KEY: 'apr_dsu', + BIDDER_CODE: 'apstream', + GVLID: 394 +}; +const storage = getStorageManager(CONSTANTS.GVLID, CONSTANTS.BIDDER_CODE); + +var dsuModule = (function() { + 'use strict'; + + var DSU_KEY = 'apr_dsu'; + var DSU_VERSION_NUMBER = '1'; + var SIGNATURE_SALT = 'YicAu6ZpNG'; + var DSU_CREATOR = {'USERREPORT': '1'}; + + function stringToU8(str) { + if (typeof TextEncoder === 'function') { + return new TextEncoder().encode(str); + } + str = unescape(encodeURIComponent(str)); + var bytes = new Uint8Array(str.length); + for (var i = 0, j = str.length; i < j; i++) { + bytes[i] = str.charCodeAt(i); + } + return bytes; + } + + function _add(a, b) { + var rl = a.l + b.l; + var a2 = { + h: a.h + b.h + (rl / 2 >>> 31) >>> 0, + l: rl >>> 0 + }; + a.h = a2.h; + a.l = a2.l; + } + function _xor(a, b) { + a.h ^= b.h; + a.h >>>= 0; + a.l ^= b.l; + a.l >>>= 0; + } + function _rotl(a, n) { + var a2 = { + h: a.h << n | a.l >>> (32 - n), + l: a.l << n | a.h >>> (32 - n) + }; + a.h = a2.h; + a.l = a2.l; + } + function _rotl32(a) { + var al = a.l; + a.l = a.h; + a.h = al; + } + + function _compress(v0, v1, v2, v3) { + _add(v0, v1); + _add(v2, v3); + _rotl(v1, 13); + _rotl(v3, 16); + _xor(v1, v0); + _xor(v3, v2); + _rotl32(v0); + _add(v2, v1); + _add(v0, v3); + _rotl(v1, 17); + _rotl(v3, 21); + _xor(v1, v2); + _xor(v3, v0); + _rotl32(v2); + } + function _getInt(a, offset) { + return a[offset + 3] << 24 | + a[offset + 2] << 16 | + a[offset + 1] << 8 | + a[offset]; + } + + function hash(key, m) { + if (typeof m === 'string') { + m = stringToU8(m); + } + var k0 = { + h: key[1] >>> 0, + l: key[0] >>> 0 + }; + var k1 = { + h: key[3] >>> 0, + l: key[2] >>> 0 + }; + var v0 = { + h: k0.h, + l: k0.l + }; + var v2 = k0; + var v1 = { + h: k1.h, + l: k1.l + }; + var v3 = k1; + var ml = m.length; + var ml7 = ml - 7; + var buf = new Uint8Array(new ArrayBuffer(8)); + _xor(v0, { + h: 0x736f6d65, + l: 0x70736575 + }); + _xor(v1, { + h: 0x646f7261, + l: 0x6e646f6d + }); + _xor(v2, { + h: 0x6c796765, + l: 0x6e657261 + }); + _xor(v3, { + h: 0x74656462, + l: 0x79746573 + }); + var mp = 0; + while (mp < ml7) { + var mi = { + h: _getInt(m, mp + 4), + l: _getInt(m, mp) + }; + _xor(v3, mi); + _compress(v0, v1, v2, v3); + _compress(v0, v1, v2, v3); + _xor(v0, mi); + mp += 8; + } + buf[7] = ml; + var ic = 0; + while (mp < ml) { + buf[ic++] = m[mp++]; + } + while (ic < 7) { + buf[ic++] = 0; + } + var mil = { + h: buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4], + l: buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0] + }; + _xor(v3, mil); + _compress(v0, v1, v2, v3); + _compress(v0, v1, v2, v3); + _xor(v0, mil); + _xor(v2, { + h: 0, + l: 0xff + }); + _compress(v0, v1, v2, v3); + _compress(v0, v1, v2, v3); + _compress(v0, v1, v2, v3); + _compress(v0, v1, v2, v3); + var h = v0; + _xor(h, v1); + _xor(h, v2); + _xor(h, v3); + return h; + } + + function hashHex(key, m) { + var r = hash(key, m); + return ('0000000' + r.h.toString(16)).substr(-8) + + ('0000000' + r.l.toString(16)).substr(-8); + } + + var SIPHASH_KEY = [0x86395a57, 0x6b5ba7f7, 0x69732c07, 0x2a6ef48d]; + var hashWithKey = hashHex.bind(null, SIPHASH_KEY); + + var parseUrlRegex = new RegExp('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?'); + var overwrite = null; + var cache = {}; + function parseUrl(url) { + var addscheme = + url.indexOf('/') !== 0 && + url.indexOf('/') !== -1 && + (url.indexOf(':') === -1 || url.indexOf(':') > url.indexOf('/')); + + var match = parseUrlRegex.exec(addscheme ? 'noscheme://' + url : url); + var res = { + scheme: addscheme ? '' : match[2] || '', + host: match[4] || '', + hostname: match[4] ? match[4].split(':')[0] : '', + pathname: match[5] || '', + search: match[7] || '', + hash: match[9] || '', + toString: function () { + return url; + } + }; + + res.origin = res.scheme + '://' + res.host; + return res; + } + + function location() { + var url = overwrite || window.location.toString(); + url = url.replace(/\.demo\.audienceproject\.com\//, '/'); + + if (cache.url === url) { + return cache.parsed; + } + var parsed = parseUrl(url); + cache.url = url; + cache.parsed = parsed; + return parsed; + } + + function getDaysSinceApEpoch() { + var timeDiff = (new Date()).getTime() - (new Date(2019, 0, 1)).getTime(); + var daysSinceApEpoch = Math.floor(timeDiff / (1000 * 3600 * 24)); + return daysSinceApEpoch; + } + + function generateDsu() { + var dsuId = utils.generateUUID(); + var loc = location(); + + var dsuIdSuffix = hashWithKey(dsuId + loc.toString()); + var suffix4 = dsuIdSuffix.substr(0, 4); + var suffix8 = dsuIdSuffix.substr(4); + + dsuId = dsuId.substr(0, 19) + suffix4 + '-' + suffix8; + + var daysSinceApEpoch = getDaysSinceApEpoch(); + var originHash = hashWithKey(loc.origin); + + var metadata = [ + DSU_CREATOR.USERREPORT, + daysSinceApEpoch, + originHash + ].join('.'); + var signature = hashWithKey(dsuId + metadata + SIGNATURE_SALT); + + return [DSU_VERSION_NUMBER, signature, dsuId, metadata].join('.'); + } + + function readOrCreateDsu() { + var dsu; + try { + dsu = storage.getDataFromLocalStorage(DSU_KEY); + } catch (err) { + return null; + } + + if (!dsu) { + dsu = generateDsu(); + } + + try { + storage.setDataInLocalStorage(DSU_KEY, dsu); + } catch (err) { + return null; + } + + return dsu; + } + + return { + readOrCreateDsu: readOrCreateDsu + } +})(); + +function serializeSizes(sizes) { + if (Array.isArray(sizes[0]) === false) { + sizes = [sizes]; + } + + return sizes.map(s => s[0] + 'x' + s[1]).join('_'); +} + +function getRawConsentString(gdprConsentConfig) { + if (!gdprConsentConfig || gdprConsentConfig.gdprApplies === false) { + return null; + } + + return gdprConsentConfig.consentString; +} + +function getConsentStringFromPrebid(gdprConsentConfig) { + const consentString = getRawConsentString(gdprConsentConfig); + if (!consentString) { + return null; + } + + let isIab = config.getConfig('consentManagement.cmpApi') != 'static'; + let vendorConsents = ( + gdprConsentConfig.vendorData.vendorConsents || + (gdprConsentConfig.vendorData.vendor || {}).consents || + {} + ); + let isConsentGiven = !!vendorConsents[CONSTANTS.GVLID.toString(10)]; + + return isIab && isConsentGiven ? consentString : null; +} + +function getIabConsentString(bidderRequest) { + if (utils.deepAccess(bidderRequest, 'gdprConsent')) { + return getConsentStringFromPrebid(bidderRequest.gdprConsent); + } + + return 'disabled'; +} + +function injectPixels(ad, pixels, scripts) { + if (!pixels && !scripts) { + return ad; + } + + let trackedAd = ad; + if (pixels) { + pixels.forEach(pixel => { + const tracker = utils.createTrackPixelHtml(pixel); + trackedAd += tracker; + }); + } + + if (scripts) { + scripts.forEach(script => { + const tracker = ``; + trackedAd += tracker; + }); + } + + return trackedAd; +} + +function getScreenParams() { + return `${window.screen.width}x${window.screen.height}@${window.devicePixelRatio}`; +} + +function getBids(bids) { + const bidArr = bids.map(bid => { + const bidId = bid.bidId; + + let mediaType = ''; + const mediaTypes = Object.keys(bid.mediaTypes) + switch (mediaTypes[0]) { + case 'video': + mediaType = 'v'; + break; + + case 'native': + mediaType = 'n'; + break; + + case 'audio': + mediaType = 'a'; + break; + + default: + mediaType = 'b'; + break; + } + + let adUnitCode = `,c=${bid.adUnitCode}`; + if (bid.params.code) { + adUnitCode = `,c=${encodeURIComponent(bid.params.code)}`; + } + if (bid.params.adunitId) { + adUnitCode = `,u=${encodeURIComponent(bid.params.adunitId)}`; + } + + return `${bidId}:t=${mediaType},s=${serializeSizes(bid.sizes)}${adUnitCode}`; + }); + + return bidArr.join(';'); +}; + +function getEndpointsGroups(bidRequests) { + let endpoints = []; + const getEndpoint = bid => { + if (bid.params.test) { + return `https://mock-bapi.userreport.com/v2/${bid.params.publisherId}/bid`; + } + + if (bid.params.endpoint) { + return `${bid.params.endpoint}${bid.params.publisherId}/bid`; + } + + return `https://bapi.userreport.com/v2/${bid.params.publisherId}/bid`; + } + bidRequests.forEach(bid => { + const exist = endpoints.filter(item => item.endpoint.indexOf(bid.params.endpoint) > -1)[0]; + if (exist) { + exist.bids.push(bid); + } else { + endpoints.push({ + endpoint: getEndpoint(bid), + bids: [bid] + }); + } + }); + + return endpoints; +} + +function isBidRequestValid(bid) { + const isPublisherIdExist = !!bid.params.publisherId; + const isOneMediaType = Object.keys(bid.mediaTypes).length === 1; + + return isPublisherIdExist && isOneMediaType; +} + +function buildRequests(bidRequests, bidderRequest) { + const data = { + med: encodeURIComponent(window.location.href), + auid: bidderRequest.auctionId, + ref: document.referrer, + dnt: utils.getDNT() ? 1 : 0, + sr: getScreenParams() + }; + + const consentData = getRawConsentString(bidderRequest.gdprConsent); + data.iab_consent = consentData; + + const options = { + withCredentials: true + }; + + const isConsent = getIabConsentString(bidderRequest); + const noDsu = config.getConfig('apstream.noDsu'); + if (!isConsent || noDsu) { + data.dsu = ''; + } else { + data.dsu = dsuModule.readOrCreateDsu(); + } + + if (!isConsent || isConsent === 'disabled') { + options.withCredentials = false; + } + + const endpoints = getEndpointsGroups(bidRequests); + const serverRequests = endpoints.map(item => ({ + method: 'GET', + url: item.endpoint, + data: { + ...data, + bids: getBids(item.bids), + rnd: Math.random() + }, + options: options + })); + + return serverRequests; +} + +function interpretResponse(serverResponse) { + let bidResponses = serverResponse && serverResponse.body; + + if (!bidResponses || !bidResponses.length) { + return []; + } + + return bidResponses.map(x => ({ + requestId: x.bidId, + cpm: x.bidDetails.cpm, + width: x.bidDetails.width, + height: x.bidDetails.height, + creativeId: x.bidDetails.creativeId, + currency: x.bidDetails.currency || 'USD', + netRevenue: x.bidDetails.netRevenue, + dealId: x.bidDetails.dealId, + ad: injectPixels(x.bidDetails.ad, x.bidDetails.noticeUrls, x.bidDetails.impressionScripts), + ttl: x.bidDetails.ttl, + })); +} + +export const spec = { + code: CONSTANTS.BIDDER_CODE, + gvlid: CONSTANTS.GVLID, + isBidRequestValid: isBidRequestValid, + buildRequests: buildRequests, + interpretResponse: interpretResponse +} + +registerBidder(spec); diff --git a/modules/apstreamBidAdapter.md b/modules/apstreamBidAdapter.md new file mode 100644 index 00000000000..e528307a003 --- /dev/null +++ b/modules/apstreamBidAdapter.md @@ -0,0 +1,97 @@ +# Overview + +``` +Module Name: AP Stream Bidder Adapter +Module Type: Bidder Adapter +Maintainer: tech@audienceproject.com +gdpr_supported: true +tcf2_supported: true +``` + +# Description + +Module that connects to AP Stream source + +# Inherit from prebid.js +``` + var adUnits = [ + { + code: '/19968336/header-bid-tag-1', + mediaTypes: { // mandatory and should be only one + banner: { + sizes: [[920,180], [920, 130]] + } + }, + bids: [{ + bidder: 'apstream', + params: { + publisherId: STREAM_PIBLISHER_ID // mandatory + } + }] + } + ]; +``` + +# Explicit ad-unit code +``` + var website = null; + switch (location.hostname) { + case "site1.com": + website = "S1"; + break; + case "site2.com": + website = "S2"; + break; + } + + var adUnits = [ + { + code: '/19968336/header-bid-tag-1', + mediaTypes: { // mandatory and should be only one + banner: { + sizes: [[920,180], [920, 130]] + } + }, + bids: [{ + bidder: 'apstream', + params: { + publisherId: STREAM_PIBLISHER_ID, // mandatory + code: website + '_Leaderboard' + } + }] + } + ]; +``` + +# Explicit ad-unit ID +``` + var adUnits = [ + { + code: '/19968336/header-bid-tag-1', + mediaTypes: { // mandatory and should be only one + banner: { + sizes: [[920,180], [920, 130]] + } + }, + bids: [{ + bidder: 'apstream', + params: { + publisherId: STREAM_PIBLISHER_ID, // mandatory + adunitId: 1234 + } + }] + } + ]; +``` + +# DSU + +To disable DSU use config option: + +``` + pbjs.setConfig({ + apstream: { + noDsu: true + } + }); +``` diff --git a/test/spec/modules/apstreamBidAdapter_spec.js b/test/spec/modules/apstreamBidAdapter_spec.js new file mode 100644 index 00000000000..c6546a3bd83 --- /dev/null +++ b/test/spec/modules/apstreamBidAdapter_spec.js @@ -0,0 +1,324 @@ +// jshint esversion: 6, es3: false, node: true +import {assert, expect} from 'chai'; +import {config} from 'src/config.js'; +import {spec} from 'modules/apstreamBidAdapter.js'; +import * as utils from 'src/utils.js'; + +const validBidRequests = [{ + bidId: 'bidId', + adUnitCode: '/id/site1/header-ad', + sizes: [[980, 120], [980, 180]], + mediaTypes: { + banner: { + sizes: [[980, 120], [980, 180]] + } + }, + params: { + publisherId: '1234' + } +}]; + +describe('AP Stream adapter', function() { + describe('isBidRequestValid', function() { + const bid = { + bidder: 'apstream', + mediaTypes: { + banner: { + sizes: [300, 250] + } + }, + params: { + } + }; + + it('should return true when publisherId is configured and one media type', function() { + bid.params.publisherId = '1234'; + assert(spec.isBidRequestValid(bid)) + }); + + it('should return false when publisherId is configured and two media types', function() { + bid.mediaTypes.video = {sizes: [300, 250]}; + assert.isFalse(spec.isBidRequestValid(bid)) + }); + }); + + describe('buildRequests', function() { + it('should send request with correct structure', function() { + const request = spec.buildRequests(validBidRequests, { })[0]; + + assert.equal(request.method, 'GET'); + assert.deepEqual(request.options, {withCredentials: false}); + assert.ok(request.data); + }); + + it('should send request with different endpoints', function() { + const validTwoBidRequests = [ + ...validBidRequests, + ...[{ + bidId: 'bidId2', + adUnitCode: '/id/site1/header-ad', + sizes: [[980, 980], [980, 900]], + mediaTypes: { + banner: { + sizes: [[980, 980], [980, 900]] + } + }, + params: { + publisherId: '1234', + endpoint: 'site2.com' + } + }] + ]; + + const request = spec.buildRequests(validTwoBidRequests, {}); + assert.isArray(request); + assert.lengthOf(request, 2); + assert.equal(request[1].data.bids, 'bidId2:t=b,s=980x980_980x900,c=/id/site1/header-ad'); + }); + + it('should send request with adUnit code', function() { + const adunitCodeValidBidRequests = [ + { + ...validBidRequests[0], + ...{ + params: { + code: 'Site1_Leaderboard' + } + } + } + ]; + + const request = spec.buildRequests(adunitCodeValidBidRequests, { })[0]; + assert.equal(request.data.bids, 'bidId:t=b,s=980x120_980x180,c=Site1_Leaderboard'); + }); + + it('should send request with adUnit id', function() { + const adunitIdValidBidRequests = [ + { + ...validBidRequests[0], + ...{ + params: { + adunitId: '12345' + } + } + } + ]; + + const request = spec.buildRequests(adunitIdValidBidRequests, { })[0]; + assert.equal(request.data.bids, 'bidId:t=b,s=980x120_980x180,u=12345'); + }); + + it('should send request with different media type', function() { + const types = { + 'audio': 'a', + 'banner': 'b', + 'native': 'n', + 'video': 'v' + } + Object.keys(types).forEach(key => { + const adunitIdValidBidRequests = [ + { + ...validBidRequests[0], + ...{ + mediaTypes: { + [key]: { + sizes: [300, 250] + } + } + } + } + ]; + + const request = spec.buildRequests(adunitIdValidBidRequests, { })[0]; + assert.equal(request.data.bids, `bidId:t=${types[key]},s=980x120_980x180,c=/id/site1/header-ad`); + }) + }); + + describe('gdpr', function() { + let mockConfig; + + beforeEach(function () { + mockConfig = { + consentManagement: { + cmpApi: 'iab' + } + }; + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + afterEach(function () { + config.getConfig.restore(); + }); + + it('should send GDPR Consent data', function() { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '394': true + } + } + } + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + assert.equal(request.iab_consent, bidderRequest.gdprConsent.consentString); + }); + }); + + describe('dsu', function() { + it('should pass DSU from local storage if set', function() { + let dsu = 'some_dsu'; + localStorage.setItem('apr_dsu', dsu); + + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '394': true + } + } + } + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + assert.equal(request.dsu, dsu); + }); + + it('should generate new DSU if nothing in local storage', function() { + localStorage.removeItem('apr_dsu'); + + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'consentDataString', + vendorData: { + vendorConsents: { + '394': true + } + } + } + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + let dsu = localStorage.getItem('apr_dsu'); + + assert.isNotEmpty(dsu); + assert.equal(request.dsu, dsu); + }); + }); + }); + + describe('dsu config', function() { + let mockConfig; + beforeEach(function () { + mockConfig = { + apstream: { + noDsu: true + } + }; + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + afterEach(function () { + config.getConfig.restore(); + }); + + it('should not send DSU if it is disabled in config', function() { + const request = spec.buildRequests(validBidRequests, { })[0]; + + assert.equal(request.data.dsu, ''); + }); + }); + + describe('interpretResponse', function () { + it('should return empty array if no body in response', function () { + const serverResponse = {}; + const bidRequest = {}; + + assert.isEmpty(spec.interpretResponse(serverResponse, bidRequest)); + }); + + it('should map server response', function () { + const serverResponse = { + body: [ + { + bidId: 123, + bidDetails: { + cpm: 1.23, + width: 980, + height: 300, + currency: 'DKK', + netRevenue: 'true', + creativeId: '1234', + dealId: '99008', + ad: '

Buy our something!

', + ttl: 360 + } + } + ] + }; + const bidRequest = {}; + + const response = spec.interpretResponse(serverResponse, bidRequest); + + const expected = { + requestId: 123, + cpm: 1.23, + width: 980, + height: 300, + currency: 'DKK', + creativeId: '1234', + netRevenue: 'true', + dealId: '99008', + ad: '

Buy our something!

', + ttl: 360 + }; + + assert.deepEqual(response[0], expected); + }); + + it('should add pixels to ad', function () { + const serverResponse = { + body: [ + { + bidId: 123, + bidDetails: { + cpm: 1.23, + width: 980, + height: 300, + currency: 'DKK', + creativeId: '1234', + netRevenue: 'true', + dealId: '99008', + ad: '

Buy our something!

', + ttl: 360, + noticeUrls: [ + 'site1', + 'site2' + ], + impressionScripts: [ + 'url_to_script' + ] + } + } + ] + }; + const bidRequest = {}; + + const response = spec.interpretResponse(serverResponse, bidRequest); + + assert.match(response[0].ad, /site1/); + assert.match(response[0].ad, /site2/); + }); + }); +}); From 609da199f4d48fc11b5627b08cfe5b4311159897 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 4 Aug 2020 06:41:28 -0700 Subject: [PATCH 0050/1476] floorProvider can come from data obj now (#5559) --- modules/priceFloors.js | 3 +- test/spec/modules/priceFloors_spec.js | 82 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 817f2103ba2..eb1f3aed84c 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -514,6 +514,7 @@ export function handleFetchResponse(fetchResponse) { _floorsConfig.data = fetchData; // set skipRate override if necessary _floorsConfig.skipRate = utils.isNumber(fetchData.skipRate) ? fetchData.skipRate : _floorsConfig.skipRate; + _floorsConfig.floorProvider = fetchData.floorProvider || _floorsConfig.floorProvider; } // if any auctions are waiting for fetch to finish, we need to continue them! @@ -569,7 +570,7 @@ export function handleSetFloorsConfig(config) { _floorsConfig = utils.pick(config, [ 'enabled', enabled => enabled !== false, // defaults to true 'auctionDelay', auctionDelay => auctionDelay || 0, - 'floorProvider', + 'floorProvider', floorProvider => utils.deepAccess(config, 'data.floorProvider', floorProvider), 'endpoint', endpoint => endpoint || {}, 'skipRate', () => !isNaN(utils.deepAccess(config, 'data.skipRate')) ? config.data.skipRate : config.skipRate || 0, 'enforcement', enforcement => utils.pick(enforcement || {}, [ diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 5cb31ffab4c..0a006e0eb48 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -365,6 +365,52 @@ describe('the price floors module', function () { floorProvider: 'floorprovider' }); }); + it('should pick the right floorProvider', function () { + let inputFloors = { + ...basicFloorConfig, + floorProvider: 'providerA', + data: { + ...basicFloorData, + floorProvider: 'providerB', + } + }; + handleSetFloorsConfig(inputFloors); + runStandardAuction(); + validateBidRequests(true, { + skipped: false, + modelVersion: 'basic model', + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: 'providerB' + }); + + // if not at data level take top level + delete inputFloors.data.floorProvider; + handleSetFloorsConfig(inputFloors); + runStandardAuction(); + validateBidRequests(true, { + skipped: false, + modelVersion: 'basic model', + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: 'providerA' + }); + + // if none should be undefined + delete inputFloors.floorProvider; + handleSetFloorsConfig(inputFloors); + runStandardAuction(); + validateBidRequests(true, { + skipped: false, + modelVersion: 'basic model', + location: 'setConfig', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined + }); + }); it('should take the right skipRate depending on input', function () { // first priority is data object sandbox.stub(Math, 'random').callsFake(() => 0.99); @@ -643,6 +689,42 @@ describe('the price floors module', function () { floorProvider: 'floorprovider' }); }); + it('it should correctly overwrite floorProvider with fetch provider', function () { + // init the fake server with response stuff + let fetchFloorData = { + ...basicFloorData, + floorProvider: 'floorProviderD', // change the floor provider + modelVersion: 'fetch model name', // change the model name + }; + fakeFloorProvider.respondWith(JSON.stringify(fetchFloorData)); + + // run setConfig indicating fetch + handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorproviderC', auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); + + // floor provider should be called + expect(fakeFloorProvider.requests.length).to.equal(1); + expect(fakeFloorProvider.requests[0].url).to.equal('http://www.fakeFloorProvider.json'); + + // start the auction it should delay and not immediately call `continueAuction` + runStandardAuction(); + + // exposedAdUnits should be undefined if the auction has not continued + expect(exposedAdUnits).to.be.undefined; + + // make the fetch respond + fakeFloorProvider.respond(); + + // the exposedAdUnits should be from the fetch not setConfig level data + // and fetchStatus is success since fetch worked + validateBidRequests(true, { + skipped: false, + modelVersion: 'fetch model name', + location: 'fetch', + skipRate: 0, + fetchStatus: 'success', + floorProvider: 'floorProviderD' + }); + }); it('it should correctly overwrite skipRate with fetch skipRate', function () { // so floors does not skip sandbox.stub(Math, 'random').callsFake(() => 0.99); From d048f270ee400c51387ef037e74239bab07825c5 Mon Sep 17 00:00:00 2001 From: susyt Date: Tue, 4 Aug 2020 06:41:40 -0700 Subject: [PATCH 0051/1476] GumGum: adds support for floor module (#5532) * adds support for floor module * fixes lint --- modules/gumgumBidAdapter.js | 35 ++++++++++++++++++++-- test/spec/modules/gumgumBidAdapter_spec.js | 29 ++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 8364cd57579..ebeb46e3c44 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -198,6 +198,34 @@ function _getVidParams (attributes) { }; } +/** + * Gets bidfloor + * @param {Object} mediaTypes + * @param {Number} bidfloor + * @param {Object} bid + * @returns {Number} floor + */ +function _getFloor (mediaTypes, bidfloor, bid) { + const curMediaType = Object.keys(mediaTypes)[0] || 'banner'; + let floor = bidfloor || 0; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: '*' + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + /** * Make a server request from the list of BidRequests. * @@ -219,6 +247,7 @@ function buildRequests (validBidRequests, bidderRequest) { transactionId, userId = {} } = bidRequest; + const bidFloor = _getFloor(mediaTypes, params.bidfloor, bidRequest); let sizes = [1, 1]; let data = {}; @@ -231,9 +260,11 @@ function buildRequests (validBidRequests, bidderRequest) { if (pageViewId) { data.pv = pageViewId; } - if (params.bidfloor) { - data.fp = params.bidfloor; + + if (bidFloor) { + data.fp = bidFloor; } + if (params.inScreenPubID) { data.pubId = params.inScreenPubID; data.pi = 2; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 65ec52aa8c4..af929f437da 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -133,6 +133,35 @@ describe('gumgumAdapter', function () { expect(bidRequest.sizes).to.equal(vidMediaTypes.video.playerSize); }); + describe('floorModule', function () { + const floorTestData = { + 'currency': 'USD', + 'floor': 1.50 + }; + bidRequests[0].getFloor = _ => { + return floorTestData; + }; + it('should return the value from getFloor if present', function () { + const request = { ...bidRequests[0] }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.fp).to.equal(floorTestData.floor); + }); + it('should return the getFloor.floor value if it is greater than bidfloor', function () { + const bidfloor = 0.80; + const request = { ...bidRequests[0] }; + request.params.bidfloor = bidfloor; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.fp).to.equal(floorTestData.floor); + }); + it('should return the bidfloor value if it is greater than getFloor.floor', function () { + const bidfloor = 1.80; + const request = { ...bidRequests[0] }; + request.params.bidfloor = bidfloor; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.fp).to.equal(bidfloor); + }); + }); + it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests)[0]; expect(request.url).to.equal(ENDPOINT); From 22f0974ca089b9a772ef850e01b6bec6d0215841 Mon Sep 17 00:00:00 2001 From: Corey Kress Date: Tue, 4 Aug 2020 10:39:07 -0400 Subject: [PATCH 0052/1476] [Synacormedia] Fix bug with regex regarding ad size (#5561) * CAP-1614 - updated docs to show correct size for banner and some other small fixes * CAP-1636 support schain object in prebid * CAP-1636 updated the review comments * CAP-1849 - split up banner and video impressions to use format * CAP-1879 - added adapter support for consent management module * CAP-1879 - updates for pr * CAP-1879 - remove unneeded checks * CAP-1920 - fixed size bug * CAP-1920 - update tests and banner * CAP-1920 - fix a comparison, and height setting Co-authored-by: Corey Kress Co-authored-by: Rajkumar Natarajan --- modules/synacormediaBidAdapter.js | 31 ++- .../modules/synacormediaBidAdapter_spec.js | 194 ++++++++++++++++-- 2 files changed, 209 insertions(+), 16 deletions(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index 6725f5aff74..abbae1e7354 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -172,7 +172,7 @@ export const spec = { .filter(param => includes(VIDEO_PARAMS, param) && sourceObj[param] !== null && (!isNaN(parseInt(sourceObj[param], 10)) || !(sourceObj[param].length < 1))) .forEach(param => destObj[param] = Array.isArray(sourceObj[param]) ? sourceObj[param] : parseInt(sourceObj[param], 10)); }, - interpretResponse: function(serverResponse) { + interpretResponse: function(serverResponse, bidRequest) { const updateMacros = (bid, r) => { return r ? r.replace(/\${AUCTION_PRICE}/g, bid.price) : r; }; @@ -189,8 +189,33 @@ export const spec = { seatbid.bid.forEach(bid => { const creative = updateMacros(bid, bid.adm); const nurl = updateMacros(bid, bid.nurl); - const [, impType, impid, width, height] = bid.impid.match(/^([vb])(.*)-(.*)x(.*)$/); - const isVideo = impType == 'v'; + const [, impType, impid] = bid.impid.match(/^([vb])(.*)$/); + let height = bid.h; + let width = bid.w; + const isVideo = impType === 'v'; + const isBanner = impType === 'b'; + if ((!height || !width) && bidRequest.data && bidRequest.data.imp && bidRequest.data.imp.length > 0) { + bidRequest.data.imp.forEach(req => { + if (bid.impid === req.id) { + if (isVideo) { + height = req.video.h; + width = req.video.w; + } else if (isBanner) { + let bannerHeight = 1; + let bannerWidth = 1; + if (req.banner.format && req.banner.format.length > 0) { + bannerHeight = req.banner.format[0].h; + bannerWidth = req.banner.format[0].w; + } + height = bannerHeight; + width = bannerWidth; + } else { + height = 1; + width = 1; + } + } + }); + } const bidObj = { requestId: impid, adId: bid.id.replace(/~/g, '-'), diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index d9f6c9b7256..1521f4a2e63 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -688,21 +688,50 @@ describe('synacormediaBidAdapter ', function () { describe('interpretResponse', function () { let bidResponse = { id: '10865933907263896~9998~0', - impid: 'b9876abcd-300x250', + impid: 'b9876abcd', price: 0.13, crid: '1022-250', adm: '', - nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=k5JkFVQ1RJT05fSU1QX0lEPXYyZjczN&AUCTION_PRICE=${AUCTION_PRICE}' + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=k5JkFVQ1RJT05fSU1QX0lEPXYyZjczN&AUCTION_PRICE=${AUCTION_PRICE}', + w: 300, + h: 250 }; let bidResponse2 = { id: '10865933907263800~9999~0', - impid: 'b9876abcd-300x600', + impid: 'b9876abcd', price: 1.99, crid: '9993-013', adm: '', - nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=OTk5OX4wJkFVQ1RJT05fU0VBVF9JR&AUCTION_PRICE=${AUCTION_PRICE}' + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=OTk5OX4wJkFVQ1RJT05fU0VBVF9JR&AUCTION_PRICE=${AUCTION_PRICE}', + w: 300, + h: 600 }; + let bidRequest = { + data: { + id: '', + imp: [ + { + id: 'abc123', + banner: { + format: [ + { + w: 400, + h: 350 + } + ], + pos: 1 + } + } + ], + }, + method: 'POST', + options: { + contentType: 'application/json', + withCredentials: true + }, + url: 'https://prebid.technoratimedia.com/openrtb/bids/prebid?src=prebid_prebid_3.27.0-pre' + }; let serverResponse; beforeEach(function() { serverResponse = { @@ -717,6 +746,26 @@ describe('synacormediaBidAdapter ', function () { }); it('should return 1 video bid when 1 bid is in the video response', function () { + bidRequest = { + data: { + id: 'abcd1234', + imp: [ + { + video: { + w: 640, + h: 480 + }, + id: 'v2da7322b2df61f' + } + ] + }, + method: 'POST', + options: { + contentType: 'application/json', + withCredentials: true + }, + url: 'https://prebid.technoratimedia.com/openrtb/bids/prebid?src=prebid_prebid_3.27.0-pre' + }; let serverRespVideo = { body: { id: 'abcd1234', @@ -725,14 +774,16 @@ describe('synacormediaBidAdapter ', function () { bid: [ { id: '11339128001692337~9999~0', - impid: 'v2da7322b2df61f-640x480', + impid: 'v2da7322b2df61f', price: 0.45, nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', adomain: [ 'psacentral.org' ], cid: 'bidder-crid', crid: 'bidder-cid', - cat: [] + cat: [], + w: 640, + h: 480 } ], seat: '9999' @@ -742,7 +793,7 @@ describe('synacormediaBidAdapter ', function () { }; // serverResponse.body.seatbid[0].bid.push(bidResponse); - let resp = spec.interpretResponse(serverRespVideo); + let resp = spec.interpretResponse(serverRespVideo, bidRequest); expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: '2da7322b2df61f', @@ -763,7 +814,7 @@ describe('synacormediaBidAdapter ', function () { it('should return 1 bid when 1 bid is in the response', function () { serverResponse.body.seatbid[0].bid.push(bidResponse); - let resp = spec.interpretResponse(serverResponse); + let resp = spec.interpretResponse(serverResponse, bidRequest); expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: '9876abcd', @@ -786,7 +837,7 @@ describe('synacormediaBidAdapter ', function () { seat: '9999', bid: [bidResponse2], }); - let resp = spec.interpretResponse(serverResponse); + let resp = spec.interpretResponse(serverResponse, bidRequest); expect(resp).to.be.an('array').to.have.lengthOf(2); expect(resp[0]).to.eql({ requestId: '9876abcd', @@ -818,7 +869,7 @@ describe('synacormediaBidAdapter ', function () { }); it('should not return a bid when no bid is in the response', function () { - let resp = spec.interpretResponse(serverResponse); + let resp = spec.interpretResponse(serverResponse, bidRequest); expect(resp).to.be.an('array').that.is.empty; }); @@ -837,14 +888,16 @@ describe('synacormediaBidAdapter ', function () { bid: [ { id: '11339128001692337~9999~0', - impid: 'v2da7322b2df61f-640x480', + impid: 'v2da7322b2df61f', price: 0.45, nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', adomain: [ 'psacentral.org' ], cid: 'bidder-crid', crid: 'bidder-cid', - cat: [] + cat: [], + w: 640, + h: 480 } ], seat: '9999' @@ -860,10 +913,125 @@ describe('synacormediaBidAdapter ', function () { return config[key]; }); - let resp = spec.interpretResponse(serverRespVideo); + let resp = spec.interpretResponse(serverRespVideo, bidRequest); sandbox.restore(); expect(resp[0].videoCacheKey).to.not.exist; }); + + it('should use video bid request height and width if not present in response', function () { + bidRequest = { + data: { + id: 'abcd1234', + imp: [ + { + video: { + w: 300, + h: 250 + }, + id: 'v2da7322b2df61f' + } + ] + }, + method: 'POST', + options: { + contentType: 'application/json', + withCredentials: true + }, + url: 'https://prebid.technoratimedia.com/openrtb/bids/prebid?src=prebid_prebid_3.27.0-pre' + }; + + let serverRespVideo = { + body: { + id: 'abcd1234', + seatbid: [ + { + bid: [ + { + id: '11339128001692337~9999~0', + impid: 'v2da7322b2df61f', + price: 0.45, + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', + adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', + adomain: [ 'psacentral.org' ], + cid: 'bidder-crid', + crid: 'bidder-cid', + cat: [] + } + ], + seat: '9999' + } + ] + } + }; + let resp = spec.interpretResponse(serverRespVideo, bidRequest); + expect(resp).to.be.an('array').to.have.lengthOf(1); + expect(resp[0]).to.eql({ + requestId: '2da7322b2df61f', + adId: '11339128001692337-9999-0', + cpm: 0.45, + width: 300, + height: 250, + creativeId: '9999_bidder-cid', + currency: 'USD', + netRevenue: true, + mediaType: 'video', + ad: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45\n\n\n', + ttl: 60, + videoCacheKey: 'QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk', + vastUrl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45' + }); + }); + + it('should use banner bid request height and width if not present in response', function () { + bidRequest = { + data: { + id: 'abc123', + imp: [ + { + banner: { + format: [{ + w: 400, + h: 350 + }] + }, + id: 'babc123' + } + ] + }, + method: 'POST', + options: { + contentType: 'application/json', + withCredentials: true + }, + url: 'https://prebid.technoratimedia.com/openrtb/bids/prebid?src=prebid_prebid_3.27.0-pre' + }; + + bidResponse = { + id: '10865933907263896~9998~0', + impid: 'babc123', + price: 0.13, + crid: '1022-250', + adm: '', + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=k5JkFVQ1RJT05fSU1QX0lEPXYyZjczN&AUCTION_PRICE=${AUCTION_PRICE}', + }; + + serverResponse.body.seatbid[0].bid.push(bidResponse); + let resp = spec.interpretResponse(serverResponse, bidRequest); + expect(resp).to.be.an('array').to.have.lengthOf(1); + expect(resp[0]).to.eql({ + requestId: 'abc123', + adId: '10865933907263896-9998-0', + cpm: 0.13, + width: 400, + height: 350, + creativeId: '9998_1022-250', + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: '', + ttl: 60 + }); + }); }); describe('getUserSyncs', function () { it('should return a usersync when iframes is enabled', function () { From a770ca75be510dce85cdad229c90183165782b0a Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Tue, 4 Aug 2020 10:42:11 -0400 Subject: [PATCH 0053/1476] parrableIdSystem: Populate userIdAsEid with Parrable ID and Optout data (#5350) * Add unit coverage for parrableIdSystem getId callback * PBID-14: Pass uspString to Parrable as us_privacy query parameter * PBID-14: Simplify parrableIdSystem us_privacy test * PBID-14: Only send us_privacy to Parrable when a value exists * PBID-11: Read new Parrable compound cookie _parrable_id Migrating from legacy _parrable_eid cookie. The new cookie contains ibaOptout and ccpaOptout status fields * Remove path check from parrableIdSystem url test * PBID-11: Integrate Parrable compound cookie, consolidating old cookies * PBID-11: Update parrableIdSystem requestBids hook test to support compound cookie value * PBID-11: Small refactor to parrableIdSystem spec to support compound cookie * PBID-11: Handle legacy ibaOptout as truthy value when migrating to compound cookie * PBID-11: Add parrableIdSystem spec tests covering migration of legacy cookies * PBID-11: Remove storage documentation from test pages and userId module docs * PBID-11: Remove SUBMODULES_THAT_ALWAYS_REFRESH_ID feature from userId system * PBID-11: Use better serialize implementation for Parrable compound cookie * PBID-11: Update parrableIdSystem interface documentation * Add missing extension to mock xhr import * PBID-11: Try to access eid property only when parrableId object exists * PBID-11: Construct parrableId from legacy cookies in same manner as compound cookie * Use hardcoded expiration date for legacy cookies * PBID-39: Return full parrableId object in decode method * PBID-39: Update all adapters to use parrableId.eid for userId value * PBID-39: Update config for ORTB EIDs to extract parrableId.eid as User UID value * PBID-39: Pass Parrable IBA and CCPA optout status into ORTB EIDs list through UID extensions * PBID-39: Pass a true CCPA optout status to adapters when the EID has been suppressed The userId/eids module will not consider our ID system for inclusion in the EIDs object if our ID value is not a string. Unfortunately when we write our cookie without an EID (in the case where CCPA optout is true) then the deserialized EID value is undefined, to save space in the cookie. So this is a hack that will return an empty string when Prebid is building the EIDs object so that we can still pass our optout status to those that require it to understand why our ID may be missing. * parrableIdSystem: Relocate new unit test from upstream * PBID-39: Fallback to cookie values when backend response is missing components Also handle another missed callback scenario if the response object parses to nothing * PBID-39: Avoid breaking openx bid adapter when renaming our id system * PBID-39: Use array find * Use supported array find method in parrableIdSystem_spec * Restore backwards-compatible parrableId passing to OpenxBidAdapter * VidazooBidAdapter: Consume new parrableId format --- modules/connectadBidAdapter.js | 6 +-- modules/openxBidAdapter.js | 5 +- modules/ozoneBidAdapter.js | 4 +- modules/parrableIdSystem.js | 2 +- modules/pulsepointBidAdapter.js | 2 +- modules/userId/eids.js | 24 ++++++++- modules/vidazooBidAdapter.js | 5 +- test/spec/modules/eids_spec.js | 4 +- test/spec/modules/openxBidAdapter_spec.js | 5 +- test/spec/modules/ozoneBidAdapter_spec.js | 6 +-- test/spec/modules/parrableIdSystem_spec.js | 50 +++++++++++++++++-- .../modules/prebidServerBidAdapter_spec.js | 2 +- test/spec/modules/pubmaticBidAdapter_spec.js | 4 +- .../spec/modules/pulsepointBidAdapter_spec.js | 2 +- test/spec/modules/vidazooBidAdapter_spec.js | 1 + 15 files changed, 97 insertions(+), 25 deletions(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 3dcb8da9838..8b34df563ec 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -85,7 +85,7 @@ export const spec = { }) } - if (validBidRequests[0].userId && typeof validBidRequests[0].userId === 'object' && (validBidRequests[0].userId.tdid || validBidRequests[0].userId.pubcid || validBidRequests[0].userId.lipb || validBidRequests[0].userId.id5id || validBidRequests[0].userId.parrableid)) { + if (validBidRequests[0].userId && typeof validBidRequests[0].userId === 'object' && (validBidRequests[0].userId.tdid || validBidRequests[0].userId.pubcid || validBidRequests[0].userId.lipb || validBidRequests[0].userId.id5id || validBidRequests[0].userId.parrableId)) { utils.deepSetValue(data, 'user.ext.eids', []); if (validBidRequests[0].userId.tdid) { @@ -118,11 +118,11 @@ export const spec = { }); } - if (validBidRequests[0].userId.parrableid) { + if (validBidRequests[0].userId.parrableId) { data.user.ext.eids.push({ source: 'parrable.com', uids: [{ - id: validBidRequests[0].userId.parrableid, + id: validBidRequests[0].userId.parrableId.eid, }] }); } diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 3d2b652d403..ca82e93137e 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -16,7 +16,7 @@ export const USER_ID_CODE_TO_QUERY_ARG = { idl_env: 'lre', // LiveRamp IdentityLink lipb: 'lipbid', // LiveIntent ID netId: 'netid', // netID - parrableid: 'parrableid', // Parrable ID + parrableId: 'parrableid', // Parrable ID pubcid: 'pubcid', // PubCommon ID tdid: 'ttduuid', // The Trade Desk Unified ID }; @@ -276,6 +276,9 @@ function appendUserIdsToQueryParams(queryParams, userIds) { case 'lipb': queryParams[key] = userIdObjectOrValue.lipbid; break; + case 'parrableId': + queryParams[key] = userIdObjectOrValue.eid; + break; default: queryParams[key] = userIdObjectOrValue; } diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 87f140556d0..451ab654c53 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -539,7 +539,7 @@ export const spec = { */ findAllUserIds(bidRequest) { var ret = {}; - let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableid', 'idl_env', 'digitrustid', 'criteortus']; + let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'digitrustid', 'criteortus']; if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; @@ -679,7 +679,7 @@ export const spec = { this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteortus.${BIDDER_CODE}.userid`), 'criteortus', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.lipb.lipbid`), 'liveintent.com', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.parrableid`), 'parrable.com', 1); + this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.parrableId.eid`), 'parrable.com', 1); } return eids; }, diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index ec033e62983..75f89fffc14 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -204,7 +204,7 @@ export const parrableIdSubmodule = { */ decode(parrableId) { if (parrableId && utils.isPlainObject(parrableId)) { - return { 'parrableid': parrableId.eid }; + return { parrableId }; } return undefined; }, diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 7bfa8686728..33fdaa44100 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -421,7 +421,7 @@ function user(bidRequest, bidderRequest) { addExternalUserId(ext.eids, bidRequest.userId.criteoId, 'criteo'); addExternalUserId(ext.eids, bidRequest.userId.idl_env, 'identityLink'); addExternalUserId(ext.eids, bidRequest.userId.id5id, 'id5-sync.com'); - addExternalUserId(ext.eids, bidRequest.userId.parrableid, 'parrable.com'); + addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.parrableId.eid'), 'parrable.com'); // liveintent if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { addExternalUserId(ext.eids, bidRequest.userId.lipb.lipbid, 'liveintent.com'); diff --git a/modules/userId/eids.js b/modules/userId/eids.js index c14777a8888..72be98eca50 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -35,9 +35,29 @@ const USER_IDS_CONFIG = { }, // parrableId - 'parrableid': { + 'parrableId': { source: 'parrable.com', - atype: 1 + atype: 1, + getValue: function(parrableId) { + if (parrableId.eid) { + return parrableId.eid; + } + if (parrableId.ccpaOptout) { + // If the EID was suppressed due to a non consenting ccpa optout then + // we still wish to provide this as a reason to the adapters + return ''; + } + return null; + }, + getUidExt: function(parrableId) { + const extendedData = utils.pick(parrableId, [ + 'ibaOptout', + 'ccpaOptout' + ]); + if (Object.keys(extendedData).length) { + return extendedData; + } + } }, // identityLink diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index b9f3297818f..94bedaa8699 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -23,7 +23,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'idl_env': 1, 'lipb': 1, 'netId': 1, - 'parrableid': 1, + 'parrableId': 1, 'pubcid': 1, 'tdid': 1, }; @@ -112,6 +112,9 @@ function appendUserIdsToRequestPayload(payloadRef, userIds) { case 'lipb': payloadRef[key] = userId.lipbid; break; + case 'parrableId': + payloadRef[key] = userId.eid; + break; default: payloadRef[key] = userId; } diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index c0b4703b7f4..434e1841a0f 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -43,7 +43,9 @@ describe('eids array generation for known sub-modules', function() { it('parrableId', function() { const userId = { - parrableid: 'some-random-id-value' + parrableId: { + eid: 'some-random-id-value' + } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 7fc490a3838..615f8f1e696 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1047,7 +1047,7 @@ describe('OpenxAdapter', function () { idl_env: '1111-idl_env', lipb: {lipbid: '1111-lipb'}, netId: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', - parrableid: 'eidVersion.encryptionKeyReference.encryptedValue', + parrableId: { eid: 'eidVersion.encryptionKeyReference.encryptedValue' }, pubcid: '1111-pubcid', tdid: '1111-tdid', }; @@ -1093,6 +1093,9 @@ describe('OpenxAdapter', function () { case 'lipb': userIdValue = EXAMPLE_DATA_BY_ATTR.lipb.lipbid; break; + case 'parrableId': + userIdValue = EXAMPLE_DATA_BY_ATTR.parrableId.eid; + break; default: userIdValue = EXAMPLE_DATA_BY_ATTR[userIdProviderKey]; } diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index c8a636efb4c..f20e75dfedd 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -66,7 +66,7 @@ var validBidRequestsWithUserIdData = [ params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, 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', 'id5id': 'ID5-someId', 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableid': 'parrableid123'} + userId: {'pubcid': '12345678', 'id5id': 'ID5-someId', 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {eid: 'parrableid123'}} } ]; var validBidRequestsMinimal = [ @@ -2121,7 +2121,7 @@ describe('ozone Adapter', function () { 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', + 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, 'pubcid': '5555', 'tdid': '6666' }; @@ -2141,7 +2141,7 @@ describe('ozone Adapter', function () { 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', + 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in 'tdid': '6666' }; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index fdfd3042561..7db22af82ab 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import find from 'core-js-pure/features/array/find.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { newStorageManager } from 'src/storageManager.js'; @@ -189,11 +190,11 @@ describe('Parrable ID System', function() { let eid = '01.123.4567890'; let parrableId = { eid, - ccpaOptout: true + ibaOptout: true }; expect(parrableIdSubmodule.decode(parrableId)).to.deep.equal({ - parrableid: eid + parrableId }); }); }); @@ -203,7 +204,7 @@ describe('Parrable ID System', function() { beforeEach(function() { adUnits = [getAdUnitMock()]; - writeParrableCookie({ eid: P_COOKIE_EID }); + writeParrableCookie({ eid: P_COOKIE_EID, ibaOptout: true }); setSubmoduleRegistry([parrableIdSubmodule]); init(config); config.setConfig(getConfigMock()); @@ -218,8 +219,47 @@ describe('Parrable ID System', function() { requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(P_COOKIE_EID); + expect(bid).to.have.deep.nested.property('userId.parrableId'); + expect(bid.userId.parrableId.eid).to.equal(P_COOKIE_EID); + expect(bid.userId.parrableId.ibaOptout).to.equal(true); + const parrableIdAsEid = find(bid.userIdAsEids, e => e.source == 'parrable.com'); + expect(parrableIdAsEid).to.deep.equal({ + source: 'parrable.com', + uids: [{ + id: P_COOKIE_EID, + atype: 1, + ext: { + ibaOptout: true + } + }] + }); + }); + }); + done(); + }, { adUnits }); + }); + + 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 }); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.parrableId'); + expect(bid.userId.parrableId).to.not.have.property('eid'); + expect(bid.userId.parrableId.ccpaOptout).to.equal(true); + const parrableIdAsEid = find(bid.userIdAsEids, e => e.source == 'parrable.com'); + expect(parrableIdAsEid).to.deep.equal({ + source: 'parrable.com', + uids: [{ + id: '', + atype: 1, + ext: { + ccpaOptout: true + } + }] + }); }); }); done(); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 654971f0404..b75ede0e9db 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1126,7 +1126,7 @@ describe('S2S Adapter', function () { criteoId: '44VmRDeUE3ZGJ5MzRkRVJHU3BIUlJ6TlFPQUFU', tdid: 'abc123', pubcid: '1234', - parrableid: '01.1563917337.test-eid', + parrableId: { eid: '01.1563917337.test-eid' }, lipb: { lipbid: 'li-xyz', segments: ['segA', 'segB'] diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 1e8ab8f9faf..dd46646abc8 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1517,7 +1517,7 @@ describe('PubMatic adapter', function () { describe('Parrable Id', function() { it('send the Parrable id if it is present', function() { bidRequests[0].userId = {}; - bidRequests[0].userId.parrableid = 'parrable-user-id'; + bidRequests[0].userId.parrableId = { eid: 'parrable-user-id' }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); @@ -1530,7 +1530,7 @@ describe('PubMatic adapter', function () { }]); }); - it('do not pass if not string', function() { + it('do not pass if not object with eid key', function() { bidRequests[0].userId = {}; bidRequests[0].userId.parrableid = 1; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 4b21856b68e..d71bd018ab3 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -631,7 +631,7 @@ describe('PulsePoint Adapter Tests', function () { criteoId: 'criteo_id234', idl_env: 'idl_id123', id5id: 'id5id_234', - parrableid: 'parrable_id234', + parrableId: { eid: 'parrable_id234' }, lipb: { lipbid: 'liveintent_id123' } diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 103cb0897a7..affcc08bd10 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -229,6 +229,7 @@ describe('VidazooBidAdapter', function () { switch (idSystemProvider) { case 'digitrustid': return { data: { id: id } }; case 'lipb': return { lipbid: id }; + case 'parrableId': return { eid: id }; default: return id; } })(); From c3a193c3dc6319021a55438715cf8c6d8ef3b05f Mon Sep 17 00:00:00 2001 From: xwang202 <57196235+xwang202@users.noreply.github.com> Date: Tue, 4 Aug 2020 22:53:32 +0800 Subject: [PATCH 0054/1476] Update warning message (#5565) --- modules/freewheel-sspBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index 5e3717ce1fe..dce678362cb 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -72,7 +72,7 @@ function getPricing(xmlNode) { price: priceNode.textContent || priceNode.innerText }; } else { - utils.logWarn('PREBID - ' + BIDDER_CODE + ': Can\'t get pricing data. Is price awareness enabled?'); + utils.logWarn('PREBID - ' + BIDDER_CODE + ': No bid received or missing pricing extension.'); } return princingData; From 71e70df3ee3397bcbbcca5c13c9c23871c2ee03d Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 5 Aug 2020 15:37:18 -0400 Subject: [PATCH 0055/1476] README - legal notice (#5570) At the suggestion of our legal counsel. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0dd5b25a50f..b3f33b27aa5 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ This README is for developers who want to contribute to Prebid.js. Additional documentation can be found at [the Prebid homepage](http://prebid.org). Working examples can be found in [the developer docs](http://prebid.org/dev-docs/getting-started.html). +Prebid.js is open source software that is offered for free as a convenience. While it is designed to help companies address legal requirements associated with header bidding, we cannot and do not warrant that your use of Prebid.js will satisfy legal requirements. You are solely responsible for ensuring that your use of Prebid.js complies with all applicable laws. We strongly encourage you to obtain legal advice when using Prebid.js to ensure your implementation complies with all laws where you operate. + **Table of Contents** - [Usage](#Usage) From 35f27559b3617591bf133f3cdad3ecf8c2e75f21 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 5 Aug 2020 15:53:27 -0400 Subject: [PATCH 0056/1476] Prebid 4.2.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13a7ecebde1..eb026f6b031 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.2.0-pre", + "version": "4.2.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0ffe83fe78954637c1ef8a90c17a11cdc647ac65 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 5 Aug 2020 16:12:47 -0400 Subject: [PATCH 0057/1476] increment prebid version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb026f6b031..3594d6d7621 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.2.0", + "version": "4.3.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7d7412979d50c3cbae4c453c81c15327275c18ae Mon Sep 17 00:00:00 2001 From: mefjush Date: Wed, 5 Aug 2020 23:11:40 +0200 Subject: [PATCH 0058/1476] Fix a bug for bids originating from DALE (#5566) --- modules/adheseBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 3c129d1bee1..cc95806491f 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -148,7 +148,7 @@ function getbaseAdResponse(response) { } function isAdheseAd(ad) { - return !ad.origin || ad.origin === 'JERLICIA' || ad.origin === 'DALE'; + return !ad.origin || ad.origin === 'JERLICIA'; } function getMediaType(markup) { From 0438f7c052fa74b82a60d3ff918b8d7e72bf053f Mon Sep 17 00:00:00 2001 From: Jimmy Tu Date: Thu, 6 Aug 2020 10:04:16 -0700 Subject: [PATCH 0059/1476] OpenX: Fix bug with floors module for non USD currencies. (#5576) --- modules/openxBidAdapter.js | 8 +- test/spec/modules/openxBidAdapter_spec.js | 98 +++++++++++++++++------ 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index ca82e93137e..f4b6288cd55 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -6,7 +6,9 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'openx'; const BIDDER_CONFIG = 'hb_pb'; -const BIDDER_VERSION = '3.0.2'; +const BIDDER_VERSION = '3.0.3'; + +const DEFAULT_CURRENCY = 'USD'; export const USER_ID_CODE_TO_QUERY_ARG = { britepoolid: 'britepoolid', // BritePool ID @@ -460,9 +462,11 @@ function createVideoBidResponses(response, {bid, startTime}) { function getBidFloor(bidRequest, mediaType) { let floorInfo = {}; + const currency = config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY; + if (typeof bidRequest.getFloor === 'function') { floorInfo = bidRequest.getFloor({ - currency: 'USD', + currency: currency, mediaType: mediaType, size: '*' }); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 615f8f1e696..0808d78c0aa 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1106,7 +1106,7 @@ describe('OpenxAdapter', function () { }); }); - context('floors', function () { + describe('floors', function () { it('should send out custom floors on bids that have customFloors specified', function () { const bidRequest = Object.assign({}, bidRequestsWithMediaTypes[0], @@ -1126,37 +1126,85 @@ describe('OpenxAdapter', function () { expect(dataParams.aumfs).to.equal('1500'); }); - it('should send out floors on bids when there', function () { - const bidRequest1 = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - getFloor: () => { - return { - currency: 'AUS', - floor: 9.99 + context('with floors module', function () { + let adServerCurrencyStub; + + beforeEach(function () { + adServerCurrencyStub = sinon + .stub(config, 'getConfig') + .withArgs('currency.adServerCurrency') + }); + + afterEach(function () { + config.getConfig.restore(); + }); + + it('should send out floors on bids', function () { + const bidRequest1 = Object.assign({}, + bidRequestsWithMediaTypes[0], + { + getFloor: () => { + return { + currency: 'AUS', + floor: 9.99 + } } } - } - ); + ); - const bidRequest2 = Object.assign({}, - bidRequestsWithMediaTypes[1], - { - getFloor: () => { - return { - currency: 'AUS', - floor: 18.881 + const bidRequest2 = Object.assign({}, + bidRequestsWithMediaTypes[1], + { + getFloor: () => { + return { + currency: 'AUS', + floor: 18.881 + } } } - } - ); + ); - const request = spec.buildRequests([bidRequest1, bidRequest2], mockBidderRequest); - const dataParams = request[0].data; + const request = spec.buildRequests([bidRequest1, bidRequest2], mockBidderRequest); + const dataParams = request[0].data; - expect(dataParams.aumfs).to.exist; - expect(dataParams.aumfs).to.equal('9990,18881'); - }); + expect(dataParams.aumfs).to.exist; + expect(dataParams.aumfs).to.equal('9990,18881'); + }); + + it('should send out floors on bids in the default currency', function () { + const bidRequest1 = Object.assign({}, + bidRequestsWithMediaTypes[0], + { + getFloor: () => { + return {}; + } + } + ); + + let getFloorSpy = sinon.spy(bidRequest1, 'getFloor'); + + spec.buildRequests([bidRequest1], mockBidderRequest); + expect(getFloorSpy.args[0][0].currency).to.equal('USD'); + }); + + it('should send out floors on bids in the ad server currency if defined', function () { + adServerCurrencyStub.returns('bitcoin'); + + const bidRequest1 = Object.assign({}, + bidRequestsWithMediaTypes[0], + { + getFloor: () => { + return {}; + } + } + ); + + let getFloorSpy = sinon.spy(bidRequest1, 'getFloor'); + + spec.buildRequests([bidRequest1], mockBidderRequest); + expect(getFloorSpy.args[0][0].currency).to.equal('bitcoin'); + }); + }) }) }); From 412b0cebe0f6b4dfbb8f5d9b91a1da9f07d172e6 Mon Sep 17 00:00:00 2001 From: mefjush Date: Thu, 6 Aug 2020 19:08:46 +0200 Subject: [PATCH 0060/1476] Switch Adhese adapter to POST method (#5574) --- modules/adheseBidAdapter.js | 29 +++++++++++------- test/spec/modules/adheseBidAdapter_spec.js | 35 +++++++++++++++++----- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index cc95806491f..cd5a44f34d8 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -20,21 +20,28 @@ export const spec = { } const { gdprConsent, refererInfo } = bidderRequest; - const account = getAccount(validBidRequests); const targets = validBidRequests.map(bid => bid.params.data).reduce(mergeTargets, {}); - const gdprParams = (gdprConsent && gdprConsent.consentString) ? [`xt${gdprConsent.consentString}`] : []; - const refererParams = (refererInfo && refererInfo.referer) ? [`xf${base64urlEncode(refererInfo.referer)}`] : []; - const id5Params = (getId5Id(validBidRequests)) ? [`x5${getId5Id(validBidRequests)}`] : []; - const targetsParams = Object.keys(targets).map(targetCode => targetCode + targets[targetCode].join(';')); - const slotsParams = validBidRequests.map(bid => 'sl' + bidToSlotName(bid)); - const params = [...slotsParams, ...targetsParams, ...gdprParams, ...refererParams, ...id5Params].map(s => `/${s}`).join(''); - const cacheBuster = '?t=' + new Date().getTime(); - const uri = 'https://ads-' + account + '.adhese.com/json' + params + cacheBuster; + const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; + const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; + const id5Params = (getId5Id(validBidRequests)) ? { x5: [getId5Id(validBidRequests)] } : {}; + const slots = validBidRequests.map(bid => ({ slotname: bidToSlotName(bid) })); + + const payload = { + slots: slots, + parameters: { ...targets, ...gdprParams, ...refererParams, ...id5Params } + } + + const account = getAccount(validBidRequests); + const uri = 'https://ads-' + account + '.adhese.com/json'; return { - method: 'GET', + method: 'POST', url: uri, - bids: validBidRequests + data: JSON.stringify(payload), + bids: validBidRequests, + options: { + contentType: 'application/json' + } }; }, diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index def504b0424..0743ddd211d 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -71,40 +71,46 @@ describe('AdheseAdapter', function () { } }; + it('should include requested slots', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(JSON.parse(req.data).slots).to.deep.include({ 'slotname': '_main_page_-leaderboard' }); + }); + it('should include all extra bid params', function () { let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }) ], bidderRequest); - expect(req.url).to.contain('/sl_main_page_-leaderboard/ag25'); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'ag': [ '25' ] }); }); it('should include duplicate bid params once', function () { let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' }) ], bidderRequest); - expect(req.url).to.contain('/sl_main_page_-leaderboard/ag25/cigent'); + expect(JSON.parse(req.data).parameters).to.deep.include({'ag': ['25']}).and.to.deep.include({ 'ci': [ 'gent' ] }); }); it('should split multiple target values', function () { let req = spec.buildRequests([ bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' }) ], bidderRequest); - expect(req.url).to.contain('/sl_main_page_-leaderboard/cilondon;gent'); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'ci': [ 'london', 'gent' ] }); }); it('should include gdpr consent param', function () { let req = spec.buildRequests([ minimalBid() ], bidderRequest); - expect(req.url).to.contain('/xtCONSENT_STRING'); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': [ 'CONSENT_STRING' ] }); }); it('should include referer param in base64url format', function () { let req = spec.buildRequests([ minimalBid() ], bidderRequest); - expect(req.url).to.contain('/xfaHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ'); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xf': [ 'aHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ' ] }); }); it('should include id5 id as /x5 param', function () { - let req = spec.buildRequests([ bidWithParams({}, {'id5id': 'ID5-1234567890'}) ], bidderRequest); + let req = spec.buildRequests([ bidWithParams({}, { 'id5id': 'ID5-1234567890' }) ], bidderRequest); - expect(req.url).to.contain('/x5ID5-1234567890'); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'x5': [ 'ID5-1234567890' ] }); }); it('should include bids', function () { @@ -113,6 +119,18 @@ describe('AdheseAdapter', function () { expect(req.bids).to.deep.equal([ bid ]); }); + + it('should make a POST request', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(req.method).to.equal('POST'); + }); + + it('should request the json endpoint', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(req.url).to.equal('https://ads-demo.adhese.com/json'); + }); }); describe('interpretResponse', () => { @@ -211,7 +229,8 @@ describe('AdheseAdapter', function () { adhese: { origin: 'RUBICON', originInstance: '', - originData: {} } + originData: {} + } }]; expect(spec.interpretResponse(sspVideoResponse, bidRequest)).to.deep.equal(expectedResponse); }); From 44e4e7a85ef0de0ba0ed225ff3eb5051c63ec9db Mon Sep 17 00:00:00 2001 From: Kanchika - Automatad Date: Thu, 6 Aug 2020 22:40:53 +0530 Subject: [PATCH 0061/1476] Automatad Bid Adapter: Add adunit code to bid request (#5567) * added automatad bid adapter * added automatad bid adapter readme * added automatad bidder adapter unit test * updated maintainer email id for automatad adapter * refactored automatadBidAdapter js * refactored automatadBidAdapter unit test * refactored automatadBidAdapter unit test * added usersync code to automatad bid adapter * Added unit test for onBidWon in automatadBidAdapter_spec * removed trailing spaces * removed trailing space * changes for getUserSync function * lint error fixes * updated usersync url * additional test for onBidWon function added * added ajax stub in test * updated winurl params * lint fixes * added adunitCode in bid request * added test for adunit code --- modules/automatadBidAdapter.js | 21 +++++++++++-------- test/spec/modules/automatadBidAdapter_spec.js | 5 +++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index 95d225cb5f7..414cadcd405 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -29,15 +29,18 @@ export const spec = { const siteId = validBidRequests[0].params.siteId const placementId = validBidRequests[0].params.placementId - const impressions = validBidRequests.map(bidRequest => ({ - id: bidRequest.bidId, - banner: { - format: bidRequest.sizes.map(sizeArr => ({ - w: sizeArr[0], - h: sizeArr[1], - })) - }, - })) + const impressions = validBidRequests.map(bidRequest => { + return { + id: bidRequest.bidId, + adUnitCode: bidRequest.adUnitCode, + banner: { + format: bidRequest.sizes.map(sizeArr => ({ + w: sizeArr[0], + h: sizeArr[1], + })) + }, + } + }) // params from bid request const openrtbRequest = { diff --git a/test/spec/modules/automatadBidAdapter_spec.js b/test/spec/modules/automatadBidAdapter_spec.js index 788fd3aefc4..a6de8810284 100644 --- a/test/spec/modules/automatadBidAdapter_spec.js +++ b/test/spec/modules/automatadBidAdapter_spec.js @@ -95,6 +95,11 @@ describe('automatadBidAdapter', function () { let r = rdata.imp[0] expect(r.siteID !== null && r.placementID !== null).to.be.true }) + + it('should include adunit code', function () { + let r = rdata.imp[0] + expect(r.adUnitCode !== null).to.be.true + }) }) describe('interpretResponse', function () { From ea45ae6f5a53935f07c76ffc506968c18011750d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Thu, 6 Aug 2020 20:40:28 +0300 Subject: [PATCH 0062/1476] Vidazoo Adapter: Feature/unique-deal-id (#5488) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(client): send unique deal id as server param * fix(client): lint errors (padded-blocks) * feat(client): move localStorage usage to storageManager * fix(client): tests Co-authored-by: roman --- modules/vidazooBidAdapter.js | 70 +++++++++--- test/spec/modules/vidazooBidAdapter_spec.js | 119 +++++++++++++++++++- 2 files changed, 171 insertions(+), 18 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 94bedaa8699..382833fbca9 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,12 +1,17 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +const GLVID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; const BIDDER_CODE = 'vidazoo'; const BIDDER_VERSION = '1.0.0'; const CURRENCY = 'USD'; const TTL_SECONDS = 60 * 5; +const DEAL_ID_EXPIRY = 1000 * 60 * 15; +const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; +const SESSION_ID_KEY = 'vidSid'; const INTERNAL_SYNC_TYPE = { IFRAME: 'iframe', IMAGE: 'img' @@ -27,6 +32,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'pubcid': 1, 'tdid': 1, }; +const storage = getStorageManager(GLVID); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; @@ -54,6 +60,8 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { const { bidFloor, ext } = params; const hashUrl = hashCode(topWindowUrl); const dealId = getNextDealId(hashUrl); + const uniqueDealId = getUniqueDealId(hashUrl); + const sId = getVidazooSessionId(); const cId = extractCID(params); const pId = extractPID(params); const subDomain = extractSubDomain(params); @@ -65,8 +73,10 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { bidId: bidId, adUnitCode: adUnitCode, publisherId: pId, + sessionId: sId, sizes: sizes, dealId: dealId, + uniqueDealId: uniqueDealId, bidderVersion: BIDDER_VERSION, prebidVersion: '$prebid.version$', res: `${screen.width}x${screen.height}` @@ -201,7 +211,7 @@ function getUserSyncs(syncOptions, responses) { return []; } -function hashCode(s, prefix = '_') { +export function hashCode(s, prefix = '_') { const l = s.length; let h = 0 let i = 0; @@ -211,35 +221,69 @@ function hashCode(s, prefix = '_') { return prefix + h; } -function getNextDealId(key) { +export function getNextDealId(key, expiry = DEAL_ID_EXPIRY) { try { - const currentValue = Number(getStorageItem(key) || 0); + const data = getStorageItem(key); + let currentValue = 0; + let timestamp; + + if (data && data.value && Date.now() - data.created < expiry) { + currentValue = data.value; + timestamp = data.created; + } + const nextValue = currentValue + 1; - setStorageItem(key, nextValue); + setStorageItem(key, nextValue, timestamp); return nextValue; } catch (e) { return 0; } } -function getStorage() { - return window['sessionStorage']; +export function getUniqueDealId(key, expiry = UNIQUE_DEAL_ID_EXPIRY) { + const storageKey = `u_${key}`; + const now = Date.now(); + const data = getStorageItem(storageKey); + let uniqueId; + + if (!data || !data.value || now - data.created > expiry) { + uniqueId = `${key}_${now.toString()}`; + setStorageItem(storageKey, uniqueId); + } else { + uniqueId = data.value; + } + + return uniqueId; +} + +export function getVidazooSessionId() { + return getStorageItem(SESSION_ID_KEY) || ''; } -function getStorageItem(key) { +export function getStorageItem(key) { try { - return getStorage().getItem(key); - } catch (e) { - return null; - } + return tryParseJSON(storage.getDataFromLocalStorage(key)); + } catch (e) { } + + return null; } -function setStorageItem(key, value) { +export function setStorageItem(key, value, timestamp) { try { - getStorage().setItem(key, String(value)); + const created = timestamp || Date.now(); + const data = JSON.stringify({ value, created }); + storage.setDataInLocalStorage(key, data); } catch (e) { } } +export function tryParseJSON(value) { + try { + return JSON.parse(value); + } catch (e) { + return value; + } +} + export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index affcc08bd10..57e7f4e8ae2 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,7 +1,22 @@ import { expect } from 'chai'; -import { spec as adapter, SUPPORTED_ID_SYSTEMS, createDomain, extractPID, extractCID, extractSubDomain } from 'modules/vidazooBidAdapter.js'; +import { + spec as adapter, + SUPPORTED_ID_SYSTEMS, + createDomain, + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, + getNextDealId, + getVidazooSessionId, +} from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; const SUB_DOMAIN = 'openrtb'; @@ -64,10 +79,6 @@ const REQUEST = { } }; -const SYNC_OPTIONS = { - 'pixelEnabled': true -}; - describe('VidazooBidAdapter', function () { describe('validtae spec', function () { it('exists and is a function', function () { @@ -129,6 +140,7 @@ describe('VidazooBidAdapter', function () { }); it('should build request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.referer); const requests = adapter.buildRequests([BID], BIDDER_REQUEST); expect(requests).to.have.length(1); expect(requests[0]).to.deep.equal({ @@ -146,6 +158,8 @@ describe('VidazooBidAdapter', function () { adUnitCode: 'div-gpt-ad-12345-0', publisherId: '59ac17c192832d0011283fe3', dealId: 1, + sessionId: '', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, bidderVersion: adapter.version, prebidVersion: version, res: `${window.top.screen.width}x${window.top.screen.height}`, @@ -264,4 +278,99 @@ describe('VidazooBidAdapter', function () { expect(subDomain).to.be.equal('prebid'); }); }); + + describe('vidazoo session id', function () { + it('should get undefined vidazoo session id', function () { + const sessionId = getVidazooSessionId(); + expect(sessionId).to.be.empty; + }); + + it('should get vidazoo session id from storage', function () { + const vidSid = '1234-5678'; + window.localStorage.setItem('vidSid', vidSid); + const sessionId = getVidazooSessionId(); + expect(sessionId).to.be.equal(vidSid); + }); + }); + + describe('deal id', function () { + const key = 'myDealKey'; + + it('should get the next deal id', function () { + const dealId = getNextDealId(key); + const nextDealId = getNextDealId(key); + expect(dealId).to.be.equal(1); + expect(nextDealId).to.be.equal(2); + }); + + it('should get the first deal id on expiration', function (done) { + setTimeout(function () { + const dealId = getNextDealId(key, 100); + expect(dealId).to.be.equal(1); + done(); + }, 200); + }); + }); + + describe('unique deal id', function () { + const key = 'myKey'; + let uniqueDealId; + + it('should get fresh unique deal id', function () { + const now = Date.now(); + uniqueDealId = getUniqueDealId(key); + expect(uniqueDealId).to.be.equal(`${key}_${now.toString()}`); + }); + + 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 () { + const current = getUniqueDealId(key, 100); + expect(current).to.not.be.equal(uniqueDealId); + }); + }); + + describe('storage utils', function () { + 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 f95e26798c950e5c2410363eff03c5507f675c11 Mon Sep 17 00:00:00 2001 From: Kylian Deau Date: Fri, 7 Aug 2020 16:31:23 +0200 Subject: [PATCH 0063/1476] Teads adapter: support time to first byte (#5575) --- modules/teadsBidAdapter.js | 30 +++++++++++++++++++++++ test/spec/modules/teadsBidAdapter_spec.js | 28 +++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index dbeed4aceae..08ae1854669 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -43,6 +43,7 @@ export const spec = { referrer: getReferrerInfo(bidderRequest), pageReferrer: document.referrer, networkBandwidth: getConnectionDownLink(window.navigator), + timeToFirstByte: getTimeToFirstByte(window), data: bids, deviceWidth: screen.width, hb_version: '$prebid.version$' @@ -123,6 +124,35 @@ function getConnectionDownLink(nav) { return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : ''; } +function getTimeToFirstByte(win) { + const performance = win.performance || win.webkitPerformance || win.msPerformance || win.mozPerformance; + + const ttfbWithTimingV2 = performance && + typeof performance.getEntriesByType === 'function' && + Object.prototype.toString.call(performance.getEntriesByType) === '[object Function]' && + performance.getEntriesByType('navigation')[0] && + performance.getEntriesByType('navigation')[0].responseStart && + performance.getEntriesByType('navigation')[0].requestStart && + performance.getEntriesByType('navigation')[0].responseStart >= 0 && + performance.getEntriesByType('navigation')[0].requestStart >= 0 && + Math.round( + performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart + ); + + if (ttfbWithTimingV2) { + return ttfbWithTimingV2.toString(); + } + + const ttfbWithTimingV1 = performance && + performance.timing.responseStart && + performance.timing.requestStart && + performance.timing.responseStart >= 0 && + performance.timing.requestStart >= 0 && + performance.timing.responseStart - performance.timing.requestStart; + + return ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : ''; +} + function findGdprStatus(gdprApplies, gdprData, apiVersion) { let status = gdprStatus.GDPR_APPLIES_PUBLISHER if (gdprApplies) { diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index cc5bbb840a9..17841b271d4 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -189,6 +189,34 @@ describe('teadsBidAdapter', () => { expect(payload.pageReferrer).to.deep.equal(document.referrer); }); + it('should add timeToFirstByte info to payload', function () { + const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const payload = JSON.parse(request.data); + const performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance; + + const ttfbExpectedV2 = performance && + typeof performance.getEntriesByType === 'function' && + Object.prototype.toString.call(performance.getEntriesByType) === '[object Function]' && + performance.getEntriesByType('navigation')[0] && + performance.getEntriesByType('navigation')[0].responseStart && + performance.getEntriesByType('navigation')[0].requestStart && + performance.getEntriesByType('navigation')[0].responseStart >= 0 && + performance.getEntriesByType('navigation')[0].requestStart >= 0 && + Math.round( + performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart + ); + + expect(payload.timeToFirstByte).to.exist; + + if (ttfbExpectedV2) { + expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV2.toString()); + } else { + const ttfbExpectedV1 = performance.timing.responseStart - performance.timing.requestStart; + + expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV1.toString()); + } + }); + it('should send GDPR to endpoint with 11 status', function() { let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; let bidderRequest = { From 46eb769268f18903e3159929deb81fb8d5a103b2 Mon Sep 17 00:00:00 2001 From: Adprime <64427228+Adprime@users.noreply.github.com> Date: Sat, 8 Aug 2020 00:16:27 +0300 Subject: [PATCH 0064/1476] Support IDL solution (#5579) * initial * fix * remove redundant language mod, use player sizes in video traff * test modify * fix * Adding Tests * add keywords param * log * log * log * fix * add idl * add idl * fix test * lint * lint * fix * lint * lint * lint * lint Co-authored-by: Aigolkin1991 --- modules/adprimeBidAdapter.js | 7 ++++++- test/spec/modules/adprimeBidAdapter_spec.js | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js index 306ab76f512..50303b82979 100644 --- a/modules/adprimeBidAdapter.js +++ b/modules/adprimeBidAdapter.js @@ -63,6 +63,7 @@ export const spec = { for (let i = 0; i < len; i++) { let bid = validBidRequests[i]; let sizes + let identeties = {} if (bid.mediaTypes) { if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { sizes = bid.mediaTypes[BANNER].sizes @@ -70,6 +71,9 @@ export const spec = { sizes = bid.mediaTypes[VIDEO].playerSize } } + if (bid.userId && bid.userId.idl_env) { + identeties.identityLink = bid.userId.idl_env + } placements.push({ placementId: bid.params.placementId, @@ -79,7 +83,8 @@ export const spec = { hPlayer: sizes ? sizes[1] : 0, traffic: bid.params.traffic || BANNER, schain: bid.schain || {}, - keywords: bid.params.keywords || [] + keywords: bid.params.keywords || [], + identeties }); } return { diff --git a/test/spec/modules/adprimeBidAdapter_spec.js b/test/spec/modules/adprimeBidAdapter_spec.js index 3508a1175a6..fe05634baae 100644 --- a/test/spec/modules/adprimeBidAdapter_spec.js +++ b/test/spec/modules/adprimeBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('AdprimebBidAdapter', 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', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'keywords'); + expect(placement).to.have.keys('placementId', 'bidId', 'identeties', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'keywords'); expect(placement.placementId).to.equal(0); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); @@ -106,6 +106,23 @@ describe('AdprimebBidAdapter', function () { expect(data.placements).to.be.an('array').that.is.empty; }); }); + describe('buildRequests with user ids', function () { + bid.userId = {} + bid.userId.idl_env = 'idl_env123'; + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Return bids with user identeties', function () { + let data = serverRequest.data; + let placements = data['placements']; + expect(data).to.be.an('object'); + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.property('identeties') + expect(placement.identeties).to.be.an('object') + expect(placement.identeties).to.have.property('identityLink') + expect(placement.identeties.identityLink).to.be.equal('idl_env123') + } + }); + }); describe('interpretResponse', function () { it('Should interpret banner response', function () { const banner = { From 54ee857ef840ca9d9c74edb7df90f69e46c7c786 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Sat, 8 Aug 2020 02:12:19 +0300 Subject: [PATCH 0065/1476] Adkernel: schain supported (#5558) --- modules/adkernelBidAdapter.js | 11 ++++++++--- test/spec/modules/adkernelBidAdapter_spec.js | 11 ++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index d069af7d56e..417c665627c 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -78,10 +78,11 @@ export const spec = { */ buildRequests: function (bidRequests, bidderRequest) { let impDispatch = dispatchImps(bidRequests, bidderRequest.refererInfo); - const requests = []; + let requests = []; + let schain = bidRequests[0].schain; Object.keys(impDispatch).forEach(host => { Object.keys(impDispatch[host]).forEach(zoneId => { - const request = buildRtbRequest(impDispatch[host][zoneId], bidderRequest); + const request = buildRtbRequest(impDispatch[host][zoneId], bidderRequest, schain); requests.push({ method: 'POST', url: `https://${host}/hb?zone=${zoneId}&v=${VERSION}`, @@ -293,9 +294,10 @@ function getAllowedSyncMethod(bidderCode) { * Builds complete rtb request * @param imps {Object} Collection of rtb impressions * @param bidderRequest {BidderRequest} + * @param schain {Object=} Supply chain config * @return {Object} Complete rtb request */ -function buildRtbRequest(imps, bidderRequest) { +function buildRtbRequest(imps, bidderRequest, schain) { let {bidderCode, gdprConsent, auctionId, refererInfo, timeout, uspConsent} = bidderRequest; let req = { @@ -329,6 +331,9 @@ function buildRtbRequest(imps, bidderRequest) { if (syncMethod) { utils.deepSetValue(req, 'ext.adk_usersync', syncMethod); } + if (schain) { + utils.deepSetValue(req, 'source.ext.schain', schain); + } return req; } diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 71637781f24..229d464a030 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -229,13 +229,22 @@ describe('Adkernel adapter', function () { cur: 'USD' }; + var sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + function buildBidderRequest(url = 'https://example.com/index.html', params = {}) { return Object.assign({}, params, {refererInfo: {referer: url, reachedTop: true}, timeout: 3000, bidderCode: 'adkernel'}); } const DEFAULT_BIDDER_REQUEST = buildBidderRequest(); function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST, dnt = true) { - let dntmock = sinon.stub(utils, 'getDNT').callsFake(() => dnt); + let dntmock = sandbox.stub(utils, 'getDNT').callsFake(() => dnt); let pbRequests = spec.buildRequests(bidRequests, bidderRequest); dntmock.restore(); let rtbRequests = pbRequests.map(r => JSON.parse(r.data)); From 96af0983e9c91d07276ca5eefe4b11b254d57b0b Mon Sep 17 00:00:00 2001 From: Latyshev Dmitry Date: Sat, 8 Aug 2020 02:25:08 +0300 Subject: [PATCH 0066/1476] Add RtbSape adapter (#5520) * Add RtbSape adapter * Add RtbSape adapter (fix useless conditional) Co-authored-by: Dmitry Latyshev --- modules/rtbsapeBidAdapter.js | 142 +++++++++++++++++ modules/rtbsapeBidAdapter.md | 51 ++++++ test/spec/modules/rtbsapeBidAdapter_spec.js | 166 ++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 modules/rtbsapeBidAdapter.js create mode 100644 modules/rtbsapeBidAdapter.md create mode 100644 test/spec/modules/rtbsapeBidAdapter_spec.js diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js new file mode 100644 index 00000000000..8473ef4dbb3 --- /dev/null +++ b/modules/rtbsapeBidAdapter.js @@ -0,0 +1,142 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {OUTSTREAM} from '../src/video.js'; +import {Renderer} from '../src/Renderer.js'; +import {triggerPixel} from '../src/utils.js'; + +const BIDDER_CODE = 'rtbsape'; +const ENDPOINT = 'https://ssp-rtb.sape.ru/prebid'; +const RENDERER_SRC = 'https://cdn-rtb.sape.ru/js/player.js'; +const MATCH_SRC = 'https://www.acint.net/mc/?dp=141'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['sape'], + 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 && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.video) && bid.params && bid.params.placeId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let tz = (new Date()).getTimezoneOffset() + let padInt = (v) => (v < 10 ? '0' + v : '' + v); + + return { + url: ENDPOINT, + method: 'POST', + data: { + auctionId: bidderRequest.auctionId, + requestId: bidderRequest.bidderRequestId, + bids: validBidRequests, + timezone: (tz > 0 ? '-' : '+') + padInt(Math.floor(Math.abs(tz) / 60)) + ':' + padInt(Math.abs(tz) % 60), + refererInfo: bidderRequest.refererInfo + }, + } + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {{data: {bids: [{mediaTypes: {banner: boolean}}]}}} bidRequest Info describing the request to the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + if (!(serverResponse.body && Array.isArray(serverResponse.body.bids))) { + return []; + } + + let bids = {}; + bidRequest.data.bids.forEach(bid => bids[bid.bidId] = bid); + + return serverResponse.body.bids.map(bid => { + let requestBid = bids[bid.requestId]; + let context = utils.deepAccess(requestBid, 'mediaTypes.video.context'); + + if (context === OUTSTREAM && (bid.vastUrl || bid.vastXml)) { + let renderer = Renderer.install({ + id: bid.requestId, + url: RENDERER_SRC, + loaded: false + }); + + let muted = utils.deepAccess(requestBid, 'params.video.playerMuted'); + if (typeof muted === 'undefined') { + muted = true; + } + + bid.playerMuted = muted; + bid.renderer = renderer + + renderer.setRender(setOutstreamRenderer); + } + + return bid; + }); + }, + + /** + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function (syncOptions) { + const sync = []; + if (syncOptions.iframeEnabled) { + sync.push({ + type: 'iframe', + url: MATCH_SRC + }); + } + return sync; + }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} bid The bid that won the auction + */ + onBidWon: function(bid) { + if (bid.nurl) { + triggerPixel(bid.nurl); + } + } +} + +/** + * Initialize RtbSape outstream player + * + * @param bid + */ +function setOutstreamRenderer(bid) { + let props = {}; + if (bid.vastUrl) { + props.url = bid.vastUrl; + } + if (bid.vastXml) { + props.xml = bid.vastXml; + } + bid.renderer.push(() => { + let player = window.sapeRtbPlayerHandler(bid.adUnitCode, bid.width, bid.height, bid.playerMuted, {singleton: true}); + props.onComplete = () => player.destroy(); + props.onError = () => player.destroy(); + player.addSlot(props); + }); +} + +registerBidder(spec); diff --git a/modules/rtbsapeBidAdapter.md b/modules/rtbsapeBidAdapter.md new file mode 100644 index 00000000000..6b1afe3867d --- /dev/null +++ b/modules/rtbsapeBidAdapter.md @@ -0,0 +1,51 @@ +# Overview + +``` +Module Name: RtbSape Bid Adapter +Module Type: Bidder Adapter +Maintainer: sergey@sape.ru +``` + +# Description +Our module makes it easy to integrate RtbSape demand sources into your website. + +Supported Ad format: +* Banner +* Video (instream and outstream) + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'rtbsape', + params: { + placeId: 553307 + } + }] + }, + // Video adUnit + { + code: 'video-div', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [600, 340] + } + }, + bids: [{ + bidder: 'rtbsape', + params: { + placeId: 553309 + } + }] + } +]; +``` diff --git a/test/spec/modules/rtbsapeBidAdapter_spec.js b/test/spec/modules/rtbsapeBidAdapter_spec.js new file mode 100644 index 00000000000..4c3814385b3 --- /dev/null +++ b/test/spec/modules/rtbsapeBidAdapter_spec.js @@ -0,0 +1,166 @@ +import {expect} from 'chai'; +import {spec} from 'modules/rtbsapeBidAdapter.js'; +import * as utils from 'src/utils.js'; +import {executeRenderer, Renderer} from 'src/Renderer.js'; + +describe('rtbsapeBidAdapterTests', function () { + describe('isBidRequestValid', function () { + it('valid', function () { + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {placeId: 4321}})).to.equal(true); + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {video: true}, params: {placeId: 4321}})).to.equal(true); + }); + + it('invalid', function () { + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {}})).to.equal(false); + expect(spec.isBidRequestValid({bidder: 'rtbsape', params: {placeId: 4321}})).to.equal(false); + }); + }); + + it('buildRequests', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'rtbsape', + params: {placeId: 4321}, + sizes: [[240, 400]] + }]; + let bidderRequest = { + auctionId: '2e208334-cafe-4c2c-b06b-f055ff876852', + bidderRequestId: '1392d0aa613366', + refererInfo: {} + }; + let request = spec.buildRequests(bidRequestData, bidderRequest); + expect(request.data.auctionId).to.equal('2e208334-cafe-4c2c-b06b-f055ff876852'); + expect(request.data.requestId).to.equal('1392d0aa613366'); + expect(request.data.bids[0].bidId).to.equal('bid1234'); + expect(request.data.timezone).to.not.equal(undefined); + }); + + describe('interpretResponse', function () { + it('banner', function () { + let serverResponse = { + body: { + bids: [{ + requestId: 'bid1234', + cpm: 2.21, + currency: 'RUB', + width: 240, + height: 400, + netRevenue: true, + ad: 'Ad html' + }] + } + }; + let bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(2.21); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid1234'); + expect(bid.ad).to.equal('Ad html'); + }); + + describe('video (outstream)', function () { + let bid; + + before(() => { + let serverResponse = { + body: { + bids: [{ + requestId: 'bid1234', + adUnitCode: 'ad-bid1234', + cpm: 3.32, + currency: 'RUB', + width: 600, + height: 340, + netRevenue: true, + vastUrl: 'https://cdn-rtb.sape.ru/vast/4321.xml', + meta: { + mediaType: 'video' + } + }] + } + }; + let serverRequest = { + data: { + bids: [{ + bidId: 'bid1234', + adUnitCode: 'ad-bid1234', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: { + placeId: 4321, + video: { + playerMuted: false + } + } + }] + } + }; + let bids = spec.interpretResponse(serverResponse, serverRequest); + expect(bids).to.have.lengthOf(1); + bid = bids[0]; + }); + + it('should add renderer', () => { + expect(bid).to.have.own.property('renderer'); + expect(bid.renderer).to.be.instanceof(Renderer); + expect(bid.renderer.url).to.equal('https://cdn-rtb.sape.ru/js/player.js'); + expect(bid.playerMuted).to.equal(false); + }); + + it('should create player instance', () => { + let spy = false; + + window.sapeRtbPlayerHandler = function (id, w, h, m) { + const player = {addSlot: () => [id, w, h, m]} + expect(spy).to.equal(false); + spy = sinon.spy(player, 'addSlot'); + return player; + }; + + executeRenderer(bid.renderer, bid); + expect(spy).to.not.equal(false); + expect(spy.called).to.be.true; + + const spyCall = spy.getCall(0); + expect(spyCall.args[0].url).to.be.equal('https://cdn-rtb.sape.ru/vast/4321.xml'); + expect(spyCall.returnValue[0]).to.be.equal('ad-bid1234'); + expect(spyCall.returnValue[1]).to.be.equal(600); + expect(spyCall.returnValue[2]).to.be.equal(340); + expect(spyCall.returnValue[3]).to.be.equal(false); + }); + }); + }); + + it('getUserSyncs', function () { + const syncs = spec.getUserSyncs({iframeEnabled: true}); + expect(syncs).to.be.an('array').that.to.have.lengthOf(1); + expect(syncs[0]).to.deep.equal({type: 'iframe', url: 'https://www.acint.net/mc/?dp=141'}); + }); + + describe('onBidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('called once', function () { + spec.onBidWon({cpm: '2.21', nurl: 'https://ssp-rtb.sape.ru/track?event=win'}); + expect(utils.triggerPixel.calledOnce).to.equal(true); + }); + + it('called false', function () { + spec.onBidWon({cpm: '2.21'}); + expect(utils.triggerPixel.called).to.equal(false); + }); + }); +}); From 7d24bb841dd4ef9a445dcdf94a923ba2ac2d290f Mon Sep 17 00:00:00 2001 From: hendrikiseke1979 <53309111+hendrikiseke1979@users.noreply.github.com> Date: Mon, 10 Aug 2020 02:11:16 +0200 Subject: [PATCH 0067/1476] Orbidder: fix getting the end point url for developoment and integration tests from local storage (#5463) * orbidder adapter: add withCredentials:true header to BidRequest and onBidWon Requests * add blank in order to trigger build again * remove blank to trigger build ... again * adding extra line to trigger build ... again * add prebid version to request * add unit test for version parameter * add version parameter to win requests * fix comment * trigger rebuild * trigger rebuild * remove onBidWon callback from adapter * fix retrieving orbidder endpoint url from local storage * fix unit tests Co-authored-by: Volk, Rainer Co-authored-by: RainerVolk4014 <53347752+RainerVolk4014@users.noreply.github.com> Co-authored-by: siggi-otto <57615762+siggi-otto@users.noreply.github.com> Co-authored-by: Hendrik Iseke <39734979+hiseke@users.noreply.github.com> Co-authored-by: Hendrik Iseke Co-authored-by: rvolk <> --- modules/orbidderBidAdapter.js | 15 +++++++++------ test/spec/modules/orbidderBidAdapter_spec.js | 4 +--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index d14e2bebd72..e01746af487 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,18 +1,20 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); +const storageManager = getStorageManager(); export const spec = { code: 'orbidder', - orbidderHost: (() => { - let ret = 'https://orbidder.otto.de'; + hostname: 'https://orbidder.otto.de', + + getHostname() { + let ret = this.hostname; try { - ret = storage.getDataFromLocalStorage('ov_orbidder_host') || ret; + ret = storageManager.getDataFromLocalStorage('ov_orbidder_host') || ret; } catch (e) { } return ret; - })(), + }, isBidRequestValid(bid) { return !!(bid.sizes && bid.bidId && bid.params && @@ -23,6 +25,7 @@ export const spec = { }, buildRequests(validBidRequests, bidderRequest) { + const hostname = this.getHostname(); return validBidRequests.map((bidRequest) => { let referer = ''; if (bidderRequest && bidderRequest.refererInfo) { @@ -30,7 +33,7 @@ export const spec = { } const ret = { - url: `${spec.orbidderHost}/bid`, + url: `${hostname}/bid`, method: 'POST', options: { withCredentials: true }, data: { diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index eec6ccd19f6..df551311c0b 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -1,8 +1,6 @@ import {expect} from 'chai'; import {spec} from 'modules/orbidderBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; -import openxAdapter from '../../../modules/openxAnalyticsAdapter.js'; -import {detectReferer} from 'src/refererDetection.js'; describe('orbidderBidAdapter', () => { const adapter = newBidder(spec); @@ -93,7 +91,7 @@ describe('orbidderBidAdapter', () => { it('sends bid request to endpoint via https using post', () => { expect(request.method).to.equal('POST'); expect(request.url.indexOf('https://')).to.equal(0); - expect(request.url).to.equal(`${spec.orbidderHost}/bid`); + expect(request.url).to.equal(`${spec.hostname}/bid`); }); it('contains prebid version parameter', () => { From fa3378c4ea7e89ded392593299f62dca4b700560 Mon Sep 17 00:00:00 2001 From: Dmitriy Labuzov Date: Mon, 10 Aug 2020 03:26:51 +0300 Subject: [PATCH 0068/1476] Yieldmo adapter: environment parameter removed to fix bug #4107 & optimize #5098, minor refactoring (#5544) Co-authored-by: Dmitriy Labuzov --- modules/yieldmoBidAdapter.js | 190 ++------------------ test/spec/modules/yieldmoBidAdapter_spec.js | 3 +- 2 files changed, 13 insertions(+), 180 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 55495979ea4..a7befecadc7 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -16,7 +16,7 @@ export const spec = { * @param {object} bid, bid to validate * @return boolean, true if valid, otherwise false */ - isBidRequestValid: function(bid) { + isBidRequestValid: function (bid) { return !!(bid && bid.adUnitCode && bid.bidId); }, /** @@ -25,7 +25,7 @@ export const spec = { * @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(bidRequests, bidderRequest) { + buildRequests: function (bidRequests, bidderRequest) { let serverRequest = { p: [], page_url: bidderRequest.refererInfo.referer, @@ -33,27 +33,16 @@ export const spec = { pr: bidderRequest.refererInfo.referer, scrd: localWindow.devicePixelRatio || 0, dnt: getDNT(), - e: getEnvironment(), description: getPageDescription(), title: localWindow.document.title || '', w: localWindow.innerWidth, h: localWindow.innerHeight, - userConsent: - JSON.stringify({ - // case of undefined, stringify will remove param - gdprApplies: - bidderRequest && bidderRequest.gdprConsent - ? bidderRequest.gdprConsent.gdprApplies - : '', - cmp: - bidderRequest && bidderRequest.gdprConsent - ? bidderRequest.gdprConsent.consentString - : '', - }), - us_privacy: - bidderRequest && bidderRequest.uspConsent - ? bidderRequest.uspConsent - : '', + userConsent: JSON.stringify({ + // case of undefined, stringify will remove param + gdprApplies: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', + cmp: utils.deepAccess(bidderRequest, 'gdprConsent.consentString') || '' + }), + us_privacy: utils.deepAccess(bidderRequest, 'uspConsent') || '' }; bidRequests.forEach(request => { @@ -91,7 +80,7 @@ export const spec = { * @param serverResponse successful response from Ad Server * @return {Bid[]} an array of bids */ - interpretResponse: function(serverResponse) { + interpretResponse: function (serverResponse) { let bids = []; let data = serverResponse.body; if (data.length > 0) { @@ -103,7 +92,7 @@ export const spec = { } return bids; }, - getUserSyncs: function() { + getUserSyncs: function () { return []; } }; @@ -175,154 +164,6 @@ function getPageDescription() { } } -/*************************************** - * Detect Environment Helper Functions - ***************************************/ - -/** - * Represents a method for loading Yieldmo ads. Environments affect - * which formats can be loaded into the page - * Environments: - * CodeOnPage: 0, // div directly on publisher's page - * Amp: 1, // google Accelerate Mobile Pages ampproject.org - * Mraid = 2, // native loaded through the MRAID spec, without Yieldmo's SDK https://www.iab.net/media/file/IAB_MRAID_v2_FINAL.pdf - * Dfp: 4, // google doubleclick for publishers https://www.doubleclickbygoogle.com/ - * DfpInAmp: 5, // AMP page containing a DFP iframe - * SafeFrame: 10, - * DfpSafeFrame: 11,Sandboxed: 16, // An iframe that can't get to the top window. - * SuperSandboxed: 89, // An iframe without allow-same-origin - * Unknown: 90, // A default sandboxed implementation delivered by EnvironmentDispatch when all positive environment checks fail - */ - -/** - * Detects what environment we're in - * @returns Environment kind - */ -function getEnvironment() { - if (isSuperSandboxedIframe()) { - return 89; - } else if (isDfpInAmp()) { - return 5; - } else if (isDfp()) { - return 4; - } else if (isAmp()) { - return 1; - } else if (isDFPSafeFrame()) { - return 11; - } else if (isSafeFrame()) { - return 10; - } else if (isMraid()) { - return 2; - } else if (isCodeOnPage()) { - return 0; - } else if (isSandboxedIframe()) { - return 16; - } else { - return 90; - } -} - -/** - * @returns true if we are running on the top window at dispatch time - */ -function isCodeOnPage() { - return window === window.parent; -} - -/** - * @returns true if the environment is both DFP and AMP - */ -function isDfpInAmp() { - return isDfp() && isAmp(); -} - -/** - * @returns true if the window is in an iframe whose id and parent element id match DFP - */ -function isDfp() { - try { - const frameElement = window.frameElement; - const parentElement = window.frameElement.parentNode; - if (frameElement && parentElement) { - return ( - frameElement.id.indexOf('google_ads_iframe') > -1 && - parentElement.id.indexOf('google_ads_iframe') > -1 - ); - } - return false; - } catch (e) { - return false; - } -} - -/** - * @returns true if there is an AMP context object - */ -function isAmp() { - try { - const ampContext = window.context || window.parent.context; - if (ampContext && ampContext.pageViewId) { - return ampContext; - } - return false; - } catch (e) { - return false; - } -} - -/** - * @returns true if the environment is a SafeFrame. - */ -function isSafeFrame() { - return window.$sf && window.$sf.ext; -} - -/** - * @returns true if the environment is a dfp safe frame. - */ -function isDFPSafeFrame() { - if (window.location && window.location.href) { - const href = window.location.href; - return ( - isSafeFrame() && - href.indexOf('google') !== -1 && - href.indexOf('safeframe') !== -1 - ); - } - return false; -} - -/** - * Return true if we are in an iframe and can't access the top window. - */ -function isSandboxedIframe() { - return window.top !== window && !window.frameElement; -} - -/** - * Return true if we cannot document.write to a child iframe (this implies no allow-same-origin) - */ -function isSuperSandboxedIframe() { - const sacrificialIframe = window.document.createElement('iframe'); - try { - sacrificialIframe.setAttribute('style', 'display:none'); - window.document.body.appendChild(sacrificialIframe); - sacrificialIframe.contentWindow._testVar = true; - window.document.body.removeChild(sacrificialIframe); - return false; - } catch (e) { - window.document.body.removeChild(sacrificialIframe); - return true; - } -} - -/** - * @returns true if the window has the attribute identifying MRAID - */ -function isMraid() { - return !!window.mraid; -} - /** * Gets an id from the userId object if it exists * @param {*} request @@ -330,14 +171,5 @@ function isMraid() { * @returns an id if there is one, or undefined */ function getId(request, idType) { - let id; - if ( - request && - request.userId && - request.userId[idType] && - typeof request.userId === 'object' - ) { - id = request.userId[idType]; - } - return id; + return (typeof utils.deepAccess(request, 'userId') === 'object') ? request.userId[idType] : undefined; } diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 1c4c4812680..caeb26266fe 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -140,12 +140,13 @@ describe('YieldmoAdapter', function () { expect(data.hasOwnProperty('pr')).to.be.true; expect(data.hasOwnProperty('scrd')).to.be.true; expect(data.dnt).to.be.false; - expect(data.e).to.equal(90); expect(data.hasOwnProperty('description')).to.be.true; expect(data.hasOwnProperty('title')).to.be.true; expect(data.hasOwnProperty('h')).to.be.true; expect(data.hasOwnProperty('w')).to.be.true; expect(data.hasOwnProperty('pubcid')).to.be.true; + expect(data.userConsent).to.equal('{"gdprApplies":"","cmp":""}'); + expect(data.us_privacy).to.equal(''); }); it('should add pubcid as parameter of request', function () { From cc4728353e1dc8092dd3c2a0ef24055e1b4e2e9a Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 10 Aug 2020 10:48:17 -0700 Subject: [PATCH 0069/1476] Fix for 5588; (#5591) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * fixing instream adUnit.size issue; sz param was empty in DFP URL --- modules/dfpAdServerVideo.js | 2 +- test/fixtures/video/adUnit.json | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 71a8471d554..1d999fdbacc 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -82,7 +82,7 @@ export function buildDfpVideoUrl(options) { const derivedParams = { correlator: Date.now(), - sz: parseSizesInput(adUnit.sizes).join('|'), + sz: parseSizesInput(deepAccess(adUnit, 'mediaTypes.video.playerSize')).join('|'), url: encodeURIComponent(location.href), }; const encodedCustomParams = getCustParams(bid, options); diff --git a/test/fixtures/video/adUnit.json b/test/fixtures/video/adUnit.json index df55eb25d79..0773d7f3a62 100644 --- a/test/fixtures/video/adUnit.json +++ b/test/fixtures/video/adUnit.json @@ -1,7 +1,11 @@ { "code": "video1", - "sizes": [640,480], - "mediaType": "video", + "mediaTypes": { + "video": { + "context": "instream", + "playerSize": [640, 480] + } + }, "bids": [ { "bidder": "appnexus", From 70ab95002a06109ddfd9be8762f37b1ffde35b46 Mon Sep 17 00:00:00 2001 From: terryc33x <64039851+terryc33x@users.noreply.github.com> Date: Mon, 10 Aug 2020 16:24:16 -0400 Subject: [PATCH 0070/1476] 33Across: Updating the endpoint url (#5586) * check gdpr in buildRequest * User sync based on whether gdpr applies or not * check if consent data exists during user sync * split user sync into further branches: 1) when gdpr does not apply 2) when consent data is unavailable * contribute viewability to ttxRequest * update tests * remove window mock from tests * use local variables * introduce ServerRequestBuilder * add withOptions() method to ServerRequestBuilder * add semicolons * sync up package-lock.json with upstream/master * stub window.top in tests * introduce getTopWindowSize() for test purpose * reformat code * add withSite() method to TtxRequestBuilder add withSite() method to TtxRequestBuilder * add isIframe() and _isViewabilityMeasurable() * handle NON_MEASURABLE viewability in nested iframes * consider page visibility, stub utils functions getWindowTop() and getWindowSelf() * contribute viewability as 0 for inactive tab * add prebidjs version to ttx request * send caller as an array * send viewability as non measurable when unable to locate target HTMLElement, add warning message * fix JSDoc in utils.js * introduce mapAdSlotPathToElementId() * introduce getAdSlotHTMLElement(), add logging * introduce mapAdSlotPathToElementId() * update logging in ad unit path to element id mapping * rephrase logging, fix tests * update adapter documentation * remove excessive logging * improve logging * revert change * fix return of _mapAdUnitPathToElementId() * improve logging of _mapAdUnitPathToElementId() * do not use Array.find() * return id once element is found * return id once element is found * let -> const * Removing killswitch behavior for GDPR * Updated comments to reflect current gdpr logic * URI encode consent string * Updated example site ID to help Prebid team e2e test our adapter * send page url in ortb * Removed redundant pageUrl default * Restored package-log.json that mirrors prebid's repo * Sending USP string during buildRequest * Adding USP consent data to user sync * add unit test for syncing without bidrequest * Changed to uspConsent to make the connatation consistent * Resetting adapter state in adapter after user sync rather than exposing it. * removed console log * Adding schain info * remove setting empty format ext * better tests invalid values * removing validation of schain * Fixed lint errors * First cut for bidfloors support * fixed where getFloors is read * fixed merge conflicts * support the guid in the api endpoint Co-authored-by: Aparna Hegde Co-authored-by: Aparna Rao-Hegde Co-authored-by: Gleb Glushtsov Co-authored-by: Gleb Glushtsov Co-authored-by: Gleb Glushtsov Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde --- modules/33acrossBidAdapter.js | 2 +- test/spec/modules/33acrossBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 5df2af7f32e..798b6450946 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -167,7 +167,7 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl // Allow the ability to configure the HB endpoint for testing purposes. const ttxSettings = config.getConfig('ttxSettings'); - const url = (ttxSettings && ttxSettings.url) || END_POINT; + const url = (ttxSettings && ttxSettings.url) || `${END_POINT}?guid=${params.siteId}`; // Return the server request return { diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 3721cef18d9..d30659791ea 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -162,7 +162,7 @@ describe('33acrossBidAdapter:', function () { function ServerRequestBuilder() { const serverRequest = { 'method': 'POST', - 'url': END_POINT, + 'url': `${END_POINT}?guid=${SITE_ID}`, 'data': null, 'options': { 'contentType': 'text/plain', From 7d113e0caeaaba466edd6b88ea0224151467fdd2 Mon Sep 17 00:00:00 2001 From: Shrikant Patwari <67643499+shrikantpatwari@users.noreply.github.com> Date: Tue, 11 Aug 2020 11:58:35 +0530 Subject: [PATCH 0071/1476] Updated YuktaMedia Analytics Adapter: updated request body with more details (#5547) * Analytic Adaptor by YuktaMedia * removed optional bid request params * test case modified * Updated YuktaMedia Analytics Adapter as per 3.0 prebid changes * Added api key in circle ci config file * reverted changes * hard coded protocol for the request * Modified request format and parameters * removed polyfill for object.entries and removed Useless assignment to local variable * Added bidId and auctionId in request as well * fixed failing unit tests * dealId captured * Updated code as per recommendation and added tests * addedadditional test case * removed non used variables and functions Co-authored-by: Shrikant Patwari --- modules/yuktamediaAnalyticsAdapter.js | 311 ++++--- modules/yuktamediaAnalyticsAdapter.md | 9 +- .../yuktamediaAnalyticsAdapter_spec.js | 545 ++++++++++++ .../yuktamediaAnalyticsAdaptor_spec.js | 788 ------------------ 4 files changed, 763 insertions(+), 890 deletions(-) create mode 100644 test/spec/modules/yuktamediaAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js diff --git a/modules/yuktamediaAnalyticsAdapter.js b/modules/yuktamediaAnalyticsAdapter.js index b346a26c843..3c27ca9754a 100644 --- a/modules/yuktamediaAnalyticsAdapter.js +++ b/modules/yuktamediaAnalyticsAdapter.js @@ -3,121 +3,70 @@ import adapter from '../src/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; -const emptyUrl = ''; -const analyticsType = 'endpoint'; -const yuktamediaAnalyticsVersion = 'v2.0.0'; +const storage = getStorageManager(); +const yuktamediaAnalyticsVersion = 'v3.0.0'; let initOptions; let auctionTimestamp; -let events = { - bids: [] + +const events = { + auctions: {} }; +const localStoragePrefix = 'yuktamediaAnalytics_'; +const utmTags = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']; -var yuktamediaAnalyticsAdapter = Object.assign(adapter( - { - emptyUrl, - analyticsType - }), { - track({ eventType, args }) { - if (typeof args !== 'undefined') { - if (eventType === CONSTANTS.EVENTS.BID_TIMEOUT) { - args.forEach(item => { mapBidResponse(item, 'timeout'); }); - } else if (eventType === CONSTANTS.EVENTS.AUCTION_INIT) { - events.auctionInit = args; - auctionTimestamp = args.timestamp; - } else if (eventType === CONSTANTS.EVENTS.BID_REQUESTED) { - mapBidRequests(args).forEach(item => { events.bids.push(item) }); - } else if (eventType === CONSTANTS.EVENTS.BID_RESPONSE) { - mapBidResponse(args, 'response'); - } else if (eventType === CONSTANTS.EVENTS.BID_WON) { - send({ - bidWon: mapBidResponse(args, 'win') - }, 'won'); - } +function getParameterByName(param) { + let vars = {}; + window.location.href.replace(location.hash, '').replace( + /[?&]+([^=&]+)=?([^&]*)?/gi, + function (m, key, value) { + vars[key] = value !== undefined ? value : ''; } + ); + return vars[param] ? vars[param] : ''; +} - if (eventType === CONSTANTS.EVENTS.AUCTION_END) { - send(events, 'auctionEnd'); - } - } -}); +function isNavigatorSendBeaconSupported() { + return ('navigator' in window) && ('sendBeacon' in window.navigator); +} -function mapBidRequests(params) { - let arr = []; - if (typeof params.bids !== 'undefined' && params.bids.length) { - params.bids.forEach(function (bid) { - arr.push({ - bidderCode: bid.bidder, - bidId: bid.bidId, - adUnitCode: bid.adUnitCode, - requestId: bid.bidderRequestId, - auctionId: bid.auctionId, - transactionId: bid.transactionId, - sizes: utils.parseSizesInput(bid.mediaTypes.banner.sizes).toString(), - renderStatus: 1, - requestTimestamp: params.auctionStart - }); - }); +function updateSessionId() { + if (isSessionIdTimeoutExpired()) { + let newSessionId = utils.generateUUID(); + storage.setDataInLocalStorage(localStoragePrefix.concat('session_id'), newSessionId); } - return arr; + initOptions.sessionId = getSessionId(); + updateSessionIdTimeout(); } -function mapBidResponse(bidResponse, status) { - if (status !== 'win') { - let bid = events.bids.filter(o => o.bidId == bidResponse.bidId || o.bidId == bidResponse.requestId)[0]; - Object.assign(bid, { - bidderCode: bidResponse.bidder, - bidId: status == 'timeout' ? bidResponse.bidId : bidResponse.requestId, - adUnitCode: bidResponse.adUnitCode, - auctionId: bidResponse.auctionId, - creativeId: bidResponse.creativeId, - transactionId: bidResponse.transactionId, - currency: bidResponse.currency, - cpm: bidResponse.cpm, - netRevenue: bidResponse.netRevenue, - mediaType: bidResponse.mediaType, - statusMessage: bidResponse.statusMessage, - status: bidResponse.status, - renderStatus: status == 'timeout' ? 3 : 2, - timeToRespond: bidResponse.timeToRespond, - requestTimestamp: bidResponse.requestTimestamp, - responseTimestamp: bidResponse.responseTimestamp - }); - } else if (status == 'win') { - return { - bidderCode: bidResponse.bidder, - bidId: bidResponse.requestId, - adUnitCode: bidResponse.adUnitCode, - auctionId: bidResponse.auctionId, - creativeId: bidResponse.creativeId, - transactionId: bidResponse.transactionId, - currency: bidResponse.currency, - cpm: bidResponse.cpm, - netRevenue: bidResponse.netRevenue, - renderedSize: bidResponse.size, - mediaType: bidResponse.mediaType, - statusMessage: bidResponse.statusMessage, - status: bidResponse.status, - renderStatus: 4, - timeToRespond: bidResponse.timeToRespond, - requestTimestamp: bidResponse.requestTimestamp, - responseTimestamp: bidResponse.responseTimestamp - } - } +function updateSessionIdTimeout() { + storage.setDataInLocalStorage(localStoragePrefix.concat('session_timeout'), Date.now()); +} + +function isSessionIdTimeoutExpired() { + let cpmSessionTimestamp = storage.getDataFromLocalStorage(localStoragePrefix.concat('session_timeout')); + return Date.now() - cpmSessionTimestamp > 3600000; +} + +function getSessionId() { + return storage.getDataFromLocalStorage(localStoragePrefix.concat('session_id')) ? storage.getDataFromLocalStorage(localStoragePrefix.concat('session_id')) : ''; +} + +function isUtmTimeoutExpired() { + let utmTimestamp = storage.getDataFromLocalStorage(localStoragePrefix.concat('utm_timeout')); + return (Date.now() - utmTimestamp) > 3600000; } function send(data, status) { - let location = utils.getWindowLocation(); - if (typeof data !== 'undefined' && typeof data.auctionInit !== 'undefined') { - data.auctionInit = Object.assign({ host: location.host, path: location.pathname, search: location.search }, data.auctionInit); - } - data.initOptions = initOptions; + const location = utils.getWindowLocation(); + data.initOptions = Object.assign({ host: location.host, path: location.pathname, search: location.search }, initOptions); - let yuktamediaAnalyticsRequestUrl = utils.buildUrl({ + const yuktamediaAnalyticsRequestUrl = utils.buildUrl({ protocol: 'https', hostname: 'analytics-prebid.yuktamedia.com', - pathname: status == 'auctionEnd' ? '/api/bids' : '/api/bid/won', + pathname: '/api/bids', search: { auctionTimestamp: auctionTimestamp, yuktamediaAnalyticsVersion: yuktamediaAnalyticsVersion, @@ -125,12 +74,176 @@ function send(data, status) { } }); - ajax(yuktamediaAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/plain' }); + if (isNavigatorSendBeaconSupported()) { + window.navigator.sendBeacon(yuktamediaAnalyticsRequestUrl, JSON.stringify(data)); + } else { + ajax(yuktamediaAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/plain' }); + } } +var yuktamediaAnalyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}), { + track({ eventType, args }) { + if (typeof args !== 'undefined') { + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: + utils.logInfo(localStoragePrefix + 'AUCTION_INIT:', JSON.stringify(args)); + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + events.auctions[args.auctionId] = { bids: {} }; + auctionTimestamp = args.timestamp; + } + break; + case CONSTANTS.EVENTS.BID_REQUESTED: + utils.logInfo(localStoragePrefix + 'BID_REQUESTED:', JSON.stringify(args)); + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + if (typeof events.auctions[args.auctionId] === 'undefined') { + events.auctions[args.auctionId] = { bids: {} }; + } + events.auctions[args.auctionId]['timeStamp'] = args.start; + args.bids.forEach(function (bidRequest) { + events.auctions[args.auctionId]['bids'][bidRequest.bidId] = { + bidder: bidRequest.bidder, + adUnit: bidRequest.adUnitCode, + sizes: utils.parseSizesInput(bidRequest.sizes).toString(), + isBid: false, + won: false, + timeout: false, + renderStatus: 'bid-requested', + bidId: bidRequest.bidId, + auctionId: args.auctionId + } + if (typeof initOptions.enableUserIdCollection !== 'undefined' && initOptions.enableUserIdCollection && typeof bidRequest['userId'] !== 'undefined') { + for (let [userIdProvider, userId] in Object.entries(bidRequest['userId'])) { + userIdProvider = typeof userIdProvider !== 'string' ? JSON.stringify(userIdProvider) : userIdProvider; + userId = typeof userId !== 'string' ? JSON.stringify(userId) : userId; + events.auctions[args.auctionId]['bids'][bidRequest.bidId]['userID-'.concat(userIdProvider)] = userId; + } + } + }); + } + break; + case CONSTANTS.EVENTS.BID_RESPONSE: + utils.logInfo(localStoragePrefix + 'BID_RESPONSE:', JSON.stringify(args)); + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + if (typeof events.auctions[args.auctionId] === 'undefined') { + events.auctions[args.auctionId] = { bids: {} }; + } else if (Object.keys(events.auctions[args.auctionId]['bids']).length) { + let bidResponse = events.auctions[args.auctionId]['bids'][args.requestId]; + bidResponse.isBid = args.getStatusCode() === CONSTANTS.STATUS.GOOD; + bidResponse.cpm = args.cpm; + bidResponse.currency = args.currency; + bidResponse.netRevenue = args.netRevenue; + bidResponse.dealId = typeof args.dealId !== 'undefined' ? args.dealId : ''; + bidResponse.mediaType = args.mediaType; + if (bidResponse.mediaType === 'native') { + bidResponse.nativeTitle = typeof args['native']['title'] !== 'undefined' ? args['native']['title'] : ''; + bidResponse.nativeSponsoredBy = typeof args['native']['sponsoredBy'] !== 'undefined' ? args['native']['sponsoredBy'] : ''; + } + bidResponse.timeToRespond = args.timeToRespond; + bidResponse.requestTimestamp = args.requestTimestamp; + bidResponse.responseTimestamp = args.responseTimestamp; + bidResponse.bidForSize = args.size; + for (const [adserverTargetingKey, adserverTargetingValue] of Object.entries(args.adserverTargeting)) { + if (['body', 'icon', 'image', 'linkurl', 'host', 'path'].every((ele) => !adserverTargetingKey.includes(ele))) { + bidResponse['adserverTargeting-' + adserverTargetingKey] = adserverTargetingValue; + } + } + bidResponse.renderStatus = 'bid-response-received'; + } + } + break; + case CONSTANTS.EVENTS.NO_BID: + utils.logInfo(localStoragePrefix + 'NO_BID:', JSON.stringify(args)); + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + if (typeof events.auctions[args.auctionId] === 'undefined') { + events.auctions[args.auctionId] = { bids: {} }; + } else if (Object.keys(events.auctions[args.auctionId]['bids']).length) { + const noBid = events.auctions[args.auctionId]['bids'][args.bidId]; + noBid.renderStatus = 'no-bid'; + } + } + break; + case CONSTANTS.EVENTS.BID_WON: + utils.logInfo(localStoragePrefix + 'BID_WON:', JSON.stringify(args)); + if (typeof initOptions.enableSession !== 'undefined' && initOptions.enableSession) { + updateSessionId(); + } + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + if (typeof events.auctions[args.auctionId] === 'undefined') { + events.auctions[args.auctionId] = { bids: {} }; + } else if (Object.keys(events.auctions[args.auctionId]['bids']).length) { + const wonBid = events.auctions[args.auctionId]['bids'][args.requestId]; + wonBid.won = true; + wonBid.renderStatus = 'bid-won'; + send({ 'bids': [wonBid] }, 'won'); + } + } + break; + case CONSTANTS.EVENTS.BID_TIMEOUT: + utils.logInfo(localStoragePrefix + 'BID_TIMEOUT:', JSON.stringify(args)); + if (args.length) { + args.forEach(timeout => { + if (typeof timeout !== 'undefined' && typeof timeout.auctionId !== 'undefined' && timeout.auctionId.length) { + if (typeof events.auctions[args.auctionId] === 'undefined') { + events.auctions[args.auctionId] = { bids: {} }; + } else if (Object.keys(events.auctions[args.auctionId]['bids']).length) { + const timeoutBid = events.auctions[timeout.auctionId].bids[timeout.bidId]; + timeoutBid.timeout = true; + timeoutBid.renderStatus = 'bid-timedout'; + } + } + }); + } + break; + case CONSTANTS.EVENTS.AUCTION_END: + utils.logInfo(localStoragePrefix + 'AUCTION_END:', JSON.stringify(args)); + if (typeof initOptions.enableSession !== 'undefined' && initOptions.enableSession) { + updateSessionId(); + } + if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { + const bids = Object.values(events.auctions[args.auctionId]['bids']); + send({ 'bids': bids }, 'auctionEnd'); + } + break; + } + } + } +}); + +yuktamediaAnalyticsAdapter.buildUtmTagData = function (options) { + let utmTagData = {}; + let utmTagsDetected = false; + if (typeof options.enableUTMCollection !== 'undefined' && options.enableUTMCollection) { + utmTags.forEach(function (utmTagKey) { + let utmTagValue = getParameterByName(utmTagKey); + if (utmTagValue !== '') { + utmTagsDetected = true; + } + utmTagData[utmTagKey] = utmTagValue; + }); + utmTags.forEach(function (utmTagKey) { + if (utmTagsDetected) { + storage.setDataInLocalStorage(localStoragePrefix.concat(utmTagKey), utmTagData[utmTagKey]); + storage.setDataInLocalStorage(localStoragePrefix.concat('utm_timeout'), Date.now()); + } else { + if (!isUtmTimeoutExpired()) { + utmTagData[utmTagKey] = storage.getDataFromLocalStorage(localStoragePrefix.concat(utmTagKey)) ? storage.getDataFromLocalStorage(localStoragePrefix.concat(utmTagKey)) : ''; + storage.setDataInLocalStorage(localStoragePrefix.concat('utm_timeout'), Date.now()); + } + } + }); + } + return utmTagData; +}; + yuktamediaAnalyticsAdapter.originEnableAnalytics = yuktamediaAnalyticsAdapter.enableAnalytics; yuktamediaAnalyticsAdapter.enableAnalytics = function (config) { - initOptions = config.options; + if (config && config.options) { + if (typeof config.options.pubId === 'undefined' || typeof config.options.pubKey === 'undefined') { + utils.logError('Need pubId and pubKey to log auction results. Please contact a YuktaMedia representative if you do not know your pubId and pubKey.'); + return; + } + } + initOptions = Object.assign({}, config.options, this.buildUtmTagData(config.options)); yuktamediaAnalyticsAdapter.originEnableAnalytics(config); }; diff --git a/modules/yuktamediaAnalyticsAdapter.md b/modules/yuktamediaAnalyticsAdapter.md index a21675b6b1d..af47985c834 100644 --- a/modules/yuktamediaAnalyticsAdapter.md +++ b/modules/yuktamediaAnalyticsAdapter.md @@ -1,5 +1,5 @@ # Overview -Module Name: YuktaMedia Analytics Adapter +Module Name: YuktaOne Analytics by YuktaMedia Module Type: Analytics Adapter @@ -15,8 +15,11 @@ Analytics adapter for prebid provided by YuktaMedia. Contact info@yuktamedia.com { provider: 'yuktamedia', options : { - pubId : 50357 //id provided by YuktaMedia LLP - pubKey: 'xxx' //key provided by YuktaMedia LLP + pubId : 50357, // id provided by YuktaMedia LLP + pubKey: 'xxx', // key provided by YuktaMedia LLP + enableUTMCollection: true, // set true if want to collect utm info + enableSession: true, // set true if want to collect information by sessions + enableUserIdCollection: true // set true if want to collect user ID module info } } ``` diff --git a/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..24781d749e0 --- /dev/null +++ b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js @@ -0,0 +1,545 @@ +import yuktamediaAnalyticsAdapter from 'modules/yuktamediaAnalyticsAdapter.js'; +import { expect } from 'chai'; +let events = require('src/events'); +let constants = require('src/constants.json'); + +let prebidAuction = { + 'auctionInit': { + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954', + 'timestamp': 1595850680304, + }, + 'bidRequested': { + 'bidderCode': 'appnexus', + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954', + 'bidderRequestId': '181df4d465699c', + 'bids': [ + { + 'bidder': 'appnexus', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'userId': { + 'id5id': 'ID5-ZHMOxXeRXPe3inZKGD-Lj0g7y8UWdDbsYXQ_n6aWMQ', + 'parrableid': '01.1595590997.46d951017bdc272ca50b88dbcfb0545cfc636bec3e3d8c02091fb1b413328fb2fd3baf65cb4114b3f782895fd09f82f02c9042b85b42c4654d08ba06dc77f0ded936c8ea3fc4085b4a99', + 'pubcid': '100a8bc9-f588-4c22-873e-a721cb68bc34' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '2bccebeda7fbe4', + 'bidderRequestId': '181df4d465699c', + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1595850680304, + 'timeout': 1100, + 'start': 1595850680307 + }, + 'noBid': {}, + 'bidTimeout': [], + 'bidResponse': { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'getStatusCode': function () { return 1; }, + 'adId': '3ade442375213f', + 'requestId': '2bccebeda7fbe4', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954', + 'responseTimestamp': 1595850681254, + 'requestTimestamp': 1595850680307, + 'bidder': 'appnexus', + 'timeToRespond': 947, + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '3ade442375213f', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + } + }, + 'auctionEnd': { + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954' + }, + 'bidWon': { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'requestId': '2bccebeda7fbe4', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': 'ca421611-0bc0-4164-a69c-fe4158c68954', + 'responseTimestamp': 1595850681254, + 'requestTimestamp': 1595850680307, + 'bidder': 'appnexus', + 'timeToRespond': 947, + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '3ade442375213f', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + 'status': 'rendered' + } +}; + +let prebidNativeAuction = { + 'auctionInit': { + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'timestamp': 1595589742100, + }, + 'bidRequested': { + 'bidderCode': 'appnexus', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'tid': 'f9c220eb-e44f-412b-92ff-8c24085ca675', + 'bids': [ + { + 'bidder': 'appnexus', + 'bid_id': '19a879bd73bc8d', + 'nativeParams': { + 'title': { + 'required': true, + 'len': 800 + }, + 'image': { + 'required': true, + 'sizes': [ + 989, + 742 + ] + }, + 'sponsoredBy': { + 'required': true + } + }, + 'mediaTypes': { + 'native': { + 'title': { + 'required': true, + 'len': 800 + }, + 'image': { + 'required': true, + 'sizes': [ + 989, + 742 + ] + }, + 'sponsoredBy': { + 'required': true + } + } + }, + 'adUnitCode': '/19968336/prebid_native_example_1', + 'sizes': [], + 'bidId': '19a879bd73bc8d', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'src': 's2s', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 0, + 'bidderWinsCount': 0 + }, + { + 'bidder': 'appnexus', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '28f8cc7d10f2db', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'src': 's2s', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 2, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1595589742100, + 'timeout': 1000, + 'src': 's2s', + 'start': 1595589742108 + }, + 'bidRequested1': { + 'bidderCode': 'ix', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'bidderRequestId': '5e64168f3654af', + 'bids': [ + { + 'bidder': 'ix', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'dfp-ad-rightrail_top', + 'sizes': [[300, 250]], + 'bidId': '9424dea605368f', + 'bidderRequestId': '5e64168f3654af', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'src': 's2s' + } + ], + 'auctionStart': 1595589742100, + 'timeout': 1000, + 'src': 's2s', + 'start': 1595589742108 + }, + 'noBid': { + 'bidder': 'ix', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'dfp-ad-rightrail_top', + 'transactionId': 'd99d90e0-663a-459d-8c87-4c92ce6a527c', + 'sizes': [[300, 250]], + 'bidId': '9424dea605368f', + 'bidderRequestId': '5e64168f3654af', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'src': 's2s', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 4, + 'bidderWinsCount': 0 + }, + 'bidTimeout': [ + { + 'bidId': '28f8cc7d10f2db', + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128' + } + ], + 'bidResponse': { + 'bidderCode': 'appnexus', + 'statusMessage': 'Bid available', + 'source': 's2s', + 'getStatusCode': function () { return 1; }, + 'cpm': 10, + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_pb': '10.00', + 'hb_adid': '4e756c72ee9044', + 'hb_size': 'undefinedxundefined', + 'hb_source': 's2s', + 'hb_format': 'native', + 'hb_native_linkurl': 'some_long_url', + 'hb_native_title': 'This is a Prebid Native Creative', + 'hb_native_brand': 'Prebid.org' + }, + 'native': { + 'clickUrl': { + 'url': 'some_long_url' + }, + 'impressionTrackers': [ + 'some_long_url' + ], + 'javascriptTrackers': [], + 'image': { + 'url': 'some_long_image_path', + 'width': 989, + 'height': 742 + }, + 'title': 'This is a Prebid Native Creative', + 'sponsoredBy': 'Prebid.org' + }, + 'currency': 'USD', + 'ttl': 60, + 'netRevenue': true, + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'responseTimestamp': 1595589742827, + 'requestTimestamp': 1595589742108, + 'bidder': 'appnexus', + 'adUnitCode': '/19968336/prebid_native_example_1', + 'timeToRespond': 719, + 'size': 'undefinedxundefined' + }, + 'auctionEnd': { + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128' + }, + 'bidWon': { + 'bidderCode': 'appnexus', + 'mediaType': 'native', + 'source': 's2s', + 'getStatusCode': function () { return 1; }, + 'cpm': 10, + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_pb': '10.00', + 'hb_adid': '4e756c72ee9044', + 'hb_size': 'undefinedxundefined', + 'hb_source': 's2s', + 'hb_format': 'native', + 'hb_native_linkurl': 'some_long_url', + 'hb_native_title': 'This is a Prebid Native Creative', + 'hb_native_brand': 'Prebid.org' + }, + 'native': { + 'clickUrl': { + 'url': 'some_long_url' + }, + 'impressionTrackers': [ + 'some_long_url' + ], + 'javascriptTrackers': [], + 'image': { + 'url': 'some_long_image_path', + 'width': 989, + 'height': 742 + }, + 'title': 'This is a Prebid Native Creative', + 'sponsoredBy': 'Prebid.org' + }, + 'currency': 'USD', + 'ttl': 60, + 'netRevenue': true, + 'auctionId': '86e005fa-1900-4782-b6df-528500f09128', + 'responseTimestamp': 1595589742827, + 'requestTimestamp': 1595589742108, + 'bidder': 'appnexus', + 'adUnitCode': '/19968336/prebid_native_example_1', + 'timeToRespond': 719, + 'size': 'undefinedxundefined', + 'status': 'rendered' + } +} + +describe('yuktamedia analytics adapter', function () { + beforeEach(() => { + sinon.stub(events, 'getEvents').returns([]); + }); + afterEach(() => { + events.getEvents.restore(); + }); + + describe('enableAnalytics', function () { + beforeEach(() => { + sinon.spy(yuktamediaAnalyticsAdapter, 'track'); + }); + afterEach(() => { + yuktamediaAnalyticsAdapter.disableAnalytics(); + yuktamediaAnalyticsAdapter.track.restore(); + }); + + it('should catch all events', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); + events.emit(constants.EVENTS.AUCTION_INIT, prebidAuction[constants.EVENTS.AUCTION_INIT]); + events.emit(constants.EVENTS.BID_REQUESTED, prebidAuction[constants.EVENTS.BID_REQUESTED]); + events.emit(constants.EVENTS.NO_BID, prebidAuction[constants.EVENTS.NO_BID]); + events.emit(constants.EVENTS.BID_TIMEOUT, prebidAuction[constants.EVENTS.BID_TIMEOUT]); + events.emit(constants.EVENTS.BID_RESPONSE, prebidAuction[constants.EVENTS.BID_RESPONSE]); + events.emit(constants.EVENTS.AUCTION_END, prebidAuction[constants.EVENTS.AUCTION_END]); + sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 6); + }); + + it('should catch no events if no pubKey and pubId', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + 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, {}); + + sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 0); + }); + + it('should catch nobid, timeout and biwon event events', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); + events.emit(constants.EVENTS.AUCTION_INIT, prebidNativeAuction[constants.EVENTS.AUCTION_INIT]); + events.emit(constants.EVENTS.BID_REQUESTED, prebidNativeAuction[constants.EVENTS.BID_REQUESTED]); + events.emit(constants.EVENTS.BID_REQUESTED, prebidNativeAuction[constants.EVENTS.BID_REQUESTED + '1']); + events.emit(constants.EVENTS.NO_BID, prebidNativeAuction[constants.EVENTS.NO_BID]); + events.emit(constants.EVENTS.BID_TIMEOUT, prebidNativeAuction[constants.EVENTS.BID_TIMEOUT]); + events.emit(constants.EVENTS.BID_RESPONSE, prebidNativeAuction[constants.EVENTS.BID_RESPONSE]); + events.emit(constants.EVENTS.AUCTION_END, prebidNativeAuction[constants.EVENTS.AUCTION_END]); + events.emit(constants.EVENTS.AUCTION_END, prebidNativeAuction[constants.EVENTS.BID_WON]); + sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 8); + }); + }); + + describe('build utm tag data', function () { + beforeEach(function () { + localStorage.setItem('yuktamediaAnalytics_utm_source', 'prebid'); + localStorage.setItem('yuktamediaAnalytics_utm_medium', 'ad'); + localStorage.setItem('yuktamediaAnalytics_utm_campaign', ''); + localStorage.setItem('yuktamediaAnalytics_utm_term', ''); + localStorage.setItem('yuktamediaAnalytics_utm_content', ''); + localStorage.setItem('yuktamediaAnalytics_utm_timeout', Date.now()); + }); + + afterEach(function () { + localStorage.clear(); + }); + + it('should build utm data from local storage', function () { + let utmTagData = yuktamediaAnalyticsAdapter.buildUtmTagData({ + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + }); + expect(utmTagData.utm_source).to.equal('prebid'); + expect(utmTagData.utm_medium).to.equal('ad'); + expect(utmTagData.utm_campaign).to.equal(''); + expect(utmTagData.utm_term).to.equal(''); + expect(utmTagData.utm_content).to.equal(''); + }); + + it('should return empty object for disabled utm setting', function () { + let utmTagData = yuktamediaAnalyticsAdapter.buildUtmTagData({ + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: false, + enableSession: true, + enableUserIdCollection: true + }); + expect(utmTagData).deep.equal({}); + }); + }); + + describe('build session information', function () { + beforeEach(() => { + sinon.spy(yuktamediaAnalyticsAdapter, 'track'); + localStorage.clear(); + }); + afterEach(() => { + yuktamediaAnalyticsAdapter.disableAnalytics(); + yuktamediaAnalyticsAdapter.track.restore(); + localStorage.clear(); + }); + + it('should create session id in local storage if enabled', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); + events.emit(constants.EVENTS.AUCTION_INIT, prebidAuction[constants.EVENTS.AUCTION_INIT]); + events.emit(constants.EVENTS.BID_REQUESTED, prebidAuction[constants.EVENTS.BID_REQUESTED]); + events.emit(constants.EVENTS.NO_BID, prebidAuction[constants.EVENTS.NO_BID]); + events.emit(constants.EVENTS.BID_TIMEOUT, prebidAuction[constants.EVENTS.BID_TIMEOUT]); + events.emit(constants.EVENTS.BID_RESPONSE, prebidAuction[constants.EVENTS.BID_RESPONSE]); + events.emit(constants.EVENTS.AUCTION_END, prebidAuction[constants.EVENTS.AUCTION_END]); + expect(localStorage.getItem('yuktamediaAnalytics_session_id')).to.not.equal(null); + }); + + it('should not create session id in local storage if disabled', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: false, + enableUserIdCollection: true + } + }); + events.emit(constants.EVENTS.AUCTION_INIT, prebidAuction[constants.EVENTS.AUCTION_INIT]); + events.emit(constants.EVENTS.BID_REQUESTED, prebidAuction[constants.EVENTS.BID_REQUESTED]); + events.emit(constants.EVENTS.NO_BID, prebidAuction[constants.EVENTS.NO_BID]); + events.emit(constants.EVENTS.BID_TIMEOUT, prebidAuction[constants.EVENTS.BID_TIMEOUT]); + events.emit(constants.EVENTS.BID_RESPONSE, prebidAuction[constants.EVENTS.BID_RESPONSE]); + events.emit(constants.EVENTS.AUCTION_END, prebidAuction[constants.EVENTS.AUCTION_END]); + expect(localStorage.getItem('yuktamediaAnalytics_session_id')).to.equal(null); + }); + }); +}); diff --git a/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js b/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js deleted file mode 100644 index 24a524c85c7..00000000000 --- a/test/spec/modules/yuktamediaAnalyticsAdaptor_spec.js +++ /dev/null @@ -1,788 +0,0 @@ -import yuktamediaAnalyticsAdapter from 'modules/yuktamediaAnalyticsAdapter.js'; -import { expect } from 'chai'; -import adapterManager from 'src/adapterManager.js'; -import * as utils from 'src/utils.js'; -import { server } from 'test/mocks/xhr.js'; - -let events = require('src/events'); -let constants = require('src/constants.json'); - -describe('yuktamedia analytics adapter', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - }); - - afterEach(function () { - events.getEvents.restore(); - }); - - describe('track', function () { - let initOptions = { - pubId: '1', - pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==' - }; - - let prebidEvent = { - 'addAdUnits': {}, - 'requestBids': {}, - 'auctionInit': { - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'timestamp': 1576823893836, - 'auctionStatus': 'inProgress', - 'adUnits': [ - { - 'code': 'div-gpt-ad-1460505748561-0', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - } - } - ], - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98' - } - ], - 'adUnitCodes': [ - 'div-gpt-ad-1460505748561-0' - ], - 'bidderRequests': [ - { - 'bidderCode': 'appnexus', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidderRequestId': '155975c76e13b1', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '263efc09896d0c', - 'bidderRequestId': '155975c76e13b1', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1576823893836, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://observer.com/integrationExamples/gpt/hello_world.html', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://observer.com/integrationExamples/gpt/hello_world.html' - ] - }, - 'start': 1576823893838 - } - ], - 'noBids': [], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 1000 - }, - 'bidRequested': { - 'bidderCode': 'appnexus', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidderRequestId': '155975c76e13b1', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '263efc09896d0c', - 'bidderRequestId': '155975c76e13b1', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1576823893836, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://observer.com/integrationExamples/gpt/hello_world.html', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://observer.com/integrationExamples/gpt/hello_world.html' - ] - }, - 'start': 1576823893838 - }, - 'bidAdjustment': { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '393976d8770041', - 'requestId': '263efc09896d0c', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 0.5, - 'creativeId': 96846035, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'appnexus': { - 'buyerMemberId': 9325 - }, - 'meta': { - 'advertiserId': 2529885 - }, - 'ad': '', - 'originalCpm': 0.5, - 'originalCurrency': 'USD', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'responseTimestamp': 1576823894050, - 'requestTimestamp': 1576823893838, - 'bidder': 'appnexus', - 'timeToRespond': 212 - }, - 'bidTimeout': [ - ], - 'bidResponse': { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '393976d8770041', - 'requestId': '263efc09896d0c', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 0.5, - 'creativeId': 96846035, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'appnexus': { - 'buyerMemberId': 9325 - }, - 'meta': { - 'advertiserId': 2529885 - }, - 'ad': '', - 'originalCpm': 0.5, - 'originalCurrency': 'USD', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'responseTimestamp': 1576823894050, - 'requestTimestamp': 1576823893838, - 'bidder': 'appnexus', - 'timeToRespond': 212, - 'pbLg': '0.50', - 'pbMg': '0.50', - 'pbHg': '0.50', - 'pbAg': '0.50', - 'pbDg': '0.50', - 'pbCg': '', - 'size': '300x250', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '393976d8770041', - 'hb_pb': '0.50', - 'hb_size': '300x250', - 'hb_source': 'client', - 'hb_format': 'banner' - } - }, - 'auctionEnd': { - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'timestamp': 1576823893836, - 'auctionEnd': 1576823894054, - 'auctionStatus': 'completed', - 'adUnits': [ - { - 'code': 'div-gpt-ad-1460505748561-0', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - } - } - ], - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98' - } - ], - 'adUnitCodes': [ - 'div-gpt-ad-1460505748561-0' - ], - 'bidderRequests': [ - { - 'bidderCode': 'appnexus', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidderRequestId': '155975c76e13b1', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '263efc09896d0c', - 'bidderRequestId': '155975c76e13b1', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1576823893836, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://observer.com/integrationExamples/gpt/hello_world.html', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://observer.com/integrationExamples/gpt/hello_world.html' - ] - }, - 'start': 1576823893838 - } - ], - 'noBids': [], - 'bidsReceived': [ - { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '393976d8770041', - 'requestId': '263efc09896d0c', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 0.5, - 'creativeId': 96846035, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'appnexus': { - 'buyerMemberId': 9325 - }, - 'meta': { - 'advertiserId': 2529885 - }, - 'ad': '', - 'originalCpm': 0.5, - 'originalCurrency': 'USD', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'responseTimestamp': 1576823894050, - 'requestTimestamp': 1576823893838, - 'bidder': 'appnexus', - 'timeToRespond': 212, - 'pbLg': '0.50', - 'pbMg': '0.50', - 'pbHg': '0.50', - 'pbAg': '0.50', - 'pbDg': '0.50', - 'pbCg': '', - 'size': '300x250', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '393976d8770041', - 'hb_pb': '0.50', - 'hb_size': '300x250', - 'hb_source': 'client', - 'hb_format': 'banner' - } - } - ], - 'winningBids': [], - 'timeout': 1000 - }, - 'setTargeting': { - 'div-gpt-ad-1460505748561-0': { - 'hb_format': 'banner', - 'hb_source': 'client', - 'hb_size': '300x250', - 'hb_pb': '0.50', - 'hb_adid': '393976d8770041', - 'hb_bidder': 'appnexus', - 'hb_format_appnexus': 'banner', - 'hb_source_appnexus': 'client', - 'hb_size_appnexus': '300x250', - 'hb_pb_appnexus': '0.50', - 'hb_adid_appnexus': '393976d8770041', - 'hb_bidder_appnexus': 'appnexus' - } - }, - 'bidderDone': { - 'bidderCode': 'appnexus', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidderRequestId': '155975c76e13b1', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '263efc09896d0c', - 'bidderRequestId': '155975c76e13b1', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1576823893836, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://observer.com/integrationExamples/gpt/hello_world.html', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://observer.com/integrationExamples/gpt/hello_world.html' - ] - }, - 'start': 1576823893838 - }, - 'bidWon': { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '393976d8770041', - 'requestId': '263efc09896d0c', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 0.5, - 'creativeId': 96846035, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'appnexus': { - 'buyerMemberId': 9325 - }, - 'meta': { - 'advertiserId': 2529885 - }, - 'ad': '', - 'originalCpm': 0.5, - 'originalCurrency': 'USD', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'responseTimestamp': 1576823894050, - 'requestTimestamp': 1576823893838, - 'bidder': 'appnexus', - 'timeToRespond': 212, - 'pbLg': '0.50', - 'pbMg': '0.50', - 'pbHg': '0.50', - 'pbAg': '0.50', - 'pbDg': '0.50', - 'pbCg': '', - 'size': '300x250', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '393976d8770041', - 'hb_pb': '0.50', - 'hb_size': '300x250', - 'hb_source': 'client', - 'hb_format': 'banner' - }, - 'status': 'rendered', - 'params': [ - { - 'placementId': 13144370 - } - ] - } - }; - let location = utils.getWindowLocation(); - - let expectedAfterBid = { - 'bids': [ - { - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidId': '263efc09896d0c', - 'bidderCode': 'appnexus', - 'cpm': 0.5, - 'creativeId': 96846035, - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': true, - 'renderStatus': 2, - 'requestId': '155975c76e13b1', - 'requestTimestamp': 1576823893838, - 'responseTimestamp': 1576823894050, - 'sizes': '300x250,300x600', - 'statusMessage': 'Bid available', - 'timeToRespond': 212 - } - ], - 'auctionInit': { - 'host': location.host, - 'path': location.pathname, - 'search': location.search, - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'timestamp': 1576823893836, - 'auctionStatus': 'inProgress', - 'adUnits': [ - { - 'code': 'div-gpt-ad-1460505748561-0', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - } - } - ], - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98' - } - ], - 'adUnitCodes': [ - 'div-gpt-ad-1460505748561-0' - ], - 'bidderRequests': [ - { - 'bidderCode': 'appnexus', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'bidderRequestId': '155975c76e13b1', - 'bids': [ - { - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'crumbs': { - 'pubcid': 'ff4002c4-ce05-4a61-b4ef-45a3cd93991a' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '6d275806-1943-4f3e-9cd5-624cbd05ad98', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '263efc09896d0c', - 'bidderRequestId': '155975c76e13b1', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1576823893836, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://observer.com/integrationExamples/gpt/hello_world.html', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://observer.com/integrationExamples/gpt/hello_world.html' - ] - }, - 'start': 1576823893838 - } - ], - 'noBids': [], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 1000, - 'config': initOptions - }, - 'initOptions': initOptions - }; - - let expectedAfterBidWon = { - 'bidWon': { - 'bidderCode': 'appnexus', - 'bidId': '263efc09896d0c', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'auctionId': 'db377024-d866-4a24-98ac-5e430f881313', - 'creativeId': 96846035, - 'currency': 'USD', - 'cpm': 0.5, - 'netRevenue': true, - 'renderedSize': '300x250', - 'mediaType': 'banner', - 'statusMessage': 'Bid available', - 'status': 'rendered', - 'renderStatus': 4, - 'timeToRespond': 212, - 'requestTimestamp': 1576823893838, - 'responseTimestamp': 1576823894050 - }, - 'initOptions': { - 'pubId': '1', - 'pubKey': 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==' - } - } - - adapterManager.registerAnalyticsAdapter({ - code: 'yuktamedia', - adapter: yuktamediaAnalyticsAdapter - }); - - beforeEach(function () { - adapterManager.enableAnalytics({ - provider: 'yuktamedia', - options: initOptions - }); - }); - - afterEach(function () { - yuktamediaAnalyticsAdapter.disableAnalytics(); - }); - - it('builds and sends auction data', function () { - // Step 1: Send auction init event - events.emit(constants.EVENTS.AUCTION_INIT, prebidEvent['auctionInit']); - - // Step 2: Send bid requested event - events.emit(constants.EVENTS.BID_REQUESTED, prebidEvent['bidRequested']); - - // Step 3: Send bid response event - events.emit(constants.EVENTS.BID_RESPONSE, prebidEvent['bidResponse']); - - // Step 4: Send bid time out event - events.emit(constants.EVENTS.BID_TIMEOUT, prebidEvent['bidTimeout']); - - // Step 5: Send auction end event - events.emit(constants.EVENTS.AUCTION_END, prebidEvent['auctionEnd']); - - expect(server.requests.length).to.equal(1); - - let realAfterBid = JSON.parse(server.requests[0].requestBody); - - expect(realAfterBid).to.deep.equal(expectedAfterBid); - - // Step 6: Send auction bid won event - events.emit(constants.EVENTS.BID_WON, prebidEvent['bidWon']); - - expect(server.requests.length).to.equal(2); - - let winEventData = JSON.parse(server.requests[1].requestBody); - - expect(winEventData).to.deep.equal(expectedAfterBidWon); - }); - }); -}); From 73880cd847b6024d84409b333fccc8b0d5cbc3bd Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Wed, 12 Aug 2020 00:58:56 +0300 Subject: [PATCH 0072/1476] Adkernel: deals support (#5585) --- modules/adkernelBidAdapter.js | 4 ++++ test/spec/modules/adkernelBidAdapter_spec.js | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 417c665627c..972dd696bf6 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -120,6 +120,9 @@ export const spec = { ttl: 360, netRevenue: true }; + if (rtbBid.dealid !== undefined) { + prBid.dealId = rtbBid.dealid; + } if ('banner' in imp) { prBid.mediaType = BANNER; prBid.width = rtbBid.w; @@ -307,6 +310,7 @@ function buildRtbRequest(imps, bidderRequest, schain) { 'at': 1, 'device': { 'ip': 'caller', + 'ipv6': 'caller', 'ua': 'caller', 'js': 1, 'language': getLanguage() diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 229d464a030..87504aa46af 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -171,7 +171,8 @@ describe('Adkernel adapter', function () { nurl: 'https://rtb.com/win?i=ZjKoPYSFI3Y_0', adm: '', w: 300, - h: 250 + h: 250, + dealid: 'deal' }] }], cur: 'USD', @@ -316,6 +317,7 @@ describe('Adkernel adapter', function () { it('should fill device with caller macro', function () { expect(bidRequest).to.have.property('device'); expect(bidRequest.device).to.have.property('ip', 'caller'); + expect(bidRequest.device).to.have.property('ipv6', 'caller'); expect(bidRequest.device).to.have.property('ua', 'caller'); expect(bidRequest.device).to.have.property('dnt', 1); }); @@ -504,6 +506,7 @@ describe('Adkernel adapter', function () { expect(resp).to.have.property('ttl'); expect(resp).to.have.property('mediaType', BANNER); expect(resp).to.have.property('ad'); + expect(resp).to.have.property('dealId', 'deal'); expect(resp.ad).to.have.string(''); }); From 3a7a741a7441239c86c4194c00c588938ca9615a Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 12 Aug 2020 00:02:00 +0200 Subject: [PATCH 0073/1476] update link to id5 docs for prebid (#5590) --- modules/userId/userId.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 9d835e23fca..a47ecd9f08c 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -25,8 +25,8 @@ pbjs.setConfig({ }, { name: "id5Id", params: { - partner: 173, //Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: "some-pd-string" // See https://console.id5.io/docs/public/prebid for details + partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id + pd: "some-pd-string" // See https://wiki.id5.io/display/PD/Prebid.js+UserId+Module for details }, storage: { type: "cookie", From b591906086647ddef3f415d7692574fd997d437d Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 12 Aug 2020 09:49:07 +0200 Subject: [PATCH 0074/1476] Adagio bid adapter 2.3.0 (#5498) * adagioBidAdapter: support cross-origin iframe context - use SafeFrame API to compute features - compute page_dimensions in SafeFrame context - Remove pageDimensions feature with cross-domain iframe * adagiobidAdapter: refactoring and improve tests * Fix getPageDimensions() width detection Use the real page width instead of the viewport one * Improve getSlotPosition() in postBid context * Load adagio script regarding TCF2 storage enforcement * Fix CI tests --- modules/adagioBidAdapter.js | 684 ++++++--- test/spec/modules/adagioBidAdapter_spec.js | 1582 ++++++++++++-------- 2 files changed, 1400 insertions(+), 866 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index b2ef5dafb41..b4c2a6ac0d6 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -5,16 +5,18 @@ import { loadExternalScript } from '../src/adloader.js' import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; import sha256 from 'crypto-js/sha256.js'; import { getStorageManager } from '../src/storageManager.js'; - -const BIDDER_CODE = 'adagio'; -const VERSION = '2.2.2'; -const FEATURES_VERSION = '1'; -const ENDPOINT = 'https://mp.4dex.io/prebid'; -const SUPPORTED_MEDIA_TYPES = ['banner']; -const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; -const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; -const GVLID = 617; -const storage = getStorageManager(GVLID, 'adagio'); +import { getRefererInfo } from '../src/refererDetection.js'; + +export const BIDDER_CODE = 'adagio'; +export const LOG_PREFIX = 'Adagio:'; +export const VERSION = '2.3.0'; +export const FEATURES_VERSION = '1'; +export const ENDPOINT = 'https://mp.4dex.io/prebid'; +export const SUPPORTED_MEDIA_TYPES = ['banner']; +export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; +export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; +export const GVLID = 617; +export const storage = getStorageManager(GVLID, 'adagio'); export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 @@ -23,17 +25,19 @@ t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj pV6EP3MTLosuUEpLaQIDAQAB -----END PUBLIC KEY-----`; +let currentWindow; + export function adagioScriptFromLocalStorageCb(ls) { try { if (!ls) { - utils.logWarn('Adagio Script not found'); + utils.logWarn(`${LOG_PREFIX} script not found.`); return; } const hashRgx = /^(\/\/ hash: (.+)\n)(.+\n)$/; if (!hashRgx.test(ls)) { - utils.logWarn('No hash found in Adagio script'); + utils.logWarn(`${LOG_PREFIX} no hash found.`); storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); } else { const r = ls.match(hashRgx); @@ -44,21 +48,32 @@ export function adagioScriptFromLocalStorageCb(ls) { jsEncrypt.setPublicKey(ADAGIO_PUBKEY); if (jsEncrypt.verify(content, hash, sha256)) { - utils.logInfo('Start Adagio script'); + utils.logInfo(`${LOG_PREFIX} start script.`); Function(ls)(); // eslint-disable-line no-new-func } else { - utils.logWarn('Invalid Adagio script found'); + utils.logWarn(`${LOG_PREFIX} invalid script found.`); storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); } } } catch (err) { - // + utils.logError(LOG_PREFIX, err); } } export function getAdagioScript() { storage.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY, (ls) => { - adagioScriptFromLocalStorageCb(ls) + internal.adagioScriptFromLocalStorageCb(ls) + }); + + storage.localStorageIsEnabled(isValid => { + if (isValid) { + loadExternalScript(ADAGIO_TAG_URL, BIDDER_CODE); + } else { + // ensure adagio removing for next time. + // It's an antipattern regarding the TCF2 enforcement logic + // but it's the only way to respect the user choice update. + window.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); + } }); } @@ -72,113 +87,197 @@ function canAccessTopWindow() { } } +function getCurrentWindow() { + return currentWindow || utils.getWindowSelf(); +} + +function isSafeFrameWindow() { + const ws = utils.getWindowSelf(); + return !!(ws.$sf && ws.$sf.ext); +} + function initAdagio() { - const w = utils.getWindowTop(); + if (canAccessTopWindow()) { + currentWindow = (canAccessTopWindow()) ? utils.getWindowTop() : utils.getWindowSelf(); + } + + const w = internal.getCurrentWindow(); w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.adUnits = w.ADAGIO.adUnits || {}; + w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits || []; w.ADAGIO.queue = w.ADAGIO.queue || []; w.ADAGIO.versions = w.ADAGIO.versions || {}; w.ADAGIO.versions.adagioBidderAdapter = VERSION; + w.ADAGIO.isSafeFrameWindow = isSafeFrameWindow(); getAdagioScript(); - - loadExternalScript(ADAGIO_TAG_URL, BIDDER_CODE) -} - -if (canAccessTopWindow()) { - initAdagio(); } export const _features = { - getPrintNumber: function (adUnitCode) { - const adagioAdUnit = _getOrAddAdagioAdUnit(adUnitCode); + getPrintNumber(adUnitCode) { + const adagioAdUnit = internal.getOrAddAdagioAdUnit(adUnitCode); return adagioAdUnit.printNumber || 1; }, - getPageDimensions: function () { - const viewportDims = _features.getViewPortDimensions().split('x'); - const w = utils.getWindowTop(); - const body = w.document.body; + getPageDimensions() { + if (isSafeFrameWindow() || !canAccessTopWindow()) { + return ''; + } + + // the page dimension can be computed on window.top only. + const wt = utils.getWindowTop(); + const body = wt.document.querySelector('body'); + if (!body) { - return '' + return ''; } - const html = w.document.documentElement; + const html = wt.document.documentElement; + const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); const pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); - return viewportDims[0] + 'x' + pageHeight; + return `${pageWidth}x${pageHeight}`; }, - getViewPortDimensions: function () { - let viewPortWidth; - let viewPortHeight; - const w = utils.getWindowTop(); - const d = w.document; + getViewPortDimensions() { + if (!isSafeFrameWindow() && !canAccessTopWindow()) { + return ''; + } + + const viewportDims = { w: 0, h: 0 }; - if (w.innerWidth) { - viewPortWidth = w.innerWidth; - viewPortHeight = w.innerHeight; + if (isSafeFrameWindow()) { + const ws = utils.getWindowSelf(); + + if (typeof ws.$sf.ext.geom !== 'function') { + utils.logWarn(`${LOG_PREFIX} cannot use the $sf.ext.geom() safeFrame API method`); + return ''; + } + + const sfGeom = ws.$sf.ext.geom().win; + viewportDims.w = Math.round(sfGeom.w); + viewportDims.h = Math.round(sfGeom.h); } else { - viewPortWidth = d.getElementsByTagName('body')[0].clientWidth; - viewPortHeight = d.getElementsByTagName('body')[0].clientHeight; + // window.top based computing + const wt = utils.getWindowTop(); + + if (wt.innerWidth) { + viewportDims.w = wt.innerWidth; + viewportDims.h = wt.innerHeight; + } else { + const d = wt.document; + const body = d.querySelector('body'); + + if (!body) { + return ''; + } + + viewportDims.w = d.querySelector('body').clientWidth; + viewportDims.h = d.querySelector('body').clientHeight; + } } - return viewPortWidth + 'x' + viewPortHeight; + return `${viewportDims.w}x${viewportDims.h}`; }, - isDomLoading: function () { - const w = utils.getWindowTop(); - let performance = w.performance || w.msPerformance || w.webkitPerformance || w.mozPerformance; - let domLoading = -1; + /** + * domLoading feature is computed on window.top if reachable. + */ + getDomLoadingDuration() { + let domLoadingDuration = -1; + let performance; + + performance = (canAccessTopWindow()) ? utils.getWindowTop().performance : utils.getWindowSelf().performance; if (performance && performance.timing && performance.timing.navigationStart > 0) { const val = performance.timing.domLoading - performance.timing.navigationStart; - if (val > 0) domLoading = val; + if (val > 0) { + domLoadingDuration = val; + } } - return domLoading; + + return domLoadingDuration; }, - getSlotPosition: function (element) { - if (!element) return ''; - - const w = utils.getWindowTop(); - const d = w.document; - const el = element; - - let box = el.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 = w.pageYOffset || docEl.scrollTop || body.scrollTop; - const scrollLeft = w.pageXOffset || docEl.scrollLeft || body.scrollLeft; - - const elComputedStyle = w.getComputedStyle(el, null); - const elComputedDisplay = elComputedStyle.display || 'block'; - const mustDisplayElement = elComputedDisplay === 'none'; - - if (mustDisplayElement) { - el.style = el.style || {}; - el.style.display = 'block'; - box = el.getBoundingClientRect(); - el.style.display = elComputedDisplay; + getSlotPosition(params) { + const { adUnitElementId, postBid } = params; + + if (!adUnitElementId) { + return ''; } - const position = { - x: Math.round(box.left + scrollLeft - clientLeft), - y: Math.round(box.top + scrollTop - clientTop) - }; + if (!isSafeFrameWindow() && !canAccessTopWindow()) { + return ''; + } + + const position = { x: 0, y: 0 }; + + if (isSafeFrameWindow()) { + const ws = utils.getWindowSelf(); + + if (typeof ws.$sf.ext.geom !== 'function') { + utils.logWarn(`${LOG_PREFIX} cannot use the $sf.ext.geom() safeFrame API method`); + return ''; + } - return position.x + 'x' + position.y; + const sfGeom = ws.$sf.ext.geom().self; + position.x = Math.round(sfGeom.t); + position.y = Math.round(sfGeom.l); + } else if (canAccessTopWindow()) { + // window.top based computing + const wt = utils.getWindowTop(); + const d = wt.document; + + let domElement; + + if (postBid === true) { + const ws = utils.getWindowSelf(); + const currentElement = ws.document.getElementById(adUnitElementId); + domElement = internal.getElementFromTopWindow(currentElement, ws); + } else { + domElement = wt.document.getElementById(adUnitElementId); + } + + 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 || {}; + domElement.style.display = 'block'; + box = domElement.getBoundingClientRect(); + domElement.style.display = elComputedDisplay; + } + position.x = Math.round(box.left + scrollLeft - clientLeft); + position.y = Math.round(box.top + scrollTop - clientTop); + } else { + return ''; + } + + return `${position.x}x${position.y}`; }, - getTimestamp: function () { + getTimestampUTC() { + // timestamp returned in seconds return Math.floor(new Date().getTime() / 1000) - new Date().getTimezoneOffset() * 60; }, - getDevice: function () { - if (!canAccessTopWindow()) return false; - const w = utils.getWindowTop(); - const ua = w.navigator.userAgent; + getDevice() { + const ws = utils.getWindowSelf(); + const ua = ws.navigator.userAgent; if ((/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i).test(ua)) { return 5; // "tablet" @@ -189,52 +288,83 @@ export const _features = { return 2; // personal computers }, - getBrowser: function () { - const w = utils.getWindowTop(); - const ua = w.navigator.userAgent; + getBrowser() { + const ws = utils.getWindowSelf(); + const ua = ws.navigator.userAgent; const uaLowerCase = ua.toLowerCase(); - return /Edge\/\d./i.test(ua) ? 'edge' : uaLowerCase.indexOf('chrome') > 0 ? 'chrome' : uaLowerCase.indexOf('firefox') > 0 ? 'firefox' : uaLowerCase.indexOf('safari') > 0 ? 'safari' : uaLowerCase.indexOf('opera') > 0 ? 'opera' : uaLowerCase.indexOf('msie') > 0 || w.MSStream ? 'ie' : 'unknow'; + return /Edge\/\d./i.test(ua) ? 'edge' : uaLowerCase.indexOf('chrome') > 0 ? 'chrome' : uaLowerCase.indexOf('firefox') > 0 ? 'firefox' : uaLowerCase.indexOf('safari') > 0 ? 'safari' : uaLowerCase.indexOf('opera') > 0 ? 'opera' : uaLowerCase.indexOf('msie') > 0 || ws.MSStream ? 'ie' : 'unknow'; }, - getOS: function () { - const w = window.top; - const ua = w.navigator.userAgent; + getOS() { + const ws = utils.getWindowSelf(); + const ua = ws.navigator.userAgent; const uaLowerCase = ua.toLowerCase(); return uaLowerCase.indexOf('linux') > 0 ? 'linux' : uaLowerCase.indexOf('mac') > 0 ? 'mac' : uaLowerCase.indexOf('win') > 0 ? 'windows' : ''; + }, + + getUrl(refererInfo) { + // top has not been reached, it means we are not sure + // to get the proper page url. + if (!refererInfo.reachedTop) { + return; + } + return refererInfo.referer; + }, + + getUrlFromParams(params) { + const { postBidOptions } = params; + if (postBidOptions && postBidOptions.url) { + return postBidOptions.url; + } } -} +}; -function _pushInAdagioQueue(ob) { - try { - if (!canAccessTopWindow()) return; - const w = utils.getWindowTop(); - w.ADAGIO.queue.push(ob); - } catch (e) {} +function enqueue(ob) { + const w = internal.getCurrentWindow(); + + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.queue = w.ADAGIO.queue || []; + w.ADAGIO.queue.push(ob); }; -function _getOrAddAdagioAdUnit(adUnitCode) { - const w = utils.getWindowTop(); +function getOrAddAdagioAdUnit(adUnitCode) { + const w = internal.getCurrentWindow(); + + w.ADAGIO = w.ADAGIO || {}; + if (w.ADAGIO.adUnits[adUnitCode]) { return w.ADAGIO.adUnits[adUnitCode] } + return w.ADAGIO.adUnits[adUnitCode] = {}; -} +}; + +function getPageviewId() { + const w = internal.getCurrentWindow(); + + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.pageviewId = w.ADAGIO.pageviewId || utils.generateUUID(); + + return w.ADAGIO.pageviewId; +}; -function _computePrintNumber(adUnitCode) { +function computePrintNumber(adUnitCode) { let printNumber = 1; - const w = utils.getWindowTop(); + const w = internal.getCurrentWindow(); + if ( w.ADAGIO && w.ADAGIO.adUnits && w.ADAGIO.adUnits[adUnitCode] && - w.ADAGIO.adUnits[adUnitCode].pageviewId === _getPageviewId() && + w.ADAGIO.adUnits[adUnitCode].pageviewId === internal.getPageviewId() && w.ADAGIO.adUnits[adUnitCode].printNumber ) { printNumber = parseInt(w.ADAGIO.adUnits[adUnitCode].printNumber, 10) + 1; } + return printNumber; -} +}; -function _getDevice() { +function getDevice() { const language = navigator.language ? 'language' : 'userLanguage'; return { userAgent: navigator.userAgent, @@ -246,35 +376,63 @@ function _getDevice() { }; }; -function _getSite() { - const w = utils.getWindowTop(); +function getSite(bidderRequest) { + let domain = ''; + let page = ''; + let referrer = ''; + + const { refererInfo } = bidderRequest; + + if (canAccessTopWindow()) { + const wt = utils.getWindowTop(); + domain = wt.location.hostname; + page = wt.location.href; + referrer = wt.document.referrer || ''; + } else if (refererInfo.reachedTop) { + const url = utils.parseUrl(refererInfo.referer); + domain = url.hostname; + page = refererInfo.referer; + } else if (refererInfo.stack && refererInfo.stack.length && refererInfo.stack[0]) { + // important note check if refererInfo.stack[0] is 'thruly' because a `null` value + // will be considered as "localhost" by the parseUrl function. + // As the isBidRequestValid returns false when it does not reach the referer + // this should never called. + const url = utils.parseUrl(refererInfo.stack[0]); + domain = url.hostname; + } + return { - domain: w.location.hostname, - page: w.location.href, - referrer: w.document.referrer || '' + domain, + page, + referrer }; }; -function _getPageviewId() { - if (!canAccessTopWindow()) return false; - const w = utils.getWindowTop(); - w.ADAGIO.pageviewId = w.ADAGIO.pageviewId || utils.generateUUID(); - return w.ADAGIO.pageviewId; -}; +function getElementFromTopWindow(element, currentWindow) { + try { + if (utils.getWindowTop() === currentWindow) { + if (!element.getAttribute('id')) { + element.setAttribute('id', `adg-${utils.getUniqueIdentifierStr()}`); + } + return element; + } else { + const frame = currentWindow.frameElement; + const frameClientRect = frame.getBoundingClientRect(); + const elementClientRect = element.getBoundingClientRect(); + + if (frameClientRect.width !== elementClientRect.width || frameClientRect.height !== elementClientRect.height) { + return false; + } -function _getElementFromTopWindow(element, currentWindow) { - if (utils.getWindowTop() === currentWindow) { - if (!element.getAttribute('id')) { - element.setAttribute('id', `adg-${utils.getUniqueIdentifierStr()}`); + return getElementFromTopWindow(frame, currentWindow.parent); } - return element; - } else { - const frame = currentWindow.frameElement; - return _getElementFromTopWindow(frame, currentWindow.parent); + } catch (err) { + utils.logWarn(`${LOG_PREFIX}`, err); + return false; } -} +}; -export function _autoDetectAdUnitElementId(adUnitCode) { +function autoDetectAdUnitElementId(adUnitCode) { const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode) let adUnitElementId = null; @@ -283,9 +441,9 @@ export function _autoDetectAdUnitElementId(adUnitCode) { } return adUnitElementId; -} +}; -function _autoDetectEnvironment() { +function autoDetectEnvironment() { const device = _features.getDevice(); let environment; switch (device) { @@ -300,53 +458,37 @@ function _autoDetectEnvironment() { break; }; return environment -} +}; -/** - * Returns all features for a specific adUnit element - * - * @param {Object} bidRequest - * @returns {Object} features for an element (see specs) - */ -function _getFeatures(bidRequest) { - if (!canAccessTopWindow()) return; - const w = utils.getWindowTop(); - const adUnitCode = bidRequest.adUnitCode; - const adUnitElementId = bidRequest.params.adUnitElementId || _autoDetectAdUnitElementId(adUnitCode); - let element; +function getFeatures(bidRequest, bidderRequest) { + const { adUnitCode, params } = bidRequest; + const { adUnitElementId } = params; + const { refererInfo } = bidderRequest; if (!adUnitElementId) { - utils.logWarn('Unable to detect adUnitElementId. Adagio measures won\'t start'); - } else { - if (bidRequest.params.postBid === true) { - window.document.getElementById(adUnitElementId); - element = _getElementFromTopWindow(element, window); - w.ADAGIO.pbjsAdUnits.map((adUnit) => { - if (adUnit.code === adUnitCode) { - const outerElementId = element.getAttribute('id'); - adUnit.outerAdUnitElementId = outerElementId; - bidRequest.params.outerAdUnitElementId = outerElementId; - } - }); - } else { - element = w.document.getElementById(adUnitElementId); - } + utils.logWarn(`${LOG_PREFIX} unable to get params.adUnitElementId. Continue without tiv.`); } const features = { - print_number: _features.getPrintNumber(bidRequest.adUnitCode).toString(), + print_number: _features.getPrintNumber(adUnitCode).toString(), page_dimensions: _features.getPageDimensions().toString(), viewport_dimensions: _features.getViewPortDimensions().toString(), - dom_loading: _features.isDomLoading().toString(), + dom_loading: _features.getDomLoadingDuration().toString(), // layout: features.getLayout().toString(), - adunit_position: _features.getSlotPosition(element).toString(), - user_timestamp: _features.getTimestamp().toString(), + adunit_position: _features.getSlotPosition(params).toString(), + user_timestamp: _features.getTimestampUTC().toString(), device: _features.getDevice().toString(), - url: w.location.origin + w.location.pathname, + url: _features.getUrl(refererInfo) || _features.getUrlFromParams(params) || '', browser: _features.getBrowser(), os: _features.getOS() }; + Object.keys(features).forEach((prop) => { + if (features[prop] === '') { + delete features[prop]; + } + }); + const adUnitFeature = {}; adUnitFeature[adUnitElementId] = { @@ -354,7 +496,7 @@ function _getFeatures(bidRequest) { version: FEATURES_VERSION }; - _pushInAdagioQueue({ + internal.enqueue({ action: 'features', ts: Date.now(), data: adUnitFeature @@ -363,22 +505,53 @@ function _getFeatures(bidRequest) { return features; }; +export const internal = { + enqueue, + getOrAddAdagioAdUnit, + getPageviewId, + computePrintNumber, + getDevice, + getSite, + getElementFromTopWindow, + autoDetectAdUnitElementId, + autoDetectEnvironment, + getFeatures, + getRefererInfo, + adagioScriptFromLocalStorageCb, + getCurrentWindow, + canAccessTopWindow +}; + function _getGdprConsent(bidderRequest) { + if (!utils.deepAccess(bidderRequest, 'gdprConsent')) { + return false; + } + + const { + apiVersion, + gdprApplies, + consentString, + allowAuctionWithoutConsent + } = bidderRequest.gdprConsent; + const consent = {}; - if (utils.deepAccess(bidderRequest, 'gdprConsent')) { - if (bidderRequest.gdprConsent.consentString !== undefined) { - consent.consentString = bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.gdprApplies !== undefined) { - consent.consentRequired = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - if (bidderRequest.gdprConsent.allowAuctionWithoutConsent !== undefined) { - consent.allowAuctionWithoutConsent = bidderRequest.gdprConsent.allowAuctionWithoutConsent ? 1 : 0; - } - if (bidderRequest.gdprConsent.apiVersion !== undefined) { - consent.apiVersion = bidderRequest.gdprConsent.apiVersion; - } + + if (apiVersion !== undefined) { + consent.apiVersion = apiVersion + } + + if (consentString !== undefined) { + consent.consentString = consentString; + } + + if (gdprApplies !== undefined) { + consent.consentRequired = (gdprApplies) ? 1 : 0; } + + if (allowAuctionWithoutConsent !== undefined) { + consent.allowAuctionWithoutConsent = allowAuctionWithoutConsent ? 1 : 0; + } + return consent; } @@ -391,84 +564,103 @@ function _getSchain(bidRequest) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaType: SUPPORTED_MEDIA_TYPES, + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, - isBidRequestValid: function (bid) { + isBidRequestValid(bid) { const { adUnitCode, auctionId, sizes, bidder, params, mediaTypes } = bid; - const { organizationId, site, placement } = bid.params; - const adUnitElementId = bid.params.adUnitElementId || _autoDetectAdUnitElementId(adUnitCode); - const environment = bid.params.environment || _autoDetectEnvironment(); - let isValid = false; + if (!params) { + utils.logWarn(`${LOG_PREFIX} the "params" property is missing.`); + return false; + } - utils.logInfo('adUnitElementId', adUnitElementId) + const { organizationId, site, placement } = params; + const adUnitElementId = params.adUnitElementId || internal.autoDetectAdUnitElementId(adUnitCode); + const environment = params.environment || internal.autoDetectEnvironment(); - try { - if (canAccessTopWindow()) { - const w = utils.getWindowTop(); - w.ADAGIO = w.ADAGIO || {}; - w.ADAGIO.adUnits = w.ADAGIO.adUnits || {}; - w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits || []; - isValid = !!(organizationId && site && placement); - - const tempAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode); - - bid.params = { - ...bid.params, - adUnitElementId, - environment - } + // insure auto-detected params are kept in `bid` object. + bid.params = { + ...params, + adUnitElementId, + environment + } - tempAdUnits.push({ - code: adUnitCode, - sizes: (mediaTypes && mediaTypes.banner && Array.isArray(mediaTypes.banner.sizes)) ? mediaTypes.banner.sizes : sizes, - bids: [{ - bidder, - params - }] - }); - w.ADAGIO.pbjsAdUnits = tempAdUnits; - - if (isValid === true) { - let printNumber = _computePrintNumber(adUnitCode); - w.ADAGIO.adUnits[adUnitCode] = { - auctionId: auctionId, - pageviewId: _getPageviewId(), - printNumber - }; - } + const debugData = () => ({ + action: 'pb-dbg', + ts: Date.now(), + data: { + bid } - } catch (e) { - return isValid; + }); + + const refererInfo = internal.getRefererInfo(); + + if (!refererInfo.reachedTop) { + utils.logWarn(`${LOG_PREFIX} the main page url is unreachabled.`); + internal.enqueue(debugData()); + + return false; + } else if (!(organizationId && site && placement)) { + utils.logWarn(`${LOG_PREFIX} at least one required param is missing.`); + internal.enqueue(debugData()); + + return false; } - return isValid; - }, - buildRequests: function (validBidRequests, bidderRequest) { - // AdagioBidAdapter works when window.top can be reached only - if (!bidderRequest.refererInfo.reachedTop) return []; + const w = internal.getCurrentWindow(); + const pageviewId = internal.getPageviewId(); + const printNumber = internal.computePrintNumber(adUnitCode); + + // Store adUnits config. + // If an adUnitCode has already been stored, it will be replaced. + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode) + w.ADAGIO.pbjsAdUnits.push({ + code: adUnitCode, + mediaTypes: mediaTypes || {}, + sizes: (mediaTypes && mediaTypes.banner && Array.isArray(mediaTypes.banner.sizes)) ? mediaTypes.banner.sizes : sizes, + bids: [{ + bidder, + params: bid.params // use the updated bid.params object with auto-detected params + }], + auctionId, + pageviewId, + printNumber + }); + + // (legacy) Store internal adUnit information + w.ADAGIO.adUnits[adUnitCode] = { + auctionId, + pageviewId, + printNumber, + }; + + return true; + }, + buildRequests(validBidRequests, bidderRequest) { const secure = (location.protocol === 'https:') ? 1 : 0; - const device = _getDevice(); - const site = _getSite(); - const pageviewId = _getPageviewId(); - const gdprConsent = _getGdprConsent(bidderRequest); + const device = internal.getDevice(); + const site = internal.getSite(bidderRequest); + const pageviewId = internal.getPageviewId(); + const gdprConsent = _getGdprConsent(bidderRequest) || {}; const schain = _getSchain(validBidRequests[0]); const adUnits = utils._map(validBidRequests, (bidRequest) => { - bidRequest.features = _getFeatures(bidRequest); + bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); return bidRequest; }); - // Regroug ad units by siteId + // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - if (adUnit.params && adUnit.params.organizationId) { - adUnit.params.organizationId = adUnit.params.organizationId.toString(); - } - (groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []).push(adUnit); + adUnit.params.organizationId = adUnit.params.organizationId.toString(); + + groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || [] + groupedAdUnits[adUnit.params.organizationId].push(adUnit); + return groupedAdUnits; }, {}); - // Build one request per siteId - const requests = utils._map(Object.keys(groupedAdUnits), (organizationId) => { + // Build one request per organizationId + const requests = utils._map(Object.keys(groupedAdUnits), organizationId => { return { method: 'POST', url: ENDPOINT, @@ -495,13 +687,13 @@ export const spec = { return requests; }, - interpretResponse: function (serverResponse, bidRequest) { + interpretResponse(serverResponse, bidRequest) { let bidResponses = []; try { const response = serverResponse.body; if (response) { if (response.data) { - _pushInAdagioQueue({ + internal.enqueue({ action: 'ssp-data', ts: Date.now(), data: response.data @@ -528,18 +720,20 @@ export const spec = { return bidResponses; }, - getUserSyncs: function (syncOptions, serverResponses) { + getUserSyncs(syncOptions, serverResponses) { if (!serverResponses.length || serverResponses[0].body === '' || !serverResponses[0].body.userSyncs) { return false; } - const syncs = serverResponses[0].body.userSyncs.map((sync) => { - return { - type: sync.t === 'p' ? 'image' : 'iframe', - url: sync.u - } - }) + + const syncs = serverResponses[0].body.userSyncs.map(sync => ({ + type: sync.t === 'p' ? 'image' : 'iframe', + url: sync.u + })); + return syncs; - } -} + }, +}; + +initAdagio(); registerBidder(spec); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 97265121ce0..52b99f274d5 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,678 +1,586 @@ +import find from 'core-js-pure/features/array/find.js'; import { expect } from 'chai'; -import { adagioScriptFromLocalStorageCb, _features, spec } from 'modules/adagioBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as utils from 'src/utils.js'; +import { _features, internal as adagio, adagioScriptFromLocalStorageCb, getAdagioScript, storage, spec, ENDPOINT, VERSION } from '../../../modules/adagioBidAdapter.js'; +import { loadExternalScript } from '../../../src/adloader.js'; +import * as utils from '../../../src/utils.js'; + +const BidRequestBuilder = function BidRequestBuilder(options) { + const defaults = { + request: { + auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', + adUnitCode: 'adunit-code', + bidder: 'adagio' + }, + params: { + organizationId: '1000', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + adUnitElementId: 'gpt-adunit-code' + }, + sizes: [[300, 250], [300, 600]], + }; + + const request = { + ...defaults.request, + ...options + }; + + this.withParams = (options) => { + request.params = { + ...defaults.params, + ...options + }; + return this; + }; + + this.build = () => request; +}; + +const BidderRequestBuilder = function BidderRequestBuilder(options) { + const defaults = { + bidderCode: 'adagio', + auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', + bidderRequestId: '7g36s867Tr4xF90X', + timeout: 3000, + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'http://test.io/index.html?pbjs_debug=true' + } + }; -describe('adagioAdapter', () => { - let utilsMock; - const adapter = newBidder(spec); - const ENDPOINT = 'https://mp.4dex.io/prebid'; - const VERSION = '2.2.2'; + const request = { + ...defaults, + ...options + }; - beforeEach(function() { - localStorage.removeItem('adagioScript'); - utilsMock = sinon.mock(utils); - }); + this.build = () => request; +}; - afterEach(function() { - utilsMock.restore(); - }); - - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); +describe('Adagio bid adapter', () => { + let adagioMock; + let utilsMock; + let sandbox; + + const fixtures = { + getElementById(width, height, x, y) { + const obj = { + x: x || 800, + y: y || 300, + width: width || 300, + height: height || 250, + }; - describe('isBidRequestValid', () => { - let sandbox; - beforeEach(function () { - sandbox = sinon.sandbox.create(); - let element = { - x: 0, - y: 0, - width: 200, - height: 300, + return { + ...obj, getBoundingClientRect: () => { return { - width: element.width, - height: element.height, - left: element.x, - top: element.y, - right: element.x + element.width, - bottom: element.y + element.height + width: obj.width, + height: obj.height, + left: obj.x, + top: obj.y, + right: obj.x + obj.width, + bottom: obj.y + obj.height }; } }; - sandbox.stub(document, 'getElementById').withArgs('banner-atf').returns(element); - }); + } + }; - afterEach(function () { - sandbox.restore(); - }); + // safeFrame implementation + const $sf = { + ext: { + geom: function() {} + } + }; + + beforeEach(() => { + window.ADAGIO = {}; + window.ADAGIO.adUnits = {}; + window.ADAGIO.pbjsAdUnits = []; + window.ADAGIO.queue = []; + window.ADAGIO.versions = {}; + window.ADAGIO.versions.adagioBidderAdapter = VERSION; + window.ADAGIO.pageviewId = 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a'; + + adagioMock = sinon.mock(adagio); + utilsMock = sinon.mock(utils); - const bid = { - 'bidder': 'adagio', - 'params': { - organizationId: '0', - placement: 'PAVE_ATF', - site: 'SITE-NAME', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - }; + sandbox = sinon.sandbox.create(); + }); - const bidWithMediaTypes = { - 'bidder': 'adagio', - 'params': { - organizationId: '0', - placement: 'PAVE_ATF', - site: 'SITE-NAME', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf' - }, - 'adUnitCode': 'adunit-code-2', - 'mediaTypes': { - banner: { - sizes: [[300, 250]], - } - }, - sizes: [[300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - } + afterEach(() => { + window.ADAGIO = undefined; - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(1); - }) + adagioMock.restore(); + utilsMock.restore(); - it('should compute a printNumber for the new bid request on same adUnitCode and same pageviewId', () => { - spec.isBidRequestValid(bid); - expect(window.top.ADAGIO.adUnits).ok; - expect(window.top.ADAGIO.adUnits['adunit-code']).ok; - expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(2); + sandbox.restore(); + }); - spec.isBidRequestValid(bid); - expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(3); + describe('isBidRequestValid()', function() { + it('should return true when required params have been found', function() { + const bid = new BidRequestBuilder().withParams().build(); - window.top.ADAGIO.pageviewId = 123; - spec.isBidRequestValid(bid); - expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(1); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when organization params is not passed', () => { - const bidTest = utils.deepClone(bid); - delete bidTest.params.organizationId; - expect(spec.isBidRequestValid(bidTest)).to.equal(false); - }); + it('should return false if bid.params is missing', function() { + sandbox.spy(utils, 'logWarn'); + const bid01 = new BidRequestBuilder().build(); - it('should return false when site params is not passed', () => { - const bidTest = utils.deepClone(bid); - delete bidTest.params.site; - expect(spec.isBidRequestValid(bidTest)).to.equal(false); + expect(spec.isBidRequestValid(bid01)).to.equal(false); + sinon.assert.callCount(utils.logWarn, 1); }); - it('should return false when placement params is not passed', () => { - const bidTest = utils.deepClone(bid); - delete bidTest.params.placement; - expect(spec.isBidRequestValid(bidTest)).to.equal(false); - }); + it('should return false when a required param is missing', function() { + const bid01 = new BidRequestBuilder({ params: { + organizationId: '1000', + placement: 'PAVE_ATF' + }}).build(); - it('should add autodetected adUnitElementId and environment params', () => { - const bidTest = utils.deepClone(bid); - delete bidTest.params.adUnitElementId; - delete bidTest.params.environment; - sandbox.stub(utils, 'getGptSlotInfoForAdUnitCode').returns({divId: 'banner-atf'}); - sandbox.stub(_features, 'getDevice').returns(4); - spec.isBidRequestValid(bidTest) - expect(bidTest.params.adUnitElementId).to.equal('banner-atf'); - expect(bidTest.params.environment).to.equal('mobile'); - }) + const bid02 = new BidRequestBuilder({ params: { + organizationId: '1000', + site: 'SITE-NAME' + }}).build(); - it('should add autodetected tablet environment params', () => { - const bidTest = utils.deepClone(bid); - delete bidTest.params.environment; - sandbox.stub(_features, 'getDevice').returns(5); - spec.isBidRequestValid(bidTest) - expect(bidTest.params.environment).to.equal('tablet'); - }) + const bid03 = new BidRequestBuilder({ params: { + placement: 'PAVE_ATF', + site: 'SITE-NAME' + }}).build(); + + expect(spec.isBidRequestValid(bid01)).to.equal(false); + expect(spec.isBidRequestValid(bid02)).to.equal(false); + expect(spec.isBidRequestValid(bid03)).to.equal(false); + }); + + it('should return false when refererInfo.reachedTop is false', function() { + sandbox.spy(utils, 'logWarn'); + sandbox.stub(adagio, 'getRefererInfo').returns({ reachedTop: false }); + const bid = new BidRequestBuilder().withParams().build(); - it('should return false if not in the window.top', () => { - sandbox.stub(utils, 'getWindowTop').throws(); expect(spec.isBidRequestValid(bid)).to.equal(false); + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, 'Adagio: the main page url is unreachabled.'); }); - it('should expose ADAGIO.pbjsAdUnits in window', () => { - spec.isBidRequestValid(bidWithMediaTypes); + it('should log warning and enqueue the bid object in ADAGIO.queue when isBidRequestValid is false', function() { + sandbox.stub(Date, 'now').returns(12345); + sandbox.spy(utils, 'logWarn'); + sandbox.spy(adagio, 'enqueue'); + + const bid = new BidRequestBuilder({'params': { + organizationId: '1000', + placement: 'PAVE_ATF' + }}).build(); + + const expectedEnqueued = { + action: 'pb-dbg', + ts: 12345, + data: { bid } + }; + spec.isBidRequestValid(bid); - expect(window.top.ADAGIO.pbjsAdUnits).ok; - expect(window.top.ADAGIO.pbjsAdUnits).to.have.lengthOf(2); - const adUnitWithMediaTypeSizes = window.top.ADAGIO.pbjsAdUnits.filter((aU) => aU.code === 'adunit-code-2')[0]; - const adUnitWithSizes = window.top.ADAGIO.pbjsAdUnits.filter((aU) => aU.code === 'adunit-code')[0]; - expect(adUnitWithMediaTypeSizes.sizes).to.eql([[300, 250]]); - expect(adUnitWithSizes.sizes).to.eql([[300, 250], [300, 600]]); + + sinon.assert.calledWith(adagio.enqueue, expectedEnqueued); + sinon.assert.callCount(utils.logWarn, 1); }); - }); - describe('buildRequests', () => { - const sandbox = sinon.createSandbox(); + describe('Store ADAGIO global in window.top or window.self depending on context', function() { + const bid01 = new BidRequestBuilder({ + adUnitCode: 'adunit-code-01', + sizes: [[300, 250], [300, 600]] + }).withParams().build(); - const banner300x250 = { - x: 0, - y: 0, - width: 300, - height: 250, - getBoundingClientRect: () => { - return { - width: banner300x250.width, - height: banner300x250.height, - left: banner300x250.x, - top: banner300x250.y, - right: banner300x250.x + banner300x250.width, - bottom: banner300x250.y + banner300x250.height - }; - }, - }; + const bid02 = new BidRequestBuilder({ + adUnitCode: 'adunit-code-02', + mediaTypes: { + banner: { sizes: [[300, 250]] } + }, + }).withParams().build(); - const banner300x600 = { - x: 0, - y: 0, - width: 300, - height: 600, - getBoundingClientRect: () => { - return { - width: banner300x600.width, - height: banner300x600.height, - left: banner300x600.x, - top: banner300x600.y, - right: banner300x600.x + banner300x600.width, - bottom: banner300x600.y + banner300x600.height - }; - }, - }; + const bid03 = new BidRequestBuilder({ + adUnitCode: 'adunit-code-02', + mediaTypes: { + banner: { sizes: [[300, 600]] } + }, + }).withParams().build(); + + const expected = [ + { + code: 'adunit-code-01', + sizes: [[300, 250], [300, 600]], + mediaTypes: {}, + bids: [{ + bidder: 'adagio', + params: { + organizationId: '1000', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + adUnitElementId: 'gpt-adunit-code', + environment: 'desktop' + } + }], + auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', + pageviewId: 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a', + printNumber: 1, + }, + { + code: 'adunit-code-02', + sizes: [[300, 600]], + mediaTypes: { + banner: { sizes: [[300, 600]] } + }, + bids: [{ + bidder: 'adagio', + params: { + organizationId: '1000', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + adUnitElementId: 'gpt-adunit-code', + environment: 'desktop' + } + }], + auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', + pageviewId: 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a', + printNumber: 2, + } + ]; - const computedStyleBlock = { - display: 'block' - }; + it('should store bids config once by bid in window.top if it accessible', function() { + sandbox.stub(adagio, 'getCurrentWindow').returns(window.top); - const computedStyleNone = { - display: 'none' - }; + // replace by the values defined in beforeEach + window.top.ADAGIO = { + ...window.ADAGIO + } - const stubs = { - topGetElementById: undefined, - topGetComputedStyle: undefined - } + spec.isBidRequestValid(bid01); + spec.isBidRequestValid(bid02); + spec.isBidRequestValid(bid03); - top.ADAGIO = top.ADAGIO || {}; - top.ADAGIO.adUnits = top.ADAGIO.adUnits || {}; - top.ADAGIO.pbjsAdUnits = top.ADAGIO.pbjsAdUnits || []; + expect(find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01')).to.deep.eql(expected[0]); + expect(find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-02')).to.deep.eql(expected[1]); + }); - beforeEach(function () { - stubs.topGetElementById = sandbox.stub(top.document, 'getElementById'); - stubs.topGetComputedStyle = sandbox.stub(top, 'getComputedStyle'); + it('should store bids config once by bid in current window', function() { + sandbox.stub(adagio, 'getCurrentWindow').returns(window.self); - stubs.topGetElementById.withArgs('banner-atf-123').returns(banner300x250); - stubs.topGetElementById.withArgs('banner-atf-456').returns(banner300x600); - stubs.topGetElementById.withArgs('does-not-exist').returns(null); - stubs.topGetComputedStyle.returns(computedStyleBlock); - }); + spec.isBidRequestValid(bid01); + spec.isBidRequestValid(bid02); + spec.isBidRequestValid(bid03); - afterEach(function () { - sandbox.restore(); + expect(find(window.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01')).to.deep.eql(expected[0]); + expect(find(window.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-02')).to.deep.eql(expected[1]); + }); }); + }); - after(function() { - sandbox.reset(); - }) - - let bidRequests = [ - { - 'bidder': 'adagio', - 'params': { - organizationId: '123', - site: 'ADAGIO-123', - placement: 'PAVE_ATF-123', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf-123' - }, - 'adUnitCode': 'adunit-code1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - }, - { - 'bidder': 'adagio', - 'params': { - organizationId: '123', - site: 'ADAGIO-123', - placement: 'PAVE_ATF-123', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf-123' - }, - 'adUnitCode': 'adunit-code2', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - }, - { - 'bidder': 'adagio', - 'params': { - organizationId: '456', - site: 'ADAGIO-456', - placement: 'PAVE_ATF-456', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf-456' - }, - 'adUnitCode': 'adunit-code3', - 'mediaTypes': { - banner: { - sizes: [[300, 250]] - } - }, - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - } + describe('buildRequests()', function() { + const expectedDataKeys = [ + 'id', + 'organizationId', + 'secure', + 'device', + 'site', + 'pageviewId', + 'adUnits', + 'gdpr', + 'schain', + 'prebidVersion', + 'adapterVersion', + 'featuresVersion' ]; - const bidRequestsWithPostBid = [ - { - 'bidder': 'adagio', - 'params': { - organizationId: '456', - site: 'ADAGIO-456', - placement: 'PAVE_ATF-456', - pagetype: 'ARTICLE', - adUnitElementId: 'banner-atf-456', - postBid: true - }, - 'adUnitCode': 'adunit-code3', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - } - ]; + it('groups requests by organizationId', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + const bid02 = new BidRequestBuilder().withParams().build(); + const bid03 = new BidRequestBuilder().withParams({ + organizationId: '1002' + }).build(); - let consentString = 'theConsentString'; - let bidderRequest = { - 'bidderCode': 'adagio', - 'auctionId': '12jejebn', - 'bidderRequestId': 'hehehehbeheh', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - gdprApplies: true, - allowAuctionWithoutConsent: true, - apiVersion: 1, - }, - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'http://test.io/index.html?pbjs_debug=true' - } - }; + const bidderRequest = new BidderRequestBuilder().build(); - let bidderRequestTCF2 = { - 'bidderCode': 'adagio', - 'auctionId': '12jejebn', - 'bidderRequestId': 'hehehehbeheh', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - vendorData: { - tcString: consentString, - gdprApplies: true - }, - gdprApplies: true, - apiVersion: 2 - }, - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'http://test.io/index.html?pbjs_debug=true' - } - }; + const requests = spec.buildRequests([bid01, bid02, bid03], bidderRequest); - it('groups requests by siteId', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests).to.have.lengthOf(2); - expect(requests[0].data.organizationId).to.equal('123'); + expect(requests[0].data.organizationId).to.equal('1000'); expect(requests[0].data.adUnits).to.have.lengthOf(2); - expect(requests[1].data.organizationId).to.equal('456'); + expect(requests[1].data.organizationId).to.equal('1002'); expect(requests[1].data.adUnits).to.have.lengthOf(1); }); - it('sends bid request to ENDPOINT_PB via POST', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(ENDPOINT); - expect(request.data.prebidVersion).to.equal('$prebid.version$'); - }); + it('should send bid request to ENDPOINT_PB via POST', function() { + sandbox.stub(adagio, 'getDevice').returns({ a: 'a' }); + sandbox.stub(adagio, 'getSite').returns({ domain: 'adagio.io', 'page': 'https://adagio.io/hb' }); + sandbox.stub(adagio, 'getPageviewId').returns('1234-567'); + sandbox.stub(adagio, 'getFeatures').returns({}); - it('features params "adunit_position" must be empty if adUnitElement is not found in the DOM', () => { - const requests = spec.buildRequests([Object.assign({}, bidRequests[0], {params: {adUnitElementId: 'does-not-exist'}})], bidderRequest); - const request = requests[0]; - expect(request.data.adUnits[0].features).to.exist; - expect(request.data.adUnits[0].features.adunit_position).to.deep.equal(''); - }); + const bid01 = new BidRequestBuilder().withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); - it('features params "adunit_position" should be computed even if DOM element is display:none', () => { - stubs.topGetComputedStyle.returns(computedStyleNone); - const requests = spec.buildRequests([Object.assign({}, bidRequests[0])], bidderRequest); - let request = requests[0]; - expect(request.data.adUnits[0].features).to.exist; - expect(request.data.adUnits[0].features.adunit_position).to.equal('0x0'); - }); + const requests = spec.buildRequests([bid01], bidderRequest); - it('features params "viewport" should be computed even if window.innerWidth is not supported', () => { - sandbox.stub(top, 'innerWidth').value(undefined); - const requests = spec.buildRequests([Object.assign({}, bidRequests[0])], bidderRequest); - let request = requests[0]; - expect(request.data.adUnits[0].features).to.exist; - expect(request.data.adUnits[0].features.viewport_dimensions).to.match(/^[\d]+x[\d]+$/); + expect(requests).to.have.lengthOf(1); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].options.contentType).to.eq('text/plain'); + expect(requests[0].data).to.have.all.keys(expectedDataKeys); }); - it('AdUnit requested should have the correct sizes array depending on the config', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[1].data.adUnits[0]).to.have.property('mediaTypes'); - }); + it('should enqueue computed features for collect usage', function() { + sandbox.stub(Date, 'now').returns(12345); - it('features params must be an object if featurejs is loaded', () => { - let requests = spec.buildRequests(bidRequests, bidderRequest); - let request = requests[0]; - expect(request.data.adUnits[0].features).to.exist; - }); + for (const prop in _features) { + sandbox.stub(_features, prop).returns(''); + } - it('outerAdUnitElementId must be added when PostBid param has been set', () => { - top.ADAGIO = top.ADAGIO || {}; - top.ADAGIO.pbjsAdUnits = []; + adagioMock.expects('enqueue').withExactArgs({ + action: 'features', + ts: 12345, + data: { + 'gpt-adunit-code': { + features: {}, + version: '1' + } + } + }).atLeast(1); - top.ADAGIO.pbjsAdUnits.push({ - code: bidRequestsWithPostBid[0].adUnitCode, - sizes: bidRequestsWithPostBid[0].sizes, - bids: [{ - bidder: bidRequestsWithPostBid[0].bidder, - params: bidRequestsWithPostBid[0].params - }] - }); - let requests = spec.buildRequests(bidRequestsWithPostBid, bidderRequest); - let request = requests[0]; - expect(request.data.adUnits[0].features).to.exist; - expect(request.data.adUnits[0].params.outerAdUnitElementId).to.exist; - top.ADAGIO.pbjsAdUnits = undefined; - }); + const bid01 = new BidRequestBuilder().withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); - it('generates a pageviewId if missing', () => { - window.top.ADAGIO = window.top.ADAGIO || {}; - delete window.top.ADAGIO.pageviewId; + const requests = spec.buildRequests([bid01], bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); + expect(requests[0].data).to.have.all.keys(expectedDataKeys); - expect(requests[0].data.pageviewId).to.exist.and.to.not.equal('_').and.to.not.equal(''); - expect(requests[0].data.pageviewId).to.equal(requests[1].data.pageviewId); + adagioMock.verify(); }); - it('uses an existing pageviewId if present', () => { - window.top.ADAGIO = window.top.ADAGIO || {}; - window.top.ADAGIO.pageviewId = 'abc'; + it('should filter some props in case refererDetection.reachedTop is false', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + const bidderRequest = new BidderRequestBuilder({ + refererInfo: { + numIframes: 2, + reachedTop: false, + referer: 'http://example.com/iframe1.html', + stack: [ + null, + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ], + canonicalUrl: '' + } + }).build(); - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); + const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.pageviewId).to.equal('abc'); - expect(requests[1].data.pageviewId).to.equal('abc'); + expect(requests).to.have.lengthOf(1); + expect(requests[0].data).to.have.all.keys(expectedDataKeys); + expect(requests[0].data.adUnits[0].features).to.exist; + expect(requests[0].data.adUnits[0].features.url).to.not.exist; }); - it('should send the printNumber in features object', () => { - window.top.ADAGIO = window.top.ADAGIO || {}; - window.top.ADAGIO.pageviewId = 'abc'; - window.top.ADAGIO.adUnits['adunit-code1'] = { - pageviewId: 'abc', - printNumber: 2 + describe('with sChain', function() { + const schain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'ssp.test', + sid: '00001', + hp: 1 + }] }; - const requests = spec.buildRequests([bidRequests[0]], bidderRequest); - const request = requests[0]; - expect(request.data.adUnits[0].features.print_number).to.equal('2'); - }); - it('organizationId param key must be a string', () => { - const requests = spec.buildRequests([Object.assign({}, bidRequests[0], {params: {organizationId: 1010}})], bidderRequest); - const request = requests[0]; - expect(request.data.adUnits[0].params).to.exist; - expect(request.data.adUnits[0].params.organizationId).to.deep.equal('1010'); - expect(request.data.organizationId).to.exist; - expect(request.data.organizationId).to.deep.equal('1010'); - }); + it('should add the schain if available at bidder level', function() { + const bid01 = new BidRequestBuilder({ schain }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); - it('GDPR consent is applied', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); - expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(1); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(1); - }); + const requests = spec.buildRequests([bid01], bidderRequest); - it('GDPR consent is applied w/ TCF2', () => { - const requests = spec.buildRequests(bidRequests, bidderRequestTCF2); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); - expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(1); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(2); - }); + expect(requests[0].data).to.have.all.keys(expectedDataKeys); + expect(requests[0].data.schain).to.deep.equal(schain); + }); - it('GDPR consent is not applied', () => { - bidderRequest.gdprConsent.gdprApplies = false; - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); - expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(0); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(1); - }); + it('Schain should not be added to the request', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); - it('GDPR consent is not applied w/ TCF2', () => { - bidderRequestTCF2.gdprConsent.gdprApplies = false; - const requests = spec.buildRequests(bidRequests, bidderRequestTCF2); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr.consentString).to.exist.and.to.equal(consentString); - expect(request.data.gdpr.consentRequired).to.exist.and.to.equal(0); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(2); - }); - - it('GDPR consent is undefined', () => { - delete bidderRequest.gdprConsent.consentString; - delete bidderRequest.gdprConsent.gdprApplies; - delete bidderRequest.gdprConsent.allowAuctionWithoutConsent; - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr).to.not.have.property('consentString'); - expect(request.data.gdpr).to.not.have.property('gdprApplies'); - expect(request.data.gdpr).to.not.have.property('allowAuctionWithoutConsent'); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(1); - }); - - it('GDPR consent is undefined w/ TCF2', () => { - delete bidderRequestTCF2.gdprConsent.consentString; - delete bidderRequestTCF2.gdprConsent.gdprApplies; - delete bidderRequestTCF2.gdprConsent.vendorData; - const requests = spec.buildRequests(bidRequests, bidderRequestTCF2); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr).to.not.have.property('consentString'); - expect(request.data.gdpr).to.not.have.property('gdprApplies'); - expect(request.data.gdpr.apiVersion).to.exist.and.to.equal(2); - }); + const requests = spec.buildRequests([bid01], bidderRequest); - it('GDPR consent bidderRequest does not have gdprConsent', () => { - delete bidderRequest.gdprConsent; - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests).to.have.lengthOf(2); - const request = requests[0]; - expect(request.data.gdpr).to.exist; - expect(request.data.gdpr).to.be.empty; + expect(requests[0].data.schain).to.not.exist; + }); }); - it('should expose version in window', () => { - expect(window.top.ADAGIO).ok; - expect(window.top.ADAGIO.versions).ok; - expect(window.top.ADAGIO.versions.adagioBidderAdapter).to.eq(VERSION); - }); + describe('with GDPR', function() { + const bid01 = new BidRequestBuilder().withParams().build(); - it('should returns an empty array if the bidder cannot access to window top (based on refererInfo.reachedTop)', () => { - const requests = spec.buildRequests(bidRequests, { - ...bidderRequest, - refererInfo: { reachedTop: false } - }); - expect(requests).to.be.empty; - }); + const consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; - it('Should add the schain if available at bidder level', () => { - const bidRequest = Object.assign({}, bidRequests[0], { - schain: { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'ssp.test', - sid: '00001', - hp: 1 - }] - } - }); + const gdprConsentBuilderTCF1 = function gdprConsentBuilderTCF1(applies, allows) { + return { + consentString, + gdprApplies: applies, + allowAuctionWithoutConsent: allows, + apiVersion: 1 + }; + }; - const requests = spec.buildRequests([bidRequest], bidderRequest); - const request = requests[0]; + const gdprConsentBuilderTCF2 = function gdprConsentBuilderTCF2(applies) { + return { + consentString, + gdprApplies: applies, + apiVersion: 2 + }; + }; - expect(request.data.schain).to.exist; - expect(request.data.schain).to.deep.equal({ - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'ssp.test', - sid: '00001', - hp: 1 - }] + context('When GDPR applies', function() { + it('send data.gdpr object to the server from TCF v.1.1 cmp', function() { + const bidderRequest = new BidderRequestBuilder({ + gdprConsent: gdprConsentBuilderTCF1(true, true) + }).build(); + + const expected = { + consentString, + allowAuctionWithoutConsent: 1, + consentRequired: 1, + apiVersion: 1 + }; + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.gdpr).to.deep.equal(expected); + }); + + it('send data.gdpr object to the server from TCF v.2 cmp', function() { + const bidderRequest = new BidderRequestBuilder({ + gdprConsent: gdprConsentBuilderTCF2(true) + }).build(); + + const expected = { + consentString, + consentRequired: 1, + apiVersion: 2 + }; + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.gdpr).to.deep.equal(expected); + }); }); - }); - it('Schain should not be added to the request', () => { - const requests = spec.buildRequests([bidRequests[0]], bidderRequest); - const request = requests[0]; - expect(request.data.schain).to.not.exist; - }); - }); + context('When GDPR does not applies', function() { + it('send data.gdpr object to the server from TCF v.1.1 cmp', function() { + const bidderRequest = new BidderRequestBuilder({ + gdprConsent: gdprConsentBuilderTCF1(false, true) + }).build(); + + const expected = { + consentString, + allowAuctionWithoutConsent: 1, + consentRequired: 0, + apiVersion: 1 + }; - describe('interpretResponse', () => { - const sandbox = sinon.createSandbox(); + const requests = spec.buildRequests([bid01], bidderRequest); - let serverResponse = { - body: { - data: { - pred: 1 - }, - bids: [ - { - ad: '
', - cpm: 1, - creativeId: 'creativeId', - currency: 'EUR', - height: 250, - netRevenue: true, - requestId: 'c180kg4267tyqz', - ttl: 360, - width: 300 - } - ] - } - }; + expect(requests[0].data.gdpr).to.deep.equal(expected); + }); - let emptyBodyServerResponse = { - body: null - }; + it('send data.gdpr object to the server from TCF v.2 cmp', function() { + const bidderRequest = new BidderRequestBuilder({ + gdprConsent: gdprConsentBuilderTCF2(false) + }).build(); - let withoutBidsArrayServerResponse = { - body: { - bids: [] - } - }; + const expected = { + consentString, + consentRequired: 0, + apiVersion: 2 + }; - let serverResponseWhichThrowsException = { + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.gdpr).to.deep.equal(expected); + }); + }); + + context('When GDPR is undefined in bidderRequest', function() { + it('send an empty data.gdpr to the server', function() { + const bidderRequest = new BidderRequestBuilder().build(); + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.gdpr).to.be.empty; + }); + }); + }); + }); + + describe('interpretResponse()', function() { + let serverResponse = { body: { data: { pred: 1 }, - bids: { - foo: 'bar' - } + bids: [{ + ad: '
', + cpm: 1, + creativeId: 'creativeId', + currency: 'EUR', + height: 250, + netRevenue: true, + requestId: 'c180kg4267tyqz', + ttl: 360, + width: 300 + }] } }; let bidRequest = { - 'data': { - 'adUnits': [ - { - 'bidder': 'adagio', - 'params': { - organizationId: '456', - site: 'ADAGIO-456', - placement: 'PAVE_ATF-456', - adUnitElementId: 'banner-atf-456', - pagetype: 'ARTICLE', - category: 'NEWS', - subcategory: 'SPORT', - environment: 'SITE-MOBILE' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c180kg4267tyqz', - 'bidderRequestId': '8vfscuixrovn8i', - 'auctionId': 'lel4fhp239i9km', - 'pageviewId': 'd8c4fl2k39i0wn', - } - ] + data: { + adUnits: [{ + bidder: 'adagio', + params: { + organizationId: '1000', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + adUnitElementId: 'gpt-adunit-code', + pagetype: 'ARTICLE', + category: 'NEWS', + subcategory: 'SPORT', + environment: 'desktop' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { sizes: [[300, 250], [300, 600]] } + }, + bidId: 'c180kg4267tyqz', + bidderRequestId: '8vfscuixrovn8i', + auctionId: 'lel4fhp239i9km', + pageviewId: 'd8c4fl2k39i0wn', + }] } }; - afterEach(function() { - sandbox.restore(); - }); + it('should return an empty response array if body is empty', function() { + expect(spec.interpretResponse({ + body: null + }, bidRequest)).to.be.an('array').length(0); - it('Should returns empty response if body is empty', () => { - expect(spec.interpretResponse(emptyBodyServerResponse, bidRequest)).to.be.an('array').length(0); - expect(spec.interpretResponse({body: {}}, bidRequest)).to.be.an('array').length(0); + expect(spec.interpretResponse({ + body: {} + }, bidRequest)).to.be.an('array').length(0); }); - it('Should returns empty response if bids array is empty', () => { - expect(spec.interpretResponse({withoutBidsArrayServerResponse}, bidRequest)).to.be.an('array').length(0); + it('should return an empty response array if bids array is empty', function() { + expect(spec.interpretResponse({ + body: { + bids: [] + } + }, bidRequest)).to.be.an('array').length(0); }); - it('should get correct bid response', () => { + it('should handle properly a correct bid response', function() { let expectedResponse = [{ ad: '
', cpm: 1, @@ -683,87 +591,512 @@ describe('adagioAdapter', () => { requestId: 'c180kg4267tyqz', ttl: 360, width: 300, - placement: 'PAVE_ATF-456', - site: 'ADAGIO-456', + placement: 'PAVE_ATF', + site: 'SITE-NAME', pagetype: 'ARTICLE', category: 'NEWS', subcategory: 'SPORT', - environment: 'SITE-MOBILE' + environment: 'desktop' }]; + expect(spec.interpretResponse(serverResponse, bidRequest)).to.be.an('array'); expect(spec.interpretResponse(serverResponse, bidRequest)).to.deep.equal(expectedResponse); }); - it('Should populate ADAGIO queue with ssp-data', () => { - spec.interpretResponse(serverResponse, bidRequest); - expect(window.top.ADAGIO).ok; - expect(window.top.ADAGIO.queue).to.be.an('array'); - }); + it('should populate ADAGIO queue with ssp-data', function() { + sandbox.stub(Date, 'now').returns(12345); + + adagioMock.expects('enqueue').withExactArgs({ + action: 'ssp-data', + ts: 12345, + data: serverResponse.body.data + }).once(); - it('Should not populate ADAGIO queue with ssp-data if not in top window', () => { - utils.getWindowTop().ADAGIO.queue = []; - sandbox.stub(utils, 'getWindowTop').throws(); spec.interpretResponse(serverResponse, bidRequest); - expect(window.top.ADAGIO).ok; - expect(window.top.ADAGIO.queue).to.be.an('array'); - expect(window.top.ADAGIO.queue).empty; + + adagioMock.verify(); }); - it('should return an empty response even if an exception is ', () => { - expect(spec.interpretResponse(serverResponseWhichThrowsException, bidRequest)).to.be.an('array').length(0); + it('should properly try-catch an exception and return an empty array', function() { + sandbox.stub(adagio, 'enqueue').throws(); + utilsMock.expects('logError').once(); + + expect(spec.interpretResponse(serverResponse, bidRequest)).to.be.an('array').length(0); + + utilsMock.verify(); }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs()', function() { const syncOptions = { - 'iframeEnabled': 'true' - } - const serverResponses = [ - { + syncEnabled: false + }; + + it('should handle user syncs if data is in the server response ', function() { + const serverResponses = [{ body: { userSyncs: [ - { - t: 'i', - u: 'https://test.url.com/setuid' - }, - { - t: 'p', - u: 'https://test.url.com/setuid' - } + { t: 'i', u: 'https://test.url.com/setuid' }, + { t: 'p', u: 'https://test.url.com/setuid' } ] } - } - ]; - - const emptyServerResponses = [ - { - body: '' - } - ]; + }]; - it('should handle correctly user syncs', () => { let result = spec.getUserSyncs(syncOptions, serverResponses); - let emptyResult = spec.getUserSyncs(syncOptions, emptyServerResponses); + expect(result[0].type).to.equal('iframe'); expect(result[0].url).contain('setuid'); + expect(result[1].type).to.equal('image'); - expect(emptyResult).to.equal(false); + expect(result[1].url).contain('setuid'); + }); + + it('should return false if data is not in server response', function() { + const serverResponse = [{ body: '' }]; + const result = spec.getUserSyncs(syncOptions, serverResponse); + expect(result).to.equal(false); }); }); - describe('adagioScriptFromLocalStorageCb', () => { + describe('Adagio features', function() { + it('should return all expected features when all expected bidder params are available', function() { + sandbox.stub(window.top.document, 'getElementById').returns( + fixtures.getElementById() + ); + sandbox.stub(window.top, 'getComputedStyle').returns({ display: 'block' }); + + const bidRequest = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); + + const bidderRequest = new BidderRequestBuilder().build(); + + const result = adagio.getFeatures(bidRequest, bidderRequest); + + expect(result.adunit_position).to.match(/^[\d]+x[\d]+$/); + expect(result.page_dimensions).to.match(/^[\d]+x[\d]+$/); + expect(result.viewport_dimensions).to.match(/^[\d]+x[\d]+$/); + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.url).to.be.a('String'); + expect(result.device).to.be.a('String'); + expect(result.os).to.be.a('String'); + expect(result.browser).to.be.a('String'); + }); + + it('should return all expected features when `adUnitElementId` param is not available', function() { + const bidRequest = new BidRequestBuilder({ + params: { + organizationId: '1000', + placement: 'PAVE_ATF', + site: 'SITE-NAME' + }, + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).build(); + + const bidderRequest = new BidderRequestBuilder().build(); + + const result = adagio.getFeatures(bidRequest, bidderRequest); + + expect(result.adunit_position).to.not.exist; + expect(result.page_dimensions).to.be.a('String'); + expect(result.viewport_dimensions).to.be.a('String'); + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.url).to.be.a('String'); + expect(result.device).to.be.a('String'); + expect(result.os).to.be.a('String'); + expect(result.browser).to.be.a('String'); + }); + + it('should not return feature with an empty value', function() { + sandbox.stub(_features, 'getDomLoadingDuration').returns(''); + sandbox.stub(_features, 'getUrl').returns(''); + sandbox.stub(_features, 'getBrowser').returns(''); + + const bidRequest = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); + + const bidderRequest = new BidderRequestBuilder().build(); + + const result = adagio.getFeatures(bidRequest, bidderRequest); + + expect(result.adunit_position).to.not.exist; + expect(result.page_dimensions).to.exist; + expect(result.viewport_dimensions).to.exist; + expect(result.print_number).to.exist; + expect(result.dom_loading).to.not.exist; + expect(result.user_timestamp).to.exist; + expect(result.url).to.not.exist; + expect(result.device).to.exist; + expect(result.os).to.exist; + expect(result.browser).to.not.exist; + }); + + describe('getPageDimensions feature', function() { + afterEach(() => { + delete window.$sf; + }); + + it('should not compute the page dimensions in cross-origin iframe', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + const result = _features.getPageDimensions(); + expect(result).to.eq(''); + }); + + it('should not compute the page dimensions even with safeFrame api', function() { + window.$sf = $sf; + const result = _features.getPageDimensions(); + expect(result).to.eq(''); + }); + + it('should not compute the page dimensions if is not in the DOM', function() { + sandbox.stub(window.top.document, 'querySelector').withArgs('body').returns(null); + const result = _features.getPageDimensions(); + expect(result).to.eq(''); + }); + + it('should compute the page dimensions based on body and viewport dimensions', function() { + sandbox.stub(window.top.document, 'querySelector').withArgs('body').returns({ scrollWidth: 1360, offsetWidth: 1280, scrollHeight: 2000, offsetHeight: 1000 }); + const result = _features.getPageDimensions(); + expect(result).to.eq('1360x2000'); + }); + }); + + describe('getViewPortDimensions feature', function() { + afterEach(() => { + delete window.$sf; + }); + + it('should not compute the viewport dimensions in cross-origin iframe', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + const result = _features.getViewPortDimensions(); + expect(result).to.eq(''); + }); + + it('should compute the viewport dimensions in cross-origin iframe w/ safeFrame api', function() { + window.$sf = $sf; + sandbox.stub(window.$sf.ext, 'geom').returns({ + win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, + self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, + }); + const result = _features.getViewPortDimensions(); + expect(result).to.eq('1920x1177'); + }); + + it('should not compute the viewport dimensions if safeFrame api is misimplemented', function() { + window.$sf = { + ext: { geom: 'nothing' } + }; + const result = _features.getViewPortDimensions(); + expect(result).to.eq(''); + }); + + it('should not compute the viewport dimensions if is not in the DOM', function() { + const querySelectorSpy = sandbox.spy(() => null); + sandbox.stub(utils, 'getWindowTop').returns({ + location: { href: 'https://mytest.io' }, + document: { querySelector: querySelectorSpy } + }); + const result = _features.getViewPortDimensions(); + expect(result).to.eq(''); + }); + + it('should compute the viewport dimensions based on window', function() { + sandbox.stub(utils, 'getWindowTop').returns({ + location: { href: 'https://mytest.io' }, + innerWidth: 960, + innerHeight: 3000 + }); + const result = _features.getViewPortDimensions(); + expect(result).to.eq('960x3000'); + }); + + it('should compute the viewport dimensions based on body', function() { + const querySelectorSpy = sandbox.spy(() => ({ clientWidth: 1024, clientHeight: 2000 })); + sandbox.stub(utils, 'getWindowTop').returns({ + location: { href: 'https://mytest.io' }, + document: { querySelector: querySelectorSpy } + }); + const result = _features.getViewPortDimensions(); + expect(result).to.eq('1024x2000'); + }); + }); + + describe('getSlotPosition feature', function() { + let getElementByIdStub; + let getComputedStyleStub; + + beforeEach(() => { + getElementByIdStub = sandbox.stub(window.top.document, 'getElementById'); + getElementByIdStub.returns(fixtures.getElementById()); + getComputedStyleStub = sandbox.stub(window.top, 'getComputedStyle'); + getComputedStyleStub.returns({ display: 'block' }); + }); + + afterEach(() => { + delete window.$sf; + getElementByIdStub.restore(); + getComputedStyleStub.restore(); + }); + + it('should not compute the slot position in cross-origin iframe', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq(''); + }); + + it('should compute the slot position in cross-origin iframe w/ safeFrame api', function() { + window.$sf = $sf; + sandbox.stub(window.$sf.ext, 'geom').returns({ + win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, + self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, + }); + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq('210x859'); + }); + + it('should not compute the slot position if safeFrame api is misimplemented', function() { + window.$sf = { + ext: { geom: 'nothing' } + }; + utilsMock.expects('logWarn').once(); + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq(''); + utilsMock.verify(); + }); + + it('should not compute the slot position due to unreachable adUnitElementId', function() { + getElementByIdStub.returns(null); + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq(''); + }); + + it('should use a quick switch to display slot and compute position', function() { + getComputedStyleStub.returns({ display: 'none' }); + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq('800x300'); + }); + + it('should compute the slot position based on window.top w/o postBid param', function() { + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); + expect(result).to.eq('800x300'); + }); + + it.skip('should compute the slot position inside the parent window (window.top) when safeFrame is not available and postBid params is `true`', function() { + const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: true }); + // expect(result).to.eq('800x300'); + }); + }); + }); + + describe('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'); + }); + }); + + describe('print number handling', function() { + it('should return 1 if no adunit-code found. This means it is the first auction', function() { + sandbox.stub(adagio, 'getPageviewId').returns('abc-def'); + expect(adagio.computePrintNumber('adunit-code')).to.eql(1); + }); + + it('should increment the adunit print number when the adunit-code has already been used for an other auction', function() { + sandbox.stub(adagio, 'getPageviewId').returns('abc-def'); + + window.top.ADAGIO.adUnits['adunit-code'] = { + pageviewId: 'abc-def', + printNumber: 1, + }; + + expect(adagio.computePrintNumber('adunit-code')).to.eql(2); + }); + }); + + describe('site information using refererDetection or window.top', function() { + it('should returns domain, page and window.referrer in a window.top context', function() { + sandbox.stub(utils, 'getWindowTop').returns({ + location: { + hostname: 'test.io', + href: 'https://test.io/article/a.html' + }, + document: { + referrer: 'https://google.com' + } + }); + + const bidderRequest = new BidderRequestBuilder({ + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'http://test.io/index.html?pbjs_debug=true' + } + }).build(); + + expect(adagio.getSite(bidderRequest)).to.deep.equal({ + domain: 'test.io', + page: 'https://test.io/article/a.html', + referrer: 'https://google.com' + }); + }); + + it('should returns domain and page in a cross-domain w/ top domain reached context', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + + const info = { + numIframes: 0, + reachedTop: true, + referer: 'http://level.io/', + stack: [ + 'http://level.io/', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ], + canonicalUrl: '' + }; + + const bidderRequest = new BidderRequestBuilder({ + refererInfo: info + }).build(); + + expect(adagio.getSite(bidderRequest)).to.deep.equal({ + domain: 'level.io', + page: 'http://level.io/', + referrer: '' + }); + }); + + it('should not return anything in a cross-domain w/o top domain reached and w/o ancestor context', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + + const info = { + numIframes: 2, + reachedTop: false, + referer: 'http://example.com/iframe1.html', + stack: [ + null, + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ], + canonicalUrl: '' + }; + + const bidderRequest = new BidderRequestBuilder({ + refererInfo: info + }).build(); + + expect(adagio.getSite(bidderRequest)).to.deep.equal({ + domain: '', + page: '', + referrer: '' + }); + }); + + it('should return domain only in a cross-domain w/o top domain reached and w/ ancestors context', function() { + sandbox.stub(utils, 'getWindowTop').throws(); + + const info = { + numIframes: 2, + reachedTop: false, + referer: 'http://example.com/iframe1.html', + stack: [ + 'http://mytest.com/', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ], + canonicalUrl: '' + }; + + const bidderRequest = new BidderRequestBuilder({ + refererInfo: info + }).build(); + + expect(adagio.getSite(bidderRequest)).to.deep.equal({ + domain: 'mytest.com', + page: '', + referrer: '' + }); + }); + }); + + describe('adagioScriptFromLocalStorageCb()', function() { const VALID_HASH = 'Lddcw3AADdQDrPtbRJkKxvA+o1CtScGDIMNRpHB3NnlC/FYmy/9RKXelKrYj/sjuWusl5YcOpo+lbGSkk655i8EKuDiOvK6ae/imxSrmdziIp+S/TA6hTFJXcB8k1Q9OIp4CMCT52jjXgHwX6G0rp+uYoCR25B1jHaHnpH26A6I='; const INVALID_HASH = 'invalid'; const VALID_SCRIPT_CONTENT = 'var _ADAGIO=function(){};(_ADAGIO)();\n'; const INVALID_SCRIPT_CONTENT = 'var _ADAGIO=function(){//corrupted};(_ADAGIO)();\n'; const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; + beforeEach(function() { + localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); + }); + + describe('getAdagioScript', function() { + it('should run storage.getDataFromLocalStorage callback and call adagioScriptFromLocalStorageCb() ', function() { + sandbox.spy(adagio, 'adagioScriptFromLocalStorageCb'); + const getDataFromLocalStorageStub = sandbox.stub(storage, 'getDataFromLocalStorage').callsArg(1); + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + VALID_HASH + '\n' + VALID_SCRIPT_CONTENT); + + getAdagioScript(); + + sinon.assert.callCount(getDataFromLocalStorageStub, 1); + sinon.assert.callCount(adagio.adagioScriptFromLocalStorageCb, 1); + }); + + it('should load external script if the user consent', function() { + sandbox.stub(storage, 'localStorageIsEnabled').callsArgWith(0, true); + getAdagioScript(); + + expect(loadExternalScript.called).to.be.true; + }); + + it('should not load external script if the user does not consent', function() { + sandbox.stub(storage, 'localStorageIsEnabled').callsArgWith(0, false); + getAdagioScript(); + + expect(loadExternalScript.called).to.be.false; + }); + + it('should remove the localStorage key if exists and the user does not consent', function() { + sandbox.stub(storage, 'localStorageIsEnabled').callsArgWith(0, false); + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, 'the script'); + + getAdagioScript(); + + expect(loadExternalScript.called).to.be.false; + expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; + }) + }); + it('should verify valid hash with valid script', function () { localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + VALID_HASH + '\n' + VALID_SCRIPT_CONTENT); - utilsMock.expects('logInfo').withExactArgs('Start Adagio script').once(); - utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').never(); + utilsMock.expects('logInfo').withExactArgs('Adagio: start script.').once(); + utilsMock.expects('logWarn').withExactArgs('Adagio: no hash found.').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: invalid script found.').never(); adagioScriptFromLocalStorageCb(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)); @@ -774,9 +1107,9 @@ describe('adagioAdapter', () => { it('should verify valid hash with invalid script', function () { localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + VALID_HASH + '\n' + INVALID_SCRIPT_CONTENT); - utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').once(); + utilsMock.expects('logInfo').withExactArgs('Adagio: start script').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: no hash found.').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: invalid script found.').once(); adagioScriptFromLocalStorageCb(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)); @@ -787,9 +1120,9 @@ describe('adagioAdapter', () => { it('should verify invalid hash with valid script', function () { localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + INVALID_HASH + '\n' + VALID_SCRIPT_CONTENT); - utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').once(); + utilsMock.expects('logInfo').withExactArgs('Adagio: start script').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: no hash found.').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: invalid script found.').once(); adagioScriptFromLocalStorageCb(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)); @@ -800,14 +1133,21 @@ describe('adagioAdapter', () => { it('should verify missing hash', function () { localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, VALID_SCRIPT_CONTENT); - utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); - utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').once(); - utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').never(); + utilsMock.expects('logInfo').withExactArgs('Adagio: start script').never(); + utilsMock.expects('logWarn').withExactArgs('Adagio: no hash found.').once(); + utilsMock.expects('logWarn').withExactArgs('Adagio: invalid script found.').never(); adagioScriptFromLocalStorageCb(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)); expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; utilsMock.verify(); }); + + it('should return false if content script does not exist in localStorage', function() { + sandbox.spy(utils, 'logWarn'); + expect(adagioScriptFromLocalStorageCb(null)).to.be.undefined; + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, 'Adagio: script not found.'); + }); }); }); From 4d2b4011a39ae31275ec595dc58baaeb10109d68 Mon Sep 17 00:00:00 2001 From: sdao-tl <49252703+sdao-tl@users.noreply.github.com> Date: Wed, 12 Aug 2020 01:00:21 -0700 Subject: [PATCH 0075/1476] Triplelift: Add Instream support (#5472) * initial commit, instream poc done * push in poc changes * push in poc changes * restore instream.html * push in poc changes * restore instream.html * restore instream.html v2 * adding instream unit tests v1 * catch up to bidfloor changes * unit tests finalized! * update adapter md * add support for mediaTypes.video Co-authored-by: Sy Dao --- modules/tripleliftBidAdapter.js | 50 +++- modules/tripleliftBidAdapter.md | 20 ++ .../spec/modules/tripleliftBidAdapter_spec.js | 276 ++++++++++++------ 3 files changed, 236 insertions(+), 110 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index af904aedc11..d6b1f95351d 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -1,4 +1,4 @@ -import { BANNER } from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; @@ -11,9 +11,13 @@ let consentString = null; export const tripleliftAdapterSpec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid: function(bid) { - return (typeof bid.params.inventoryCode !== 'undefined'); + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid: function (bid) { + if (bid.mediaTypes.video) { + let video = _getORTBVideo(bid); + if (!video.w || !video.h) return false; + } + return typeof bid.params.inventoryCode !== 'undefined'; }, buildRequests: function(bidRequests, bidderRequest) { @@ -107,15 +111,18 @@ function _getSyncType(syncOptions) { function _buildPostBody(bidRequests) { let data = {}; let { schain } = bidRequests[0]; - data.imp = bidRequests.map(function(bid, index) { - return { + data.imp = bidRequests.map(function(bidRequest, index) { + let imp = { id: index, - tagid: bid.params.inventoryCode, - floor: _getFloor(bid), - banner: { - format: _sizes(bid.sizes) - } + tagid: bidRequest.params.inventoryCode, + floor: _getFloor(bidRequest) + }; + if (bidRequest.mediaTypes.video) { + imp.video = _getORTBVideo(bidRequest); + } else if (bidRequest.mediaTypes.banner) { + imp.banner = { format: _sizes(bidRequest.sizes) }; }; + return imp; }); let eids = [ @@ -138,6 +145,17 @@ function _buildPostBody(bidRequests) { return data; } +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]; + if (video.context === 'instream') video.placement = 1; + // clean up oRTB object + delete video.playerSize; + return video; +} + function _getFloor (bid) { let floor = null; if (typeof bid.getFloor === 'function') { @@ -207,10 +225,11 @@ function _buildResponseObject(bidderRequest, bid) { let height = bid.height || 1; let dealId = bid.deal_id || ''; let creativeId = bid.crid || ''; + let breq = bidderRequest.bids[bid.imp_id]; if (bid.cpm != 0 && bid.ad) { bidResponse = { - requestId: bidderRequest.bids[bid.imp_id].bidId, + requestId: breq.bidId, cpm: bid.cpm, width: width, height: height, @@ -220,7 +239,12 @@ function _buildResponseObject(bidderRequest, bid) { dealId: dealId, currency: 'USD', ttl: 300, - tl_source: bid.tl_source, + tl_source: bid.tl_source + }; + + if (breq.mediaTypes.video) { + bidResponse.vastXml = bid.ad; + bidResponse.mediaType = 'video'; }; }; return bidResponse; diff --git a/modules/tripleliftBidAdapter.md b/modules/tripleliftBidAdapter.md index d5f88a2bece..03dcee3b980 100644 --- a/modules/tripleliftBidAdapter.md +++ b/modules/tripleliftBidAdapter.md @@ -58,5 +58,25 @@ var adUnits = [{ floor: 0 } }] +}, { + code: 'instream-div-1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + } + }, + bids: [ + { + bidder: 'triplelift', + params: { + inventoryCode: 'instream_test', + video: { + mimes: ['video/mp4'], + w: 640, + h: 480, + }, + } + }] }]; ``` diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 73373293114..c6c3b622755 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -10,49 +10,88 @@ const GDPR_CONSENT_STR = 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7V describe('triplelift adapter', function () { const adapter = newBidder(tripleliftAdapterSpec); + let bid, instreamBid; - 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 = { + this.beforeEach(() => { + bid = { bidder: 'triplelift', params: { inventoryCode: '12345', floor: 1.0, }, + mediaTypes: { + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + instreamBid = { + bidder: 'triplelift', + params: { + inventoryCode: 'insteam_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, 'adUnitCode': 'adunit-code', 'sizes': [[300, 250], [300, 600]], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', }; + }) + 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 for valid bid request', function () { expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(true); }); it('should return true when required params found', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - inventoryCode: 'another_inv_code', - floor: 0.05 - }; + bid.params.inventoryCode = 'another_inv_code'; expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(true); }); + it('should return true when required params found - instream', function () { + expect(tripleliftAdapterSpec.isBidRequestValid(instreamBid)).to.equal(true); + }); + it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - floor: 1.0 - }; + delete bid.params.inventoryCode; expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(false); }); + + it('should return false when required params are not passed - instream', function () { + delete instreamBid.mediaTypes.playerSize; + delete instreamBid.params.video.w; + delete instreamBid.params.video.h; + expect(tripleliftAdapterSpec.isBidRequestValid(instreamBid)).to.equal(false); + }); }); describe('buildRequests', function () { @@ -81,6 +120,14 @@ describe('triplelift adapter', function () { inventoryCode: '12345', floor: 1.0, }, + mediaTypes: { + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + } + }, adUnitCode: 'adunit-code', sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], bidId: '30b31c1838de1e', @@ -88,6 +135,33 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + }, + { + bidder: 'triplelift', + params: { + inventoryCode: 'insteam_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, } ]; @@ -103,6 +177,13 @@ describe('triplelift adapter', function () { height: 250, ad: 'ad-markup', iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg' + }, + { + imp_id: 1, + crid: '10092_76480_i2j6qm8u', + cpm: 0.01, + ad: 'The Trade Desk', + tlx_source: 'hdx' } ], refererInfo: { @@ -120,6 +201,11 @@ describe('triplelift adapter', function () { expect(request).to.exist.and.to.be.a('object'); }); + it('should be able find video object from the instream request', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + expect(request.data.imp[1].video).to.exist.and.to.be.a('object'); + }); + it('should only parse sizes that are of the proper length and format', function () { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); expect(request.data.imp[0].banner.format).to.have.length(2); @@ -133,6 +219,9 @@ 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}]); + 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'); }); it('should add tdid to the payload if included', function () { @@ -311,68 +400,9 @@ describe('triplelift adapter', function () { }); describe('interpretResponse', function () { - let response = { - body: { - bids: [ - { - imp_id: 0, - cpm: 1.062, - width: 300, - height: 250, - ad: 'ad-markup', - iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', - tl_source: 'tlx', - } - ] - } - }; - let bidderRequest = { - bidderCode: 'triplelift', - auctionId: 'a7ebcd1d-66ff-4b5c-a82c-6a21a6ee5a18', - bidderRequestId: '5c55612f99bc11', - bids: [ - { - imp_id: 0, - cpm: 1.062, - width: 300, - height: 250, - ad: 'ad-markup', - iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', - tl_source: 'tlx', - } - ], - refererInfo: { - referer: 'https://examplereferer.com' - }, - gdprConsent: { - consentString: GDPR_CONSENT_STR, - gdprApplies: true - } - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: '3db3773286ee59', - cpm: 1.062, - width: 300, - height: 250, - netRevenue: true, - ad: 'ad-markup', - creativeId: 29681110, - dealId: '', - currency: 'USD', - ttl: 33, - tl_source: 'tlx', - } - ]; - let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result).to.have.length(1); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('should return multiple responses to support SRA', function () { - let response = { + let response, bidderRequest; + this.beforeEach(() => { + response = { body: { bids: [ { @@ -385,18 +415,16 @@ describe('triplelift adapter', function () { tl_source: 'tlx', }, { - imp_id: 0, - cpm: 1.9, - width: 300, - height: 600, - ad: 'ad-markup-2', - iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', - tl_source: 'tlx', + imp_id: 1, + crid: '10092_76480_i2j6qm8u', + cpm: 9.99, + ad: 'The Trade Desk', + tlx_source: 'hdx' } ] } }; - let bidderRequest = { + bidderRequest = { bidderCode: 'triplelift', auctionId: 'a7ebcd1d-66ff-4b5c-a82c-6a21a6ee5a18', bidderRequestId: '5c55612f99bc11', @@ -405,19 +433,33 @@ describe('triplelift adapter', function () { imp_id: 0, cpm: 1.062, width: 300, - height: 600, + height: 250, ad: 'ad-markup', iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', tl_source: 'tlx', + mediaTypes: { + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + } + }, + bidId: '30b31c1838de1e', }, { - imp_id: 0, - cpm: 1.9, - width: 300, - height: 250, - ad: 'ad-markup-2', - iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', - tl_source: 'tlx', + imp_id: 1, + crid: '10092_76480_i2j6qm8u', + cpm: 9.99, + ad: 'The Trade Desk', + tlx_source: 'hdx', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, + bidId: '30b31c1838de1e', } ], refererInfo: { @@ -428,6 +470,46 @@ describe('triplelift adapter', function () { gdprApplies: true } }; + }) + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '30b31c1838de1e', + cpm: 1.062, + width: 300, + height: 250, + netRevenue: true, + ad: 'ad-markup', + creativeId: 29681110, + dealId: '', + currency: 'USD', + ttl: 33, + tl_source: 'tlx', + }, + { + requestId: '30b31c1838de1e', + cpm: 1.062, + width: 300, + height: 250, + netRevenue: true, + ad: 'The Trade Desk', + creativeId: 29681110, + dealId: '', + currency: 'USD', + ttl: 33, + tl_source: 'hdx', + mediaType: 'video', + vastXml: 'The Trade Desk', + } + ]; + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result).to.have.length(2); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(Object.keys(result[1])).to.have.members(Object.keys(expectedResponse[1])); + }); + + it('should return multiple responses to support SRA', function () { let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); expect(result).to.have.length(2); }); From d88bd0e96f995b22ecc8afe54f014523d7fcda24 Mon Sep 17 00:00:00 2001 From: Dmitriy Labuzov Date: Wed, 12 Aug 2020 12:11:58 +0300 Subject: [PATCH 0076/1476] Add mtp (maxTouchPoints) parameter to Yieldmo adapter (#5597) Co-authored-by: Dmitriy Labuzov --- modules/yieldmoBidAdapter.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index a7befecadc7..829b573ffd9 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -45,6 +45,11 @@ export const spec = { us_privacy: utils.deepAccess(bidderRequest, 'uspConsent') || '' }; + const mtp = window.navigator.maxTouchPoints; + if (mtp) { + serverRequest.mtp = mtp; + } + bidRequests.forEach(request => { serverRequest.p.push(addPlacement(request)); const pubcid = getId(request, 'pubcid'); From 3bedc792b752800fe3906e199c1922cced88b515 Mon Sep 17 00:00:00 2001 From: Vladislav Yatsun Date: Wed, 12 Aug 2020 19:49:50 +0400 Subject: [PATCH 0077/1476] Brightom Bid Adapter: Add GDPR support (#5594) --- modules/brightcomBidAdapter.js | 5 ++++ test/spec/modules/brightcomBidAdapter_spec.js | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index a4b013a2fe2..2aad211b186 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -70,6 +70,11 @@ function buildRequests(bidReqs, bidderRequest) { tmax: config.getConfig('bidderTimeout') }; + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(brightcomBidReq, 'regs.ext.gdpr', +bidderRequest.gdprConsent.gdprApplies); + utils.deepSetValue(brightcomBidReq, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + return { method: 'POST', url: URL, diff --git a/test/spec/modules/brightcomBidAdapter_spec.js b/test/spec/modules/brightcomBidAdapter_spec.js index 6477e94d9d6..a89391d681e 100644 --- a/test/spec/modules/brightcomBidAdapter_spec.js +++ b/test/spec/modules/brightcomBidAdapter_spec.js @@ -141,6 +141,31 @@ describe('brightcomBidAdapter', function() { expect(payload.site.publisher.id).to.equal(1234567); }); + it('sends gdpr info if exists', function () { + const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const bidderRequest = { + 'bidderCode': 'brightcom', + 'auctionId': '1d1a030790a437', + 'bidderRequestId': '22edbae2744bf5', + 'timeout': 3000, + gdprConsent: { + consentString: consentString, + gdprApplies: true + }, + refererInfo: { + referer: 'http://example.com/page.html', + } + }; + bidderRequest.bids = bidRequests; + + const data = JSON.parse(spec.buildRequests(bidRequests, bidderRequest).data); + + expect(data.regs.ext.gdpr).to.exist.and.to.be.a('number'); + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.exist.and.to.be.a('string'); + expect(data.user.ext.consent).to.equal(consentString); + }); + context('when element is fully in view', function() { it('returns 100', function() { Object.assign(element, { width: 600, height: 400 }); From b9ebe16b0c93798b0aa6281846d9f647ad5d6382 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 13 Aug 2020 17:43:03 +0200 Subject: [PATCH 0078/1476] user id module refresh ids when consent changes (#5451) * first cut at making the userId module aware of user consent choices so it can refresh the ID if consent changes * typos in comments * fix failing tests * refactor consent changes tests to prepare for adding tcf v2 tests * an update in 4.0 changed the interface for the `setStoredValue()` method which caused the previous code to break. Here I changed the code to read/write the consent data cookie to just do it directly rather than use the code for handling storing the actual id objects. --- modules/userId/index.js | 82 ++++++++++- test/spec/modules/userId_spec.js | 234 +++++++++++++++++++++++++++++-- 2 files changed, 300 insertions(+), 16 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 2a37723e3a0..afdd93a57ba 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -124,6 +124,10 @@ const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const DEFAULT_SYNC_DELAY = 500; const NO_AUCTION_DELAY = 0; +const CONSENT_DATA_COOKIE_STORAGE_CONFIG = { + name: '_pbjs_userid_consent_data', + expires: 30 // 30 days expiration, which should match how often consent is refreshed by CMPs +}; export const coreStorage = getCoreStorageManager('userid'); /** @type {string[]} */ @@ -221,6 +225,72 @@ function getStoredValue(storage, key = undefined) { return storedValue; } +/** + * makes an object that can be stored with only the keys we need to check. + * excluding the vendorConsents object since the consentString is enough to know + * if consent has changed without needing to have all the details in an object + * @param consentData + * @returns {{apiVersion: number, gdprApplies: boolean, consentString: string}} + */ +function makeStoredConsentDataObject(consentData) { + const storedConsentData = { + consentString: '', + gdprApplies: false, + apiVersion: 0 + }; + + if (consentData) { + storedConsentData.consentString = consentData.consentString; + storedConsentData.gdprApplies = consentData.gdprApplies; + storedConsentData.apiVersion = consentData.apiVersion; + } + + return storedConsentData; +} + +/** + * puts the current consent data into cookie storage + * @param consentData + */ +export function setStoredConsentData(consentData) { + try { + const expiresStr = (new Date(Date.now() + (CONSENT_DATA_COOKIE_STORAGE_CONFIG.expires * (60 * 60 * 24 * 1000)))).toUTCString(); + coreStorage.setCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name, JSON.stringify(makeStoredConsentDataObject(consentData)), expiresStr, 'Lax'); + } catch (error) { + utils.logError(error); + } +} + +/** + * get the stored consent data from local storage, if any + * @returns {string} + */ +function getStoredConsentData() { + let storedValue; + try { + storedValue = JSON.parse(coreStorage.getCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name)); + } catch (e) { + utils.logError(e); + } + return storedValue; +} + +/** + * test if the consent object stored locally matches the current consent data. + * if there is nothing in storage, return true and we'll do an actual comparison next time. + * this way, we don't force a refresh for every user when this code rolls out + * @param storedConsentData + * @param consentData + * @returns {boolean} + */ +function storedConsentDataMatchesConsentData(storedConsentData, consentData) { + return ( + typeof storedConsentData === 'undefined' || + storedConsentData === null || + utils.deepEqual(storedConsentData, makeStoredConsentDataObject(consentData)) + ); +} + /** * test if consent module is present, applies, and is valid for local storage or cookies (purpose 1) * @param {ConsentData} consentData @@ -308,7 +378,7 @@ function addIdDataToAdUnitBids(adUnits, submodules) { } /** - * This is a common function that will initalize subModules if not already done and it will also execute subModule callbacks + * This is a common function that will initialize subModules if not already done and it will also execute subModule callbacks */ function initializeSubmodulesAndExecuteCallbacks(continueAuction) { let delayed = false; @@ -411,6 +481,10 @@ export const validateGdprEnforcement = hook('sync', function (submodules, consen * @returns {SubmoduleContainer[]} initialized submodules */ function initSubmodules(submodules, consentData) { + // we always want the latest consentData stored, even if we don't execute any submodules + const storedConsentData = getStoredConsentData(); + setStoredConsentData(consentData); + // gdpr consent with purpose one is required, otherwise exit immediately let {userIdModules, hasValidated} = validateGdprEnforcement(submodules, consentData); if (!hasValidated && !hasGDPRConsent(consentData)) { @@ -432,8 +506,8 @@ function initSubmodules(submodules, consentData) { refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000); } - if (!storedId || refreshNeeded) { - // No previously saved id. Request one from submodule. + if (!storedId || refreshNeeded || !storedConsentDataMatchesConsentData(storedConsentData, consentData)) { + // No id previously saved, or a refresh is needed, or consent has changed. Request a new id from the submodule. response = submodule.submodule.getId(submodule.config.params, consentData, storedId); } else if (typeof submodule.submodule.extendId === 'function') { // If the id exists already, give submodule a chance to decide additional actions that need to be taken @@ -569,7 +643,7 @@ export function init(config) { utils.logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); return; } - // _pubcid_optout is checked for compatiblility with pubCommonId + // _pubcid_optout is checked for compatibility with pubCommonId if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (coreStorage.getDataFromLocalStorage('_pbjs_id_optout') || coreStorage.getDataFromLocalStorage('_pubcid_optout'))) { utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); return; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index a0b7d68bcce..d9671aabc84 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -6,7 +6,8 @@ import { setSubmoduleRegistry, syncDelay, coreStorage, - setStoredValue + setStoredValue, + setStoredConsentData } from 'modules/userId/index.js'; import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; @@ -14,6 +15,8 @@ import * as utils from 'src/utils.js'; import events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import {getGlobal} from 'src/prebidGlobal.js'; +import {setConsentConfig, requestBidsHook as consentManagementRequestBidsHook, resetConsentData} from 'modules/consentManagement.js'; +import {gdprDataHandler} from 'src/adapterManager.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem.js'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; @@ -28,6 +31,7 @@ import {server} from 'test/mocks/xhr.js'; let assert = require('chai').assert; let expect = require('chai').expect; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const CONSENT_LOCAL_STORAGE_NAME = '_pbjs_userid_consent_data'; describe('User ID', function() { function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8) { @@ -87,6 +91,10 @@ describe('User ID', function() { localStorage.removeItem('_pubcid_optout'); }); + beforeEach(function() { + coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); + }); + describe('Decorate Ad Units', function() { beforeEach(function() { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -211,8 +219,9 @@ describe('User ID', function() { }); }); }); - // Because the cookie exists already, there should be no setCookie call by default - expect(coreStorage.setCookie.callCount).to.equal(0); + // Because the cookie exists already, there should be no setCookie call by default; the only setCookie call is + // to store consent data + expect(coreStorage.setCookie.callCount).to.equal(1); }); it('Extend cookie', function() { @@ -237,8 +246,9 @@ describe('User ID', function() { }); }); }); - // Because extend is true, the cookie will be updated even if it exists already - expect(coreStorage.setCookie.callCount).to.equal(1); + // Because extend is true, the cookie will be updated even if it exists already. The second setCookie call + // is for storing consentData + expect(coreStorage.setCookie.callCount).to.equal(2); }); it('Disable auto create', function() { @@ -259,7 +269,8 @@ describe('User ID', function() { expect(bid).to.not.have.deep.nested.property('userIdAsEids'); }); }); - expect(coreStorage.setCookie.callCount).to.equal(0); + // setCookie is called once in order to store consentData + expect(coreStorage.setCookie.callCount).to.equal(1); }); it('pbjs.getUserIds', function() { @@ -1447,16 +1458,16 @@ describe('User ID', function() { describe('Set cookie behavior', function() { let coreStorageSpy; - beforeEach(function() { + beforeEach(function () { coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); }); - afterEach(function() { + afterEach(function () { coreStorageSpy.restore(); }); it('should allow submodules to override the domain', function () { const submodule = { submodule: { - domainOverride: function() { + domainOverride: function () { return 'foo.com' } }, @@ -1472,9 +1483,7 @@ describe('User ID', function() { it('should pass null for domain if submodule does not override the domain', function () { const submodule = { - submodule: { - - }, + submodule: {}, config: { storage: { type: 'cookie' @@ -1485,4 +1494,205 @@ describe('User ID', function() { expect(coreStorage.setCookie.getCall(0).args[4]).to.equal(null); }); }); + + describe('Consent changes determine getId refreshes', function() { + let expStr; + let adUnits; + + const mockIdCookieName = 'MOCKID'; + let mockGetId = sinon.stub(); + let mockDecode = sinon.stub(); + let mockExtendId = sinon.stub(); + const mockIdSystem = { + name: 'mockId', + getId: mockGetId, + decode: mockDecode, + extendId: mockExtendId + }; + const userIdConfig = { + userSync: { + userIds: [{ + name: 'mockId', + storage: { + name: 'MOCKID', + type: 'cookie', + refreshInSeconds: 30 + } + }], + auctionDelay: 5 + } + }; + + let cmpStub; + let testConsentData; + const consentConfig = { + cmpApi: 'iab', + timeout: 7500, + allowAuctionWithoutConsent: false + }; + + const sharedBeforeFunction = function() { + // clear cookies + expStr = (new Date(Date.now() + 25000).toUTCString()); + coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie(`${mockIdCookieName}_last`, '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); + + // init + adUnits = [getAdUnitMock()]; + init(config); + + // init id system + attachIdSystem(mockIdSystem); + config.setConfig(userIdConfig); + } + const sharedAfterFunction = function () { + config.resetConfig(); + mockGetId.reset(); + mockDecode.reset(); + mockExtendId.reset(); + cmpStub.restore(); + resetConsentData(); + delete window.__cmp; + delete window.__tcfapi; + }; + + describe('TCF v1', function() { + testConsentData = { + gdprApplies: true, + consentData: 'xyz', + apiVersion: 1 + }; + + beforeEach(function () { + sharedBeforeFunction(); + + // init v1 consent management + window.__cmp = function () { }; + delete window.__tcfapi; + cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { + args[2](testConsentData); + }); + setConsentConfig(consentConfig); + }); + + afterEach(function() { + sharedAfterFunction(); + }); + + it('does not call getId if no stored consent data and refresh is not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + let innerAdUnits; + consentManagementRequestBidsHook(() => { }, {}); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + + sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.calledOnce(mockExtendId); + + let consent = gdprDataHandler.getConsentData(); + let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); + expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); + expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); + expect(userIdStoredConsent.consentString).to.equal(consent.consentString); + expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); + }); + + it('calls getId if no stored consent data but refresh is needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); + + let innerAdUnits; + consentManagementRequestBidsHook(() => { }, {}); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); + + let consent = gdprDataHandler.getConsentData(); + let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); + expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); + expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); + expect(userIdStoredConsent.consentString).to.equal(consent.consentString); + expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); + }); + + it('calls getId if empty stored consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData(); + + let innerAdUnits; + consentManagementRequestBidsHook(() => { }, {}); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); + + let consent = gdprDataHandler.getConsentData(); + let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); + expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); + expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); + expect(userIdStoredConsent.consentString).to.equal(consent.consentString); + expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); + }); + + it('calls getId if stored consent does not match current consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData({ + gdprApplies: testConsentData.gdprApplies, + consentString: 'abc', + apiVersion: testConsentData.apiVersion + }); + + let innerAdUnits; + consentManagementRequestBidsHook(() => { }, {}); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); + + let consent = gdprDataHandler.getConsentData(); + let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); + expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); + expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); + expect(userIdStoredConsent.consentString).to.equal(consent.consentString); + expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); + }); + + it('does not call getId if stored consent matches current consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData({ + gdprApplies: testConsentData.gdprApplies, + consentString: testConsentData.consentData, + apiVersion: testConsentData.apiVersion + }); + + let innerAdUnits; + consentManagementRequestBidsHook(() => { }, {}); + requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + + sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.calledOnce(mockExtendId); + + let consent = gdprDataHandler.getConsentData(); + let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); + expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); + expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); + expect(userIdStoredConsent.consentString).to.equal(consent.consentString); + expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); + }); + }); + }); }); From ef00b3f171e8c394a3a978b7cc7a193fecb8aa51 Mon Sep 17 00:00:00 2001 From: akiselicki-liveramp <65005198+akiselicki-liveramp@users.noreply.github.com> Date: Thu, 13 Aug 2020 20:02:42 +0200 Subject: [PATCH 0079/1476] Fix v2CmpResponseCallback handle (#5564) * Fix v2CmpResponseCallback handle * Add case when gdpr doesn't apply to v2CmpResponseCallback Co-authored-by: Aleksandar Kiselicki --- modules/consentManagement.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index a5ed134420e..0c48d8c854c 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -100,7 +100,9 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { function v2CmpResponseCallback(tcfData, success) { utils.logInfo('Received a response from CMP', tcfData); if (success) { - if (tcfData.eventStatus === 'tcloaded' || tcfData.eventStatus === 'useractioncomplete') { + if (tcfData.gdprApplies === false) { + cmpSuccess(tcfData, hookConfig); + } else if (tcfData.eventStatus === 'tcloaded' || tcfData.eventStatus === 'useractioncomplete') { cmpSuccess(tcfData, hookConfig); } else if (tcfData.eventStatus === 'cmpuishown' && tcfData.tcString && tcfData.purposeOneTreatment === true) { cmpSuccess(tcfData, hookConfig); From c34786df2138b3a75c532f8974301db23676ad71 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Thu, 13 Aug 2020 23:47:19 +0530 Subject: [PATCH 0080/1476] TCFv2.0 Purpose 7 (#5444) * TCF v2.0 enforcement * test/spec/modules/gdprEnforcement_spec.js * add check for gdpr version * add logInfo message * remove comment and store value of PURPOSES in an object * add gvlid check * add unit tests for validateRules function * remove purposeId parameter from validateRules function * add extra tests * make failing unit test case pass * deprecate allowAuctionWithouConsent with tcf 2 workflow * add extra checks for defaults * remove tcf 2 test page * add strict gvlid check * add comments and shorten log messages * shorted log messages * add unit tests for setEnforcementConfig * add gvlid for alias and gvlMapping support * remove gvlid check * add support to add gvlid for aliases * add enableAnalytics hook * purpose 7 implementation: 1.hook added 2.new field to set gvlid for analytics adapters * add enableAnalytics hook * emit tcf2 events * fix regression * modify mechanism of event emitted after auction end * add unit test for enableAnalyticsHook * add unit test for auction end event Co-authored-by: Jaimin Panchal --- modules/appnexusAnalyticsAdapter.js | 3 +- modules/gdprEnforcement.js | 119 ++++++++++++++++++++-- src/adapterManager.js | 12 ++- src/constants.json | 4 +- src/prebid.js | 15 ++- test/spec/modules/gdprEnforcement_spec.js | 116 +++++++++++++++++++-- 6 files changed, 242 insertions(+), 27 deletions(-) diff --git a/modules/appnexusAnalyticsAdapter.js b/modules/appnexusAnalyticsAdapter.js index d697d31cdd3..868b317d7d4 100644 --- a/modules/appnexusAnalyticsAdapter.js +++ b/modules/appnexusAnalyticsAdapter.js @@ -13,7 +13,8 @@ var appnexusAdapter = adapter({ adapterManager.registerAnalyticsAdapter({ adapter: appnexusAdapter, - code: 'appnexus' + code: 'appnexus', + gvlid: 32 }); export default appnexusAdapter; diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 0a32441c813..97eaedd92be 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -16,9 +16,13 @@ import { EVENTS } from '../src/constants.json'; const TCF2 = { 'purpose1': { id: 1, name: 'storage' }, - 'purpose2': { id: 2, name: 'basicAds' } + 'purpose2': { id: 2, name: 'basicAds' }, + 'purpose7': { id: 7, name: 'measurement' } } +/* + These rules would be used if `consentManagement.gdpr.rules` is undefined by the publisher. +*/ const DEFAULT_RULES = [{ purpose: 'storage', enforcePurpose: true, @@ -33,9 +37,21 @@ const DEFAULT_RULES = [{ export let purpose1Rule; export let purpose2Rule; -let addedDeviceAccessHook = false; +export let purpose7Rule; + export let enforcementRules; +const storageBlocked = []; +const biddersBlocked = []; +const analyticsBlocked = []; + +let addedDeviceAccessHook = false; + +/** + * Returns gvlId for Bid Adapters. If a bidder does not have an associated gvlId, it returns 'undefined'. + * @param {string=} bidderCode - The 'code' property on the Bidder spec. + * @retuns {number} gvlId + */ function getGvlid(bidderCode) { let gvlid; bidderCode = bidderCode || config.getCurrentBidder(); @@ -53,6 +69,11 @@ function getGvlid(bidderCode) { return gvlid; } +/** + * Returns gvlId for userId module. If a userId modules does not have an associated gvlId, it returns 'undefined'. + * @param {Object} userIdModule + * @retuns {number} gvlId + */ function getGvlidForUserIdModule(userIdModule) { let gvlId; const gvlMapping = config.getConfig('gvlMapping'); @@ -64,6 +85,22 @@ function getGvlidForUserIdModule(userIdModule) { return gvlId; } +/** + * Returns gvlId for analytics adapters. If a analytics adapter does not have an associated gvlId, it returns 'undefined'. + * @param {string} code - 'provider' property on the analytics adapter config + * @returns {number} gvlId + */ +function getGvlidForAnalyticsAdapter(code) { + let gvlId; + const gvlMapping = config.getConfig('gvlMapping'); + if (gvlMapping && gvlMapping[code]) { + gvlId = gvlMapping[code]; + } else { + gvlId = adapterManager.getAnalyticsAdapter(code).gvlid; + } + return gvlId; +} + /** * 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. @@ -136,8 +173,9 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { result.valid = true; fn.call(this, gvlid, moduleName, result); } else { - curModule && utils.logWarn(`Device access denied for ${curModule} by TCF2`); + curModule && utils.logWarn(`TCF2 denied device access for ${curModule}`); result.valid = false; + storageBlocked.push(curModule); fn.call(this, gvlid, moduleName, result); } } else { @@ -168,6 +206,7 @@ export function userSyncHook(fn, ...args) { fn.call(this, ...args); } else { utils.logWarn(`User sync not allowed for ${curBidder}`); + storageBlocked.push(curBidder); } } else { // The module doesn't enforce TCF1.1 strings @@ -195,10 +234,11 @@ export function userIdHook(fn, submodules, consentData) { return submodule; } else { utils.logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); + storageBlocked.push(moduleName); } return undefined; }).filter(module => module) - fn.call(this, userIdModules, {...consentData, hasValidated: true}); + fn.call(this, userIdModules, { ...consentData, hasValidated: true }); } else { // The module doesn't enforce TCF1.1 strings fn.call(this, submodules, consentData); @@ -209,8 +249,8 @@ export function userIdHook(fn, submodules, consentData) { } /** - * Checks if a bidder is allowed in Auction. - * Enforces "purpose 2 (basic ads)" of TCF v2.0 spec + * Checks if bidders are allowed in the auction. + * Enforces "purpose 2 (Basic Ads)" of TCF v2.0 spec * @param {Function} fn - Function reference to the original function. * @param {Array} adUnits */ @@ -218,17 +258,15 @@ export function makeBidRequestsHook(fn, adUnits, ...args) { const consentData = gdprDataHandler.getConsentData(); if (consentData && consentData.gdprApplies) { if (consentData.apiVersion === 2) { - const disabledBidders = []; adUnits.forEach(adUnit => { adUnit.bids = adUnit.bids.filter(bid => { const currBidder = bid.bidder; const gvlId = getGvlid(currBidder); - if (includes(disabledBidders, currBidder)) return false; + if (includes(biddersBlocked, currBidder)) return false; const isAllowed = !!validateRules(purpose2Rule, consentData, currBidder, gvlId); if (!isAllowed) { utils.logWarn(`TCF2 blocked auction for ${currBidder}`); - events.emit(EVENTS.BIDDER_BLOCKED, currBidder); - disabledBidders.push(currBidder); + biddersBlocked.push(currBidder); } return isAllowed; }); @@ -243,8 +281,64 @@ export function makeBidRequestsHook(fn, adUnits, ...args) { } } +/** + * Checks if Analytics adapters are allowed to send data to their servers for furhter processing. + * Enforces "purpose 7 (Measurement)" of TCF v2.0 spec + * @param {Function} fn - Function reference to the original function. + * @param {Array} config - Configuration object passed to pbjs.enableAnalytics() + */ +export function enableAnalyticsHook(fn, config) { + const consentData = gdprDataHandler.getConsentData(); + if (consentData && consentData.gdprApplies) { + if (consentData.apiVersion === 2) { + if (!utils.isArray(config)) { + config = [config] + } + config = config.filter(conf => { + const analyticsAdapterCode = conf.provider; + const gvlid = getGvlidForAnalyticsAdapter(analyticsAdapterCode); + const isAllowed = !!validateRules(purpose7Rule, consentData, analyticsAdapterCode, gvlid); + if (!isAllowed) { + analyticsBlocked.push(analyticsAdapterCode); + utils.logWarn(`TCF2 blocked analytics adapter ${conf.provider}`); + } + return isAllowed; + }); + fn.call(this, config); + } else { + // This module doesn't enforce TCF1.1 strings + fn.call(this, config); + } + } else { + fn.call(this, config); + } +} + +/** + * Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event. + */ +function emitTCF2FinalResults() { + // remove null and duplicate values + const formatArray = function (arr) { + return arr.filter((i, k) => i !== null && arr.indexOf(i) === k); + } + const tcf2FinalResults = { + storageBlocked: formatArray(storageBlocked), + biddersBlocked: formatArray(biddersBlocked), + analyticsBlocked: formatArray(analyticsBlocked) + }; + + events.emit(EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); +} + +events.on(EVENTS.AUCTION_END, emitTCF2FinalResults); + +/* + Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find(). +*/ const hasPurpose1 = (rule) => { return rule.purpose === TCF2.purpose1.name } const hasPurpose2 = (rule) => { return rule.purpose === TCF2.purpose2.name } +const hasPurpose7 = (rule) => { return rule.purpose === TCF2.purpose7.name } /** * A configuration function that initializes some module variables, as well as adds hooks @@ -261,6 +355,7 @@ export function setEnforcementConfig(config) { purpose1Rule = find(enforcementRules, hasPurpose1); purpose2Rule = find(enforcementRules, hasPurpose2); + purpose7Rule = find(enforcementRules, hasPurpose7); if (!purpose1Rule) { purpose1Rule = DEFAULT_RULES[0]; @@ -280,6 +375,10 @@ export function setEnforcementConfig(config) { if (purpose2Rule) { getHook('makeBidRequests').before(makeBidRequestsHook); } + + if (purpose7Rule) { + getHook('enableAnalyticsCb').before(enableAnalyticsHook); + } } config.getConfig('consentManagement', config => setEnforcementConfig(config.consentManagement)); diff --git a/src/adapterManager.js b/src/adapterManager.js index 06ccba9787e..5124ae99694 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -468,11 +468,11 @@ adapterManager.aliasBidAdapter = function (bidderCode, alias, options) { } }; -adapterManager.registerAnalyticsAdapter = function ({adapter, code}) { +adapterManager.registerAnalyticsAdapter = function ({adapter, code, gvlid}) { if (adapter && code) { if (typeof adapter.enableAnalytics === 'function') { adapter.code = code; - _analyticsRegistry[code] = adapter; + _analyticsRegistry[code] = { adapter, gvlid }; } else { utils.logError(`Prebid Error: Analytics adaptor error for analytics "${code}" analytics adapter must implement an enableAnalytics() function`); @@ -488,7 +488,7 @@ adapterManager.enableAnalytics = function (config) { } utils._each(config, adapterConfig => { - var adapter = _analyticsRegistry[adapterConfig.provider]; + var adapter = _analyticsRegistry[adapterConfig.provider].adapter; if (adapter) { adapter.enableAnalytics(adapterConfig); } else { @@ -496,12 +496,16 @@ adapterManager.enableAnalytics = function (config) { ${adapterConfig.provider}.`); } }); -}; +} adapterManager.getBidAdapter = function(bidder) { return _bidderRegistry[bidder]; }; +adapterManager.getAnalyticsAdapter = function(code) { + return _analyticsRegistry[code]; +} + // the s2sTesting module is injected when it's loaded rather than being imported // importing it causes the packager to include it even when it's not explicitly included in the build export function setS2STestingModule(module) { diff --git a/src/constants.json b/src/constants.json index 946c43754d5..1b5feda6a05 100644 --- a/src/constants.json +++ b/src/constants.json @@ -36,8 +36,8 @@ "BEFORE_REQUEST_BIDS": "beforeRequestBids", "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", - "AD_RENDER_FAILED" : "adRenderFailed", - "BIDDER_BLOCKED": "bidderBlocked" + "AD_RENDER_FAILED": "adRenderFailed", + "TCF2_ENFORCEMENT": "tcf2Enforcement" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocuemnt", diff --git a/src/prebid.js b/src/prebid.js index 67402a995cf..827ba2e2270 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -536,8 +536,9 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo auction.callBids(); }); -export function executeStorageCallbacks(fn, reqBidsConfigObj) { +export function executeCallbacks(fn, reqBidsConfigObj) { runAll(storageCallbacks); + runAll(enableAnalyticsCallbacks); fn.call(this, reqBidsConfigObj); function runAll(queue) { var queued; @@ -548,7 +549,7 @@ export function executeStorageCallbacks(fn, reqBidsConfigObj) { } // This hook will execute all storage callbacks which were registered before gdpr enforcement hook was added. Some bidders, user id modules use storage functions when module is parsed but gdpr enforcement hook is not added at that stage as setConfig callbacks are yet to be called. Hence for such calls we execute all the stored callbacks just before requestBids. At this hook point we will know for sure that gdprEnforcement module is added or not -$$PREBID_GLOBAL$$.requestBids.before(executeStorageCallbacks, 49); +$$PREBID_GLOBAL$$.requestBids.before(executeCallbacks, 49); /** * @@ -667,13 +668,21 @@ $$PREBID_GLOBAL$$.createBid = function (statusCode) { * @param {Object} config.options The options for this particular analytics adapter. This will likely vary between adapters. * @alias module:pbjs.enableAnalytics */ -$$PREBID_GLOBAL$$.enableAnalytics = function (config) { + +// Stores 'enableAnalytics' callbacks for later execution. +const enableAnalyticsCallbacks = []; + +const enableAnalyticsCb = hook('async', function (config) { if (config && !utils.isEmpty(config)) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.enableAnalytics for: ', config); adapterManager.enableAnalytics(config); } else { utils.logError('$$PREBID_GLOBAL$$.enableAnalytics should be called with option {}'); } +}, 'enableAnalyticsCb'); + +$$PREBID_GLOBAL$$.enableAnalytics = function (config) { + enableAnalyticsCallbacks.push(enableAnalyticsCb.bind(this, config)); }; /** diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index 9b02f74f4bb..d5c8d7bfb88 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -1,11 +1,20 @@ -import { deviceAccessHook, setEnforcementConfig, userSyncHook, userIdHook, makeBidRequestsHook, validateRules, enforcementRules, purpose1Rule, purpose2Rule } from 'modules/gdprEnforcement.js'; +import { + deviceAccessHook, + setEnforcementConfig, + userSyncHook, + userIdHook, + makeBidRequestsHook, + validateRules, + enforcementRules, + purpose1Rule, + purpose2Rule, + enableAnalyticsHook +} 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 { executeStorageCallbacks } from 'src/prebid.js'; import events from 'src/events.js'; -import { EVENTS } from 'src/constants.json'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -36,7 +45,8 @@ describe('gdpr enforcement', function () { 'consents': { '1': true, '2': true, - '3': true + '3': true, + '7': true }, 'legitimateInterests': { '1': false, @@ -87,7 +97,7 @@ describe('gdpr enforcement', function () { after(function () { validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); - $$PREBID_GLOBAL$$.requestBids.getHooks({ hook: executeStorageCallbacks }).remove(); + $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); adapterManager.makeBidRequests.getHooks({ hook: makeBidRequestsHook }).remove(); }) @@ -662,8 +672,6 @@ describe('gdpr enforcement', function () { }], []); expect(logWarnSpy.calledOnce).to.equal(true); - expect(emitEventSpy.calledOnce).to.equal(true); - sinon.assert.calledWith(emitEventSpy, EVENTS.BIDDER_BLOCKED, 'bidder_2'); }); it('should skip validation checks if GDPR version is not equal to "2"', function () { @@ -694,6 +702,71 @@ describe('gdpr enforcement', function () { }); }); + describe('enableAnalyticsHook', function () { + let sandbox; + let adapterManagerStub; + + const MOCK_ANALYTICS_ADAPTER_CONFIG = [{ + provider: 'analyticsAdapter_A', + options: {} + }, { + provider: 'analyticsAdapter_B', + options: {} + }, { + provider: 'analyticsAdapter_C', + options: {} + }]; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); + adapterManagerStub = sandbox.stub(adapterManager, 'getAnalyticsAdapter'); + logWarnSpy = sandbox.spy(utils, 'logWarn'); + nextFnSpy = sandbox.spy(); + }); + + afterEach(function() { + config.resetConfig(); + sandbox.restore(); + }); + + it('should block analytics adapter which does not have consent and allow the one(s) which have consent', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'measurement', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: ['analyticsAdapter_B'] + }] + } + }); + + const consentData = {}; + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + + gdprDataHandlerStub.returns(consentData); + adapterManagerStub.withArgs('analyticsAdapter_A').returns({ gvlid: 3 }); + adapterManagerStub.withArgs('analyticsAdapter_B').returns({ gvlid: 5 }); + adapterManagerStub.withArgs('analyticsAdapter_C').returns({ gvlid: 1 }); + + enableAnalyticsHook(nextFnSpy, MOCK_ANALYTICS_ADAPTER_CONFIG); + + // Assertions + expect(nextFnSpy.calledOnce).to.equal(true); + sinon.assert.calledWith(nextFnSpy, [{ + provider: 'analyticsAdapter_B', + options: {} + }, { + provider: 'analyticsAdapter_C', + options: {} + }]); + expect(logWarnSpy.calledOnce).to.equal(true); + }); + }); + describe('validateRules', function () { const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = []) => ({ purpose: purposeName, @@ -965,4 +1038,33 @@ describe('gdpr enforcement', function () { expect(enforcementRules).to.deep.equal(rules); }); }); + + describe('TCF2FinalResults', function() { + let sandbox; + beforeEach(function() { + sandbox = sinon.createSandbox(); + sandbox.spy(events, 'emit'); + }); + afterEach(function() { + config.resetConfig(); + sandbox.restore(); + }); + it('should emit TCF2 enforcement data on auction end', function() { + const rules = [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: false + }, { + purpose: 'basicAds', + enforcePurpose: false, + enforceVendor: false + }] + setEnforcementConfig({gdpr: { rules }}); + + events.emit('auctionEnd', {}) + + // Assertions + sinon.assert.calledWith(events.emit.getCall(1), 'tcf2Enforcement', sinon.match.object); + }) + }); }); From a761b3173b4e0a14fb85fe9f592140fc657263e2 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Thu, 13 Aug 2020 15:13:19 -0400 Subject: [PATCH 0081/1476] prebid.js 4.3.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3594d6d7621..b61e3e338eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.3.0-pre", + "version": "4.3.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 22a59bdc3aa27d0adfad02a65d0a6222bf45d23d Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Thu, 13 Aug 2020 15:27:04 -0400 Subject: [PATCH 0082/1476] 4.4.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b61e3e338eb..3cb04dc6f8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.3.0", + "version": "4.4.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7ede93b9f4b20cb08aea0faa88e95c2fbf75aadd Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Fri, 14 Aug 2020 06:31:45 -0700 Subject: [PATCH 0083/1476] PubMatic to support passing content object set in pbjs.setConfig (#5592) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * read content object from config and send it in site.content and app.content * do not use content object from config if content object is present in app as app.content * fixed the test-cases * app.content related test cases --- modules/pubmaticBidAdapter.js | 10 +++ test/spec/modules/pubmaticBidAdapter_spec.js | 73 ++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 2e52fd27cf1..437e37de83e 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -865,6 +865,11 @@ export const spec = { payload.site.page = conf.kadpageurl.trim() || payload.site.page.trim(); payload.site.domain = _getDomainFromURL(payload.site.page); + // add the content object from config in request + if (typeof config.getConfig('content') === 'object') { + payload.site.content = config.getConfig('content'); + } + // merge the device from config.getConfig('device') if (typeof config.getConfig('device') === 'object') { payload.device = Object.assign(payload.device, config.getConfig('device')); @@ -910,6 +915,11 @@ export const spec = { // not copying domain from site as it is a derived value from page payload.app.publisher = payload.site.publisher; payload.app.ext = payload.site.ext || UNDEFINED; + // We will also need to pass content object in app.content if app object is also set into the config; + // BUT do not use content object from config if content object is present in app as app.content + if (typeof payload.app.content !== 'object') { + payload.app.content = payload.site.content || UNDEFINED; + } delete payload.site; } diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index dd46646abc8..64f88f0c906 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -776,6 +776,23 @@ describe('PubMatic adapter', function () { expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); + it('Set content from config, set site.content', function() { + let sandbox = sinon.sandbox.create(); + const content = { + 'id': 'alpha-numeric-id' + }; + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + content: content + }; + return config[key]; + }); + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.site.content).to.deep.equal(content); + sandbox.restore(); + }); + it('Merge the device info from config', function() { let sandbox = sinon.sandbox.create(); sandbox.stub(config, 'getConfig').callsFake((key) => { @@ -840,6 +857,62 @@ describe('PubMatic adapter', function () { sandbox.restore(); }); + it('Set app, content from config, copy publisher and ext from site, unset site, config.content in app.content', function() { + let sandbox = sinon.sandbox.create(); + const content = { + 'id': 'alpha-numeric-id' + }; + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + content: content, + app: { + bundle: 'org.prebid.mobile.demoapp', + domain: 'prebid.org' + } + }; + return config[key]; + }); + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); + expect(data.app.domain).to.equal('prebid.org'); + expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); + expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); + expect(data.app.content).to.deep.equal(content); + expect(data.site).to.not.exist; + sandbox.restore(); + }); + + it('Set app.content, content from config, copy publisher and ext from site, unset site, config.app.content in app.content', function() { + let sandbox = sinon.sandbox.create(); + const content = { + 'id': 'alpha-numeric-id' + }; + const appContent = { + id: 'app-content-id-2' + }; + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + content: content, + app: { + bundle: 'org.prebid.mobile.demoapp', + domain: 'prebid.org', + content: appContent + } + }; + return config[key]; + }); + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); + expect(data.app.domain).to.equal('prebid.org'); + expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); + expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); + expect(data.app.content).to.deep.equal(appContent); + expect(data.site).to.not.exist; + sandbox.restore(); + }); + it('Request params check: without adSlot', function () { delete bidRequests[0].params.adSlot; let request = spec.buildRequests(bidRequests); From db02d8a252b928a052748a2e307876c46708d560 Mon Sep 17 00:00:00 2001 From: pm-shashank-jain <40654031+pm-shashank-jain@users.noreply.github.com> Date: Fri, 14 Aug 2020 19:11:50 +0530 Subject: [PATCH 0084/1476] PubMatic: Support for Outstream Renderer (BlueBilyWig Renderer) (#5553) * changes to support native in pubmaticbid adapter * Removed port from endpoint * Removed protocol from endpoint * Formatting * Fix request payload * Updated test case * Changed request and response as per ortb spec * Change in request and response * Removed comments and extra code * Code Review comments * Code Review Comments and Test cases for request and response * Removed data type as all data asset types are handled * Code Review Changes * Code Review Comments * Supporting both banner and native and sending 0x0 in case of native * Bug Fixes * Bug response not processed by prebid * Change warning message * Fixed typo * Do not send request in case of invalid native bid * Do not send request in case of invalid native requests * objects converted to strings in log for debug purposes * Fixed logic to check for required parmas * Fixed typo for stringify * documentation for native * Review comments from Prebid * Typo * Typo * Updated pub id for native * Code Review * Support for pubid * Test Cases for PubCommonId in PubMatic adapter * Delete yarn.lock * Rename adaptermanager.js to adapterManager.js * Rename yieldNexusBidAdapter.js to yieldnexusBidAdapter.js * Rename yieldNexusBidAdapter.md to yieldnexusBidAdapter.md * Rename yieldNexusBidAdapter_spec.js to yieldnexusBidAdapter_spec.yieldnexusBidAdaptera * Rename yieldnexusBidAdapter_spec.yieldnexusBidAdaptera to yieldnexusBidAdapter_spec.js * bluebillywig outstream player support in pubmatic adapter * removed pubcommon id test cases * BBW Renderer --- modules/pubmaticBidAdapter.js | 91 ++++++++++++- modules/pubmaticBidAdapter.md | 1 + test/spec/modules/pubmaticBidAdapter_spec.js | 132 ++++++++++++++++++- 3 files changed, 219 insertions(+), 5 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 437e37de83e..d21854a57c4 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -2,6 +2,7 @@ import * as utils 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'; +import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'pubmatic'; const LOG_WARN_PREFIX = 'PubMatic: '; @@ -14,6 +15,8 @@ const UNDEFINED = undefined; const DEFAULT_WIDTH = 0; const DEFAULT_HEIGHT = 0; const PREBID_NATIVE_HELP_LINK = 'http://prebid.org/dev-docs/show-native-ads.html'; +const PUBLICATION = 'pubmatic'; // Your publication on Blue Billywig, potentially with environment (e.g. publication.bbvms.com or publication.test.bbvms.com) +const RENDERER_URL = 'https://pubmatic.bbvms.com/r/'.concat('$RENDERER', '.js'); // URL of the renderer application const CUSTOM_PARAMS = { 'kadpageurl': '', // Custom page url 'gender': '', // User gender @@ -104,6 +107,60 @@ const dealChannelValues = { 5: 'PREF', 6: 'PMPG' }; +// BB stands for Blue BillyWig +const BB_RENDERER = { + bootstrapPlayer: function(bid) { + const config = { + code: bid.adUnitCode, + }; + + if (bid.vastXml) config.vastXml = bid.vastXml; + else if (bid.vastUrl) config.vastUrl = bid.vastUrl; + + if (!bid.vastXml && !bid.vastUrl) { + utils.logWarn(`${LOG_WARN_PREFIX}: No vastXml or vastUrl on bid, bailing...`); + return; + } + + const rendererId = BB_RENDERER.getRendererId(PUBLICATION, bid.rendererCode); + + const ele = document.getElementById(bid.adUnitCode); // NB convention + + let renderer; + + for (let rendererIndex = 0; rendererIndex < window.bluebillywig.renderers.length; rendererIndex++) { + if (window.bluebillywig.renderers[rendererIndex]._id === rendererId) { + renderer = window.bluebillywig.renderers[rendererIndex]; + break; + } + } + + if (renderer) renderer.bootstrap(config, ele); + else utils.logWarn(`${LOG_WARN_PREFIX}: Couldn't find a renderer with ${rendererId}`); + }, + newRenderer: function(rendererCode, adUnitCode) { + var rendererUrl = RENDERER_URL.replace('$RENDERER', rendererCode); + const renderer = Renderer.install({ + url: rendererUrl, + loaded: false, + adUnitCode + }); + + try { + renderer.setRender(BB_RENDERER.outstreamRender); + } catch (err) { + utils.logWarn(`${LOG_WARN_PREFIX}: Error tying to setRender on renderer`, err); + } + + return renderer; + }, + outstreamRender: function(bid) { + bid.renderer.push(function() { BB_RENDERER.bootstrapPlayer(bid) }); + }, + getRendererId: function(pub, renderer) { + return `${pub}-${renderer}`; // NB convention! + } +}; let publisherId = 0; let isInvalidNativeRequest = false; @@ -760,6 +817,23 @@ function _handleDealCustomTargetings(payload, dctrArr, validBidRequests) { } } +function _assignRenderer(newBid, request) { + let bidParams, context, adUnitCode; + if (request.bidderRequest && request.bidderRequest.bids) { + for (let bidderRequestBidsIndex = 0; bidderRequestBidsIndex < request.bidderRequest.bids.length; bidderRequestBidsIndex++) { + if (request.bidderRequest.bids[bidderRequestBidsIndex].bidId === newBid.requestId) { + bidParams = request.bidderRequest.bids[bidderRequestBidsIndex].params; + context = request.bidderRequest.bids[bidderRequestBidsIndex].mediaTypes[VIDEO].context; + adUnitCode = request.bidderRequest.bids[bidderRequestBidsIndex].adUnitCode; + } + } + if (context && context === 'outstream' && bidParams && bidParams.outstreamAU && adUnitCode) { + newBid.rendererCode = bidParams.outstreamAU; + newBid.renderer = BB_RENDERER.newRenderer(newBid.rendererCode, adUnitCode); + } + } +}; + export const spec = { code: BIDDER_CODE, gvlid: 76, @@ -782,6 +856,19 @@ export const spec = { utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); return false; } + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { + utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); + return false; + } + if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU)) { + utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids outstreamAU is required. Rejecting bid: `, bid); + return false; + } + } else { + utils.logError(`${LOG_WARN_PREFIX}: mediaTypes or mediaTypes.video is not specified. Rejecting bid: `, bid); + return false; + } } return true; } @@ -926,7 +1013,8 @@ export const spec = { return { method: 'POST', url: ENDPOINT, - data: JSON.stringify(payload) + data: JSON.stringify(payload), + bidderRequest: bidderRequest }; }, @@ -976,6 +1064,7 @@ export const spec = { newBid.width = bid.hasOwnProperty('w') ? bid.w : req.video.w; newBid.height = bid.hasOwnProperty('h') ? bid.h : req.video.h; newBid.vastXml = bid.adm; + _assignRenderer(newBid, request); break; case NATIVE: _parseNativeResponse(bid, newBid); diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index a045bed3e2b..cd9398477f4 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -25,6 +25,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required + oustreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream'. This value can be get by BlueBillyWig Team. adSlot: 'pubmatic_test2', // optional pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 64f88f0c906..0f51a8df61c 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -26,6 +26,9 @@ describe('PubMatic adapter', function () { let bannerBidResponse; let videoBidResponse; let schainConfig; + let outstreamBidRequest; + let validOutstreamBidRequest; + let outstreamVideoBidResponse; beforeEach(function () { schainConfig = { @@ -55,7 +58,7 @@ describe('PubMatic adapter', function () { } }, params: { - publisherId: '301', + publisherId: '5670', adSlot: '/15671365/DMDemo@300x250:0', kadfloor: '1.2', pmzoneid: 'aabc, ddef', @@ -656,7 +659,89 @@ describe('PubMatic adapter', function () { }] }] } - } + }; + outstreamBidRequest = + [ + { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream' + } + }, + bidder: 'pubmatic', + bidId: '47acc48ad47af5', + requestId: '0fb4905b-1234-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + params: { + publisherId: '5670', + outstreamAU: 'pubmatic-test', + adSlot: 'Div1@0x0', // ad_id or tagid + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30 + } + } + } + ]; + + validOutstreamBidRequest = { + auctionId: '92489f71-1bf2-49a0-adf9-000cea934729', + auctionStart: 1585918458868, + bidderCode: 'pubmatic', + bidderRequestId: '47acc48ad47af5', + bids: [{ + adUnitCode: 'video1', + auctionId: '92489f71-1bf2-49a0-adf9-000cea934729', + bidId: '47acc48ad47af5', + bidRequestsCount: 1, + bidder: 'pubmatic', + bidderRequestId: '47acc48ad47af5', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: { + publisherId: '5670', + outstreamAU: 'pubmatic-test', + adSlot: 'Div1@0x0', // ad_id or tagid + video: { + mimes: ['video/mp4', 'video/x-flv'], + skippable: true, + minduration: 5, + maxduration: 30 + } + }, + sizes: [[768, 432], [640, 480], [630, 360]], + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + }], + start: 11585918458869, + timeout: 3000 + }; + + outstreamVideoBidResponse = { + 'body': { + 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', + 'seatbid': [{ + 'bid': [{ + 'id': '0fb4905b-1234-4152-86be-c6f6d259ba99', + 'impid': '47acc48ad47af5', + 'price': 1.3, + 'adm': 'Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.pubmatic.com', + 'h': 250, + 'w': 300, + 'ext': { + 'deal_channel': 6 + } + }] + }] + } + }; }); describe('implementation', function () { @@ -725,7 +810,17 @@ describe('PubMatic adapter', function () { let request = spec.buildRequests(bidRequests); expect(request.url).to.equal('https://hbopenbid.pubmatic.com/translator?source=prebid-client'); expect(request.method).to.equal('POST'); - }); + }); + + it('should return bidderRequest property', function() { + let request = spec.buildRequests(bidRequests, validOutstreamBidRequest); + expect(request.bidderRequest).to.equal(validOutstreamBidRequest); + }); + + it('bidderRequest should be undefined if bidderRequest is not present', function() { + let request = spec.buildRequests(bidRequests); + expect(request.bidderRequest).to.be.undefined; + }); it('test flag not sent when pubmaticTest=true is absent in page url', function() { let request = spec.buildRequests(bidRequests); @@ -2454,7 +2549,6 @@ describe('PubMatic adapter', function () { it('should check for valid video mediaType in case of multiformat request', function() { let request = spec.buildRequests(videoBidRequests); let response = spec.interpretResponse(videoBidResponse, request); - expect(response[0].mediaType).to.equal('video'); }); @@ -2464,6 +2558,36 @@ describe('PubMatic adapter', function () { expect(response[0].mediaType).to.equal('native'); }); + + it('should assign renderer if bid is video and request is for outstream', function() { + let request = spec.buildRequests(outstreamBidRequest, validOutstreamBidRequest); + let response = spec.interpretResponse(outstreamVideoBidResponse, request); + expect(response[0].renderer).to.exist; + }); + + it('should not assign renderer if bidderRequest is not present', function() { + let request = spec.buildRequests(outstreamBidRequest); + let response = spec.interpretResponse(outstreamVideoBidResponse, request); + expect(response[0].renderer).to.not.exist; + }); + + it('should not assign renderer if bid is video and request is for instream', function() { + let request = spec.buildRequests(videoBidRequests); + let response = spec.interpretResponse(videoBidResponse, request); + expect(response[0].renderer).to.not.exist; + }); + + it('should not assign renderer if bid is native', function() { + let request = spec.buildRequests(nativeBidRequests); + let response = spec.interpretResponse(nativeBidResponse, request); + expect(response[0].renderer).to.not.exist; + }); + + it('should not assign renderer if bid is of banner', function() { + let request = spec.buildRequests(bidRequests); + let response = spec.interpretResponse(bidResponses, request); + expect(response[0].renderer).to.not.exist; + }); }); describe('getUserSyncs', function() { From 84e2361cdf6fd82c5d4b1de3a74dab2e33e7f9c4 Mon Sep 17 00:00:00 2001 From: Dan Harton Date: Mon, 17 Aug 2020 10:24:48 -0700 Subject: [PATCH 0085/1476] AdButler Bid Adapter: Add Doceree as alias (#5598) --- modules/adbutlerBidAdapter.js | 2 +- modules/docereeBidAdapter.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 modules/docereeBidAdapter.md diff --git a/modules/adbutlerBidAdapter.js b/modules/adbutlerBidAdapter.js index 06c2eea5d67..10edd8ae3e3 100644 --- a/modules/adbutlerBidAdapter.js +++ b/modules/adbutlerBidAdapter.js @@ -9,7 +9,7 @@ const BIDDER_CODE = 'adbutler'; export const spec = { code: BIDDER_CODE, pageID: Math.floor(Math.random() * 10e6), - aliases: ['divreach'], + aliases: ['divreach', 'doceree'], isBidRequestValid: function (bid) { return !!(bid.params.accountID && bid.params.zoneID); diff --git a/modules/docereeBidAdapter.md b/modules/docereeBidAdapter.md new file mode 100644 index 00000000000..9e3d4bbe1b8 --- /dev/null +++ b/modules/docereeBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +Module Name: Doceree Bidder Adapter +Module Type: Bidder Adapter + +# Description + +Connects to Doceree demand source to fetch bids. +Please use ```doceree``` as the bidder code. + +# Test Parameters +``` + var adUnits = [ + { + code: 'desktop-banner-ad-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "doceree", + params: { + accountID: '167283', + zoneID: '445501', + domain: 'adbserver.doceree.com', + extra: { + tuid: '1234-abcd' + } + } + } + ] + }, + ]; +``` From 952e0fb651316bdd8d35af60d1d1cc3c76d06647 Mon Sep 17 00:00:00 2001 From: rtuschkany <35923908+rtuschkany@users.noreply.github.com> Date: Mon, 17 Aug 2020 19:26:53 +0200 Subject: [PATCH 0086/1476] Digitrust removal, broader identity support, floor-module support (#5599) * Digitrust remove, broader identity support, floor-module support * Digitrust removal, broader identity support, floor-module support --- modules/connectadBidAdapter.js | 95 +++++-------------- test/spec/modules/connectadBidAdapter_spec.js | 24 ++++- 2 files changed, 45 insertions(+), 74 deletions(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 8b34df563ec..04459ec1f09 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -2,6 +2,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' import {config} from '../src/config.js'; +import {createEidsArray} from './userId/eids.js'; const BIDDER_CODE = 'connectad'; const BIDDER_CODE_ALIAS = 'connectadrealtime'; @@ -18,8 +19,6 @@ export const spec = { }, buildRequests: function(validBidRequests, bidderRequest) { - let digitrust; - let ret = { method: 'POST', url: '', @@ -41,7 +40,8 @@ export const spec = { screensize: getScreenSize(), dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, language: navigator.language, - ua: navigator.userAgent + ua: navigator.userAgent, + pversion: '$prebid.version$' }); // coppa compliance @@ -69,80 +69,19 @@ export const spec = { utils.deepSetValue(data, 'user.ext.us_privacy', bidderRequest.uspConsent); } - // Digitrust Support - const bidRequestDigitrust = utils.deepAccess(validBidRequests[0], 'userId.digitrustid.data'); - if (bidRequestDigitrust && (!bidRequestDigitrust.privacy || !bidRequestDigitrust.privacy.optout)) { - digitrust = { - id: bidRequestDigitrust.id, - keyv: bidRequestDigitrust.keyv - } - } - - if (digitrust) { - utils.deepSetValue(data, 'user.ext.digitrust', { - id: digitrust.id, - keyv: digitrust.keyv - }) - } - - if (validBidRequests[0].userId && typeof validBidRequests[0].userId === 'object' && (validBidRequests[0].userId.tdid || validBidRequests[0].userId.pubcid || validBidRequests[0].userId.lipb || validBidRequests[0].userId.id5id || validBidRequests[0].userId.parrableId)) { - utils.deepSetValue(data, 'user.ext.eids', []); - - if (validBidRequests[0].userId.tdid) { - data.user.ext.eids.push({ - source: 'adserver.org', - uids: [{ - id: validBidRequests[0].userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } - - if (validBidRequests[0].userId.pubcid) { - data.user.ext.eids.push({ - source: 'pubcommon', - uids: [{ - id: validBidRequests[0].userId.pubcid, - }] - }); - } - - if (validBidRequests[0].userId.id5id) { - data.user.ext.eids.push({ - source: 'id5-sync.com', - uids: [{ - id: validBidRequests[0].userId.id5id, - }] - }); - } - - if (validBidRequests[0].userId.parrableId) { - data.user.ext.eids.push({ - source: 'parrable.com', - uids: [{ - id: validBidRequests[0].userId.parrableId.eid, - }] - }); - } - - if (validBidRequests[0].userId.lipb && validBidRequests[0].userId.lipb.lipbid) { - data.user.ext.eids.push({ - source: 'liveintent.com', - uids: [{ - id: validBidRequests[0].userId.lipb.lipbid - }] - }); - } + // EIDS Support + if (validBidRequests[0].userId) { + data.user.ext.eids = createEidsArray(validBidRequests[0].userId); } validBidRequests.map(bid => { const placement = Object.assign({ id: bid.transactionId, divName: bid.bidId, + pisze: bid.mediaTypes.banner.sizes[0] || bid.sizes[0], sizes: bid.mediaTypes.banner.sizes, - adTypes: getSize(bid.mediaTypes.banner.sizes || bid.sizes) + adTypes: getSize(bid.mediaTypes.banner.sizes || bid.sizes), + floor: getBidFloor(bid) }, bid.params); if (placement.networkId && placement.siteId) { @@ -277,6 +216,22 @@ sizeMap[331] = '320x250'; sizeMap[3301] = '320x267'; sizeMap[2730] = '728x250'; +function getBidFloor(bidRequest) { + let floorInfo = {}; + + if (typeof bidRequest.getFloor === 'function') { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: '*' + }); + } + + let floor = floorInfo.floor || 0; + + return floor; +} + function getSize(sizes) { const result = []; sizes.forEach(function(size) { diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index 626018241c4..aef4fb562a7 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -14,7 +14,8 @@ describe('ConnectAd Adapter', function () { bidder: 'conntectad', params: { siteId: 123456, - networkId: 123456 + networkId: 123456, + bidfloor: 0.50 }, adUnitCode: '/19968336/header-bid-tag-1', mediaTypes: { @@ -46,8 +47,7 @@ describe('ConnectAd Adapter', function () { bidderRequestId: '1c56ad30b9b8ca8', transactionId: 'e76cbb58-f3e1-4ad9-9f4c-718c1919d0df', userId: { - tdid: '123456', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} + tdid: '123456' } }]; @@ -154,6 +154,23 @@ describe('ConnectAd Adapter', function () { expect(requestparse.placements[0].networkId).to.equal(123456); }); + it('should process floors module if available', function() { + const floorInfo = { + currency: 'USD', + floor: 5.20 + }; + bidRequests[0].getFloor = () => floorInfo; + const request = spec.buildRequests(bidRequests, bidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.placements[0].floor).to.equal(5.20); + }); + + it('should be 0 if no floormodule is available', function() { + const request = spec.buildRequests(bidRequests, bidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.placements[0].floor).to.equal(0); + }); + it('should contain gdpr info', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const requestparse = JSON.parse(request.data); @@ -227,7 +244,6 @@ describe('ConnectAd Adapter', function () { const requestparse = JSON.parse(request.data); expect(requestparse.user.ext.eids).to.be.an('array'); expect(requestparse.user.ext.eids[0].uids[0].id).to.equal('123456'); - expect(requestparse.user.ext.digitrust.id).to.equal('DTID'); }); it('should add referer info', function () { From 7696428cf8165fd2ccac3e92f5fdede198cd8ac7 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 17 Aug 2020 15:52:41 -0400 Subject: [PATCH 0087/1476] Adds support for additional consent (#5600) * add addtlConsent consent to consent object * add unit test for additional consent * Update consentManagement.js * Update index.js * Update prebidServerBidAdapter_spec.js * condense else if --- modules/consentManagement.js | 5 ++- modules/prebidServerBidAdapter/index.js | 3 ++ test/spec/modules/consentManagement_spec.js | 26 ++++++++++++++++ .../modules/prebidServerBidAdapter_spec.js | 31 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 0c48d8c854c..90fe24735db 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -371,7 +371,7 @@ function cmpFailed(errMsg, hookConfig, extraArgs) { } /** - * Stores CMP data locally in module and then invokes gdprDataHandler.setConsentData() to make information available in adaptermanger.js for later in the auction + * Stores CMP data locally in module and then invokes gdprDataHandler.setConsentData() 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) { @@ -387,6 +387,9 @@ function storeConsentData(cmpConsentObject) { vendorData: (cmpConsentObject) || undefined, gdprApplies: cmpConsentObject && typeof cmpConsentObject.gdprApplies === 'boolean' ? cmpConsentObject.gdprApplies : gdprScope }; + if (cmpConsentObject.addtlConsent && utils.isStr(cmpConsentObject.addtlConsent)) { + consentData.addtlConsent = cmpConsentObject.addtlConsent; + }; } consentData.apiVersion = cmpVersion; gdprDataHandler.setConsentData(consentData); diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 1007305b324..b3d559d956f 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -641,6 +641,9 @@ const OPEN_RTB_PROTOCOL = { } utils.deepSetValue(request, 'regs.ext.gdpr', gdprApplies); utils.deepSetValue(request, 'user.ext.consent', firstBidRequest.gdprConsent.consentString); + if (firstBidRequest.gdprConsent.addtlConsent && typeof firstBidRequest.gdprConsent.addtlConsent === 'string') { + utils.deepSetValue(request, 'user.ext.ConsentedProvidersSettings.consented_providers', firstBidRequest.gdprConsent.addtlConsent); + } } // US Privacy (CCPA) support diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 3ebebfef1ee..deaacbc5a28 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -643,6 +643,32 @@ describe('consentManagement', function () { expect(consent.apiVersion).to.equal(2); }); + it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { + let testConsentData = { + tcString: 'abc12345234', + addtlConsent: 'superduperstring', + gdprApplies: true, + purposeOneTreatment: false, + eventStatus: 'tcloaded' + }; + cmpStub = sinon.stub(window, '__tcfapi').callsFake((...args) => { + args[2](testConsentData, true); + }); + + setConsentConfig(goodConfigWithAllowAuction); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gdprDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(didHookReturn).to.be.true; + expect(consent.consentString).to.equal(testConsentData.tcString); + expect(consent.addtlConsent).to.equal(testConsentData.addtlConsent); + expect(consent.gdprApplies).to.be.true; + expect(consent.apiVersion).to.equal(2); + }); + it('throws an error when processCmpData check fails + does not call requestBids callbcack even when allowAuction is true', function () { let testConsentData = {}; let bidsBackHandlerReturn = false; diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index b75ede0e9db..5abe068c100 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -547,6 +547,37 @@ describe('S2S Adapter', function () { expect(requestBid.user).to.not.exist; }); + it('adds additional consent information to ortb2 request depending on presence of module', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = { + consentString: 'abc123', + addtlConsent: 'superduperconsent', + gdprApplies: true + }; + + adapter.callBids(REQUEST, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.regs.ext.gdpr).is.equal(1); + expect(requestBid.user.ext.consent).is.equal('abc123'); + expect(requestBid.user.ext.ConsentedProvidersSettings.consented_providers).is.equal('superduperconsent'); + + config.resetConfig(); + config.setConfig({ s2sConfig: CONFIG }); + + adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + requestBid = JSON.parse(server.requests[1].requestBody); + + expect(requestBid.regs).to.not.exist; + expect(requestBid.user).to.not.exist; + }); + it('check gdpr info gets added into cookie_sync request: have consent data', function () { let cookieSyncConfig = utils.deepClone(CONFIG); cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; From 97713dd0646f5bae4578ff323a865f74c7e017e0 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Mon, 17 Aug 2020 15:23:02 -0500 Subject: [PATCH 0088/1476] pass along providers (#5610) --- modules/rubiconAnalyticsAdapter.js | 26 ++++++++----------- .../modules/rubiconAnalyticsAdapter_spec.js | 20 ++++++++++---- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 7a1bdce84d5..6defd7b45ae 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -40,8 +40,6 @@ const cache = { timeouts: {}, }; -const validFloorProviders = ['rubicon', 'rubi', 'magnite', 'mgni']; - export function getHostNameFromReferer(referer) { try { rubiconAdapter.referrerHostname = utils.parseUrl(referer, {noDecodeWholeURL: true}).hostname; @@ -205,12 +203,13 @@ function sendMessage(auctionId, bidWonId) { } else { auction.floors = utils.pick(auctionCache.floorData, [ 'location', - 'modelName', () => auctionCache.floorData.modelVersion, + 'modelVersion as modelName', 'skipped', 'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'), 'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'), 'skipRate', - 'fetchStatus' + 'fetchStatus', + 'floorProvider as provider' ]); } } @@ -285,7 +284,7 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { if (previousBidResponse && previousBidResponse.bidPriceUSD > responsePrice) { return previousBidResponse; } - let bidResponse = utils.pick(bid, [ + return utils.pick(bid, [ 'bidPriceUSD', () => responsePrice, 'dealId', 'status', @@ -295,12 +294,9 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { 'height' ]), 'seatBidId', + 'floorValue', () => utils.deepAccess(bid, 'floorData.floorValue'), + 'floorRule', () => utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined ]); - if (auctionFloorData) { - bidResponse.floorValue = utils.deepAccess(bid, 'floorData.floorValue'); - bidResponse.floorRule = utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined - } - return bidResponse; } let samplingFactor = 1; @@ -380,9 +376,9 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { cacheEntry.bids = {}; cacheEntry.bidsWon = {}; cacheEntry.referrer = args.bidderRequests[0].refererInfo.referer; - const floorProvider = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData.floorProvider'); - if (validFloorProviders.indexOf(floorProvider) !== -1) { - cacheEntry.floorData = {...args.bidderRequests[0].bids[0].floorData}; + const floorData = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData'); + if (floorData) { + cacheEntry.floorData = {...floorData}; } cache.auctions[args.auctionId] = cacheEntry; break; @@ -468,7 +464,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { bid.adUnit.adSlot = args.floorData.matchedFields.gptSlot; } // if we have not set enforcements yet set it - if (auctionEntry.floorData && !auctionEntry.floorData.enforcements && utils.deepAccess(args, 'floorData.enforcements')) { + if (!utils.deepAccess(auctionEntry, 'floorData.enforcements') && utils.deepAccess(args, 'floorData.enforcements')) { auctionEntry.floorData.enforcements = {...args.floorData.enforcements}; } if (!bid) { @@ -492,7 +488,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }; } bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp; - bid.bidResponse = parseBidResponse(args, bid.bidResponse, auctionEntry.floorData); + bid.bidResponse = parseBidResponse(args, bid.bidResponse); break; case BIDDER_DONE: args.bids.forEach(bid => { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 466435d9652..0c2c83a4b37 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -743,7 +743,8 @@ describe('rubicon analytics adapter', function () { enforcement: true, dealsEnforced: false, skipRate: 15, - fetchStatus: 'error' + fetchStatus: 'error', + provider: 'rubicon' }); // first adUnit's adSlot expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports'); @@ -765,19 +766,28 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); }); - it('should not send floor info if provider is not rubicon', function () { + it('should still send floor info if provider is not rubicon', function () { let message = performFloorAuction('randomProvider') // verify our floor stuff is passed // top level floor info - expect(message.auctions[0].floors).to.be.undefined; + expect(message.auctions[0].floors).to.deep.equal({ + location: 'setConfig', + modelName: 'someModelName', + skipped: false, + enforcement: true, + dealsEnforced: false, + skipRate: 15, + fetchStatus: 'error', + provider: 'randomProvider' + }); // first adUnit's adSlot expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports'); // since no other bids, we set adUnit status to no-bid expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); // first adUnits bid is rejected expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected'); - expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.be.undefined; + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.equal(4); // if bid rejected should take cpmAfterAdjustments val expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); @@ -787,7 +797,7 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].status).to.equal('success'); // second adUnits bid is success expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); - expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.be.undefined; + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.equal(1); expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); }); From f983af22b8817bb23eaf1dd666769cd2ff260306 Mon Sep 17 00:00:00 2001 From: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Date: Tue, 18 Aug 2020 03:54:05 -0400 Subject: [PATCH 0089/1476] Emx bid adapter: gdpr user sync update (#5611) * adding ccpa support for emx_digital adapter * emx_digital ccpa compliance: lint fix * emx 3.0 compliance update * fix outstream renderer issue, update test spec * refactor formatVideoResponse function to use core-js/find * Added GVLID to Adapter, Updated getUserSyncs to accept and leverage gdprConsent * Added testing coverage for gdpr in getUserSyncs Co-authored-by: Nick Colletti Co-authored-by: Nick Colletti Co-authored-by: Kiyoshi Hara Co-authored-by: Dan Bogdan --- modules/emx_digitalBidAdapter.js | 14 +++++++++-- .../modules/emx_digitalBidAdapter_spec.js | 23 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 6688d15d8e9..fa58481548a 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -162,6 +162,7 @@ export const emxAdapter = { export const spec = { code: BIDDER_CODE, + gvlid: 183, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { if (!bid || !bid.params) { @@ -279,12 +280,21 @@ export const spec = { } return emxBidResponses; }, - getUserSyncs: function (syncOptions) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const syncs = []; if (syncOptions.iframeEnabled) { + let url = 'https://biddr.brealtime.com/check.html'; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + // add 'gdpr' only if 'gdprApplies' is defined + if (typeof gdprConsent.gdprApplies === 'boolean') { + url += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + url += `?gdpr_consent=${gdprConsent.consentString}`; + } + } syncs.push({ type: 'iframe', - url: 'https://biddr.brealtime.com/check.html' + url: url }); } return syncs; diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 7be8a2ce5ac..138786b9c74 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -612,12 +612,23 @@ describe('emx_digital Adapter', function () { }); describe('getUserSyncs', function () { - let syncOptionsIframe = { iframeEnabled: true }; - let syncOptionsPixel = { pixelEnabled: true }; - it('Should push the correct sync type depending on the config', function () { - let iframeSync = spec.getUserSyncs(syncOptionsIframe); - expect(iframeSync.length).to.equal(1); - expect(iframeSync[0].type).to.equal('iframe'); + it('should register the iframe sync url', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + }); + + it('should pass gdpr params', function () { + let syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + gdprApplies: false, consentString: 'test' + }); + expect(syncs).to.not.be.an('undefined'); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.contains('gdpr=0'); }); }); }); From cc58b2320f2f6808d478103a0f420fb40cf4ef5f Mon Sep 17 00:00:00 2001 From: hbanalytics <55453525+hbanalytics@users.noreply.github.com> Date: Tue, 18 Aug 2020 11:18:53 +0300 Subject: [PATCH 0090/1476] Yieldone Analytics Adapter: Fix empty events (#5617) * Added Y1 Analytics Adapter * rename y1AnalyticsAdapter in yieldoneAnalyticsAdapter * Yieldone Bid Adapter: fixes from lint check * Yieldone Analytics Adapter: fix endpoint protocol * Added spec file for yieldone Analytics Adapter * Add adUnitName to analytics data for Yieldone Analytics Adapter * Fix yieldone Analytics Adapter to log only id from adUnitPath * Fix bug with timeout event in Yieldone Analytics Adapter * Update yieldone analytics adapter to remove excess 'ad' field from data * Update yieldone analytics adapter * Yieldone Analytics Adapter: remove dispensable events from log * Platform One Analytics Adapter: fixes after review * Fix empty events in Yieldone Analytics Adapter --- modules/yieldoneAnalyticsAdapter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/yieldoneAnalyticsAdapter.js b/modules/yieldoneAnalyticsAdapter.js index dd40e205b07..542c0917708 100644 --- a/modules/yieldoneAnalyticsAdapter.js +++ b/modules/yieldoneAnalyticsAdapter.js @@ -123,7 +123,7 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { auctionManager.getAdUnitCodes(), auctionManager.getBidsReceived() ); - if (yieldoneAnalytics.eventsStorage[currentAuctionId]) { + if (yieldoneAnalytics.eventsStorage[currentAuctionId] && yieldoneAnalytics.eventsStorage[currentAuctionId].events.length) { yieldoneAnalytics.eventsStorage[currentAuctionId].page = {url: referrers[currentAuctionId]}; yieldoneAnalytics.eventsStorage[currentAuctionId].pubId = pubId; yieldoneAnalytics.eventsStorage[currentAuctionId].wrapper_version = '$prebid.version$'; @@ -139,8 +139,8 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { } } }, - sendStat(events, auctionId) { - if (!events) return; + sendStat(data, auctionId) { + if (!data || !data.events || !data.events.length) return; delete yieldoneAnalytics.eventsStorage[auctionId]; ajax( url, @@ -148,7 +148,7 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { success: function() {}, error: function() {} }, - JSON.stringify(events), + JSON.stringify(data), { method: 'POST' } From 8646ba7511abc447db20e9e99c3db474a75dd7e6 Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 18 Aug 2020 14:09:28 +0530 Subject: [PATCH 0091/1476] Add Bright Mountain Media Bid Adapter (#5593) * Add Bright Mountain Media Bid Adapter * Fix missing quotes around placement_id string * Update maintainer email --- modules/brightMountainMediaBidAdapter.js | 89 +++++++++++ modules/brightMountainMediaBidAdapter.md | 34 +++++ .../brightMountainMediaBidAdapter_spec.js | 139 ++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 modules/brightMountainMediaBidAdapter.js create mode 100644 modules/brightMountainMediaBidAdapter.md create mode 100644 test/spec/modules/brightMountainMediaBidAdapter_spec.js diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js new file mode 100644 index 00000000000..5a285be71c0 --- /dev/null +++ b/modules/brightMountainMediaBidAdapter.js @@ -0,0 +1,89 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'brightmountainmedia'; +const AD_URL = 'https://console.brightmountainmedia.com/hb/bid'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && bid.params.placement_id); + }, + + buildRequests: (validBidRequests, bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language : '', + 'secure': 1, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + if (bidderRequest) { + if (bidderRequest.gdprConsent) { + request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' + request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + } + } + for (let i = 0; i < validBidRequests.length; i++) { + let bid = validBidRequests[i]; + let traff = bid.params.traffic || BANNER + let placement = { + placementId: bid.params.placement_id, + bidId: bid.bidId, + sizes: bid.mediaTypes[traff].sizes, + traffic: traff + }; + if (bid.schain) { + placement.schain = bid.schain; + } + placements.push(placement); + } + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + try { + serverResponse = serverResponse.body; + for (let i = 0; i < serverResponse.length; i++) { + let resItem = serverResponse[i]; + + response.push(resItem); + } + } catch (e) { + utils.logMessage(e); + }; + return response; + }, + + getUserSyncs: (syncOptions) => { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: 'https://console.brightmountainmedia.com:4444/cookieSync' + }]; + } + }, + +}; + +registerBidder(spec); diff --git a/modules/brightMountainMediaBidAdapter.md b/modules/brightMountainMediaBidAdapter.md new file mode 100644 index 00000000000..a9900b5264d --- /dev/null +++ b/modules/brightMountainMediaBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: Bright Mountain Media Bidder Adapter +Module Type: Bidder Adapter +Maintainer: dev@brightmountainmedia.com +``` + +# Description + +Connects to Bright Mountain Media exchange for bids. + +Bright Mountain Media bid adapter currently supports Banner. + +# Test Parameters +``` + var adUnits = [ + code: 'placementid_0', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'brightmountainmedia', + params: { + placement_id: '5f21784949be82079d08c', + traffic: 'banner' + } + } + ] + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js new file mode 100644 index 00000000000..3b7e46e55a7 --- /dev/null +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -0,0 +1,139 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/brightMountainMediaBidAdapter.js'; + +describe('brightMountainMediaBidAdapter_spec', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'brightmountainmedia', + bidderRequestId: '145e1d6a7837c9', + params: { + placement_id: '123qwerty' + }, + placementCode: 'placementid_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + }; + let bidderRequest = { + bidderCode: 'brightmountainmedia', + auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', + bidderRequestId: 'ffffffffffffff', + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000, + uspConsent: '1YN-', + refererInfo: { + referer: 'http://www.example.com', + reachedTop: true, + }, + bids: [bid] + } + + describe('isBidRequestValid', function () { + it('Should return true when when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when required params are not passed', function () { + bid.params = {} + 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://console.brightmountainmedia.com/hb/bid'); + }); + + 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'); + 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'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); + expect(placement.placementId).to.be.a('string'); + expect(placement.bidId).to.be.a('string'); + expect(placement.traffic).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + } + }); + 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 () { + let resObject = { + body: [{ + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

Hello ad

', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + }] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('getUserSyncs', function () { + let syncoptionsIframe = { + 'iframeEnabled': 'true' + } + it('should return iframe sync option', function () { + expect(spec.getUserSyncs(syncoptionsIframe)).to.be.an('array').with.lengthOf(1); + expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.exist; + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.exist; + expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe') + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('https://console.brightmountainmedia.com:4444/cookieSync') + }); + }); +}); From 38e078ef99804b2320b60c868a2375470e917185 Mon Sep 17 00:00:00 2001 From: liranbaruch Date: Tue, 18 Aug 2020 12:38:17 +0300 Subject: [PATCH 0092/1476] Added IronSource bidder tests and adapter according to specs (#5568) * Added IronSource bidder tests and adapter according to specs * Update tests according to request --- modules/ironsourceBidAdapter.js | 234 ++++++++++++ modules/ironsourceBidAdapter.md | 49 +++ .../spec/modules/ironsourceBidAdapter_spec.js | 339 ++++++++++++++++++ 3 files changed, 622 insertions(+) create mode 100644 modules/ironsourceBidAdapter.js create mode 100644 modules/ironsourceBidAdapter.md create mode 100644 test/spec/modules/ironsourceBidAdapter_spec.js diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js new file mode 100644 index 00000000000..795302762cd --- /dev/null +++ b/modules/ironsourceBidAdapter.js @@ -0,0 +1,234 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import {VIDEO} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; + +const SUPPORTED_AD_TYPES = [VIDEO]; +const BIDDER_CODE = 'ironSource'; +const BIDDER_VERSION = '4.0.0'; +const TTL = 360; +const SELLER_ENDPOINT = 'https://hb.yellowblue.io/hb'; +const SUPPORTED_SYNC_METHODS = { + IFRAME: 'iframe', + PIXEL: 'pixel' +} + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: SUPPORTED_AD_TYPES, + isBidRequestValid: function(bidRequest) { + return !!(bidRequest.params.isOrg); + }, + buildRequests: function (bidRequests, bidderRequest) { + if (bidRequests.length === 0) { + return []; + } + + const requests = []; + + bidRequests.forEach(bid => { + requests.push(buildVideoRequest(bid, bidderRequest)); + }); + + return requests; + }, + interpretResponse: function({body}) { + const bidResponses = []; + + const bidResponse = { + requestId: body.requestId, + cpm: body.cpm, + width: body.width, + height: body.height, + creativeId: body.requestId, + currency: body.currency, + netRevenue: body.netRevenue, + ttl: TTL, + vastXml: body.vastXml, + mediaType: VIDEO + }; + + bidResponses.push(bidResponse); + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + for (const response of serverResponses) { + if (syncOptions.iframeEnabled && response.body.userSyncURL) { + syncs.push({ + type: 'iframe', + url: response.body.userSyncURL + }); + } + if (syncOptions.pixelEnabled && utils.isArray(response.body.userSyncPixels)) { + const pixels = response.body.userSyncPixels.map(pixel => { + return { + type: 'image', + url: pixel + } + }) + syncs.push(...pixels) + } + } + return syncs; + } +}; + +registerBidder(spec); + +/** + * Build the video request + * @param bid {bid} + * @param bidderRequest {bidderRequest} + * @returns {Object} + */ +function buildVideoRequest(bid, bidderRequest) { + const sellerParams = generateParameters(bid, bidderRequest); + return { + method: 'GET', + url: SELLER_ENDPOINT, + data: sellerParams + }; +} + +/** + * Get the the ad size from the bid + * @param bid {bid} + * @returns {Array} + */ +function getSizes(bid) { + if (utils.deepAccess(bid, 'mediaTypes.video.sizes')) { + return bid.mediaTypes.video.sizes[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { + return bid.sizes[0]; + } + return []; +} + +/** + * Get schain string value + * @param schainObject {Object} + * @returns {string} + */ +function getSupplyChain(schainObject) { + if (utils.isEmpty(schainObject)) { + return ''; + } + let scStr = `${schainObject.ver},${schainObject.complete}`; + schainObject.nodes.forEach((node) => { + scStr += '!'; + scStr += `${getEncodedValIfNotEmpty(node.asi)},`; + scStr += `${getEncodedValIfNotEmpty(node.sid)},`; + scStr += `${getEncodedValIfNotEmpty(node.hp)},`; + scStr += `${getEncodedValIfNotEmpty(node.rid)},`; + scStr += `${getEncodedValIfNotEmpty(node.name)},`; + scStr += `${getEncodedValIfNotEmpty(node.domain)}`; + }); + return scStr; +} + +/** + * Get encoded node value + * @param val {string} + * @returns {string} + */ +function getEncodedValIfNotEmpty(val) { + return !utils.isEmpty(val) ? encodeURIComponent(val) : ''; +} + +/** + * Get preferred user-sync method based on publisher configuration + * @param bidderCode {string} + * @returns {string} + */ +function getAllowedSyncMethod(filterSettings, bidderCode) { + const iframeConfigsToCheck = ['all', 'iframe']; + const pixelConfigToCheck = 'image'; + if (filterSettings && iframeConfigsToCheck.some(config => isSyncMethodAllowed(filterSettings[config], bidderCode))) { + return SUPPORTED_SYNC_METHODS.IFRAME; + } + if (!filterSettings || !filterSettings[pixelConfigToCheck] || isSyncMethodAllowed(filterSettings[pixelConfigToCheck], bidderCode)) { + return SUPPORTED_SYNC_METHODS.PIXEL; + } +} + +/** + * Check if sync rule is supported + * @param syncRule {Object} + * @param bidderCode {string} + * @returns {boolean} + */ +function isSyncMethodAllowed(syncRule, bidderCode) { + if (!syncRule) { + return false; + } + const isInclude = syncRule.filter === 'include'; + const bidders = utils.isArray(syncRule.bidders) ? syncRule.bidders : [bidderCode]; + return isInclude && utils.contains(bidders, bidderCode); +} + +/** + * Generate query parameters for the request + * @param bid {bid} + * @param bidderRequest {bidderRequest} + * @returns {Object} + */ +function generateParameters(bid, bidderRequest) { + const timeout = config.getConfig('bidderTimeout'); + const { syncEnabled, filterSettings } = config.getConfig('userSync'); + const [ width, height ] = getSizes(bid); + const { params } = bid; + const { bidderCode } = bidderRequest; + const domain = window.location.hostname; + + const requestParams = { + auction_start: utils.timestamp(), + ad_unit_code: utils.getBidIdParameter('adUnitCode', bid), + tmax: timeout, + width: width, + height: height, + publisher_id: params.isOrg, + floor_price: params.floorPrice, + ua: navigator.userAgent, + bid_id: utils.getBidIdParameter('bidId', bid), + bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), + transaction_id: utils.getBidIdParameter('transactionId', bid), + session_id: utils.getBidIdParameter('auctionId', bid), + publisher_name: domain, + site_domain: domain, + bidder_version: BIDDER_VERSION + }; + + if (syncEnabled) { + const allowedSyncMethod = getAllowedSyncMethod(filterSettings, bidderCode); + if (allowedSyncMethod) { + requestParams.cs_method = allowedSyncMethod; + } + } + + if (bidderRequest.uspConsent) { + requestParams.us_privacy = bidderRequest.uspConsent; + } + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + requestParams.gdpr = bidderRequest.gdprConsent.gdprApplies; + requestParams.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + + if (params.ifa) { + requestParams.ifa = params.ifa; + } + + if (bid.schain) { + requestParams.schain = getSupplyChain(bid.schain); + } + + if (bidderRequest && bidderRequest.refererInfo) { + requestParams.referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + requestParams.page_url = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); + } + + return requestParams; +} diff --git a/modules/ironsourceBidAdapter.md b/modules/ironsourceBidAdapter.md new file mode 100644 index 00000000000..378a344b672 --- /dev/null +++ b/modules/ironsourceBidAdapter.md @@ -0,0 +1,49 @@ +#Overview + +Module Name: IronSource Bidder Adapter + +Module Type: Bidder Adapter + +Maintainer: prebid-digital-brands@ironsrc.com + + +# Description + +Module that connects to IronSource's demand sources. + +The IronSource adapter requires setup and approval from the IronSource. Please reach out to prebid-digital-brands@ironsrc.com to create an IronSource account. + +The adapter supports Video(instream). For the integration, IronSource returns content as vastXML and requires the publisher to define the cache url in config passed to Prebid for it to be valid in the auction. + +# Bid Parameters +## Video + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `isOrg` | required | String | IronSource publisher Id provided by your IronSource representative | "56f91cd4d3e3660002000033" +| `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 +| `ifa` | optional | String | The ID for advertisers (also referred to as "IDFA") | "XXX-XXX" + +# Test Parameters +```javascript +var adUnits = [ + { + code: 'dfp-video-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' + } + }, + bids: [{ + bidder: 'ironSource', + params: { + isOrg: '56f91cd4d3e3660002000033', // Required + floorPrice: 2.00, // Optional + ifa: 'XXX-XXX', // Optional + } + }] + } + ]; +``` diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js new file mode 100644 index 00000000000..0ddc12f235c --- /dev/null +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -0,0 +1,339 @@ +import { expect } from 'chai'; +import { spec } from 'modules/ironsourceBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; +import { VIDEO } from '../../../src/mediaTypes.js'; + +const ENDPOINT = 'https://hb.yellowblue.io/hb'; +const TTL = 360; + +describe('ironsourceAdapter', 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 () { + const bid = { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [['640', '480']], + 'params': { + 'isOrg': 'jdye8weeyirk00000001' + } + }; + + it('should return true when required params are passed', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not found', function () { + const newBid = Object.assign({}, bid); + delete newBid.params; + newBid.params = { + 'isOrg': null + }; + expect(spec.isBidRequestValid(newBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [[640, 480]], + 'params': { + 'isOrg': 'jdye8weeyirk00000001' + }, + 'bidId': '299ffc8cca0b87', + 'bidderRequestId': '1144f487e563f9', + 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', + } + ]; + + const bidderRequest = { + bidderCode: 'ironSource', + } + + it('sends bid request to ENDPOINT via GET', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET'); + } + }); + + it('should send the correct bid Id', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.bid_id).to.equal('299ffc8cca0b87'); + } + }); + + it('should send the correct width and height', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('width', 640); + expect(request.data).to.have.property('height', 480); + } + }); + + it('should respect syncEnabled option', function() { + config.setConfig({ + userSync: { + syncEnabled: false, + filterSettings: { + all: { + bidders: '*', + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('cs_method'); + } + }); + + it('should respect "iframe" filter settings', function () { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + iframe: { + bidders: [spec.code], + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'iframe'); + } + }); + + it('should respect "all" filter settings', function () { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + all: { + bidders: [spec.code], + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'iframe'); + } + }); + + it('should send the pixel user sync param if userSync is enabled and no "iframe" or "all" configs are present', function () { + config.setConfig({ + userSync: { + syncEnabled: true + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'pixel'); + } + }); + + it('should respect total exclusion', function() { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + image: { + bidders: [spec.code], + filter: 'exclude' + }, + iframe: { + bidders: [spec.code], + filter: 'exclude' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('cs_method'); + } + }); + + it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithUSP); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('us_privacy', '1YNN'); + } + }); + + it('should have an empty us_privacy param if usPrivacy is missing in the bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('us_privacy'); + } + }); + + it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('gdpr'); + expect(request.data).to.not.have.property('gdpr_consent'); + } + }); + + it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('gdpr', true); + expect(request.data).to.have.property('gdpr_consent', 'test-consent-string'); + } + }); + + it('should have schain param if it is available in the bidRequest', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + }; + bidRequests[0].schain = schain; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,,,,'); + } + }); + }); + + describe('interpretResponse', function () { + const response = { + cpm: 12.5, + vastXml: '', + width: 640, + height: 480, + requestId: '21e12606d47ba7', + netRevenue: true, + currency: 'USD' + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '21e12606d47ba7', + cpm: 12.5, + width: 640, + height: 480, + creativeId: '21e12606d47ba7', + currency: 'USD', + netRevenue: true, + ttl: TTL, + vastXml: '', + mediaType: VIDEO + } + ]; + const result = spec.interpretResponse({ body: [response] }); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + }) + + describe('getUserSyncs', function() { + const imageSyncResponse = { + body: { + userSyncPixels: [ + 'https://image-sync-url.test/1', + 'https://image-sync-url.test/2', + 'https://image-sync-url.test/3' + ] + } + }; + + const iframeSyncResponse = { + body: { + userSyncURL: 'https://iframe-sync-url.test' + } + }; + + it('should register all img urls from the response', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imageSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://image-sync-url.test/1' + }, + { + type: 'image', + url: 'https://image-sync-url.test/2' + }, + { + type: 'image', + url: 'https://image-sync-url.test/3' + } + ]); + }); + + it('should register the iframe url from the response', function() { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [iframeSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://iframe-sync-url.test' + } + ]); + }); + + it('should register both image and iframe urls from the responses', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [iframeSyncResponse, imageSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://iframe-sync-url.test' + }, + { + type: 'image', + url: 'https://image-sync-url.test/1' + }, + { + type: 'image', + url: 'https://image-sync-url.test/2' + }, + { + type: 'image', + url: 'https://image-sync-url.test/3' + } + ]); + }); + + it('should handle an empty response', function() { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('should handle when user syncs are disabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imageSyncResponse]); + expect(syncs).to.deep.equal([]); + }); + }) +}); From 0e182b79a174453ddc19cfb185bba58ff580065a Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 18 Aug 2020 10:57:28 -0500 Subject: [PATCH 0093/1476] add provider when noData (#5621) --- modules/rubiconAnalyticsAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 6defd7b45ae..0bee4d926fc 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -198,7 +198,8 @@ function sendMessage(auctionId, bidWonId) { if (auctionCache.floorData.location === 'noData') { auction.floors = utils.pick(auctionCache.floorData, [ 'location', - 'fetchStatus' + 'fetchStatus', + 'floorProvider as provider' ]); } else { auction.floors = utils.pick(auctionCache.floorData, [ From 6c61de8e501cf6c107ab99eb373fea2d46c463f1 Mon Sep 17 00:00:00 2001 From: mefjush Date: Tue, 18 Aug 2020 20:07:31 +0200 Subject: [PATCH 0094/1476] Configure Adhese gvlid, filter out empty params to reduce the Adhese request size (#5602) --- modules/adheseBidAdapter.js | 17 +++++++++++------ test/spec/modules/adheseBidAdapter_spec.js | 8 ++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index cd5a44f34d8..606e56c13d5 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -4,10 +4,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'adhese'; +const GVLID = 553; const USER_SYNC_BASE_URL = 'https://user-sync.adhese.com/iframe/user_sync.html'; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { @@ -112,12 +114,15 @@ function mergeTargets(targets, target) { if (target) { Object.keys(target).forEach(function (key) { const val = target[key]; - const values = Array.isArray(val) ? val : [val]; - if (targets[key]) { - const distinctValues = values.filter(v => targets[key].indexOf(v) < 0); - targets[key].push.apply(targets[key], distinctValues); - } else { - targets[key] = values; + const dirtyValues = Array.isArray(val) ? val : [val]; + const values = dirtyValues.filter(v => v === 0 || v); + if (values.length > 0) { + if (targets[key]) { + const distinctValues = values.filter(v => targets[key].indexOf(v) < 0); + targets[key].push.apply(targets[key], distinctValues); + } else { + targets[key] = values; + } } }); } diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 0743ddd211d..aa4872641b4 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -95,6 +95,14 @@ describe('AdheseAdapter', function () { expect(JSON.parse(req.data).parameters).to.deep.include({ 'ci': [ 'london', 'gent' ] }); }); + it('should filter out empty params', function () { + let req = spec.buildRequests([ bidWithParams({ 'aa': [], 'bb': null, 'cc': '', 'dd': [ '', '' ], 'ee': [ 0, 1, null ], 'ff': 0, 'gg': [ 'x', 'y', '' ] }) ], bidderRequest); + + let params = JSON.parse(req.data).parameters; + expect(params).to.not.have.any.keys('aa', 'bb', 'cc', 'dd'); + expect(params).to.deep.include({ 'ee': [ 0, 1 ], 'ff': [ 0 ], 'gg': [ 'x', 'y' ] }); + }); + it('should include gdpr consent param', function () { let req = spec.buildRequests([ minimalBid() ], bidderRequest); From 28a4c345306824cbad81f8de70551603fc40ae24 Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Tue, 18 Aug 2020 12:08:26 -0700 Subject: [PATCH 0095/1476] Update rubicon adapter for liveramp userid requirements change (#5620) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * update liveramp userid support Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/rubiconBidAdapter.js | 4 ++-- test/spec/modules/rubiconBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 0c563987331..47ca694dc43 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -289,7 +289,7 @@ export const spec = { // support identityLink (aka LiveRamp) if (bidRequest.userId.idl_env) { data.user.ext.eids.push({ - source: 'liveramp.com', + source: 'liveramp_idl', uids: [{ id: bidRequest.userId.idl_env }] @@ -554,7 +554,7 @@ export const spec = { // support identityLink (aka LiveRamp) if (bidRequest.userId.idl_env) { - data['tpid_liveramp.com'] = bidRequest.userId.idl_env; + data['x_liverampidl'] = bidRequest.userId.idl_env; } } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c21a9b879f1..808d4840418 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1343,7 +1343,7 @@ describe('the rubicon adapter', function () { }); describe('LiveRamp support', function () { - it('should send tpid_liveramp.com when userId defines idl_env', function () { + it('should send x_liverampidl when userId defines idl_env', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { idl_env: '1111-2222-3333-4444' @@ -1351,7 +1351,7 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); - expect(data['tpid_liveramp.com']).to.equal('1111-2222-3333-4444'); + expect(data['x_liverampidl']).to.equal('1111-2222-3333-4444'); }); }); }) @@ -1546,7 +1546,7 @@ describe('the rubicon adapter', function () { expect(post.user.ext.tpid.source).to.equal('liveintent.com'); expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); // LiveRamp should exist - expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); + expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); expect(post.rp).that.is.an('object'); expect(post.rp.target).that.is.an('object'); From 3b5a747bdb50fb120405258ca3abc5c41c3b7b71 Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Tue, 18 Aug 2020 23:17:40 +0300 Subject: [PATCH 0096/1476] Add intentIq (#5624) --- modules/.submodules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/.submodules.json b/modules/.submodules.json index 58ab8798fa5..dd40557c35b 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -10,7 +10,8 @@ "criteoIdSystem", "netIdSystem", "identityLinkIdSystem", - "sharedIdSystem" + "sharedIdSystem", + "intentIqIdSystem" ], "adpod": [ "freeWheelAdserverVideo", From fb72d518e3cd68d5dedffd46416db6a0cdfc0ef5 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 18 Aug 2020 17:21:44 -0400 Subject: [PATCH 0097/1476] Adds advertiserDomains to kargo adapter (#5625) * Update kargoBidAdapter.js * Update kargoBidAdapter_spec.js --- modules/kargoBidAdapter.js | 3 ++- test/spec/modules/kargoBidAdapter_spec.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 31c35f4afe3..03767efc135 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -65,7 +65,8 @@ export const spec = { let meta; if (adUnit.metadata && adUnit.metadata.landingPageDomain) { meta = { - clickUrl: adUnit.metadata.landingPageDomain + clickUrl: adUnit.metadata.landingPageDomain, + advertiserDomains: [adUnit.metadata.landingPageDomain] }; } bidResponses.push({ diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index be73e140839..9dbcca8e331 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -505,7 +505,8 @@ describe('kargo adapter tests', function () { netRevenue: true, currency: 'USD', meta: { - clickUrl: 'https://foobar.com' + clickUrl: 'https://foobar.com', + advertiserDomains: ['https://foobar.com'] } }, { requestId: '3', From c0850e91eccff86a31142d3444099690aefb3e45 Mon Sep 17 00:00:00 2001 From: Itay Nave <38345760+itaynave@users.noreply.github.com> Date: Wed, 19 Aug 2020 00:23:11 +0300 Subject: [PATCH 0098/1476] Adding alias (#5622) * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Fix Consent parameters * Update aniviewBidAdapter.js V3 support * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Update refererInfo * Update aniviewBidAdapter.js Fix tabs and spaces * Update aniviewBidAdapter.js fixes * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Add ccpa support * Update aniviewBidAdapter.js Typo * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js * Fix size and sample Fixed sizes from playerSize Updated md sample * Fix tabs * Fix sizes * Recheck * Add tgt parameter * Update sample * Add support for cookie sync + tests * Add support for cookie sync + tests * Add support for cookie sync + tests * Support aliases Support aliases * Update Update * Fix lint Fix lint * Update spec Update spec * Aniview Bid Adapter: Added the new alias Co-authored-by: Roman Shevchenko --- modules/aniviewBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index f0fab83aeea..e1f61a30abc 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -252,7 +252,7 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, - aliases: ['selectmediavideo'], + aliases: ['avantisvideo', 'selectmediavideo'], supportedMediaTypes: [VIDEO], isBidRequestValid, buildRequests, From b01fe091418727ed24d8011794c5a94e011873dd Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Wed, 19 Aug 2020 00:28:23 +0300 Subject: [PATCH 0099/1476] oneVideo Adapter: User sync pixel updates (VIDEOPUB-17981) (#5583) * updated DBM sync pixel to ups/57304 + removed yahoo sync pixel * updated unit tests * updated GDPR tests * Update package-lock.json * Update package-lock.json * update local branch to origin * added sync pixel unit tests * Update package-lock.json --- modules/oneVideoBidAdapter.js | 13 ++++--------- test/spec/modules/oneVideoBidAdapter_spec.js | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 26b0a7dccc2..c5bc054ac04 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,12 +4,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.0.3', + VERSION: '3.0.4', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', - SYNC_ENDPOINT1: 'https://cm.g.doubleclick.net/pixel?google_nid=adaptv_dbm&google_cm&google_sc', - SYNC_ENDPOINT2: 'https://pr-bh.ybp.yahoo.com/sync/adaptv_ortb/{combo_uid}', - SYNC_ENDPOINT3: 'https://match.adsrvr.org/track/cmf/generic?ttd_pid=adaptv&ttd_tpi=1', + SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', + SYNC_ENDPOINT2: 'https://match.adsrvr.org/track/cmf/generic?ttd_pid=adaptv&ttd_tpi=1', supportedMediaTypes: ['video', 'banner'], /** * Determines whether or not the given bid request is valid. @@ -136,17 +135,13 @@ export const spec = { type: 'image', url: spec.SYNC_ENDPOINT1 }, - { - type: 'image', - url: spec.SYNC_ENDPOINT2 - }, { type: 'image', url: `https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0` + encodeURI(`&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}`) }, { type: 'image', - url: spec.SYNC_ENDPOINT3 + url: spec.SYNC_ENDPOINT2 }]; } } diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index a91e9ac27e8..ae29bcd48ec 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -217,7 +217,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.0.3'; + const VERSION = '3.0.4'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -593,14 +593,25 @@ describe('OneVideoBidAdapter', function () { it('should get correct user sync when iframeEnabled', function () { let pixel = spec.getUserSyncs({pixelEnabled: true}, {}, {gdprApplies: true, consentString: GDPR_CONSENT_STRING}) - expect(pixel[2].type).to.equal('image'); - expect(pixel[2].url).to.equal('https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=1&gdpr_consent=' + GDPR_CONSENT_STRING + '&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0&gdpr=1&gdpr_consent=' + encodeURI(GDPR_CONSENT_STRING)); + expect(pixel[1].type).to.equal('image'); + expect(pixel[1].url).to.equal('https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=1&gdpr_consent=' + GDPR_CONSENT_STRING + '&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0&gdpr=1&gdpr_consent=' + encodeURI(GDPR_CONSENT_STRING)); }); it('should default to gdprApplies=0 when consentData is undefined', function () { let pixel = spec.getUserSyncs({pixelEnabled: true}, {}, undefined); - expect(pixel[2].url).to.equal('https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=0&gdpr_consent=&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0&gdpr=0&gdpr_consent='); + expect(pixel[1].url).to.equal('https://sync-tm.everesttech.net/upi/pid/m7y5t93k?gdpr=0&gdpr_consent=&redir=https%3A%2F%2Fpixel.advertising.com%2Fups%2F55986%2Fsync%3Fuid%3D%24%7BUSER_ID%7D%26_origin%3D0&gdpr=0&gdpr_consent='); }); }); + + describe('verify sync pixels', function () { + let pixel = spec.getUserSyncs({pixelEnabled: true}, {}, undefined); + it('should be UPS sync pixel for DBM', function () { + expect(pixel[0].url).to.equal('https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true') + }); + + it('should be TTD sync pixel', function () { + expect(pixel[2].url).to.equal('https://match.adsrvr.org/track/cmf/generic?ttd_pid=adaptv&ttd_tpi=1') + }); + }) }); }); From e1100af152b35946c6d2e1dcd0efb7aa0da30491 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 19 Aug 2020 10:44:43 -0700 Subject: [PATCH 0100/1476] surround getFloor in try catch (#5628) --- modules/rubiconBidAdapter.js | 30 ++++++++++++++------- test/spec/modules/rubiconBidAdapter_spec.js | 29 +++++++++++++++++++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 47ca694dc43..5d4c8d8f78c 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -203,11 +203,16 @@ export const spec = { let bidFloor; if (typeof bidRequest.getFloor === 'function' && !config.getConfig('rubicon.disableFloors')) { - let floorInfo = bidRequest.getFloor({ - currency: 'USD', - mediaType: 'video', - size: parseSizes(bidRequest, 'video') - }); + let floorInfo; + try { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'video', + size: parseSizes(bidRequest, 'video') + }); + } catch (e) { + utils.logError('Rubicon: getFloor threw an error: ', e); + } bidFloor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined; } else { bidFloor = parseFloat(utils.deepAccess(bidRequest, 'params.floor')); @@ -527,11 +532,16 @@ export const spec = { // If floors module is enabled and we get USD floor back, send it in rp_hard_floor else undfined if (typeof bidRequest.getFloor === 'function' && !config.getConfig('rubicon.disableFloors')) { - let floorInfo = bidRequest.getFloor({ - currency: 'USD', - mediaType: 'banner', - size: '*' - }); + let floorInfo; + try { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'video', + size: parseSizes(bidRequest, 'video') + }); + } catch (e) { + utils.logError('Rubicon: getFloor threw an error: ', e); + } data['rp_hard_floor'] = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined; } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 808d4840418..cddf190fda4 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -13,7 +13,8 @@ describe('the rubicon adapter', function () { let sandbox, bidderRequest, sizeMap, - getFloorResponse; + getFloorResponse, + logErrorSpy; /** * @typedef {Object} sizeMapConverted @@ -272,6 +273,7 @@ describe('the rubicon adapter', function () { beforeEach(function () { sandbox = sinon.sandbox.create(); + logErrorSpy = sinon.spy(utils, 'logError'); getFloorResponse = {}; bidderRequest = { bidderCode: 'rubicon', @@ -343,6 +345,7 @@ describe('the rubicon adapter', function () { afterEach(function () { sandbox.restore(); + utils.logError.restore(); }); describe('MAS mapping / ordering', function () { @@ -1597,6 +1600,30 @@ describe('the rubicon adapter', function () { [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].bidfloor).to.equal(1.23); }); + + it('should continue with auction and log error if getFloor throws one', function () { + createVideoBidderRequest(); + // default getFloor response is empty object so should not break and not send hard_floor + bidderRequest.bids[0].getFloor = () => { + throw new Error('An exception!'); + }; + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + + // log error called + expect(logErrorSpy.calledOnce).to.equal(true); + + // should have an imp + expect(request.data.imp).to.exist.and.to.be.a('array'); + expect(request.data.imp).to.have.lengthOf(1); + + // should be NO bidFloor + expect(request.data.imp[0].bidfloor).to.be.undefined; + }); + it('should add alias name to PBS Request', function() { createVideoBidderRequest(); From 23af840a1045b5e97e947378fc6b3f2deeb8afa5 Mon Sep 17 00:00:00 2001 From: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Date: Wed, 19 Aug 2020 22:20:09 +0300 Subject: [PATCH 0101/1476] Extending the Real Time Data Module (#5519) * 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 * RTD module extend #4610 * add hook for submodule init variables naming --- modules/browsiRtdProvider.js | 32 +- modules/rtdModule/index.js | 214 +++++++--- src/targeting.js | 5 +- test/spec/modules/realTimeModule_spec.js | 484 +++++++++++------------ 4 files changed, 425 insertions(+), 310 deletions(-) diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 9317786fb8d..3aff3c6aac6 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -37,6 +37,8 @@ let _moduleParams = {}; let _data = null; /** @type {null | function} */ let _dataReadyCallback = null; +/** @type {string} */ +const DEF_KEYNAME = 'browsiViewability'; /** * add browsi script to page @@ -117,16 +119,14 @@ function waitForData(callback) { function sendDataToModule(adUnits, onDone) { try { waitForData(_predictionsData => { - const _predictions = _predictionsData.p; - if (!_predictions || !Object.keys(_predictions).length) { - return onDone({}); - } + const _predictions = _predictionsData.p || {}; let dataToReturn = adUnits.reduce((rp, cau) => { const adUnitCode = cau && cau.code; if (!adUnitCode) { return rp } const adSlot = getSlotByCode(adUnitCode); const identifier = adSlot ? getMacroId(_predictionsData.pmd, adSlot) : adUnitCode; const predictionData = _predictions[identifier]; + rp[adUnitCode] = getKVObject(-1, _predictionsData.kn); if (!predictionData) { return rp } if (predictionData.p) { @@ -160,7 +160,7 @@ function getAllSlots() { function getKVObject(p, keyName) { const prValue = p < 0 ? 'NA' : (Math.floor(p * 10) / 10).toFixed(2); let prObject = {}; - prObject[((_moduleParams['keyName'] || keyName).toString())] = prValue.toString(); + prObject[((_moduleParams['keyName'] || keyName || DEF_KEYNAME).toString())] = prValue.toString(); return prObject; } /** @@ -231,7 +231,7 @@ function evaluate(macro, divId, adUnit, replacer) { * @param {string} url server url with query params */ function getPredictionsFromServer(url) { - let ajax = ajaxBuilder(_moduleParams.auctionDelay || _moduleParams.timeout || DEF_TIMEOUT); + let ajax = ajaxBuilder(_moduleParams.auctionDelay || _moduleParams.timeout); ajax(url, { @@ -286,21 +286,26 @@ export const browsiSubmodule = { * @param {adUnit[]} adUnits * @param {function} onDone */ - getData: sendDataToModule + getData: sendDataToModule, + init: init }; -export function init(config) { +function init(config, gdpr, usp) { + return true; +} + +export function beforeInit(config) { const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { try { _moduleParams = realTimeData.dataProviders && realTimeData.dataProviders.filter( pr => pr.name && pr.name.toLowerCase() === 'browsi')[0].params; + confListener(); _moduleParams.auctionDelay = realTimeData.auctionDelay; - _moduleParams.timeout = realTimeData.timeout; + _moduleParams.timeout = realTimeData.timeout || DEF_TIMEOUT; } catch (e) { _moduleParams = {}; } if (_moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) { - confListener(); collectData(); } else { utils.logError('missing params for Browsi provider'); @@ -308,5 +313,8 @@ export function init(config) { }); } -submodule('realTimeData', browsiSubmodule); -init(config); +function registerSubModule() { + submodule('realTimeData', browsiSubmodule); +} +registerSubModule(); +beforeInit(config); diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 3aa7753d204..9acd484cec8 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -23,14 +23,56 @@ */ /** - * @interface ModuleConfig + * @property + * @summary used to link submodule with config + * @name RtdSubmodule#config + * @type {Object} */ /** - * @property - * @summary sub module name - * @name ModuleConfig#name - * @type {string} + * @function + * @summary init sub module + * @name RtdSubmodule#init + * @param {Object} config + * @param {Object} gdpr settings + * @param {Object} usp settings + * @return {boolean} false to remove sub module + */ + +/** + * @function? + * @summary on auction init event + * @name RtdSubmodule#auctionInit + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on auction end event + * @name RtdSubmodule#auctionEnd + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on bid request event + * @name RtdSubmodule#updateBidRequest + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on bid response event + * @name RtdSubmodule#updateBidResponse + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @interface ModuleConfig */ /** @@ -40,18 +82,43 @@ * @type {number} */ +/** + * @property + * @summary timeout (if no auction dealy) + * @name ModuleConfig#timeout + * @type {number} + */ + +/** + * @property + * @summary list of sub modules + * @name ModuleConfig#dataProviders + * @type {SubmoduleConfig[]} + */ + +/** + * @interface SubModuleConfig + */ + /** * @property * @summary params for provide (sub module) - * @name ModuleConfig#params + * @name SubModuleConfig#params * @type {Object} */ /** * @property - * @summary timeout (if no auction dealy) - * @name ModuleConfig#timeout - * @type {number} + * @summary name + * @name ModuleConfig#name + * @type {string} + */ + +/** + * @property + * @summary delay auction for this sub module + * @name ModuleConfig#waitForIt + * @type {boolean} */ import {getGlobal} from '../../src/prebidGlobal.js'; @@ -59,15 +126,21 @@ import {config} from '../../src/config.js'; import {targeting} from '../../src/targeting.js'; import {getHook, module} from '../../src/hook.js'; import * as utils from '../../src/utils.js'; +import events from '../../src/events.js'; +import CONSTANTS from '../../src/constants.json'; +import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; +import find from 'core-js-pure/features/array/find.js'; /** @type {string} */ const MODULE_NAME = 'realTimeData'; /** @type {number} */ const DEF_TIMEOUT = 1000; /** @type {RtdSubmodule[]} */ -let subModules = []; +export let subModules = []; /** @type {ModuleConfig} */ let _moduleConfig; +/** @type {SubmoduleConfig[]} */ +let _dataProviders = []; /** * enable submodule in User ID @@ -85,6 +158,9 @@ export function init(config) { } confListener(); // unsubscribe config listener _moduleConfig = realTimeData; + _dataProviders = realTimeData.dataProviders; + getHook('makeBidRequests').before(initSubModules); + setEventsListeners(); if (typeof (_moduleConfig.auctionDelay) === 'undefined') { _moduleConfig.auctionDelay = 0; } @@ -97,61 +173,82 @@ export function init(config) { }); } +/** + * call each sub module init function by config order + * if no init function / init return failure / module not configured - remove it from submodules list + */ +export function initSubModules(next, adUnits, auctionStart, auctionId, cbTimeout, labels) { + let subModulesByOrder = []; + _dataProviders.forEach(provider => { + const sm = find(subModules, s => s.name === provider.name); + const initResponse = sm && sm.init && sm.init(provider, gdprDataHandler.getConsentData(), uspDataHandler.getConsentData()); + if (initResponse) { + subModulesByOrder.push(Object.assign(sm, {config: provider})); + } + }); + subModules = subModulesByOrder; + next(adUnits, auctionStart, auctionId, cbTimeout, labels) +} + +/** + * call each sub module event function by config order + */ +function setEventsListeners() { + events.on(CONSTANTS.EVENTS.AUCTION_INIT, (args) => { + subModules.forEach(sm => { sm.auctionInit && sm.auctionInit(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.AUCTION_END, (args) => { + subModules.forEach(sm => { sm.auctionEnd && sm.auctionEnd(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, (args) => { + subModules.forEach(sm => { sm.updateBidRequest && sm.updateBidRequest(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.BID_RESPONSE, (args) => { + subModules.forEach(sm => { sm.updateBidResponse && sm.updateBidResponse(args, sm.config) }) + }); +} + /** * get data from sub module * @param {AdUnit[]} adUnits received from auction * @param {function} callback callback function on data received */ -function getProviderData(adUnits, callback) { - const callbackExpected = subModules.length; - let dataReceived = []; - let processDone = false; - const dataWaitTimeout = setTimeout(() => { - processDone = true; - callback(dataReceived); - }, _moduleConfig.auctionDelay || _moduleConfig.timeout || DEF_TIMEOUT); +export function getProviderData(adUnits, callback) { + /** + * invoke callback if one of the conditions met: + * timeout reached + * all submodules answered + * all sub modules configured "waitForIt:true" answered (as long as there is at least one configured) + */ + const waitForSubModulesLength = subModules.filter(sm => sm.config && sm.config.waitForIt).length; + let callbacksExpected = waitForSubModulesLength || subModules.length; + const shouldWaitForAllSubModules = waitForSubModulesLength === 0; + let dataReceived = {}; + let processDone = false; + const dataWaitTimeout = setTimeout(done, _moduleConfig.auctionDelay || _moduleConfig.timeout || DEF_TIMEOUT); subModules.forEach(sm => { - sm.getData(adUnits, onDataReceived); + sm.getData(adUnits, onDataReceived.bind(sm)); }); function onDataReceived(data) { if (processDone) { return } - dataReceived.push(data); - if (dataReceived.length === callbackExpected) { - processDone = true; + dataReceived[this.name] = data; + if (shouldWaitForAllSubModules || (this.config && this.config.waitForIt)) { + callbacksExpected-- + } + if (callbacksExpected <= 0) { clearTimeout(dataWaitTimeout); - callback(dataReceived); + done(); } } -} -/** - * delete invalid data received from provider - * this is to ensure working flow for GPT - * @param {Object} data received from provider - * @return {Object} valid data for GPT targeting - */ -export function validateProviderDataForGPT(data) { - // data must be an object, contains object with string as value - if (typeof data !== 'object') { - return {}; - } - for (let key in data) { - if (data.hasOwnProperty(key)) { - for (let innerKey in data[key]) { - if (data[key].hasOwnProperty(innerKey)) { - if (typeof data[key][innerKey] !== 'string') { - utils.logWarn(`removing ${key}: {${innerKey}:${data[key][innerKey]} } from GPT targeting because of invalid type (must be string)`); - delete data[key][innerKey]; - } - } - } - } + function done() { + processDone = true; + callback(dataReceived); } - return data; } /** @@ -163,7 +260,7 @@ export function validateProviderDataForGPT(data) { export function setTargetsAfterRequestBids(next, adUnits) { getProviderData(adUnits, (data) => { if (data && Object.keys(data).length) { - const _mergedData = deepMerge(data); + const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); if (Object.keys(_mergedData).length) { setDataForPrimaryAdServer(_mergedData); } @@ -172,6 +269,22 @@ export function setTargetsAfterRequestBids(next, adUnits) { }); } +/** + * return an array providers data in reverse order,so the data merge will be according to correct config order + * @param {Submodule[]} modules + * @param {Object} data - data retrieved from providers + * @return {array} reversed order ready for merge + */ +function setDataOrderByProvider(modules, data) { + let rd = []; + for (let i = modules.length; i--; i > 0) { + if (data[modules[i].name]) { + rd.push(data[modules[i].name]) + } + } + return rd; +} + /** * deep merge array of objects * @param {array} arr - objects array @@ -207,7 +320,7 @@ export function deepMerge(arr) { export function requestBidsHook(fn, reqBidsConfigObj) { getProviderData(reqBidsConfigObj.adUnits || getGlobal().adUnits, (data) => { if (data && Object.keys(data).length) { - const _mergedData = deepMerge(data); + const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); if (Object.keys(_mergedData).length) { setDataForPrimaryAdServer(_mergedData); addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, _mergedData); @@ -222,7 +335,6 @@ export function requestBidsHook(fn, reqBidsConfigObj) { * @param {Object} data - key values to set */ function setDataForPrimaryAdServer(data) { - data = validateProviderDataForGPT(data); if (utils.isGptPubadsDefined()) { targeting.setTargetingForGPT(data, null) } else { @@ -247,5 +359,5 @@ function addIdDataToAdUnitBids(adUnits, data) { }); } -init(config); module('realTimeData', attachRealTimeDataProvider); +init(config); diff --git a/src/targeting.js b/src/targeting.js index 5873eeeb559..1b1e14fd4a6 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -325,7 +325,10 @@ export function newTargeting(auctionManager) { Object.keys(targetingConfig).filter(customSlotMatching ? customSlotMatching(slot) : isAdUnitCodeMatchingSlot(slot)) .forEach(targetId => Object.keys(targetingConfig[targetId]).forEach(key => { - let valueArr = targetingConfig[targetId][key].split(','); + let valueArr = targetingConfig[targetId][key]; + if (typeof valueArr === 'string') { + valueArr = valueArr.split(','); + } valueArr = (valueArr.length > 1) ? [valueArr] : valueArr; valueArr.map((value) => { utils.logMessage(`Attempting to set key value for slot: ${slot.getSlotElementId()} key: ${key} value: ${value}`); diff --git a/test/spec/modules/realTimeModule_spec.js b/test/spec/modules/realTimeModule_spec.js index ca149fe7a44..f47068724d1 100644 --- a/test/spec/modules/realTimeModule_spec.js +++ b/test/spec/modules/realTimeModule_spec.js @@ -1,27 +1,176 @@ -import { - init, - requestBidsHook, - setTargetsAfterRequestBids, - deepMerge, - validateProviderDataForGPT -} from 'modules/rtdModule/index.js'; -import { - init as browsiInit, - addBrowsiTag, - isIdMatchingAdUnit, - setData, - getMacroId -} from 'modules/browsiRtdProvider.js'; -import { - init as audigentInit, - setData as setAudigentData -} from 'modules/audigentRtdProvider.js'; +import * as rtdModule from 'modules/rtdModule/index.js'; import { config } from 'src/config.js'; -import { makeSlot } from '../integration/faker/googletag.js'; - -let expect = require('chai').expect; +import {makeSlot} from '../integration/faker/googletag.js'; +import * as browsiRTD from '../../../modules/browsiRtdProvider.js'; + +const validSM = { + name: 'validSM', + init: () => { return true }, + getData: (adUnits, onDone) => { + setTimeout(() => { + return onDone({'key': 'validSM'}) + }, 500) + } +}; + +const validSMWait = { + name: 'validSMWait', + init: () => { return true }, + getData: (adUnits, onDone) => { + setTimeout(() => { + return onDone({'ad1': {'key': 'validSMWait'}}) + }, 50) + } +}; + +const invalidSM = { + name: 'invalidSM' +}; + +const failureSM = { + name: 'failureSM', + init: () => { return false } +}; + +const nonConfSM = { + name: 'nonConfSM', + init: () => { return true } +}; + +const conf = { + 'realTimeData': { + 'auctionDelay': 250, + dataProviders: [ + { + 'name': 'validSMWait', + 'waitForIt': true, + }, + { + 'name': 'validSM', + 'waitForIt': false, + }, + { + 'name': 'invalidSM' + }, + { + 'name': 'failureSM' + }] + } +}; + +function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: { banner: {}, native: {} }, + sizes: [[300, 200], [300, 600]], + bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] + }; +} describe('Real time module', function () { + after(function () { + config.resetConfig(); + }); + + beforeEach(function () { + config.setConfig(conf); + }); + + it('should use only valid modules', function (done) { + rtdModule.attachRealTimeDataProvider(validSM); + rtdModule.attachRealTimeDataProvider(invalidSM); + rtdModule.attachRealTimeDataProvider(failureSM); + rtdModule.attachRealTimeDataProvider(nonConfSM); + rtdModule.attachRealTimeDataProvider(validSMWait); + rtdModule.initSubModules(afterInitSubModules); + function afterInitSubModules() { + expect(rtdModule.subModules).to.eql([validSMWait, validSM]); + done(); + } + rtdModule.init(config); + }); + + it('should only wait for must have sub modules', function (done) { + rtdModule.getProviderData([], (data) => { + expect(data).to.eql({validSMWait: {'ad1': {'key': 'validSMWait'}}}); + done(); + }) + }); + + it('deep merge object', function () { + const obj1 = { + id1: { + key: 'value', + key2: 'value2' + }, + id2: { + k: 'v' + } + }; + const obj2 = { + id1: { + key3: 'value3' + } + }; + const obj3 = { + id3: { + key: 'value' + } + }; + const expected = { + id1: { + key: 'value', + key2: 'value2', + key3: 'value3' + }, + id2: { + k: 'v' + }, + id3: { + key: 'value' + } + }; + + const merged = rtdModule.deepMerge([obj1, obj2, obj3]); + assert.deepEqual(expected, merged); + }); + + it('check module using bidsBackCallback', function (done) { + // set slot + const slot = makeSlot({ code: '/code1', divId: 'ad1' }); + window.googletag.pubads().setSlots([slot]); + + function afterBidHook() { + expect(slot.getTargeting().length).to.equal(1); + expect(slot.getTargeting()[0].key).to.equal('validSMWait'); + done(); + } + rtdModule.setTargetsAfterRequestBids(afterBidHook, []); + }); + + it('check module using requestBidsHook', function (done) { + // set slot + const slotsB = makeSlot({ code: '/code1', divId: 'ad1' }); + window.googletag.pubads().setSlots([slotsB]); + let adUnits = [getAdUnitMock('ad1')]; + + function afterBidHook(data) { + expect(slotsB.getTargeting().length).to.equal(1); + expect(slotsB.getTargeting()[0].key).to.equal('validSMWait'); + + data.adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid.realTimeData).to.have.property('key'); + expect(bid.realTimeData.key).to.equal('validSMWait'); + }); + }); + done(); + } + rtdModule.requestBidsHook(afterBidHook, { adUnits: adUnits }); + }); +}); + +describe('browsi Real time data sub module', function () { const conf = { 'realTimeData': { 'auctionDelay': 250, @@ -33,250 +182,93 @@ describe('Real time module', function () { 'pubKey': 'testPub', 'keyName': 'bv' } - }, { - 'name': 'audigent' }] } }; - const predictions = { - p: { - 'browsiAd_2': { - 'w': [ - '/57778053/Browsi_Demo_Low', - '/57778053/Browsi_Demo_300x250' - ], - 'p': 0.07 - }, - 'browsiAd_1': { - 'w': [], - 'p': 0.06 - }, - 'browsiAd_3': { - 'w': [], - 'p': 0.53 - }, - 'browsiAd_4': { - 'w': [ - '/57778053/Browsi_Demo' - ], - 'p': 0.85 - } - } - }; - - const audigentSegments = { - audigent_segments: { 'a': 1, 'b': 2 } - } - - function getAdUnitMock(code = 'adUnit-code') { - return { - code, - mediaTypes: { banner: {}, native: {} }, - sizes: [[300, 200], [300, 600]], - bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] - }; - } - - function createSlots() { - const slot1 = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - const slot2 = makeSlot({ code: '/57778053/Browsi', divId: 'browsiAd_1' }); - return [slot1, slot2]; - } - - describe('Real time module with browsi provider', function () { - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - }); + beforeEach(function () { + config.setConfig(conf); + }); - after(function () { - config.resetConfig(); - }); + after(function () { + config.resetConfig(); + }); - it('check module using bidsBackCallback', function () { - let adUnits1 = [getAdUnitMock('browsiAd_1')]; - let targeting = []; - init(config); - browsiInit(config); - config.setConfig(conf); - setData(predictions); - - // set slot - const slots = createSlots(); - window.googletag.pubads().setSlots(slots); - - function afterBidHook() { - slots.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); + it('should init and return true', function () { + browsiRTD.beforeInit(config); + expect(browsiRTD.browsiSubmodule.init()).to.equal(true) + }); - expect(targeting.indexOf('bv')).to.be.greaterThan(-1); - } - setTargetsAfterRequestBids(afterBidHook, adUnits1, true); - }); + it('should create browsi script', function () { + const script = browsiRTD.addBrowsiTag('scriptUrl.com'); + expect(script.getAttribute('data-sitekey')).to.equal('testKey'); + expect(script.getAttribute('data-pubkey')).to.equal('testPub'); + expect(script.async).to.equal(true); + expect(script.prebidData.kn).to.equal(conf.realTimeData.dataProviders[0].params.keyName); + }); - it('check module using requestBidsHook', function () { - let adUnits1 = [getAdUnitMock('browsiAd_1')]; - let targeting = []; - let dataReceived = null; - - // set slot - const slotsB = createSlots(); - window.googletag.pubads().setSlots(slotsB); - - function afterBidHook(data) { - dataReceived = data; - slotsB.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); + it('should match placement with ad unit', function () { + const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - expect(targeting.indexOf('bv')).to.be.greaterThan(-1); - dataReceived.adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.realTimeData).to.have.property('bv'); - }); - }); - } - requestBidsHook(afterBidHook, { adUnits: adUnits1 }); - }); + const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true + const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true + const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false + const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true - it('check object deep merge', function () { - const obj1 = { - id1: { - key: 'value', - key2: 'value2' - }, - id2: { - k: 'v' - } - }; - const obj2 = { - id1: { - key3: 'value3' - } - }; - const obj3 = { - id3: { - key: 'value' - } - }; - const expected = { - id1: { - key: 'value', - key2: 'value2', - key3: 'value3' - }, - id2: { - k: 'v' - }, - id3: { - key: 'value' - } - }; + expect(test1).to.equal(true); + expect(test2).to.equal(true); + expect(test3).to.equal(false); + expect(test4).to.equal(true); + }); - const merged = deepMerge([obj1, obj2, obj3]); - assert.deepEqual(expected, merged); - }); + it('should return correct macro values', function () { + const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - it('check data validation for GPT targeting', function () { - // non strings values should be removed - const obj = { - valid: {'key': 'value'}, - invalid: {'key': ['value']}, - combine: { - 'a': 'value', - 'b': [] - } - }; + slot.setTargeting('test', ['test', 'value']); + // slot getTargeting doesn't act like GPT so we can't expect real value + const macroResult = browsiRTD.getMacroId({p: '/'}, slot); + expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); - const expected = { - valid: {'key': 'value'}, - invalid: {}, - combine: { - 'a': 'value', - } - }; - const validationResult = validateProviderDataForGPT(obj); - assert.deepEqual(expected, validationResult); - }); + const macroResultB = browsiRTD.getMacroId({}, slot); + expect(macroResultB).to.equal('browsiAd_1'); - it('check browsi sub module', function () { - const script = addBrowsiTag('scriptUrl.com'); - expect(script.getAttribute('data-sitekey')).to.equal('testKey'); - expect(script.getAttribute('data-pubkey')).to.equal('testPub'); - expect(script.async).to.equal(true); - - const slots = createSlots(); - const test1 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_300x250']); // true - const test2 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true - const test3 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_Low']); // false - const test4 = isIdMatchingAdUnit(slots[0], []); // true - - expect(test1).to.equal(true); - expect(test2).to.equal(true); - expect(test3).to.equal(false); - expect(test4).to.equal(true); - - // macro results - slots[0].setTargeting('test', ['test', 'value']); - // slot getTargeting doesn't act like GPT so we can't expect real value - const macroResult = getMacroId({p: '/'}, slots[0]); - expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); - - const macroResultB = getMacroId({}, slots[0]); - expect(macroResultB).to.equal('browsiAd_1'); - - const macroResultC = getMacroId({p: '', s: {s: 0, e: 1}}, slots[0]); - expect(macroResultC).to.equal('/'); - }) + const macroResultC = browsiRTD.getMacroId({p: '', s: {s: 0, e: 1}}, slot); + expect(macroResultC).to.equal('/'); }); - describe('Real time module with Audigent provider', function () { - before(function () { - init(config); - audigentInit(config); - config.setConfig(conf); - setAudigentData(audigentSegments); + describe('should return data to RTD module', function () { + it('should return empty if no ad units defined', function (done) { + browsiRTD.setData({}); + browsiRTD.browsiSubmodule.getData([], onDone); + function onDone(data) { + expect(data).to.eql({}); + done(); + } }); - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - config.resetConfig(); + it('should return NA if no prediction for ad unit', function (done) { + const adUnits = [getAdUnitMock('adMock')]; + browsiRTD.setData({}); + browsiRTD.browsiSubmodule.getData(adUnits, onDone); + function onDone(data) { + expect(data).to.eql({adMock: {bv: 'NA'}}); + done(); + } }); - it('check module using requestBidsHook', function () { - let adUnits1 = [getAdUnitMock('audigentAd_1')]; - let targeting = []; - let dataReceived = null; - - // set slot - const slotsB = createSlots(); - window.googletag.pubads().setSlots(slotsB); - - function afterBidHook(data) { - dataReceived = data; - slotsB.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); - - dataReceived.adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.realTimeData).to.have.property('audigent_segments'); - expect(bid.realTimeData.audigent_segments).to.deep.equal(audigentSegments.audigent_segments); - }); - }); + it('should return prediction from server', function (done) { + const adUnits = [getAdUnitMock('hasPrediction')]; + const data = { + p: {'hasPrediction': {p: 0.234}}, + kn: 'bv', + pmd: undefined + }; + browsiRTD.setData(data); + browsiRTD.browsiSubmodule.getData(adUnits, onDone); + function onDone(data) { + expect(data).to.eql({hasPrediction: {bv: '0.20'}}); + done(); } - - requestBidsHook(afterBidHook, { adUnits: adUnits1 }); - }); - }); + }) + }) }); From f0e4433b07b8270bf8f741c735726b0c80a34918 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 20 Aug 2020 05:53:26 -0400 Subject: [PATCH 0102/1476] Update dfpAdServerVideo.js to allow for vast4 (#5608) * Update dfpAdServerVideo.js This had a bug in which all requests were hardcoded to vast 3, although pubs may have selected vast 3 or 4 * Update dfpAdServerVideo_spec.js * Update dfpAdServerVideo_spec.js --- modules/dfpAdServerVideo.js | 2 +- test/spec/modules/dfpAdServerVideo_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 1d999fdbacc..9fe8c26e4f6 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -42,7 +42,7 @@ import { auctionManager } from '../src/auctionManager.js'; const defaultParamConstants = { env: 'vp', gdfp_req: 1, - output: 'xml_vast3', + output: 'vast', unviewed_position_start: 1, }; diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index cd412530d2b..c0ecb9cad5e 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -38,7 +38,7 @@ describe('The DFP video support module', function () { expect(queryParams).to.have.property('env', 'vp'); expect(queryParams).to.have.property('gdfp_req', '1'); expect(queryParams).to.have.property('iu', 'my/adUnit'); - expect(queryParams).to.have.property('output', 'xml_vast3'); + expect(queryParams).to.have.property('output', 'vast'); expect(queryParams).to.have.property('sz', '640x480'); expect(queryParams).to.have.property('unviewed_position_start', '1'); expect(queryParams).to.have.property('url'); @@ -375,7 +375,7 @@ describe('The DFP video support module', function () { expect(queryParams).to.have.property('env', 'vp'); expect(queryParams).to.have.property('gdfp_req', '1'); expect(queryParams).to.have.property('iu', 'my/adUnit'); - expect(queryParams).to.have.property('output', 'xml_vast3'); + expect(queryParams).to.have.property('output', 'vast'); expect(queryParams).to.have.property('sz', '640x480'); expect(queryParams).to.have.property('unviewed_position_start', '1'); expect(queryParams).to.have.property('url'); From ce00f4c3f6672a857255c439a9d67fca3285326b Mon Sep 17 00:00:00 2001 From: invibes <51820283+invibes@users.noreply.github.com> Date: Thu, 20 Aug 2020 18:55:17 +0300 Subject: [PATCH 0103/1476] purposes in call for invibes (#5533) * added tcf 2.0 * Updated adapter to support gdprEnforcement * reverted storage manager initialization * add purposes in call * send purposes in string array * InvibesBidAdapter - gdpr updates * [InvibesBidAdapter] GDPR - purpose adjustments * [InvibesBidAdapter] fixed tests for new alg & reordered adapter checks * add tc string Co-authored-by: florin_nedelcu_invibes Co-authored-by: Cristian Grigoras Co-authored-by: raduchept --- modules/invibesBidAdapter.js | 198 ++++++-------------- test/spec/modules/invibesBidAdapter_spec.js | 168 +++++++++-------- 2 files changed, 145 insertions(+), 221 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 220aed47e15..50ed7d5f57c 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -8,7 +8,7 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 2, + PREBID_VERSION: 4, METHOD: 'GET', INVIBES_VENDOR_ID: 436 }; @@ -39,9 +39,7 @@ export const spec = { }, getUserSyncs: function (syncOptions) { if (syncOptions.iframeEnabled) { - handlePostMessage(); const syncUrl = buildSyncUrl(); - return { type: 'iframe', url: syncUrl @@ -55,6 +53,7 @@ registerBidder(spec); // some state info is required: cookie info, unique user visit id const topWin = getTopMostWindow(); let invibes = topWin.invibes = topWin.invibes || {}; +invibes.purposes = invibes.purposes || [false, false, false, false, false, false, false, false, false, false]; let _customUserSync; function isBidRequestValid(bid) { @@ -89,13 +88,11 @@ function buildRequest(bidRequests, bidderRequest) { _customUserSync = _customUserSync || bidRequest.params.customUserSync; }); - invibes.visitId = invibes.visitId || generateRandomId(); + invibes.optIn = invibes.optIn || readGdprConsent(bidderRequest.gdprConsent); - cookieDomain = detectTopmostCookieDomain(); + invibes.visitId = invibes.visitId || generateRandomId(); invibes.noCookies = invibes.noCookies || invibes.getCookie('ivNoCookie'); - invibes.optIn = invibes.optIn || invibes.getCookie('ivOptIn') || readGdprConsent(bidderRequest.gdprConsent); - - initDomainId(invibes.domainOptions); + let lid = initDomainId(invibes.domainOptions); const currentQueryStringParams = parseQueryStringParams(); @@ -117,14 +114,16 @@ function buildRequest(bidRequests, bidderRequest) { width: topWin.innerWidth, height: topWin.innerHeight, - noc: !cookieDomain, oi: invibes.optIn, - kw: keywords + kw: keywords, + purposes: invibes.purposes.toString(), + + tc: invibes.gdpr_consent }; - if (invibes.dom.id) { - data.lId = invibes.dom.id; + if (lid) { + data.lId = lid; } const parametersToPassForward = 'videoaddebug,advs,bvci,bvid,istop,trybvid,trybvci'.split(','); @@ -278,6 +277,8 @@ function renderCreative(bidModel) { function getCappedCampaignsAsString() { const key = 'ivvcap'; + if (!invibes.optIn || !invibes.purposes[0]) { return ''; } + let loadData = function () { try { return JSON.parse(storage.getDataFromLocalStorage(key)) || {}; @@ -348,49 +349,48 @@ function buildSyncUrl() { return syncUrl; } -function handlePostMessage() { - try { - if (window.addEventListener) { - window.addEventListener('message', acceptPostMessage); - } - } catch (e) { } -} - -function acceptPostMessage(e) { - let msg = e.data || {}; - if (msg.ivbscd === 1) { - invibes.setCookie(msg.name, msg.value, msg.exdays, msg.domain); - } else if (msg.ivbscd === 2) { - invibes.dom.graduate(); - } -} - function readGdprConsent(gdprConsent) { if (gdprConsent && gdprConsent.vendorData) { + invibes.gdpr_consent = getVendorConsentData(gdprConsent.vendorData); + if (!gdprConsent.vendorData.gdprApplies || gdprConsent.vendorData.hasGlobalConsent) { + var index; + for (index = 0; index < invibes.purposes; ++index) { + invibes.purposes[index] = true; + } return 2; } let purposeConsents = getPurposeConsents(gdprConsent.vendorData); if (purposeConsents == null) { return 0; } - let properties = Object.keys(purposeConsents); - let purposeConsentsCounter = getPurposeConsentsCounter(gdprConsent.vendorData); + let purposesLength = getPurposeConsentsCounter(gdprConsent.vendorData); - if (properties.length < purposeConsentsCounter) { + if (purposeConsents instanceof Array) { + for (let i = 0; i < purposesLength && i < purposeConsents.length; i++) { + invibes.purposes[i] = !((purposeConsents[i] === false || purposeConsents[i] === 'false' || purposeConsents[i] == null)); + } + } else if (typeof purposeConsents === 'object' && purposeConsents !== null) { + let i = 0; + for (let prop in purposeConsents) { + if (i === purposesLength) { + break; + } + + if (purposeConsents.hasOwnProperty(prop)) { + invibes.purposes[i] = !((purposeConsents[prop] === false || purposeConsents[prop] === 'false' || purposeConsents[prop] == null)); + i++; + } + } + } else { return 0; } - for (let i = 0; i < purposeConsentsCounter; i++) { - if (!purposeConsents[properties[i]] || purposeConsents[properties[i]] === 'false') { return 0; } - } - + let invibesVendorId = CONSTANTS.INVIBES_VENDOR_ID.toString(10); let vendorConsents = getVendorConsents(gdprConsent.vendorData); - if (vendorConsents == null || vendorConsents[CONSTANTS.INVIBES_VENDOR_ID.toString(10)] == null) { - return 4; - } + if (vendorConsents == null || vendorConsents[invibesVendorId] == null) { return 4; } - if (vendorConsents[CONSTANTS.INVIBES_VENDOR_ID.toString(10)] === false) { return 0; } + if (vendorConsents[invibesVendorId] === false) { return 0; } return 2; } @@ -418,6 +418,13 @@ function getPurposeConsents(vendorData) { return null; } +function getVendorConsentData(vendorData) { + if (vendorData.purpose && vendorData.purpose.consents) { + if (vendorData.tcString != null) { return vendorData.tcString; } + } + return vendorData.consentData; +}; + function getVendorConsents(vendorData) { if (vendorData.vendor && vendorData.vendor.consents) { return vendorData.vendor.consents; @@ -443,55 +450,15 @@ invibes.Uid = { } }; -let cookieDomain; invibes.getCookie = function (name) { if (!storage.cookiesAreEnabled()) { return; } - let i, x, y; - let cookies = document.cookie.split(';'); - for (i = 0; i < cookies.length; i++) { - x = cookies[i].substr(0, cookies[i].indexOf('=')); - y = cookies[i].substr(cookies[i].indexOf('=') + 1); - x = x.replace(/^\s+|\s+$/g, ''); - if (x === name) { - return unescape(y); - } - } -}; -invibes.setCookie = function (name, value, exdays, domain) { - if (!storage.cookiesAreEnabled()) { return; } - let whiteListed = name == 'ivNoCookie' || name == 'IvbsCampIdsLocal'; - if (invibes.noCookies && !whiteListed && (exdays || 0) >= 0) { return; } - if (exdays > 365) { exdays = 365; } - domain = domain || cookieDomain; - let exdate = new Date(); - let exms = exdays * 24 * 60 * 60 * 1000; - exdate.setTime(exdate.getTime() + exms); - storage.setCookie(name, value, exdate.toUTCString(), undefined, domain); -}; + if (!invibes.optIn || !invibes.purposes[0]) { return; } -let detectTopmostCookieDomain = function () { - let testCookie = invibes.Uid.generate(); - let hostParts = location.hostname.split('.'); - if (hostParts.length === 1) { - return location.hostname; - } - for (let i = hostParts.length - 1; i >= 0; i--) { - let domain = '.' + hostParts.slice(i).join('.'); - invibes.setCookie(testCookie, testCookie, 1, domain); - let val = invibes.getCookie(testCookie); - if (val === testCookie) { - invibes.setCookie(testCookie, testCookie, -1, domain); - return domain; - } - } + return storage.getCookie(name); }; let initDomainId = function (options) { - if (invibes.dom) { return; } - - options = options || {}; - let cookiePersistence = { cname: 'ivbsdid', load: function () { @@ -499,75 +466,16 @@ let initDomainId = function (options) { try { return JSON.parse(str); } catch (e) { } - }, - save: function (obj) { - invibes.setCookie(this.cname, JSON.stringify(obj), 365); - } - }; - - let persistence = options.persistence || cookiePersistence; - let state; - let minHC = 2; - - let validGradTime = function (state) { - if (!state.cr) { return false; } - let min = 151 * 10e9; - if (state.cr < min) { - return false; } - let now = new Date().getTime(); - let age = now - state.cr; - let minAge = 24 * 60 * 60 * 1000; - return age > minAge; - }; - - state = persistence.load() || { - id: invibes.Uid.generate(), - cr: new Date().getTime(), - hc: 1, }; - if (state.id.match(/\./)) { - state.id = invibes.Uid.generate(); - } - - let graduate = function () { - if (!state.cr) { return; } - delete state.cr; - delete state.hc; - persistence.save(state); - setId(); - }; + options = options || {}; - let regenerateId = function () { - state.id = invibes.Uid.generate(); - persistence.save(state); - }; + var persistence = options.persistence || cookiePersistence; - let setId = function () { - invibes.dom = { - get id() { - return (!state.cr && invibes.optIn > 0) ? state.id : undefined; - }, - get tempId() { - return (invibes.optIn > 0) ? state.id : undefined; - }, - graduate: graduate, - regen: regenerateId - }; - }; + let state = persistence.load(); - if (state.cr && !options.noVisit) { - if (state.hc < minHC) { - state.hc++; - } - if ((state.hc >= minHC && validGradTime(state)) || options.skipGraduation) { - graduate(); - } - } - persistence.save(state); - setId(); - ivLogger.info('Did=' + invibes.dom.id); + return state ? (state.id || state.tempId) : undefined; }; let keywords = (function () { diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index b75c6a4578f..442039504f8 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -137,11 +137,19 @@ describe('invibesBidAdapter:', function () { }); it('has capped ids if local storage variable is correctly formatted', function () { + top.window.invibes.optIn = 1; + top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false]; localStorage.ivvcap = '{"9731":[1,1768600800000]}'; const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); expect(request.data.capCounts).to.equal('9731=1'); }); + it('does not have capped ids if local storage variable is correctly formatted but no opt in', function () { + localStorage.ivvcap = '{"9731":[1,1768600800000]}'; + const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + expect(request.data.capCounts).to.equal(''); + }); + it('does not have capped ids if local storage variable is incorrectly formatted', function () { localStorage.ivvcap = ':[1,1574334216992]}'; const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); @@ -178,104 +186,102 @@ describe('invibesBidAdapter:', function () { expect(JSON.parse(request.data.bidParamsJson).placementIds).to.contain(bidRequests[1].params.placementId); }); - it('uses cookies', function () { - global.document.cookie = 'ivNoCookie=1'; + it('sends undefined lid when no cookie', function () { let request = spec.buildRequests(bidRequests); expect(request.data.lId).to.be.undefined; }); - it('doesnt send the domain id if not graduated', function () { - global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1522929537626,"hc":1}'; - let request = spec.buildRequests(bidRequests); - expect(request.data.lId).to.not.exist; - }); - - it('try to graduate but not enough count - doesnt send the domain id', function () { - global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":0}'; - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - }); - - it('try to graduate but not old enough - doesnt send the domain id', function () { - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":5}'; - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - }); - - it('graduate and send the domain id', function () { - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - stubDomainOptions(new StubbedPersistence('{"id":"dvdjkams6nkq","cr":1521818537626,"hc":7}')); - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.exist; - }); - - it('send the domain id if already graduated', function () { - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi"}')); - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.exist; - expect(top.window.invibes.dom.tempId).to.exist; - }); - - it('send the domain id after replacing it with new format', function () { + it('sends lid when comes on cookie', function () { + top.window.invibes.optIn = 1; + top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false]; + global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}'; let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.lId).to.exist; - expect(top.window.invibes.dom.tempId).to.exist; }); - it('dont send the domain id if consent declined on tcf v1', function () { + it('should send purpose 1', function () { let bidderRequest = { gdprConsent: { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: false} + vendor: {consents: {436: true}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } } } }; - stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - expect(top.window.invibes.dom.tempId).to.not.exist; - expect(request.data.oi).to.equal(0); + expect(request.data.purposes.split(',')[0]).to.equal('true'); }); - it('dont send the domain id if consent declined on tcf v2', function () { + it('should send purpose 2 & 7', function () { let bidderRequest = { gdprConsent: { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: false}} + vendor: {consents: {436: true}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } } } }; - stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - expect(top.window.invibes.dom.tempId).to.not.exist; - expect(request.data.oi).to.equal(0); - }); - - it('dont send the domain id if no consent', function () { - let bidderRequest = {}; - stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}')); - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - expect(top.window.invibes.dom.tempId).to.not.exist; + expect(request.data.purposes.split(',')[1] && request.data.purposes.split(',')[6]).to.equal('true'); }); - it('try to init id but was already loaded on page - does not increment the id again', function () { - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; - global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":0}'; + it('should send purpose 9', function () { + let bidderRequest = { + gdprConsent: { + vendorData: { + gdprApplies: true, + hasGlobalConsent: false, + vendor: {consents: {436: true}}, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true + } + } + } + } + }; let request = spec.buildRequests(bidRequests, bidderRequest); - request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.lId).to.not.exist; - expect(top.window.invibes.dom.tempId).to.exist; + expect(request.data.purposes.split(',')[9]).to.equal('true'); }); it('should send oi = 0 when vendorData is null', function () { @@ -344,7 +350,7 @@ describe('invibesBidAdapter:', function () { expect(request.data.oi).to.equal(0); }); - it('should send oi = 0 when purpose consents weren\'t approved on tcf v2', function () { + it('should send oi = 2 when purpose consents weren\'t approved on tcf v2', function () { let bidderRequest = { gdprConsent: { vendorData: { @@ -369,10 +375,10 @@ describe('invibesBidAdapter:', function () { } }; let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.oi).to.equal(0); + expect(request.data.oi).to.equal(2); }); - it('should send oi = 0 when purpose consents are less then 10 on tcf v2', function () { + it('should send oi = 2 when purpose consents are less then 10 on tcf v2', function () { let bidderRequest = { gdprConsent: { vendorData: { @@ -392,7 +398,7 @@ describe('invibesBidAdapter:', function () { } }; let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.oi).to.equal(0); + expect(request.data.oi).to.equal(2); }); it('should send oi = 4 when vendor consents are null on tcf v2', function () { @@ -527,7 +533,7 @@ describe('invibesBidAdapter:', function () { expect(request.data.oi).to.equal(2); }); - it('should send oi = 0 when purpose consents weren\'t approved on tcf v1', function () { + it('should send oi = 2 when purpose consents weren\'t approved on tcf v1', function () { let bidderRequest = { gdprConsent: { vendorData: { @@ -545,10 +551,10 @@ describe('invibesBidAdapter:', function () { } }; let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.oi).to.equal(0); + expect(request.data.oi).to.equal(2); }); - it('should send oi = 0 when purpose consents are less then 5 on tcf v1', function () { + it('should send oi = 2 when purpose consents are less then 5 on tcf v1', function () { let bidderRequest = { gdprConsent: { vendorData: { @@ -564,7 +570,7 @@ describe('invibesBidAdapter:', function () { } }; let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.oi).to.equal(0); + expect(request.data.oi).to.equal(2); }); it('should send oi = 0 when vendor consents for invibes are false on tcf v1', function () { @@ -669,8 +675,8 @@ describe('invibesBidAdapter:', function () { context('when the response is valid', function () { it('responds with a valid bid', function () { - top.window.invibes.setCookie('a', 'b', 370); - top.window.invibes.setCookie('c', 'd', 0); + // top.window.invibes.setCookie('a', 'b', 370); + // top.window.invibes.setCookie('c', 'd', 0); let result = spec.interpretResponse({body: response}, {bidRequests}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); @@ -705,6 +711,16 @@ describe('invibesBidAdapter:', function () { expect(response.url).to.include(SYNC_ENDPOINT); expect(response.url).to.include('optIn'); expect(response.url).to.include('ivvbks'); + }); + + it('returns an iframe with params including if enabled', function () { + top.window.invibes.optIn = 1; + global.document.cookie = 'ivvbks=17639.0,1,2;ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}'; + let response = spec.getUserSyncs({iframeEnabled: true}); + expect(response.type).to.equal('iframe'); + expect(response.url).to.include(SYNC_ENDPOINT); + expect(response.url).to.include('optIn'); + expect(response.url).to.include('ivvbks'); expect(response.url).to.include('ivbsdid'); }); From 2c0ea15334bd756aa2bd0dbcc37c921f2b95abcb Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Thu, 20 Aug 2020 08:57:28 -0700 Subject: [PATCH 0104/1476] Update rubicon adapter for sharedid userid support (#5627) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * update liveramp userid support * changed source value to all lowercase * update share id name * add unit test for shareid eid * update shareid obj paths Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/rubiconBidAdapter.js | 21 +++++++++++++++- test/spec/modules/rubiconBidAdapter_spec.js | 28 ++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 5d4c8d8f78c..0e41bf6c6ad 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -248,7 +248,7 @@ export const spec = { } if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { + (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedid)) { utils.deepSetValue(data, 'user.ext.eids', []); if (bidRequest.userId.tdid) { @@ -300,6 +300,20 @@ export const spec = { }] }); } + + // support shared id + if (bidRequest.userId.sharedid) { + data.user.ext.eids.push({ + source: 'sharedid.org', + uids: [{ + id: bidRequest.userId.sharedid.id, + atype: 3, + ext: { + third: bidRequest.userId.sharedid.third + } + }] + }); + } } if (config.getConfig('coppa') === true) { @@ -566,6 +580,11 @@ export const spec = { if (bidRequest.userId.idl_env) { data['x_liverampidl'] = bidRequest.userId.idl_env; } + + // support shared id + if (bidRequest.userId.sharedid) { + data['eid_sharedid.org'] = `${bidRequest.userId.sharedid.id}^3^${bidRequest.userId.sharedid.third}`; + } } if (bidderRequest.gdprConsent) { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index cddf190fda4..1d5769b9ac1 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -224,7 +224,11 @@ describe('the rubicon adapter', function () { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, - idl_env: '1111-2222-3333-4444' + idl_env: '1111-2222-3333-4444', + sharedid: { + id: '1111', + third: '2222' + } }; bid.storedAuctionResponse = 11111; } @@ -1357,6 +1361,22 @@ describe('the rubicon adapter', function () { expect(data['x_liverampidl']).to.equal('1111-2222-3333-4444'); }); }); + + describe('SharedID support', function () { + it('should send sharedid when userId defines sharedId', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + sharedid: { + id: '1111', + third: '2222' + } + }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_sharedid.org']).to.equal('1111^3^2222'); + }); + }); }) describe('Prebid AdSlot', function () { @@ -1551,6 +1571,12 @@ describe('the rubicon adapter', function () { // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + // SharedId should exist + expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); + expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); + expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); + expect(post.rp).that.is.an('object'); expect(post.rp.target).that.is.an('object'); expect(post.rp.target.LIseg).that.is.an('array'); From 1a38f40ad0d29478e68f76851a14dc8206ae4069 Mon Sep 17 00:00:00 2001 From: Michael Griego Date: Thu, 20 Aug 2020 10:59:01 -0500 Subject: [PATCH 0105/1476] Allow hp:0 in supply chain nodes passed to Rubicon (#5629) --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 0e41bf6c6ad..2768b41da21 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1195,7 +1195,7 @@ export function hasValidSupplyChainParams(schain) { if (!schain.nodes) return isValid; isValid = schain.nodes.reduce((status, node) => { if (!status) return status; - return requiredFields.every(field => node[field]); + return requiredFields.every(field => node.hasOwnProperty(field)); }, true); if (!isValid) utils.logError('Rubicon: required schain params missing'); return isValid; From a1692da2779e60144efb51cb129fbba7bf0bdbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E6=80=9D=E6=95=8F?= <506374983@qq.com> Date: Fri, 21 Aug 2020 00:02:48 +0800 Subject: [PATCH 0106/1476] =?UTF-8?q?Add=20a=20new=20bidder=20adapter=20?= =?UTF-8?q?=EF=BC=9AmediagoBidderAdapter=20(#5614)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mediago bid adaptor * 修改去掉固定ip * mediago bid adapter: add test spec * spec code style * change into prod EP and auto size. * delete other param * 修改为强制https协议 --- modules/mediagoBidAdapter.js | 362 ++++++++++++++++++++ modules/mediagoBidAdapter.md | 33 ++ test/spec/modules/mediagoBidAdapter_spec.js | 101 ++++++ 3 files changed, 496 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..68eb95ddae3 --- /dev/null +++ b/modules/mediagoBidAdapter.js @@ -0,0 +1,362 @@ +/** + * gulp serve --modules=mediagoBidAdapter --nolint --notest + */ + +import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { + registerBidder +} from '../src/adapters/bidderFactory.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 +) { + return a // if the placeholder was passed, return + ? ( // a random number from 0 to 15 + 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|bb\d+|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; +} + +/** + * 将尺寸转为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; +} + +/** + * 获取广告位配置 + * @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) { + if (size.width === 300 && size.height === 250) { + matchSize = size; + break; + } + } + + // if (mediaTypes.native) {} + // banner广告类型 + if (mediaTypes.banner) { + let id = '' + (i + 1); + ret = { + id: id, + // bidFloor: 0, // todo + banner: { + h: matchSize.height, + w: matchSize.width, + pos: 1, + } + }; + 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) { + // console.log(validBidRequests, bidderRequest); + let isMobile = isMobileAndTablet() ? 1 : 0; + let isTest = 0; + let auctionId = getProperty(bidderRequest, 'auctionId') || getRandomId(); + let items = getItems(validBidRequests, bidderRequest); + + const domain = document.domain; + const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + + if (items && items.length) { + let c = { + 'id': 'mgprebidjs_' + auctionId, + 'test': +isTest, + 'at': 1, + 'cur': ['USD'], + 'device': { + // 'dnt':0, + // 'devicetype':2, + 'js': 1, + 'os': navigator.platform || '', + 'ua': navigator.userAgent, + 'language': /en/.test(navigator.language) ? 'en' : navigator.language, + // 'geo':{ + // 'country':'USA' + // } + }, + 'user': { + 'id': getUserID() // todo + }, + 'site': { + 'name': domain, + 'domain': domain, + 'page': location, + 'ref': location, + 'mobile': isMobile, + 'cat': [], // todo + 'publisher': { // todo + 'id': domain, + 'name': domain + } + }, + 'imp': items + }; + 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') + }; + 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); + // Bidder specific code + }, + + /** + * 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..87c38f662a3 --- /dev/null +++ b/modules/mediagoBidAdapter.md @@ -0,0 +1,33 @@ +# 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..25cfc72672b --- /dev/null +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -0,0 +1,101 @@ +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' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '5e24a2ce-db03-4565-a8a3-75dbddca9377', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '54d73f19c9d47a', + 'bidderRequestId': '4fec04e87ad785', + 'auctionId': '7fae02a9-0195-472f-ba94-708d3bc2c0d9', + '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); + // console.log(request); + let req_data = JSON.parse(request.data); + expect(req_data.imp).to.have.lengthOf(1); + }); + + it('mediago:validate_response_params', function() { + let serverResponse = { + body: { + 'id': '7244645c-a81a-4760-8fd6-9d908d2c4a44', + 'seatbid': [{ + 'bid': [{ + 'id': 'aa86796a857ebedda9a2d7128a87dab1', + 'impid': '1', + 'price': 0.05, + 'nurl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=1\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=-BFDu2NYtc4PYTplFW_2JcnDSRVLOOfaERbwJABjFyG6NUB4ywA3dUaXt5zPlyCUpBCOxjH9gk4E6yWTshzuSfQSx7g_TxvcXYUgh7YtY9NQZxx14InmNCTsezqID5UztV7llz8SXWHQ-ZsutH1nJIZzl1jH3i2uCPi91shqIZLN1bLJ5guAr5O4WyxVeOqIKyD_GiVcY9Olm51iI_3wgwFyDEN_dIDv-ObgNxpbPD0L11-62bjhGw3__7RuEo6XLdox-g46Fcqk6i0zayfsPM4QeMAhWJ4lsg-xswSI0YAfzyoOIeTWB78mdpt_GmN5PKZZPqyO7VkbwHEasn-mTyYTddbz5v2fzEkRO0AQZtAZx96PANGrNvcOHnRVmCdkzN96b5Ur1_8ipdyzHOFRtJ-z_KmKaxig6himvMCePozZvrvihiGhigP4RGiFT7ytVYKHyUGAV2PF5SwtgnB0uGCltd7o1CLhZyZEQNgE7LSESyGztZ5kM9N_VZV9gPZVhvlJDfYFNRW9i6D2pZxV0Gd63rA9gpeUJ3mhbkj-B27VRKrNTBSrwIAU7P0RPD5_opl3G8nPD1Ce2vKuQK8qynHWQblfeA61nDok-fRezSKbzwepqi8oxXadFrCmN7KxP_mPqA794xYzIw5-mS64NA', + 'burl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=2\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=dXerAvyp4zYQzsQ56eGB4JtiA4yFaYlTqcHffccrvCg', + 'lurl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=0\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=ptSxg_vR7-fdx-WAkkUADJb__BntE5a6-RSeYdUewvk', + 'adm': '\u003clink rel=\"stylesheet\" href=\"//cdn.mediago.io/js/style/style_banner_300*250.css\"\u003e\u003cdiv id=\"mgcontainer-583ce3286b442001205b2fb9a5488efc\" class=\"mediago-placement imgTopTitleBottom\" style=\"position:relative;width:298px;height:248px;overflow:hidden\"\u003e\u003ca href=\"http://trace.mediago.io/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=102\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026clickid=25_aa86796a857ebedda9a2d7128a87dab1_25-300x175-1\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\u0026jt=2\u0026url=PQFFci337KgCVkx7KTgRItClLaWH0lgTcIlgBRTpfKngVJES4uKLfxXz9mjLcDWIbWQcEVVk_gfTcIaK8oKO2YyVG5lc3hjZeZr0VaIDHbWggPJaqtfDK9T0HZIKvrpe\" target=\"_blank\"\u003e\u003cimg alt=\"Robert Vowed To Keep Silent, But Decided To Put The People First And Speak\" src=\"https://d2cli4kgl5uxre.cloudfront.net/ML/b5e361889beef5eaf69987384b7a56e8__300x175.png\" style=\"height:70%;width:100%;border-width:0;border:none;\"\u003e\u003ch3 class=\"title\" style=\"font-size:16px;\"\u003eRobert Vowed To Keep Silent, But Decided To Put The People First And Speak\u003c/h3\u003e\u003c/a\u003e\u003cspan class=\"source\"\u003e\u003ca class=\"sourcename\" href=\"//www.mediago.io\" target=\"_blank\"\u003e\u003cspan\u003eAd\u003c/span\u003e \u003c/a\u003e\u003ca class=\"srcnameadslabelurl\" href=\"//www.mediago.io/privacy\" target=\"_blank\"\u003e\u003cspan\u003eViral Net News\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/div\u003e\u003cscript\u003e!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){\"undefined\"!=typeof Symbol\u0026\u0026Symbol.toStringTag\u0026\u0026Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},n.t=function(e,t){if(1\u0026t\u0026\u0026(e=n(e)),8\u0026t)return e;if(4\u0026t\u0026\u0026\"object\"==typeof e\u0026\u0026e\u0026\u0026e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,\"default\",{enumerable:!0,value:e}),2\u0026t\u0026\u0026\"string\"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e\u0026\u0026e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\"a\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\"\",n(n.s=24)}({24:function(e,t,n){\"use strict\";function o(e){var t=new Image;t.src=e,t.style=\"display:none;visibility:hidden\",t.width=0,t.height=0,document.body.appendChild(t)}o(\"http://d21uzv52i0cqie.cloudfront.net/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=101\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\");var r=document.getElementById(\"mgcontainer-583ce3286b442001205b2fb9a5488efc\"),i=!1;!function e(){setTimeout((function(){var t,n;!i\u0026\u0026(t=r,n=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,(t.getBoundingClientRect()\u0026\u0026t.getBoundingClientRect().top)\u003c=n-.75*(t.offsetHeight||t.clientHeight))?(i=!0,o(\"http://d21uzv52i0cqie.cloudfront.net/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=104\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\u0026sid=16__11__13\u0026format=\u0026crid=b5e361889beef5eaf69987384b7a56e8\")):e()}),500)}()}});\u003c/script\u003e\u003cscript type=\"text/javascript\" src=\"http://d21uzv52i0cqie.cloudfront.net/api/track?tn=341443089c0cb829164455a42d216ee3\u0026price=${AUCTION_PRICE}\u0026evt=5\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026impid=1\u0026offerid=\u0026tagid=\u0026test=0\u0026time=1597714943\u0026adp=5zrCZ2rC-WLafYkNpeTnzA72tDqVZUlOA3Js6_eJjYU\u0026dsp_id=22\u0026cp=${cp}\u0026url=\u0026type=script\"\u003e\u003c/script\u003e\u003cscript\u003edocument.addEventListener\u0026\u0026document.addEventListener(\"click\",function(){var a=document.createElement(\"script\");a.src=\"http://d21uzv52i0cqie.cloudfront.net/api/track?tn=341443089c0cb829164455a42d216ee3\u0026price=${AUCTION_PRICE}\u0026evt=6\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026impid=1\u0026offerid=\u0026tagid=\u0026test=0\u0026time=1597714943\u0026adp=5zrCZ2rC-WLafYkNpeTnzA72tDqVZUlOA3Js6_eJjYU\u0026dsp_id=22\u0026cp=${cp}\u0026url=\u0026clickid=25_aa86796a857ebedda9a2d7128a87dab1_1\";document.body.appendChild(a)});\u003c/script\u003e', + 'cid': '1003540', + 'crid': 'b5e361889beef5eaf69987384b7a56e8', + '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('b5e361889beef5eaf69987384b7a56e8'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); +}); From 0d8d9bf65bff06386b0dea66b158afd4a326e3b0 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 20 Aug 2020 18:30:34 +0200 Subject: [PATCH 0107/1476] updated userid module to stop caching the entire consent object (#5641) * updated userid module to stop caching the entire consent object but rather just a hash of it, since all we need it for is comparison purposes. * IE doesn't support Math.imul, so providing a polyfill for it when necessary * use `===` to compare consent values; convert hashes to a string when returning them * add test for string response and fix @returns doc * don't use polyfills! --- modules/userId/index.js | 13 ++++------- src/utils.js | 40 ++++++++++++++++++++++++++++++++ test/spec/modules/userId_spec.js | 35 ---------------------------- test/spec/utils_spec.js | 20 ++++++++++++++++ 4 files changed, 65 insertions(+), 43 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index afdd93a57ba..bcc0f68b2b0 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -232,7 +232,7 @@ function getStoredValue(storage, key = undefined) { * @param consentData * @returns {{apiVersion: number, gdprApplies: boolean, consentString: string}} */ -function makeStoredConsentDataObject(consentData) { +function makeStoredConsentDataHash(consentData) { const storedConsentData = { consentString: '', gdprApplies: false, @@ -244,8 +244,7 @@ function makeStoredConsentDataObject(consentData) { storedConsentData.gdprApplies = consentData.gdprApplies; storedConsentData.apiVersion = consentData.apiVersion; } - - return storedConsentData; + return utils.simpleHash(JSON.stringify(storedConsentData)); } /** @@ -255,7 +254,7 @@ function makeStoredConsentDataObject(consentData) { export function setStoredConsentData(consentData) { try { const expiresStr = (new Date(Date.now() + (CONSENT_DATA_COOKIE_STORAGE_CONFIG.expires * (60 * 60 * 24 * 1000)))).toUTCString(); - coreStorage.setCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name, JSON.stringify(makeStoredConsentDataObject(consentData)), expiresStr, 'Lax'); + coreStorage.setCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name, makeStoredConsentDataHash(consentData), expiresStr, 'Lax'); } catch (error) { utils.logError(error); } @@ -266,13 +265,11 @@ export function setStoredConsentData(consentData) { * @returns {string} */ function getStoredConsentData() { - let storedValue; try { - storedValue = JSON.parse(coreStorage.getCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name)); + return coreStorage.getCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name); } catch (e) { utils.logError(e); } - return storedValue; } /** @@ -287,7 +284,7 @@ function storedConsentDataMatchesConsentData(storedConsentData, consentData) { return ( typeof storedConsentData === 'undefined' || storedConsentData === null || - utils.deepEqual(storedConsentData, makeStoredConsentDataObject(consentData)) + storedConsentData === makeStoredConsentDataHash(consentData) ); } diff --git a/src/utils.js b/src/utils.js index f0fa57c4cff..6801a7dc1d1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1215,3 +1215,43 @@ export function mergeDeep(target, ...sources) { return mergeDeep(target, ...sources); } + +/** + * returns a hash of a string using a fast algorithm + * source: https://stackoverflow.com/a/52171480/845390 + * @param str + * @param seed (optional) + * @returns {string} + */ +export function simpleHash(str, seed = 0) { + // IE doesn't support imul + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul#Polyfill + let imul = function(opA, opB) { + if (isFn(Math.imul)) { + return Math.imul(opA, opB); + } else { + opB |= 0; // ensure that opB is an integer. opA will automatically be coerced. + // floating points give us 53 bits of precision to work with plus 1 sign bit + // automatically handled for our convienence: + // 1. 0x003fffff /*opA & 0x000fffff*/ * 0x7fffffff /*opB*/ = 0x1fffff7fc00001 + // 0x1fffff7fc00001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/ + var result = (opA & 0x003fffff) * opB; + // 2. We can remove an integer coersion from the statement above because: + // 0x1fffff7fc00001 + 0xffc00000 = 0x1fffffff800001 + // 0x1fffffff800001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/ + if (opA & 0xffc00000) result += (opA & 0xffc00000) * opB | 0; + return result | 0; + } + }; + + let h1 = 0xdeadbeef ^ seed; + let h2 = 0x41c6ce57 ^ seed; + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = imul(h1 ^ ch, 2654435761); + h2 = imul(h2 ^ ch, 1597334677); + } + h1 = imul(h1 ^ (h1 >>> 16), 2246822507) ^ imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = imul(h2 ^ (h2 >>> 16), 2246822507) ^ imul(h1 ^ (h1 >>> 13), 3266489909); + return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(); +} diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index d9671aabc84..fd7e8a76972 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1591,13 +1591,6 @@ describe('User ID', function() { sinon.assert.notCalled(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.calledOnce(mockExtendId); - - let consent = gdprDataHandler.getConsentData(); - let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); - expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); - expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); - expect(userIdStoredConsent.consentString).to.equal(consent.consentString); - expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); }); it('calls getId if no stored consent data but refresh is needed', function () { @@ -1611,13 +1604,6 @@ describe('User ID', function() { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); - - let consent = gdprDataHandler.getConsentData(); - let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); - expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); - expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); - expect(userIdStoredConsent.consentString).to.equal(consent.consentString); - expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); }); it('calls getId if empty stored consent and refresh not needed', function () { @@ -1633,13 +1619,6 @@ describe('User ID', function() { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); - - let consent = gdprDataHandler.getConsentData(); - let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); - expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); - expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); - expect(userIdStoredConsent.consentString).to.equal(consent.consentString); - expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); }); it('calls getId if stored consent does not match current consent and refresh not needed', function () { @@ -1659,13 +1638,6 @@ describe('User ID', function() { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); - - let consent = gdprDataHandler.getConsentData(); - let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); - expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); - expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); - expect(userIdStoredConsent.consentString).to.equal(consent.consentString); - expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); }); it('does not call getId if stored consent matches current consent and refresh not needed', function () { @@ -1685,13 +1657,6 @@ describe('User ID', function() { sinon.assert.notCalled(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.calledOnce(mockExtendId); - - let consent = gdprDataHandler.getConsentData(); - let userIdStoredConsent = JSON.parse(coreStorage.getCookie(CONSENT_LOCAL_STORAGE_NAME)); - expect(userIdStoredConsent.gdprApplies).to.equal(consent.gdprApplies); - expect(userIdStoredConsent.gdprApplies).to.equal(testConsentData.gdprApplies); - expect(userIdStoredConsent.consentString).to.equal(consent.consentString); - expect(userIdStoredConsent.consentString).to.equal(testConsentData.consentData); }); }); }); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index fca59633ebe..dbaad919bd3 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1177,5 +1177,25 @@ describe('Utils', function () { } expect(utils.deepEqual(obj1, obj2)).to.equal(false); }); + + describe('simpleHash', function() { + it('should return the same hash for the same string', function() { + const stringOne = 'string1'; + expect(utils.simpleHash(stringOne)).to.equal(utils.simpleHash(stringOne)); + }); + it('should return a different hash for the same string with different seeds', function() { + const stringOne = 'string1'; + expect(utils.simpleHash(stringOne, 1)).to.not.equal(utils.simpleHash(stringOne, 2)); + }); + it('should return a different hash for different strings with the same seed', function() { + const stringOne = 'string1'; + const stringTwo = 'string2'; + expect(utils.simpleHash(stringOne)).to.not.equal(utils.simpleHash(stringTwo)); + }); + it('should return a string value, not a number', function() { + const stringOne = 'string1'; + expect(typeof utils.simpleHash(stringOne)).to.equal('string'); + }); + }); }); }); From b4c6b47bf90f48e6ffcab8f7e5aa287de0119a57 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Thu, 20 Aug 2020 12:38:22 -0500 Subject: [PATCH 0108/1476] New bidder & analytics adapter: Concert (attempt #2) (#5623) * Add concert bid adapter, doc and tests. * Add analytics adapter * Add email * fix alert from lgtm * try to fix test for ie 11 * Handle USP string for PPID * Fix linking error * Debug: Find out why IE11 is failing * More debugging * More debugging * Attempt to store queue in-prototype * Revert "Attempt to store queue in-prototype" This reverts commit 829ad84485369fbae3f65c738b37d1af2bbc4625. * More debugging * More debugging * Remove Array.includes to support IE11 Co-authored-by: Messay Bekele Co-authored-by: Messay Bekele Co-authored-by: Andrew Amato --- modules/concertAnalyticsAdapter.js | 120 ++++++++++ modules/concertAnalyticsAdapter.md | 11 + modules/concertBidAdapter.js | 208 +++++++++++++++++ modules/concertBidAdapter.md | 33 +++ .../modules/concertAnalyticsAdapter_spec.js | 157 +++++++++++++ test/spec/modules/concertBidAdapter_spec.js | 219 ++++++++++++++++++ 6 files changed, 748 insertions(+) create mode 100644 modules/concertAnalyticsAdapter.js create mode 100644 modules/concertAnalyticsAdapter.md create mode 100644 modules/concertBidAdapter.js create mode 100644 modules/concertBidAdapter.md create mode 100644 test/spec/modules/concertAnalyticsAdapter_spec.js create mode 100644 test/spec/modules/concertBidAdapter_spec.js diff --git a/modules/concertAnalyticsAdapter.js b/modules/concertAnalyticsAdapter.js new file mode 100644 index 00000000000..a81d07e63b5 --- /dev/null +++ b/modules/concertAnalyticsAdapter.js @@ -0,0 +1,120 @@ +import {ajax} from '../src/ajax.js'; +import adapter from '../src/AnalyticsAdapter.js'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager.js'; +import * as utils from '../src/utils.js'; + +const analyticsType = 'endpoint'; + +// We only want to send about 1% of events for sampling purposes +const SAMPLE_RATE_PERCENTAGE = 1 / 100; +const pageIncludedInSample = sampleAnalytics(); + +const url = 'https://bids.concert.io/analytics'; + +const { + EVENTS: { + BID_RESPONSE, + BID_WON, + AUCTION_END + } +} = CONSTANTS; + +let queue = []; + +let concertAnalytics = Object.assign(adapter({url, analyticsType}), { + track({ eventType, args }) { + switch (eventType) { + case BID_RESPONSE: + if (args.bidder !== 'concert') break; + queue.push(mapBidEvent(eventType, args)); + break; + + case BID_WON: + if (args.bidder !== 'concert') break; + queue.push(mapBidEvent(eventType, args)); + break; + + case AUCTION_END: + // Set a delay, as BID_WON events will come after AUCTION_END events + setTimeout(() => sendEvents(), 3000); + break; + + default: + break; + } + } +}); + +function mapBidEvent(eventType, args) { + const { adId, auctionId, cpm, creativeId, width, height, timeToRespond } = args; + const [gamCreativeId, concertRequestId] = getConcertRequestId(creativeId); + + const payload = { + event: eventType, + concert_rid: concertRequestId, + adId, + auctionId, + creativeId: gamCreativeId, + position: args.adUnitCode, + url: window.location.href, + cpm, + width, + height, + timeToRespond + } + + return payload; +} + +/** + * In order to pass back the concert_rid from CBS, it is tucked into the `creativeId` + * slot in the bid response and combined with a pipe `|`. This method splits the creative ID + * and the concert_rid. + * + * @param {string} creativeId + */ +function getConcertRequestId(creativeId) { + if (!creativeId || creativeId.indexOf('|') < 0) return [null, null]; + + return creativeId.split('|'); +} + +function sampleAnalytics() { + return Math.random() <= SAMPLE_RATE_PERCENTAGE; +} + +function sendEvents() { + concertAnalytics.eventsStorage = queue; + + if (!queue.length) return; + + if (!pageIncludedInSample) { + utils.logMessage('Page not included in sample for Concert Analytics'); + return; + } + + try { + const body = JSON.stringify(queue); + ajax(url, () => queue = [], body, { + contentType: 'application/json', + method: 'POST' + }); + } catch (err) { utils.logMessage('Concert Analytics error') } +} + +// save the base class function +concertAnalytics.originEnableAnalytics = concertAnalytics.enableAnalytics; +concertAnalytics.eventsStorage = []; + +// override enableAnalytics so we can get access to the config passed in from the page +concertAnalytics.enableAnalytics = function (config) { + concertAnalytics.originEnableAnalytics(config); +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: concertAnalytics, + code: 'concert' +}); + +export default concertAnalytics; diff --git a/modules/concertAnalyticsAdapter.md b/modules/concertAnalyticsAdapter.md new file mode 100644 index 00000000000..7c9b6d22703 --- /dev/null +++ b/modules/concertAnalyticsAdapter.md @@ -0,0 +1,11 @@ +# Overview + +``` +Module Name: Concert Analytics Adapter +Module Type: Analytics Adapter +Maintainer: support@concert.io +``` + +# Description + +Analytics adapter for concert. \ No newline at end of file diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js new file mode 100644 index 00000000000..d153ddf9ee2 --- /dev/null +++ b/modules/concertBidAdapter.js @@ -0,0 +1,208 @@ + +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js' + +const BIDDER_CODE = 'concert'; +const CONCERT_ENDPOINT = 'https://bids.concert.io'; +const USER_SYNC_URL = 'https://cdn.concert.io/lib/bids/sync.html'; + +export const spec = { + code: BIDDER_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) { + if (!bid.params.partnerId) { + utils.logWarn('Missing partnerId bid parameter'); + return false; + } + + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @param {bidderRequest} - + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + utils.logMessage(validBidRequests); + utils.logMessage(bidderRequest); + let payload = { + meta: { + prebidVersion: '$prebid.version$', + pageUrl: bidderRequest.refererInfo.referer, + screen: [window.screen.width, window.screen.height].join('x'), + debug: utils.debugTurnedOn(), + uid: getUid(bidderRequest), + optedOut: hasOptedOutOfPersonalization(), + adapterVersion: '1.1.0', + uspConsent: bidderRequest.uspConsent, + gdprConsent: bidderRequest.gdprConsent + } + } + + payload.slots = validBidRequests.map(bidRequest => { + let slot = { + name: bidRequest.adUnitCode, + bidId: bidRequest.bidId, + transactionId: bidRequest.transactionId, + sizes: bidRequest.sizes, + partnerId: bidRequest.params.partnerId, + slotType: bidRequest.params.slotType + } + + return slot; + }); + + utils.logMessage(payload); + + return { + method: 'POST', + url: `${CONCERT_ENDPOINT}/bids/prebid`, + data: JSON.stringify(payload) + } + }, + /** + * 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) { + utils.logMessage(serverResponse); + utils.logMessage(bidRequest); + + const serverBody = serverResponse.body; + + if (!serverBody || typeof serverBody !== 'object') { + return []; + } + + let bidResponses = []; + + bidResponses = serverBody.bids.map(bid => { + return { + requestId: bid.bidId, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + ad: bid.ad, + ttl: bid.ttl, + creativeId: bid.creativeId, + netRevenue: bid.netRevenue, + currency: bid.currency + } + }); + + if (utils.debugTurnedOn() && serverBody.debug) { + utils.logMessage(`CONCERT`, serverBody.debug); + } + + utils.logMessage(bidResponses); + 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. + * @param {gdprConsent} object GDPR consent object. + * @param {uspConsent} string US Privacy String. + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = [] + if (syncOptions.iframeEnabled && !hasOptedOutOfPersonalization()) { + let params = []; + + if (gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean')) { + params.push(`gdpr_applies=${gdprConsent.gdprApplies ? '1' : '0'}`); + } + if (gdprConsent && (typeof gdprConsent.consentString === 'string')) { + params.push(`gdpr_consent=${gdprConsent.consentString}`); + } + if (uspConsent && (typeof uspConsent === 'string')) { + params.push(`usp_consent=${uspConsent}`); + } + + syncs.push({ + type: 'iframe', + url: USER_SYNC_URL + (params.length > 0 ? `?${params.join('&')}` : '') + }); + } + return syncs; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ + onTimeout: function(data) { + utils.logMessage('concert bidder timed out'); + utils.logMessage(data); + }, + + /** + * 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) { + utils.logMessage('concert bidder won bid'); + utils.logMessage(bid); + } + +} + +registerBidder(spec); + +const storage = getStorageManager(); + +/** + * Check or generate a UID for the current user. + */ +function getUid(bidderRequest) { + if (hasOptedOutOfPersonalization() || !consentAllowsPpid(bidderRequest)) { + return false; + } + + const CONCERT_UID_KEY = 'c_uid'; + + let uid = storage.getDataFromLocalStorage(CONCERT_UID_KEY); + + if (!uid) { + uid = utils.generateUUID(); + storage.setDataInLocalStorage(CONCERT_UID_KEY, uid); + } + + return uid; +} + +/** + * Whether the user has opted out of personalization. + */ +function hasOptedOutOfPersonalization() { + const CONCERT_NO_PERSONALIZATION_KEY = 'c_nap'; + + return storage.getDataFromLocalStorage(CONCERT_NO_PERSONALIZATION_KEY) === 'true'; +} + +/** + * Whether the privacy consent strings allow personalization. + * + * @param {BidderRequest} bidderRequest Object which contains any data consent signals + */ +function consentAllowsPpid(bidderRequest) { + /* NOTE: We cannot 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') +} diff --git a/modules/concertBidAdapter.md b/modules/concertBidAdapter.md new file mode 100644 index 00000000000..faf774946d1 --- /dev/null +++ b/modules/concertBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +``` +Module Name: Concert Bid Adapter +Module Type: Bidder Adapter +Maintainer: support@concert.io +``` + +# Description + +Module that connects to Concert demand sources + +# Test Paramters +``` + var adUnits = [ + { + code: 'desktop_leaderboard_variable', + mediaTypes: { + banner: { + sizes: [[1030, 590]] + } + } + bids: [ + { + bidder: "concert", + params: { + partnerId: 'test_partner' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..b0aad2f3156 --- /dev/null +++ b/test/spec/modules/concertAnalyticsAdapter_spec.js @@ -0,0 +1,157 @@ +import concertAnalytics from 'modules/concertAnalyticsAdapter.js'; +import { expect } from 'chai'; +const sinon = require('sinon'); +let adapterManager = require('src/adapterManager').default; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('ConcertAnalyticsAdapter', function() { + let sandbox; + let xhr; + let requests; + let clock; + let timestamp = 1896134400; + let auctionId = '9f894496-10fe-4652-863d-623462bf82b8'; + let timeout = 1000; + + before(function () { + sandbox = sinon.createSandbox(); + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + + xhr.onCreate = function (request) { + requests.push(request); + }; + clock = sandbox.useFakeTimers(1896134400); + }); + + after(function () { + sandbox.restore(); + }); + + describe('track', function() { + beforeEach(function () { + sandbox.stub(events, 'getEvents').returns([]); + + adapterManager.enableAnalytics({ + provider: 'concert' + }); + }); + + afterEach(function () { + events.getEvents.restore(); + concertAnalytics.eventsStorage = []; + concertAnalytics.disableAnalytics(); + }); + + it('should catch all events', function() { + sandbox.spy(concertAnalytics, 'track'); + + fireBidEvents(events); + sandbox.assert.callCount(concertAnalytics.track, 5); + }); + + it('should report data for BID_RESPONSE, BID_WON events', function() { + fireBidEvents(events); + clock.tick(3000 + 1000); + + const eventsToReport = ['bidResponse', 'bidWon']; + for (var i = 0; i < concertAnalytics.eventsStorage.length; i++) { + expect(eventsToReport.indexOf(concertAnalytics.eventsStorage[i].event)).to.be.above(-1); + } + + for (var i = 0; i < eventsToReport.length; i++) { + expect(concertAnalytics.eventsStorage.some(function(event) { + return event.event === eventsToReport[i] + })).to.equal(true); + } + }); + + it('should report data in the shape expected by analytics endpoint', function() { + fireBidEvents(events); + clock.tick(3000 + 1000); + + const requiredFields = ['event', 'concert_rid', 'adId', 'auctionId', 'creativeId', 'position', 'url', 'cpm', 'width', 'height', 'timeToRespond']; + + for (var i = 0; i < requiredFields.length; i++) { + expect(concertAnalytics.eventsStorage[0]).to.have.property(requiredFields[i]); + } + }); + }); + + const adUnits = [{ + code: 'desktop_leaderboard_variable', + sizes: [[1030, 590]], + mediaTypes: { + banner: { + sizes: [[1030, 590]] + } + }, + bids: [ + { + bidder: 'concert', + params: { + partnerId: 'test_partner' + } + } + ] + }]; + + const bidResponse = { + 'bidderCode': 'concert', + 'width': 1030, + 'height': 590, + 'statusMessage': 'Bid available', + 'adId': '642f13fe18ab7dc', + 'requestId': '4062fba2e039919', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 6, + 'ad': '', + 'ttl': 360, + 'creativeId': '138308483085|62bac030-a5d3-11ea-b3be-55590c8153a5', + 'netRevenue': false, + 'currency': 'USD', + 'originalCpm': 6, + 'originalCurrency': 'USD', + 'auctionId': '9f894496-10fe-4652-863d-623462bf82b8', + 'responseTimestamp': 1591213790366, + 'requestTimestamp': 1591213790017, + 'bidder': 'concert', + 'adUnitCode': 'desktop_leaderboard_variable', + 'timeToRespond': 349, + 'status': 'rendered', + 'params': [ + { + 'partnerId': 'cst' + } + ] + } + + const bidWon = { + 'adId': '642f13fe18ab7dc', + 'mediaType': 'banner', + 'requestId': '4062fba2e039919', + 'cpm': 6, + 'creativeId': '138308483085|62bac030-a5d3-11ea-b3be-55590c8153a5', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 360, + 'auctionId': '9f894496-10fe-4652-863d-623462bf82b8', + 'statusMessage': 'Bid available', + 'responseTimestamp': 1591213790366, + 'requestTimestamp': 1591213790017, + 'bidder': 'concert', + 'adUnitCode': 'desktop_leaderboard_variable', + 'sizes': [[1030, 590]], + 'size': [1030, 590] + } + + function fireBidEvents(events) { + events.emit(constants.EVENTS.AUCTION_INIT, {timestamp, auctionId, timeout, adUnits}); + events.emit(constants.EVENTS.BID_REQUESTED, {bidder: 'concert'}); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_WON, bidWon); + } +}); diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js new file mode 100644 index 00000000000..df999f45df9 --- /dev/null +++ b/test/spec/modules/concertBidAdapter_spec.js @@ -0,0 +1,219 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { spec } from 'modules/concertBidAdapter.js'; +import { getStorageManager } from '../../../src/storageManager.js' + +describe('ConcertAdapter', function () { + let bidRequests; + let bidRequest; + let bidResponse; + + beforeEach(function () { + bidRequests = [ + { + bidder: 'concert', + params: { + partnerId: 'foo', + slotType: 'fizz' + }, + adUnitCode: 'desktop_leaderboard_variable', + bidId: 'foo', + transactionId: '', + sizes: [[1030, 590]] + } + ]; + + bidRequest = { + refererInfo: { + referer: 'https://www.google.com' + }, + uspConsent: '1YYY', + gdprConsent: {} + }; + + bidResponse = { + body: { + bids: [ + { + bidId: '16d2e73faea32d9', + cpm: '6', + width: '1030', + height: '590', + ad: '', + ttl: '360', + creativeId: '123349|a7d62700-a4bf-11ea-829f-ad3b0b7a9383', + netRevenue: false, + currency: 'USD' + } + ] + } + } + }); + + describe('spec.isBidRequestValid', function() { + it('should return when it recieved all the required params', function() { + const bid = bidRequests[0]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when partner id is missing', function() { + const bid = { + bidder: 'concert', + params: {} + } + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('spec.buildRequests', function() { + it('should build a payload object with the shape expected by server', function() { + const request = spec.buildRequests(bidRequests, bidRequest); + const payload = JSON.parse(request.data); + expect(payload).to.have.property('meta'); + expect(payload).to.have.property('slots'); + + const metaRequiredFields = ['prebidVersion', 'pageUrl', 'screen', 'debug', 'uid', 'optedOut', 'adapterVersion', 'uspConsent', 'gdprConsent']; + const slotsRequiredFields = ['name', 'bidId', 'transactionId', 'sizes', 'partnerId', 'slotType']; + + metaRequiredFields.forEach(function(field) { + expect(payload.meta).to.have.property(field); + }); + slotsRequiredFields.forEach(function(field) { + expect(payload.slots[0]).to.have.property(field); + }); + }); + + 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); + + expect(payload.meta.uid).to.equal(false); + }); + + 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); + + 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'); + storage.removeDataFromLocalStorage('c_nap'); + const request = spec.buildRequests(bidRequests, bidRequest); + const payload = JSON.parse(request.data); + + expect(payload.meta.uid).to.equal('foo'); + }); + }); + + describe('spec.interpretResponse', function() { + it('should return bids in the shape expected by prebid', function() { + const bids = spec.interpretResponse(bidResponse, bidRequest); + const requiredFields = ['requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency']; + + requiredFields.forEach(function(field) { + expect(bids[0]).to.have.property(field); + }); + }); + + it('should return empty bids if there is no response from server', function() { + const bids = spec.interpretResponse({ body: null }, bidRequest); + expect(bids).to.have.lengthOf(0); + }); + + it('should return empty bids if there are no bids from the server', function() { + const bids = spec.interpretResponse({ body: {bids: []} }, bidRequest); + expect(bids).to.have.lengthOf(0); + }); + }); + + describe('spec.getUserSyncs', function() { + it('should not register syncs when iframe is not enabled', function() { + const opts = { + iframeEnabled: false + } + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync).to.have.lengthOf(0); + }); + + it('should not register syncs when the user has opted out', function() { + const opts = { + iframeEnabled: true + }; + const storage = getStorageManager(); + storage.setDataInLocalStorage('c_nap', 'true'); + + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync).to.have.lengthOf(0); + }); + + it('should set gdprApplies flag to 1 if the user is in area where GDPR applies', function() { + const opts = { + iframeEnabled: true + }; + const storage = getStorageManager(); + storage.removeDataFromLocalStorage('c_nap'); + + bidRequest.gdprConsent = { + gdprApplies: true + }; + + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync[0].url).to.have.string('gdpr_applies=1'); + }); + + it('should set gdprApplies flag to 1 if the user is in area where GDPR applies', function() { + const opts = { + iframeEnabled: true + }; + const storage = getStorageManager(); + storage.removeDataFromLocalStorage('c_nap'); + + bidRequest.gdprConsent = { + gdprApplies: false + }; + + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync[0].url).to.have.string('gdpr_applies=0'); + }); + + it('should set gdpr consent param with the user\'s choices on consent', function() { + const opts = { + iframeEnabled: true + }; + const storage = getStorageManager(); + storage.removeDataFromLocalStorage('c_nap'); + + bidRequest.gdprConsent = { + gdprApplies: false, + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' + }; + + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync[0].url).to.have.string('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + }); + + it('should set ccpa consent param with the user\'s choices on consent', function() { + const opts = { + iframeEnabled: true + }; + const storage = getStorageManager(); + storage.removeDataFromLocalStorage('c_nap'); + + bidRequest.gdprConsent = { + gdprApplies: false, + uspConsent: '1YYY' + }; + + const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); + expect(sync[0].url).to.have.string('usp_consent=1YY'); + }); + }); +}); From 40e5c7da5ee1ead9b29de8072a91759de214452e Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 20 Aug 2020 19:54:21 +0200 Subject: [PATCH 0109/1476] rename simpleHash to cyrb53Hash to make it clearer which hash function is actually used. (#5644) --- modules/userId/index.js | 2 +- src/utils.js | 2 +- test/spec/utils_spec.js | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index bcc0f68b2b0..14f7ad3599b 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -244,7 +244,7 @@ function makeStoredConsentDataHash(consentData) { storedConsentData.gdprApplies = consentData.gdprApplies; storedConsentData.apiVersion = consentData.apiVersion; } - return utils.simpleHash(JSON.stringify(storedConsentData)); + return utils.cyrb53Hash(JSON.stringify(storedConsentData)); } /** diff --git a/src/utils.js b/src/utils.js index 6801a7dc1d1..591c1d1bb2b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1223,7 +1223,7 @@ export function mergeDeep(target, ...sources) { * @param seed (optional) * @returns {string} */ -export function simpleHash(str, seed = 0) { +export function cyrb53Hash(str, seed = 0) { // IE doesn't support imul // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul#Polyfill let imul = function(opA, opB) { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index dbaad919bd3..6494ead78e7 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1178,23 +1178,23 @@ describe('Utils', function () { expect(utils.deepEqual(obj1, obj2)).to.equal(false); }); - describe('simpleHash', function() { + describe('cyrb53Hash', function() { it('should return the same hash for the same string', function() { const stringOne = 'string1'; - expect(utils.simpleHash(stringOne)).to.equal(utils.simpleHash(stringOne)); + expect(utils.cyrb53Hash(stringOne)).to.equal(utils.cyrb53Hash(stringOne)); }); it('should return a different hash for the same string with different seeds', function() { const stringOne = 'string1'; - expect(utils.simpleHash(stringOne, 1)).to.not.equal(utils.simpleHash(stringOne, 2)); + expect(utils.cyrb53Hash(stringOne, 1)).to.not.equal(utils.cyrb53Hash(stringOne, 2)); }); it('should return a different hash for different strings with the same seed', function() { const stringOne = 'string1'; const stringTwo = 'string2'; - expect(utils.simpleHash(stringOne)).to.not.equal(utils.simpleHash(stringTwo)); + expect(utils.cyrb53Hash(stringOne)).to.not.equal(utils.cyrb53Hash(stringTwo)); }); it('should return a string value, not a number', function() { const stringOne = 'string1'; - expect(typeof utils.simpleHash(stringOne)).to.equal('string'); + expect(typeof utils.cyrb53Hash(stringOne)).to.equal('string'); }); }); }); From 67d184a7f7410268973d75acabf351f8a63d642d Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Thu, 20 Aug 2020 14:06:47 -0400 Subject: [PATCH 0110/1476] DMX support for video instream and more user id support (#5618) * adding DMX test @97%, two files added one updated * Update districtm_spec.js * Update districtmDMX.js * adding all districtm needed file * remove legacy file * remove typo || 0 in the test method * force default to return a valid width and height * update unit test code for failing test * changed class for an object * remove package-lock.json * change file name for dmx adapter * renamed files * restaure package-lock.json * update to last package-lock state * update gdpr user consent * fix sizes issue * Documentation updates Adding the readme.md info * update file name and update unit testing import file location * current machine state * lint correction * remove variable assigment duplicate * adding CCPA support for DMX * adding test for ccpa and gdpr * districtm dmx adding deal id field * idsync support ccpa & gdpr * Adding code getProtocols and gegtApi function + video object support * Resolve userId detection on dmx * adding support for video and vast support inline and wrap * Change video setting to be only taken from mediaTypes object from placement setting * Adding documentation for video support * Support for bid floor modules and remove deprecated user id module * fixed caniuse error for ie11 Array.includes and add advertiser domain in meta object * replace includes array method for indexOf * switch anoter includes array function for indexOf Co-authored-by: Steve Alliance Co-authored-by: Luis Co-authored-by: Steve Alliance Co-authored-by: Steve Alliance Co-authored-by: steve-a-districtm --- modules/districtmDMXBidAdapter.js | 160 ++++++++++++++++-- modules/districtmDmxBidAdapter.md | 61 ++++++- .../modules/districtmDmxBidAdapter_spec.js | 154 ++++++++++++++++- 3 files changed, 358 insertions(+), 17 deletions(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index 03dca0b607d..21f3b7b3586 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -1,14 +1,26 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'districtmDMX'; const DMXURI = 'https://dmx.districtm.io/b/v1'; +const VIDEO_MAPPING = { + playback_method: { + 'auto_play_sound_on': 1, + 'auto_play_sound_off': 2, + 'click_to_play': 3, + 'mouse_over': 4, + 'viewport_sound_on': 5, + 'viewport_sound_off': 6 + } +}; export const spec = { code: BIDDER_CODE, - supportedFormat: ['banner'], + supportedFormat: [BANNER, VIDEO], + supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid(bid) { return !!(bid.params.dmxid && bid.params.memberid); }, @@ -27,14 +39,23 @@ export const spec = { nBid.requestId = nBid.impid; nBid.width = nBid.w || width; nBid.height = nBid.h || height; + nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : null; + if (nBid.mediaType) { + nBid.vastXml = cleanVast(nBid.adm); + } if (nBid.dealid) { nBid.dealId = nBid.dealid; } + nBid.uuid = nBid.bidId; nBid.ad = nBid.adm; nBid.netRevenue = true; nBid.creativeId = nBid.crid; nBid.currency = 'USD'; nBid.ttl = 60; + nBid.meta = nBid.meta || {}; + if (nBid.adomain && nBid.adomain.length > 0) { + nBid.meta.advertiserDomains = nBid.adomain; + } return nBid; } else { oBid.cpm = oBid.price; @@ -83,12 +104,19 @@ export const spec = { } let eids = []; - if (bidRequest && bidRequest.userId) { - bindUserId(eids, utils.deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 1); - bindUserId(eids, utils.deepAccess(bidRequest, `userId.digitrustid.data.id`), 'digitru.st', 1); - bindUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 1); - bindUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcid.org', 1); - bindUserId(eids, utils.deepAccess(bidRequest, `userId.tdid`), 'adserver.org', 1); + if (bidRequest[0] && bidRequest[0].userId) { + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.idl_env`), 'liveramp.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.id5id`), 'id5-sync.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.pubcid`), 'pubcid.org', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.tdid`), 'adserver.org', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.criteoId`), 'criteo.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.britepoolid`), 'britepool.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.lipb.lipbid`), 'liveintent.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.intentiqid`), 'intentiq.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.lotamePanoramaId`), 'lotame.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.parrableId`), 'parrable.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.netId`), 'netid.de', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.sharedid`), 'sharedid.org', 1); dmxRequest.user = dmxRequest.user || {}; dmxRequest.user.ext = dmxRequest.user.ext || {}; dmxRequest.user.ext.eids = eids; @@ -122,14 +150,34 @@ export const spec = { obj.id = dmx.bidId; obj.tagid = String(dmx.params.dmxid); obj.secure = 1; - obj.banner = { - topframe: 1, - w: cleanSizes(dmx.sizes, 'w'), - h: cleanSizes(dmx.sizes, 'h'), - format: cleanSizes(dmx.sizes).map(s => { - return {w: s[0], h: s[1]}; - }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number') - }; + obj.bidfloor = getFloor(dmx); + if (dmx.mediaTypes && dmx.mediaTypes.video) { + obj.video = { + topframe: 1, + skip: dmx.mediaTypes.video.skippable || 0, + linearity: dmx.mediaTypes.video.linearity || 1, + minduration: dmx.mediaTypes.video.minduration || 5, + maxduration: dmx.mediaTypes.video.maxduration || 60, + playbackmethod: getPlaybackmethod(dmx.mediaTypes.video.playback_method), + api: getApi(dmx.mediaTypes.video), + mimes: dmx.mediaTypes.video.mimes || ['video/mp4'], + protocols: getProtocols(dmx.mediaTypes.video), + w: dmx.mediaTypes.video.playerSize[0][0], + h: dmx.mediaTypes.video.playerSize[0][1], + format: dmx.mediaTypes.video.playerSize.map(s => { + return {w: s[0], h: s[1]}; + }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number') + }; + } else { + obj.banner = { + topframe: 1, + w: cleanSizes(dmx.sizes, 'w'), + h: cleanSizes(dmx.sizes, 'h'), + format: cleanSizes(dmx.sizes).map(s => { + return {w: s[0], h: s[1]}; + }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number') + }; + } return obj; }); @@ -169,6 +217,27 @@ export const spec = { } } +export function getFloor (bid) { + let floor = null; + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: bid.mediaTypes.video ? 'video' : 'banner', + size: bid.sizes.map(size => { + return { + w: size[0], + h: size[1] + } + }) + }); + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { + floor = parseFloat(floorInfo.floor); + } + } + return floor !== null ? floor : bid.params.floor; +} + export function cleanSizes(sizes, value) { const supportedSize = [ { @@ -310,4 +379,65 @@ export function bindUserId(eids, value, source, atype) { }) } } + +export function getApi({protocols}) { + let defaultValue = [2]; + let listProtocols = [ + {key: 'VPAID_1_0', value: 1}, + {key: 'VPAID_2_0', value: 2}, + {key: 'MRAID_1', value: 3}, + {key: 'ORMMA', value: 4}, + {key: 'MRAID_2', value: 5}, + {key: 'MRAID_3', value: 6}, + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1; + }).map(p => p.value) + } else { + return defaultValue; + } +} +export function getPlaybackmethod(playback) { + if (Array.isArray(playback) && playback.length > 0) { + return playback.map(label => { + return VIDEO_MAPPING.playback_method[label] + }) + } + return [2] +} + +export function getProtocols({protocols}) { + let defaultValue = [2, 3, 5, 6, 7, 8]; + let listProtocols = [ + {key: 'VAST_1_0', value: 1}, + {key: 'VAST_2_0', value: 2}, + {key: 'VAST_3_0', value: 3}, + {key: 'VAST_1_0_WRAPPER', value: 4}, + {key: 'VAST_2_0_WRAPPER', value: 5}, + {key: 'VAST_3_0_WRAPPER', value: 6}, + {key: 'VAST_4_0', value: 7}, + {key: 'VAST_4_0_WRAPPER', value: 8} + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1 + }).map(p => p.value); + } else { + return defaultValue; + } +} + +export function cleanVast(str) { + const toberemove = /]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/ + const [img, url] = str.match(toberemove) + str = str.replace(toberemove, '') + if (img) { + if (url) { + const insrt = `` + str = str.replace('', `${insrt}`) + } + } + return str; +} registerBidder(spec); diff --git a/modules/districtmDmxBidAdapter.md b/modules/districtmDmxBidAdapter.md index 792cf2e7305..5d5dd2affe6 100644 --- a/modules/districtmDmxBidAdapter.md +++ b/modules/districtmDmxBidAdapter.md @@ -20,13 +20,14 @@ The `districtmDmxAdapter` module allows publishers to include DMX Exchange deman ## Media Types * Banner - +* Video ## Bidder Parameters | Key | Scope | Type | Description | --- | --- | --- | --- | `dmxid` | Mandatory | Integer | Unique identifier of the placement, dmxid can be obtained in the district m Boost platform. | `memberid` | Mandatory | Integer | Unique identifier for your account, memberid can be obtained in the district m Boost platform. +| `floor` | Optional | float | Most placement can have floor set in our platform, but this can now be set on the request too. # Ad Unit Configuration Example @@ -48,6 +49,35 @@ The `districtmDmxAdapter` module allows publishers to include DMX Exchange deman }]; ``` +# Ad Unit Configuration Example for video request + +```javascript + var videoAdUnit = { + code: 'video1', + sizes: [640,480], + mediaTypes: { video: {context: 'instream', //or 'outstream' + playerSize: [[640, 480]], + skipppable: true, + minduration: 5, + maxduration: 45, + playback_method: ['auto_play_sound_off', 'viewport_sound_off'], + mimes: ["application/javascript", + "video/mp4"], + + } }, + bids: [ + { + bidder: 'districtmDMX', + params: { + dmxid: '100001', + memberid: '100003', + } + } + + ] + }; +``` + # Ad Unit Configuration when COPPA is needed @@ -117,6 +147,35 @@ Our demand and adapter supports multiple sizes per placement, as such a single d }]; ``` +Our bidder only supports instream context at the moment and we strongly like to put the media types and setting in the ad unit settings. +If no value is set the default value will be applied. + +```javascript + var videoAdUnit = { + code: 'video1', + sizes: [640,480], + mediaTypes: { video: {context: 'instream', //or 'outstream' + playerSize: [[640, 480]], + skipppable: true, + minduration: 5, + maxduration: 45, + playback_method: ['auto_play_sound_off', 'viewport_sound_off'], + mimes: ["application/javascript", + "video/mp4"], + + } }, + bids: [ + { + bidder: 'districtmDMX', + params: { + dmxid: '250258', + memberid: '100600', + } + } + ] + }; +``` + ###### 4. Implementation Checking Once the bidder is live in your Prebid configuration you may confirm it is making requests to our end point by looking for requests to `https://dmx.districtm.io/b/v1`. diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js index d8f0beb9a36..9b65ab855d8 100644 --- a/test/spec/modules/districtmDmxBidAdapter_spec.js +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -1,6 +1,60 @@ import {expect} from 'chai'; import * as _ from 'lodash'; -import {spec, matchRequest, checkDeepArray, defaultSize, upto5, cleanSizes, shuffle} from '../../../modules/districtmDMXBidAdapter.js'; +import {spec, matchRequest, checkDeepArray, defaultSize, upto5, cleanSizes, shuffle, getApi, bindUserId, getPlaybackmethod, getProtocols, cleanVast} from '../../../modules/districtmDMXBidAdapter.js'; + +const sample_vast = ` + + + + + + + + + 00:00:15 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` const supportedSize = [ { @@ -42,6 +96,28 @@ const bidRequest = [{ 'dmxid': 100001, 'memberid': 100003, }, + 'userId': { + idl_env: {}, + digitrustid: { + data: { + id: {} + } + }, + id5id: {}, + pubcid: {}, + tdid: {}, + criteoId: {}, + britepoolid: {}, + intentiqid: {}, + lotamePanoramaId: {}, + parrableId: {}, + netId: {}, + sharedid: {}, + lipb: { + lipbid: {} + }, + + }, 'adUnitCode': 'div-gpt-ad-12345678-1', 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', 'sizes': [ @@ -53,6 +129,31 @@ const bidRequest = [{ 'auctionId': '3d62f2d3-56a2-4991-888e-f7754619ddcf' }]; +const bidRequestVideo = [{ + 'bidder': 'districtmDMX', + 'params': { + 'dmxid': 100001, + 'memberid': 100003, + 'video': { + id: 123, + skipppable: true, + playback_method: ['auto_play_sound_off', 'viewport_sound_off'], + mimes: ['application/javascript', + 'video/mp4'], + } + }, + 'mediaTypes': { video: {context: 'instream', // or 'outstream' + playerSize: [[640, 480]]} }, + 'adUnitCode': 'div-gpt-ad-12345678-1', + 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '29a28a1bbc8a8d', + 'bidderRequestId': '124b579a136515', + 'auctionId': '3d62f2d3-56a2-4991-888e-f7754619ddcf' +}]; const bidRequestNoCoppa = [{ 'bidder': 'districtmDMX', 'params': { @@ -505,6 +606,51 @@ describe('DistrictM Adaptor', function () { expect(upto5([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], bidRequest, bidderRequest, 'https://google').length).to.be.equal(3) }) }) + + describe('test vast tag', function() { + it('img tag should not be present', function() { + expect(cleanVast(sample_vast).indexOf('img') !== -1).to.be.equal(false) + }) + }) + describe('Test getApi function', function() { + const data = { + protocols: ['VPAID_1_0'] + } + it('Will return 1 for vpaid version 1', function() { + expect(getApi(data)[0]).to.be.equal(1) + }) + it('Will return 2 for vpaid default', function() { + expect(getApi({})[0]).to.be.equal(2) + }) + }) + + describe('Test cleanSizes function', function() { + it('sequence will be respected', function() { + expect(cleanSizes(bidderRequest.bids[0].sizes).toString()).to.be.equal('300,250,300,600') + }) + it('sequence will be respected', function() { + expect(cleanSizes([[728, 90], [970, 90], [300, 600], [320, 50]]).toString()).to.be.equal('728,90,320,50,300,600,970,90') + }) + }) + + describe('Test getPlaybackmethod function', function() { + it('getPlaybackmethod will return 2', function() { + expect(getPlaybackmethod([])[0]).to.be.equal(2) + }) + it('getPlaybackmethod will return 6', function() { + expect(getPlaybackmethod(['viewport_sound_off'])[0]).to.be.equal(6) + }) + }) + + describe('Test getProtocols function', function() { + it('getProtocols will return 3', function() { + expect(getProtocols({protocols: ['VAST_3_0']})[0]).to.be.equal(3) + }) + it('getProtocols will return 6', function() { + expect(_.isEqual(getProtocols({}), [2, 3, 5, 6, 7, 8])).to.be.equal(true) + }) + }) + describe('All needed functions are available', function () { it(`isBidRequestValid is present and type function`, function () { expect(districtm.isBidRequestValid).to.exist.and.to.be.a('function') @@ -589,6 +735,12 @@ describe('DistrictM Adaptor', function () { }); }); + describe('bidRequest Video testing', function() { + const request = districtm.buildRequests(bidRequestVideo, bidRequestVideo); + const data = JSON.parse(request.data) + expect(data instanceof Object).to.be.equal(true) + }) + describe(`interpretResponse test usage`, function () { const responseResults = districtm.interpretResponse(responses, {bidderRequest}); const emptyResponseResults = districtm.interpretResponse(emptyResponse, {bidderRequest}); From 494015f9f3a09a6500de3a83b558fba8afb55a04 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 20 Aug 2020 14:12:08 -0400 Subject: [PATCH 0111/1476] JW Player Real Time Data Provider (#5537) * [AD-469] Add player vendor. * ssets up targeting module * implements getTargeting * implements getPlayer * blocks bids until all targeting requests complete * makes getTarget more resilient * enables mdule hook * replaces triple dot notation * Revert "replaces triple dot notation" This reverts commit 7a76ea62e1eb210c61abdc8e74da8ee68d47590c. * Revert "Revert "replaces triple dot notation"" This reverts commit 130aa2adf5103013814537e8666e4ec49cd2d127. * checks current item only if mediaid is missing * adds unit tests * completes test cases * stores segments for current item * renames jwp targeting * refactors fetch tests * refactors get targeting tests * refactors blocking tests * renames module * cleans changes made to app nexus * removes setup and player utilities * renames onFetchCompletion * renames onFetchCOmpletion in unti tests * throws instead of early return * reduces timeout and introduces override * targeting timeout supersedes * renames feed fetch timeout * adds inline doc * uses find util * adds jwplayer rtd provider * implements targeting retrieval * ensures provider is found * implements init * jwTargeting is object * commits test file * adds file extension * adds tests * fixes test for proper structure * uses default clock mock * ends reqs before rtd module timeout * removes obsolete export * request counts updates in aggregate * cleans server mock after each test * deletes jwplayer targeting * includes content id * adds test for missing segment * getSegments is nullable * replaces condition with guard * updates doc * adds md file * adds example page Co-authored-by: vseventer Co-authored-by: karimJWP --- .../gpt/jwplayerRtdProvider_example.html | 98 +++++ modules/.submodules.json | 3 +- modules/jwplayerRtdProvider.js | 209 +++++++++ modules/jwplayerRtdProvider.md | 96 +++++ test/spec/modules/jwplayerRtdProvider_spec.js | 399 ++++++++++++++++++ 5 files changed, 804 insertions(+), 1 deletion(-) create mode 100644 integrationExamples/gpt/jwplayerRtdProvider_example.html create mode 100644 modules/jwplayerRtdProvider.js create mode 100644 modules/jwplayerRtdProvider.md create mode 100644 test/spec/modules/jwplayerRtdProvider_spec.js diff --git a/integrationExamples/gpt/jwplayerRtdProvider_example.html b/integrationExamples/gpt/jwplayerRtdProvider_example.html new file mode 100644 index 00000000000..3791ab42137 --- /dev/null +++ b/integrationExamples/gpt/jwplayerRtdProvider_example.html @@ -0,0 +1,98 @@ + + + + + + + JW Player RTD Provider Example + + + + + + + + +
Div-1
+
+ +
+ + diff --git a/modules/.submodules.json b/modules/.submodules.json index dd40557c35b..af7806616e1 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -18,6 +18,7 @@ "dfpAdServerVideo" ], "rtdModule": [ - "browsiRtdProvider" + "browsiRtdProvider", + "jwplayerRtdProvider" ] } diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js new file mode 100644 index 00000000000..b7c8879ed8e --- /dev/null +++ b/modules/jwplayerRtdProvider.js @@ -0,0 +1,209 @@ +/** + * This module adds the jwplayer provider to the Real Time Data module (rtdModule) + * The {@link module:modules/realTimeData} module is required + * The module will allow Ad Bidders to obtain JW Player's Video Ad Targeting information + * The module will fetch segments for the media ids present in the prebid config when the module loads. If any bid + * requests are made while the segments are being fetched, they will be blocked until all requests complete, or the + * timeout expires. + * @module modules/jwplayerRtdProvider + * @requires module:modules/realTimeData + */ + +import { submodule } from '../src/hook.js'; +import { config } from '../src/config.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { logError } from '../src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; + +const SUBMODULE_NAME = 'jwplayer'; +let requestCount = 0; +let requestTimeout = 150; +const segCache = {}; +let resumeBidRequest; + +/** @type {RtdSubmodule} */ +export const jwplayerSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: SUBMODULE_NAME, + /** + * get data and send back to realTimeData module + * @function + * @param {adUnit[]} adUnits + * @param {function} onDone + */ + getData: getSegments, + init +}; + +config.getConfig('realTimeData', ({realTimeData}) => { + const providers = realTimeData.dataProviders; + const jwplayerProvider = providers && find(providers, pr => pr.name && pr.name.toLowerCase() === SUBMODULE_NAME); + const params = jwplayerProvider && jwplayerProvider.params; + if (!params) { + return; + } + const rtdModuleTimeout = params.auctionDelay || params.timeout; + requestTimeout = rtdModuleTimeout === undefined ? requestTimeout : Math.max(rtdModuleTimeout - 1, 0); + fetchTargetingInformation(params); +}); + +submodule('realTimeData', jwplayerSubmodule); + +function init(config, gdpr, usp) { + return true; +} + +export function fetchTargetingInformation(jwTargeting) { + const mediaIDs = jwTargeting.mediaIDs; + if (!mediaIDs) { + return; + } + mediaIDs.forEach(mediaID => { + fetchTargetingForMediaId(mediaID); + }); +} + +export function fetchTargetingForMediaId(mediaId) { + const ajax = ajaxBuilder(requestTimeout); + requestCount++; + ajax(`https://cdn.jwplayer.com/v2/media/${mediaId}`, { + success: function (response) { + try { + const data = JSON.parse(response); + if (!data) { + throw ('Empty response'); + } + + const playlist = data.playlist; + if (!playlist || !playlist.length) { + throw ('Empty playlist'); + } + + const jwpseg = playlist[0].jwpseg; + if (jwpseg) { + segCache[mediaId] = jwpseg; + } + } catch (err) { + logError(err); + } + onRequestCompleted(); + }, + error: function () { + logError('failed to retrieve targeting information'); + onRequestCompleted(); + } + }); +} + +function onRequestCompleted() { + requestCount--; + if (requestCount > 0) { + return; + } + + if (resumeBidRequest) { + resumeBidRequest(); + resumeBidRequest = null; + } +} + +function getSegments(adUnits, onDone) { + executeAfterPrefetch(() => { + const realTimeData = adUnits.reduce((data, adUnit) => { + const code = adUnit.code; + const vat = code && getTargetingForBid(adUnit); + if (!vat) { + return data; + } + + const { segments, mediaID } = vat; + const jwTargeting = {}; + if (segments && segments.length) { + jwTargeting.segments = segments; + } + + if (mediaID) { + const id = 'jw_' + mediaID; + jwTargeting.content = { + id + } + } + + data[code] = { + jwTargeting + }; + return data; + }, {}); + onDone(realTimeData); + }); +} + +function executeAfterPrefetch(callback) { + if (requestCount > 0) { + resumeBidRequest = callback; + } else { + callback(); + } +} + +/** + * Retrieves the targeting information pertaining to a bid request. + * @param bidRequest {object} - the bid which is passed to a prebid adapter for use in `buildRequests`. It must contain + * a jwTargeting property. + * @returns targetingInformation {object} nullable - contains the media ID as well as the jwpseg targeting segments + * found for the given bidRequest information + */ +export function getTargetingForBid(bidRequest) { + const jwTargeting = bidRequest.jwTargeting; + if (!jwTargeting) { + return null; + } + const playerID = jwTargeting.playerID; + let mediaID = jwTargeting.mediaID; + let segments = segCache[mediaID]; + if (segments) { + return { + segments, + mediaID + }; + } + + const player = getPlayer(playerID); + if (!player) { + return null; + } + + const item = mediaID ? find(player.getPlaylist(), item => item.mediaid === mediaID) : player.getPlaylistItem(); + if (!item) { + return null; + } + + mediaID = mediaID || item.mediaid; + segments = item.jwpseg; + if (segments && mediaID) { + segCache[mediaID] = segments; + } + + return { + segments, + mediaID + }; +} + +function getPlayer(playerID) { + const jwplayer = window.jwplayer; + if (!jwplayer) { + logError('jwplayer.js was not found on page'); + return; + } + + const player = jwplayer(playerID); + if (!player || !player.getPlaylist) { + logError('player ID did not match any players'); + return; + } + return player; +} diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md new file mode 100644 index 00000000000..06a7f69f497 --- /dev/null +++ b/modules/jwplayerRtdProvider.md @@ -0,0 +1,96 @@ +The purpose of this Real Time Data Provider is to allow publishers to target against their JW Player media without +having to integrate with the VPB product. This prebid module makes JW Player's video ad targeting information accessible +to Bid Adapters. + +**Usage for Publishers:** + +Compile the JW Player RTD Provider into your Prebid build: + +`gulp build --modules=jwplayerRtdProvider` + +Publishers must register JW Player as a real time data provider by setting up a Prebid Config conformant to the +following structure: + +```javascript +const jwplayerDataProvider = { + name: "jwplayer" +}; + +pbjs.setConfig({ + ..., + realTimeData: { + dataProviders: [ + jwplayerDataProvider + ] + } +}); +``` + +In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var: + +```javascript +const jwplayerDataProvider = { + name: "jwplayer", + params: { + mediaIDs: ['abc', 'def', 'ghi', 'jkl'] + } +}; +``` +Lastly, include the content's media ID and/or the player's ID in the matching AdUnit: + +```javascript +const adUnit = { + code: '/19968336/prebid_native_example_1', + ... + jwTargeting: { + playerID: 'abcd', + mediaID: '1234' + } +}; + +pbjs.que.push(function() { + pbjs.addAdUnits([adUnit]); + pbjs.requestBids({ + ... + }); +}); +``` + +**Usage for Bid Adapters:** + +Implement the `buildRequests` function. When it is called, the `bidRequests` param will be an array of bids. +Each bid for which targeting information was found will conform to the following object structure: + +```javascript +{ + adUnitCode: 'xyz', + bidId: 'abc', + ... + realTimeData: { + ..., + jwTargeting: { + segments: ['123', '456'], + content: { + id: 'jw_abc123' + } + } + } +} +``` + +where: +- `segments` is an array of jwpseg targeting segments, of type string. +- `content` is an object containing metadata for the media. It may contain the following information: + - `id` is a unique identifier for the specific media asset. + +**Example:** + +To view an example: + +- in your cli run: + +`gulp serve --modules=jwplayerRtdProvider` + +- in your browser, navigate to: + +`http://localhost:9999/integrationExamples/gpt/jwplayerRtdProvider_example.html` diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js new file mode 100644 index 00000000000..b5bacdc3694 --- /dev/null +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -0,0 +1,399 @@ +import { fetchTargetingForMediaId, getTargetingForBid, + fetchTargetingInformation, jwplayerSubmodule } from 'modules/jwplayerRtdProvider.js'; +import { server } from 'test/mocks/xhr.js'; + +describe('jwplayerRtdProvider', function() { + const testIdForSuccess = 'test_id_for_success'; + const testIdForFailure = 'test_id_for_failure'; + const validSegments = ['test_seg_1', 'test_seg_2']; + const responseHeader = {'Content-Type': 'application/json'}; + + describe('Fetch targeting for mediaID tests', function () { + let request; + + describe('Fetch succeeds', function () { + beforeEach(function () { + fetchTargetingForMediaId(testIdForSuccess); + request = server.requests[0]; + }); + + afterEach(function () { + server.respond(); + }); + + it('should reach out to media endpoint', function () { + expect(request.url).to.be.eq(`https://cdn.jwplayer.com/v2/media/${testIdForSuccess}`); + }); + + it('should write to cache when successful', function () { + request.respond( + 200, + responseHeader, + JSON.stringify({ + playlist: [ + { + file: 'test.mp4', + jwpseg: validSegments + } + ] + }) + ); + + const targetingInfo = getTargetingForBid({ + jwTargeting: { + mediaID: testIdForSuccess + } + }); + + const validTargeting = { + segments: validSegments, + mediaID: testIdForSuccess + }; + + expect(targetingInfo).to.deep.equal(validTargeting); + }); + }); + + describe('Fetch fails', function () { + beforeEach(function () { + fetchTargetingForMediaId(testIdForFailure); + request = server.requests[0] + }); + + it('should not write to cache when response is malformed', function() { + request.respond('{]'); + const targetingInfo = getTargetingForBid({ + jwTargeting: { + mediaID: testIdForFailure + } + }); + expect(targetingInfo).to.be.null; + }); + + it('should not write to cache when playlist is absent', function() { + request.respond({}); + const targetingInfo = getTargetingForBid({ + jwTargeting: { + mediaID: testIdForFailure + } + }); + expect(targetingInfo).to.be.null; + }); + + it('should not write to cache when segments are absent', function() { + request.respond( + 200, + responseHeader, + JSON.stringify({ + playlist: [ + { + file: 'test.mp4' + } + ] + }) + ); + const targetingInfo = getTargetingForBid({ + jwTargeting: { + mediaID: testIdForFailure + } + }); + expect(targetingInfo).to.be.null; + }); + + it('should not write to cache when request errors', function() { + request.error(); + const targetingInfo = getTargetingForBid({ + jwTargeting: { + mediaID: testIdForFailure + } + }); + expect(targetingInfo).to.be.null; + }); + }); + }); + + describe('Get targeting for bid', function() { + const mediaIdWithSegment = 'media_ID_1'; + const mediaIdNoSegment = 'media_ID_2'; + const mediaIdForCurrentItem = 'media_ID_current'; + const mediaIdNotCached = 'media_test_ID'; + + const validPlayerID = 'player_test_ID_valid'; + const invalidPlayerID = 'player_test_ID_invalid'; + + it('returns null when targeting block is missing', function () { + const targeting = getTargetingForBid({}); + expect(targeting).to.be.null; + }); + + it('returns null when jwplayer.js is absent from page', function () { + const targeting = getTargetingForBid({ + jwTargeting: { + playerID: invalidPlayerID, + mediaID: mediaIdNotCached + } + }); + expect(targeting).to.be.null; + }); + + describe('When jwplayer.js is on page', function () { + const playlistItemWithSegmentMock = { + mediaid: mediaIdWithSegment, + jwpseg: validSegments + }; + + const targetingForMediaWithSegment = { + segments: validSegments, + mediaID: mediaIdWithSegment + }; + + const playlistItemNoSegmentMock = { + mediaid: mediaIdNoSegment + }; + + const currentItemSegments = ['test_seg_3', 'test_seg_4']; + const currentPlaylistItemMock = { + mediaid: mediaIdForCurrentItem, + jwpseg: currentItemSegments + }; + const targetingForCurrentItem = { + segments: currentItemSegments, + mediaID: mediaIdForCurrentItem + }; + + const playerInstanceMock = { + getPlaylist: function () { + return [playlistItemWithSegmentMock, playlistItemNoSegmentMock]; + }, + + getPlaylistItem: function () { + return currentPlaylistItemMock; + } + }; + + const jwplayerMock = function(playerID) { + if (playerID === validPlayerID) { + return playerInstanceMock; + } else { + return {}; + } + }; + + beforeEach(function () { + window.jwplayer = jwplayerMock; + }); + + it('returns null when player ID does not match player on page', function () { + const targeting = getTargetingForBid({ + jwTargeting: { + playerID: invalidPlayerID, + mediaID: mediaIdNotCached + } + }); + expect(targeting).to.be.null; + }); + + it('returns segments when media ID matches a playlist item with segments', function () { + const targeting = getTargetingForBid({ + jwTargeting: { + playerID: validPlayerID, + mediaID: mediaIdWithSegment + } + }); + expect(targeting).to.deep.equal(targetingForMediaWithSegment); + }); + + it('caches segments media ID matches a playist item with segments', function () { + getTargetingForBid({ + jwTargeting: { + playerID: validPlayerID, + mediaID: mediaIdWithSegment + } + }); + + window.jwplayer = null; + const targeting2 = getTargetingForBid({ + jwTargeting: { + playerID: invalidPlayerID, + mediaID: mediaIdWithSegment + } + }); + expect(targeting2).to.deep.equal(targetingForMediaWithSegment); + }); + + it('returns segments of current item when media ID is missing', function () { + const targeting = getTargetingForBid({ + jwTargeting: { + playerID: validPlayerID + } + }); + expect(targeting).to.deep.equal(targetingForCurrentItem); + }); + + it('caches segments from the current item', function () { + getTargetingForBid({ + jwTargeting: { + playerID: validPlayerID + } + }); + + window.jwplayer = null; + const targeting2 = getTargetingForBid({ + jwTargeting: { + playerID: invalidPlayerID, + mediaID: mediaIdForCurrentItem + } + }); + expect(targeting2).to.deep.equal(targetingForCurrentItem); + }); + + it('returns undefined segments when segments are absent', function () { + const targeting = getTargetingForBid({ + jwTargeting: { + playerID: validPlayerID, + mediaID: mediaIdNoSegment + } + }); + expect(targeting).to.deep.equal({ + mediaID: mediaIdNoSegment, + segments: undefined + }); + }); + }); + }); + + describe('jwplayerSubmodule', function () { + it('successfully instantiates', function () { + expect(jwplayerSubmodule.init()).to.equal(true); + }); + + describe('getData', function () { + const validMediaIDs = ['media_ID_1', 'media_ID_2', 'media_ID_3']; + let bidRequestSpy; + let fakeServer; + let clock; + + beforeEach(function () { + bidRequestSpy = sinon.spy(); + + fakeServer = sinon.createFakeServer(); + fakeServer.respondImmediately = false; + fakeServer.autoRespond = false; + + clock = sinon.useFakeTimers(); + }); + + afterEach(function () { + clock.restore(); + fakeServer.respond(); + }); + + it('executes callback immediately when no requests are pending', function () { + fetchTargetingInformation({ + mediaIDs: [] + }); + jwplayerSubmodule.getData([], bidRequestSpy); + expect(bidRequestSpy.calledOnce).to.be.true; + }); + + it('executes callback after requests complete', function() { + fetchTargetingInformation({ + mediaIDs: validMediaIDs + }); + jwplayerSubmodule.getData([], bidRequestSpy); + expect(bidRequestSpy.notCalled).to.be.true; + + const req1 = fakeServer.requests[0]; + const req2 = fakeServer.requests[1]; + const req3 = fakeServer.requests[2]; + + req1.respond(); + expect(bidRequestSpy.notCalled).to.be.true; + + req2.respond(); + expect(bidRequestSpy.notCalled).to.be.true; + + req3.respond(); + expect(bidRequestSpy.calledOnce).to.be.true; + }); + + it('executes callback after timeout', function () { + fetchTargetingInformation({ + mediaIDs: validMediaIDs + }); + jwplayerSubmodule.getData([], bidRequestSpy); + expect(bidRequestSpy.notCalled).to.be.true; + clock.tick(150); + expect(bidRequestSpy.calledOnce).to.be.true; + }); + + it('executes callback only once if requests succeed after timeout', function () { + fetchTargetingInformation({ + mediaIDs: validMediaIDs + }); + jwplayerSubmodule.getData([], bidRequestSpy); + expect(bidRequestSpy.notCalled).to.be.true; + clock.tick(150); + expect(bidRequestSpy.calledOnce).to.be.true; + + fakeServer.respond(); + expect(bidRequestSpy.calledOnce).to.be.true; + }); + + it('returns data in proper structure', function () { + const adUnitCode = 'test_ad_unit'; + const adUnitWithMediaId = { + code: adUnitCode, + jwTargeting: { + mediaID: testIdForSuccess + } + }; + const adUnitEmpty = { + code: 'test_ad_unit_empty' + }; + const expectedData = {}; + const expectedContentId = 'jw_' + testIdForSuccess; + expectedData[adUnitCode] = { + jwTargeting: { + segments: validSegments, + content: { + id: expectedContentId + } + } + }; + jwplayerSubmodule.getData([adUnitWithMediaId, adUnitEmpty], bidRequestSpy); + expect(bidRequestSpy.calledOnceWithExactly(expectedData)).to.be.true; + }); + + it('returns an empty object when media id is invalid', function () { + const adUnitCode = 'test_ad_unit'; + const adUnitWithMediaId = { + code: adUnitCode, + jwTargeting: { + mediaID: testIdForFailure + } + }; + const adUnitEmpty = { + code: 'test_ad_unit_empty' + }; + + jwplayerSubmodule.getData([adUnitWithMediaId, adUnitEmpty], bidRequestSpy); + expect(bidRequestSpy.calledOnceWithExactly({})).to.be.true; + }); + + it('returns an empty object when jwTargeting block is absent', function () { + const adUnitCode = 'test_ad_unit'; + const adUnitWithMediaId = { + code: adUnitCode, + mediaID: testIdForSuccess + }; + const adUnitEmpty = { + code: 'test_ad_unit_empty' + }; + + jwplayerSubmodule.getData([adUnitWithMediaId, adUnitEmpty], bidRequestSpy); + expect(bidRequestSpy.calledOnceWithExactly({})).to.be.true; + }); + }); + }); +}); From 1d1ffa3ded51654e6321c1ea8cef703225301047 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 20 Aug 2020 11:49:09 -0700 Subject: [PATCH 0112/1476] Prebid 4.4.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3cb04dc6f8a..04016700633 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.4.0-pre", + "version": "4.4.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 8513f130330aa9d878a473af714d44d25ca22b09 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 20 Aug 2020 12:07:56 -0700 Subject: [PATCH 0113/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04016700633..af36566b95b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.4.0", + "version": "4.5.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 32066aa711d194d0ae6d95c6774f8b6c0eb33d26 Mon Sep 17 00:00:00 2001 From: bretg Date: Sat, 22 Aug 2020 11:49:03 -0400 Subject: [PATCH 0114/1476] module rule documentation updates (#5619) * module rule updates * Adding FPD.user --- CONTRIBUTING.md | 2 ++ PR_REVIEW.md | 23 +++++++++++------------ README.md | 6 ++---- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 016f4055216..962e057fbc5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,8 @@ master branch. Pull requests must have 80% code coverage before beign considered for merge. Additional details about the process can be found [here](./PR_REVIEW.md). +There are more details available if you'd like to contribute a [bid adapter](https://docs.prebid.org/dev-docs/bidder-adaptor.html) or [analytics adapter](https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html). + ## Issues [prebid.org](http://prebid.org/) contains documentation that may help answer questions you have about using Prebid.js. If you can't find the answer there, try searching for a similar issue on the [issues page](https://github.com/prebid/Prebid.js/issues). diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 6402fcbbbaa..9a57539a0cd 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -14,7 +14,7 @@ For modules and core platform updates, the initial reviewer should request an ad - Review for obvious errors or bad coding practice / use best judgement here. - If the change is a new feature / change to core prebid.js - review the change with a Tech Lead on the project and make sure they agree with the nature of change. - If the change results in needing updates to docs (such as public API change, module interface etc), add a label for "needs docs" and inform the submitter they must submit a docs PR to update the appropriate area of Prebid.org **before the PR can merge**. Help them with finding where the docs are located on prebid.org if needed. - - Below are some examples of bidder specific updates that should require docs update (in their dev-docs/bidders/bidder.md file): + - Below are some examples of bidder specific updates that should require docs update (in their dev-docs/bidders/BIDDER.md file): - If they support the GDPR consentManagement module and TCF1, add `gdpr_supported: true` - If they support the GDPR consentManagement module and TCF2, add `tcf2_supported: true` - If they support the US Privacy consentManagementUsp module, add `usp_supported: true` @@ -23,7 +23,7 @@ For modules and core platform updates, the initial reviewer should request an ad - If they support COPPA, add `coppa_supported: true` - If they support SChain, add `schain_supported: true` - If their bidder doesn't work well with safeframed creatives, add `safeframes_ok: false`. This will alert publishers to not use safeframed creatives when creating the ad server entries for their bidder. - - If they're a member of Prebid.org, add `prebid_member: true` + - If they're setting a deal ID in some scenarios, add `bidder_supports_deals: true` - If all above is good, add a `LGTM` comment and request 1 additional core member to review. - Once there is 2 `LGTM` on the PR, merge to master - Ask the submitter to add a PR for documentation if applicable. @@ -34,18 +34,17 @@ For modules and core platform updates, the initial reviewer should request an ad - Follow steps above for general review process. In addition, please verify the following: - Verify that bidder has submitted valid bid params and that bids are being received. - Verify that bidder is not manipulating the prebid.js auction in any way or doing things that go against the principles of the project. If unsure check with the Tech Lead. -- Verify that the bidder is being as efficient as possible, ideally not loading an external library, however if they do load a library it should be cached. - Verify that code re-use is being done properly and that changes introduced by a bidder don't impact other bidders. - If the adapter being submitted is an alias type, check with the bidder contact that is being aliased to make sure it's allowed. -- If the adapter is triggering any user syncs make sure they are using the user sync module in the Prebid.js core. -- Requests to the bidder should support HTTPS -- Responses from the bidder should be compressed (such as gzip, compress, deflate) -- Bid responses may not use JSONP: All requests must be AJAX with JSON responses -- Video openrtb params should be read from the ad unit when available; however bidder config can override the ad unit. -- All user-sync (aka pixel) activity must be registered via the provided functions -- Adapters may not use the $$PREBID_GLOBAL$$ variable -- All adapters must support the creation of multiple concurrent instances. This means, for example, that adapters cannot rely on mutable global variables. -- Adapters may not globally override or default the standard ad server targeting values: hb_adid, hb_bidder, hb_pb, hb_deal, or hb_size, hb_source, hb_format. +- All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. +- All bidder parameter conventions must be followed: + - Video params must be read from AdUnit.mediaTypes.video when available; however bidder config can override the ad unit. + - First party data must be read from [`fpd.context` and `fpd.user`](https://docs.prebid.org/dev-docs/publisher-api-reference.html#setConfig-fpd). + - Adapters that accept a floor parameter must also support the [floors module](https://docs.prebid.org/dev-docs/modules/floors.html) -- look for a call to the `getFloors()` function. + - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidRequest.schain. + - The bidRequest page referrer must checked in addition to any bidder-specific parameter. + - If they're getting the COPPA flag, it must come from config.getConfig('coppa'); + - After a new adapter is approved, let the submitter know they may open a PR in the [headerbid-expert repository](https://github.com/prebid/headerbid-expert) to have their adapter recognized by the [Headerbid Expert extension](https://chrome.google.com/webstore/detail/headerbid-expert/cgfkddgbnfplidghapbbnngaogeldmop). The PR should be to the [bidder patterns file](https://github.com/prebid/headerbid-expert/blob/master/bidderPatterns.js), adding an entry with their adapter's name and the url the adapter uses to send and receive bid responses. ## Ticket Coordinator diff --git a/README.md b/README.md index b3f33b27aa5..44882570d89 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,7 @@ As you make code changes, the bundles will be rebuilt and the page reloaded auto ## Contribute -Many SSPs, bidders, and publishers have contributed to this project. [60+ Bidders](https://github.com/prebid/Prebid.js/tree/master/src/adapters) are supported by Prebid.js. +Many SSPs, bidders, and publishers have contributed to this project. [Hundreds of bidders](https://github.com/prebid/Prebid.js/tree/master/src/adapters) are supported by Prebid.js. For guidelines, see [Contributing](./CONTRIBUTING.md). @@ -276,9 +276,7 @@ Our PR review process can be found [here](https://github.com/prebid/Prebid.js/tr ### Add a Bidder Adapter -To add a bidder adapter module, see the instructions in [How to add a bidder adaptor](http://prebid.org/dev-docs/bidder-adaptor.html). - -Please **do NOT load Prebid.js inside your adapter**. If you do this, we will reject or remove your adapter as appropriate. +To add a bidder adapter module, see the instructions in [How to add a bidder adapter](https://docs.prebid.org/dev-docs/bidder-adaptor.html). ### Code Quality From 29bd8de8b089721ed032df92fb87b79a21d1981a Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Mon, 24 Aug 2020 18:51:47 +0200 Subject: [PATCH 0115/1476] Add sspBC adapter (#5531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add sspbc adapter * tests for sspbc adapter * sspBC adapter v4.5: set correct creativeId, add adomain to bid.meta, set test mode in adexchange, read site SN from bid response * sspBC adapter v4.5: set meta.advertiserDomains, update test to expect bid.meta * sspBC Adapter: add ajax tests (test ad with & without gdpr) * sspBC Adapter: remove ajax tests Co-authored-by: Wojciech Biały --- modules/sspBCAdapter.js | 319 +++++++++++++++++++++++ modules/sspBCAdapter.md | 40 +++ test/spec/modules/sspBCAdapter_spec.js | 334 +++++++++++++++++++++++++ 3 files changed, 693 insertions(+) create mode 100644 modules/sspBCAdapter.js create mode 100644 modules/sspBCAdapter.md create mode 100644 test/spec/modules/sspBCAdapter_spec.js diff --git a/modules/sspBCAdapter.js b/modules/sspBCAdapter.js new file mode 100644 index 00000000000..ef89fb08449 --- /dev/null +++ b/modules/sspBCAdapter.js @@ -0,0 +1,319 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'sspBC'; +const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; +const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; +const TMAX = 450; +const BIDDER_VERSION = '4.5'; +const W = window; +const { navigator } = W; + +const cookieSupport = () => { + const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); + const useCookies = navigator.cookieEnabled || !!document.cookie.length; + + return !isSafari && useCookies; +}; + +const applyClientHints = ortbRequest => { + const connection = navigator.connection || false; + const viewport = W.visualViewport || false; + const segments = []; + const hints = { + 'CH-Ect': connection.effectiveType, + 'CH-Rtt': connection.rtt, + 'CH-SaveData': connection.saveData, + 'CH-Downlink': connection.downlink, + 'CH-DeviceMemory': navigator.deviceMemory, + 'CH-Dpr': W.devicePixelRatio, + 'CH-ViewportWidth': viewport.width, + }; + + Object.keys(hints).forEach(key => { + const hint = hints[key]; + + if (hint) { + segments.push({ + name: key, + value: hint.toString(), + }); + } + }); + const data = [ + { + id: '12', + name: 'NetInfo', + segment: segments, + }]; + + ortbRequest.user = Object.assign(ortbRequest.user, { data }); +}; + +function applyGdpr(bidderRequest, ortbRequest) { + if (bidderRequest && bidderRequest.gdprConsent) { + ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': bidderRequest.gdprConsent.gdprApplies ? 1 : 0 }); + ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': bidderRequest.gdprConsent.consentString }); + } +} + +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = utils.deepAccess(collection[i], key); + + if (result) { + return result; + } + } +} + +/** + * @param {object} slot Ad Unit Params by Prebid + * @returns {object} Banner by OpenRTB 2.5 §3.2.6 + */ +function mapBanner(slot) { + if (slot.mediaType === 'banner' || + utils.deepAccess(slot, 'mediaTypes.banner') || + (!slot.mediaType && !slot.mediaTypes)) { + const format = slot.sizes.map(size => ({ + w: size[0], + h: size[1], + })); + + // override - tylko 1szy wymiar + // format = format.slice(0, 1); + return { + format, + id: slot.bidId, + }; + } +} + +function mapImpression(slot) { + const imp = { + id: slot.params.id, + banner: mapBanner(slot), + /* native: mapNative(slot), */ + tagid: slot.params.id, + }; + + const bidfloor = parseFloat(slot.params.bidfloor); + + if (bidfloor) { + imp.bidfloor = bidfloor; + } + + return imp; +} + +function renderCreative(site, auctionId, bid, seat, request) { + let gam; + + const mcad = { + id: auctionId, + seat, + seatbid: [{ + bid: [bid], + }], + }; + + const mcbase = btoa(encodeURI(JSON.stringify(mcad))); + + if (bid.adm) { + // parse adm for gam config + try { + gam = JSON.parse(bid.adm).gam; + + if (!gam || !Object.keys(gam).length) { + gam = undefined; + } else { + gam.namedSizes = ['fluid']; + gam.div = 'div-gpt-ad-x01'; + gam.targeting = Object.assign(gam.targeting || {}, { + OAS_retarg: '0', + PREBID_ON: '1', + emptygaf: '0', + }); + } + + if (gam && !gam.targeting) { + gam.targeting = {}; + } + } catch (err) { + utils.logWarn('Could not parse adm data', bid.adm); + } + } + + let adcode = ` + + + + + + + +
+ + + `; + + return adcode; +} + +const spec = { + code: BIDDER_CODE, + aliases: [], + supportedMediaTypes: [BANNER], + isBidRequestValid(bid) { + if (bid.params && bid.params.siteId && bid.params.id) { + return true; + } + + return false; + }, + buildRequests(validBidRequests, bidderRequest) { + if ((!validBidRequests) || (validBidRequests.length < 1)) { + return false; + } + + const siteId = setOnAny(validBidRequests, 'params.siteId'); + const page = setOnAny(validBidRequests, 'params.page') || bidderRequest.refererInfo.referer; + const domain = setOnAny(validBidRequests, 'params.domain') || utils.parseUrl(page).hostname; + const tmax = setOnAny(validBidRequests, 'params.tmax') ? parseInt(setOnAny(validBidRequests, 'params.tmax'), 10) : TMAX; + const pbver = '$prebid.version$'; + const testMode = setOnAny(validBidRequests, 'params.test') ? 1 : undefined; + + let ref; + + try { + if (W.self === W.top && document.referrer) { ref = document.referrer; } + } catch (e) { + } + + const payload = { + id: bidderRequest.auctionId, + site: { id: siteId, page, domain, ref }, + imp: validBidRequests.map(slot => mapImpression(slot)), + tmax, + user: {}, + regs: {}, + test: testMode, + }; + + applyGdpr(bidderRequest, payload); + applyClientHints(payload); + + return { + method: 'POST', + url: BIDDER_URL + '?cs=' + cookieSupport() + '&bdver=' + BIDDER_VERSION + '&pbver=' + pbver + '&inver=0', + data: JSON.stringify(payload), + bidderRequest, + }; + }, + + interpretResponse(serverResponse, request) { + const response = serverResponse.body; + const bids = []; + let site = JSON.parse(request.data).site; // get page and referer data from request + site.sn = response.sn || 'mc_adapter'; // WPM site name (wp_sn) + let seat; + + if (response.seatbid !== undefined) { + response.seatbid.forEach(seatbid => { + seat = seatbid.seat; + seatbid.bid.forEach(serverBid => { + const bidRequest = request.bidderRequest.bids.filter(b => b.params.id === serverBid.impid)[0]; + + if (bidRequest) { + const bidFloor = bidRequest.params.bidFloor || 0; + const bidCpm = bidRequest.params.flatCpm; + + if (!serverBid.gam && bidRequest.params.gam) { + // build GAM config + serverBid.gam = JSON.stringify({ + placement: bidRequest.params.gam, + multiplier: 1, + floor: bidRequest.params.gamFloor, + ceil: 100, + namedSizes: ['fluid'], + div: 'div-gpt-ad-x01', + targeting: { + OAS_retarg: '0', + PREBID_ON: '1', + DFPHASH: '', + emptygaf: '0', + }, + }); + } + + const bid = { + requestId: bidRequest.bidId, + creativeId: serverBid.crid || 'mcad_' + request.bidderRequest.auctionId + '_' + request.bidderRequest.params.id, + cpm: bidCpm || serverBid.price, + currency: response.cur, + ttl: serverBid.exp || 300, + width: serverBid.w, + height: serverBid.h, + bidderCode: BIDDER_CODE, + mediaType: 'banner', + meta: { + advertiserDomains: serverBid.adomain, + }, + netRevenue: true, + ad: renderCreative(site, response.id, serverBid, seat, request.bidderRequest), + }; + + if (bid.cpm > 0) { + if (bid.cpm >= bidFloor) { + bids.push(bid); + } else { + utils.logWarn('Discarding bid due to bidFloor setting', bid.cpm, bidFloor); + } + } + } else { + utils.logWarn('Discarding response - no matching request', serverBid.impid); + } + }); + }); + } + + return bids; + }, + getUserSyncs(syncOptions) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: SYNC_URL, + }]; + } + utils.logWarn('sspBC adapter requires iframe based user sync.'); + }, + onTimeout() { + }, +}; + +registerBidder(spec); + +export { + spec, +}; diff --git a/modules/sspBCAdapter.md b/modules/sspBCAdapter.md new file mode 100644 index 00000000000..645f41fcdc1 --- /dev/null +++ b/modules/sspBCAdapter.md @@ -0,0 +1,40 @@ +# Overview + +Module Name: sspBC Bidder Adapter +Module Type: Bidder Adapter +Maintainer: wojciech.bialy@grupawp.pl + +# Description + +Module that connects to Wirtualna Polska Media header bidding endpoint to fetch bids. +Only banner format is supported. +Supported currencies: USD, EUR, PLN + + +Required parameters: + + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'sspBC', + params: { + id: '006', // required + siteId: '235911', // required + domain: 'somesite.pl', // optional + page: 'somesite.pl/somepage.html', // optional + tmax: 250 // optional + } + }] + } +]; +``` diff --git a/test/spec/modules/sspBCAdapter_spec.js b/test/spec/modules/sspBCAdapter_spec.js new file mode 100644 index 00000000000..2cb0e8defa4 --- /dev/null +++ b/test/spec/modules/sspBCAdapter_spec.js @@ -0,0 +1,334 @@ +import { assert, expect } from 'chai'; +import { spec } from 'modules/sspBCAdapter.js'; +import * as utils from 'src/utils.js'; + +const BIDDER_CODE = 'sspBC'; +const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; +const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; + +describe('SSPBC adapter', function () { + function prepareTestData() { + const bidderRequestId = '1041bb47b0fafa'; + const auctionId = '8eda6d06-3d7c-4a94-9b35-74e42fbb3089'; + const transactionId = '50259989-b5c0-4edf-8f47-b1ef5fbedf39'; + const gdprConsent = { + consentString: 'BOtq-3dOtq-30BIABCPLC4-AAAAthr_7__7-_9_-_f__9uj3Or_v_f__30ccL59v_h_7v-_7fi_20nV4u_1vft9yfk1-5ctDztp505iakivHmqNeb9v_mz1_5pRP78k89r7337Ew_v8_v-b7JCON_Ig', + gdprApplies: true, + } + const bids = [{ + adUnitCode: 'test_wideboard', + bidder: BIDDER_CODE, + mediaTypes: { + banner: { + sizes: [ + [728, 90], + [750, 100], + [750, 200] + ] + } + }, + sizes: [ + [728, 90], + [750, 100], + [750, 200] + ], + params: { + id: '003', + siteId: '8816', + }, + auctionId, + bidderRequestId, + bidId: auctionId + '1', + transactionId, + }, + { + adUnitCode: 'test_rectangle', + bidder: BIDDER_CODE, + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + sizes: [ + [300, 250] + ], + params: { + id: '005', + siteId: '8816', + }, + auctionId, + bidderRequestId, + bidId: auctionId + '1', + transactionId, + } + ]; + const bids_test = [{ + adUnitCode: 'test_wideboard', + bidder: BIDDER_CODE, + mediaTypes: { + banner: { + sizes: [ + [970, 300], + [750, 300], + [750, 200], + [750, 100], + [300, 250] + ] + } + }, + sizes: [ + [970, 300], + [750, 300], + [750, 200], + [750, 100], + [300, 250] + ], + params: { + id: '005', + siteId: '235911', + test: 1 + }, + auctionId, + bidderRequestId, + bidId: auctionId + '1', + transactionId, + }]; + const bidRequest = { + auctionId, + bidderCode: BIDDER_CODE, + bidderRequestId, + bids, + gdprConsent, + refererInfo: { + reachedTop: true, + referer: 'https://test.site.pl/', + stack: ['https://test.site.pl/'], + } + }; + const bidRequestSingle = { + auctionId, + bidderCode: BIDDER_CODE, + bidderRequestId, + bids: [bids[0]], + gdprConsent, + refererInfo: { + reachedTop: true, + referer: 'https://test.site.pl/', + stack: ['https://test.site.pl/'], + } + }; + const bidRequestTest = { + auctionId, + bidderCode: BIDDER_CODE, + bidderRequestId, + bids: bids_test, + gdprConsent, + refererInfo: { + reachedTop: true, + referer: 'https://test.site.pl/', + stack: ['https://test.site.pl/'], + } + }; + const bidRequestTestNoGDPR = { + auctionId, + bidderCode: BIDDER_CODE, + bidderRequestId, + bids: bids_test, + refererInfo: { + reachedTop: true, + referer: 'https://test.site.pl/', + stack: ['https://test.site.pl/'], + } + }; + const serverResponse = { + 'body': { + 'id': auctionId, + 'seatbid': [{ + 'bid': [{ + 'id': '3347324c-6889-46d2-a800-ae78a5214c06', + 'impid': '003', + 'price': 1, + 'adid': 'lxHWkB7OnZeso3QiN1N4', + 'nurl': '', + 'adm': 'AD CODE 1', + 'adomain': ['adomain.pl'], + 'cid': 'BZ4gAg21T5nNtxlUCDSW', + 'crid': 'lxHWkB7OnZeso3QiN1N4', + 'w': 728, + 'h': 90 + }], + 'seat': 'dsp1', + 'group': 0 + }, { + 'bid': [{ + 'id': '2d766853-ea07-4529-8299-5f0ebadc546a', + 'impid': '005', + 'price': 2, + 'adm': 'AD CODE 2', + 'cid': '57744', + 'crid': '858252', + 'w': 300, + 'h': 250 + }], + 'seat': 'dsp2', + 'group': 0 + }], + 'cur': 'PLN' + } + }; + const serverResponseSingle = { + 'body': { + 'id': auctionId, + 'seatbid': [{ + 'bid': [{ + 'id': '3347324c-6889-46d2-a800-ae78a5214c06', + 'impid': '003', + 'price': 1, + 'adid': 'lxHWkB7OnZeso3QiN1N4', + 'nurl': '', + 'adm': 'AD CODE 1', + 'adomain': ['adomain.pl'], + 'cid': 'BZ4gAg21T5nNtxlUCDSW', + 'crid': 'lxHWkB7OnZeso3QiN1N4', + 'w': 728, + 'h': 90 + }], + 'seat': 'dsp1', + 'group': 0 + }], + 'cur': 'PLN' + } + }; + const emptyResponse = { + 'body': { + 'id': auctionId, + } + } + return { + bids, + bids_test, + bidRequest, + bidRequestSingle, + bidRequestTest, + bidRequestTestNoGDPR, + serverResponse, + serverResponseSingle, + emptyResponse + }; + }; + + describe('dependencies', function () { + it('utils should contain required functions', function () { + expect(utils.parseUrl).to.be.a('function'); + expect(utils.deepAccess).to.be.a('function'); + expect(utils.logWarn).to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + const { bids } = prepareTestData(); + let bid = bids[0]; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + bid.params.id = undefined; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + const { bids, bidRequest, bidRequestSingle } = prepareTestData(); + const request = spec.buildRequests(bids, bidRequest); + const requestSingle = spec.buildRequests([bids[0]], bidRequestSingle); + const payload = request ? JSON.parse(request.data) : { site: false, imp: false }; + const payloadSingle = request ? JSON.parse(requestSingle.data) : { site: false, imp: false }; + + it('should send bid request to endpoint via POST', function () { + expect(request.url).to.contain(BIDDER_URL); + expect(request.method).to.equal('POST'); + }); + + it('should contain prebid and bidder versions', function () { + expect(request.url).to.contain('bdver'); + expect(request.url).to.contain('pbver=$prebid.version$'); + }); + + it('should create one imp object per bid', function () { + expect(payload.imp.length).to.equal(bids.length); + expect(payloadSingle.imp.length).to.equal(1); + }); + + it('should save bidder request data', function () { + expect(request.bidderRequest).to.deep.equal(bidRequest); + }); + + it('should send site Id from bidder params', function () { + expect(payload.site.id).to.equal(bids[0].params.siteId); + }); + + it('should send page url from refererInfo', function () { + expect(payload.site.page).to.equal(bidRequest.refererInfo.referer); + }); + + it('should send gdpr data', function () { + expect(payload.regs).to.be.an('object').and.to.have.property('[ortb_extensions.gdpr]', 1); + expect(payload.user).to.be.an('object').and.to.have.property('[ortb_extensions.consent]', bidRequest.gdprConsent.consentString); + }); + }); + + describe('interpretResponse', function () { + const { bids, emptyResponse, serverResponse, serverResponseSingle, bidRequest, bidRequestSingle } = prepareTestData(); + const request = spec.buildRequests(bids, bidRequest); + const requestSingle = spec.buildRequests([bids[0]], bidRequestSingle); + + it('should handle nobid responses', function () { + let result = spec.interpretResponse(emptyResponse, request); + expect(result.length).to.equal(0); + }); + + it('should create bids from non-empty responses', function () { + let result = spec.interpretResponse(serverResponse, request); + let resultSingle = spec.interpretResponse(serverResponseSingle, requestSingle); + + expect(result.length).to.equal(bids.length); + expect(resultSingle.length).to.equal(1); + expect(resultSingle[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'bidderCode', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl'); + }); + + it('should handle a partial response', function () { + let resultPartial = spec.interpretResponse(serverResponseSingle, request); + expect(resultPartial.length).to.equal(1); + }); + + it('banner ad code should contain required variables', function () { + let resultSingle = spec.interpretResponse(serverResponseSingle, requestSingle); + let adcode = resultSingle[0].ad; + expect(adcode).to.be.a('string'); + expect(adcode).to.contain('window.rekid'); + expect(adcode).to.contain('window.mcad'); + expect(adcode).to.contain('window.gdpr'); + expect(adcode).to.contain('window.page'); + }) + }); + + describe('getUserSyncs', function () { + let syncResultAll = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }); + let syncResultImage = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }); + let syncResultNone = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); + + it('should provide correct url, if frame sync is allowed', function () { + expect(syncResultAll).to.have.length(1); + expect(syncResultAll[0].url).to.be.equal(SYNC_URL); + }); + + it('should send no syncs, if frame sync is not allowed', function () { + expect(syncResultImage).to.be.undefined; + expect(syncResultNone).to.be.undefined; + }); + }); +}); From b26bfe31485b12288eafc89639fe8e671c1ccf4e Mon Sep 17 00:00:00 2001 From: NemanjaRajkovic9 <45632924+NemanjaRajkovic9@users.noreply.github.com> Date: Mon, 24 Aug 2020 19:40:58 +0200 Subject: [PATCH 0116/1476] [IdentityLinkIdSystem] - pass tcfv2 consent string to envelope api (#5634) * [IdentityLinkIdSystem] - pass tcfv2 consent string to envelope api * [IdentityLinkIdSystem] - used deepAccess --- modules/identityLinkIdSystem.js | 22 ++++++++--- .../spec/modules/identityLinkIdSystem_spec.js | 38 ++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index c516c06d11a..14c33329b2d 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -6,8 +6,8 @@ */ import * as utils from '../src/utils.js' -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; /** @type {Submodule} */ export const identityLinkSubmodule = { @@ -16,6 +16,11 @@ export const identityLinkSubmodule = { * @type {string} */ name: 'identityLink', + /** + * used to specify vendor id + * @type {number} + */ + gvlid: 97, /** * decode the stored id value for passing to bid requests * @function @@ -39,10 +44,15 @@ export const identityLinkSubmodule = { } const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const tcfPolicyV2 = utils.deepAccess(consentData, 'vendorData.tcfPolicyVersion') === 2; // use protocol relative urls for http or https - const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? '&ct=1&cv=' + gdprConsentString : ''}`; + if (hasGdpr && (!gdprConsentString || gdprConsentString === '')) { + utils.logInfo('Consent string is required to call envelope API.'); + return; + } + const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? (tcfPolicyV2 ? '&ct=4&cv=' : '&ct=1&cv=') + gdprConsentString : ''}`; let resp; - resp = function(callback) { + resp = function (callback) { // Check ats during callback so it has a chance to initialise. // If ats library is available, use it to retrieve envelope. If not use standard third party endpoint if (window.ats) { @@ -60,7 +70,7 @@ export const identityLinkSubmodule = { } }; - return {callback: resp}; + return { callback: resp }; } }; // return envelope from third party endpoint @@ -83,7 +93,7 @@ function getEnvelope(url, callback) { callback(); } }; - ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); + ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); } submodule('userId', identityLinkSubmodule); diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index 0d539d5988c..9f36ba92558 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -41,7 +41,22 @@ describe('IdentityLinkId tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the LiveRamp envelope endpoint with consent string', function () { + it('should NOT call the LiveRamp envelope endpoint if gdpr applies but consent string is empty string', function () { + let consentData = { + gdprApplies: true, + consentString: '' + }; + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, consentData); + expect(submoduleCallback).to.be.undefined; + }); + + it('should NOT call the LiveRamp envelope endpoint if gdpr applies but consent string is missing', function () { + let consentData = { gdprApplies: true }; + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, consentData); + expect(submoduleCallback).to.be.undefined; + }); + + it('should call the LiveRamp envelope endpoint with IAB consent string v1', function () { let callBackSpy = sinon.spy(); let consentData = { gdprApplies: true, @@ -59,6 +74,27 @@ describe('IdentityLinkId tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); + it('should call the LiveRamp envelope endpoint with IAB consent string v2', function () { + let callBackSpy = sinon.spy(); + let consentData = { + gdprApplies: true, + consentString: 'CO4VThZO4VTiuADABBENAzCgAP_AAEOAAAAAAwwAgAEABhAAgAgAAA.YAAAAAAAAAA', + vendorData: { + tcfPolicyVersion: 2 + } + }; + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, consentData).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.rlcdn.com/api/identity/envelope?pid=14&ct=4&cv=CO4VThZO4VTiuADABBENAzCgAP_AAEOAAAAAAwwAgAEABhAAgAgAAA.YAAAAAAAAAA'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + it('should not throw Uncaught TypeError when envelope endpoint returns empty response', function () { let callBackSpy = sinon.spy(); let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; From 01f4b287d4a284c95e569d97b22387a534b365c9 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Mon, 24 Aug 2020 13:25:32 -0700 Subject: [PATCH 0117/1476] Fix bug and add tests to catch next time (#5656) --- modules/rubiconBidAdapter.js | 4 ++-- test/spec/modules/rubiconBidAdapter_spec.js | 23 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 2768b41da21..9f5c2ec10d5 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -550,8 +550,8 @@ export const spec = { try { floorInfo = bidRequest.getFloor({ currency: 'USD', - mediaType: 'video', - size: parseSizes(bidRequest, 'video') + mediaType: 'banner', + size: '*' }); } catch (e) { utils.logError('Rubicon: getFloor threw an error: ', e); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 1d5769b9ac1..49a3f60bac7 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,5 +1,4 @@ import {expect} from 'chai'; -import adapterManager from 'src/adapterManager.js'; import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter.js'; import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; @@ -419,7 +418,18 @@ describe('the rubicon adapter', function () { it('should correctly send hard floors when getFloor function is present and returns valid floor', function () { // default getFloor response is empty object so should not break and not send hard_floor bidderRequest.bids[0].getFloor = () => getFloorResponse; + sinon.spy(bidderRequest.bids[0], 'getFloor'); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + + // make sure banner bid called with right stuff + expect( + bidderRequest.bids[0].getFloor.calledWith({ + currency: 'USD', + mediaType: 'banner', + size: '*' + }) + ).to.be.true; + let data = parseQuery(request.data); expect(data.rp_hard_floor).to.be.undefined; @@ -1597,12 +1607,23 @@ describe('the rubicon adapter', function () { createVideoBidderRequest(); // default getFloor response is empty object so should not break and not send hard_floor bidderRequest.bids[0].getFloor = () => getFloorResponse; + sinon.spy(bidderRequest.bids[0], 'getFloor'); + sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + // make sure banner bid called with right stuff + expect( + bidderRequest.bids[0].getFloor.calledWith({ + currency: 'USD', + mediaType: 'video', + size: [640, 480] + }) + ).to.be.true; + // not an object should work and not send expect(request.data.imp[0].bidfloor).to.be.undefined; From 7001a23635d7f6d5cbfbec082e9ceaf2e32f6f49 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 25 Aug 2020 11:02:48 -0400 Subject: [PATCH 0118/1476] Update vidazooBidAdapter_spec.js (#5639) * Update vidazooBidAdapter_spec.js * Update vidazooBidAdapter_spec.js --- test/spec/modules/vidazooBidAdapter_spec.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 57e7f4e8ae2..8b3a492d2e5 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -315,12 +315,7 @@ describe('VidazooBidAdapter', function () { describe('unique deal id', function () { const key = 'myKey'; let uniqueDealId; - - it('should get fresh unique deal id', function () { - const now = Date.now(); - uniqueDealId = getUniqueDealId(key); - expect(uniqueDealId).to.be.equal(`${key}_${now.toString()}`); - }); + uniqueDealId = getUniqueDealId(key); it('should get current unique deal id', function (done) { // waiting some time so `now` will become past From 71ff9344510439235f6528c5785cb6e1ccdda421 Mon Sep 17 00:00:00 2001 From: smartclip-adtech <65160328+smartclip-adtech@users.noreply.github.com> Date: Tue, 25 Aug 2020 18:10:21 +0200 Subject: [PATCH 0119/1476] Add smartxBidAdapter (#5275) * Add smartclipBidAdapter * Updated imported files * .js file * Beautify code * Revert "Beautify code" This reverts commit da2aea761e4187fd3136e9b8b4f4ab5857266114. * testing * partial tab fix * syntax fixes * further syntax fixes * Trailing spaces... * const parameters fpr smartInTxt Functions * const to var * // eslint-disable-next-line camelcase * new commit * Update Targeting * Update syntax * beautify code * remove trailing spaces * Update smartclipBidAdapter.md * Update smartclipBidAdapter.js * Update smartclipBidAdapter.js * rename files * added first unit tests, further adjustments * adjusted queries, ran gulp lint, etc * adjustments, further tests * extendet unit tests * naming for spec * adjusted bidfloor and bidfloorcur, removed old queries, adjustments in the markdown with more use cases * added more use cases within md file * layout fix * md layout * smartxBidAdapter.js - triple equals, removed context2 due to not used in params.ad_units. Adjusted query. smartxBidAdapter_spec.js - fixed typo Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 400 ++++++++++++++++ modules/smartxBidAdapter.md | 159 +++++++ test/spec/modules/smartxBidAdapter_spec.js | 513 +++++++++++++++++++++ 3 files changed, 1072 insertions(+) create mode 100644 modules/smartxBidAdapter.js create mode 100644 modules/smartxBidAdapter.md create mode 100644 test/spec/modules/smartxBidAdapter_spec.js diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js new file mode 100644 index 00000000000..a745c54e39c --- /dev/null +++ b/modules/smartxBidAdapter.js @@ -0,0 +1,400 @@ +import * as utils from '../src/utils.js'; +import { + Renderer +} from '../src/Renderer.js'; +import { + registerBidder +} from '../src/adapters/bidderFactory.js'; +import { + VIDEO +} from '../src/mediaTypes.js'; +const BIDDER_CODE = 'smartx'; +const URL = 'https://bid.sxp.smartclip.net/bid/1000'; +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO], + /** + * Determines whether or not the given bid request is valid. + * From Prebid.js: isBidRequestValid - Verify the the AdUnits.bids, respond with true (valid) or false (invalid). + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + if (bid && typeof bid.params !== 'object') { + utils.logError(BIDDER_CODE + ': params is not defined or is incorrect in the bidder settings.'); + return false; + } + if (!utils.deepAccess(bid, 'mediaTypes.video')) { + utils.logError(BIDDER_CODE + ': mediaTypes.video is not present in the bidder settings.'); + return false; + } + const playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); + if (!playerSize || !utils.isArray(playerSize)) { + utils.logError(BIDDER_CODE + ': mediaTypes.video.playerSize is not defined in the bidder settings.'); + return false; + } + if (!utils.getBidIdParameter('tagId', bid.params)) { + utils.logError(BIDDER_CODE + ': tagId is not present in bidder params'); + return false; + } + if (!utils.getBidIdParameter('publisherId', bid.params)) { + utils.logError(BIDDER_CODE + ': publisherId is not present in bidder params'); + return false; + } + if (!utils.getBidIdParameter('siteId', bid.params)) { + utils.logError(BIDDER_CODE + ': siteId is not present in bidder params'); + return false; + } + if (!utils.getBidIdParameter('bidfloor', bid.params)) { + utils.logError(BIDDER_CODE + ': bidfloor is not present in bidder params'); + return false; + } + if (!utils.getBidIdParameter('bidfloorcur', bid.params)) { + utils.logError(BIDDER_CODE + ': bidfloorcur is not present in bidder params'); + return false; + } + if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + if (!utils.getBidIdParameter('outstream_options', bid.params)) { + utils.logError(BIDDER_CODE + ': outstream_options parameter is not defined'); + return false; + } + if (!utils.getBidIdParameter('slot', bid.params.outstream_options)) { + utils.logError(BIDDER_CODE + ': slot parameter is not defined in outstream_options object in the configuration'); + return false; + } + if (!utils.getBidIdParameter('outstream_function', bid.params)) { + utils.logMessage(BIDDER_CODE + ': outstream_function parameter is not defined. The default outstream renderer will be injected in the header. You can override the default SmartX outstream rendering by defining your own Outstream function using field outstream_function.'); + return true; + } + } + + return true; + }, + /** + * Make a server request from the list of BidRequests. + * from Prebid.js: buildRequests - Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. + * + * @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 (bidRequests, bidderRequest) { + const page = bidderRequest.refererInfo.referer; + const isPageSecure = !!page.match(/^https:/) + + const smartxRequests = bidRequests.map(function (bid) { + const tagId = utils.getBidIdParameter('tagId', bid.params); + const publisherId = utils.getBidIdParameter('publisherId', bid.params); + const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); + const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params); + const siteId = utils.getBidIdParameter('siteId', bid.params); + const domain = utils.getBidIdParameter('domain', bid.params); + const cat = utils.getBidIdParameter('cat', bid.params); + let pubcid = null; + const playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); + const contentWidth = playerSize[0][0]; + const contentHeight = playerSize[0][1]; + const secure = +(isPageSecure || (utils.getBidIdParameter('secure', bid.params) ? 1 : 0)); + const ext = { + sdk_name: 'Prebid 1+' + }; + const mimes = utils.getBidIdParameter('mimes', bid.params) || ['application/javascript', 'video/mp4', 'video/webm']; + const linearity = utils.getBidIdParameter('linearity', bid.params) || 1; + const minduration = utils.getBidIdParameter('minduration', bid.params) || 0; + const maxduration = utils.getBidIdParameter('maxduration', bid.params) || 500; + const startdelay = utils.getBidIdParameter('startdelay', bid.params) || 0; + const minbitrate = utils.getBidIdParameter('minbitrate', bid.params) || 0; + const maxbitrate = utils.getBidIdParameter('maxbitrate', bid.params) || 3500; + const delivery = utils.getBidIdParameter('delivery', bid.params) || [2]; + const pos = utils.getBidIdParameter('pos', bid.params) || 1; + const api = utils.getBidIdParameter('api', bid.params) || [2]; + const protocols = utils.getBidIdParameter('protocols', bid.params) || [2, 3, 5, 6]; + var contextcustom = utils.deepAccess(bid, 'mediaTypes.video.context'); + var placement = 1; + + if (contextcustom === 'outstream') { + placement = 3; + } + + let smartxReq = { + id: bid.bidId, + secure: secure, + bidfloor: bidfloor, + bidfloorcur: bidfloorcur, + video: { + w: contentWidth, + h: contentHeight, + mimes: mimes, + linearity: linearity, + minduration: minduration, + maxduration: maxduration, + startdelay: startdelay, + protocols: protocols, + minbitrate: minbitrate, + maxbitrate: maxbitrate, + delivery: delivery, + pos: pos, + placement: placement, + api: api, + ext: ext + }, + tagid: tagId, + ext: { + 'smart.bidpricetype': 1 + } + }; + + if (bid.crumbs && bid.crumbs.pubcid) { + pubcid = bid.crumbs.pubcid; + } + + const language = navigator.language ? 'language' : 'userLanguage'; + const device = { + h: screen.height, + w: screen.width, + dnt: utils.getDNT() ? 1 : 0, + language: navigator[language].split('-')[0], + make: navigator.vendor ? navigator.vendor : '', + ua: navigator.userAgent + }; + const at = utils.getBidIdParameter('at', bid.params) || 2; + const cur = utils.getBidIdParameter('cur', bid.params) || ['EUR']; + const requestPayload = { + id: utils.generateUUID(), + imp: smartxReq, + site: { + id: siteId, + page: page, + cat: cat, + content: 'content', + domain: domain, + publisher: { + id: publisherId + } + }, + device: device, + at: at, + cur: cur + }; + const userExt = {}; + + // Add GDPR flag and consent string + if (bidderRequest && bidderRequest.gdprConsent) { + userExt.consent = bidderRequest.gdprConsent.consentString; + if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') { + requestPayload.regs = { + ext: { + gdpr: (bidderRequest.gdprConsent.gdprApplies ? 1 : 0) + } + }; + } + } + + // Add common id if available + if (pubcid) { + userExt.fpc = pubcid; + } + + // Only add the user object if it's not empty + if (!utils.isEmpty(userExt)) { + requestPayload.user = { + ext: userExt + }; + } + + // Targeting + if (utils.getBidIdParameter('data', bid.params.user)) { + var targetingarr = []; + for (var i = 0; i < bid.params.user.data.length; i++) { + var isemq = (bid.params.user.data[i].name) || 'empty'; + if (isemq !== 'empty') { + var provider = bid.params.user.data[i].name; + var targetingstring = (bid.params.user.data[i].segment[0].value) || 'empty'; + targetingarr.push({ + id: provider, + name: provider, + segment: { + name: provider, + value: targetingstring, + } + }) + } + } + + requestPayload.user = { + ext: userExt, + data: targetingarr + } + } + + return { + method: 'POST', + url: URL, + data: requestPayload, + bidRequest: bidderRequest, + options: { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.3' + } + } + }; + }); + + return smartxRequests; + }, + /** + * 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, bidderRequest) { + const bidResponses = []; + const serverResponseBody = serverResponse.body; + if (serverResponseBody && utils.isArray(serverResponseBody.seatbid)) { + utils._each(serverResponseBody.seatbid, function (bids) { + utils._each(bids.bid, function (smartxBid) { + let currentBidRequest = {}; + for (let i in bidderRequest.bidRequest.bids) { + if (smartxBid.impid == bidderRequest.bidRequest.bids[i].bidId) { + currentBidRequest = bidderRequest.bidRequest.bids[i]; + } + } + /** + * Make sure currency and price are the right ones + * TODO: what about the pre_market_bid partners sizes? + */ + utils._each(currentBidRequest.params.pre_market_bids, function (pmb) { + if (pmb.deal_id == smartxBid.id) { + smartxBid.price = pmb.price; + serverResponseBody.cur = pmb.currency; + } + }); + const bid = { + requestId: currentBidRequest.bidId, + currency: serverResponseBody.cur || 'USD', + cpm: smartxBid.price, + creativeId: smartxBid.crid || '', + ttl: 360, + netRevenue: true, + vastContent: smartxBid.adm, + vastXml: smartxBid.adm, + mediaType: VIDEO, + width: smartxBid.w, + height: smartxBid.h + }; + const context = utils.deepAccess(currentBidRequest, 'mediaTypes.video.context'); + if (context === 'outstream') { + const playersize = utils.deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); + const renderer = Renderer.install({ + id: 0, + url: '//', + config: { + adText: 'SmartX Outstream Video Ad via Prebid.js', + player_width: playersize[0][0], + player_height: playersize[0][1], + content_page_url: utils.deepAccess(bidderRequest, 'data.site.page'), + ad_mute: +!!utils.deepAccess(currentBidRequest, 'params.ad_mute'), + hide_skin: +!!utils.deepAccess(currentBidRequest, 'params.hide_skin'), + outstream_options: utils.deepAccess(currentBidRequest, 'params.outstream_options'), + outstream_function: utils.deepAccess(currentBidRequest, 'params.outstream_function') + } + }); + try { + renderer.setRender(outstreamRender); + renderer.setEventHandlers({ + impression: function impression() { + return utils.logMessage('SmartX outstream video impression event'); + }, + loaded: function loaded() { + return utils.logMessage('SmartX outstream video loaded event'); + }, + ended: function ended() { + utils.logMessage('SmartX outstream renderer video event'); + } + }); + } catch (err) { + utils.logWarn('Prebid Error calling setRender or setEventHandlers on renderer', err); + } + bid.renderer = renderer; + } + bidResponses.push(bid); + }) + }); + } + return bidResponses; + } +} + +function createOutstreamScript(bid) { + // const slot = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options); + utils.logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); + const elementId = bid.adUnitCode; + // eslint-disable-next-line camelcase + var sc_smartIntxtStart; + // eslint-disable-next-line camelcase + var sc_smartIntxtNoad; + // eslint-disable-next-line camelcase + var sc_smartIntxtEnd; + var SmartPlay; + let smartPlayObj = { + minAdWidth: 290, + maxAdWidth: 900, + elementLocator: { + allowInViewport: false, + minimumElementWidth: 290, + scanPixelsBelowViewport: 800 + }, + onStartCallback: function (m, n) { + try { + sc_smartIntxtStart(n); + } catch (f) {} + }, + onCappedCallback: function (m, n) { + try { + sc_smartIntxtNoad(n); + } catch (f) {} + }, + onEndCallback: function (m, n) { + try { + sc_smartIntxtEnd(n); + } catch (f) {} + }, + debug: true + }; + smartPlayObj.adResponse = bid.vastContent; + const script = window.document.createElement('script'); + script.type = 'text/javascript'; + script.async = 'true'; + script.src = 'https://dco.smartclip.net/?plc=7777777'; + script.onload = script.onreadystatechange = function () { + var rs = this.readyState; + if (rs && rs != 'complete' && rs != 'loaded') return; + try { + SmartPlay(elementId, smartPlayObj); + } catch (e) { + utils.logError('error caught : ' + e); + } + }; + 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 slot = utils.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) { + utils.logError('[SMARTX][renderer] Error:' + err.message) + } + } +} +registerBidder(spec); diff --git a/modules/smartxBidAdapter.md b/modules/smartxBidAdapter.md new file mode 100644 index 00000000000..a53af839e2b --- /dev/null +++ b/modules/smartxBidAdapter.md @@ -0,0 +1,159 @@ +# Overview + +``` +Module Name: smartclip Bidder Adapter +Module Type: Bidder Adapter +Maintainer: adtech@smartclip.tv +``` + +# Description + +Connect to smartx for bids. + +This adapter requires setup and approval from the smartclip team. + +# Test Parameters - Use case #1 - Out-Stream example and default rendering options +``` + var adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 360] + } + }, + bids: [{ + bidder: 'smartx', + params: { + tagId: 'Nu68JuOWAvrbzoyrOR9a7A', + publisherId: '11986', + siteId: '22860', + bidfloor: 0.3, + bidfloorcur: "EUR", + at: 2, + cur: ["EUR"], + outstream_options: { + slot: 'video1' + }, + } + }], + }]; +``` + +# Test Parameters - Use case #2 - Out-Stream with targeting example and default rendering options +``` + var adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 360] + } + }, + bids: [{ + bidder: 'smartx', + params: { + tagId: 'Nu68JuOWAvrbzoyrOR9a7A', + publisherId: '11986', + siteId: '22860', + bidfloor: 0.3, + bidfloorcur: "EUR", + at: 2, + cur: ["EUR"], + outstream_options: { + slot: 'video1' + }, + user: { + data: [{ + id: 'emq', + name: 'emq', + segment: [{ + id: 'emq', + name: 'emq', + value: 'e0:k14:e24' + }] + }, { + id: 'gs', + name: 'gs', + segment: [{ + id: 'gs', + name: 'gs', + value: 'tone_of_voice_dislike:tone_of_voice_negative:gs_health' + }] + }] + } + } + }] + }]; +``` + +# Test Parameters - Use case #3 - In-Stream example and default rendering options +``` + var adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 360] + } + }, + bids: [{ + bidder: 'smartx', + params: { + tagId: 'Nu68JuOWAvrbzoyrOR9a7A', + publisherId: '11986', + siteId: '22860', + bidfloor: 0.3, + bidfloorcur: "EUR", + at: 2, + cur: ["EUR"] + } + }], + }]; +``` + +# Test Parameters - Use case #4 - In-Stream with targeting example and default rendering options +``` + var adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 360] + } + }, + bids: [{ + bidder: 'smartx', + params: { + tagId: 'Nu68JuOWAvrbzoyrOR9a7A', + publisherId: '11986', + siteId: '22860', + bidfloor: 0.3, + bidfloorcur: "EUR", + at: 2, + cur: ["EUR"], + user: { + data: [{ + id: 'emq', + name: 'emq', + segment: [{ + id: 'emq', + name: 'emq', + value: 'e0:k14:e24' + }] + }, + { + id: 'gs', + name: 'gs', + segment: [{ + id: 'gs', + name: 'gs', + value: 'tone_of_voice_dislike:tone_of_voice_negative:gs_health' + }] + } + ] + } + } + }], + }]; +``` \ No newline at end of file diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js new file mode 100644 index 00000000000..efc6abcc5fa --- /dev/null +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -0,0 +1,513 @@ +import { + expect +} from 'chai'; +import { + spec +} from 'modules/smartxBidAdapter.js'; + +describe('The smartx adapter', function () { + function getValidBidObject() { + return { + bidId: 123, + mediaTypes: { + video: { + // context: 'outstream', + playerSize: [ + ['640', '360'] + ] + } + }, + params: { + tagId: 'Nu68JuOWAvrbzoyrOR9a7A', + publisherId: '__name__', + siteId: '__name__', + bidfloor: 0.3, + bidfloorcur: 'EUR', + // user: { + // data: '' + // } + // outstream_options: { + // slot: 'yourelementid' + // } + } + + }; + }; + + describe('isBidRequestValid', function () { + var bid; + + beforeEach(function () { + bid = getValidBidObject(); + }); + + it('should fail validation if the bid isn\'t defined or not an object', function () { + var result = spec.isBidRequestValid(); + + expect(result).to.equal(false); + + result = spec.isBidRequestValid('not an object'); + + expect(result).to.equal(false); + }); + + it('should succeed validation with all the right parameters', function () { + expect(spec.isBidRequestValid(getValidBidObject())).to.equal(true); + }); + + it('should succeed validation with mediaType and outstream_function or outstream_options', function () { + bid.mediaType = 'video'; + bid.params.outstream_function = 'outstream_func'; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.params.outstream_function; + bid.params.outstream_options = { + slot: 'yourelementid' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should succeed with ad_unit outstream and outstream function set', function () { + bid.params.ad_unit = 'outstream'; + bid.params.outstream_function = function () {}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should succeed with mediaTypes_video_context outstream, options set for outstream and slot provided', function () { + bid.mediaTypes.video.context = 'outstream'; + bid.params.outstream_options = { + slot: 'yourelementid' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should fail without video', function () { + delete bid.mediaTypes.video; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without playerSize', function () { + delete bid.mediaTypes.video.playerSize; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without tagId', function () { + delete bid.params.tagId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without publisherId', function () { + delete bid.params.publisherId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without siteId', function () { + delete bid.params.siteId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without bidfloor', function () { + delete bid.params.bidfloor; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail without bidfloorcur', function () { + delete bid.params.bidfloorcur; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail with context outstream but no options set for outstream', function () { + bid.mediaTypes.video.context = 'outstream'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should fail with context outstream, options set for outstream but no slot provided', function () { + bid.mediaTypes.video.context = 'outstream'; + bid.params.outstream_options = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should succeed with context outstream, options set for outstream but no outstream_function is set', function () { + bid.mediaTypes.video.context = 'outstream'; + bid.params.outstream_options = { + slot: 'yourelementid' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + var bid, + bidRequestObj; + + beforeEach(function () { + bid = getValidBidObject(); + bidRequestObj = { + refererInfo: { + referer: 'prebid.js' + } + }; + }); + + it('should build a very basic request', function () { + var request = spec.buildRequests([bid], bidRequestObj)[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://bid.sxp.smartclip.net/bid/1000'); + expect(request.bidRequest).to.equal(bidRequestObj); + expect(request.data.imp.id).to.match(/\d+/); + expect(request.data.imp.secure).to.equal(0); + + expect(request.data.imp.video).to.deep.equal({ + ext: { + sdk_name: 'Prebid 1+' + }, + h: '360', + w: '640', + mimes: [ + 'application/javascript', 'video/mp4', 'video/webm' + ], + api: [2], + delivery: [2], + linearity: 1, + maxbitrate: 3500, + maxduration: 500, + minbitrate: 0, + minduration: 0, + protocols: [ + 2, 3, 5, 6 + ], + startdelay: 0, + placement: 1, + pos: 1 + }); + + expect(request.data.site).to.deep.equal({ + content: 'content', + id: '__name__', + page: 'prebid.js', + cat: '', + domain: '', + publisher: { + id: '__name__' + } + }); + }); + + it('should change request parameters based on options sent', function () { + var request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.imp.video.ext).to.deep.equal({ + sdk_name: 'Prebid 1+' + }); + + expect(request.data.imp.video).to.contain({ + placement: 1 + }); + + bid.mediaTypes.video.context = 'outstream'; + + bid.params = { + outstream_options: { + foo: 'bar' + }, + outstream_function: '987', + mimes: 'foo', + linearity: 2, + minduration: 5, + maxduration: 10, + startdelay: 1, + minbitrate: 50, + maxbitrate: 500, + delivery: [1], + pos: 2, + api: [1], + protocols: [ + 2, 3, 5 + ], + bidfloor: 55, + bidfloorcur: 'foo', + at: 1, + cur: ['FOO'] + }; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.imp.video.ext).to.deep.equal({ + sdk_name: 'Prebid 1+' + }); + + expect(request.data.imp.video).to.contain({ + minduration: 5, + maxduration: 10 + }); + + expect(request.data.imp.video.startdelay).to.equal(1); + + expect(request.data.imp.video).to.contain({ + placement: 3 + }); + + expect(request.data.imp.bidfloor).to.equal(55); + + expect(request.data.imp.bidfloorcur).to.equal('foo'); + + expect(request.data.imp.video.linearity).to.equal(2); + + expect(request.data.imp.video.minbitrate).to.equal(50); + + expect(request.data.imp.video.maxbitrate).to.equal(500); + }); + + it('should pass GDPR params', function () { + var request; + + bidRequestObj.gdprConsent = { + gdprApplies: true, + consentString: 'foo' + } + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.regs.ext.gdpr).to.equal(1); + expect(request.data.user.ext.consent).to.be.an('string'); + expect(request.data.user.ext.consent).to.equal('foo'); + }); + + it('should pass emq params', function () { + var request; + + bid.params.user = { + data: [{ + id: 'emq', + name: 'emq', + segment: [{ + name: 'emq', + value: 'foo' + }] + }] + } + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.user.data).to.deep.equal([{ + id: 'emq', + name: 'emq', + segment: { + name: 'emq', + value: 'foo' + } + }]); + }); + + it('should pass crumbs params', function () { + var request; + + bid.crumbs = { + pubcid: 'pubcid_1' + }; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.user.ext).to.contain({ + fpc: 'pubcid_1' + }); + }); + + it('should pass linearity params', function () { + var request; + + bid.params.linearity = 3 + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.imp.video.linearity).to.equal(3); + }); + + it('should pass min and max duration params', function () { + var request; + + bid.params.minduration = 3 + bid.params.maxduration = 15 + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.imp.video.minduration).to.equal(3); + expect(request.data.imp.video.maxduration).to.equal(15); + }); + }); + + describe('interpretResponse', function () { + var serverResponse, bidderRequestObj; + + beforeEach(function () { + bidderRequestObj = { + bidRequest: { + bids: [{ + mediaTypes: { + video: { + playerSize: [ + ['400', '300'] + ] + } + }, + bidId: 123, + params: { + player_width: 400, + player_height: 300, + content_page_url: 'prebid.js', + ad_mute: 1, + outstream_options: { + foo: 'bar' + }, + outstream_function: 'function', + } + }, { + mediaTypes: { + video: { + playerSize: [ + ['200', '100'] + ] + } + }, + bidId: 124, + params: { + player_width: 200, + player_height: 100, + content_page_url: 'prebid.js', + ad_mute: 1, + outstream_options: { + foo: 'bar' + }, + outstream_function: 'function' + } + }] + } + }; + + serverResponse = { + body: { + id: 12345, + seatbid: [{ + bid: [{ + impid: 123, + cur: 'USD', + price: 12, + adomain: ['abc.com'], + crid: 321, + w: 400, + h: 300, + ext: { + slot: 'slot123' + } + }, { + impid: 124, + cur: 'USD', + price: 13, + adomain: ['def.com'], + crid: 654, + w: 200, + h: 100, + ext: { + slot: 'slot124' + } + }] + }] + } + }; + }); + + it('should return an array of bid responses', function () { + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + expect(responses).to.be.an('array').with.length(2); + expect(responses[0].requestId).to.equal(123); + expect(responses[0].currency).to.equal('USD'); + expect(responses[0].cpm).to.equal(12); + expect(responses[0].creativeId).to.equal(321); + expect(responses[0].ttl).to.equal(360); + expect(responses[0].netRevenue).to.equal(true); + expect(responses[0].mediaType).to.equal('video'); + expect(responses[0].width).to.equal(400); + expect(responses[0].height).to.equal(300); + expect(responses[1].requestId).to.equal(124); + expect(responses[1].currency).to.equal('USD'); + expect(responses[1].cpm).to.equal(13); + expect(responses[1].creativeId).to.equal(654); + expect(responses[1].ttl).to.equal(360); + expect(responses[1].netRevenue).to.equal(true); + expect(responses[1].mediaType).to.equal('video'); + expect(responses[1].width).to.equal(200); + expect(responses[1].height).to.equal(100); + }); + }); + + describe('oustreamRender', function () { + var serverResponse, bidderRequestObj; + + beforeEach(function () { + bidderRequestObj = { + bidRequest: { + bids: [{ + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + ['400', '300'] + ] + } + }, + bidId: 123, + params: { + player_width: 400, + player_height: 300, + content_page_url: 'prebid.js', + ad_mute: 1, + outstream_options: { + slot: 'slot123' + }, + outstream_function: 'function', + } + }] + } + }; + + serverResponse = { + body: { + id: 12345, + seatbid: [{ + bid: [{ + impid: 123, + cur: 'USD', + price: 12, + adomain: ['abc.com'], + crid: 321, + w: 400, + h: 300, + ext: { + slot: 'slot123' + } + }] + }] + } + }; + }); + + it('should attempt to insert the EASI script', function () { + var scriptTag; + sinon.stub(window.document, 'getElementById').returns({ + appendChild: sinon.stub().callsFake(function (script) { + scriptTag = script + }) + }); + var 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://dco.smartclip.net/?plc=7777777'); + + window.document.getElementById.restore(); + }); + }); +}) From e2c4e69ed6c1b81a89d0a8924a397d3abad7469f Mon Sep 17 00:00:00 2001 From: Mirko Feddern <3244291+mirkorean@users.noreply.github.com> Date: Wed, 26 Aug 2020 03:25:53 +0200 Subject: [PATCH 0120/1476] Add GDPR parameters to yieldlab delivery adtag (#5658) --- modules/yieldlabBidAdapter.js | 10 +++-- test/spec/modules/yieldlabBidAdapter_spec.js | 39 ++++++++++++++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index b2a9176e342..1d1636bda69 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -68,7 +68,8 @@ export const spec = { return { method: 'GET', url: `${ENDPOINT}/yp/${adslots}?${queryString}`, - validBidRequests: validBidRequests + validBidRequests: validBidRequests, + queryParams: query } }, @@ -80,6 +81,7 @@ export const spec = { interpretResponse: function (serverResponse, originalBidRequest) { const bidResponses = [] const timestamp = Date.now() + const reqParams = originalBidRequest.queryParams originalBidRequest.validBidRequests.forEach(function (bidRequest) { if (!serverResponse.body) { @@ -95,6 +97,8 @@ export const spec = { const customsize = bidRequest.params.adSize !== undefined ? parseSize(bidRequest.params.adSize) : primarysize 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 bidResponse = { requestId: bidRequest.bidId, @@ -107,7 +111,7 @@ export const spec = { netRevenue: false, ttl: BID_RESPONSE_TTL_SEC, referrer: '', - ad: `` + ad: `` } if (isVideo(bidRequest, adType)) { @@ -117,7 +121,7 @@ export const spec = { bidResponse.height = playersize[1] } bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}${extId}` + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}` if (isOutstream(bidRequest)) { const renderer = Renderer.install({ diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 1e20343c1cd..e7a9285cb48 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -47,6 +47,16 @@ const VIDEO_RESPONSE = Object.assign({}, RESPONSE, { 'adtype': 'VIDEO' }) +const REQPARAMS = { + json: true, + ts: 1234567890 +} + +const REQPARAMS_GDPR = Object.assign({}, REQPARAMS, { + gdpr: true, + consent: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA' +}) + describe('yieldlabBidAdapter', function () { const adapter = newBidder(spec) @@ -131,7 +141,7 @@ describe('yieldlabBidAdapter', function () { }) it('should get correct bid response', function () { - const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST]}) + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST], queryParams: REQPARAMS}) expect(result[0].requestId).to.equal('2d925f27f5079f') expect(result[0].cpm).to.equal(0.01) @@ -147,6 +157,13 @@ describe('yieldlabBidAdapter', function () { expect(result[0].ad).to.include('&id=abc') }) + it('should append gdpr parameters to adtag', function () { + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST], queryParams: REQPARAMS_GDPR}) + + expect(result[0].ad).to.include('&gdpr=true') + expect(result[0].ad).to.include('&consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA') + }) + it('should get correct bid response when passing more than one size', function () { const REQUEST2 = Object.assign({}, REQUEST, { 'sizes': [ @@ -155,7 +172,7 @@ describe('yieldlabBidAdapter', function () { [970, 90], ] }) - const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST2]}) + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST2], queryParams: REQPARAMS}) expect(result[0].requestId).to.equal('2d925f27f5079f') expect(result[0].cpm).to.equal(0.01) @@ -179,7 +196,7 @@ describe('yieldlabBidAdapter', function () { } } }) - const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST]}) + const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS}) expect(result[0].requestId).to.equal('2d925f27f5079f') expect(result[0].cpm).to.equal(0.01) @@ -188,6 +205,20 @@ describe('yieldlabBidAdapter', function () { expect(result[0].vastUrl).to.include('&id=abc') }) + it('should append gdpr parameters to vastUrl', function () { + const VIDEO_REQUEST = Object.assign({}, REQUEST, { + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }) + const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_GDPR}) + + expect(result[0].vastUrl).to.include('&gdpr=true') + expect(result[0].vastUrl).to.include('&consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA') + }) + it('should add renderer if outstream context', function () { const OUTSTREAM_REQUEST = Object.assign({}, REQUEST, { 'mediaTypes': { @@ -197,7 +228,7 @@ describe('yieldlabBidAdapter', function () { } } }) - const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [OUTSTREAM_REQUEST]}) + const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [OUTSTREAM_REQUEST], queryParams: REQPARAMS}) expect(result[0].renderer.id).to.equal('2d925f27f5079f') expect(result[0].renderer.url).to.equal('https://ad2.movad.net/dynamic.ad?a=o193092&ma_loadEvent=ma-start-event') From eb1aea4cd1a8868fbf577081b26a9b7c8ac0a52d Mon Sep 17 00:00:00 2001 From: liranbaruch Date: Wed, 26 Aug 2020 10:26:52 +0300 Subject: [PATCH 0121/1476] Change ironsource to be lower case all over code (#5649) --- modules/ironsourceBidAdapter.js | 2 +- modules/ironsourceBidAdapter.md | 2 +- test/spec/modules/ironsourceBidAdapter_spec.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js index 795302762cd..34650d46a0f 100644 --- a/modules/ironsourceBidAdapter.js +++ b/modules/ironsourceBidAdapter.js @@ -4,7 +4,7 @@ import {VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; const SUPPORTED_AD_TYPES = [VIDEO]; -const BIDDER_CODE = 'ironSource'; +const BIDDER_CODE = 'ironsource'; const BIDDER_VERSION = '4.0.0'; const TTL = 360; const SELLER_ENDPOINT = 'https://hb.yellowblue.io/hb'; diff --git a/modules/ironsourceBidAdapter.md b/modules/ironsourceBidAdapter.md index 378a344b672..2f9e38b69e8 100644 --- a/modules/ironsourceBidAdapter.md +++ b/modules/ironsourceBidAdapter.md @@ -37,7 +37,7 @@ var adUnits = [ } }, bids: [{ - bidder: 'ironSource', + bidder: 'ironsource', params: { isOrg: '56f91cd4d3e3660002000033', // Required floorPrice: 2.00, // Optional diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js index 0ddc12f235c..cfdc51e0235 100644 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -56,7 +56,7 @@ describe('ironsourceAdapter', function () { ]; const bidderRequest = { - bidderCode: 'ironSource', + bidderCode: 'ironsource', } it('sends bid request to ENDPOINT via GET', function () { From 347be077efa160ddbf17626bd0f1800bef00cdd4 Mon Sep 17 00:00:00 2001 From: Itay Nave <38345760+itaynave@users.noreply.github.com> Date: Wed, 26 Aug 2020 10:48:57 +0300 Subject: [PATCH 0122/1476] aniviewBidAdapter - update renderer config (#5636) * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Fix Consent parameters * Update aniviewBidAdapter.js V3 support * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Update refererInfo * Update aniviewBidAdapter.js Fix tabs and spaces * Update aniviewBidAdapter.js fixes * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Add ccpa support * Update aniviewBidAdapter.js Typo * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js * Fix size and sample Fixed sizes from playerSize Updated md sample * Fix tabs * Fix sizes * Recheck * Add tgt parameter * Update sample * Add support for cookie sync + tests * Add support for cookie sync + tests * Add support for cookie sync + tests * Support aliases Support aliases * Update Update * Fix lint Fix lint * Update spec Update spec * Aniview Bid Adapter: Added the new alias * Aniview Bid Adapter: Added the new configs for the renderer * Aniview Bid Adapter: Added unit tests for the renderer * Aniview Bid Adapter: Have added gvlid Co-authored-by: Roman Shevchenko --- modules/aniviewBidAdapter.js | 24 ++++++++++++++++-- test/spec/modules/aniviewBidAdapter_spec.js | 27 +++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index e1f61a30abc..6b83c40897e 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -3,6 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'aniview'; +const GVLID = 780; const TTL = 600; function avRenderer(bid) { @@ -24,10 +25,28 @@ function avRenderer(bid) { } function newRenderer(bidRequest) { - let playerDomain = bidRequest && bidRequest.bidRequest && bidRequest.bidRequest.params && bidRequest.bidRequest.params.playerDomain ? bidRequest.bidRequest.params.playerDomain : 'player.aniview.com'; + let playerDomain = 'player.aniview.com'; + const config = {}; + + if (bidRequest && bidRequest.bidRequest && bidRequest.bidRequest.params) { + const params = bidRequest.bidRequest.params + + if (params.playerDomain) { + playerDomain = params.playerDomain; + } + + if (params.AV_PUBLISHERID) { + config.AV_PUBLISHERID = params.AV_PUBLISHERID; + } + + if (params.AV_CHANNELID) { + config.AV_CHANNELID = params.AV_CHANNELID; + } + } + const renderer = Renderer.install({ url: 'https://' + playerDomain + '/script/6.1/prebidRenderer.js', - config: {}, + config: config, loaded: false, }); @@ -252,6 +271,7 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, aliases: ['avantisvideo', 'selectmediavideo'], supportedMediaTypes: [VIDEO], isBidRequestValid, diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js index cca175c3388..2e1fdb56201 100644 --- a/test/spec/modules/aniviewBidAdapter_spec.js +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -177,6 +177,33 @@ describe('ANIVIEW Bid Adapter Test', function () { let result = spec.interpretResponse(nobidResponse, bidRequest); expect(result.length).to.equal(0); }); + + it('should add renderer if outstream context', function () { + const bidRequest = spec.buildRequests([ + { + bidId: '253dcb69fb2577', + params: { + playerDomain: 'example.com', + AV_PUBLISHERID: '55b78633181f4603178b4568', + AV_CHANNELID: '55b7904d181f46410f8b4568' + }, + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'outstream' + } + } + } + ])[0] + const bidResponse = spec.interpretResponse(serverResponse, bidRequest)[0] + + expect(bidResponse.renderer.url).to.equal('https://example.com/script/6.1/prebidRenderer.js') + expect(bidResponse.renderer.config.AV_PUBLISHERID).to.equal('55b78633181f4603178b4568') + expect(bidResponse.renderer.config.AV_CHANNELID).to.equal('55b7904d181f46410f8b4568') + expect(bidResponse.renderer.loaded).to.equal(false) + expect(bidResponse.width).to.equal(640) + expect(bidResponse.height).to.equal(480) + }) }); describe('getUserSyncs', function () { From 353d2c4324eec6a6076a311dae81f3ab9bf01b8c Mon Sep 17 00:00:00 2001 From: AbhijitBhosale72 <69898157+AbhijitBhosale72@users.noreply.github.com> Date: Wed, 26 Aug 2020 13:22:21 +0530 Subject: [PATCH 0123/1476] yuktamedia Analytics Adapter: added pageViewId, language & refererInfo (#5655) --- modules/yuktamediaAnalyticsAdapter.js | 37 +++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/modules/yuktamediaAnalyticsAdapter.js b/modules/yuktamediaAnalyticsAdapter.js index 3c27ca9754a..caca3bf3341 100644 --- a/modules/yuktamediaAnalyticsAdapter.js +++ b/modules/yuktamediaAnalyticsAdapter.js @@ -4,18 +4,36 @@ import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import * as utils from '../src/utils.js'; import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; const storage = getStorageManager(); -const yuktamediaAnalyticsVersion = 'v3.0.0'; +const yuktamediaAnalyticsVersion = 'v3.1.0'; let initOptions; -let auctionTimestamp; const events = { auctions: {} }; const localStoragePrefix = 'yuktamediaAnalytics_'; const utmTags = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']; +const location = utils.getWindowLocation(); +const referer = getRefererInfo().referer; +const _pageInfo = { + userAgent: window.navigator.userAgent, + timezoneOffset: new Date().getTimezoneOffset(), + language: window.navigator.language, + screenWidth: window.screen.width, + screenHeight: window.screen.height, + pageViewId: utils.generateUUID(), + host: location.host, + path: location.pathname, + search: location.search, + hash: location.hash, + referer: referer, + refererDomain: utils.parseUrl(referer).host, + yuktamediaAnalyticsVersion: yuktamediaAnalyticsVersion, + prebidVersion: $$PREBID_GLOBAL$$.version +}; function getParameterByName(param) { let vars = {}; @@ -60,20 +78,12 @@ function isUtmTimeoutExpired() { } function send(data, status) { - const location = utils.getWindowLocation(); - data.initOptions = Object.assign({ host: location.host, path: location.pathname, search: location.search }, initOptions); - + data.initOptions = Object.assign(_pageInfo, initOptions); const yuktamediaAnalyticsRequestUrl = utils.buildUrl({ protocol: 'https', hostname: 'analytics-prebid.yuktamedia.com', - pathname: '/api/bids', - search: { - auctionTimestamp: auctionTimestamp, - yuktamediaAnalyticsVersion: yuktamediaAnalyticsVersion, - prebidVersion: $$PREBID_GLOBAL$$.version - } + pathname: '/api/bids' }); - if (isNavigatorSendBeaconSupported()) { window.navigator.sendBeacon(yuktamediaAnalyticsRequestUrl, JSON.stringify(data)); } else { @@ -81,7 +91,7 @@ function send(data, status) { } } -var yuktamediaAnalyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}), { +var yuktamediaAnalyticsAdapter = Object.assign(adapter({ analyticsType: 'endpoint' }), { track({ eventType, args }) { if (typeof args !== 'undefined') { switch (eventType) { @@ -89,7 +99,6 @@ var yuktamediaAnalyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint utils.logInfo(localStoragePrefix + 'AUCTION_INIT:', JSON.stringify(args)); if (typeof args.auctionId !== 'undefined' && args.auctionId.length) { events.auctions[args.auctionId] = { bids: {} }; - auctionTimestamp = args.timestamp; } break; case CONSTANTS.EVENTS.BID_REQUESTED: From d38b5d017e658aaebe28e846318f522fd0dfacc6 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Wed, 26 Aug 2020 20:14:04 +0530 Subject: [PATCH 0124/1476] fixed running of single spec file (#5648) Co-authored-by: monis.q --- karma.conf.maker.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 712ef14caa1..8af216d6262 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -170,6 +170,16 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { plugins: plugins } + + // To ensure that, we are able to run single spec file + // here we are adding preprocessors, when file is passed + if (file) { + config.files.forEach((file) => { + config.preprocessors[file] = ['webpack', 'sourcemap']; + }); + delete config.preprocessors['test/test_index.js']; + } + setReporters(config, codeCoverage, browserstack); setBrowsers(config, browserstack); return config; From a234c874ea9cc2aa0f1cad2a06ade4773a6b3d87 Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Wed, 26 Aug 2020 14:00:56 -0700 Subject: [PATCH 0125/1476] RP Bid Adapter read user.id (#5666) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * pass config user.id to get and xapi requests Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/rubiconBidAdapter.js | 12 +++ test/spec/modules/rubiconBidAdapter_spec.js | 88 ++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9f5c2ec10d5..b5d03133dde 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -316,6 +316,12 @@ export const spec = { } } + // set user.id value from config value + const configUserId = config.getConfig('user.id'); + if (configUserId) { + utils.deepSetValue(data, 'user.id', configUserId); + } + if (config.getConfig('coppa') === true) { utils.deepSetValue(data, 'regs.coppa', 1); } @@ -587,6 +593,12 @@ export const spec = { } } + // set ppuid value from config value + const configUserId = config.getConfig('user.id'); + if (configUserId) { + data['ppuid'] = configUserId; + } + if (bidderRequest.gdprConsent) { // add 'gdpr' only if 'gdprApplies' is defined if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 49a3f60bac7..cbc3e5bbd8e 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1387,7 +1387,24 @@ describe('the rubicon adapter', function () { expect(data['eid_sharedid.org']).to.equal('1111^3^2222'); }); }); - }) + + describe('Config user.id support', function () { + it('should send ppuid when config defines user.id', function () { + config.setConfig({ user: { id: '123' } }); + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + sharedid: { + id: '1111', + third: '2222' + } + }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['ppuid']).to.equal('123'); + }); + }); + }); describe('Prebid AdSlot', function () { beforeEach(function () { @@ -2023,6 +2040,75 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.ext.prebid.bidders.rubicon.integration).to.equal('testType'); }); + + it('should pass the user.id provided in the config', function () { + config.setConfig({ user: { id: '123' } }); + createVideoBidderRequest(); + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = request.data; + + expect(post).to.have.property('imp') + // .with.length.of(1); + let imp = post.imp[0]; + expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(imp.exp).to.equal(300); + expect(imp.video.w).to.equal(640); + expect(imp.video.h).to.equal(480); + expect(imp.video.pos).to.equal(1); + expect(imp.video.context).to.equal('instream'); + expect(imp.video.minduration).to.equal(15); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.startdelay).to.equal(0); + expect(imp.video.skip).to.equal(1); + expect(imp.video.skipafter).to.equal(15); + expect(imp.ext.rubicon.video.playerWidth).to.equal(640); + expect(imp.ext.rubicon.video.playerHeight).to.equal(480); + expect(imp.ext.rubicon.video.size_id).to.equal(201); + expect(imp.ext.rubicon.video.language).to.equal('en'); + // Also want it to be in post.site.content.language + expect(post.site.content.language).to.equal('en'); + expect(imp.ext.rubicon.video.skip).to.equal(1); + expect(imp.ext.rubicon.video.skipafter).to.equal(15); + expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); + expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); + expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); + expect(post.user.ext.tpid).that.is.an('object'); + expect(post.user.ext.tpid.source).to.equal('liveintent.com'); + expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); + // LiveRamp should exist + expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); + expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + // SharedId should exist + expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); + expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); + expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); + + expect(post.rp).that.is.an('object'); + expect(post.rp.target).that.is.an('object'); + expect(post.rp.target.LIseg).that.is.an('array'); + expect(post.rp.target.LIseg[0]).to.equal('segA'); + expect(post.rp.target.LIseg[1]).to.equal('segB'); + + // Config user.id + expect(post.user.id).to.equal('123'); + + expect(post.regs.ext.gdpr).to.equal(1); + expect(post.regs.ext.us_privacy).to.equal('1NYN'); + expect(post).to.have.property('ext').that.is.an('object'); + expect(post.ext.prebid.targeting.includewinners).to.equal(true); + expect(post.ext.prebid).to.have.property('cache').that.is.an('object'); + expect(post.ext.prebid.cache).to.have.property('vastxml').that.is.an('object'); + expect(post.ext.prebid.cache.vastxml).to.have.property('returnCreative').that.is.an('boolean'); + expect(post.ext.prebid.cache.vastxml.returnCreative).to.equal(false); + expect(post.ext.prebid.bidders.rubicon.integration).to.equal(PBS_INTEGRATION); + }) }); describe('combineSlotUrlParams', function () { From aaae81fdc45d859e4a1ea56a17cdaf0c846ccdc3 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 26 Aug 2020 14:03:05 -0700 Subject: [PATCH 0126/1476] add bidResponse object to cpmAdjustment calculator (#5609) --- modules/priceFloors.js | 6 ++--- test/spec/modules/priceFloors_spec.js | 33 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index eb1f3aed84c..2acd21918ef 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -138,10 +138,10 @@ function generatePossibleEnumerations(arrayOfFields, delimiter) { /** * @summary If a the input bidder has a registered cpmadjustment it returns the input CPM after being adjusted */ -export function getBiddersCpmAdjustment(bidderName, inputCpm) { +export function getBiddersCpmAdjustment(bidderName, inputCpm, bid = {}) { const adjustmentFunction = utils.deepAccess(getGlobal(), `bidderSettings.${bidderName}.bidCpmAdjustment`); if (adjustmentFunction) { - return parseFloat(adjustmentFunction(inputCpm)); + return parseFloat(adjustmentFunction(inputCpm, {...bid, cpm: inputCpm})); } return parseFloat(inputCpm); } @@ -682,7 +682,7 @@ export function addBidResponseHook(fn, adUnitCode, bid) { } // ok we got the bid response cpm in our desired currency. Now we need to run the bidders CPMAdjustment function if it exists - adjustedCpm = getBiddersCpmAdjustment(bid.bidderCode, adjustedCpm); + adjustedCpm = getBiddersCpmAdjustment(bid.bidderCode, adjustedCpm, bid); // add necessary data information for analytics adapters / floor providers would possibly need addFloorDataToBid(floorData, floorInfo, bid, adjustedCpm); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 0a006e0eb48..d4ac2e5ad72 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -1101,6 +1101,39 @@ describe('the price floors module', function () { floor: 1.3334 // 1.3334 * 0.75 = 1.000005 which is the floor (we cut off getFloor at 4 decimal points) }); }); + it('should work when cpmAdjust function uses bid object', function () { + getGlobal().bidderSettings = { + rubicon: { + bidCpmAdjustment: function (bidCpm, bidResponse) { + return bidResponse.cpm * 0.5; + }, + }, + appnexus: { + bidCpmAdjustment: function (bidCpm, bidResponse) { + return bidResponse.cpm * 0.75; + }, + } + }; + _floorDataForAuction[bidRequest.auctionId] = utils.deepClone(basicFloorConfig); + _floorDataForAuction[bidRequest.auctionId].data.values = { '*': 1.0 }; + let appnexusBid = { + ...bidRequest, + bidder: 'appnexus' + }; + + // the conversion should be what the bidder would need to return in order to match the actual floor + // rubicon + expect(bidRequest.getFloor()).to.deep.equal({ + currency: 'USD', + floor: 2.0 // a 2.0 bid after rubicons cpm adjustment would be 1.0 and thus is the floor after adjust + }); + + // appnexus + expect(appnexusBid.getFloor()).to.deep.equal({ + currency: 'USD', + floor: 1.3334 // 1.3334 * 0.75 = 1.000005 which is the floor (we cut off getFloor at 4 decimal points) + }); + }); it('should correctly pick the right attributes if * is passed in and context can be assumed', function () { let inputBidReq = { bidder: 'rubicon', From ae956bea9ac08351b80f5c167ae127d5800972ae Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Thu, 27 Aug 2020 09:27:16 -0700 Subject: [PATCH 0127/1476] RP Bid Adapter: Use EID data set from userId/eids.js (#5657) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * update liveramp userid support * changed source value to all lowercase * update share id name * add unit test for shareid eid * update shareid obj paths * fixed atype value * update to use eids set by userId/eids.js * update eid to include ext object for adserver.org * optimize remove lines * optimize for line reduction * optimization * optimization * fixed liveramp missing property * fixed import ordering * reverted change * undo revert * fix tests expected values * added fields to expected test results * refactor to user eids.js to mock test data * optimize code to reduce lines * removed extra braces * removed extra lines and renamed userIdAsEids to eids * add tests for all supported sources * removed unnecessary comments * udate condition to check for eids length * removed liveramp.com from eidMap, and refactored to use single variable Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/rubiconBidAdapter.js | 77 ++++----------------- test/spec/modules/rubiconBidAdapter_spec.js | 73 +++++++++---------- 2 files changed, 50 insertions(+), 100 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b5d03133dde..4a0dd10281b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -2,6 +2,7 @@ import * as utils 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 find from 'core-js-pure/features/array/find.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; @@ -247,73 +248,19 @@ export const spec = { utils.deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedid)) { - utils.deepSetValue(data, 'user.ext.eids', []); - - if (bidRequest.userId.tdid) { - data.user.ext.eids.push({ - source: 'adserver.org', - uids: [{ - id: bidRequest.userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } - - if (bidRequest.userId.pubcid) { - data.user.ext.eids.push({ - source: 'pubcommon', - uids: [{ - id: bidRequest.userId.pubcid, - }] - }); - } - - // support liveintent ID - if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { - data.user.ext.eids.push({ - source: 'liveintent.com', - uids: [{ - id: bidRequest.userId.lipb.lipbid - }] - }); - - data.user.ext.tpid = { - source: 'liveintent.com', - uid: bidRequest.userId.lipb.lipbid - }; - - if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { - utils.deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); + const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); + if (eids && eids.length) { + // filter out unsupported id systems + utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); + + // liveintent requires additional props to be set + const liveIntentEid = find(data.user.ext.eids, eid => eid.source === 'liveintent.com'); + if (liveIntentEid) { + utils.deepSetValue(data, 'user.ext.tpid', { source: liveIntentEid.source, uid: liveIntentEid.uids[0].id }); + if (liveIntentEid.ext && liveIntentEid.ext.segments) { + utils.deepSetValue(data, 'rp.target.LIseg', liveIntentEid.ext.segments); } } - - // support identityLink (aka LiveRamp) - if (bidRequest.userId.idl_env) { - data.user.ext.eids.push({ - source: 'liveramp_idl', - uids: [{ - id: bidRequest.userId.idl_env - }] - }); - } - - // support shared id - if (bidRequest.userId.sharedid) { - data.user.ext.eids.push({ - source: 'sharedid.org', - uids: [{ - id: bidRequest.userId.sharedid.id, - atype: 3, - ext: { - third: bidRequest.userId.sharedid.third - } - }] - }); - } } // set user.id value from config value diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index cbc3e5bbd8e..c28af4a3f9b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -4,6 +4,7 @@ import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import find from 'core-js-pure/features/array/find.js'; +import { createEidsArray } from 'modules/userId/eids.js'; const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid const PBS_INTEGRATION = 'pbjs'; @@ -219,16 +220,13 @@ describe('the rubicon adapter', function () { 'size_id': 201, }; bid.userId = { - lipb: { - lipbid: '0000-1111-2222-3333', - segments: ['segA', 'segB'] - }, + lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, idl_env: '1111-2222-3333-4444', - sharedid: { - id: '1111', - third: '2222' - } + sharedid: { id: '1111', third: '2222' }, + tdid: '3000', + pubcid: '4000' }; + bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; } @@ -1323,6 +1321,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1337,6 +1336,7 @@ describe('the rubicon adapter', function () { lipbid: '0000-1111-2222-3333' } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1351,6 +1351,7 @@ describe('the rubicon adapter', function () { segments: ['segD', 'segE'] } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); const unescapedData = unescape(request.data); @@ -1365,6 +1366,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { idl_env: '1111-2222-3333-4444' }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1381,6 +1383,7 @@ describe('the rubicon adapter', function () { third: '2222' } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1590,25 +1593,44 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + // EIDs should exist + expect(post.user.ext).to.have.property('eids').that.is.an('array'); + // LiveIntent should exist expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); - expect(post.user.ext.tpid).that.is.an('object'); + expect(post.user.ext.eids[0].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[0]).to.have.property('ext').that.is.an('object'); + expect(post.user.ext.eids[0].ext).to.have.property('segments').that.is.an('array'); + expect(post.user.ext.eids[0].ext.segments[0]).to.equal('segA'); + expect(post.user.ext.eids[0].ext.segments[1]).to.equal('segB'); + // Non-EID properties set using liveintent EID values + expect(post.user.ext).to.have.property('tpid').that.is.an('object'); expect(post.user.ext.tpid.source).to.equal('liveintent.com'); expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); + expect(post).to.have.property('rp').that.is.an('object'); + expect(post.rp).to.have.property('target').that.is.an('object'); + expect(post.rp.target).to.have.property('LIseg').that.is.an('array'); + expect(post.rp.target.LIseg[0]).to.equal('segA'); + expect(post.rp.target.LIseg[1]).to.equal('segB'); // LiveRamp should exist - expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); + expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + expect(post.user.ext.eids[1].uids[0].atype).to.equal(1); + // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); + // UnifiedId should exist + expect(post.user.ext.eids[3].source).to.equal('adserver.org'); + expect(post.user.ext.eids[3].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[3].uids[0].id).to.equal('3000'); + // PubCommonId should exist + expect(post.user.ext.eids[4].source).to.equal('pubcid.org'); + expect(post.user.ext.eids[4].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[4].uids[0].id).to.equal('4000'); - expect(post.rp).that.is.an('object'); - expect(post.rp.target).that.is.an('object'); - expect(post.rp.target.LIseg).that.is.an('array'); - expect(post.rp.target.LIseg[0]).to.equal('segA'); - expect(post.rp.target.LIseg[1]).to.equal('segB'); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); expect(post).to.have.property('ext').that.is.an('object'); @@ -2076,25 +2098,6 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); - expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); - expect(post.user.ext.tpid).that.is.an('object'); - expect(post.user.ext.tpid.source).to.equal('liveintent.com'); - expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); - // LiveRamp should exist - expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); - expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); - // SharedId should exist - expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); - expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); - expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); - - expect(post.rp).that.is.an('object'); - expect(post.rp.target).that.is.an('object'); - expect(post.rp.target.LIseg).that.is.an('array'); - expect(post.rp.target.LIseg[0]).to.equal('segA'); - expect(post.rp.target.LIseg[1]).to.equal('segB'); // Config user.id expect(post.user.id).to.equal('123'); From 4274342b667a8671d3aec720bc8319054ca707a7 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 27 Aug 2020 11:02:03 -0700 Subject: [PATCH 0128/1476] Prebid 4.5.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af36566b95b..c850132c6f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.5.0-pre", + "version": "4.5.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 3f0ae076cb73becfce1d219a1a1a94cea0f9b68e Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 27 Aug 2020 11:20:30 -0700 Subject: [PATCH 0129/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c850132c6f9..2a0f4077462 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.5.0", + "version": "4.6.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2b05803d191368ffe46d6771c18c121799890bf1 Mon Sep 17 00:00:00 2001 From: Sleiman Jneidi Date: Fri, 28 Aug 2020 10:00:14 +0100 Subject: [PATCH 0130/1476] qcadapter - tcf2 remove germany specific logic (#5664) --- modules/quantcastBidAdapter.js | 5 ----- test/spec/modules/quantcastBidAdapter_spec.js | 20 ------------------- 2 files changed, 25 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 91022d70df9..894bb991a71 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -85,11 +85,6 @@ function checkTCFv1(vendorData) { } function checkTCFv2(tcData) { - if (tcData.purposeOneTreatment && tcData.publisherCC === 'DE') { - // special purpose 1 treatment for Germany - return true; - } - let restrictions = tcData.publisher ? tcData.publisher.restrictions : {}; let qcRestriction = restrictions && restrictions[PURPOSE_DATA_COLLECT] ? restrictions[PURPOSE_DATA_COLLECT][QUANTCAST_VENDOR_ID] diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index cd168ec61e6..caa554c8cd8 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -430,26 +430,6 @@ describe('Quantcast adapter', function () { expect(requests).to.equal(undefined); }); - it('allows TCF v2 request from Germany for purpose 1', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - publisherCC: 'DE', - purposeOneTreatment: true - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - const parsed = JSON.parse(requests[0].data); - - expect(parsed.gdprSignal).to.equal(1); - expect(parsed.gdprConsent).to.equal('consentString'); - }); - it('allows TCF v2 request when Quantcast has consent for purpose 1', function() { const bidderRequest = { gdprConsent: { From 3d4e25d1bd076f85785bf92ea3bd2891df450669 Mon Sep 17 00:00:00 2001 From: angelamerkelprebid <69318193+angelamerkelprebid@users.noreply.github.com> Date: Fri, 28 Aug 2020 05:58:28 -0400 Subject: [PATCH 0131/1476] merkleId Identity submodule submission (#5577) * Create merkleIdSystem.js * Update userId_example.html * Update eids.md * Update userId_spec.js * Update eids.md fix to merkleinc.com * fix string break in gdpr applies warning * Update merkleIdSystem.js add consentData as param to getId * Update merkleIdSystem.js remove useless conditional part * Update userId_spec.js fix test strings to match * Update merkleIdSystem.js fixes decode function * Update merkleIdSystem.js change back decode * Update userId_spec.js fix set cookie format * Update userId_spec.js fix space before value * Update eids_spec.js * Update eids_spec.js fix spacing issue * Update merkleIdSystem.js fix decode again * Update merkleIdSystem.js typo * Update merkleIdSystem.js * Update merkleIdSystem.js * Update merkleIdSystem.js * Update .submodules.json * Update eids.js * Update eids_spec.js * Update eids.js --- integrationExamples/gpt/userId_example.html | 16 ++++- modules/.submodules.json | 1 + modules/merkleIdSystem.js | 80 +++++++++++++++++++++ modules/userId/eids.js | 6 ++ modules/userId/eids.md | 8 +++ test/spec/modules/eids_spec.js | 12 ++++ test/spec/modules/userId_spec.js | 38 ++++++++-- 7 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 modules/merkleIdSystem.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 4acbe0aae31..9307bc91456 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -115,7 +115,7 @@ consentManagement: { cmpApi: 'iab', timeout: 1000, - allowAuctionWithoutConsent: true + defaultGdprScope: true }, // consentManagement: { // cmpApi: 'static', @@ -128,7 +128,7 @@ // } // } // }, - usersync: { + userSync: { userIds: [{ name: "unifiedId", params: { @@ -164,6 +164,18 @@ }, }, { + name: "merkleId", + params: { + ptk: '12345678-aaaa-bbbb-cccc-123456789abc', //Set your real merkle partner key here + pubid: 'EXAMPLE' //Set your real merkle publisher id here + }, + storage: { + type: "html5", + name: "merkleId", + expires: 30 + }, + + },{ name: "parrableId", params: { // change to Parrable Partner Client ID(s) you received from the Parrable Partners you are using diff --git a/modules/.submodules.json b/modules/.submodules.json index af7806616e1..50d17fc5f6c 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -7,6 +7,7 @@ "britepoolIdSystem", "liveIntentIdSystem", "lotamePanoramaId", + "merkleIdSystem", "criteoIdSystem", "netIdSystem", "identityLinkIdSystem", diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js new file mode 100644 index 00000000000..d6bf96618df --- /dev/null +++ b/modules/merkleIdSystem.js @@ -0,0 +1,80 @@ +/** + * This module adds merkleId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/merkleIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js' + +const MODULE_NAME = 'merkleId'; + +/** @type {Submodule} */ +export const merkleIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{merkleId:string}} + */ + decode(value) { + const id = (value && value.ppid && typeof value.ppid.id === 'string') ? value.ppid.id : undefined; + return id ? { 'merkleId': id } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ + getId(configParams, consentData) { + if (!configParams || typeof configParams.pubid !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid pubid to be defined'); + return; + } + + if (typeof configParams.ptk !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid ptk string to be defined'); + return; + } + + if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { + utils.logError('User ID - merkleId submodule does not currently handle consent strings'); + return; + } + + const url = `https://mid.rkdms.com/idsv2?ptk=${configParams.ptk}&pubid=${configParams.pubid}`; + + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, + error: error => { + utils.logError(`${MODULE_NAME}: merkleId fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); + }; + return {callback: resp}; + } +}; + +submodule('userId', merkleIdSubmodule); diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 72be98eca50..15399b9b980 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -100,6 +100,12 @@ const USER_IDS_CONFIG = { atype: 1 }, + // merkleId + 'merkleId': { + source: 'merkleinc.com', + atype: 1 + }, + // NetId 'netId': { source: 'netid.de', diff --git a/modules/userId/eids.md b/modules/userId/eids.md index f42b7e61a52..846b9b19207 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -55,6 +55,14 @@ userIdAsEids = [ } }, + { + source: 'merkleinc.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { source: 'britepool.com', uids: [{ diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 434e1841a0f..8ad44f0b1ad 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -55,6 +55,18 @@ describe('eids array generation for known sub-modules', function() { }); }); + it('merkleId', function() { + const userId = { + merkleId: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'merkleinc.com', + uids: [{id: 'some-random-id-value', atype: 1}] + }); + }); + it('identityLink', function() { const userId = { idl_env: 'some-random-id-value' diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index fd7e8a76972..5ac68de345d 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -23,6 +23,7 @@ import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; import {id5IdSubmodule} from 'modules/id5IdSystem.js'; import {identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; import {liveIntentIdSubmodule} from 'modules/liveIntentIdSystem.js'; +import {merkleIdSubmodule} from 'modules/merkleIdSystem.js'; import {netIdSubmodule} from 'modules/netIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; @@ -34,7 +35,7 @@ const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const CONSENT_LOCAL_STORAGE_NAME = '_pbjs_userid_consent_data'; describe('User ID', function() { - function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8) { + function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8, configArr9) { return { userSync: { syncDelay: 0, @@ -46,7 +47,8 @@ describe('User ID', function() { (configArr5 && configArr5.length >= 3) ? getStorageMock.apply(null, configArr5) : null, (configArr6 && configArr6.length >= 3) ? getStorageMock.apply(null, configArr6) : null, (configArr7 && configArr7.length >= 3) ? getStorageMock.apply(null, configArr7) : null, - (configArr8 && configArr8.length >= 3) ? getStorageMock.apply(null, configArr8) : null + (configArr8 && configArr8.length >= 3) ? getStorageMock.apply(null, configArr8) : null, + (configArr9 && configArr9.length >= 3) ? getStorageMock.apply(null, configArr9) : null ].filter(i => i) } } @@ -352,7 +354,7 @@ describe('User ID', function() { }); it('handles config with no usersync object', function() { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -360,14 +362,14 @@ describe('User ID', function() { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -378,7 +380,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -1116,6 +1118,30 @@ describe('User ID', function() { }, {adUnits}); }); + it('test hook from merkleId cookies', function(done) { + // simulate existing browser local storage values + coreStorage.setCookie('merkleId', JSON.stringify({'ppid': {'id': 'testmerkleId'}}), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([merkleIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['merkleId', 'merkleId', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.merkleId'); + expect(bid.userId.merkleId).to.equal('testmerkleId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'merkleinc.com', + uids: [{id: 'testmerkleId', atype: 1}] + }); + }); + }); + coreStorage.setCookie('merkleId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, sharedId and netId have data to pass', function(done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); From 2d3b3bd4dff17f76f8bf0e64d2475fe76a3ef947 Mon Sep 17 00:00:00 2001 From: CPMStar Date: Fri, 28 Aug 2020 11:49:57 -0700 Subject: [PATCH 0132/1476] Added schain support and usersync output to cpmstarBidAdapter (#5660) * Added CPMStar Bid Adapter * Updated getPlayerSize for cpmstarBidAdapter * Improved cpmstarBidAdapter code coverage * updated test spec, removed empty functions, made imports relative, added warnings to erroneous server responses, and removed the default value for ad in bid response. * added test video ad unit * added support for gdpr and coppa * changed != undefined to != null * changed let to var * added unit for GDPR, COPPA, and USP. * added support for usersync and schain along with updated unit tests * merged schain support * Revert "merged schain support" This reverts commit cdbe4c275dbd111110fa5a66e7796c9518997812. * merged schain support * merged schain test * merged schain and usersync support Co-authored-by: Nicholas Elek --- modules/cpmstarBidAdapter.js | 39 ++++++- test/spec/modules/cpmstarBidAdapter_spec.js | 113 ++++++++++++++------ 2 files changed, 116 insertions(+), 36 deletions(-) mode change 100755 => 100644 modules/cpmstarBidAdapter.js mode change 100755 => 100644 test/spec/modules/cpmstarBidAdapter_spec.js diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js old mode 100755 new mode 100644 index b416c00c2d0..61ccc0e786b --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -13,6 +13,12 @@ const ENDPOINT_PRODUCTION = 'https://server.cpmstar.com/view.aspx'; const DEFAULT_TTL = 300; const DEFAULT_CURRENCY = 'USD'; +function fixedEncodeURIComponent(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16); + }); +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], @@ -47,13 +53,29 @@ export const spec = { 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=' + utils.getBidIdParameter('placementId', bidRequest.params) + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + '&requestid=' + bidRequest.bidId + '&referer=' + encodeURIComponent(referer); + if (bidRequest.schain && bidRequest.schain.nodes) { + var schain = bidRequest.schain; + var schainString = ''; + schainString += schain.ver + ',' + schain.complete; + for (var i2 = 0; i2 < schain.nodes.length; i2++) { + var node = schain.nodes[i2]; + schainString += '!' + + fixedEncodeURIComponent(node.asi || '') + ',' + + fixedEncodeURIComponent(node.sid || '') + ',' + + fixedEncodeURIComponent(node.hp || '') + ',' + + fixedEncodeURIComponent(node.rid || '') + ',' + + fixedEncodeURIComponent(node.name || '') + ',' + + fixedEncodeURIComponent(node.domain || ''); + } + url += '&schain=' + schainString + } + if (bidderRequest.gdprConsent) { if (bidderRequest.gdprConsent.consentString != null) { url += '&gdpr_consent=' + bidderRequest.gdprConsent.consentString; @@ -138,6 +160,21 @@ export const spec = { } return bidResponses; + }, + + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + if (serverResponses.length == 0 || !serverResponses[0].body) return syncs; + var usersyncs = serverResponses[0].body[0].syncs; + if (!usersyncs || usersyncs.length < 0) return syncs; + for (var i = 0; i < usersyncs.length; i++) { + var us = usersyncs[i]; + if ((us.type === 'image' && syncOptions.pixelEnabled) || (us.type == 'iframe' && syncOptions.iframeEnabled)) { + syncs.push(us); + } + } + return syncs; } + }; registerBidder(spec); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js old mode 100755 new mode 100644 index 1993f28e5d5..285fca9690a --- a/test/spec/modules/cpmstarBidAdapter_spec.js +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -3,6 +3,41 @@ import { spec } from 'modules/cpmstarBidAdapter.js'; import { deepClone } from 'src/utils.js'; import { config } from 'src/config.js'; +const valid_bid_requests = [{ + 'bidder': 'cpmstar', + 'params': { + 'placementId': '57' + }, + 'sizes': [[300, 250]], + 'bidId': 'bidId' +}]; + +const bidderRequest = { + refererInfo: { + referer: 'referer', + reachedTop: false, + } +}; + +const serverResponse = { + body: [{ + creatives: [{ + cpm: 1, + width: 0, + height: 0, + currency: 'USD', + netRevenue: true, + ttl: 1, + creativeid: '1234', + requestid: '11123', + code: 'no idea', + media: 'banner', + } + ], + syncs: [{ type: 'image', url: 'https://server.cpmstar.com/pixel.aspx' }] + }] +}; + describe('Cpmstar Bid Adapter', function () { describe('isBidRequestValid', function () { it('should return true since the bid is valid', @@ -42,23 +77,6 @@ describe('Cpmstar Bid Adapter', function () { }); describe('buildRequests', function () { - const valid_bid_requests = [{ - 'bidder': 'cpmstar', - 'params': { - 'placementId': '57' - }, - 'sizes': [[300, 250]], - 'bidId': 'bidId' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'referer', - reachedTop: false, - } - - }; - it('should produce a valid production request', function () { var requests = spec.buildRequests(valid_bid_requests, bidderRequest); expect(requests[0]).to.have.property('method'); @@ -109,7 +127,30 @@ describe('Cpmstar Bid Adapter', function () { expect(requests[0]).to.have.property('url'); expect(requests[0].url).to.include('tfcd=1'); }); - }) + }); + + it('should produce a request with support for OpenRTB SupplyChain', function () { + var reqs = deepClone(valid_bid_requests); + reqs[0].schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1 + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1 + } + ] + }; + var requests = spec.buildRequests(reqs, bidderRequest); + expect(requests[0]).to.have.property('url'); + expect(requests[0].url).to.include('&schain=1.0,1!exchange1.com,1234,1,,,!exchange2.com,abcd,1,,,'); + }); describe('interpretResponse', function () { const request = { @@ -117,23 +158,6 @@ describe('Cpmstar Bid Adapter', function () { mediaType: 'BANNER' } }; - const serverResponse = { - body: [{ - creatives: [{ - cpm: 1, - width: 0, - height: 0, - currency: 'USD', - netRevenue: true, - ttl: 1, - creativeid: '1234', - requestid: '11123', - code: 'no idea', - media: 'banner', - } - ], - }] - }; it('should return a valid bidresponse array', function () { var r = spec.interpretResponse(serverResponse, request) @@ -185,4 +209,23 @@ describe('Cpmstar Bid Adapter', function () { expect(spec.interpretResponse(dealServer, request)[0].dealId).to.equal('deal'); }); }); + + describe('getUserSyncs', function () { + var sres = [deepClone(serverResponse)]; + + it('should return a valid pixel sync', function () { + var syncs = spec.getUserSyncs({ pixelEnabled: true }, sres); + expect(syncs.length).equal(1); + expect(syncs[0].type).equal('image'); + expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); + }); + + it('should return a valid iframe sync', function () { + sres[0].body[0].syncs[0].type = 'iframe'; + var syncs = spec.getUserSyncs({ iframeEnabled: true }, sres); + expect(syncs.length).equal(1); + expect(syncs[0].type).equal('iframe'); + expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); + }); + }); }); From 6c41243d7baa1ecda2c45474609d5d4fa9a76214 Mon Sep 17 00:00:00 2001 From: Jimmy Tu Date: Mon, 31 Aug 2020 09:12:03 -0700 Subject: [PATCH 0133/1476] OpenX: Analytics Adapter update (#5449) --- modules/openxAnalyticsAdapter.js | 860 ++++++++++++++---- modules/openxAnalyticsAdapter.md | 10 +- .../modules/openxAnalyticsAdapter_spec.js | 738 +++++++++------ 3 files changed, 1134 insertions(+), 474 deletions(-) diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 7addfe68bc6..3ee8ab588a9 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -1,260 +1,730 @@ import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import { config } from '../src/config.js'; import { ajax } from '../src/ajax.js'; -import * as utils from '../src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +const utils = require('../src/utils.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, BID_WON } + EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, AUCTION_END, BID_WON } } = CONSTANTS; - const SLOT_LOADED = 'slotOnload'; -const ENDPOINT = 'https://ads.openx.net/w/1.0/pban'; +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' +}; -let initOptions; +/** + * @typedef {Object} OxAnalyticsConfig + * @property {string} orgId + * @property {string} publisherPlatformId + * @property {number} publisherAccountId + * @property {number} sampling + * @property {boolean} enableV2 + * @property {boolean} testPipeline + * @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 -function onAuctionInit({ auctionId }) { - auctionMap[auctionId] = { - adUnitMap: {} - }; +let googletag = window.googletag || {}; +googletag.cmd = googletag.cmd || []; + +let openxAdapter = Object.assign(adapter({ urlParam: URL_PARAM, analyticsType: ANALYTICS_TYPE })); + +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}; + + utils.logInfo('OpenX Analytics enabled with config', analyticsConfig); + + // override track method with v2 handlers + 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}); + utils.logInfo('OX: SlotOnLoad event triggered'); + }); + } + }); + + openxAdapter.originEnableAnalytics(adapterConfig); + } +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: openxAdapter, + code: 'openx' +}); + +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], + ['sampling', 'number', false], + ['enableV2', 'boolean', false], + ['testPipeline', 'boolean', 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) { + utils.logError(`OpenXAnalyticsAdapter: Expected '${property}' to exist and of type '${type}'`); + } else { + utils.logError(`OpenXAnalyticsAdapter: Expected '${property}' to be type '${type}'`); + } + } + + return !failedValidation; } -function onBidRequested({ auctionId, auctionStart, bids, start }) { - const adUnitMap = auctionMap[auctionId]['adUnitMap']; +function buildCampaignFromUtmCodes() { + let campaign = {}; + let queryParams = utils.parseQS(utils.getWindowLocation() && utils.getWindowLocation().search); - bids.forEach(bid => { - const { adUnitCode, bidId, bidder, params, transactionId } = bid; + UTM_TAGS.forEach(function(utmKey) { + let utmValue = queryParams[utmKey]; + if (utmValue) { + let key = UTM_TO_CAMPAIGN_PROPERTIES[utmKey]; + campaign[key] = 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'; +} - adUnitMap[adUnitCode] = adUnitMap[adUnitCode] || { - auctionId, - auctionStart, - transactionId, - bidMap: {} +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}) { + utils.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, enableV2: true}/* + */ + +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++; +} - adUnitMap[adUnitCode]['bidMap'][bidId] = { +/** + * @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} = 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, - requestTimestamp: start + mediaTypes, + source: src, + startTime: start, + timedOut: false, + bids: {} }; }); } -function onBidResponse({ - auctionId, - adUnitCode, - requestId: bidId, - cpm, - creativeId, - responseTimestamp, - ts, - adId -}) { - const adUnit = auctionMap[auctionId]['adUnitMap'][adUnitCode]; - const bid = adUnit['bidMap'][bidId]; - bid.cpm = cpm; - bid.creativeId = creativeId; - bid.responseTimestamp = responseTimestamp; - bid.ts = ts; - bid.adId = adId; +/** + * + * @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) { - utils - ._map(args, value => value) - .forEach(({ auctionId, adUnitCode, bidId }) => { - const bid = - auctionMap[auctionId]['adUnitMap'][adUnitCode]['bidMap'][bidId]; - bid.timedOut = true; - }); + utils._each(args, ({auctionId, adUnitCode, bidId: requestId}) => { + let timedOutRequest = utils.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; + } -function onBidWon({ auctionId, adUnitCode, requestId: bidId }) { - const adUnit = auctionMap[auctionId]['adUnitMap'][adUnitCode]; - const bid = adUnit['bidMap'][bidId]; - bid.won = true; + clearAuctionTimer(auction); + auction.endTime = endedAuction.auctionEnd; + auction.state = AUCTION_STATES.ENDED; + delayedSend(auction); } -function onSlotLoaded({ slot }) { - const targeting = slot.getTargetingKeys().reduce((targeting, key) => { - targeting[key] = slot.getTargeting(key); - return targeting; - }, {}); - utils.logMessage( - 'GPT slot is loaded. Current targeting set on slot:', - targeting - ); +/** + * + * @param {BidResponse} bidResponse + */ +function onBidWon(bidResponse) { + const { auctionId, adUnitCode, requestId, adId } = bidResponse; + let winningBid = utils.deepAccess(auctionMap, + `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}.bids.${adId}`); + + if (winningBid) { + winningBid.winner = true + } +} - const adId = slot.getTargeting('hb_adid')[0]; - if (!adId) { - return; +/** + * + * @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 + } } - const adUnit = getAdUnitByAdId(adId); - if (!adUnit) { - return; + 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'; } - const adUnitData = getAdUnitData(adUnit); - const performanceData = getPerformanceData(adUnit.auctionStart); - const commonFields = { - 'hb.asiid': slot.getAdUnitPath(), - 'hb.cur': config.getConfig('currency.adServerCurrency'), - 'hb.pubid': initOptions.publisherId - }; + if (auction.adunitCodesRenderedCount === auction.adUnitCodesCount) { + auction.state = AUCTION_STATES.COMPLETED; + } - const data = Object.assign({}, adUnitData, performanceData, commonFields); - sendEvent(data); + // 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 getAdUnitByAdId(adId) { - let result; +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 { + utils.logWarn('OX: DOM element not for id ' + elementId); + } + return isAtf; +} - utils._map(auctionMap, value => value).forEach(auction => { - utils._map(auction.adUnitMap, value => value).forEach(adUnit => { - utils._map(adUnit.bidMap, value => value).forEach(bid => { - if (adId === bid.adId) { - result = adUnit; - } - }) - }); - }); +// 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; - return result; + var y = (window.pageYOffset !== undefined) + ? window.pageYOffset + : (document.documentElement || document.body.parentNode || document.body).scrollTop; + return {x, y}; } -function getAdUnitData(adUnit) { - const bids = utils._map(adUnit.bidMap, value => value); - const bidders = bids.map(bid => bid.bidder); - const requestTimes = bids.map( - bid => bid.requestTimestamp && bid.requestTimestamp - adUnit.auctionStart - ); - const responseTimes = bids.map( - bid => bid.responseTimestamp && bid.responseTimestamp - adUnit.auctionStart - ); - const bidValues = bids.map(bid => bid.cpm || 0); - const timeouts = bids.map(bid => !!bid.timedOut); - const creativeIds = bids.map(bid => bid.creativeId); - const winningBid = bids.filter(bid => bid.won)[0]; - const winningExchangeIndex = bids.indexOf(winningBid); - const openxBid = bids.filter(bid => bid.bidder === 'openx')[0]; +function delayedSend(auction) { + const delayTime = auction.adunitCodesRenderedCount === auction.adUnitCodesCount + ? analyticsConfig.payloadWaitTime + : analyticsConfig.payloadWaitTime + analyticsConfig.payloadWaitTimePadding; - return { - 'hb.ct': adUnit.auctionStart, - 'hb.rid': adUnit.auctionId, - 'hb.exn': bidders.join(','), - 'hb.sts': requestTimes.join(','), - 'hb.ets': responseTimes.join(','), - 'hb.bv': bidValues.join(','), - 'hb.to': timeouts.join(','), - 'hb.crid': creativeIds.join(','), - 'hb.we': winningExchangeIndex, - 'hb.g1': winningExchangeIndex === -1, - dddid: adUnit.transactionId, - ts: openxBid && openxBid.ts, - auid: openxBid && openxBid.params && openxBid.params.unit - }; + auction.auctionSendDelayTimer = setTimeout(() => { + let payload = JSON.stringify([buildAuctionPayload(auction)]); + ajax(ENDPOINT, deleteAuctionMap, payload, { contentType: 'application/json' }); + + function deleteAuctionMap() { + delete auctionMap[auction.id]; + } + }, delayTime); } -function getPerformanceData(auctionStart) { - let timing; - try { - timing = window.top.performance.timing; - } catch (e) {} +function clearAuctionTimer(auction) { + // reset the delay timer to send the auction data + if (auction.auctionSendDelayTimer) { + clearTimeout(auction.auctionSendDelayTimer); + auction.auctionSendDelayTimer = void (0); + } +} - if (!timing) { - return; +/** + * 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 []; } - const { fetchStart, domContentLoadedEventEnd, loadEventEnd } = timing; - const domContentLoadTime = domContentLoadedEventEnd - fetchStart; - const pageLoadTime = loadEventEnd - fetchStart; - const timeToAuction = auctionStart - fetchStart; - const timeToRender = Date.now() - fetchStart; + utils._each(auctionMap, currentAuction => { + // skip completed auctions + if (currentAuction.state === AUCTION_STATES.COMPLETED) { + return; + } - return { - 'hb.dcl': domContentLoadTime, - 'hb.dl': pageLoadTime, - 'hb.tta': timeToAuction, - 'hb.ttr': timeToRender - }; + utils._each(currentAuction.adUnitCodeToAdUnitMap, (currentAdunit) => { + utils._each(currentAdunit.bidRequestsMap, currentBiddRequest => { + utils._each(currentBiddRequest.bids, (currentBidResponse, bidResponseId) => { + if (bidId === bidResponseId) { + auction = currentAuction; + adUnit = currentAdunit; + bidResponse = currentBidResponse; + } + }); + }); + }); + }); + return [auction, adUnit, bidResponse]; } -function sendEvent(data) { - utils._map(data, (value, key) => [key, value]).forEach(([key, value]) => { - if ( - value === undefined || - value === null || - (typeof value === 'number' && isNaN(value)) - ) { - delete data[key]; +function getAuctionByGoogleTagSLot(slot) { + let slotAdunitCodes = [slot.getSlotElementId(), slot.getAdUnitPath()]; + let slotAuction; + + utils._each(auctionMap, auction => { + if (auction.state === AUCTION_STATES.COMPLETED) { + return; } + + utils._each(auction.adUnitCodeToAdUnitMap, (bidderRequestIdMap, adUnitCode) => { + if (includes(slotAdunitCodes, adUnitCode)) { + slotAuction = auction; + } + }); }); - ajax(ENDPOINT, null, data, { method: 'GET' }); + + return slotAuction; } -let googletag = window.googletag || {}; -googletag.cmd = googletag.cmd || []; -googletag.cmd.push(function() { - googletag.pubads().addEventListener(SLOT_LOADED, args => { - openxAdapter.track({ eventType: SLOT_LOADED, args }); - }); -}); +function buildAuctionPayload(auction) { + let {startTime, endTime, state, timeout, auctionOrder, userIds, adUnitCodeToAdUnitMap} = auction; + let {orgId, publisherPlatformId, publisherAccountId, campaign} = analyticsConfig; -const openxAdapter = Object.assign( - adapter({ url: ENDPOINT, analyticsType: 'endpoint' }), - { - track({ eventType, args }) { - utils.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 BID_WON: - onBidWon(args); - break; - case SLOT_LOADED: - onSlotLoaded(args); - break; + return { + adapterVersion: ADAPTER_VERSION, + schemaVersion: SCHEMA_VERSION, + orgId, + publisherPlatformId, + publisherAccountId, + campaign, + state, + startTime, + endTime, + timeLimit: timeout, + auctionOrder, + deviceType: detectMob() ? 'Mobile' : 'Desktop', + deviceOSType: detectOS(), + browser: detectBrowser(), + testCode: analyticsConfig.testCode, + // return an array of module name that have user data + userIdProviders: buildUserIdProviders(userIds), + adUnits: buildAdUnitsPayload(adUnitCodeToAdUnitMap), + }; + + function buildAdUnitsPayload(adUnitCodeToAdUnitMap) { + return utils._map(adUnitCodeToAdUnitMap, (adUnit) => { + let {code, adPosition} = adUnit; + + return { + code, + adPosition, + bidRequests: buildBidRequestPayload(adUnit.bidRequestsMap) + }; + + function buildBidRequestPayload(bidRequestsMap) { + return utils._map(bidRequestsMap, (bidRequest) => { + let {bidder, source, bids, mediaTypes, timedOut} = bidRequest; + return { + bidder, + source, + hasBidderResponded: Object.keys(bids).length > 0, + availableAdSizes: getMediaTypeSizes(mediaTypes), + availableMediaTypes: getMediaTypes(mediaTypes), + timedOut, + bidResponses: utils._map(bidRequest.bids, (bidderBidResponse) => { + let { + cpm, + creativeId, + ts, + meta, + mediaType, + dealId, + ttl, + netRevenue, + currency, + width, + height, + latency, + winner, + rendered, + renderTime + } = bidderBidResponse; + + return { + microCpm: cpm * 1000000, + netRevenue, + currency, + mediaType, + height, + width, + size: `${width}x${height}`, + dealId, + latency, + ttl, + winner, + creativeId, + ts, + rendered, + renderTime, + meta + } + }) + } + }); } - } + }); } -); - -// save the base class function -openxAdapter.originEnableAnalytics = openxAdapter.enableAnalytics; -// override enableAnalytics so we can get access to the config passed in from the page -openxAdapter.enableAnalytics = function(config) { - if (!config || !config.options || !config.options.publisherId) { - utils.logError('OpenX analytics adapter: publisherId is required.'); - return; + function buildUserIdProviders(userIds) { + return utils._map(userIds, (userId) => { + return utils._map(userId, (id, module) => { + return hasUserData(module, id) ? module : false + }).filter(module => module); + }).reduce(utils.flatten, []).filter(utils.uniques).sort(); } - initOptions = config.options; - openxAdapter.originEnableAnalytics(config); // call the base class function -}; -// reset the cache for unit tests -openxAdapter.reset = function() { - auctionMap = {}; -}; + function hasUserData(module, idOrIdObject) { + let normalizedId; + + switch (module) { + case 'digitrustid': + normalizedId = utils.deepAccess(idOrIdObject, 'data.id'); + break; + case 'lipb': + normalizedId = idOrIdObject.lipbid; + break; + default: + normalizedId = idOrIdObject; + } -adapterManager.registerAnalyticsAdapter({ - adapter: openxAdapter, - code: 'openx' -}); + return !utils.isEmpty(normalizedId); + } -export default openxAdapter; + function getMediaTypeSizes(mediaTypes) { + return utils._map(mediaTypes, (mediaTypeConfig, mediaType) => { + return utils.parseSizesInput(mediaTypeConfig.sizes) + .map(size => `${mediaType}_${size}`); + }).reduce(utils.flatten, []); + } + + function getMediaTypes(mediaTypes) { + return utils._map(mediaTypes, (mediaTypeConfig, mediaType) => mediaType); + } +} diff --git a/modules/openxAnalyticsAdapter.md b/modules/openxAnalyticsAdapter.md index ac739f36c76..af40486f2a4 100644 --- a/modules/openxAnalyticsAdapter.md +++ b/modules/openxAnalyticsAdapter.md @@ -102,11 +102,13 @@ Configuration options are a follows: | Property | Type | Required? | Description | Example | |:---|:---|:---|:---|:---| -| `publisherPlatformId` | `string` | Yes | Used to determine ownership of data. | `a3aece0c-9e80-4316-8deb-faf804779bd1` | -| `publisherAccountId` | `number` | Yes | Used to determine ownership of data. | `1537143056` | +| `orgId` | `string` | Yes | Used to determine ownership of data. | `aa1bb2cc-3dd4-4316-8deb-faf804779bd1` | +| `publisherPlatformId` | `string` | No
**__Deprecated. Please use orgId__** | Used to determine ownership of data. | `a3aece0c-9e80-4316-8deb-faf804779bd1` | +| `publisherAccountId` | `number` | No
**__Deprecated. Please use orgId__** | Used to determine ownership of data. | `1537143056` | | `sampling` | `number` | Yes | Sampling rate | Undefined or `1.00` - No sampling. Analytics is sent all the time.
0.5 - 50% of users will send analytics data. | | `testCode` | `string` | No | Used to label analytics data for the purposes of tests.
This label is treated as a dimension and can be compared against other labels. | `timeout_config_1`
`timeout_config_2`
`timeout_default` | - +| `campaign` | `Object` | No | Object with 5 parameters:
  • content
  • medium
  • name
  • source
  • term
Each parameter is a free-form string. Refer to metrics doc on when to use these fields. By setting a value to one of these properties, you override the associated url utm query parameter. | | +| `payloadWaitTime` | `number` | No | Delay after all slots of an auction renders before the payload is sent.
Defaults to 100ms | 1000 | --- # Viewing Data @@ -114,7 +116,7 @@ The Prebid Report available in the Reporting in the Cloud tool, allows you to vi **To view your data:** -1. Log in to Reporting in the Cloud. +1. Log in to [OpenX Reporting](https://openx.sigmoid.io/app). 2. In the top right, click on the **View** list and then select **Prebidreport**. diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index 805435abf80..be7ae6fcdc4 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -1,114 +1,190 @@ import { expect } from 'chai'; -import openxAdapter from 'modules/openxAnalyticsAdapter.js'; -import { config } from 'src/config.js'; +import openxAdapter, {AUCTION_STATES} from 'modules/openxAnalyticsAdapter.js'; import events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; +import find from 'core-js-pure/features/array/find.js'; const { - EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, BID_WON } + 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() { - it('should require publisher id', function() { - sinon.spy(utils, 'logError'); + describe('when validating the configuration', function () { + let spy; + beforeEach(function () { + spy = sinon.spy(utils, 'logError'); + }); + + afterEach(function() { + utils.logError.restore(); + }); + + it('should require organization id when no configuration is passed', 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/); + }); - openxAdapter.enableAnalytics(); - expect( - utils.logError.calledWith( - 'OpenX analytics adapter: publisherId is required.' - ) - ).to.be.true; + it('should validate types', function() { + openxAdapter.enableAnalytics({ + provider: 'openx', + options: { + orgId: 'test platformId', + sampling: 'invalid-float' + } + }); - utils.logError.restore(); + expect(spy.firstCall.args[0]).to.match(/sampling/); + expect(spy.firstCall.args[0]).to.match(/type 'number'/); + }); }); - describe('sending analytics event', function() { - const auctionInit = { auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' }; + 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', + sample: 1.0, + enableV2: true, + 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: 'add5eb0f-587d-441d-86ec-bbb722c70f79', - auctionStart: 1540944528017, + auctionId: 'test-auction-id', + auctionStart: CURRENT_TIME, bids: [ { - adUnitCode: 'div-1', - bidId: '2f0c647b904e25', + adUnitCode: AD_UNIT_CODE, + bidId: 'test-openx-request-id', bidder: 'openx', - params: { unit: '540249866' }, - transactionId: 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873' + 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: 1540944528021 + start: CURRENT_TIME + 10 }; const bidRequestedCloseX = { - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', - auctionStart: 1540944528017, + auctionId: 'test-auction-id', + auctionStart: CURRENT_TIME, bids: [ { - adUnitCode: 'div-1', - bidId: '43d454020e9409', + adUnitCode: AD_UNIT_CODE, + bidId: 'test-closex-request-id', bidder: 'closex', - params: { unit: '513144370' }, - transactionId: 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873' + params: { unit: 'test-closex-ad-unit-id' }, + userId: { + bla_id: '2', + tdid: 'test-tradedesk-id' + } } ], - start: 1540944528026 + start: CURRENT_TIME + 20 }; const bidResponseOpenX = { - requestId: '2f0c647b904e25', - adId: '33dddbb61d359a', - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + 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', - responseTimestamp: 1540944528184, - ts: '2DAABBgABAAECAAIBAAsAAgAAAJccGApKSGt6NUZxRXYyHBbinsLj' + currency: 'USD', + timeToRespond: 100, + responseTimestamp: CURRENT_TIME + 30, + ts: 'test-openx-ts' }; const bidResponseCloseX = { - requestId: '43d454020e9409', - adId: '43dddbb61d359a', - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', + 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', - responseTimestamp: 1540944528196, - ts: 'hu1QWo6iD3MHs6NG_AQAcFtyNqsj9y4S0YRbX7Kb06IrGns0BABb' + currency: 'USD', + timeToRespond: 200, + dealId: 'test-closex-deal-id', + responseTimestamp: CURRENT_TIME + 40, + ts: 'test-closex-ts' }; const bidTimeoutOpenX = { 0: { - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', - bidId: '2f0c647b904e25' - } - }; + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id', + bidId: 'test-openx-request-id' + }}; const bidTimeoutCloseX = { 0: { - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', - bidId: '43d454020e9409' + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id', + bidId: 'test-closex-request-id' } }; const bidWonOpenX = { - requestId: '2f0c647b904e25', - adId: '33dddbb61d359a', - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' + 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: '43d454020e9409', - adId: '43dddbb61d359a', - adUnitCode: 'div-1', - auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79' + requestId: 'test-closex-request-id', + adId: 'test-closex-ad-id', + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id' }; function simulateAuction(events) { @@ -116,328 +192,440 @@ describe('openx analytics adapter', function() { events.forEach(event => { const [eventType, args] = event; - openxAdapter.track({ eventType, args }); if (eventType === BID_RESPONSE) { highestBid = highestBid || args; if (highestBid.cpm < args.cpm) { highestBid = args; } } - }); - openxAdapter.track({ - eventType: SLOT_LOADED, - args: { - slot: { - getAdUnitPath: () => { - return '/90577858/test_ad_unit'; - }, - getTargetingKeys: () => { - return []; - }, - getTargeting: sinon - .stub() - .withArgs('hb_adid') - .returns(highestBid ? [highestBid.adId] : []) - } + 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 }); } }); } - function getQueryData(url) { - const queryArgs = url.split('?')[1].split('&'); - return queryArgs.reduce((data, arg) => { - const [key, val] = arg.split('='); - data[key] = val; - return data; - }, {}); - } + let clock; - before(function() { + beforeEach(function() { sinon.stub(events, 'getEvents').returns([]); - openxAdapter.enableAnalytics({ - options: { - publisherId: 'test123' - } - }); + clock = sinon.useFakeTimers(CURRENT_TIME); }); - after(function() { + afterEach(function() { events.getEvents.restore(); - openxAdapter.disableAnalytics(); + clock.restore(); }); - beforeEach(function() { - openxAdapter.reset(); - }); + 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] + ]); - afterEach(function() {}); + simulateAuction([ + [AUCTION_INIT, {...auctionInit, auctionId: 'second-auction-id'}], + [SLOT_LOADED] + ]); - it('should not send request if no bid response', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX] - ]); + clock.tick(SLOT_LOAD_WAIT_TIME); + auction = JSON.parse(server.requests[0].requestBody)[0]; + auction2 = JSON.parse(server.requests[1].requestBody)[0]; + }); - expect(server.requests.length).to.equal(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 send 1 request to the right endpoint', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] - ]); + it('should track auction count', function () { + expect(auction.auctionOrder).to.equal(1); + expect(auction2.auctionOrder).to.equal(2); + }); - expect(server.requests.length).to.equal(1); + 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); + }); - const endpoint = server.requests[0].url.split('?')[0]; - // note IE11 returns the default secure port, so we look for this alternate value as well in these tests - expect(endpoint).to.be.oneOf(['https://ads.openx.net/w/1.0/pban', 'https://ads.openx.net:443/w/1.0/pban']); + it('should track the orgId', function () { + expect(auction.publisherAccountId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.publisherAccountId); + }); }); - describe('hb.ct, hb.rid, dddid, hb.asiid, hb.pubid', function() { - it('should always be in the query string', function() { + 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], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] + [SLOT_LOADED], ]); + clock.tick(SLOT_LOAD_WAIT_TIME); + auction = JSON.parse(server.requests[0].requestBody)[0]; + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - 'hb.ct': String(bidRequestedOpenX.auctionStart), - 'hb.rid': auctionInit.auctionId, - dddid: bidRequestedOpenX.bids[0].transactionId, - 'hb.asiid': '/90577858/test_ad_unit', - 'hb.pubid': 'test123' - }); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); + + it('should track the custom test code', function () { + expect(auction.testCode).to.equal('test-code'); }); }); - describe('hb.cur', function() { - it('should be in the query string if currency is set', function() { - sinon - .stub(config, 'getConfig') - .withArgs('currency.adServerCurrency') - .returns('bitcoin'); + 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-campaign-name&' + + 'utm_source=test-source&' + + 'utm_medium=test-medium&' + }); + + openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); simulateAuction([ [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] + [SLOT_LOADED], ]); + clock.tick(SLOT_LOAD_WAIT_TIME); + auction = JSON.parse(server.requests[0].requestBody)[0]; + + 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; + }); - config.getConfig.restore(); + 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' + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - 'hb.cur': 'bitcoin' + openxAdapter.enableAnalytics({ + options: { + ...DEFAULT_V2_ANALYTICS_CONFIG, + campaign: { + name: 'test-config-name', + source: 'test-config-source', + medium: 'test-config-medium' + } + } }); - }); - it('should not be in the query string if currency is not set', function() { simulateAuction([ [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] + [SLOT_LOADED], ]); - - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.not.have.key('hb.cur'); + 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('hb.dcl, hb.dl, hb.tta, hb.ttr', function() { - it('should be in the query string if browser supports performance API', function() { - const timing = { - fetchStart: 1540944528000, - domContentLoadedEventEnd: 1540944528010, - loadEventEnd: 1540944528110 - }; - const originalPerf = window.top.performance; - window.top.performance = { timing }; + describe('when there are bid requests', function () { + let auction; + let openxBidder; + let closexBidder; - const renderTime = 1540944528100; - sinon.stub(Date, 'now').returns(renderTime); + beforeEach(function () { + openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); simulateAuction([ [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedCloseX], [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] + [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'); + }); - window.top.performance = originalPerf; - Date.now.restore(); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - 'hb.dcl': String(timing.domContentLoadedEventEnd - timing.fetchStart), - 'hb.dl': String(timing.loadEventEnd - timing.fetchStart), - 'hb.tta': String(bidRequestedOpenX.auctionStart - timing.fetchStart), - 'hb.ttr': String(renderTime - timing.fetchStart) - }); + 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; - it('should not be in the query string if browser does not support performance API', function() { - const originalPerf = window.top.performance; - window.top.performance = undefined; + beforeEach(function () { + openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); simulateAuction([ [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedCloseX], [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] + [BID_TIMEOUT, bidTimeoutCloseX], + [BID_TIMEOUT, bidTimeoutOpenX], + [AUCTION_END, auctionEnd] ]); + clock.tick(SLOT_LOAD_WAIT_TIME * 2); + auction = JSON.parse(server.requests[0].requestBody)[0]; - window.top.performance = originalPerf; + openxBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); + closexBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.not.have.keys( - 'hb.dcl', - 'hb.dl', - 'hb.tta', - 'hb.ttr' - ); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); + + it('should track the timeout', function () { + expect(openxBidRequest.timedOut).to.equal(true); + expect(closexBidRequest.timedOut).to.equal(true); }); }); - describe('ts, auid', function() { - it('OpenX is in auction and has a bid response', function() { + 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, bidRequestedOpenX], [BID_REQUESTED, bidRequestedCloseX], + [BID_REQUESTED, bidRequestedOpenX], [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX] + [BID_RESPONSE, bidResponseCloseX], + [AUCTION_END, auctionEnd] ]); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - ts: bidResponseOpenX.ts, - auid: bidRequestedOpenX.bids[0].params.unit - }); + 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]; }); - it('OpenX is in auction but no bid response', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseCloseX] - ]); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - auid: bidRequestedOpenX.bids[0].params.unit - }); - expect(queryData).to.not.have.key('ts'); + 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('OpenX is not in auction', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseCloseX] - ]); + 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); + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.not.have.keys('auid', 'ts'); + 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('hb.exn, hb.sts, hb.ets, hb.bv, hb.crid, hb.to', function() { - it('2 bidders in auction', function() { + 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] + [BID_RESPONSE, bidResponseCloseX], + [AUCTION_END, auctionEnd], + [BID_WON, bidWonOpenX] ]); - const queryData = getQueryData(server.requests[0].url); - const auctionStart = bidRequestedOpenX.auctionStart; - expect(queryData).to.include({ - 'hb.exn': [ - bidRequestedOpenX.bids[0].bidder, - bidRequestedCloseX.bids[0].bidder - ].join(','), - 'hb.sts': [ - bidRequestedOpenX.start - auctionStart, - bidRequestedCloseX.start - auctionStart - ].join(','), - 'hb.ets': [ - bidResponseOpenX.responseTimestamp - auctionStart, - bidResponseCloseX.responseTimestamp - auctionStart - ].join(','), - 'hb.bv': [bidResponseOpenX.cpm, bidResponseCloseX.cpm].join(','), - 'hb.crid': [ - bidResponseOpenX.creativeId, - bidResponseCloseX.creativeId - ].join(','), - 'hb.to': [false, false].join(',') - }); + clock.tick(SLOT_LOAD_WAIT_TIME * 2); + auction = JSON.parse(server.requests[0].requestBody)[0]; }); - it('OpenX timed out', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseCloseX], - [BID_TIMEOUT, bidTimeoutOpenX] - ]); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); - const queryData = getQueryData(server.requests[0].url); - const auctionStart = bidRequestedOpenX.auctionStart; - expect(queryData).to.include({ - 'hb.exn': [ - bidRequestedOpenX.bids[0].bidder, - bidRequestedCloseX.bids[0].bidder - ].join(','), - 'hb.sts': [ - bidRequestedOpenX.start - auctionStart, - bidRequestedCloseX.start - auctionStart - ].join(','), - 'hb.ets': [ - undefined, - bidResponseCloseX.responseTimestamp - auctionStart - ].join(','), - 'hb.bv': [0, bidResponseCloseX.cpm].join(','), - 'hb.crid': [undefined, bidResponseCloseX.creativeId].join(','), - 'hb.to': [true, false].join(',') - }); + 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('hb.we, hb.g1', function() { - it('OpenX won', function() { + 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_WON, bidWonOpenX] + [BID_RESPONSE, bidResponseCloseX], + [AUCTION_END, auctionEnd], + [BID_WON, bidWonOpenX], + [SLOT_LOADED] ]); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - 'hb.we': '0', - 'hb.g1': 'false' - }); + clock.tick(SLOT_LOAD_WAIT_TIME * 2); + auction = JSON.parse(server.requests[0].requestBody)[0]; }); - it('DFP won', function() { - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX] - ]); + afterEach(function () { + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); - const queryData = getQueryData(server.requests[0].url); - expect(queryData).to.include({ - 'hb.we': '-1', - 'hb.g1': 'true' - }); + 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); }); }); }); From a2f2ac65f311f153b81c2451500fde5e854d9c17 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 31 Aug 2020 12:43:21 -0400 Subject: [PATCH 0134/1476] Update rubiconAnalyticsAdapter.js with gvlid (#5681) * Update rubiconAnalyticsAdapter.js This adds the gvl id as per https://github.com/prebid/Prebid.js/pull/5444/files * Update rubiconAnalyticsAdapter.js --- modules/rubiconAnalyticsAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 0bee4d926fc..00fcd6ba8ff 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -548,7 +548,8 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { adapterManager.registerAnalyticsAdapter({ adapter: rubiconAdapter, - code: 'rubicon' + code: 'rubicon', + gvlid: 52 }); export default rubiconAdapter; From e4bfe40b73dc99392c395007e0a0feb2b03cf2b4 Mon Sep 17 00:00:00 2001 From: Nate Lapinski Date: Tue, 1 Sep 2020 03:03:23 +0900 Subject: [PATCH 0135/1476] SpotX: Set ad_mute correctly. (#5486) * Set ad_mute correctly. In the SpotX Prebid adapter, data-spotx_ad_mute was being set to '0' isntead of '1', in scenarios where the user passed in 'ad_mute'. * Updates unit test to ensure ad_mute is set correctly Co-authored-by: Nate Lapinski --- modules/spotxBidAdapter.js | 2 +- test/spec/modules/spotxBidAdapter_spec.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index a8d874c57e9..9a3779dc65c 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -397,7 +397,7 @@ function createOutstreamScript(bid) { utils.logMessage('[SPOTX][renderer] Default beahavior'); if (utils.getBidIdParameter('ad_mute', bid.renderer.config.outstream_options)) { - dataSpotXParams['data-spotx_ad_mute'] = '0'; + dataSpotXParams['data-spotx_ad_mute'] = '1'; } dataSpotXParams['data-spotx_collapse'] = '0'; dataSpotXParams['data-spotx_autoplay'] = '1'; diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 94cc335fd8e..baece710ee1 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -515,6 +515,7 @@ describe('the spotx adapter', function () { 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(); }); From d642ebecbd7e87a992a1ee1cdcacf62fdcd69ebb Mon Sep 17 00:00:00 2001 From: Corey Kress Date: Mon, 31 Aug 2020 14:44:47 -0400 Subject: [PATCH 0136/1476] [Synacormedia] Update bid url scheme (#5665) * CAP-1614 - updated docs to show correct size for banner and some other small fixes * CAP-1636 support schain object in prebid * CAP-1636 updated the review comments * CAP-1849 - split up banner and video impressions to use format * CAP-1879 - added adapter support for consent management module * CAP-1879 - updates for pr * CAP-1879 - remove unneeded checks * CAP-1920 - fixed size bug * CAP-1920 - update tests and banner * CAP-1920 - fix a comparison, and height setting * CAP-1966: Fix video impid parsing * CAP-1966: Set hostname based on seat * CAP-1966 - update unit test Co-authored-by: Corey Kress Co-authored-by: Rajkumar Natarajan Co-authored-by: Lasse Saarinen --- modules/synacormediaBidAdapter.js | 7 ++++--- test/spec/modules/synacormediaBidAdapter_spec.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index abbae1e7354..e0d017a6c51 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -6,7 +6,8 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import includes from 'core-js-pure/features/array/includes.js'; import {config} from '../src/config.js'; -const BID_HOST = 'https://prebid.technoratimedia.com'; +const BID_SCHEME = 'https://'; +const BID_DOMAIN = 'technoratimedia.com'; const USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com'; const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'linearity', 'mimes', 'protocols', 'api' ]; const BLOCKED_AD_SIZES = [ @@ -93,7 +94,7 @@ export const spec = { if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', - url: `${BID_HOST}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`, + url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`, data: openRtbBidRequest, options: { contentType: 'application/json', @@ -189,7 +190,7 @@ export const spec = { seatbid.bid.forEach(bid => { const creative = updateMacros(bid, bid.adm); const nurl = updateMacros(bid, bid.nurl); - const [, impType, impid] = bid.impid.match(/^([vb])(.*)$/); + const [, impType, impid] = bid.impid.match(/^([vb])([\w\d]+)/); let height = bid.h; let width = bid.w; const isVideo = impType === 'v'; diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 1521f4a2e63..dd40e634723 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -269,7 +269,7 @@ describe('synacormediaBidAdapter ', function () { let req = spec.buildRequests([mismatchedSeatBidRequest, validBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/somethingelse?'); + expect(req.url).to.contain('https://somethingelse.technoratimedia.com/openrtb/bids/somethingelse?'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { From c4c060ec6f04aa0bcddf847280272d18e2b27267 Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Mon, 31 Aug 2020 11:55:21 -0700 Subject: [PATCH 0137/1476] Rubicon Bid Adapter: fix incorrect sharedid param (#5671) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * fix for rp adapter incorrect sharedid value Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/rubiconBidAdapter.js | 32 ++++++++++----------- test/spec/modules/rubiconBidAdapter_spec.js | 12 ++++---- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 4a0dd10281b..da23cca62d1 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -516,27 +516,25 @@ export const spec = { // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; - if (bidRequest.userId) { - if (bidRequest.userId.tdid) { - data['tpid_tdid'] = bidRequest.userId.tdid; + if (bidRequest.userIdAsEids && bidRequest.userIdAsEids.length) { + const unifiedId = find(bidRequest.userIdAsEids, eid => eid.source === 'adserver.org'); + if (unifiedId) { + data['tpid_tdid'] = unifiedId.uids[0].id; } - - // support liveintent ID - if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { - data['tpid_liveintent.com'] = bidRequest.userId.lipb.lipbid; - if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { - data['tg_v.LIseg'] = bidRequest.userId.lipb.segments.join(','); + const liveintentId = find(bidRequest.userIdAsEids, eid => eid.source === 'liveintent.com'); + if (liveintentId) { + data['tpid_liveintent.com'] = liveintentId.uids[0].id; + if (liveintentId.ext && Array.isArray(liveintentId.ext.segments) && liveintentId.ext.segments.length) { + data['tg_v.LIseg'] = liveintentId.ext.segments.join(','); } } - - // support identityLink (aka LiveRamp) - if (bidRequest.userId.idl_env) { - data['x_liverampidl'] = bidRequest.userId.idl_env; + const liverampId = find(bidRequest.userIdAsEids, eid => eid.source === 'liveramp.com'); + if (liverampId) { + data['x_liverampidl'] = liverampId.uids[0].id; } - - // support shared id - if (bidRequest.userId.sharedid) { - data['eid_sharedid.org'] = `${bidRequest.userId.sharedid.id}^3^${bidRequest.userId.sharedid.third}`; + const sharedId = find(bidRequest.userIdAsEids, eid => eid.source === 'sharedid.org'); + if (sharedId) { + data['eid_sharedid.org'] = `${sharedId.uids[0].id}^${sharedId.uids[0].atype}^${sharedId.uids[0].ext.third}`; } } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c28af4a3f9b..a2b554c1f5d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1316,7 +1316,7 @@ describe('the rubicon adapter', function () { }); describe('user id config', function() { - it('should send tpid_tdid when userId defines tdid', function () { + it('should send tpid_tdid when userIdAsEids contains unifiedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' @@ -1329,7 +1329,7 @@ describe('the rubicon adapter', function () { }); describe('LiveIntent support', function () { - it('should send tpid_liveintent.com when userId defines lipd', function () { + it('should send tpid_liveintent.com when userIdAsEids contains liveintentId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { lipb: { @@ -1343,7 +1343,7 @@ describe('the rubicon adapter', function () { expect(data['tpid_liveintent.com']).to.equal('0000-1111-2222-3333'); }); - it('should send tg_v.LIseg when userId defines lipd.segments', function () { + it('should send tg_v.LIseg when userIdAsEids contains liveintentId with ext.segments as array', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { lipb: { @@ -1361,7 +1361,7 @@ describe('the rubicon adapter', function () { }); describe('LiveRamp support', function () { - it('should send x_liverampidl when userId defines idl_env', function () { + it('should send x_liverampidl when userIdAsEids contains liverampId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { idl_env: '1111-2222-3333-4444' @@ -1375,7 +1375,7 @@ describe('the rubicon adapter', function () { }); describe('SharedID support', function () { - it('should send sharedid when userId defines sharedId', function () { + it('should send sharedid when userIdAsEids contains sharedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { sharedid: { @@ -1387,7 +1387,7 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); - expect(data['eid_sharedid.org']).to.equal('1111^3^2222'); + expect(data['eid_sharedid.org']).to.equal('1111^1^2222'); }); }); From 6af92befdd407ce6715ed78d5d8d395471b5d345 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 1 Sep 2020 15:46:29 +0200 Subject: [PATCH 0138/1476] Add native support to ablida Bid Adapter (#5545) * add onBidWon function, add bidder adapter version to bid requests * add support for native * use triggerPxel instead of ajax, because ajax was called 3 times with native --- modules/ablidaBidAdapter.js | 20 +++++++++-------- modules/ablidaBidAdapter.md | 26 +++++++++++++++++++++- test/spec/modules/ablidaBidAdapter_spec.js | 16 +++++++++---- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index 9bd22ef1f0d..470a845cd20 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -1,13 +1,14 @@ import * as utils from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ajax} from '../src/ajax.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; const BIDDER_CODE = 'ablida'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], /** * Determines whether or not the given bid request is valid. @@ -31,20 +32,22 @@ export const spec = { return []; } return validBidRequests.map(bidRequest => { - const sizes = utils.parseSizesInput(bidRequest.sizes)[0]; - const size = sizes.split('x'); + let sizes = [] + if (bidRequest.mediaTypes && bidRequest.mediaTypes[BANNER] && bidRequest.mediaTypes[BANNER].sizes) { + sizes = bidRequest.mediaTypes[BANNER].sizes; + } const jaySupported = 'atob' in window && 'currentScript' in document; const device = getDevice(); const payload = { placementId: bidRequest.params.placementId, - width: size[0], - height: size[1], + sizes: sizes, bidId: bidRequest.bidId, categories: bidRequest.params.categories, referer: bidderRequest.refererInfo.referer, jaySupported: jaySupported, device: device, - adapterVersion: 2 + adapterVersion: 3, + mediaTypes: bidRequest.mediaTypes }; return { method: 'POST', @@ -72,9 +75,8 @@ export const spec = { return bidResponses; }, onBidWon: function (bid) { - if (!bid['nurl']) { return false; } - ajax(bid['nurl'], null); - return true; + if (!bid['nurl']) { return; } + utils.triggerPixel(bid['nurl']); } }; diff --git a/modules/ablidaBidAdapter.md b/modules/ablidaBidAdapter.md index 70e6576cd30..001bee4f35c 100644 --- a/modules/ablidaBidAdapter.md +++ b/modules/ablidaBidAdapter.md @@ -27,6 +27,30 @@ Module that connects to Ablida's bidder for bids. } } ] + }, { + code: 'native-ad-div', + mediaTypes: { + native: { + image: { + sendId: true, + required: true + }, + title: { + required: true + }, + body: { + required: true + } + } + }, + bids: [ + { + bidder: 'ablida', + params: { + placementId: 'native-demo' + } + } + ] } - ]; + ]; ``` diff --git a/test/spec/modules/ablidaBidAdapter_spec.js b/test/spec/modules/ablidaBidAdapter_spec.js index 0238c14fe5c..e32531b1eac 100644 --- a/test/spec/modules/ablidaBidAdapter_spec.js +++ b/test/spec/modules/ablidaBidAdapter_spec.js @@ -1,6 +1,7 @@ import {assert, expect} from 'chai'; import {spec} from 'modules/ablidaBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; @@ -104,16 +105,23 @@ describe('ablidaBidAdapter', function () { }); describe('onBidWon', function() { - it('Should not ajax call if bid does not contain nurl', function() { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function() { + utils.triggerPixel.restore(); + }); + + it('Should not trigger pixel if bid does not contain nurl', function() { const result = spec.onBidWon({}); - expect(result).to.equal(false) + expect(utils.triggerPixel.callCount).to.equal(0) }) - it('Should ajax call if bid nurl', function() { + it('Should trigger pixel if bid nurl', function() { const result = spec.onBidWon({ nurl: 'https://example.com/some-tracker' }); - expect(result).to.equal(true) + expect(utils.triggerPixel.callCount).to.equal(1) }) }) }); From d90642d2dd15b9f47a9933e46e8d20e9b40754a3 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 1 Sep 2020 10:36:45 -0400 Subject: [PATCH 0139/1476] update parameter field from consentManagement iframe call (#5505) * remove parameter field from consentManagement iframe call * update to undefined approach --- modules/consentManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 90fe24735db..debddd4bdc6 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -225,7 +225,7 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { window.addEventListener('message', readPostMessageResponse, false); // call CMP - window[apiName](commandName, null, moduleCallback); + window[apiName](commandName, undefined, moduleCallback); function readPostMessageResponse(event) { let cmpDataPkgName = `${apiName}Return`; From c8a6010e8b583197827283bfe67990ca2f898c76 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 1 Sep 2020 11:03:18 -0400 Subject: [PATCH 0140/1476] remove cmpuishown event for tcf2 logic (#5642) * remove cmpuishown event for tcf2 logic * store consent on "cmpuishown" event * reverted last consnetManagement cmpuishown commit * combine if statements * fix typo Co-authored-by: Eric Harper --- modules/consentManagement.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index debddd4bdc6..19fbe827eb1 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -100,11 +100,7 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { function v2CmpResponseCallback(tcfData, success) { utils.logInfo('Received a response from CMP', tcfData); if (success) { - if (tcfData.gdprApplies === false) { - cmpSuccess(tcfData, hookConfig); - } else if (tcfData.eventStatus === 'tcloaded' || tcfData.eventStatus === 'useractioncomplete') { - cmpSuccess(tcfData, hookConfig); - } else if (tcfData.eventStatus === 'cmpuishown' && tcfData.tcString && tcfData.purposeOneTreatment === true) { + if (tcfData.gdprApplies === false || tcfData.eventStatus === 'tcloaded' || tcfData.eventStatus === 'useractioncomplete') { cmpSuccess(tcfData, hookConfig); } } else { From 0e054a52b4d7b6f8bea8a634d6746dbd4ca7895e Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Wed, 2 Sep 2020 03:34:52 +0200 Subject: [PATCH 0141/1476] bug sync RA (#5678) Co-authored-by: sgimenez --- modules/richaudienceBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 43bef356a73..e51cc79eb82 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -132,7 +132,7 @@ export const spec = { var consent = ''; if (gdprConsent && typeof gdprConsent.consentString === 'string' && typeof gdprConsent.consentString != 'undefined') { - consent = `consentString=’${gdprConsent.consentString}` + consent = `consentString=${gdprConsent.consentString}` } if (syncOptions.iframeEnabled) { From badaab83fda02439e486aca10f3e81fb34a4c19a Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Wed, 2 Sep 2020 11:59:21 +0200 Subject: [PATCH 0142/1476] ATS-analytics - add gvlid property (#5672) --- modules/atsAnalyticsAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index ad488aa50d9..9811c306738 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -165,7 +165,8 @@ atsAnalyticsAdapter.enableAnalytics = function (config) { adaptermanager.registerAnalyticsAdapter({ adapter: atsAnalyticsAdapter, - code: 'atsAnalytics' + code: 'atsAnalytics', + gvlid: 97 }); export default atsAnalyticsAdapter; From bafa13937b10dce776a2f212d6d62da7fa66a18a Mon Sep 17 00:00:00 2001 From: Kanchika - Automatad Date: Wed, 2 Sep 2020 15:52:26 +0530 Subject: [PATCH 0143/1476] Automatad Bid Adapter: Fix built request json to support multiple bids (#5669) * added automatad bid adapter * added automatad bid adapter readme * added automatad bidder adapter unit test * updated maintainer email id for automatad adapter * refactored automatadBidAdapter js * refactored automatadBidAdapter unit test * refactored automatadBidAdapter unit test * added usersync code to automatad bid adapter * Added unit test for onBidWon in automatadBidAdapter_spec * removed trailing spaces * removed trailing space * changes for getUserSync function * lint error fixes * updated usersync url * additional test for onBidWon function added * added ajax stub in test * updated winurl params * lint fixes * added adunitCode in bid request * added test for adunit code * add placement in impression object --- modules/automatadBidAdapter.js | 3 +-- test/spec/modules/automatadBidAdapter_spec.js | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index 414cadcd405..e1a69a37513 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -27,12 +27,12 @@ export const spec = { } const siteId = validBidRequests[0].params.siteId - const placementId = validBidRequests[0].params.placementId const impressions = validBidRequests.map(bidRequest => { return { id: bidRequest.bidId, adUnitCode: bidRequest.adUnitCode, + placement: bidRequest.params.placementId, banner: { format: bidRequest.sizes.map(sizeArr => ({ w: sizeArr[0], @@ -48,7 +48,6 @@ export const spec = { imp: impressions, site: { id: siteId, - placement: placementId, domain: window.location.hostname, page: window.location.href, ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null, diff --git a/test/spec/modules/automatadBidAdapter_spec.js b/test/spec/modules/automatadBidAdapter_spec.js index a6de8810284..e0341a1d255 100644 --- a/test/spec/modules/automatadBidAdapter_spec.js +++ b/test/spec/modules/automatadBidAdapter_spec.js @@ -86,6 +86,11 @@ describe('automatadBidAdapter', function () { expect(rdata.imp.length).to.equal(1) }) + it('should include placement', function () { + let r = rdata.imp[0] + expect(r.placement !== null).to.be.true + }) + it('should include media types', function () { let r = rdata.imp[0] expect(r.media_types !== null).to.be.true From cb3a3b3ce67ffc8a9962709117daa45bdb759649 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 2 Sep 2020 08:22:07 -0400 Subject: [PATCH 0144/1476] Fix typo in warning (#5682) * Update prebid.js fix typo in warning * Update prebid.js * Update prebid.js --- src/prebid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prebid.js b/src/prebid.js index 827ba2e2270..bd85f416883 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -779,7 +779,7 @@ $$PREBID_GLOBAL$$.markWinningBidAsUsed = function (markBidRequest) { } else if (markBidRequest.adId) { bids = auctionManager.getBidsReceived().filter(bid => bid.adId === markBidRequest.adId); } else { - utils.logWarn('Inproper usage of markWinningBidAsUsed. It\'ll need an adUnitCode and/or adId to function.'); + utils.logWarn('Improper use of markWinningBidAsUsed. It needs an adUnitCode or an adId to function.'); } if (bids.length > 0) { From bb18807b30cc86743b23084b25380740d1720e44 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 2 Sep 2020 05:42:55 -0700 Subject: [PATCH 0145/1476] Price Floors: Fix bug when caching floor lookup (#5673) * Cannot pass cached floor by reference, adjustments break it! * fix typo Co-authored-by: Leif Wickland Co-authored-by: Leif Wickland --- modules/priceFloors.js | 2 +- test/spec/modules/priceFloors_spec.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 2acd21918ef..1b865e05c0a 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -103,7 +103,7 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) // if we already have gotten the matching rule from this matching input then use it! No need to look again let previousMatch = utils.deepAccess(floorData, `matchingInputs.${matchingInput}`); if (previousMatch) { - return previousMatch; + return {...previousMatch}; } let allPossibleMatches = generatePossibleEnumerations(fieldValues, utils.deepAccess(floorData, 'schema.delimiter') || '|'); let matchingRule = find(allPossibleMatches, hashValue => floorData.values.hasOwnProperty(hashValue)); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index d4ac2e5ad72..ae45244f03d 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -182,6 +182,20 @@ describe('the price floors module', function () { matchingRule: '*' }); }); + it('does not alter cached matched input if conversion occurs', function () { + let inputData = {...basicFloorData}; + [0.2, 0.4, 0.6, 0.8].forEach(modifier => { + let result = getFirstMatchingFloor(inputData, basicBidRequest, {mediaType: 'banner', size: '*'}); + // result should always be the same + expect(result).to.deep.equal({ + matchingFloor: 1.0, + matchingData: 'banner', + matchingRule: 'banner' + }); + // make sure a post retrieval adjustment does not alter the cached floor + result.matchingFloor = result.matchingFloor * modifier; + }); + }); it('selects the right floor for different sizes', function () { let inputFloorData = { currency: 'USD', From bba4b023290423855fd379fbdcf29d65544401e6 Mon Sep 17 00:00:00 2001 From: smartclip-adtech <65160328+smartclip-adtech@users.noreply.github.com> Date: Wed, 2 Sep 2020 19:13:16 +0200 Subject: [PATCH 0146/1476] smartxBidAdapter.js - removed unused variables, removed debug, added window before the outstream related functions (#5689) Co-authored-by: Gino --- modules/smartxBidAdapter.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index a745c54e39c..4409e4e9dfb 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -331,13 +331,6 @@ function createOutstreamScript(bid) { // const slot = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options); utils.logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); const elementId = bid.adUnitCode; - // eslint-disable-next-line camelcase - var sc_smartIntxtStart; - // eslint-disable-next-line camelcase - var sc_smartIntxtNoad; - // eslint-disable-next-line camelcase - var sc_smartIntxtEnd; - var SmartPlay; let smartPlayObj = { minAdWidth: 290, maxAdWidth: 900, @@ -348,20 +341,19 @@ function createOutstreamScript(bid) { }, onStartCallback: function (m, n) { try { - sc_smartIntxtStart(n); + window.sc_smartIntxtStart(n); } catch (f) {} }, onCappedCallback: function (m, n) { try { - sc_smartIntxtNoad(n); + window.sc_smartIntxtNoad(n); } catch (f) {} }, onEndCallback: function (m, n) { try { - sc_smartIntxtEnd(n); + window.sc_smartIntxtEnd(n); } catch (f) {} }, - debug: true }; smartPlayObj.adResponse = bid.vastContent; const script = window.document.createElement('script'); @@ -372,7 +364,7 @@ function createOutstreamScript(bid) { var rs = this.readyState; if (rs && rs != 'complete' && rs != 'loaded') return; try { - SmartPlay(elementId, smartPlayObj); + window.SmartPlay(elementId, smartPlayObj); } catch (e) { utils.logError('error caught : ' + e); } From 6842e8ba17cd2c1488c2a08ba585d080f13f99c7 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 2 Sep 2020 17:19:01 -0400 Subject: [PATCH 0147/1476] Prebid 4.6.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a0f4077462..fb30a4397be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.6.0-pre", + "version": "4.6.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e783c7539e93076530024dd5ee1be4b5262cfc2e Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 2 Sep 2020 17:31:42 -0400 Subject: [PATCH 0148/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fb30a4397be..3fdc3ba4b83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.6.0", + "version": "4.7.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From deb7d5b7acd724c9ad9d77beb5ec11d94466bb63 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 3 Sep 2020 17:19:10 -0400 Subject: [PATCH 0149/1476] parrableIdSystem: Add an optional timezone and timezone offset allow/block filter (#5569) * Add unit coverage for parrableIdSystem getId callback * PBID-14: Pass uspString to Parrable as us_privacy query parameter * PBID-14: Simplify parrableIdSystem us_privacy test * PBID-14: Only send us_privacy to Parrable when a value exists * PBID-11: Read new Parrable compound cookie _parrable_id Migrating from legacy _parrable_eid cookie. The new cookie contains ibaOptout and ccpaOptout status fields * Remove path check from parrableIdSystem url test * PBID-11: Integrate Parrable compound cookie, consolidating old cookies * PBID-11: Update parrableIdSystem requestBids hook test to support compound cookie value * PBID-11: Small refactor to parrableIdSystem spec to support compound cookie * PBID-11: Handle legacy ibaOptout as truthy value when migrating to compound cookie * PBID-11: Add parrableIdSystem spec tests covering migration of legacy cookies * PBID-11: Remove storage documentation from test pages and userId module docs * PBID-11: Remove SUBMODULES_THAT_ALWAYS_REFRESH_ID feature from userId system * PBID-11: Use better serialize implementation for Parrable compound cookie * PBID-11: Update parrableIdSystem interface documentation * Add missing extension to mock xhr import * PBID-11: Try to access eid property only when parrableId object exists * PBID-11: Construct parrableId from legacy cookies in same manner as compound cookie * Use hardcoded expiration date for legacy cookies * PBID-39: Return full parrableId object in decode method * PBID-39: Update all adapters to use parrableId.eid for userId value * PBID-39: Update config for ORTB EIDs to extract parrableId.eid as User UID value * PBID-39: Pass Parrable IBA and CCPA optout status into ORTB EIDs list through UID extensions * PBID-39: Pass a true CCPA optout status to adapters when the EID has been suppressed The userId/eids module will not consider our ID system for inclusion in the EIDs object if our ID value is not a string. Unfortunately when we write our cookie without an EID (in the case where CCPA optout is true) then the deserialized EID value is undefined, to save space in the cookie. So this is a hack that will return an empty string when Prebid is building the EIDs object so that we can still pass our optout status to those that require it to understand why our ID may be missing. * parrableIdSystem: Relocate new unit test from upstream * PBID-39: Fallback to cookie values when backend response is missing components Also handle another missed callback scenario if the response object parses to nothing * PBID-39: Avoid breaking openx bid adapter when renaming our id system * PBID-39: Use array find * Use supported array find method in parrableIdSystem_spec * Restore backwards-compatible parrableId passing to OpenxBidAdapter * PBID-25: Add time zone and offset filtering of impressions to parrableIdSystem * PBID-25: Better group existing getId tests * PBID-25: Add unit tests covering time zone and offset filtering functionality * PBID-25: Remove parrable .only test scope --- modules/parrableIdSystem.js | 49 +++ test/spec/modules/parrableIdSystem_spec.js | 352 ++++++++++++++++----- 2 files changed, 314 insertions(+), 87 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 75f89fffc14..2d6a2d6d6e5 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -113,6 +113,51 @@ function migrateLegacyCookies(parrableId) { } } +function shouldFilterImpression(configParams, parrableId) { + const config = configParams.timezoneFilter; + + if (!config) { + return false; + } + + if (parrableId) { + return false; + } + + const offset = (new Date()).getTimezoneOffset() / 60; + const zone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + function isAllowed() { + if (utils.isEmpty(config.allowedZones) && + utils.isEmpty(config.allowedOffsets)) { + return true; + } + if (utils.contains(config.allowedZones, zone)) { + return true; + } + if (utils.contains(config.allowedOffsets, offset)) { + return true; + } + return false; + } + + function isBlocked() { + if (utils.isEmpty(config.blockedZones) && + utils.isEmpty(config.blockedOffsets)) { + return false; + } + if (utils.contains(config.blockedZones, zone)) { + return true; + } + if (utils.contains(config.blockedOffsets, offset)) { + return true; + } + return false; + } + + return !isAllowed() || isBlocked(); +} + function fetchId(configParams) { if (!isValidConfig(configParams)) return; @@ -122,6 +167,10 @@ function fetchId(configParams) { migrateLegacyCookies(parrableId); } + if (shouldFilterImpression(configParams, parrableId)) { + return null; + } + const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 7db22af82ab..1cc89240bc3 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -75,113 +75,116 @@ function removeParrableCookie() { } describe('Parrable ID System', function() { - describe('parrableIdSystem.getId() callback', function() { - let logErrorStub; - let callbackSpy = sinon.spy(); + describe('parrableIdSystem.getId()', function() { + describe('response callback function', function() { + let logErrorStub; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); + callbackSpy.resetHistory(); + writeParrableCookie({ eid: P_COOKIE_EID }); + }); - beforeEach(function() { - logErrorStub = sinon.stub(utils, 'logError'); - callbackSpy.resetHistory(); - writeParrableCookie({ eid: P_COOKIE_EID }); - }); + afterEach(function() { + removeParrableCookie(); + logErrorStub.restore(); + }) - afterEach(function() { - removeParrableCookie(); - logErrorStub.restore(); - }) + it('creates xhr to Parrable that synchronizes the ID', function() { + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); - it('creates xhr to Parrable that synchronizes the ID', function() { - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + getIdResult.callback(callbackSpy); - getIdResult.callback(callbackSpy); + let request = server.requests[0]; + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(queryParams.data)); - let request = server.requests[0]; - let queryParams = utils.parseQS(request.url.split('?')[1]); - let data = JSON.parse(atob(queryParams.data)); + expect(getIdResult.callback).to.be.a('function'); + expect(request.url).to.contain('h.parrable.com'); - expect(getIdResult.callback).to.be.a('function'); - expect(request.url).to.contain('h.parrable.com'); + expect(queryParams).to.not.have.property('us_privacy'); + expect(data).to.deep.equal({ + eid: P_COOKIE_EID, + trackers: P_CONFIG_MOCK.params.partner.split(','), + url: getRefererInfo().referer + }); - expect(queryParams).to.not.have.property('us_privacy'); - expect(data).to.deep.equal({ - eid: P_COOKIE_EID, - trackers: P_CONFIG_MOCK.params.partner.split(','), - url: getRefererInfo().referer - }); + server.requests[0].respond(200, + { 'Content-Type': 'text/plain' }, + JSON.stringify({ eid: P_XHR_EID }) + ); - server.requests[0].respond(200, - { 'Content-Type': 'text/plain' }, - JSON.stringify({ eid: P_XHR_EID }) - ); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({ + eid: P_XHR_EID + }); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({ - eid: P_XHR_EID + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID) + ); }); - expect(storage.getCookie(P_COOKIE_NAME)).to.equal( - encodeURIComponent('eid:' + P_XHR_EID) - ); - }); - - it('xhr passes the uspString to Parrable', function() { - let uspString = '1YNN'; - uspDataHandler.setConsentData(uspString); - parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, - null, - null - ).callback(callbackSpy); - uspDataHandler.setConsentData(null); - expect(server.requests[0].url).to.contain('us_privacy=' + uspString); - }); + it('xhr passes the uspString to Parrable', function() { + let uspString = '1YNN'; + uspDataHandler.setConsentData(uspString); + parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + null + ).callback(callbackSpy); + uspDataHandler.setConsentData(null); + expect(server.requests[0].url).to.contain('us_privacy=' + uspString); + }); - it('should log an error and continue to callback if ajax request errors', function () { - let callBackSpy = sinon.spy(); - let submoduleCallback = parrableIdSubmodule.getId({partner: 'prebid'}).callback; - submoduleCallback(callBackSpy); - let request = server.requests[0]; - expect(request.url).to.contain('h.parrable.com'); - request.respond( - 503, - null, - 'Unavailable' - ); - expect(logErrorStub.calledOnce).to.be.true; - expect(callBackSpy.calledOnce).to.be.true; + it('should log an error and continue to callback if ajax request errors', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = parrableIdSubmodule.getId({partner: 'prebid'}).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.contain('h.parrable.com'); + request.respond( + 503, + null, + 'Unavailable' + ); + expect(logErrorStub.calledOnce).to.be.true; + expect(callBackSpy.calledOnce).to.be.true; + }); }); - }); - describe('parrableIdSystem.getId() id', function() { - it('provides the stored Parrable values if a cookie exists', function() { - writeParrableCookie({ eid: P_COOKIE_EID }); - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); - removeParrableCookie(); + describe('response id', function() { + it('provides the stored Parrable values if a cookie exists', function() { + writeParrableCookie({ eid: P_COOKIE_EID }); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + removeParrableCookie(); - expect(getIdResult.id).to.deep.equal({ - eid: P_COOKIE_EID + expect(getIdResult.id).to.deep.equal({ + eid: P_COOKIE_EID + }); }); - }); - it('provides the stored legacy Parrable ID values if cookies exist', function() { - let oldEid = '01.111.old-eid'; - let oldEidCookieName = '_parrable_eid'; - let oldOptoutCookieName = '_parrable_optout'; + it('provides the stored legacy Parrable ID values if cookies exist', function() { + let oldEid = '01.111.old-eid'; + let oldEidCookieName = '_parrable_eid'; + let oldOptoutCookieName = '_parrable_optout'; - storage.setCookie(oldEidCookieName, oldEid); - storage.setCookie(oldOptoutCookieName, 'true'); + storage.setCookie(oldEidCookieName, oldEid); + storage.setCookie(oldOptoutCookieName, 'true'); - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); - expect(getIdResult.id).to.deep.equal({ - eid: oldEid, - ibaOptout: true - }); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + expect(getIdResult.id).to.deep.equal({ + eid: oldEid, + ibaOptout: true + }); - // The ID system is expected to migrate old cookies to the new format - expect(storage.getCookie(P_COOKIE_NAME)).to.equal( - encodeURIComponent('eid:' + oldEid + ',ibaOptout:1') - ); - expect(storage.getCookie(oldEidCookieName)).to.equal(null); - expect(storage.getCookie(oldOptoutCookieName)).to.equal(null); + // The ID system is expected to migrate old cookies to the new format + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + oldEid + ',ibaOptout:1') + ); + expect(storage.getCookie(oldEidCookieName)).to.equal(null); + expect(storage.getCookie(oldOptoutCookieName)).to.equal(null); + removeParrableCookie(); + }); }); }); @@ -199,6 +202,181 @@ describe('Parrable ID System', function() { }); }); + describe('timezone filtering', function() { + before(function() { + sinon.stub(Intl, 'DateTimeFormat'); + }); + + after(function() { + Intl.DateTimeFormat.restore(); + }); + + it('permits an impression when no timezoneFilter is configured', function() { + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + })).to.have.property('callback'); + }); + + it('permits an impression from a blocked timezone when a cookie exists', function() { + const blockedZone = 'Antarctica/South_Pole'; + const resolvedOptions = sinon.stub().returns({ timeZone: blockedZone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + writeParrableCookie({ eid: P_COOKIE_EID }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedZones: [ blockedZone ] + } + })).to.have.property('callback'); + expect(resolvedOptions.called).to.equal(false); + + removeParrableCookie(); + }) + + it('permits an impression from an allowed timezone', function() { + const allowedZone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: allowedZone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + allowedZones: [ allowedZone ] + } + })).to.have.property('callback'); + expect(resolvedOptions.called).to.equal(true); + }); + + it('permits an impression from a timezone that is not blocked', function() { + const blockedZone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: 'Iceland' }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedZones: [ blockedZone ] + } + })).to.have.property('callback'); + expect(resolvedOptions.called).to.equal(true); + }); + + it('does not permit an impression from a blocked timezone', function() { + const blockedZone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: blockedZone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedZones: [ blockedZone ] + } + })).to.equal(null); + expect(resolvedOptions.called).to.equal(true); + }); + + it('does not permit an impression from a blocked timezone even when also allowed', function() { + const timezone = 'America/New_York'; + const resolvedOptions = sinon.stub().returns({ timeZone: timezone }); + Intl.DateTimeFormat.returns({ resolvedOptions }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + allowedZones: [ timezone ], + blockedZones: [ timezone ] + } + })).to.equal(null); + expect(resolvedOptions.called).to.equal(true); + }); + }); + + describe('timezone offset filtering', function() { + before(function() { + sinon.stub(Date.prototype, 'getTimezoneOffset'); + }); + + afterEach(function() { + Date.prototype.getTimezoneOffset.reset(); + }) + + after(function() { + Date.prototype.getTimezoneOffset.restore(); + }); + + it('permits an impression from a blocked offset when a cookie exists', function() { + const blockedOffset = -4; + Date.prototype.getTimezoneOffset.returns(blockedOffset * 60); + + writeParrableCookie({ eid: P_COOKIE_EID }); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedOffsets: [ blockedOffset ] + } + })).to.have.property('callback'); + + removeParrableCookie(); + }); + + it('permits an impression from an allowed offset', function() { + const allowedOffset = -5; + Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + allowedOffsets: [ allowedOffset ] + } + })).to.have.property('callback'); + expect(Date.prototype.getTimezoneOffset.called).to.equal(true); + }); + + it('permits an impression from an offset that is not blocked', function() { + const allowedOffset = -5; + const blockedOffset = 5; + Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedOffsets: [ blockedOffset ] + } + })).to.have.property('callback'); + expect(Date.prototype.getTimezoneOffset.called).to.equal(true); + }); + + it('does not permit an impression from a blocked offset', function() { + const blockedOffset = -5; + Date.prototype.getTimezoneOffset.returns(blockedOffset * 60); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + blockedOffsets: [ blockedOffset ] + } + })).to.equal(null); + expect(Date.prototype.getTimezoneOffset.called).to.equal(true); + }); + + it('does not permit an impression from a blocked offset even when also allowed', function() { + const offset = -5; + Date.prototype.getTimezoneOffset.returns(offset * 60); + + expect(parrableIdSubmodule.getId({ + partner: 'prebid-test', + timezoneFilter: { + allowedOffset: [ offset ], + blockedOffsets: [ offset ] + } + })).to.equal(null); + expect(Date.prototype.getTimezoneOffset.called).to.equal(true); + }); + }); + describe('userId requestBids hook', function() { let adUnits; From 7743713c508529e49453371897a713728cbcf81a Mon Sep 17 00:00:00 2001 From: Klaas-Jan Boon Date: Fri, 4 Sep 2020 22:09:26 +0200 Subject: [PATCH 0150/1476] Blue Billywig bid adapter update (#5584) * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Code quality update, always hit user syncs, improved video params Co-authored-by: Klaas-Jan Boon Co-authored-by: Klaas-Jan Boon --- modules/bluebillywigBidAdapter.js | 189 ++++++++---------- .../modules/bluebillywigBidAdapter_spec.js | 177 +++++++++++++++- 2 files changed, 250 insertions(+), 116 deletions(-) diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js index 4d40d931e1d..afacc48aa8e 100644 --- a/modules/bluebillywigBidAdapter.js +++ b/modules/bluebillywigBidAdapter.js @@ -1,4 +1,5 @@ import * as utils from '../src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; @@ -17,7 +18,10 @@ const BB_CONSTANTS = { DEFAULT_TTL: 300, DEFAULT_WIDTH: 768, DEFAULT_HEIGHT: 432, - DEFAULT_NET_REVENUE: true + DEFAULT_NET_REVENUE: true, + VIDEO_PARAMS: ['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'] }; // Aliasing @@ -26,8 +30,6 @@ const getConfig = config.getConfig; // Helper Functions export const BB_HELPERS = { addSiteAppDevice: function(request, pageUrl) { - if (!request) return; - if (typeof getConfig('app') === 'object') request.app = getConfig('app'); else { request.site = {}; @@ -41,21 +43,15 @@ export const BB_HELPERS = { if (!request.device.h) request.device.h = window.innerHeight; }, addSchain: function(request, validBidRequests) { - if (!request) return; - const schain = utils.deepAccess(validBidRequests, '0.schain'); if (schain) request.source.ext = { schain: schain }; }, addCurrency: function(request) { - if (!request) return; - const adServerCur = getConfig('currency.adServerCurrency'); if (adServerCur && typeof adServerCur === 'string') request.cur = [adServerCur]; else if (Array.isArray(adServerCur) && adServerCur.length) request.cur = [adServerCur[0]]; }, addUserIds: function(request, validBidRequests) { - if (!request) return; - const bidUserId = utils.deepAccess(validBidRequests, '0.userId'); const eids = createEidsArray(bidUserId); @@ -63,10 +59,6 @@ export const BB_HELPERS = { utils.deepSetValue(request, 'user.ext.eids', eids); } }, - addDigiTrust: function(request, bidRequests) { - const digiTrust = BB_HELPERS.getDigiTrustParams(bidRequests && bidRequests[0]); - if (digiTrust) utils.deepSetValue(request, 'user.ext.digitrust', digiTrust); - }, substituteUrl: function (url, publication, renderer) { return url.replace('$$URL_START', (DEV_MODE) ? 'https://dev.' : 'https://').replace('$$PUBLICATION', publication).replace('$$RENDERER', renderer); }, @@ -79,41 +71,59 @@ export const BB_HELPERS = { getRendererUrl: function(publication, renderer) { return BB_HELPERS.substituteUrl(BB_CONSTANTS.RENDERER_URL, publication, renderer); }, - getDigiTrustParams: function(bidRequest) { - const digiTrustId = BB_HELPERS.getDigiTrustId(bidRequest); + transformVideoParams: function(videoParams, videoParamsExt) { + videoParams = utils.deepClone(videoParams); - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) return null; - return { - id: digiTrustId.id, - keyv: digiTrustId.keyv - } - }, - getDigiTrustId: function(bidRequest) { - const bidRequestDigiTrust = utils.deepAccess(bidRequest, 'userId.digitrustid.data'); - if (bidRequestDigiTrust) return bidRequestDigiTrust; + let playerSize = videoParams.playerSize || [BB_CONSTANTS.DEFAULT_WIDTH, BB_CONSTANTS.DEFAULT_HEIGHT]; + if (Array.isArray(playerSize[0])) playerSize = playerSize[0]; + + videoParams.w = playerSize[0]; + videoParams.h = playerSize[1]; + videoParams.placement = 3; + + if (videoParamsExt) videoParams = Object.assign(videoParams, videoParamsExt); + + const videoParamsProperties = Object.keys(videoParams); - const digiTrustUser = getConfig('digiTrustId'); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; + videoParamsProperties.forEach(property => { + if (BB_CONSTANTS.VIDEO_PARAMS.indexOf(property) === -1) delete videoParams[property]; + }); + + return videoParams; }, transformRTBToPrebidProps: function(bid, serverResponse) { - bid.cpm = bid.price; delete bid.price; - bid.bidId = bid.impid; - bid.requestId = bid.impid; delete bid.impid; - bid.width = bid.w || BB_CONSTANTS.DEFAULT_WIDTH; - bid.height = bid.h || BB_CONSTANTS.DEFAULT_HEIGHT; + const bidObject = { + cpm: bid.price, + currency: serverResponse.cur, + netRevenue: BB_CONSTANTS.DEFAULT_NET_REVENUE, + bidId: bid.impid, + requestId: bid.impid, + creativeId: bid.crid, + mediaType: VIDEO, + width: bid.w || BB_CONSTANTS.DEFAULT_WIDTH, + height: bid.h || BB_CONSTANTS.DEFAULT_HEIGHT, + ttl: BB_CONSTANTS.DEFAULT_TTL + }; + + const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting'); + const extPrebidCache = utils.deepAccess(bid, 'ext.prebid.cache'); + + if (extPrebidCache && typeof extPrebidCache.vastXml === 'object' && extPrebidCache.vastXml.cacheId && extPrebidCache.vastXml.url) { + bidObject.videoCacheKey = extPrebidCache.vastXml.cacheId; + bidObject.vastUrl = extPrebidCache.vastXml.url; + } else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_host && extPrebidTargeting.hb_cache_path) { + bidObject.videoCacheKey = extPrebidTargeting.hb_uuid; + bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`; + } if (bid.adm) { - bid.ad = bid.adm; - bid.vastXml = bid.adm; - delete bid.adm; + bidObject.ad = bid.adm; + bidObject.vastXml = bid.adm; } - if (bid.nurl && !bid.adm) { // ad markup is on win notice url, and adm is ommited according to OpenRTB 2.5 - bid.vastUrl = bid.nurl; - delete bid.nurl; + if (!bidObject.vastUrl && bid.nurl && !bid.adm) { // ad markup is on win notice url, and adm is ommited according to OpenRTB 2.5 + bidObject.vastUrl = bid.nurl; } - bid.netRevenue = BB_CONSTANTS.DEFAULT_NET_REVENUE; - bid.creativeId = bid.crid; delete bid.crid; - bid.currency = serverResponse.cur; - bid.ttl = BB_CONSTANTS.DEFAULT_TTL; + + return bidObject; }, }; @@ -132,18 +142,14 @@ const BB_RENDERER = { return; } - const rendererId = BB_RENDERER.getRendererId(bid.publicationName, bid.rendererCode); + if (!(window.bluebillywig && window.bluebillywig.renderers)) { + utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: renderer code failed to initialize...`); + return; + } + const rendererId = BB_RENDERER.getRendererId(bid.publicationName, bid.rendererCode); const ele = document.getElementById(bid.adUnitCode); // NB convention - - let renderer; - - for (let rendererIndex = 0; rendererIndex < window.bluebillywig.renderers.length; rendererIndex++) { - if (window.bluebillywig.renderers[rendererIndex]._id === rendererId) { - renderer = window.bluebillywig.renderers[rendererIndex]; - break; - } - } + const renderer = find(window.bluebillywig.renderers, r => r._id === rendererId); if (renderer) renderer.bootstrap(config, ele); else utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: Couldn't find a renderer with ${rendererId}`); @@ -212,9 +218,8 @@ export const spec = { utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: connections is not of type array. Rejecting bid: `, bid); return false; } else { - for (let connectionIndex = 0; connectionIndex < bid.params.connections.length; connectionIndex++) { - const connection = bid.params.connections[connectionIndex]; - if (!bid.params.hasOwnProperty(connection)) { + for (let i = 0; i < bid.params.connections.length; i++) { + if (!bid.params.hasOwnProperty(bid.params.connections[i])) { utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: connection specified in params.connections, but not configured in params. Rejecting bid: `, bid); return false; } @@ -225,6 +230,11 @@ export const spec = { return false; } + if (bid.params.hasOwnProperty('video') && (bid.params.video === null || typeof bid.params.video !== 'object')) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: params.video must be of type object. Rejecting bid: `, bid); + return false; + } + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no context specified in bid. Rejecting bid: `, bid); @@ -245,20 +255,21 @@ export const spec = { buildRequests(validBidRequests, bidderRequest) { const imps = []; - for (let validBidRequestIndex = 0; validBidRequestIndex < validBidRequests.length; validBidRequestIndex++) { - const validBidRequest = validBidRequests[validBidRequestIndex]; - const _this = this; + validBidRequests.forEach(validBidRequest => { + if (!this.syncStore.publicationName) this.syncStore.publicationName = validBidRequest.params.publicationName; + if (!this.syncStore.accountId) this.syncStore.accountId = validBidRequest.params.accountId; - const ext = validBidRequest.params.connections.reduce(function(extBuilder, connection) { + const ext = validBidRequest.params.connections.reduce((extBuilder, connection) => { extBuilder[connection] = validBidRequest.params[connection]; - if (_this.syncStore.bidders.indexOf(connection) === -1) _this.syncStore.bidders.push(connection); + if (this.syncStore.bidders.indexOf(connection) === -1) this.syncStore.bidders.push(connection); return extBuilder; }, {}); - imps.push({ id: validBidRequest.bidId, ext, secure: window.location.protocol === 'https' ? 1 : 0, video: utils.deepAccess(validBidRequest, 'mediaTypes.video') }); - } + const videoParams = BB_HELPERS.transformVideoParams(utils.deepAccess(validBidRequest, 'mediaTypes.video'), utils.deepAccess(validBidRequest, 'params.video')); + imps.push({ id: validBidRequest.bidId, ext, secure: window.location.protocol === 'https' ? 1 : 0, video: videoParams }); + }); const request = { id: bidderRequest.auctionId, @@ -293,7 +304,6 @@ export const spec = { BB_HELPERS.addSchain(request, validBidRequests); BB_HELPERS.addCurrency(request); BB_HELPERS.addUserIds(request, validBidRequests); - BB_HELPERS.addDigiTrust(request, validBidRequests); return { method: 'POST', @@ -311,74 +321,43 @@ export const spec = { const bids = []; - for (let seatbidIndex = 0; seatbidIndex < serverResponse.seatbid.length; seatbidIndex++) { - const seatbid = serverResponse.seatbid[seatbidIndex]; - if (!seatbid.bid || !Array.isArray(seatbid.bid)) continue; - for (let bidIndex = 0; bidIndex < seatbid.bid.length; bidIndex++) { - const bid = seatbid.bid[bidIndex]; - BB_HELPERS.transformRTBToPrebidProps(bid, serverResponse); - - let bidParams; - for (let bidderRequestBidsIndex = 0; bidderRequestBidsIndex < request.bidderRequest.bids.length; bidderRequestBidsIndex++) { - if (request.bidderRequest.bids[bidderRequestBidsIndex].bidId === bid.bidId) { - bidParams = request.bidderRequest.bids[bidderRequestBidsIndex].params; - } - } + serverResponse.seatbid.forEach(seatbid => { + if (!seatbid.bid || !Array.isArray(seatbid.bid)) return; + seatbid.bid.forEach(bid => { + bid = BB_HELPERS.transformRTBToPrebidProps(bid, serverResponse); - if (bidParams) { - bid.publicationName = bidParams.publicationName; - bid.rendererCode = bidParams.rendererCode; - bid.accountId = bidParams.accountId; - } + const bidParams = find(request.bidderRequest.bids, bidderRequestBid => bidderRequestBid.bidId === bid.bidId).params; + bid.publicationName = bidParams.publicationName; + bid.rendererCode = bidParams.rendererCode; + bid.accountId = bidParams.accountId; const rendererUrl = BB_HELPERS.getRendererUrl(bid.publicationName, bid.rendererCode); - bid.renderer = BB_RENDERER.newRenderer(rendererUrl, bid.adUnitCode); bids.push(bid); - } - } + }); + }); return bids; }, getUserSyncs(syncOptions, serverResponses, gdpr) { - if (!serverResponses || !serverResponses.length) return []; if (!syncOptions.iframeEnabled) return []; const queryString = []; - let accountId; - let publication; - - const serverResponse = serverResponses[0]; - if (!serverResponse.body || !serverResponse.body.seatbid) return []; - - for (let seatbidIndex = 0; seatbidIndex < serverResponse.body.seatbid.length; seatbidIndex++) { - const seatbid = serverResponse.body.seatbid[seatbidIndex]; - for (let bidIndex = 0; bidIndex < seatbid.bid.length; bidIndex++) { - const bid = seatbid.bid[bidIndex]; - accountId = bid.accountId || null; - publication = bid.publicationName || null; - - if (publication && accountId) break; - } - if (publication && accountId) break; - } - - if (!publication || !accountId) return []; if (gdpr.gdprApplies) queryString.push(`gdpr=${gdpr.gdprApplies ? 1 : 0}`); if (gdpr.gdprApplies && gdpr.consentString) queryString.push(`gdpr_consent=${gdpr.consentString}`); if (this.syncStore.uspConsent) queryString.push(`usp_consent=${this.syncStore.uspConsent}`); - queryString.push(`accountId=${accountId}`); + queryString.push(`accountId=${this.syncStore.accountId}`); queryString.push(`bidders=${btoa(JSON.stringify(this.syncStore.bidders))}`); queryString.push(`cb=${Date.now()}-${Math.random().toString().replace('.', '')}`); if (DEV_MODE) queryString.push('bbpbs_debug=true'); // NB syncUrl by default starts with ?pub=$$PUBLICATION - const syncUrl = `${BB_HELPERS.getSyncUrl(publication)}&${queryString.join('&')}`; + const syncUrl = `${BB_HELPERS.getSyncUrl(this.syncStore.publicationName)}&${queryString.join('&')}`; return [{ type: 'iframe', diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js index 73bd4803358..56ea29d6d73 100644 --- a/test/spec/modules/bluebillywigBidAdapter_spec.js +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -40,6 +40,13 @@ describe('BlueBillywigAdapter', () => { expect(spec.isBidRequestValid(baseValidBid)).to.equal(true); }); + it('should return false when params missing', () => { + const bid = deepClone(baseValidBid); + delete bid.params; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + it('should return false when publicationName is missing', () => { const bid = deepClone(baseValidBid); delete bid.params.publicationName; @@ -184,6 +191,25 @@ describe('BlueBillywigAdapter', () => { bid.mediaTypes[VIDEO].context = 'instream'; expect(spec.isBidRequestValid(bid)).to.equal(false); }); + + it('should fail if video is specified but is not an object', () => { + const bid = deepClone(baseValidBid); + + bid.params.video = null; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.video = 'string'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.video = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.video = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.video = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); describe('buildRequests', () => { @@ -510,30 +536,64 @@ describe('BlueBillywigAdapter', () => { expect(deepAccess(payload, 'user.ext.eids')).to.be.undefined; }); - it('should set digitrust when present on bid', () => { - const digiTrust = {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}; + it('should set imp.0.video.[w|h|placement] by default', () => { + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + + expect(deepAccess(payload, 'imp.0.video.w')).to.equal(768); + expect(deepAccess(payload, 'imp.0.video.h')).to.equal(432); + expect(deepAccess(payload, 'imp.0.video.placement')).to.equal(3); + }); + it('should update imp0.video.[w|h] when present in config', () => { const newBaseValidBidRequests = deepClone(baseValidBidRequests); - newBaseValidBidRequests[0].userId = { digitrustid: digiTrust }; + newBaseValidBidRequests[0].mediaTypes.video.playerSize = [1, 1]; const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); const payload = JSON.parse(request.data); - expect(payload).to.have.nested.property('user.ext.digitrust'); - expect(payload.user.ext.digitrust.id).to.equal(digiTrust.data.id); - expect(payload.user.ext.digitrust.keyv).to.equal(digiTrust.data.keyv); + expect(deepAccess(payload, 'imp.0.video.w')).to.equal(1); + expect(deepAccess(payload, 'imp.0.video.h')).to.equal(1); }); - it('should not set digitrust when opted out', () => { - const digiTrust = {data: {id: 'DTID', keyv: 4, privacy: {optout: true}, producer: 'ABC', version: 2}}; + it('should allow overriding any imp0.video key through params.video', () => { + const newBaseValidBidRequests = deepClone(baseValidBidRequests); + newBaseValidBidRequests[0].params.video = { + w: 2, + h: 2, + placement: 1, + minduration: 15, + maxduration: 30 + }; + + const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(deepAccess(payload, 'imp.0.video.w')).to.equal(2); + expect(deepAccess(payload, 'imp.0.video.h')).to.equal(2); + expect(deepAccess(payload, 'imp.0.video.placement')).to.equal(1); + expect(deepAccess(payload, 'imp.0.video.minduration')).to.equal(15); + expect(deepAccess(payload, 'imp.0.video.maxduration')).to.equal(30); + }); + + it('should not allow placing any non-OpenRTB 2.5 keys on imp.0.video through params.video', () => { const newBaseValidBidRequests = deepClone(baseValidBidRequests); - newBaseValidBidRequests[0].userId = { digitrustid: digiTrust }; + newBaseValidBidRequests[0].params.video = { + 'true': true, + 'testing': 'some', + 123: {}, + '': 'values' + }; const request = spec.buildRequests(newBaseValidBidRequests, validBidderRequest); const payload = JSON.parse(request.data); - expect(deepAccess(payload, 'user.ext.digitrust')).to.be.undefined; + expect(deepAccess(request, 'imp.0.video.true')).to.be.undefined; + expect(deepAccess(payload, 'imp.0.video.testing')).to.be.undefined; + expect(deepAccess(payload, 'imp.0.video.123')).to.be.undefined; + expect(deepAccess(payload, 'imp.0.video.')).to.be.undefined; }); }); describe('interpretResponse', () => { @@ -571,7 +631,7 @@ describe('BlueBillywigAdapter', () => { bidder: BB_CONSTANTS.BIDDER_CODE, bidderRequestId: '1a2345b67c8d9e0', params: baseValidBid.params, - sizes: [[768, 432], [640, 480], [630, 360]], + sizes: [[640, 480], [630, 360]], transactionId: '2b34c5de-f67a-8901-bcd2-34567efabc89' }], start: 11585918458869, @@ -759,6 +819,101 @@ describe('BlueBillywigAdapter', () => { expect(result.length).to.equal(0); } }); + + it('should take default width and height when w/h not present', () => { + const bidSizesMissing = deepClone(serverResponse); + + delete bidSizesMissing.body.seatbid[0].bid[0].w; + delete bidSizesMissing.body.seatbid[0].bid[0].h; + + const response = bidSizesMissing; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(deepAccess(result, '0.width')).to.equal(768); + expect(deepAccess(result, '0.height')).to.equal(432); + }); + + it('should take nurl value when adm not present', () => { + const bidAdmMissing = deepClone(serverResponse); + + delete bidAdmMissing.body.seatbid[0].bid[0].adm; + bidAdmMissing.body.seatbid[0].bid[0].nurl = 'https://bluebillywig.com'; + + const response = bidAdmMissing; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(deepAccess(result, '0.vastXml')).to.be.undefined; + expect(deepAccess(result, '0.vastUrl')).to.equal('https://bluebillywig.com'); + }); + + it('should not take nurl value when adm present', () => { + const bidAdmNurlPresent = deepClone(serverResponse); + + bidAdmNurlPresent.body.seatbid[0].bid[0].nurl = 'https://bluebillywig.com'; + + const response = bidAdmNurlPresent; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(deepAccess(result, '0.vastXml')).to.equal(bidAdmNurlPresent.body.seatbid[0].bid[0].adm); + expect(deepAccess(result, '0.vastUrl')).to.be.undefined; + }); + + it('should take ext.prebid.cache data when present, ignore ext.prebid.targeting and nurl', () => { + const bidExtPrebidCache = deepClone(serverResponse); + + delete bidExtPrebidCache.body.seatbid[0].bid[0].adm; + bidExtPrebidCache.body.seatbid[0].bid[0].nurl = 'https://notnurl.com'; + + bidExtPrebidCache.body.seatbid[0].bid[0].ext = { + prebid: { + cache: { + vastXml: { + url: 'https://bluebillywig.com', + cacheId: '12345' + } + }, + targeting: { + hb_uuid: '23456', + hb_cache_host: 'bluebillywig.com', + hb_cache_path: '/cache' + } + } + }; + + const response = bidExtPrebidCache; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(deepAccess(result, '0.vastUrl')).to.equal('https://bluebillywig.com'); + expect(deepAccess(result, '0.videoCacheKey')).to.equal('12345'); + }); + + it('should take ext.prebid.targeting data when ext.prebid.cache not present, and ignore nurl', () => { + const bidExtPrebidTargeting = deepClone(serverResponse); + + delete bidExtPrebidTargeting.body.seatbid[0].bid[0].adm; + bidExtPrebidTargeting.body.seatbid[0].bid[0].nurl = 'https://notnurl.com'; + + bidExtPrebidTargeting.body.seatbid[0].bid[0].ext = { + prebid: { + targeting: { + hb_uuid: '34567', + hb_cache_host: 'bluebillywig.com', + hb_cache_path: '/cache' + } + } + }; + + const response = bidExtPrebidTargeting; + const request = spec.buildRequests(baseValidBidRequests, validBidderRequest); + const result = spec.interpretResponse(response, request); + + expect(deepAccess(result, '0.vastUrl')).to.equal('https://bluebillywig.com/cache?uuid=34567'); + expect(deepAccess(result, '0.videoCacheKey')).to.equal('34567'); + }); }); describe('getUserSyncs', () => { const publicationName = 'bbprebid.dev'; From 9626398920021cf2b745e0998b73147c746bc618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Sat, 5 Sep 2020 18:53:25 +0300 Subject: [PATCH 0151/1476] Vidazoo Adapter: refactor/user-sync (#5654) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(client): initial refactor commit * fix(client): lint issues Co-authored-by: roman --- modules/vidazooBidAdapter.js | 45 +++++---------------- test/spec/modules/vidazooBidAdapter_spec.js | 4 +- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 382833fbca9..0718f22d0d2 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -12,14 +12,6 @@ const TTL_SECONDS = 60 * 5; const DEAL_ID_EXPIRY = 1000 * 60 * 15; const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; const SESSION_ID_KEY = 'vidSid'; -const INTERNAL_SYNC_TYPE = { - IFRAME: 'iframe', - IMAGE: 'img' -}; -const EXTERNAL_SYNC_TYPE = { - IFRAME: 'iframe', - IMAGE: 'image' -}; export const SUPPORTED_ID_SYSTEMS = { 'britepoolid': 1, 'criteoId': 1, @@ -176,39 +168,24 @@ function interpretResponse(serverResponse, request) { } } -function getUserSyncs(syncOptions, responses) { +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 || '')}` if (iframeEnabled) { - return [{ + syncs.push({ type: 'iframe', - url: 'https://static.cootlogix.com/basev/sync/user_sync.html' - }]; + url: `https://prebid.cootlogix.com/api/sync/iframe/${params}` + }); } - if (pixelEnabled) { - const lookup = {}; - const syncs = []; - responses.forEach(response => { - const { body } = response; - const results = body ? body.results || [] : []; - results.forEach(result => { - (result.cookies || []).forEach(cookie => { - if (cookie.type === INTERNAL_SYNC_TYPE.IMAGE) { - if (pixelEnabled && !lookup[cookie.src]) { - syncs.push({ - type: EXTERNAL_SYNC_TYPE.IMAGE, - url: cookie.src - }); - } - } - }); - }); + syncs.push({ + type: 'image', + url: `https://prebid.cootlogix.com/api/sync/image/${params}` }); - return syncs; } - - return []; + return syncs; } export function hashCode(s, prefix = '_') { diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 8b3a492d2e5..1a503e46b5c 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -179,7 +179,7 @@ describe('VidazooBidAdapter', function () { expect(result).to.deep.equal([{ type: 'iframe', - url: 'https://static.cootlogix.com/basev/sync/user_sync.html' + url: 'https://prebid.cootlogix.com/api/sync/iframe/?gdpr=0&gdpr_consent=&us_privacy=' }]); }); @@ -187,7 +187,7 @@ describe('VidazooBidAdapter', function () { const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ - 'url': 'https://sync.com', + 'url': 'https://prebid.cootlogix.com/api/sync/image/?gdpr=0&gdpr_consent=&us_privacy=', 'type': 'image' }]); }) From 783a3bb9af8788584e2ee27f7853a13d73f6ecf9 Mon Sep 17 00:00:00 2001 From: Junus Date: Mon, 7 Sep 2020 22:49:51 +0600 Subject: [PATCH 0152/1476] New Bid Adapter: a4g (#5688) * Updated a4g adapter * use https and mediaTypes sizes --- modules/a4gBidAdapter.js | 90 ++++++++++++ modules/a4gBidAdapter.md | 18 ++- test/spec/modules/a4gBidAdapter_spec.js | 173 ++++++++++++++++++++++++ 3 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 modules/a4gBidAdapter.js create mode 100644 test/spec/modules/a4gBidAdapter_spec.js diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js new file mode 100644 index 00000000000..b7d8722e9f9 --- /dev/null +++ b/modules/a4gBidAdapter.js @@ -0,0 +1,90 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; + +const A4G_BIDDER_CODE = 'a4g'; +const A4G_CURRENCY = 'USD'; +const A4G_DEFAULT_BID_URL = 'https://ads.ad4game.com/v1/bid'; +const A4G_TTL = 120; + +const LOCATION_PARAM_NAME = 'siteurl'; +const ID_PARAM_NAME = 'id'; +const IFRAME_PARAM_NAME = 'if'; +const ZONE_ID_PARAM_NAME = 'zoneId'; +const SIZE_PARAM_NAME = 'size'; + +const ARRAY_PARAM_SEPARATOR = ';'; +const ARRAY_SIZE_SEPARATOR = ','; +const SIZE_SEPARATOR = 'x'; + +export const spec = { + code: A4G_BIDDER_CODE, + isBidRequestValid: function(bid) { + return bid.params && !!bid.params.zoneId; + }, + + buildRequests: function(validBidRequests, bidderRequest) { + let deliveryUrl = ''; + const idParams = []; + const sizeParams = []; + const zoneIds = []; + + utils._each(validBidRequests, function(bid) { + if (!deliveryUrl && typeof bid.params.deliveryUrl === 'string') { + deliveryUrl = bid.params.deliveryUrl; + } + idParams.push(bid.bidId); + let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes; + sizeParams.push(bidSizes.map(size => size.join(SIZE_SEPARATOR)).join(ARRAY_SIZE_SEPARATOR)); + zoneIds.push(bid.params.zoneId); + }); + + if (!deliveryUrl) { + deliveryUrl = A4G_DEFAULT_BID_URL; + } + + let data = { + [IFRAME_PARAM_NAME]: 0, + [LOCATION_PARAM_NAME]: (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) ? bidderRequest.refererInfo.referer : window.location.href, + [SIZE_PARAM_NAME]: sizeParams.join(ARRAY_PARAM_SEPARATOR), + [ID_PARAM_NAME]: idParams.join(ARRAY_PARAM_SEPARATOR), + [ZONE_ID_PARAM_NAME]: zoneIds.join(ARRAY_PARAM_SEPARATOR) + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + data.gdpr = { + applies: bidderRequest.gdprConsent.gdprApplies, + consent: bidderRequest.gdprConsent.consentString + }; + } + + return { + method: 'GET', + url: deliveryUrl, + data: data + }; + }, + + interpretResponse: function(serverResponses, request) { + const bidResponses = []; + utils._each(serverResponses.body, function(response) { + if (response.cpm > 0) { + const bidResponse = { + requestId: response.id, + creativeId: response.id, + adId: response.id, + cpm: response.cpm, + width: response.width, + height: response.height, + currency: A4G_CURRENCY, + netRevenue: true, + ttl: A4G_TTL, + ad: response.ad + }; + bidResponses.push(bidResponse); + } + }); + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/a4gBidAdapter.md b/modules/a4gBidAdapter.md index dcab312ed29..70f110724b0 100644 --- a/modules/a4gBidAdapter.md +++ b/modules/a4gBidAdapter.md @@ -6,32 +6,40 @@ Maintainer: devops@ad4game.com # Description -Ad4Game Bidder Adapter for Prebid.js. It should be tested on real domain. `localhost` should be rewritten (ex. example.com). +Ad4Game Bidder Adapter for Prebid.js. It should be tested on real domain. `localhost` should be rewritten (ex. example.com). # Test Parameters ``` var adUnits = [ { code: 'test-div', - sizes: [[300, 250]], // a display size + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, bids: [ { bidder: 'a4g', params: { zoneId: 59304, - deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + deliveryUrl: 'https://dev01.ad4game.com/v1/bid' } } ] },{ code: 'test-div', - sizes: [[300, 50]], // a mobile size + mediaTypes: { + banner: { + sizes: [[300, 50]], // a mobile size + } + }, bids: [ { bidder: 'a4g', params: { zoneId: 59354, - deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + deliveryUrl: 'https://dev01.ad4game.com/v1/bid' } } ] diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js new file mode 100644 index 00000000000..3dccbb28426 --- /dev/null +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -0,0 +1,173 @@ +import { expect } from 'chai'; +import { spec } from 'modules/a4gBidAdapter.js'; + +describe('a4gAdapterTests', function () { + describe('bidRequestValidity', function () { + it('bidRequest with zoneId and deliveryUrl params', function () { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + zoneId: 59304, + deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + } + })).to.equal(true); + }); + + it('bidRequest with only zoneId', function () { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + zoneId: 59304 + } + })).to.equal(true); + }); + + it('bidRequest with only deliveryUrl', function () { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + } + })).to.equal(false); + }); + }); + + describe('bidRequest', function () { + const DEFAULT_OPTION = { + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' + } + }; + + const bidRequests = [{ + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59304, + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 50], [300, 250], [300, 600]] + } + }, + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }, { + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59354, + 'deliveryUrl': '//dev01.ad4game.com/v1/bid' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 50], [300, 250], [300, 600]] + } + }, + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest method', function () { + const request = spec.buildRequests(bidRequests, DEFAULT_OPTION); + expect(request.method).to.equal('GET'); + }); + + it('bidRequest url', function () { + const request = spec.buildRequests(bidRequests, DEFAULT_OPTION); + expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`)); + }); + + it('bidRequest data', function () { + const request = spec.buildRequests(bidRequests, DEFAULT_OPTION); + expect(request.data).to.exist; + }); + + it('bidRequest zoneIds', function () { + const request = spec.buildRequests(bidRequests, DEFAULT_OPTION); + expect(request.data.zoneId).to.equal('59304;59354'); + }); + + it('bidRequest gdpr consent', function () { + const consentString = 'consentString'; + const bidderRequest = { + bidderCode: 'a4g', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + timeout: 3000, + gdprConsent: { + consentString: consentString, + gdprApplies: true + }, + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr.applies).to.exist.and.to.be.true; + expect(request.data.gdpr.consent).to.exist.and.to.equal(consentString); + }); + }); + + describe('interpretResponse', function () { + const bidRequest = [{ + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59304, + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 50], [300, 250], [300, 600]] + } + }, + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + const bidResponse = { + body: [{ + 'id': 'div-gpt-ad-1460505748561-0', + 'ad': 'test ad', + 'width': 320, + 'height': 250, + 'cpm': 5.2 + }], + headers: {} + }; + + it('required keys', function () { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let requiredKeys = [ + 'requestId', + 'creativeId', + 'adId', + 'cpm', + 'width', + 'height', + 'currency', + 'netRevenue', + 'ttl', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function(key) { + expect(requiredKeys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +}); From cacb5eb7412b5f61886f52cd409fee1809a5400e Mon Sep 17 00:00:00 2001 From: ofirpaBrowsi <55348874+ofirpaBrowsi@users.noreply.github.com> Date: Tue, 8 Sep 2020 18:39:37 +0300 Subject: [PATCH 0153/1476] Adding errors event listener (#5563) * Adding errors event listener * Changed event name to 'auctionDebug', with 'type' property, to support future debug event types. * Update AnalyticsAdapter.js * Update AnalyticsAdapter_spec.js * Update AnalyticsAdapter_spec.js * Update AnalyticsAdapter.js * Removed trailing spaces * fixed tests assertion to handle new error events * Fixed analytics test to expect auctionDebug event too * Update yuktamediaAnalyticsAdapter_spec.js * Removed port dependency on readpeak adapter's test Co-authored-by: Patrick McCann --- src/AnalyticsAdapter.js | 2 + src/constants.json | 3 +- src/utils.js | 2 + test/spec/AnalyticsAdapter_spec.js | 12 ++ .../prebidmanagerAnalyticsAdapter_spec.js | 2 +- test/spec/modules/readpeakBidAdapter_spec.js | 13 +- .../modules/sigmoidAnalyticsAdapter_spec.js | 2 +- .../yuktamediaAnalyticsAdapter_spec.js | 176 +++++++++++++++++- 8 files changed, 196 insertions(+), 16 deletions(-) diff --git a/src/AnalyticsAdapter.js b/src/AnalyticsAdapter.js index f3297412a35..80c12a3eb8e 100644 --- a/src/AnalyticsAdapter.js +++ b/src/AnalyticsAdapter.js @@ -18,6 +18,7 @@ const { BIDDER_DONE, SET_TARGETING, AD_RENDER_FAILED, + AUCTION_DEBUG, ADD_AD_UNITS } } = CONSTANTS; @@ -112,6 +113,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } [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 }), + [AUCTION_DEBUG]: args => this.enqueue({ eventType: AUCTION_DEBUG, args }), [ADD_AD_UNITS]: args => this.enqueue({ eventType: ADD_AD_UNITS, args }), [AUCTION_INIT]: args => { args.config = typeof config === 'object' ? config.options || {} : {}; // enableAnaltyics configuration object diff --git a/src/constants.json b/src/constants.json index 1b5feda6a05..7c0af445cdb 100644 --- a/src/constants.json +++ b/src/constants.json @@ -37,7 +37,8 @@ "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", "AD_RENDER_FAILED": "adRenderFailed", - "TCF2_ENFORCEMENT": "tcf2Enforcement" + "TCF2_ENFORCEMENT": "tcf2Enforcement", + "AUCTION_DEBUG": "auctionDebug" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocuemnt", diff --git a/src/utils.js b/src/utils.js index 591c1d1bb2b..9426308daf4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -21,6 +21,7 @@ let consoleLogExists = Boolean(consoleExists && window.console.log); let consoleInfoExists = Boolean(consoleExists && window.console.info); let consoleWarnExists = Boolean(consoleExists && window.console.warn); let consoleErrorExists = Boolean(consoleExists && window.console.error); +var events = require('./events.js'); // this allows stubbing of utility functions that are used internally by other utility functions export const internal = { @@ -261,6 +262,7 @@ export function logError() { if (debugTurnedOn() && consoleErrorExists) { console.error.apply(console, decorateLog(arguments, 'ERROR:')); } + events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'ERROR', arguments: arguments}); } function decorateLog(args, prefix) { diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 4afa430f81e..71fb9f87fa0 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -9,6 +9,7 @@ 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 AUCTION_DEBUG = CONSTANTS.EVENTS.AUCTION_DEBUG; const ADD_AD_UNITS = CONSTANTS.EVENTS.ADD_AD_UNITS; const AnalyticsAdapter = require('src/AnalyticsAdapter').default; @@ -83,6 +84,17 @@ FEATURE: Analytics Adapters API expect(result).to.deep.equal({args: {call: 'adRenderFailed'}, eventType: 'adRenderFailed'}); }); + 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' }; diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index e87be40314c..ce97789fe3e 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -98,7 +98,7 @@ describe('Prebid Manager Analytics Adapter', function () { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(prebidmanagerAnalytics.track, 6); + sinon.assert.callCount(prebidmanagerAnalytics.track, 7); }); }); diff --git a/test/spec/modules/readpeakBidAdapter_spec.js b/test/spec/modules/readpeakBidAdapter_spec.js index eb9077fac39..0c6f942e724 100644 --- a/test/spec/modules/readpeakBidAdapter_spec.js +++ b/test/spec/modules/readpeakBidAdapter_spec.js @@ -177,15 +177,10 @@ describe('ReadPeakAdapter', function() { expect(data.id).to.equal(bidRequest.bidderRequestId); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); expect(data.imp[0].bidfloorcur).to.equal('USD'); - expect(data.site).to.deep.equal({ - publisher: { - id: bidRequest.params.publisherId, - domain: 'http://localhost:9876' - }, - id: bidRequest.params.siteId, - page: bidderRequest.refererInfo.referer, - domain: parseUrl(bidderRequest.refererInfo.referer).hostname - }); + expect(data.site.publisher.id).to.equal(bidRequest.params.publisherId); + expect(data.site.id).to.equal(bidRequest.params.siteId); + expect(data.site.page).to.equal(bidderRequest.refererInfo.referer); + expect(data.site.domain).to.equal(parseUrl(bidderRequest.refererInfo.referer).hostname); expect(data.device).to.deep.contain({ ua: navigator.userAgent, language: navigator.language diff --git a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js index 75afb0ed86e..854c3a8e22d 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, 5); + sinon.assert.callCount(sigmoidAnalytic.track, 7); }); }); describe('build utm tag data', function () { diff --git a/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js index 24781d749e0..c8643c547e0 100644 --- a/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js +++ b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js @@ -391,7 +391,7 @@ describe('yuktamedia analytics adapter', function () { yuktamediaAnalyticsAdapter.track.restore(); }); - it('should catch all events', function () { + it('should catch all events 1', function () { yuktamediaAnalyticsAdapter.enableAnalytics({ provider: 'yuktamedia', options: { @@ -403,12 +403,82 @@ describe('yuktamedia analytics adapter', function () { } }); events.emit(constants.EVENTS.AUCTION_INIT, prebidAuction[constants.EVENTS.AUCTION_INIT]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch all events 2', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_REQUESTED, prebidAuction[constants.EVENTS.BID_REQUESTED]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch all events 3', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.NO_BID, prebidAuction[constants.EVENTS.NO_BID]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch all events 4', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_TIMEOUT, prebidAuction[constants.EVENTS.BID_TIMEOUT]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch all events 5', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_RESPONSE, prebidAuction[constants.EVENTS.BID_RESPONSE]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch all events 6', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.AUCTION_END, prebidAuction[constants.EVENTS.AUCTION_END]); - sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 6); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); }); it('should catch no events if no pubKey and pubId', function () { @@ -427,7 +497,7 @@ describe('yuktamedia analytics adapter', function () { sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 0); }); - it('should catch nobid, timeout and biwon event events', function () { + it('should catch nobid, timeout and bidwon event events one of eight', function () { yuktamediaAnalyticsAdapter.enableAnalytics({ provider: 'yuktamedia', options: { @@ -439,14 +509,112 @@ describe('yuktamedia analytics adapter', function () { } }); events.emit(constants.EVENTS.AUCTION_INIT, prebidNativeAuction[constants.EVENTS.AUCTION_INIT]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events two of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_REQUESTED, prebidNativeAuction[constants.EVENTS.BID_REQUESTED]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events three of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_REQUESTED, prebidNativeAuction[constants.EVENTS.BID_REQUESTED + '1']); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events four of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.NO_BID, prebidNativeAuction[constants.EVENTS.NO_BID]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events five of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_TIMEOUT, prebidNativeAuction[constants.EVENTS.BID_TIMEOUT]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events six of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.BID_RESPONSE, prebidNativeAuction[constants.EVENTS.BID_RESPONSE]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events seven of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.AUCTION_END, prebidNativeAuction[constants.EVENTS.AUCTION_END]); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); + }); + + it('should catch nobid, timeout and bidwon event events eight of eight', function () { + yuktamediaAnalyticsAdapter.enableAnalytics({ + provider: 'yuktamedia', + options: { + pubId: '1', + pubKey: 'ZXlKaGJHY2lPaUpJVXpJMU5pSjkuT==', + enableUTMCollection: true, + enableSession: true, + enableUserIdCollection: true + } + }); events.emit(constants.EVENTS.AUCTION_END, prebidNativeAuction[constants.EVENTS.BID_WON]); - sinon.assert.callCount(yuktamediaAnalyticsAdapter.track, 8); + sinon.assert.called(yuktamediaAnalyticsAdapter.track); }); }); From 33e1691498e979a737e975fdd52191e455a7774f Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 8 Sep 2020 17:43:09 +0200 Subject: [PATCH 0154/1476] Prebid 4.7.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fdc3ba4b83..846a35e4127 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.7.0-pre", + "version": "4.7.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bcf7b5ada39ea1716ec7b25fe9060aefa1ffaf45 Mon Sep 17 00:00:00 2001 From: Drilon Kastrati Date: Tue, 8 Sep 2020 18:13:33 +0200 Subject: [PATCH 0155/1476] added adapters for gjirafa and malltv (#5587) * added adapters for gjirafa and malltv * interpretResponse fix for empty result * updated testing propertyId and placementId --- modules/gjirafaBidAdapter.js | 91 +++++++++++++ modules/gjirafaBidAdapter.md | 69 ++++++---- modules/malltvBidAdapter.js | 91 +++++++++++++ modules/malltvBidAdapter.md | 51 +++++++ test/spec/modules/gjirafaBidAdapter_spec.js | 140 ++++++++++++++++++++ test/spec/modules/malltvBidAdapter_spec.js | 140 ++++++++++++++++++++ 6 files changed, 555 insertions(+), 27 deletions(-) create mode 100644 modules/gjirafaBidAdapter.js create mode 100644 modules/malltvBidAdapter.js create mode 100644 modules/malltvBidAdapter.md create mode 100644 test/spec/modules/gjirafaBidAdapter_spec.js create mode 100644 test/spec/modules/malltvBidAdapter_spec.js diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js new file mode 100644 index 00000000000..ca7fb4af32d --- /dev/null +++ b/modules/gjirafaBidAdapter.js @@ -0,0 +1,91 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'gjirafa'; +const ENDPOINT_URL = 'https://central.gjirafa.com/bid'; +const DIMENSION_SEPARATOR = 'x'; +const SIZE_SEPARATOR = ';'; + +export const spec = { + code: BIDDER_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) { + return !!(bid.params.propertyId && bid.params.placementId); + }, + /** + * 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) { + let response = validBidRequests.map(bidRequest => { + let sizes = generateSizeParam(bidRequest.sizes); + let propertyId = bidRequest.params.propertyId; + let placementId = bidRequest.params.placementId; + let adUnitId = bidRequest.adUnitCode; + let pageViewGuid = bidRequest.params.pageViewGuid || ''; + let contents = bidRequest.params.contents || []; + const body = { + sizes: sizes, + adUnitId: adUnitId, + placementId: placementId, + propertyId: propertyId, + pageViewGuid: pageViewGuid, + url: bidderRequest ? bidderRequest.refererInfo.referer : '', + requestid: bidRequest.bidderRequestId, + bidid: bidRequest.bidId, + contents: contents + }; + return { + method: 'POST', + url: ENDPOINT_URL, + data: body + }; + }); + return response + }, + /** + * 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) { + window.adnResponse = serverResponse; + const responses = serverResponse.body; + const bidResponses = []; + for (var i = 0; i < responses.length; i++) { + const bidResponse = { + requestId: bidRequest.data.bidid, + cpm: responses[i].CPM, + width: responses[i].Width, + height: responses[i].Height, + creativeId: responses[i].CreativeId, + currency: responses[i].Currency, + netRevenue: responses[i].NetRevenue, + ttl: responses[i].TTL, + referrer: responses[i].Referrer, + ad: responses[i].Ad + }; + bidResponses.push(bidResponse); + } + return bidResponses; + } +} + +/** +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ +function generateSizeParam(sizes) { + return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); +} + +registerBidder(spec); diff --git a/modules/gjirafaBidAdapter.md b/modules/gjirafaBidAdapter.md index 1ec8222d8de..53d3a76c5ed 100644 --- a/modules/gjirafaBidAdapter.md +++ b/modules/gjirafaBidAdapter.md @@ -1,36 +1,51 @@ # Overview Module Name: Gjirafa Bidder Adapter Module Type: Bidder Adapter -Maintainer: agonq@gjirafa.com +Maintainer: drilon@gjirafa.com # Description Gjirafa Bidder Adapter for Prebid.js. # Test Parameters var adUnits = [ -{ - code: 'test-div', - sizes: [[728, 90]], // leaderboard - bids: [ - { - bidder: 'gjirafa', - params: { - placementId: '71-3' - } - } - ] -},{ - code: 'test-div', - sizes: [[300, 250]], // mobile rectangle - bids: [ - { - bidder: 'gjirafa', - params: { - minCPM: 0.0001, - minCPC: 0.001, - explicit: true - } - } - ] -} -]; \ No newline at end of file + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bids: [ + { + bidder: 'gjirafa', + params: { + propertyId: '105227', + placementId: '846841' + } + } + ] + }, + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'gjirafa', + params: { + propertyId: '105227', + placementId: '846848', + contents: [ //optional + { + type: 'article', + id: '123' + } + ] + } + } + ] + } +]; diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js new file mode 100644 index 00000000000..4cdb5d45328 --- /dev/null +++ b/modules/malltvBidAdapter.js @@ -0,0 +1,91 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'malltv'; +const ENDPOINT_URL = 'https://central.mall.tv/bid'; +const DIMENSION_SEPARATOR = 'x'; +const SIZE_SEPARATOR = ';'; + +export const spec = { + code: BIDDER_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) { + return !!(bid.params.propertyId && bid.params.placementId); + }, + /** + * 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) { + let response = validBidRequests.map(bidRequest => { + let sizes = generateSizeParam(bidRequest.sizes); + let propertyId = bidRequest.params.propertyId; + let placementId = bidRequest.params.placementId; + let adUnitId = bidRequest.adUnitCode; + let pageViewGuid = bidRequest.params.pageViewGuid || ''; + let contents = bidRequest.params.contents || []; + const body = { + sizes: sizes, + adUnitId: adUnitId, + placementId: placementId, + propertyId: propertyId, + pageViewGuid: pageViewGuid, + url: bidderRequest ? bidderRequest.refererInfo.referer : '', + requestid: bidRequest.bidderRequestId, + bidid: bidRequest.bidId, + contents: contents + }; + return { + method: 'POST', + url: ENDPOINT_URL, + data: body + }; + }); + return response + }, + /** + * 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) { + window.adnResponse = serverResponse; + const responses = serverResponse.body; + const bidResponses = []; + for (var i = 0; i < responses.length; i++) { + const bidResponse = { + requestId: bidRequest.data.bidid, + cpm: responses[i].CPM, + width: responses[i].Width, + height: responses[i].Height, + creativeId: responses[i].CreativeId, + currency: responses[i].Currency, + netRevenue: responses[i].NetRevenue, + ttl: responses[i].TTL, + referrer: responses[i].Referrer, + ad: responses[i].Ad + }; + bidResponses.push(bidResponse); + } + return bidResponses; + } +} + +/** +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ +function generateSizeParam(sizes) { + return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); +} + +registerBidder(spec); diff --git a/modules/malltvBidAdapter.md b/modules/malltvBidAdapter.md new file mode 100644 index 00000000000..72db0cef6c7 --- /dev/null +++ b/modules/malltvBidAdapter.md @@ -0,0 +1,51 @@ +# Overview +Module Name: MallTV Bidder Adapter Module +Type: Bidder Adapter +Maintainer: drilon@gjirafa.com + +# Description +MallTV Bidder Adapter for Prebid.js. + +# Test Parameters +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 300]] + } + }, + bids: [ + { + bidder: 'malltv', + params: { + propertyId: '105134', + placementId: '846832' + } + } + ] + }, + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 300]] + } + }, + bids: [ + { + bidder: 'malltv', + params: { + propertyId: '105134', + placementId: '846832', + contents: [ //optional + { + type: 'video', + id: '123' + } + ] + } + } + ] + } +]; diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js new file mode 100644 index 00000000000..566b1243f62 --- /dev/null +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -0,0 +1,140 @@ +import { expect } from 'chai'; +import { spec } from 'modules/gjirafaBidAdapter'; + +describe('gjirafaAdapterTest', () => { + describe('bidRequestValidity', () => { + it('bidRequest with propertyId and placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'gjirafa', + params: { + propertyId: '{propertyId}', + placementId: '{placementId}' + } + })).to.equal(true); + }); + + it('bidRequest without propertyId', () => { + expect(spec.isBidRequestValid({ + bidder: 'gjirafa', + params: { + placementId: '{placementId}' + } + })).to.equal(false); + }); + + it('bidRequest without placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'gjirafa', + params: { + propertyId: '{propertyId}', + } + })).to.equal(false); + }); + + it('bidRequest without propertyId orplacementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'gjirafa', + params: { + propertyId: '{propertyId}', + } + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'gjirafa', + 'params': { + 'propertyId': '{propertyId}', + 'placementId': '{placementId}' + }, + 'adUnitCode': 'hb-leaderboard', + 'transactionId': 'b6b889bb-776c-48fd-bc7b-d11a1cf0425e', + 'sizes': [[728, 90]], + 'bidId': '10bdc36fe0b48c8', + 'bidderRequestId': '70deaff71c281d', + 'auctionId': 'f9012acc-b6b7-4748-9098-97252914f9dc' + }]; + + it('bidRequest HTTP method', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.method).to.equal('POST'); + }); + }); + + it('bidRequest url', () => { + const endpointUrl = 'https://central.gjirafa.com/bid'; + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.url).to.match(new RegExp(`${endpointUrl}`)); + }); + }); + + it('bidRequest data', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.data).to.exist; + }); + }); + + it('bidRequest sizes', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.sizes).to.equal('728x90'); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = { + 'method': 'POST', + 'url': 'https://central.gjirafa.com/bid', + 'data': { + 'sizes': '728x90', + 'adUnitId': 'hb-leaderboard', + 'placementId': '{placementId}', + 'propertyId': '{propertyId}', + 'pageViewGuid': '{pageViewGuid}', + 'url': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'requestid': '26ee8fe87940da7', + 'bidid': '2962dbedc4768bf' + } + }; + + const bidResponse = { + body: [{ + 'CPM': 1, + 'Width': 728, + 'Height': 90, + 'Referrer': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'Ad': '
Test ad
', + 'CreativeId': '123abc', + 'NetRevenue': false, + 'Currency': 'EUR', + 'TTL': 360 + }], + headers: {} + }; + + it('all keys present', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let keys = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'creativeId', + 'currency', + 'netRevenue', + 'ttl', + 'referrer', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function (key) { + expect(keys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +}); diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js new file mode 100644 index 00000000000..10161b319c5 --- /dev/null +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -0,0 +1,140 @@ +import { expect } from 'chai'; +import { spec } from 'modules/malltvBidAdapter'; + +describe('malltvAdapterTest', () => { + describe('bidRequestValidity', () => { + it('bidRequest with propertyId and placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'malltv', + params: { + propertyId: '{propertyId}', + placementId: '{placementId}' + } + })).to.equal(true); + }); + + it('bidRequest without propertyId', () => { + expect(spec.isBidRequestValid({ + bidder: 'malltv', + params: { + placementId: '{placementId}' + } + })).to.equal(false); + }); + + it('bidRequest without placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'malltv', + params: { + propertyId: '{propertyId}', + } + })).to.equal(false); + }); + + it('bidRequest without propertyId or placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'malltv', + params: { + propertyId: '{propertyId}', + } + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'malltv', + 'params': { + 'propertyId': '{propertyId}', + 'placementId': '{placementId}' + }, + 'adUnitCode': 'hb-leaderboard', + 'transactionId': 'b6b889bb-776c-48fd-bc7b-d11a1cf0425e', + 'sizes': [[300, 250]], + 'bidId': '10bdc36fe0b48c8', + 'bidderRequestId': '70deaff71c281d', + 'auctionId': 'f9012acc-b6b7-4748-9098-97252914f9dc' + }]; + + it('bidRequest HTTP method', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.method).to.equal('POST'); + }); + }); + + it('bidRequest url', () => { + const endpointUrl = 'https://central.mall.tv/bid'; + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.url).to.match(new RegExp(`${endpointUrl}`)); + }); + }); + + it('bidRequest data', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.data).to.exist; + }); + }); + + it('bidRequest sizes', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.sizes).to.equal('300x250'); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = { + 'method': 'POST', + 'url': 'https://central.mall.tv/bid', + 'data': { + 'sizes': '300x250', + 'adUnitId': 'rectangle', + 'placementId': '{placementId}', + 'propertyId': '{propertyId}', + 'pageViewGuid': '{pageViewGuid}', + 'url': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'requestid': '26ee8fe87940da7', + 'bidid': '2962dbedc4768bf' + } + }; + + const bidResponse = { + body: [{ + 'CPM': 1, + 'Width': 300, + 'Height': 250, + 'Referrer': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'Ad': '
Test ad
', + 'CreativeId': '123abc', + 'NetRevenue': false, + 'Currency': 'EUR', + 'TTL': 360 + }], + headers: {} + }; + + it('all keys present', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let keys = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'creativeId', + 'currency', + 'netRevenue', + 'ttl', + 'referrer', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function (key) { + expect(keys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +}); From c3c04f55448625012d7465f98c53a86c37e4a843 Mon Sep 17 00:00:00 2001 From: harpere Date: Tue, 8 Sep 2020 13:18:20 -0400 Subject: [PATCH 0156/1476] minor validation update to consentManagement.js (#5701) Co-authored-by: Eric Harper --- modules/consentManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 19fbe827eb1..f44fde0554d 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -383,7 +383,7 @@ function storeConsentData(cmpConsentObject) { vendorData: (cmpConsentObject) || undefined, gdprApplies: cmpConsentObject && typeof cmpConsentObject.gdprApplies === 'boolean' ? cmpConsentObject.gdprApplies : gdprScope }; - if (cmpConsentObject.addtlConsent && utils.isStr(cmpConsentObject.addtlConsent)) { + if (cmpConsentObject && cmpConsentObject.addtlConsent && utils.isStr(cmpConsentObject.addtlConsent)) { consentData.addtlConsent = cmpConsentObject.addtlConsent; }; } From 44a3797692d2e47a381f29d9234db321b21d5fe3 Mon Sep 17 00:00:00 2001 From: Vladyslav Laktionov Date: Tue, 8 Sep 2020 20:20:32 +0300 Subject: [PATCH 0157/1476] New Bid Adapter: decenterads (#5711) * add new version decenteradsBidAdapter.js * add new version decenteradsBidAdapter_spec.js * add .js to import * replace method getWindowLocation * replace urls Co-authored-by: vlad --- modules/decenteradsBidAdapter.js | 90 ++++++++ .../modules/decenteradsBidAdapter_spec.js | 207 ++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 modules/decenteradsBidAdapter.js create mode 100644 test/spec/modules/decenteradsBidAdapter_spec.js diff --git a/modules/decenteradsBidAdapter.js b/modules/decenteradsBidAdapter.js new file mode 100644 index 00000000000..823a59a3768 --- /dev/null +++ b/modules/decenteradsBidAdapter.js @@ -0,0 +1,90 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' +import * as utils from '../src/utils.js' + +const BIDDER_CODE = 'decenterads' +const URL = 'https://supply.decenterads.com/?c=o&m=multi' +const URL_SYNC = 'https://supply.decenterads.com/?c=o&m=cookie' + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: function (opts) { + return Boolean(opts.bidId && opts.params && !isNaN(opts.params.placementId)) + }, + + buildRequests: function (validBidRequests) { + validBidRequests = validBidRequests || [] + let winTop = window + try { + window.top.location.toString() + winTop = window.top + } catch (e) { utils.logMessage(e) } + + const location = utils.getWindowLocation() + const placements = [] + + for (let i = 0; i < validBidRequests.length; i++) { + const p = validBidRequests[i] + + placements.push({ + placementId: p.params.placementId, + bidId: p.bidId, + traffic: p.params.traffic || BANNER + }) + } + + return { + method: 'POST', + url: URL, + data: { + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language : '', + secure: +(location.protocol === 'https:'), + host: location.hostname, + page: location.pathname, + placements: placements + } + } + }, + + interpretResponse: function (opts) { + const body = opts.body + const response = [] + + for (let i = 0; i < body.length; i++) { + const item = body[i] + if (isBidResponseValid(item)) { + delete item.mediaType + response.push(item) + } + } + + return response + }, + + getUserSyncs: function (syncOptions, serverResponses) { + return [{ type: 'image', url: URL_SYNC }] + } +} + +registerBidder(spec) + +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) + case NATIVE: + return Boolean(bid.title && bid.image && bid.impressionTrackers) + default: + return false + } +} diff --git a/test/spec/modules/decenteradsBidAdapter_spec.js b/test/spec/modules/decenteradsBidAdapter_spec.js new file mode 100644 index 00000000000..257094cae3a --- /dev/null +++ b/test/spec/modules/decenteradsBidAdapter_spec.js @@ -0,0 +1,207 @@ +import { expect } from 'chai' +import { spec } from '../../../modules/decenteradsBidAdapter.js' +import { deepStrictEqual, notEqual, ok, strictEqual } from 'assert' + +describe('DecenteradsAdapter', () => { + const bid = { + bidId: '9ec5b177515ee2e5', + bidder: 'decenterads', + params: { + placementId: 0, + traffic: 'banner' + } + } + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + strictEqual(true, spec.isBidRequestValid(bid)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.placementId + strictEqual(false, spec.isBidRequestValid(b)) + }) + }) + + describe('buildRequests', () => { + const serverRequest = spec.buildRequests([bid]) + + it('Creates a ServerRequest object with method, URL and data', () => { + ok(serverRequest) + ok(serverRequest.method) + ok(serverRequest.url) + ok(serverRequest.data) + }) + + it('Returns POST method', () => { + strictEqual('POST', serverRequest.method) + }) + + it('Returns valid URL', () => { + strictEqual('https://supply.decenterads.com/?c=o&m=multi', serverRequest.url) + }) + + it('Returns valid data if array of bids is valid', () => { + const { data } = serverRequest + strictEqual('object', typeof data) + deepStrictEqual(['deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) + strictEqual('number', typeof data.deviceWidth) + strictEqual('number', typeof data.deviceHeight) + strictEqual('string', typeof data.language) + strictEqual('string', typeof data.host) + strictEqual('string', typeof data.page) + notEqual(-1, [0, 1].indexOf(data.secure)) + + const placement = data.placements[0] + deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) + strictEqual(0, placement.placementId) + strictEqual('9ec5b177515ee2e5', placement.bidId) + strictEqual('banner', placement.traffic) + }) + + it('Returns empty data if no valid requests are passed', () => { + const { placements } = spec.buildRequests([]).data + + expect(spec.buildRequests([]).data.placements).to.be.an('array') + strictEqual(0, placements.length) + }) + }) + + describe('interpretResponse', () => { + const validData = [ + { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + vastUrl: 'decenterads.com', + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'decenterads.com', + title: 'Test', + image: 'decenterads.com', + creativeId: '2', + impressionTrackers: ['decenterads.com'], + ttl: 120, + cpm: 0.4, + requestId: '9ec5b177515ee2e5', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of validData) { + const { mediaType } = obj.body[0] + + it(`Should interpret ${mediaType} response`, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(1, response.length) + + const copy = { ...obj.body[0] } + delete copy.mediaType + deepStrictEqual(copy, response[0]) + }) + } + + const invalidData = [ + { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'decenterads.com', + title: 'Test', + impressionTrackers: ['decenterads.com'], + ttl: 120, + requestId: '9ec5b177515ee2e5', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of invalidData) { + const { mediaType } = obj.body[0] + + it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + } + + it('Should return an empty array if invalid response is passed', () => { + const response = spec.interpretResponse({ + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + }) + + describe('getUserSyncs', () => { + it('Returns valid URL and type', () => { + const expectedResult = [{ type: 'image', url: 'https://supply.decenterads.com/?c=o&m=cookie' }] + deepStrictEqual(expectedResult, spec.getUserSyncs()) + }) + }) +}) From 1676c76c4d47833fbb6408b2d1261bd485238966 Mon Sep 17 00:00:00 2001 From: "Isaac A. Dettman" Date: Tue, 8 Sep 2020 10:29:22 -0700 Subject: [PATCH 0158/1476] fix GPT Pre-Auction PBS path (#5650) * 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 * Re-add rubicon analytics without deprecated getTopWindowUrl util * Cache referrer on auction_init instead of bid_requested * merged remote master changes * updated unit tests for object path changes * updated rp bid adapter unit tests for GAM object path changes * updated naming and changed iterator to use arrow syntax * continue renaming and cleanup Co-authored-by: nakamoto Co-authored-by: Chandra Prakash Co-authored-by: Eric Harper Co-authored-by: TJ Eastmond Co-authored-by: Mark Monday Co-authored-by: msm0504 <51493331+msm0504@users.noreply.github.com> --- modules/prebidServerBidAdapter/index.js | 14 ++++++++------ modules/rubiconBidAdapter.js | 14 ++++++++------ .../modules/prebidServerBidAdapter_spec.js | 19 +++++++++++-------- test/spec/modules/rubiconBidAdapter_spec.js | 8 +++++--- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index b3d559d956f..a7a3199675d 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -551,13 +551,15 @@ const OPEN_RTB_PROTOCOL = { } /** - * GAM Ad Unit - * @type {(string|undefined)} + * Copy GAM AdUnit and Name to imp */ - const gamAdUnit = utils.deepAccess(adUnit, 'fpd.context.adServer.adSlot'); - if (typeof gamAdUnit === 'string' && gamAdUnit) { - utils.deepSetValue(imp, 'ext.context.data.adslot', gamAdUnit); - } + ['name', 'adSlot'].forEach(name => { + /** @type {(string|undefined)} */ + const value = utils.deepAccess(adUnit, `fpd.context.adserver.${name}`); + if (typeof value === 'string' && value) { + utils.deepSetValue(imp, `ext.context.data.adserver.${name.toLowerCase()}`, value); + } + }); Object.assign(imp, mediaTypes); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index da23cca62d1..979cc430f15 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -308,13 +308,15 @@ export const spec = { } /** - * GAM Ad Unit - * @type {(string|undefined)} + * Copy GAM AdUnit and Name to imp */ - const gamAdUnit = utils.deepAccess(bidRequest, 'fpd.context.adServer.adSlot'); - if (typeof gamAdUnit === 'string' && gamAdUnit) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', gamAdUnit); - } + ['name', 'adSlot'].forEach(name => { + /** @type {(string|undefined)} */ + const value = utils.deepAccess(bidRequest, `fpd.context.adserver.${name}`); + if (typeof value === 'string' && value) { + utils.deepSetValue(data.imp[0].ext, `context.data.adserver.${name.toLowerCase()}`, value); + } + }); // if storedAuctionResponse has been set, pass SRID if (bidRequest.storedAuctionResponse) { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 5abe068c100..a300a10d31b 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1516,7 +1516,7 @@ describe('S2S Adapter', function () { }); describe('GAM ad unit config', function () { - it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context\" is undefined', function () { + it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1531,7 +1531,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" is undefined', function () { + it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1547,7 +1547,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" is empty string', function () { + it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is empty string', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1569,7 +1569,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should send \"imp.ext.context.data.adslot\" if \"fpd.context.adServer.adSlot\" value is a non-empty string', function () { + it('should send both \"adslot\" and \"name\" from \"imp.ext.context.data.adserver\" if \"fpd.context.adserver.adSlot\" and \"fpd.context.adserver.name\" values are non-empty strings', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1577,8 +1577,9 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); bidRequest.ad_units[0].fpd = { context: { - adServer: { - adSlot: '/a/b/c' + adserver: { + adSlot: '/a/b/c', + name: 'adserverName1' } } }; @@ -1588,8 +1589,10 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adslot'); - expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.adslot'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.name'); + expect(parsedRequestBody.imp[0].ext.context.data.adserver.adslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0].ext.context.data.adserver.name).to.equal('adserverName1'); }); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index a2b554c1f5d..c5c0f644643 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2037,8 +2037,9 @@ describe('the rubicon adapter', function () { createVideoBidderRequest(); bidderRequest.bids[0].fpd = { context: { - adServer: { - adSlot: '1234567890' + adserver: { + adSlot: '1234567890', + name: 'adServerName1' } } }; @@ -2048,7 +2049,8 @@ describe('the rubicon adapter', function () { ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.context.data.adserver.adslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.context.data.adserver.name).to.equal('adServerName1'); }); it('should use the integration type provided in the config instead of the default', () => { From 22ce19ff42ff25c51502c32a6a2a1079a5b76a38 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 8 Sep 2020 19:34:07 +0200 Subject: [PATCH 0159/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 846a35e4127..7ad0781d648 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.7.0", + "version": "4.8.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0d3c632622d699d34b9b897012e57319fef0c343 Mon Sep 17 00:00:00 2001 From: Estavillo Date: Tue, 8 Sep 2020 22:38:06 +0200 Subject: [PATCH 0160/1476] GumGumBidAdapter: Add support for multiple sizes (#5626) add UT UT Co-authored-by: Estavillo --- modules/gumgumBidAdapter.js | 11 ++++++++++ test/spec/modules/gumgumBidAdapter_spec.js | 25 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index ebeb46e3c44..f8e17e0fe71 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -275,6 +275,17 @@ function buildRequests (validBidRequests, bidderRequest) { } if (params.inSlot) { data.si = parseInt(params.inSlot, 10); + // check for sizes and type + if (params.sizes && Array.isArray(params.sizes)) { + const bf = params.sizes.reduce(function(r, i) { + // only push if it's an array of length 2 + if (Array.isArray(i) && i.length === 2) { + r.push(`${i[0]}x${i[1]}`); + } + return r; + }, []); + data.bf = bf.toString(); + } data.pi = 3; } if (params.ICV) { diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index af929f437da..cf672d89e22 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -46,6 +46,17 @@ describe('gumgumAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); + it('should return true when inslot sends sizes and trackingid', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'inSlot': '789', + 'sizes': [[0, 1], [2, 3], [4, 5], [6, 7]] + }; + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return false when no unit type is specified', function () { let bid = Object.assign({}, bid); delete bid.params; @@ -81,6 +92,7 @@ describe('gumgumAdapter', function () { }); describe('buildRequests', function () { + let sizesArray = [[300, 250], [300, 600]]; let bidRequests = [ { 'bidder': 'gumgum', @@ -88,7 +100,7 @@ describe('gumgumAdapter', function () { 'inSlot': '9' }, 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], + 'sizes': sizesArray, 'bidId': '30b31c1838de1e', 'schain': { 'ver': '1.0', @@ -132,7 +144,16 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.sizes).to.equal(vidMediaTypes.video.playerSize); }); - + it('should handle multiple sizes for inslot', function () { + const request = Object.assign({}, bidRequests[0]); + delete request.params; + request.params = { + 'inSlot': '123', + 'sizes': [[0, 1], [0, 2]] + }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.bf).to.equal('0x1,0x2'); + }); describe('floorModule', function () { const floorTestData = { 'currency': 'USD', From 05283d0a06f897c0cda476383dc0408afff7330c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Proch=C3=A1zka?= Date: Wed, 9 Sep 2020 03:10:29 +0200 Subject: [PATCH 0161/1476] Add host to gulpfile (#5710) * Add host to gulpfile * Edit arg.host to FAKE_SERVER_HOST Co-authored-by: VasekProchazka --- gulpfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gulpfile.js b/gulpfile.js index 64152baa7ba..879e34ae588 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -110,6 +110,7 @@ function watch(done) { connect.server({ https: argv.https, port: port, + host: FAKE_SERVER_HOST, root: './', livereload: true }); From b96c1ccd3b9a718013539db21534c65daf3bdb32 Mon Sep 17 00:00:00 2001 From: frstua Date: Wed, 9 Sep 2020 04:18:10 +0300 Subject: [PATCH 0162/1476] Move test and publisherId parameters to bidder specific config (#5692) Co-authored-by: Yevhenii Tykhostup --- modules/apstreamBidAdapter.js | 19 ++++++++++------- modules/apstreamBidAdapter.md | 14 +++++++++++++ test/spec/modules/apstreamBidAdapter_spec.js | 22 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js index 324c125f5ef..4fb89b9c720 100644 --- a/modules/apstreamBidAdapter.js +++ b/modules/apstreamBidAdapter.js @@ -378,23 +378,27 @@ function getBids(bids) { function getEndpointsGroups(bidRequests) { let endpoints = []; const getEndpoint = bid => { - if (bid.params.test) { - return `https://mock-bapi.userreport.com/v2/${bid.params.publisherId}/bid`; + const publisherId = bid.params.publisherId || config.getConfig('apstream.publisherId'); + const isTestConfig = bid.params.test || config.getConfig('apstream.test'); + + if (isTestConfig) { + return `https://mock-bapi.userreport.com/v2/${publisherId}/bid`; } if (bid.params.endpoint) { - return `${bid.params.endpoint}${bid.params.publisherId}/bid`; + return `${bid.params.endpoint}${publisherId}/bid`; } - return `https://bapi.userreport.com/v2/${bid.params.publisherId}/bid`; + return `https://bapi.userreport.com/v2/${publisherId}/bid`; } bidRequests.forEach(bid => { - const exist = endpoints.filter(item => item.endpoint.indexOf(bid.params.endpoint) > -1)[0]; + const endpoint = getEndpoint(bid); + const exist = endpoints.filter(item => item.endpoint.indexOf(endpoint) > -1)[0]; if (exist) { exist.bids.push(bid); } else { endpoints.push({ - endpoint: getEndpoint(bid), + endpoint: endpoint, bids: [bid] }); } @@ -404,7 +408,8 @@ function getEndpointsGroups(bidRequests) { } function isBidRequestValid(bid) { - const isPublisherIdExist = !!bid.params.publisherId; + const publisherId = config.getConfig('apstream.publisherId'); + const isPublisherIdExist = !!(publisherId || bid.params.publisherId); const isOneMediaType = Object.keys(bid.mediaTypes).length === 1; return isPublisherIdExist && isOneMediaType; diff --git a/modules/apstreamBidAdapter.md b/modules/apstreamBidAdapter.md index e528307a003..6b87b33489a 100644 --- a/modules/apstreamBidAdapter.md +++ b/modules/apstreamBidAdapter.md @@ -95,3 +95,17 @@ To disable DSU use config option: } }); ``` + +To set `test` and `publisherId` parameters globally use config options (it can be overrided if set in specific bid): + +``` +pbjs.setBidderConfig({ + bidders: ["apstream"], + config: { + appstream: { + publisherId: '1234 + test: true + } + } +}); +``` diff --git a/test/spec/modules/apstreamBidAdapter_spec.js b/test/spec/modules/apstreamBidAdapter_spec.js index c6546a3bd83..e640c009989 100644 --- a/test/spec/modules/apstreamBidAdapter_spec.js +++ b/test/spec/modules/apstreamBidAdapter_spec.js @@ -31,6 +31,22 @@ describe('AP Stream adapter', function() { } }; + let mockConfig; + beforeEach(function () { + mockConfig = { + apstream: { + publisherId: '4321' + } + }; + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + afterEach(function () { + config.getConfig.restore(); + }); + it('should return true when publisherId is configured and one media type', function() { bid.params.publisherId = '1234'; assert(spec.isBidRequestValid(bid)) @@ -40,6 +56,12 @@ describe('AP Stream adapter', function() { bid.mediaTypes.video = {sizes: [300, 250]}; assert.isFalse(spec.isBidRequestValid(bid)) }); + + it('should return true when publisherId is configured via config', function() { + delete bid.mediaTypes.video; + delete bid.params.publisherId; + assert.isTrue(spec.isBidRequestValid(bid)) + }); }); describe('buildRequests', function() { From 3726fd6f25981cdbc7b0c57ac4fefc871cf99584 Mon Sep 17 00:00:00 2001 From: Shikhar Sharma <36563196+shikhar-dev-proj@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:58:55 +0530 Subject: [PATCH 0163/1476] fix userId_example.html (#5606) --- integrationExamples/gpt/userId_example.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 9307bc91456..8115e60fcd1 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -84,6 +84,9 @@ { code: 'test-div', sizes: [[300,250],[300,600],[728,90]], + mediaTypes: { + banner: {} + }, bids: [ { bidder: 'rubicon', From 3c9e42fd3038c87ec645c01c12e4199c6d92c9b5 Mon Sep 17 00:00:00 2001 From: Rahul Shandilya <67756716+c3p-0@users.noreply.github.com> Date: Wed, 9 Sep 2020 13:06:28 +0530 Subject: [PATCH 0164/1476] MediaNet SChain Support (#5685) --- modules/medianetBidAdapter.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 9ff0192eab4..5decaa148e3 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -131,11 +131,16 @@ function getCoordinates(id) { return null; } -function extParams(params, gdpr, uspConsent, userId) { - let windowSize = spec.getWindowSize(); - let gdprApplies = !!(gdpr && gdpr.gdprApplies); - let uspApplies = !!(uspConsent); - let coppaApplies = !!(config.getConfig('coppa')); +function extParams(bidRequest, bidderRequests) { + const params = utils.deepAccess(bidRequest, 'params'); + const gdpr = utils.deepAccess(bidderRequests, 'gdprConsent'); + const uspConsent = utils.deepAccess(bidderRequests, 'uspConsent'); + const userId = utils.deepAccess(bidRequest, 'userId'); + const sChain = utils.deepAccess(bidRequest, 'schain') || {}; + const windowSize = spec.getWindowSize(); + const gdprApplies = !!(gdpr && gdpr.gdprApplies); + const uspApplies = !!(uspConsent); + const coppaApplies = !!(config.getConfig('coppa')); return Object.assign({}, { customer_id: params.cid }, { prebid_version: $$PREBID_GLOBAL$$.version }, @@ -146,7 +151,8 @@ function extParams(params, gdpr, uspConsent, userId) { {coppa_applies: coppaApplies}, windowSize.w !== -1 && windowSize.h !== -1 && { screen: windowSize }, userId && { user_id: userId }, - $$PREBID_GLOBAL$$.medianetGlobals.analyticsEnabled && { analytics: true } + $$PREBID_GLOBAL$$.medianetGlobals.analyticsEnabled && { analytics: true }, + !utils.isEmpty(sChain) && {schain: sChain} ); } @@ -254,7 +260,7 @@ function getBidderURL(cid) { function generatePayload(bidRequests, bidderRequests) { return { site: siteDetails(bidRequests[0].params.site), - ext: extParams(bidRequests[0].params, bidderRequests.gdprConsent, bidderRequests.uspConsent, bidRequests[0].userId), + ext: extParams(bidRequests[0], bidderRequests), id: bidRequests[0].auctionId, imp: bidRequests.map(request => slotParams(request)), tmax: bidderRequests.timeout || config.getConfig('bidderTimeout') From b0714570bdaea441a5df1ed851458876a6751aa6 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Wed, 9 Sep 2020 05:41:10 -0400 Subject: [PATCH 0165/1476] PubWise.io Analytics Module Update - SPOT Support, Module Rules & Minor Features (#5677) * updates to bring module up to current module rules, lints, etc. also add activationId and improve tests * fix eslint error * Fix IE11 Includes Check Failing Tests * revert debug setting * updates to fix PR review issues * updates to fix default params handling --- modules/pubwiseAnalyticsAdapter.js | 291 +++++++++++++++--- .../modules/pubwiseAnalyticsAdapter_spec.js | 159 ++++++++-- 2 files changed, 386 insertions(+), 64 deletions(-) diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 915aeb58f99..74b56c21a2b 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -4,7 +4,6 @@ import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; const utils = require('../src/utils.js'); - const storage = getStorageManager(); /**** @@ -17,30 +16,54 @@ const storage = getStorageManager(); pbjs.enableAnalytics({ provider: 'pubwise', options: { - site: 'test-test-test-test', - endpoint: 'https://api.pubwise.io/api/v4/event/add/', + site: 'b1ccf317-a6fc-428d-ba69-0c9c208aa61c' } }); - */ + +Changes in 4.0 Version +4.0.1 - Initial Version for Prebid 4.x, adds activationId, adds additiona testing, removes prebid global in favor of a prebid.version const +4.0.2 - Updates to include dedicated default site to keep everything from getting rate limited + +*/ const analyticsType = 'endpoint'; -const analyticsName = 'PubWise Analytics: '; -let defaultUrl = 'https://api.pubwise.io/api/v4/event/default/'; -let pubwiseVersion = '3.0'; -let pubwiseSchema = 'AVOCET'; -let configOptions = {site: '', endpoint: 'https://api.pubwise.io/api/v4/event/default/', debug: ''}; +const analyticsName = 'PubWise:'; +const prebidVersion = '$prebid.version$'; +let pubwiseVersion = '4.0.1'; +let configOptions = {site: '', endpoint: 'https://api.pubwise.io/api/v5/event/add/', debug: null}; let pwAnalyticsEnabled = false; let utmKeys = {utm_source: '', utm_medium: '', utm_campaign: '', utm_term: '', utm_content: ''}; +let sessionData = {sessionId: '', activationId: ''}; +let pwNamespace = 'pubwise'; +let pwEvents = []; +let metaData = {}; +let auctionEnded = false; +let sessTimeout = 60 * 30 * 1000; // 30 minutes, G Analytics default session length +let sessName = 'sess_id'; +let sessTimeoutName = 'sess_timeout'; -function markEnabled() { - utils.logInfo(`${analyticsName}Enabled`, configOptions); - pwAnalyticsEnabled = true; +function enrichWithSessionInfo(dataBag) { + try { + // eslint-disable-next-line + // console.log(sessionData); + dataBag['session_id'] = sessionData.sessionId; + dataBag['activation_id'] = sessionData.activationId; + } catch (e) { + dataBag['error_sess'] = 1; + } + + return dataBag; } function enrichWithMetrics(dataBag) { try { + if (window.PREBID_TIMEOUT) { + dataBag['target_timeout'] = window.PREBID_TIMEOUT; + } else { + dataBag['target_timeout'] = 'NA'; + } dataBag['pw_version'] = pubwiseVersion; - dataBag['pbjs_version'] = $$PREBID_GLOBAL$$.version; + dataBag['pbjs_version'] = prebidVersion; dataBag['debug'] = configOptions.debug; } catch (e) { dataBag['error_metric'] = 1; @@ -54,7 +77,7 @@ function enrichWithUTM(dataBag) { try { for (let prop in utmKeys) { utmKeys[prop] = utils.getParameterByName(prop); - if (utmKeys[prop] != '') { + if (utmKeys[prop]) { newUtm = true; dataBag[prop] = utmKeys[prop]; } @@ -62,64 +85,246 @@ function enrichWithUTM(dataBag) { if (newUtm === false) { for (let prop in utmKeys) { - let itemValue = storage.getDataFromLocalStorage(`pw-${prop}`); - if (itemValue.length !== 0) { + let itemValue = storage.getDataFromLocalStorage(setNamespace(prop)); + if (itemValue !== null && typeof itemValue !== 'undefined' && itemValue.length !== 0) { dataBag[prop] = itemValue; } } } else { for (let prop in utmKeys) { - storage.setDataInLocalStorage(`pw-${prop}`, utmKeys[prop]); + storage.setDataInLocalStorage(setNamespace(prop), utmKeys[prop]); } } } catch (e) { - utils.logInfo(`${analyticsName}Error`, e); + pwInfo(`Error`, e); dataBag['error_utm'] = 1; } return dataBag; } -function sendEvent(eventType, data) { - utils.logInfo(`${analyticsName}Event ${eventType} ${pwAnalyticsEnabled}`, data); +function expireUtmData() { + pwInfo(`Session Expiring UTM Data`); + for (let prop in utmKeys) { + storage.removeDataFromLocalStorage(setNamespace(prop)); + } +} + +function enrichWithCustomSegments(dataBag) { + // c_script_type: '', c_slot1: '', c_slot2: '', c_slot3: '', c_slot4: '' + if (configOptions.custom) { + if (configOptions.custom.c_script_type) { + dataBag['c_script_type'] = configOptions.custom.c_script_type; + } + + if (configOptions.custom.c_host) { + dataBag['c_host'] = configOptions.custom.c_host; + } + + if (configOptions.custom.c_slot1) { + dataBag['c_slot1'] = configOptions.custom.c_slot1; + } + + if (configOptions.custom.c_slot2) { + dataBag['c_slot2'] = configOptions.custom.c_slot2; + } + + if (configOptions.custom.c_slot3) { + dataBag['c_slot3'] = configOptions.custom.c_slot3; + } + + if (configOptions.custom.c_slot4) { + dataBag['c_slot4'] = configOptions.custom.c_slot4; + } + } + + return dataBag; +} + +function setNamespace(itemText) { + return pwNamespace.concat('_' + itemText); +} + +function localStorageSessTimeoutName() { + return setNamespace(sessTimeoutName); +} + +function localStorageSessName() { + return setNamespace(sessName); +} + +function extendUserSessionTimeout() { + storage.setDataInLocalStorage(localStorageSessTimeoutName(), Date.now().toString()); +} + +function userSessionID() { + return storage.getDataFromLocalStorage(localStorageSessName()) ? localStorage.getItem(localStorageSessName()) : ''; +} + +function sessionExpired() { + let sessLastTime = storage.getDataFromLocalStorage(localStorageSessTimeoutName()); + return (Date.now() - parseInt(sessLastTime)) > sessTimeout; +} + +function flushEvents() { + if (pwEvents.length > 0) { + let dataBag = {metaData: metaData, eventList: pwEvents.splice(0)}; // put all the events together with the metadata and send + ajax(configOptions.endpoint, (result) => pwInfo(`Result`, result), JSON.stringify(dataBag)); + } +} + +function isIngestedEvent(eventType) { + const ingested = [ + CONSTANTS.EVENTS.AUCTION_INIT, + CONSTANTS.EVENTS.BID_REQUESTED, + CONSTANTS.EVENTS.BID_RESPONSE, + CONSTANTS.EVENTS.BID_WON, + CONSTANTS.EVENTS.BID_TIMEOUT, + CONSTANTS.EVENTS.AD_RENDER_FAILED, + CONSTANTS.EVENTS.TCF2_ENFORCEMENT + ]; + return ingested.indexOf(eventType) !== -1; +} - // put the typical items in the data bag - let dataBag = { - eventType: eventType, - args: data, - target_site: configOptions.site, - pubwiseSchema: pubwiseSchema, - debug: configOptions.debug ? 1 : 0, - }; +function markEnabled() { + pwInfo(`Enabled`, configOptions); + pwAnalyticsEnabled = true; + setInterval(flushEvents, 100); +} + +function pwInfo(info, context) { + utils.logInfo(`${analyticsName} ` + info, context); +} - dataBag = enrichWithMetrics(dataBag); - // for certain events, track additional info - if (eventType == CONSTANTS.EVENTS.AUCTION_INIT) { - dataBag = enrichWithUTM(dataBag); +function filterBidResponse(data) { + let modified = Object.assign({}, data); + // clean up some properties we don't track in public version + if (typeof modified.ad !== 'undefined') { + modified.ad = ''; } + if (typeof modified.adUrl !== 'undefined') { + modified.adUrl = ''; + } + if (typeof modified.adserverTargeting !== 'undefined') { + modified.adserverTargeting = ''; + } + if (typeof modified.ts !== 'undefined') { + modified.ts = ''; + } + // clean up a property to make simpler + if (typeof modified.statusMessage !== 'undefined' && modified.statusMessage === 'Bid returned empty or error response') { + modified.statusMessage = 'eoe'; + } + modified.auctionEnded = auctionEnded; + return modified; +} - ajax(configOptions.endpoint, (result) => utils.logInfo(`${analyticsName}Result`, result), JSON.stringify(dataBag)); +function filterAuctionInit(data) { + let modified = Object.assign({}, data); + + modified.refererInfo = {}; + // handle clean referrer, we only need one + if (typeof modified.bidderRequests !== 'undefined' && typeof modified.bidderRequests[0] !== 'undefined' && typeof modified.bidderRequests[0].refererInfo !== 'undefined') { + modified.refererInfo = modified.bidderRequests[0].refererInfo; + } + + if (typeof modified.adUnitCodes !== 'undefined') { + delete modified.adUnitCodes; + } + if (typeof modified.adUnits !== 'undefined') { + delete modified.adUnits; + } + if (typeof modified.bidderRequests !== 'undefined') { + delete modified.bidderRequests; + } + if (typeof modified.bidsReceived !== 'undefined') { + delete modified.bidsReceived; + } + if (typeof modified.config !== 'undefined') { + delete modified.config; + } + if (typeof modified.noBids !== 'undefined') { + delete modified.noBids; + } + if (typeof modified.winningBids !== 'undefined') { + delete modified.winningBids; + } + + return modified; } -let pubwiseAnalytics = Object.assign(adapter( - { - defaultUrl, - analyticsType - }), -{ +let pubwiseAnalytics = Object.assign(adapter({analyticsType}), { // Override AnalyticsAdapter functions by supplying custom methods track({eventType, args}) { - sendEvent(eventType, args); + this.handleEvent(eventType, args); } }); +pubwiseAnalytics.handleEvent = function(eventType, data) { + // we log most events, but some are information + if (isIngestedEvent(eventType)) { + pwInfo(`Emitting Event ${eventType} ${pwAnalyticsEnabled}`, data); + + // record metadata + metaData = { + target_site: configOptions.site, + debug: configOptions.debug ? 1 : 0, + }; + metaData = enrichWithSessionInfo(metaData); + metaData = enrichWithMetrics(metaData); + metaData = enrichWithUTM(metaData); + metaData = enrichWithCustomSegments(metaData); + + // add data on init to the metadata container + if (eventType === CONSTANTS.EVENTS.AUCTION_INIT) { + data = filterAuctionInit(data); + } else if (eventType === CONSTANTS.EVENTS.BID_RESPONSE) { + data = filterBidResponse(data); + } + + // add all ingested events + pwEvents.push({ + eventType: eventType, + args: data + }); + } else { + pwInfo(`Skipping Event ${eventType} ${pwAnalyticsEnabled}`, data); + } + + // once the auction ends, or the event is a bid won send events + if (eventType === CONSTANTS.EVENTS.AUCTION_END || eventType === CONSTANTS.EVENTS.BID_WON) { + flushEvents(); + } +} + +pubwiseAnalytics.storeSessionID = function (userSessID) { + storage.setDataInLocalStorage(localStorageSessName(), userSessID); + pwInfo(`New Session Generated`, userSessID); +}; + +// ensure a session exists, if not make one, always store it +pubwiseAnalytics.ensureSession = function () { + if (sessionExpired() === true || userSessionID() === null || userSessionID() === '') { + let generatedId = utils.generateUUID(); + expireUtmData(); + this.storeSessionID(generatedId); + sessionData.sessionId = generatedId; + } + // eslint-disable-next-line + // console.log('ensured session'); + extendUserSessionTimeout(); +}; + pubwiseAnalytics.adapterEnableAnalytics = pubwiseAnalytics.enableAnalytics; pubwiseAnalytics.enableAnalytics = function (config) { - if (config.options.debug === undefined) { - config.options.debug = utils.debugTurnedOn(); + configOptions = Object.assign(configOptions, config.options); + // take the PBJS debug for our debug setting if no PW debug is defined + if (configOptions.debug === null) { + configOptions.debug = utils.debugTurnedOn(); } - configOptions = config.options; markEnabled(); + sessionData.activationId = utils.generateUUID(); + this.ensureSession(); pubwiseAnalytics.adapterEnableAnalytics(config); }; diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index 5e4b2be894e..3be4ea3d69c 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -1,47 +1,164 @@ +import { expect } from 'chai'; import pubwiseAnalytics from 'modules/pubwiseAnalyticsAdapter.js'; +import {server} from 'test/mocks/xhr.js'; let events = require('src/events'); let adapterManager = require('src/adapterManager').default; let constants = require('src/constants.json'); describe('PubWise Prebid Analytics', function () { - after(function () { + let requests; + let sandbox; + let xhr; + let clock; + let mock = {}; + + mock.DEFAULT_PW_CONFIG = { + provider: 'pubwiseanalytics', + options: { + site: ['b1ccf317-a6fc-428d-ba69-0c9c208aa61c'], + custom: {'c_script_type': 'test-script-type', 'c_host': 'test-host', 'c_slot1': 'test-slot1', 'c_slot2': 'test-slot2', 'c_slot3': 'test-slot3', 'c_slot4': 'test-slot4'} + } + }; + mock.AUCTION_INIT = {auctionId: '53c35d77-bd62-41e7-b920-244140e30c77'}; + mock.AUCTION_INIT_EXTRAS = { + auctionId: '53c35d77-bd62-41e7-b920-244140e30c77', + adUnitCodes: 'not empty', + adUnits: '', + bidderRequests: ['0'], + bidsReceived: '0', + config: {test: 'config'}, + noBids: 'no bids today', + winningBids: 'winning bids', + extraProp: 'extraProp retained' + }; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + clock = sandbox.useFakeTimers(); + sandbox.stub(events, 'getEvents').returns([]); + + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + }); + + afterEach(function () { + sandbox.restore(); + clock.restore(); pubwiseAnalytics.disableAnalytics(); }); describe('enableAnalytics', function () { beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - }); - - afterEach(function () { - events.getEvents.restore(); + requests = []; }); it('should catch all events', function () { - sinon.spy(pubwiseAnalytics, 'track'); + pubwiseAnalytics.enableAnalytics(mock.DEFAULT_PW_CONFIG); - adapterManager.registerAnalyticsAdapter({ - code: 'pubwiseanalytics', - adapter: pubwiseAnalytics - }); + sandbox.spy(pubwiseAnalytics, 'track'); - adapterManager.enableAnalytics({ - provider: 'pubwiseanalytics', - options: { - site: ['test-test-test-test'] - } - }); - - events.emit(constants.EVENTS.AUCTION_INIT, {}); + // 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, {}); - events.emit(constants.EVENTS.BID_TIMEOUT, {}); + + // eslint-disable-next-line + //console.log(requests); /* testing for 6 calls, including the 2 we're not currently tracking */ - sinon.assert.callCount(pubwiseAnalytics.track, 6); + sandbox.assert.callCount(pubwiseAnalytics.track, 7); + }); + + it('should initialize the auction properly', function () { + pubwiseAnalytics.enableAnalytics(mock.DEFAULT_PW_CONFIG); + + // 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, {}); + // force flush + clock.tick(500); + + /* check for critical values */ + let request = requests[0]; + let data = JSON.parse(request.requestBody); + // eslint-disable-next-line + // 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 + expect(data.metaData.activation_id, 'activation id').not.to.be.empty + + // check custom metadata slots + expect(data.metaData.c_script_type, 'c_script_type property').to.exist; + expect(data.metaData.c_script_type, 'c_script_type').not.to.be.empty + expect(data.metaData.c_script_type).to.equal('test-script-type'); + + expect(data.metaData.c_host, 'c_host property').to.exist; + expect(data.metaData.c_host, 'c_host').not.to.be.empty + expect(data.metaData.c_host).to.equal('test-host'); + + expect(data.metaData.c_slot1, 'c_slot1 property').to.exist; + expect(data.metaData.c_slot1, 'c_slot1').not.to.be.empty + expect(data.metaData.c_slot1).to.equal('test-slot1'); + + expect(data.metaData.c_slot2, 'c_slot1 property').to.exist; + expect(data.metaData.c_slot2, 'c_slot1').not.to.be.empty + expect(data.metaData.c_slot2).to.equal('test-slot2'); + + expect(data.metaData.c_slot3, 'c_slot1 property').to.exist; + expect(data.metaData.c_slot3, 'c_slot1').not.to.be.empty + expect(data.metaData.c_slot3).to.equal('test-slot3'); + + expect(data.metaData.c_slot4, 'c_slot1 property').to.exist; + expect(data.metaData.c_slot4, 'c_slot1').not.to.be.empty + expect(data.metaData.c_slot4).to.equal('test-slot4'); + + // check for version info too + expect(data.metaData.pw_version, 'pw_version property').to.exist; + expect(data.metaData.pbjs_version, 'pbjs_version property').to.exist; + }); + + it('should remove extra data on init', function () { + pubwiseAnalytics.enableAnalytics(mock.DEFAULT_PW_CONFIG); + + // sent + events.emit(constants.EVENTS.AUCTION_INIT, mock.AUCTION_INIT_EXTRAS); + // force flush + clock.tick(500); + + /* check for critical values */ + let request = requests[0]; + let data = JSON.parse(request.requestBody); + + // check the basics + expect(data.eventList, 'eventList property').to.exist; + expect(data.eventList[0], 'eventList property').to.exist; + expect(data.eventList[0].args, 'eventList property').to.exist; + + // eslint-disable-next-line + // console.log(data.eventList[0].args); + + let eventArgs = data.eventList[0].args; + // the props we want removed should go away + expect(eventArgs.adUnitCodes, 'adUnitCodes property').not.to.exist; + expect(eventArgs.bidderRequests, 'adUnitCodes property').not.to.exist; + expect(eventArgs.bidsReceived, 'adUnitCodes property').not.to.exist; + expect(eventArgs.config, 'adUnitCodes property').not.to.exist; + expect(eventArgs.noBids, 'adUnitCodes property').not.to.exist; + expect(eventArgs.winningBids, 'adUnitCodes property').not.to.exist; + + // the extra prop should still exist + expect(eventArgs.extraProp, 'adUnitCodes property').to.exist; }); }); }); From 565d329c7a54aeab20d421d93571745705ba5ad1 Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Wed, 9 Sep 2020 06:57:45 -0400 Subject: [PATCH 0166/1476] update amx bid adapter (#5605) * add support for RTI adapters/userID * add coppa support * add first party data support * more flexibility in sizes * enable reporting by ad unit ID * document ad unit ID * add bid request count data to the request --- modules/amxBidAdapter.js | 74 +++++++++++++++++++- modules/amxBidAdapter.md | 1 + test/spec/modules/amxBidAdapter_spec.js | 91 ++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 3 deletions(-) diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 90fcf878d62..2e9529b633c 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -1,14 +1,18 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'amx'; +const storage = getStorageManager(737, BIDDER_CODE); const SIMPLE_TLD_TEST = /\.co\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; -const VERSION = 'pba1.0'; +const VERSION = 'pba1.2'; const xmlDTDRxp = /^\s*<\?xml[^\?]+\?>/; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; +const AMUID_KEY = '__amuidpb'; const getLocation = (request) => parseUrl(deepAccess(request, 'refererInfo.canonicalUrl', location.href)) @@ -47,6 +51,22 @@ function getID(loc) { const enc = encodeURIComponent; +function getUIDSafe() { + try { + return storage.getDataFromLocalStorage(AMUID_KEY) + } catch (e) { + return null + } +} + +function setUIDSafe(uid) { + try { + storage.setDataInLocalStorage(AMUID_KEY, uid) + } catch (e) { + // do nothing + } +} + function nestedQs (qsData) { const out = []; Object.keys(qsData || {}).forEach((key) => { @@ -77,9 +97,20 @@ function convertRequest(bid) { const av = isVideoBid || size[1] > 100; const tid = deepAccess(bid, 'params.tagId') + const au = bid.params != null && typeof bid.params.adUnitId === 'string' + ? bid.params.adUnitId : bid.adUnitCode; + + const multiSizes = [ + bid.sizes, + deepAccess(bid, `mediaTypes.${BANNER}.sizes`, []) || [], + deepAccess(bid, `mediaTypes.${VIDEO}.sizes`, []) || [], + ] + const params = { + au, av, vr: isVideoBid, + ms: multiSizes, aw: size[0], ah: size[1], tf: 0, @@ -159,6 +190,16 @@ function resolveSize(bid, request, bidId) { return [bidRequest.aw, bidRequest.ah]; } +function values(source) { + if (Object.values != null) { + return Object.values(source) + } + + return Object.keys(source).map((key) => { + return source[key] + }); +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], @@ -173,11 +214,19 @@ export const spec = { const loc = getLocation(bidderRequest); const tagId = deepAccess(bidRequests[0], 'params.tagId', null); const testMode = deepAccess(bidRequests[0], 'params.testMode', 0); + const fbid = bidRequests[0] != null ? bidRequests[0] : { + bidderRequestsCount: 0, + bidderWinsCount: 0, + bidRequestsCount: 0 + } const payload = { a: bidderRequest.auctionId, B: 0, b: loc.host, + brc: fbid.bidderRequestsCount || 0, + bwc: fbid.bidderWinsCount || 0, + trc: fbid.bidRequestsCount || 0, tm: testMode, V: '$prebid.version$', i: (testMode && tagId != null) ? tagId : getID(loc), @@ -187,15 +236,32 @@ export const spec = { st: 'prebid', h: screen.height, w: screen.width, - gs: deepAccess(bidderRequest, 'gdprConsent.gdprApplies', '0'), + gs: deepAccess(bidderRequest, 'gdprConsent.gdprApplies', ''), gc: deepAccess(bidderRequest, 'gdprConsent.consentString', ''), u: deepAccess(bidderRequest, 'refererInfo.canonicalUrl', loc.href), do: loc.host, re: deepAccess(bidderRequest, 'refererInfo.referer'), + am: getUIDSafe(), usp: bidderRequest.uspConsent || '1---', smt: 1, d: '', m: createBidMap(bidRequests), + cpp: config.getConfig('coppa') ? 1 : 0, + fpd: config.getConfig('fpd'), + eids: values(bidRequests.reduce((all, bid) => { + // we only want unique ones in here + if (bid == null || bid.userIdAsEids == null) { + return all + } + + _each(bid.userIdAsEids, (value) => { + if (value == null) { + return; + } + all[value.source] = value + }); + return all; + }, {})), }; return { @@ -234,6 +300,10 @@ export const spec = { return []; } + if (response.am && typeof response.am === 'string') { + setUIDSafe(response.am); + } + return flatMap(Object.keys(response.r), (bidID) => { return flatMap(response.r[bidID], (siteBid) => siteBid.b.map((bid) => { diff --git a/modules/amxBidAdapter.md b/modules/amxBidAdapter.md index 4577f5f4f7c..2c38955028f 100644 --- a/modules/amxBidAdapter.md +++ b/modules/amxBidAdapter.md @@ -18,6 +18,7 @@ This module connects web publishers to AMX RTB video and display demand. | --- | -------- | ------- | ----------- | | `testMode` | no | `true` | this will activate test mode / 100% fill with sample ads | | `tagId` | no | `"cHJlYmlkLm9yZw"` | can be used for more specific targeting of inventory. Your account manager will provide this ID if needed | +| `adUnitId` | no | `"sticky_banner"` | optional. To override the bid.adUnitCode provided by prebid. For use in ad-unit level reporting | # Test Parameters diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 790f3bf2581..4b21244501d 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -1,7 +1,9 @@ import * as utils from 'src/utils.js'; +import { createEidsArray } from 'modules/userId/eids.js'; import { expect } from 'chai'; import { spec } from 'modules/amxBidAdapter.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; const sampleRequestId = '82c91e127a9b93e'; const sampleDisplayAd = (additionalImpressions) => `${additionalImpressions}`; @@ -14,6 +16,28 @@ const sampleVideoAd = (addlImpression) => ` const embeddedTrackingPixel = `https://1x1.a-mo.net/hbx/g_impression?A=sample&B=20903`; const sampleNurl = 'https://example.exchange/nurl'; +const sampleFPD = { + context: { + keywords: 'sample keywords', + data: { + pageType: 'article' + } + }, + user: { + gender: 'O', + yob: 1982, + } +}; + +const stubConfig = (withStub) => { + const stub = sinon.stub(config, 'getConfig').callsFake( + (arg) => arg === 'fpd' ? sampleFPD : null + ) + + withStub(); + stub.restore(); +}; + const sampleBidderRequest = { gdprConsent: { gdprApplies: true, @@ -165,12 +189,59 @@ describe('AmxBidAdapter', () => { expect(data.tm).to.equal(true); }); - it('handles referer data and GDPR, USP Consent', () => { + it('handles referer data and GDPR, USP Consent, COPPA', () => { const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); delete data.m; // don't deal with "m" in this test expect(data.gs).to.equal(sampleBidderRequest.gdprConsent.gdprApplies) expect(data.gc).to.equal(sampleBidderRequest.gdprConsent.consentString) expect(data.usp).to.equal(sampleBidderRequest.uspConsent) + expect(data.cpp).to.equal(0) + }); + + it('will forward bid request count & wins count data', () => { + const bidderRequestsCount = Math.floor(Math.random() * 100) + const bidderWinsCount = Math.floor(Math.random() * 100) + const { data } = spec.buildRequests([{ + ...sampleBidRequestBase, + bidderRequestsCount, + bidderWinsCount + }], sampleBidderRequest); + + expect(data.brc).to.equal(bidderRequestsCount) + expect(data.bwc).to.equal(bidderWinsCount) + expect(data.trc).to.equal(0) + }); + it('will forward first-party data', () => { + stubConfig(() => { + const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); + expect(data.fpd).to.deep.equal(sampleFPD) + }); + }); + + it('will collect & forward RTI user IDs', () => { + const randomRTI = `greatRTI${Math.floor(Math.random() * 100)}` + const userId = { + britepoolid: 'sample-britepool', + criteoId: 'sample-criteo', + digitrustid: {data: {id: 'sample-digitrust'}}, + id5id: 'sample-id5', + idl_env: 'sample-liveramp', + lipb: {lipbid: 'sample-liveintent'}, + netId: 'sample-netid', + parrableId: { eid: 'sample-parrable' }, + pubcid: 'sample-pubcid', + [randomRTI]: 'sample-unknown', + tdid: 'sample-ttd', + }; + + const eids = createEidsArray(userId); + const bid = { + ...sampleBidRequestBase, + userIdAsEids: eids + }; + + const { data } = spec.buildRequests([bid, bid], sampleBidderRequest); + expect(data.eids).to.deep.equal(eids) }); it('can build a banner request', () => { @@ -188,6 +259,12 @@ describe('AmxBidAdapter', () => { expect(Object.keys(data.m).length).to.equal(2); expect(data.m[sampleRequestId]).to.deep.equal({ av: true, + au: 'div-gpt-ad-example', + ms: [ + [[320, 50]], + [[300, 250]], + [] + ], aw: 300, ah: 250, tf: 0, @@ -196,6 +273,12 @@ describe('AmxBidAdapter', () => { expect(data.m[sampleRequestId + '_2']).to.deep.equal({ av: true, aw: 300, + au: 'div-gpt-ad-example', + ms: [ + [[320, 50]], + [[300, 250]], + [] + ], i: 'example', ah: 250, tf: 0, @@ -207,6 +290,12 @@ describe('AmxBidAdapter', () => { const { data } = spec.buildRequests([sampleBidRequestVideo], sampleBidderRequest); expect(Object.keys(data.m).length).to.equal(1); expect(data.m[sampleRequestId + '_video']).to.deep.equal({ + au: 'div-gpt-ad-example', + ms: [ + [[300, 150]], + [], + [[360, 250]] + ], av: true, aw: 360, ah: 250, From 85cf495aaede7c7b51dfa07875ff2c49018b4584 Mon Sep 17 00:00:00 2001 From: Pub-X <63354024+Pub-X@users.noreply.github.com> Date: Thu, 10 Sep 2020 03:27:30 +0900 Subject: [PATCH 0167/1476] Add Pub-X Bid adapter (#5676) * add Pub-X Bid Adapter * add Pub-X Bid Adapter * remove alias --- modules/pubxBidAdapter.js | 47 +++++++++ modules/pubxBidAdapter.md | 32 ++++++ test/spec/modules/pubxBidAdapter_spec.js | 125 +++++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 modules/pubxBidAdapter.js create mode 100644 modules/pubxBidAdapter.md create mode 100644 test/spec/modules/pubxBidAdapter_spec.js diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js new file mode 100644 index 00000000000..44c95e8e19a --- /dev/null +++ b/modules/pubxBidAdapter.js @@ -0,0 +1,47 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +const BIDDER_CODE = 'pubx'; +const BID_ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + if (!(bid.params.sid)) { + return false; + } else { return true } + }, + buildRequests: function(validBidRequests) { + return validBidRequests.map(bidRequest => { + const bidId = bidRequest.bidId; + const params = bidRequest.params; + const sid = params.sid; + const payload = { + sid: sid + }; + return { + id: bidId, + method: 'GET', + url: BID_ENDPOINT, + data: payload, + } + }); + }, + interpretResponse: function(serverResponse, bidRequest) { + const body = serverResponse.body; + const bidResponses = []; + if (body.cid) { + const bidResponse = { + requestId: bidRequest.id, + cpm: body.cpm, + currency: body.currency, + width: body.width, + height: body.height, + creativeId: body.cid, + netRevenue: true, + ttl: body.TTL, + ad: body.adm + }; + bidResponses.push(bidResponse); + } else {}; + return bidResponses; + } +} +registerBidder(spec); diff --git a/modules/pubxBidAdapter.md b/modules/pubxBidAdapter.md new file mode 100644 index 00000000000..da7d960c831 --- /dev/null +++ b/modules/pubxBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +Module Name: pubx Bid Adapter + +Maintainer: x@pub-x.io + +# Description + +Module that connects to Pub-X's demand sources +Supported MediaTypes: banner only + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'test', + mediaTypes: { + banner: { + sizes: [300,250] + } + }, + bids: [ + { + bidder: 'pubx', + params: { + sid: 'eDMR' //ID should be provided by Pub-X + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/pubxBidAdapter_spec.js b/test/spec/modules/pubxBidAdapter_spec.js new file mode 100644 index 00000000000..d5f1a0f5da3 --- /dev/null +++ b/test/spec/modules/pubxBidAdapter_spec.js @@ -0,0 +1,125 @@ +import {expect} from 'chai'; +import {spec} from 'modules/pubxBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +describe('pubxAdapter', function () { + const adapter = newBidder(spec); + const ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + const bid = { + bidder: 'pubx', + params: { + sid: '12345abc' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + id: '26c1ee0038ac11', + params: { + sid: '12345abc' + } + } + ]; + + const data = { + banner: { + sid: '12345abc' + } + }; + + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET'); + }); + + it('should attach params to the banner request', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.deep.equal(data.banner); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + TTL: 300, + adm: '
some creative
', + cid: 'TKmB', + cpm: 500, + currency: 'JPY', + height: 250, + width: 300, + } + } + + const bidRequests = [ + { + id: '26c1ee0038ac11', + params: { + sid: '12345abc' + } + } + ]; + + const bidResponses = [ + { + requestId: '26c1ee0038ac11', + cpm: 500, + currency: 'JPY', + width: 300, + height: 250, + creativeId: 'TKmB', + netRevenue: true, + ttl: 300, + ad: '
some creative
' + } + ]; + it('should return empty array when required param is empty', function () { + const serverResponseWithCidEmpty = { + body: { + TTL: 300, + adm: '
some creative
', + cid: '', + cpm: '', + currency: 'JPY', + height: 250, + width: 300, + } + } + const result = spec.interpretResponse(serverResponseWithCidEmpty, bidRequests[0]); + expect(result).to.be.empty; + }); + it('handles banner responses', function () { + const result = spec.interpretResponse(serverResponse, bidRequests[0])[0]; + expect(result.requestId).to.equal(bidResponses[0].requestId); + expect(result.width).to.equal(bidResponses[0].width); + expect(result.height).to.equal(bidResponses[0].height); + expect(result.creativeId).to.equal(bidResponses[0].creativeId); + expect(result.currency).to.equal(bidResponses[0].currency); + expect(result.netRevenue).to.equal(bidResponses[0].netRevenue); + expect(result.ttl).to.equal(bidResponses[0].ttl); + expect(result.ad).to.equal(bidResponses[0].ad); + }); + }); +}); From 9a92a2203f5efad3f61ca063a26a285df9c6672b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Thu, 10 Sep 2020 00:17:19 +0300 Subject: [PATCH 0168/1476] New adapter "Cointraffic" added (#5695) * New adapter "Cointraffic" added * removed mobile detection * The sizes property has been updated, added supportedMediaTypes. --- modules/cointrafficBidAdapter.js | 81 ++++++++++ modules/cointrafficBidAdapter.md | 28 ++++ .../modules/cointrafficBidAdapter_spec.js | 145 ++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 modules/cointrafficBidAdapter.js create mode 100644 modules/cointrafficBidAdapter.md create mode 100644 test/spec/modules/cointrafficBidAdapter_spec.js diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js new file mode 100644 index 00000000000..aa6860d1fc6 --- /dev/null +++ b/modules/cointrafficBidAdapter.js @@ -0,0 +1,81 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js' + +const BIDDER_CODE = 'cointraffic'; +const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [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. + */ + isBidRequestValid: function (bid) { + return !!(bid.params.placementId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param validBidRequests + * @param bidderRequest + * @return Array Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + return validBidRequests.map(bidRequest => { + const sizes = utils.parseSizesInput(bidRequest.params.size || bidRequest.sizes); + + const payload = { + placementId: bidRequest.params.placementId, + sizes: sizes, + bidId: bidRequest.bidId, + referer: bidderRequest.refererInfo.referer, + }; + + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload + }; + }); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + const response = serverResponse.body; + + if (utils.isEmpty(response)) { + return bidResponses; + } + + const bidResponse = { + requestId: response.requestId, + cpm: response.cpm, + currency: response.currency, + netRevenue: response.netRevenue, + width: response.width, + height: response.height, + creativeId: response.creativeId, + ttl: response.ttl, + ad: response.ad + }; + + bidResponses.push(bidResponse); + + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/cointrafficBidAdapter.md b/modules/cointrafficBidAdapter.md new file mode 100644 index 00000000000..ad608a1319e --- /dev/null +++ b/modules/cointrafficBidAdapter.md @@ -0,0 +1,28 @@ +# Overview + +``` +Module Name: Cointraffic Bidder Adapter +Module Type: Cointraffic Adapter +Maintainer: tech@cointraffic.io +``` + +# Description +The Cointraffic client module makes it easy to implement Cointraffic directly into your website. To get started, simply replace the ``placementId`` with your assigned tracker key. This is dependent on the size required by your account dashboard. For additional information on this module, please contact us at ``support@cointraffic.io``. + +# Test Parameters +``` + var adUnits = [{ + code: 'test-ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'cointraffic', + params: { + placementId: 'testPlacementId' + } + }] + }]; +``` diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js new file mode 100644 index 00000000000..6d948e36cb9 --- /dev/null +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -0,0 +1,145 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cointrafficBidAdapter.js'; + +const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; + +describe('cointrafficBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + bidder: 'cointraffic', + params: { + placementId: 'testPlacementId' + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250] + ], + bidId: 'bidId12345', + bidderRequestId: 'bidderRequestId12345', + auctionId: 'auctionId12345' + }; + + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: 'cointraffic', + params: { + placementId: 'testPlacementId' + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250] + ], + bidId: 'bidId12345', + bidderRequestId: 'bidderRequestId12345', + auctionId: 'auctionId12345', + }, + { + bidder: 'cointraffic', + params: { + placementId: 'testPlacementId' + }, + adUnitCode: 'adunit-code2', + sizes: [ + [300, 250] + ], + bidId: 'bidId67890"', + bidderRequestId: 'bidderRequestId67890', + auctionId: 'auctionId12345', + } + ]; + + let bidderRequests = { + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'https://example.com', + stack: [ + 'https://example.com' + ] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + expect(request[1].method).to.equal('POST'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[1].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'testPlacementId', + device: 'desktop', + sizes: ['300x250'], + bidId: 'bidId12345', + referer: 'www.example.com' + } + } + ]; + + it('should get the correct bid response', function () { + let serverResponse = { + body: { + requestId: 'bidId12345', + cpm: 3.9, + currency: 'EUR', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

', + } + }; + + let expectedResponse = [{ + requestId: 'bidId12345', + cpm: 3.9, + currency: 'EUR', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + + it('should get empty bid response if server response body is empty', function () { + let serverResponse = { + body: {} + }; + + let expectedResponse = []; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + + it('should get empty bid response if no server response', function () { + let serverResponse = {}; + + let expectedResponse = []; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + }); +}); From a2da917f028110f768b23d8f5652162f12399ada Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Thu, 10 Sep 2020 02:28:27 +0200 Subject: [PATCH 0169/1476] Send GDPR data in analytics request (#5653) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter * Collect info on ad units receiving any valid bid * Support for ID5 Pass metadata from adapter * Typo in test + eids on wrong level * Fix for Prebid 3.0 * Fix get referer * http -> https in tests * Native support * Read sizes from mediatype.banner * Revert accidental commit * Support native data collection + minor refactorings * Set analytics endpoint * Support for app parameters * Fix issue where adunits with bids were not counted on reload * Send debug info from adapter to external debugger * SChain support * Send GDPR data in analytics request --- modules/livewrappedAnalyticsAdapter.js | 27 ++++++++-- .../livewrappedAnalyticsAdapter_spec.js | 52 +++++++++++++++++-- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index b331448161e..4b1c162c67c 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -34,6 +34,9 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE cache.auctions[args.auctionId].timeStamp = args.start; args.bids.forEach(function(bidRequest) { + cache.auctions[args.auctionId].gdprApplies = args.gdprConsent ? args.gdprConsent.gdprApplies : undefined; + cache.auctions[args.auctionId].gdprConsent = args.gdprConsent ? args.gdprConsent.consentString : undefined; + cache.auctions[args.auctionId].bids[bidRequest.bidId] = { bidder: bidRequest.bidder, adUnit: bidRequest.adUnitCode, @@ -116,9 +119,11 @@ livewrappedAnalyticsAdapter.enableAnalytics = function (config) { }; livewrappedAnalyticsAdapter.sendEvents = function() { + var sentRequests = getSentRequests(); var events = { publisherId: initOptions.publisherId, - requests: getSentRequests(), + gdpr: sentRequests.gdpr, + requests: sentRequests.sentRequests, responses: getResponses(), wins: getWins(), timeouts: getTimeouts(), @@ -144,10 +149,23 @@ function getAdblockerRecovered() { function getSentRequests() { var sentRequests = []; + var gdpr = []; Object.keys(cache.auctions).forEach(auctionId => { + let auction = cache.auctions[auctionId]; + var gdprPos = 0; + for (gdprPos = 0; gdprPos < gdpr.length; gdprPos++) { + if (gdpr[gdprPos].gdprApplies == auction.gdprApplies && + gdpr[gdprPos].gdprConsent == auction.gdprConsent) { + break; + } + } + + if (gdprPos == gdpr.length) { + gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; + } + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { - let auction = cache.auctions[auctionId]; let bid = auction.bids[bidId]; if (!(bid.sendStatus & REQUESTSENT)) { bid.sendStatus |= REQUESTSENT; @@ -155,13 +173,14 @@ function getSentRequests() { sentRequests.push({ timeStamp: auction.timeStamp, adUnit: bid.adUnit, - bidder: bid.bidder + bidder: bid.bidder, + gdpr: gdprPos }); } }); }); - return sentRequests; + return {gdpr: gdpr, sentRequests: sentRequests}; } function getResponses() { diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js index 4e05d1a31ff..c723f589fa0 100644 --- a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -120,6 +120,7 @@ const MOCK = { const ANALYTICS_MESSAGE = { publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', + gdpr: [{}], bidAdUnits: [ { adUnit: 'panorama_d_1', @@ -134,17 +135,20 @@ const ANALYTICS_MESSAGE = { { adUnit: 'panorama_d_1', bidder: 'livewrapped', - timeStamp: 1519149562216 + timeStamp: 1519149562216, + gdpr: 0 }, { adUnit: 'box_d_1', bidder: 'livewrapped', - timeStamp: 1519149562216 + timeStamp: 1519149562216, + gdpr: 0 }, { adUnit: 'box_d_2', bidder: 'livewrapped', - timeStamp: 1519149562216 + timeStamp: 1519149562216, + gdpr: 0 } ], responses: [ @@ -321,6 +325,48 @@ describe('Livewrapped analytics adapter', function () { expect(message.rcv).to.equal(true); }); + + it('should forward GDPR data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'livewrapped', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'livewrapped', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ecff0db240757', + }, + { + 'bidder': 'livewrapped', + 'adUnitCode': 'box_d_1', + 'bidId': '3ecff0db240757', + } + ], + 'start': 1519149562216, + 'gdprConsent': { + 'gdprApplies': true, + 'consentString': 'consentstring' + } + }, + ); + events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + expect(message.gdpr[0].gdprApplies).to.equal(true); + expect(message.gdpr[0].gdprConsent).to.equal('consentstring'); + expect(message.requests.length).to.equal(2); + expect(message.requests[0].gdpr).to.equal(0); + expect(message.requests[1].gdpr).to.equal(0); + }); }); describe('when given other endpoint', function () { From 8f249dc099db5d9eb7faaf16614b05244f4ea20d Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Thu, 10 Sep 2020 14:31:28 +0530 Subject: [PATCH 0170/1476] GDPR Enforcement - Bugfix (#5686) * consolicate getGVLID function into a single function * pass correct arguments to gvlid getter functions * have one master getGvlid getter function to rule other gvlId getter functions * restore to file state on master branch * remove unnecessary example * remove unnecessary reference from internal object * works on comments and change getgvlidForAnalyticds Adapter to a one liner --- modules/gdprEnforcement.js | 93 ++++++++++++++--------- test/spec/modules/gdprEnforcement_spec.js | 57 +++++++++++++- 2 files changed, 111 insertions(+), 39 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 97eaedd92be..adbccd8666d 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -47,58 +47,75 @@ const analyticsBlocked = []; let addedDeviceAccessHook = false; +// Helps in stubbing these functions in unit tests. +export const internal = { + getGvlidForBidAdapter, + getGvlidForUserIdModule, + getGvlidForAnalyticsAdapter +}; + /** - * Returns gvlId for Bid Adapters. If a bidder does not have an associated gvlId, it returns 'undefined'. - * @param {string=} bidderCode - The 'code' property on the Bidder spec. - * @retuns {number} gvlId + * Returns GVL ID for a Bid adapter / an USERID submodule / an Analytics adapter. + * If modules of different types have the same moduleCode: For example, 'appnexus' is the code for both Bid adapter and Analytics adapter, + * then, we assume that their GVL IDs are same. This function first checks if GVL ID is defined for a Bid adapter, if not found, tries to find User ID + * submodule's GVL ID, if not found, tries to find Analytics adapter's GVL ID. In this process, as soon as it finds a GVL ID, it returns it + * without going to the next check. + * @param {{string|Object}} - module + * @return {number} - GVL ID */ -function getGvlid(bidderCode) { - let gvlid; +export function getGvlid(module) { + let gvlid = null; + if (module) { + // Check user defined GVL Mapping in pbjs.setConfig() + const gvlMapping = config.getConfig('gvlMapping'); + + // For USER ID Module, we pass the submodule object itself as the "module" parameter, this check is required to grab the module code + const moduleCode = typeof module === 'string' ? module : module.name; + + // Return GVL ID from user defined gvlMapping + if (gvlMapping && gvlMapping[moduleCode]) { + gvlid = gvlMapping[moduleCode]; + return gvlid; + } + + gvlid = internal.getGvlidForBidAdapter(moduleCode) || internal.getGvlidForUserIdModule(module) || internal.getGvlidForAnalyticsAdapter(moduleCode); + } + return gvlid; +} + +/** + * Returns GVL ID for a bid adapter. If the adapter does not have an associated GVL ID, it returns 'null'. + * @param {string=} bidderCode - The 'code' property of the Bidder spec. + * @return {number} GVL ID + */ +function getGvlidForBidAdapter(bidderCode) { + let gvlid = null; bidderCode = bidderCode || config.getCurrentBidder(); if (bidderCode) { - const gvlMapping = config.getConfig('gvlMapping'); - if (gvlMapping && gvlMapping[bidderCode]) { - gvlid = gvlMapping[bidderCode]; - } else { - const bidder = adapterManager.getBidAdapter(bidderCode); - if (bidder && bidder.getSpec) { - gvlid = bidder.getSpec().gvlid; - } + const bidder = adapterManager.getBidAdapter(bidderCode); + if (bidder && bidder.getSpec) { + gvlid = bidder.getSpec().gvlid; } } return gvlid; } /** - * Returns gvlId for userId module. If a userId modules does not have an associated gvlId, it returns 'undefined'. + * Returns GVL ID for an userId submodule. If an userId submodules does not have an associated GVL ID, it returns 'null'. * @param {Object} userIdModule - * @retuns {number} gvlId + * @return {number} GVL ID */ function getGvlidForUserIdModule(userIdModule) { - let gvlId; - const gvlMapping = config.getConfig('gvlMapping'); - if (gvlMapping && gvlMapping[userIdModule.name]) { - gvlId = gvlMapping[userIdModule.name]; - } else { - gvlId = userIdModule.gvlid; - } - return gvlId; + return (typeof userIdModule === 'object' ? userIdModule.gvlid : null); } /** - * Returns gvlId for analytics adapters. If a analytics adapter does not have an associated gvlId, it returns 'undefined'. + * 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 - * @returns {number} gvlId + * @return {number} GVL ID */ function getGvlidForAnalyticsAdapter(code) { - let gvlId; - const gvlMapping = config.getConfig('gvlMapping'); - if (gvlMapping && gvlMapping[code]) { - gvlId = gvlMapping[code]; - } else { - gvlId = adapterManager.getAnalyticsAdapter(code).gvlid; - } - return gvlId; + return adapterManager.getAnalyticsAdapter(code) && (adapterManager.getAnalyticsAdapter(code).gvlid || null); } /** @@ -165,7 +182,7 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { if (curBidder && (curBidder != moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { gvlid = getGvlid(curBidder); } else { - gvlid = getGvlid(moduleName); + gvlid = getGvlid(moduleName) || gvlid; } const curModule = moduleName || curBidder; let isAllowed = validateRules(purpose1Rule, consentData, curModule, gvlid); @@ -199,8 +216,8 @@ export function userSyncHook(fn, ...args) { const consentData = gdprDataHandler.getConsentData(); if (consentData && consentData.gdprApplies) { if (consentData.apiVersion === 2) { - const gvlid = getGvlid(); const curBidder = config.getCurrentBidder(); + const gvlid = getGvlid(curBidder); let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); if (isAllowed) { fn.call(this, ...args); @@ -227,7 +244,7 @@ export function userIdHook(fn, submodules, consentData) { if (consentData && consentData.gdprApplies) { if (consentData.apiVersion === 2) { let userIdModules = submodules.map((submodule) => { - const gvlid = getGvlidForUserIdModule(submodule.submodule); + const gvlid = getGvlid(submodule.submodule); const moduleName = submodule.submodule.name; let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid); if (isAllowed) { @@ -296,7 +313,7 @@ export function enableAnalyticsHook(fn, config) { } config = config.filter(conf => { const analyticsAdapterCode = conf.provider; - const gvlid = getGvlidForAnalyticsAdapter(analyticsAdapterCode); + const gvlid = getGvlid(analyticsAdapterCode); const isAllowed = !!validateRules(purpose7Rule, consentData, analyticsAdapterCode, gvlid); if (!isAllowed) { analyticsBlocked.push(analyticsAdapterCode); @@ -347,7 +364,7 @@ const hasPurpose7 = (rule) => { return rule.purpose === TCF2.purpose7.name } export function setEnforcementConfig(config) { const rules = utils.deepAccess(config, 'gdpr.rules'); if (!rules) { - utils.logWarn('TCF2: enforcing P1 and P2'); + utils.logWarn('TCF2: enforcing P1 and P2 by default'); enforcementRules = DEFAULT_RULES; } else { enforcementRules = rules; diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index d5c8d7bfb88..82cb70f42be 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -8,7 +8,9 @@ import { enforcementRules, purpose1Rule, purpose2Rule, - enableAnalyticsHook + enableAnalyticsHook, + getGvlid, + internal } from 'modules/gdprEnforcement.js'; import { config } from 'src/config.js'; import adapterManager, { gdprDataHandler } from 'src/adapterManager.js'; @@ -1067,4 +1069,57 @@ describe('gdpr enforcement', function () { sinon.assert.calledWith(events.emit.getCall(1), 'tcf2Enforcement', sinon.match.object); }) }); + + describe('getGvlid', function() { + 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); + }); + + 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); + + 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); + }); + + 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); + }); + }); }); From 2acca6f2e1b7a1fc4f6e977936f6a06686cd23b1 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 10 Sep 2020 07:38:48 -0400 Subject: [PATCH 0171/1476] changes SameSite from None to Lax for tests on Chrome 85.0.4183 which added rejection of insecure SameSite=None cookies (#5719) --- test/spec/modules/fintezaAnalyticsAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index 29136c85241..4c76f79f518 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -12,7 +12,7 @@ function setCookie(name, value, expires) { document.cookie = name + '=' + value + '; path=/' + (expires ? ('; expires=' + expires.toUTCString()) : '') + - '; SameSite=None'; + '; SameSite=Lax'; } describe('finteza analytics adapter', function () { From c01cab138b66f937ded5a8cb191c902a025ef661 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Thu, 10 Sep 2020 06:41:14 -0700 Subject: [PATCH 0172/1476] implement issue #5687 (#5716) --- modules/prebidServerBidAdapter/index.js | 3 +++ test/spec/modules/prebidServerBidAdapter_spec.js | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a7a3199675d..0ff967f1da9 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -504,6 +504,9 @@ const OPEN_RTB_PROTOCOL = { // Don't push oustream w/o renderer to request object. utils.logError('Outstream bid without renderer cannot be sent to Prebid Server.'); } else { + if (videoParams.context === 'instream' && !videoParams.hasOwnProperty('placement')) { + videoParams.placement = 1; + } mediaTypes['video'] = videoParams; } } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index a300a10d31b..d29657e8e53 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -509,6 +509,22 @@ describe('S2S Adapter', function () { expect(requestBid.imp[0].video).to.not.exist; }); + it('should default video placement if not defined and instream', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + config.setConfig({ s2sConfig: ortb2Config }); + + let videoBid = utils.deepClone(VIDEO_REQUEST); + videoBid.ad_units[0].mediaTypes.video.context = 'instream'; + adapter.callBids(videoBid, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(server.requests[0].requestBody); + expect(requestBid.imp[0].banner).to.not.exist; + expect(requestBid.imp[0].video).to.exist; + expect(requestBid.imp[0].video.placement).to.equal(1); + }); + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); From 466b49e85a1e173de255ef31c61a73f0cc4d8c57 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 10 Sep 2020 10:17:45 -0400 Subject: [PATCH 0173/1476] allow publisher to define backup renderer (#5638) * Update Renderer.js * Update auction.js * Update renderer_spec.js * Update auctionmanager_spec.js * Update prebidServerBidAdapter_spec.js --- src/Renderer.js | 6 ++--- src/auction.js | 4 ++-- test/spec/auctionmanager_spec.js | 23 +++++++++++++++++++ .../modules/prebidServerBidAdapter_spec.js | 2 +- test/spec/renderer_spec.js | 23 +++++++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/Renderer.js b/src/Renderer.js index 85bcbb383e8..f073d97d052 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -39,7 +39,7 @@ export function Renderer(options) { // use a function, not an arrow, in order to be able to pass "arguments" through this.render = function () { - if (!isRendererDefinedOnAdUnit(adUnitCode)) { + if (!isRendererPreferredFromAdUnit(adUnitCode)) { // we expect to load a renderer url once only so cache the request to load script loadExternalScript(url, moduleCode, this.callback); } else { @@ -110,10 +110,10 @@ export function executeRenderer(renderer, bid) { renderer.render(bid); } -function isRendererDefinedOnAdUnit(adUnitCode) { +function isRendererPreferredFromAdUnit(adUnitCode) { const adUnits = $$PREBID_GLOBAL$$.adUnits; const adUnit = find(adUnits, adUnit => { return adUnit.code === adUnitCode; }); - return !!(adUnit && adUnit.renderer && adUnit.renderer.url && adUnit.renderer.render); + return !!(adUnit && adUnit.renderer && adUnit.renderer.url && adUnit.renderer.render && !(utils.isBoolean(adUnit.renderer.backupOnly) && adUnit.renderer.backupOnly)); } diff --git a/src/auction.js b/src/auction.js index b82b4752479..5858c3edf78 100644 --- a/src/auction.js +++ b/src/auction.js @@ -57,7 +57,7 @@ * @property {function(): void} callBids - sends requests to all adapters for bids */ -import {flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl} from './utils.js'; +import {flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl, isBoolean} from './utils.js'; import { getPriceBucketString } from './cpmBucketManager.js'; import { getNativeTargeting } from './native.js'; import { getCacheUrl, store } from './videoCache.js'; @@ -512,7 +512,7 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { const bidReq = bidderRequest.bids && find(bidderRequest.bids, bid => bid.adUnitCode == adUnitCode); const adUnitRenderer = bidReq && bidReq.renderer; - if (adUnitRenderer && adUnitRenderer.url) { + if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly && isBoolean(adUnitRenderer.backupOnly) && bid.renderer)) { bidObject.renderer = Renderer.install({ url: adUnitRenderer.url }); bidObject.renderer.setRender(adUnitRenderer.render); } diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 35a29727614..e35b1406fbf 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -740,6 +740,29 @@ describe('auctionmanager.js', function () { assert.equal(addedBid.renderer.url, 'renderer.js'); }); + it('installs publisher-defined backup renderers on bids', function () { + let renderer = { + url: 'renderer.js', + backupOnly: true, + render: (bid) => bid + }; + let bidRequests = [Object.assign({}, TEST_BID_REQS[0])]; + bidRequests[0].bids[0] = Object.assign({ renderer }, bidRequests[0].bids[0]); + makeRequestsStub.returns(bidRequests); + + let bids1 = Object.assign({}, + bids[0], + { + bidderCode: BIDDER_CODE, + mediaType: 'video-outstream', + } + ); + spec.interpretResponse.returns(bids1); + auction.callBids(); + const addedBid = auction.getBidsReceived().pop(); + assert.equal(addedBid.renderer.url, 'renderer.js'); + }); + it('bid for a regular unit and a video unit', function() { let renderer = { url: 'renderer.js', diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d29657e8e53..d6f755914e5 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -497,7 +497,7 @@ describe('S2S Adapter', function () { resetSyncedStatus(); }); - it('should not add outstrean without renderer', function () { + it('should not add outstream without renderer', function () { let ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js index 77d806e4dbc..9bf551f35e8 100644 --- a/test/spec/renderer_spec.js +++ b/test/spec/renderer_spec.js @@ -132,6 +132,29 @@ describe('Renderer', function () { expect(utilsSpy.callCount).to.equal(1); }); + it('should load renderer adunit renderer when backupOnly', function() { + $$PREBID_GLOBAL$$.adUnits = [{ + code: 'video1', + renderer: { + url: 'http://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + backupOnly: true, + render: sinon.spy() + } + }] + + let testRenderer = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config1' }, + id: 1, + adUnitCode: 'video1' + + }); + testRenderer.setRender(() => {}) + + testRenderer.render() + expect(loadExternalScript.called).to.be.true; + }); + it('should call loadExternalScript() for script not defined on adUnit, only when .render() is called', function() { $$PREBID_GLOBAL$$.adUnits = [{ code: 'video1', From 22bb7581484fef0a06c13cb283b624e9abaf7f34 Mon Sep 17 00:00:00 2001 From: Kanchika - Automatad Date: Fri, 11 Sep 2020 00:30:21 +0530 Subject: [PATCH 0174/1476] Automatad Bid Adapter: Support multiple bids in response (#5699) * added automatad bid adapter * added automatad bid adapter readme * added automatad bidder adapter unit test * updated maintainer email id for automatad adapter * refactored automatadBidAdapter js * refactored automatadBidAdapter unit test * refactored automatadBidAdapter unit test * added usersync code to automatad bid adapter * Added unit test for onBidWon in automatadBidAdapter_spec * removed trailing spaces * removed trailing space * changes for getUserSync function * lint error fixes * updated usersync url * additional test for onBidWon function added * added ajax stub in test * updated winurl params * lint fixes * added adunitCode in bid request * added test for adunit code * add placement in impression object * added code to interpret multiple bid response in seatbid --- modules/automatadBidAdapter.js | 30 +++++------ test/spec/modules/automatadBidAdapter_spec.js | 50 +++++++++++++++++++ 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index e1a69a37513..6b66044f5e5 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -71,20 +71,22 @@ export const spec = { const bidResponses = [] const response = (serverResponse || {}).body - if (response && response.seatbid && response.seatbid.length === 1 && response.seatbid[0].bid && response.seatbid[0].bid.length) { - response.seatbid[0].bid.forEach(bid => { - bidResponses.push({ - requestId: bid.impid, - cpm: bid.price, - ad: bid.adm, - adDomain: bid.adomain[0], - currency: DEFAULT_CURRENCY, - ttl: DEFAULT_BID_TTL, - creativeId: bid.crid, - width: bid.w, - height: bid.h, - netRevenue: DEFAULT_NET_REVENUE, - nurl: bid.nurl, + if (response && response.seatbid && response.seatbid[0].bid && response.seatbid[0].bid.length) { + response.seatbid.forEach(bidObj => { + bidObj.bid.forEach(bid => { + bidResponses.push({ + requestId: bid.impid, + cpm: bid.price, + ad: bid.adm, + adDomain: bid.adomain[0], + currency: DEFAULT_CURRENCY, + ttl: DEFAULT_BID_TTL, + creativeId: bid.crid, + width: bid.w, + height: bid.h, + netRevenue: DEFAULT_NET_REVENUE, + nurl: bid.nurl, + }) }) }) } else { diff --git a/test/spec/modules/automatadBidAdapter_spec.js b/test/spec/modules/automatadBidAdapter_spec.js index e0341a1d255..fca1a464ff2 100644 --- a/test/spec/modules/automatadBidAdapter_spec.js +++ b/test/spec/modules/automatadBidAdapter_spec.js @@ -113,6 +113,56 @@ describe('automatadBidAdapter', function () { expect(result).to.be.an('array').that.is.not.empty }) + it('should interpret multiple bids in seatbid', function () { + let multipleBidResponse = [{ + 'body': { + 'id': 'abc-321', + 'seatbid': [ + { + 'bid': [ + { + 'adm': '', + 'adomain': [ + 'someAdDomain' + ], + 'crid': 123, + 'h': 600, + 'id': 'bid1', + 'impid': 'imp1', + 'nurl': 'https://example/win', + 'price': 0.5, + 'w': 300 + } + ] + }, { + 'bid': [ + { + 'adm': '', + 'adomain': [ + 'someAdDomain' + ], + 'crid': 321, + 'h': 600, + 'id': 'bid1', + 'impid': 'imp2', + 'nurl': 'https://example/win', + 'price': 0.5, + 'w': 300 + } + ] + } + ] + } + }] + let result = spec.interpretResponse(multipleBidResponse[0]).map(bid => { + const {requestId} = bid; + return [ requestId ]; + }); + + assert.equal(result.length, 2); + assert.deepEqual(result, [[ 'imp1' ], [ 'imp2' ]]); + }) + it('handles empty bid response', function () { let response = { body: '' From 277fb9b98ade48704211876a56074f9482f3a106 Mon Sep 17 00:00:00 2001 From: Carlos Barreiro Mata Date: Fri, 11 Sep 2020 00:29:45 +0200 Subject: [PATCH 0175/1476] Fix: check mandatory video params (#5470) * Fix: check mandatory video params * Simplifying mediaType video existence check --- modules/seedtagBidAdapter.js | 12 ++--- test/spec/modules/seedtagBidAdapter_spec.js | 59 ++++++++++++++------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 018339fabe4..e1832e50020 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -18,8 +18,8 @@ function mapMediaType(seedtagMediaType) { else return seedtagMediaType; } -function getMediaTypeFromBid(bid) { - return bid.mediaTypes && Object.keys(bid.mediaTypes)[0] +function hasVideoMediaType(bid) { + return !!bid.mediaTypes && !!bid.mediaTypes.video } function hasMandatoryParams(params) { @@ -34,7 +34,7 @@ function hasMandatoryParams(params) { ); } -function hasVideoMandatoryParams(mediaTypes) { +function hasMandatoryVideoParams(mediaTypes) { const isVideoInStream = !!mediaTypes.video && mediaTypes.video.context === 'instream'; const isPlayerSize = @@ -65,7 +65,7 @@ function buildBidRequests(validBidRequests) { bidRequest.adPosition = params.adPosition; } - if (params.video) { + if (hasVideoMediaType(validBidRequest)) { bidRequest.videoParams = params.video || {}; bidRequest.videoParams.w = validBidRequest.mediaTypes.video.playerSize[0][0]; @@ -124,8 +124,8 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid(bid) { - return getMediaTypeFromBid(bid) === VIDEO - ? hasMandatoryParams(bid.params) && hasVideoMandatoryParams(bid.mediaTypes) + return hasVideoMediaType(bid) + ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid.mediaTypes) : hasMandatoryParams(bid.params); }, diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 5c8c58196f7..8957bde6bd9 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -1,6 +1,9 @@ import { expect } from 'chai' import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter.js' +const PUBLISHER_ID = '0000-0000-01' +const ADUNIT_ID = '000000' + function getSlotConfigs(mediaTypes, params) { return { params: params, @@ -16,10 +19,16 @@ function getSlotConfigs(mediaTypes, params) { } } +function createVideoSlotConfig(mediaType) { + return getSlotConfigs(mediaType, { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'video' + }) +} + describe('Seedtag Adapter', function() { describe('isBidRequestValid method', function() { - const PUBLISHER_ID = '0000-0000-01' - const ADUNIT_ID = '000000' describe('returns true', function() { describe('when banner slot config has all mandatory params', () => { describe('and placement has the correct value', function() { @@ -66,13 +75,13 @@ describe('Seedtag Adapter', function() { }) describe('returns false', function() { describe('when params are not correct', function() { - function createSlotconfig(params) { + function createSlotConfig(params) { return getSlotConfigs({ banner: {} }, params) } it('does not have the PublisherToken.', function() { const isBidRequestValid = spec.isBidRequestValid( - createSlotconfig({ - adUnitId: '000000', + createSlotConfig({ + adUnitId: ADUNIT_ID, placement: 'banner' }) ) @@ -80,8 +89,8 @@ describe('Seedtag Adapter', function() { }) it('does not have the AdUnitId.', function() { const isBidRequestValid = spec.isBidRequestValid( - createSlotconfig({ - publisherId: '0000-0000-01', + createSlotConfig({ + publisherId: PUBLISHER_ID, placement: 'banner' }) ) @@ -89,18 +98,18 @@ describe('Seedtag Adapter', function() { }) it('does not have the placement.', function() { const isBidRequestValid = spec.isBidRequestValid( - createSlotconfig({ - publisherId: '0000-0000-01', - adUnitId: '000000' + createSlotConfig({ + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID }) ) expect(isBidRequestValid).to.equal(false) }) it('does not have a the correct placement.', function() { const isBidRequestValid = spec.isBidRequestValid( - createSlotconfig({ - publisherId: '0000-0000-01', - adUnitId: '000000', + createSlotConfig({ + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, placement: 'another_thing' }) ) @@ -117,19 +126,19 @@ describe('Seedtag Adapter', function() { } it('is a void object', function() { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotconfig({ video: {} }) + createVideoSlotConfig({ video: {} }) ) expect(isBidRequestValid).to.equal(false) }) it('does not have playerSize.', function() { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotconfig({ video: { context: 'instream' } }) + createVideoSlotConfig({ video: { context: 'instream' } }) ) expect(isBidRequestValid).to.equal(false) }) it('is not instream ', function() { const isBidRequestValid = spec.isBidRequestValid( - createVideoSlotconfig({ + createVideoSlotConfig({ video: { context: 'outstream', playerSize: [[600, 200]] @@ -138,6 +147,20 @@ describe('Seedtag Adapter', function() { ) 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: {} }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('when video is the first slot', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotConfig({ video: {}, banner: {} }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) }) }) }) @@ -148,8 +171,8 @@ describe('Seedtag Adapter', function() { timeout: 1000 } const mandatoryParams = { - publisherId: '0000-0000-01', - adUnitId: '000000', + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, placement: 'banner' } const inStreamParams = Object.assign({}, mandatoryParams, { From 3c6e07508071b8cf54c66a049965f0cee40dba18 Mon Sep 17 00:00:00 2001 From: Eric Nolte Date: Fri, 11 Sep 2020 04:14:03 -0400 Subject: [PATCH 0176/1476] add verizon alias to aol (#5722) * add verizon alias to aol * Update aolBidAdapter.js --- modules/aolBidAdapter.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index d7ff7453870..1f43231e495 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -4,6 +4,7 @@ import { BANNER } from '../src/mediaTypes.js'; const AOL_BIDDERS_CODES = { AOL: 'aol', + VERIZON: 'verizon', ONEMOBILE: 'onemobile', ONEDISPLAY: 'onedisplay' }; @@ -48,10 +49,10 @@ const NUMERIC_VALUES = { }; function template(strings, ...keys) { - return function(...values) { + return function (...values) { let dict = values[values.length - 1] || {}; let result = [strings[0]]; - keys.forEach(function(key, i) { + keys.forEach(function (key, i) { let value = utils.isInteger(key) ? values[key] : dict[key]; result.push(value, strings[i + 1]); }); @@ -59,12 +60,16 @@ function template(strings, ...keys) { }; } -function _isMarketplaceBidder(bidder) { - return bidder === AOL_BIDDERS_CODES.AOL || bidder === AOL_BIDDERS_CODES.ONEDISPLAY; +function _isMarketplaceBidder(bidderCode) { + return bidderCode === AOL_BIDDERS_CODES.AOL || + bidderCode === AOL_BIDDERS_CODES.VERIZON || + bidderCode === AOL_BIDDERS_CODES.ONEDISPLAY; } function _isOneMobileBidder(bidderCode) { - return bidderCode === AOL_BIDDERS_CODES.AOL || bidderCode === AOL_BIDDERS_CODES.ONEMOBILE; + return bidderCode === AOL_BIDDERS_CODES.AOL || + bidderCode === AOL_BIDDERS_CODES.VERIZON || + bidderCode === AOL_BIDDERS_CODES.ONEMOBILE; } function _isNexageRequestPost(bid) { @@ -101,7 +106,11 @@ function resolveEndpointCode(bid) { export const spec = { code: AOL_BIDDERS_CODES.AOL, gvlid: 25, - aliases: [AOL_BIDDERS_CODES.ONEMOBILE, AOL_BIDDERS_CODES.ONEDISPLAY], + aliases: [ + AOL_BIDDERS_CODES.ONEMOBILE, + AOL_BIDDERS_CODES.ONEDISPLAY, + AOL_BIDDERS_CODES.VERIZON + ], supportedMediaTypes: [BANNER], isBidRequestValid(bid) { return isMarketplaceBid(bid) || isMobileBid(bid); @@ -121,7 +130,7 @@ export const spec = { } }); }, - interpretResponse({body}, bidRequest) { + interpretResponse({ body }, bidRequest) { if (!body) { utils.logError('Empty bid response', bidRequest.bidderCode, body); } else { @@ -216,11 +225,11 @@ export const spec = { })); }, buildOneMobileGetUrl(bid, consentData) { - let {dcn, pos, ext} = bid.params; + let { dcn, pos, ext } = bid.params; let nexageApi = this.buildOneMobileBaseUrl(bid); if (dcn && pos) { let dynamicParams = this.formatOneMobileDynamicParams(ext, consentData); - nexageApi += nexageGetApiTemplate({dcn, pos, dynamicParams}); + nexageApi += nexageGetApiTemplate({ dcn, pos, dynamicParams }); } return nexageApi; }, From ba8ef8624aec1247df9f0f2df4815836d82faa07 Mon Sep 17 00:00:00 2001 From: Dmitriy Labuzov Date: Fri, 11 Sep 2020 12:25:30 +0300 Subject: [PATCH 0177/1476] Add prebid version to ad-server call (#5730) Co-authored-by: Dmitriy Labuzov --- modules/yieldmoBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 829b573ffd9..08dc3189eda 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -27,6 +27,7 @@ export const spec = { */ buildRequests: function (bidRequests, bidderRequest) { let serverRequest = { + pbav: '$prebid.version$', p: [], page_url: bidderRequest.refererInfo.referer, bust: new Date().getTime().toString(), From 1e9be731d43a5c9ce7734f48eb967c1adb4c2c2e Mon Sep 17 00:00:00 2001 From: shikharsharma-zeotap Date: Fri, 11 Sep 2020 21:45:07 +0530 Subject: [PATCH 0178/1476] Zeotap ID+ submodule (#5640) * IDU-117 IDU-119 Add zeotap submodule * IDU-117 IDU-119 Add tests for zeotapId+ module * add zeotapId+ module spec * Add IDP base64 decode logic * remove unwanted file changes * rename zeotapId+ to zeotapIdPlus * add zeotapIdPlus submodule to submodules.json * refactor code for requested changes: remove storage from configParams * add tests to eids_spec * rebase n resolve conflicts --- integrationExamples/gpt/userId_example.html | 3 + modules/.submodules.json | 3 +- modules/userId/eids.js | 5 + modules/userId/eids.md | 9 +- modules/zeotapIdPlusIdSystem.js | 52 +++++++ test/spec/modules/eids_spec.js | 14 ++ test/spec/modules/userId_spec.js | 87 ++++++++--- .../spec/modules/zeotapIdPlusIdSystem_spec.js | 141 ++++++++++++++++++ 8 files changed, 292 insertions(+), 22 deletions(-) create mode 100644 modules/zeotapIdPlusIdSystem.js create mode 100644 test/spec/modules/zeotapIdPlusIdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 8115e60fcd1..51b9f2aef90 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -230,6 +230,9 @@ name: "_li_pbid", expires: 28 } + }, + { + name: "zeotapIdPlus" }], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/.submodules.json b/modules/.submodules.json index 50d17fc5f6c..bacc543401f 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -12,7 +12,8 @@ "netIdSystem", "identityLinkIdSystem", "sharedIdSystem", - "intentIqIdSystem" + "intentIqIdSystem", + "zeotapIdPlusIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 15399b9b980..e6c3dbd5bd8 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -123,6 +123,11 @@ const USER_IDS_CONFIG = { third: data.third } : undefined; } + }, + // zeotapIdPlus + 'IDP': { + source: 'zeotap.com', + atype: 1 } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 846b9b19207..fc46fef7b97 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -95,6 +95,13 @@ userIdAsEids = [ third: 'some-random-id-value' } }] - } + }, + { + source: 'zeotap.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, ] ``` diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js new file mode 100644 index 00000000000..c194a9b9679 --- /dev/null +++ b/modules/zeotapIdPlusIdSystem.js @@ -0,0 +1,52 @@ +/** + * This module adds Zeotap to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/zeotapIdPlusIdSystem + * @requires module:modules/userId + */ +import * as utils from '../src/utils.js' +import {submodule} from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const ZEOTAP_COOKIE_NAME = 'IDP'; +const storage = getStorageManager(); + +function readCookie() { + return storage.cookiesAreEnabled ? storage.getCookie(ZEOTAP_COOKIE_NAME) : null; +} + +function readFromLocalStorage() { + return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(ZEOTAP_COOKIE_NAME) : null; +} + +/** @type {Submodule} */ +export const zeotapIdPlusSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'zeotapIdPlus', + /** + * decode the stored id value for passing to bid requests + * @function + * @param { Object | string | undefined } value + * @return { Object | string | undefined } + */ + decode(value) { + const id = value ? utils.isStr(value) ? value : utils.isPlainObject(value) ? value.id : undefined : undefined; + return id ? { + 'IDP': JSON.parse(atob(id)) + } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} configParams + * @return {{id: string | undefined} | undefined} + */ + getId() { + const id = readCookie() || readFromLocalStorage(); + return id ? { id } : undefined; + } +}; +submodule('userId', zeotapIdPlusSubmodule); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 8ad44f0b1ad..a0bc0a84ee3 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -192,6 +192,20 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('zeotapIdPlus', function() { + const userId = { + IDP: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'zeotap.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }); + }); }); describe('Negative case', function() { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 5ac68de345d..167187a281f 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -26,6 +26,7 @@ import {liveIntentIdSubmodule} from 'modules/liveIntentIdSystem.js'; import {merkleIdSubmodule} from 'modules/merkleIdSystem.js'; import {netIdSubmodule} from 'modules/netIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; +import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; import {server} from 'test/mocks/xhr.js'; @@ -354,7 +355,7 @@ describe('User ID', function() { }); it('handles config with no usersync object', function() { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -362,14 +363,14 @@ describe('User ID', function() { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -380,7 +381,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -397,15 +398,15 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 9 configurations should result in 9 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + it('config with 10 configurations should result in 10 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -436,14 +437,16 @@ describe('User ID', function() { }, { name: 'intentIqId', storage: { name: 'intentIqId', type: 'cookie' } + }, { + name: 'zeotapIdPlus' }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 9 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 10 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -458,7 +461,7 @@ describe('User ID', function() { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -473,7 +476,7 @@ describe('User ID', function() { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ userSync: { @@ -1142,7 +1145,31 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, sharedId and netId have data to pass', function(done) { + it('test hook from zeotapIdPlus cookies', function(done) { + // simulate existing browser local storage values + coreStorage.setCookie('IDP', btoa(JSON.stringify('abcdefghijk')), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([zeotapIdPlusSubmodule]); + init(config); + config.setConfig(getConfigMock(['zeotapIdPlus', 'IDP', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('abcdefghijk'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'zeotap.com', + uids: [{id: 'abcdefghijk', atype: 1}] + }); + }); + }); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId and netId have data to pass', function(done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1150,9 +1177,10 @@ describe('User ID', function() { coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1161,7 +1189,8 @@ describe('User ID', function() { ['britepoolId', 'britepoolid', 'cookie'], ['netId', 'netId', 'cookie'], ['sharedId', 'sharedid', 'cookie'], - ['intentIqId', 'intentIqId', 'cookie'])); + ['intentIqId', 'intentIqId', 'cookie'], + ['zeotapIdPlus', 'IDP', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -1192,7 +1221,10 @@ describe('User ID', function() { // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); - expect(bid.userIdAsEids.length).to.equal(8); + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + expect(bid.userIdAsEids.length).to.equal(9); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1203,11 +1235,12 @@ describe('User ID', function() { coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, sharedId and netId have their modules added before and after init', function(done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId and netId have their modules added before and after init', function(done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1216,6 +1249,7 @@ describe('User ID', function() { coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1232,6 +1266,7 @@ describe('User ID', function() { attachIdSystem(netIdSubmodule); attachIdSystem(sharedIdSubmodule); attachIdSystem(intentIqIdSubmodule); + attachIdSystem(zeotapIdPlusSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1240,7 +1275,8 @@ describe('User ID', function() { ['britepoolId', 'britepoolid', 'cookie'], ['netId', 'netId', 'cookie'], ['sharedId', 'sharedid', 'cookie'], - ['intentIqId', 'intentIqId', 'cookie'])); + ['intentIqId', 'intentIqId', 'cookie'], + ['zeotapIdPlus', 'IDP', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -1271,7 +1307,10 @@ describe('User ID', function() { // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); - expect(bid.userIdAsEids.length).to.equal(8); + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + expect(bid.userIdAsEids.length).to.equal(9); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1282,6 +1321,7 @@ describe('User ID', function() { coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -1316,9 +1356,10 @@ describe('User ID', function() { coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); init(config); config.setConfig({ @@ -1340,6 +1381,8 @@ describe('User ID', function() { name: 'sharedId', storage: {name: 'sharedid', type: 'cookie'} }, { name: 'intentIqId', storage: { name: 'intentIqId', type: 'cookie' } + }, { + name: 'zeotapIdPlus' }, { name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} }] @@ -1393,7 +1436,10 @@ describe('User ID', function() { // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); - expect(bid.userIdAsEids.length).to.equal(8); + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + expect(bid.userIdAsEids.length).to.equal(9); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1404,6 +1450,7 @@ describe('User ID', function() { coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); diff --git a/test/spec/modules/zeotapIdPlusIdSystem_spec.js b/test/spec/modules/zeotapIdPlusIdSystem_spec.js new file mode 100644 index 00000000000..52698ecffc9 --- /dev/null +++ b/test/spec/modules/zeotapIdPlusIdSystem_spec.js @@ -0,0 +1,141 @@ +import { expect } from 'chai'; +import find from 'core-js-pure/features/array/find.js'; +import { config } from 'src/config.js'; +import { newStorageManager } from 'src/storageManager.js'; +import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { zeotapIdPlusSubmodule } from 'modules/zeotapIdPlusIdSystem.js'; + +const storage = newStorageManager(); + +const ZEOTAP_COOKIE_NAME = 'IDP'; +const ZEOTAP_COOKIE = 'THIS-IS-A-DUMMY-COOKIE'; +const ENCODED_ZEOTAP_COOKIE = btoa(JSON.stringify(ZEOTAP_COOKIE)); + +function getConfigMock() { + return { + userSync: { + syncDelay: 0, + userIds: [{ + name: 'zeotapIdPlus' + }] + } + } +} + +function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: {banner: {}, native: {}}, + sizes: [ + [300, 200], + [300, 600] + ], + bids: [{ + bidder: 'sampleBidder', + params: { placementId: 'banner-only-bidder' } + }] + }; +} + +function unsetCookie() { + storage.setCookie(ZEOTAP_COOKIE_NAME, ''); +} + +function unsetLocalStorage() { + storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ''); +} + +describe('Zeotap ID System', function() { + describe('test method: getId', function() { + afterEach(() => { + unsetCookie(); + unsetLocalStorage(); + }) + + it('provides the stored Zeotap id if a cookie exists', function() { + storage.setCookie( + ZEOTAP_COOKIE_NAME, + ENCODED_ZEOTAP_COOKIE, + (new Date(Date.now() + 5000).toUTCString()), + ); + let id = zeotapIdPlusSubmodule.getId(); + expect(id).to.deep.equal({ + id: ENCODED_ZEOTAP_COOKIE + }); + }); + + it('provides the stored Zeotap id if cookie is absent but present in local storage', function() { + storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ENCODED_ZEOTAP_COOKIE); + let id = zeotapIdPlusSubmodule.getId(); + expect(id).to.deep.equal({ + id: ENCODED_ZEOTAP_COOKIE + }); + }); + + it('returns undefined if both cookie and local storage are empty', function() { + let id = zeotapIdPlusSubmodule.getId(); + expect(id).to.be.undefined + }) + }); + + describe('test method: decode', function() { + it('provides the Zeotap ID (IDP) from a stored object', function() { + let zeotapId = { + id: ENCODED_ZEOTAP_COOKIE, + }; + + expect(zeotapIdPlusSubmodule.decode(zeotapId)).to.deep.equal({ + IDP: ZEOTAP_COOKIE + }); + }); + + it('provides the Zeotap ID (IDP) from a stored string', function() { + let zeotapId = ENCODED_ZEOTAP_COOKIE; + + expect(zeotapIdPlusSubmodule.decode(zeotapId)).to.deep.equal({ + IDP: ZEOTAP_COOKIE + }); + }); + }); + + describe('requestBids hook', function() { + let adUnits; + + beforeEach(function() { + adUnits = [getAdUnitMock()]; + storage.setCookie( + ZEOTAP_COOKIE_NAME, + ENCODED_ZEOTAP_COOKIE, + (new Date(Date.now() + 5000).toUTCString()), + ); + setSubmoduleRegistry([zeotapIdPlusSubmodule]); + init(config); + config.setConfig(getConfigMock()); + }); + + afterEach(function() { + unsetCookie(); + unsetLocalStorage(); + }); + + it('when a stored Zeotap ID exists it is added to bids', function(done) { + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal(ZEOTAP_COOKIE); + const zeotapIdAsEid = find(bid.userIdAsEids, e => e.source == 'zeotap.com'); + expect(zeotapIdAsEid).to.deep.equal({ + source: 'zeotap.com', + uids: [{ + id: ZEOTAP_COOKIE, + atype: 1, + }] + }); + }); + }); + done(); + }, { adUnits }); + }); + }); +}); From 896cc0f00e9c0fe2df4cc30310b589b1fc8854c6 Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 11 Sep 2020 15:19:12 -0400 Subject: [PATCH 0179/1476] Prebid Server returns exp rather than ttl (#5715) Updating how pbsBidAdapter sets the "TTL" for bids. TTL in PBJS terms is how long the bid can stay in cache. However, the OpenRTB standard location for this value is `exp`. --- modules/prebidServerBidAdapter/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 0ff967f1da9..b153d0bf8db 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -825,9 +825,9 @@ const OPEN_RTB_PROTOCOL = { bidObject.meta = bidObject.meta || {}; if (bid.adomain) { bidObject.meta.advertiserDomains = bid.adomain; } - // TODO: Remove when prebid-server returns ttl and netRevenue const configTtl = _s2sConfig.defaultTtl || DEFAULT_S2S_TTL; - bidObject.ttl = (bid.ttl) ? bid.ttl : configTtl; + // the OpenRTB location for "TTL" as understood by Prebid.js is "exp" (expiration). + bidObject.ttl = (bid.exp) ? bid.exp : configTtl; bidObject.netRevenue = (bid.netRevenue) ? bid.netRevenue : DEFAULT_S2S_NETREVENUE; bids.push({ adUnit: bid.impid, bid: bidObject }); From 65b8dc082102fe41f8952f4476984ca2189a5ee7 Mon Sep 17 00:00:00 2001 From: Rigel Cheng Date: Mon, 14 Sep 2020 14:56:09 +0800 Subject: [PATCH 0180/1476] Update the checking rule of bid param for bridgewellBidAdapter (#5736) * Update the checking rule of bid param for bridgewellBidAdapter * Update bridgewellBidAdapter.js Co-authored-by: rigel_home --- modules/bridgewellBidAdapter.js | 12 ++++---- .../spec/modules/bridgewellBidAdapter_spec.js | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 0303e4f74bd..a2d3a2e70a2 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -5,7 +5,7 @@ import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'bridgewell'; const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb=' + Math.random(); -const BIDDER_VERSION = '0.0.2'; +const BIDDER_VERSION = '0.0.3'; export const spec = { code: BIDDER_CODE, @@ -19,11 +19,13 @@ export const spec = { */ isBidRequestValid: function (bid) { let valid = false; - - if (bid && bid.params && bid.params.ChannelID) { - valid = true; + if (bid && bid.params) { + if ((bid.params.cid) && (typeof bid.params.cid === 'number')) { + valid = true; + } else if (bid.params.ChannelID) { + valid = true; + } } - return valid; }, diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 644f468abe8..fea2454393d 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -22,6 +22,16 @@ describe('bridgewellBidAdapter', function () { expect(spec.isBidRequestValid(validTag)).to.equal(true); }); + it('should return true when required params found', function () { + const validTag = { + 'bidder': 'bridgewell', + 'params': { + 'cid': 1234 + }, + }; + expect(spec.isBidRequestValid(validTag)).to.equal(true); + }); + it('should return false when required params not found', function () { const invalidTag = { 'bidder': 'bridgewell', @@ -39,6 +49,26 @@ describe('bridgewellBidAdapter', function () { }; expect(spec.isBidRequestValid(invalidTag)).to.equal(false); }); + + it('should return false when required params are empty', function () { + const invalidTag = { + 'bidder': 'bridgewell', + 'params': { + 'cid': '', + }, + }; + expect(spec.isBidRequestValid(invalidTag)).to.equal(false); + }); + + it('should return false when required param cid is not a number', function () { + const invalidTag = { + 'bidder': 'bridgewell', + 'params': { + 'cid': 'bad_cid', + }, + }; + expect(spec.isBidRequestValid(invalidTag)).to.equal(false); + }); }); describe('buildRequests', function () { From fd38cff451c8a7459e34b23ee474b822e8804687 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Mon, 14 Sep 2020 03:01:35 -0400 Subject: [PATCH 0181/1476] Add gvlid (#5737) --- modules/pubwiseAnalyticsAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 74b56c21a2b..fe217454b88 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -330,7 +330,8 @@ pubwiseAnalytics.enableAnalytics = function (config) { adapterManager.registerAnalyticsAdapter({ adapter: pubwiseAnalytics, - code: 'pubwise' + code: 'pubwise', + gvlid: 842 }); export default pubwiseAnalytics; From 0bd72ab4c8f88effd9e25a22db51a9d9dbceccfe Mon Sep 17 00:00:00 2001 From: colbertk <50499465+colbertk@users.noreply.github.com> Date: Mon, 14 Sep 2020 04:01:38 -0400 Subject: [PATCH 0182/1476] Triplelift: fpd and advertiser name support (#5731) * 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 * TripleLift: Sending schain (#1) * Sending schain * null -> undefined * Hardcode sync endpoint protocol * Switch to EB2 sync endpoint * Add support for image based user syncing * Rename endpoint variable * Add assertion * Add CCPA query param * Simplify check for usPrivacy argument * put advertiser name in the bid.meta field if it exists * update unit tests with meta.advertiserName field * Triplelift: FPD key value pair support (#5) * Triplelift: Add support for global fpd * don't filter fpd * adds coppa support back in Co-authored-by: Will Chapin Co-authored-by: David Andersen Co-authored-by: Brandon Ling Co-authored-by: Kevin Zhou Co-authored-by: kzhouTL <43545828+kzhouTL@users.noreply.github.com> --- modules/tripleliftBidAdapter.js | 49 +++++++++++++++++-- .../spec/modules/tripleliftBidAdapter_spec.js | 40 +++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index d6b1f95351d..b003de7785f 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -111,6 +111,8 @@ function _getSyncType(syncOptions) { function _buildPostBody(bidRequests) { let data = {}; let { schain } = bidRequests[0]; + const globalFpd = _getGlobalFpd(); + data.imp = bidRequests.map(function(bidRequest, index) { let imp = { id: index, @@ -137,10 +139,10 @@ function _buildPostBody(bidRequests) { }; } - if (schain) { - data.ext = { - schain - } + let ext = _getExt(schain, globalFpd); + + if (!utils.isEmpty(ext)) { + data.ext = ext; } return data; } @@ -172,6 +174,38 @@ function _getFloor (bid) { return floor !== null ? floor : bid.params.floor; } +function _getGlobalFpd() { + let fpd = {}; + const fpdContext = Object.assign({}, config.getConfig('fpd.context')); + const fpdUser = Object.assign({}, config.getConfig('fpd.user')); + + _addEntries(fpd, fpdContext); + _addEntries(fpd, fpdUser); + + return fpd; +} + +function _addEntries(target, source) { + if (!utils.isEmpty(source)) { + Object.keys(source).forEach(key => { + if (source[key] != null) { + target[key] = source[key]; + } + }); + } +} + +function _getExt(schain, fpd) { + let ext = {}; + if (!utils.isEmpty(schain)) { + ext.schain = { ...schain }; + } + if (!utils.isEmpty(fpd)) { + ext.fpd = { ...fpd }; + } + return ext; +} + function getUnifiedIdEids(bidRequests) { return getEids(bidRequests, 'tdid', 'adserver.org', 'TDID'); } @@ -239,13 +273,18 @@ function _buildResponseObject(bidderRequest, bid) { dealId: dealId, currency: 'USD', ttl: 300, - tl_source: bid.tl_source + tl_source: bid.tl_source, + meta: {} }; if (breq.mediaTypes.video) { bidResponse.vastXml = bid.ad; bidResponse.mediaType = 'video'; }; + + if (bid.advertiser_name) { + bidResponse.meta.advertiserName = bid.advertiser_name; + } }; return bidResponse; } diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index c6c3b622755..797b3fab0c1 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -4,6 +4,7 @@ import { newBidder } from 'src/adapters/bidderFactory.js'; import { deepClone } from 'src/utils.js'; import { config } from 'src/config.js'; import prebid from '../../../package.json'; +import * as utils from 'src/utils.js'; const ENDPOINT = 'https://tlx.3lift.com/header/auction?'; const GDPR_CONSENT_STR = 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY'; @@ -11,6 +12,7 @@ const GDPR_CONSENT_STR = 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7V describe('triplelift adapter', function () { const adapter = newBidder(tripleliftAdapterSpec); let bid, instreamBid; + let sandbox; this.beforeEach(() => { bid = { @@ -194,6 +196,10 @@ describe('triplelift adapter', function () { gdprApplies: true }, }; + sandbox = sinon.sandbox.create(); + }); + afterEach(() => { + sandbox.restore(); }); it('exists and is an object', function () { @@ -397,6 +403,31 @@ describe('triplelift adapter', function () { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); expect(request.data.imp[0].floor).to.equal(1.99); }); + it('should send fpd on root level ext if kvps are available', function() { + const sens = null; + const category = ['news', 'weather', 'hurricane']; + const pmp_elig = 'true'; + const fpd = { + context: { + pmp_elig, + category, + }, + user: { + sens, + } + } + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + fpd + }; + return utils.deepAccess(config, key); + }); + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const { data: payload } = request; + expect(payload.ext.fpd).to.not.haveOwnProperty('sens'); + expect(payload.ext.fpd).to.haveOwnProperty('category'); + expect(payload.ext.fpd).to.haveOwnProperty('pmp_elig'); + }); }); describe('interpretResponse', function () { @@ -413,6 +444,7 @@ describe('triplelift adapter', function () { ad: 'ad-markup', iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', tl_source: 'tlx', + advertiser_name: 'fake advertiser name' }, { imp_id: 1, @@ -486,6 +518,7 @@ describe('triplelift adapter', function () { currency: 'USD', ttl: 33, tl_source: 'tlx', + meta: {} }, { requestId: '30b31c1838de1e', @@ -501,6 +534,7 @@ describe('triplelift adapter', function () { tl_source: 'hdx', mediaType: 'video', vastXml: 'The Trade Desk', + meta: {} } ]; let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); @@ -513,6 +547,12 @@ describe('triplelift adapter', function () { let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); expect(result).to.have.length(2); }); + + it('should include the advertiser name in the meta field if available', function () { + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result[0].meta.advertiserName).to.equal('fake advertiser name') + expect(result[1].meta).to.not.have.key('advertiserName'); + }); }); describe('getUserSyncs', function() { From 1e065e43474740565852a162babb5dc9733eb0e9 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Mon, 14 Sep 2020 09:29:31 -0400 Subject: [PATCH 0183/1476] Package Lock Should Match Package.json (#5734) This drifted away for some reason, likely some quirk of update order, in a PR that added "deep-equal". https://github.com/prebid/Prebid.js/commit/477fe0c10d78d878aa8135cc4852957874447759 --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 4f3d2120d72..1784b885be9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.27.0-pre", + "version": "4.8.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { From 1704572233d736fef19f6f92a2c5162ec07eb19f Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 14 Sep 2020 12:36:42 -0700 Subject: [PATCH 0184/1476] PubMatic BidAdapter: pass auctionId as default value for wiid param (#5744) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * pass auctionId as default value for wiid param in pubmatic adpater external call --- modules/pubmaticBidAdapter.js | 2 +- test/spec/modules/pubmaticBidAdapter_spec.js | 308 ++++++++++++++----- 2 files changed, 240 insertions(+), 70 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index d21854a57c4..1651f1f5cc0 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -938,7 +938,7 @@ export const spec = { payload.ext.wrapper = {}; payload.ext.wrapper.profile = parseInt(conf.profId) || UNDEFINED; payload.ext.wrapper.version = parseInt(conf.verId) || UNDEFINED; - payload.ext.wrapper.wiid = conf.wiid || UNDEFINED; + payload.ext.wrapper.wiid = conf.wiid || bidderRequest.auctionId; // eslint-disable-next-line no-undef payload.ext.wrapper.wv = $$REPO_AND_VERSION$$; payload.ext.wrapper.transactionId = conf.transactionId; diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 0f51a8df61c..c1f62ee9f2b 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -796,18 +796,24 @@ describe('PubMatic adapter', function () { describe('Request formation', function () { it('buildRequests function should not modify original bidRequests object', function () { let originalBidRequests = utils.deepClone(bidRequests); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); expect(bidRequests).to.deep.equal(originalBidRequests); }); it('buildRequests function should not modify original nativebidRequests object', function () { let originalBidRequests = utils.deepClone(nativeBidRequests); - let request = spec.buildRequests(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests, { + auctionId: 'new-auction-id' + }); expect(nativeBidRequests).to.deep.equal(originalBidRequests); }); it('Endpoint checking', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); expect(request.url).to.equal('https://hbopenbid.pubmatic.com/translator?source=prebid-client'); expect(request.method).to.equal('POST'); }); @@ -823,7 +829,9 @@ describe('PubMatic adapter', function () { }); it('test flag not sent when pubmaticTest=true is absent in page url', function() { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.test).to.equal(undefined); }); @@ -833,13 +841,17 @@ describe('PubMatic adapter', function () { xit('test flag set to 1 when pubmaticTest=true is present in page url', function() { window.location.href += '#pubmaticTest=true'; // now all the test cases below will have window.location.href with #pubmaticTest=true - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.test).to.equal(1); }); it('Request params check', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.at).to.equal(1); // auction type expect(data.cur[0]).to.equal('USD'); // currency @@ -882,7 +894,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.site.content).to.deep.equal(content); sandbox.restore(); @@ -898,7 +912,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.device.js).to.equal(1); expect(data.device.dnt).to.equal((navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0); @@ -920,7 +936,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.device.js).to.equal(1); expect(data.device.dnt).to.equal((navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0); @@ -942,7 +960,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); @@ -967,7 +987,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); @@ -997,7 +1019,9 @@ describe('PubMatic adapter', function () { }; return config[key]; }); - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); @@ -1010,7 +1034,9 @@ describe('PubMatic adapter', function () { it('Request params check: without adSlot', function () { delete bidRequests[0].params.adSlot; - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.at).to.equal(1); // auction type expect(data.cur[0]).to.equal('USD'); // currency @@ -1068,7 +1094,9 @@ describe('PubMatic adapter', function () { } ]; /* case 1 - size passed in adslot */ - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].banner.w).to.equal(300); // width @@ -1081,7 +1109,9 @@ describe('PubMatic adapter', function () { sizes: [[300, 600], [300, 250]] } }; - request = spec.buildRequests(bidRequests); + request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].banner.w).to.equal(300); // width @@ -1095,7 +1125,9 @@ describe('PubMatic adapter', function () { sizes: [[300, 250], [300, 600]] } }; - request = spec.buildRequests(bidRequests); + request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].banner.w).to.equal(300); // width @@ -1163,7 +1195,9 @@ describe('PubMatic adapter', function () { output: imp[0] and imp[1] both use currency specified in bidRequests[0].params.currency */ - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); @@ -1175,7 +1209,9 @@ describe('PubMatic adapter', function () { */ delete multipleBidRequests[1].params.currency; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); expect(data.imp[1].bidfloorcur).to.equal(bidRequests[0].params.currency); @@ -1186,7 +1222,9 @@ describe('PubMatic adapter', function () { */ delete multipleBidRequests[0].params.currency; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].bidfloorcur).to.equal('USD'); expect(data.imp[1].bidfloorcur).to.equal('USD'); @@ -1197,12 +1235,46 @@ describe('PubMatic adapter', function () { */ multipleBidRequests[1].params.currency = 'AUD'; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].bidfloorcur).to.equal('USD'); expect(data.imp[1].bidfloorcur).to.equal('USD'); }); + it('Pass auctiondId as wiid if wiid is not passed in params', function () { + let bidRequest = { + auctionId: 'new-auction-id' + }; + delete bidRequests[0].params.wiid; + let request = spec.buildRequests(bidRequests, bidRequest); + let data = JSON.parse(request.data); + expect(data.at).to.equal(1); // auction type + expect(data.cur[0]).to.equal('USD'); // currency + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL + expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id + expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB + expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender + expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.wiid).to.equal('new-auction-id'); // OpenWrap: Wrapper Impression ID + expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID + expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID + + expect(data.imp[0].id).to.equal(bidRequests[0].bidId); // Prebid bid id is passed as id + expect(data.imp[0].bidfloor).to.equal(parseFloat(bidRequests[0].params.kadfloor)); // kadfloor + expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid + expect(data.imp[0].banner.w).to.equal(300); // width + expect(data.imp[0].banner.h).to.equal(250); // height + expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + }); + it('Request params check with GDPR Consent', function () { let bidRequest = { gdprConsent: { @@ -1311,7 +1383,9 @@ describe('PubMatic adapter', function () { it('bidfloor should be undefined if calculation is <= 0', function() { floorModuleTestData.banner.floor = 0; // lowest of them all newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(undefined); @@ -1320,7 +1394,9 @@ describe('PubMatic adapter', function () { it('ignore floormodule o/p if floor is not number', function() { floorModuleTestData.banner.floor = 'INR'; newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(2.5); // video will be lowest now @@ -1329,7 +1405,9 @@ describe('PubMatic adapter', function () { it('ignore floormodule o/p if currency is not matched', function() { floorModuleTestData.banner.currency = 'INR'; newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(2.5); // video will be lowest now @@ -1337,7 +1415,9 @@ describe('PubMatic adapter', function () { it('kadfloor is not passed, use minimum from floorModule', function() { newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(1.5); @@ -1345,7 +1425,9 @@ describe('PubMatic adapter', function () { it('kadfloor is passed as 3, use kadfloor as it is highest', function() { newRequest[0].params.kadfloor = '3.0';// yes, we want it as a string - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(3); @@ -1353,7 +1435,9 @@ describe('PubMatic adapter', function () { it('kadfloor is passed as 1, use min of fllorModule as it is highest', function() { newRequest[0].params.kadfloor = '1.0';// yes, we want it as a string - let request = spec.buildRequests(newRequest); + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.bidfloor).to.equal(1.5); @@ -1807,7 +1891,9 @@ describe('PubMatic adapter', function () { }); it('Request params check for video ad', function () { - let request = spec.buildRequests(videoBidRequests); + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].video).to.exist; expect(data.imp[0].tagid).to.equal('Div1'); @@ -1845,7 +1931,9 @@ describe('PubMatic adapter', function () { }); it('Request params check for 1 banner and 1 video ad', function () { - let request = spec.buildRequests(multipleMediaRequests); + let request = spec.buildRequests(multipleMediaRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp).to.be.an('array') @@ -1913,7 +2001,9 @@ describe('PubMatic adapter', function () { }); it('Request params should have valid native bid request for all valid params', function () { - let request = spec.buildRequests(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].native).to.exist; expect(data.imp[0].native['request']).to.exist; @@ -1923,13 +2013,17 @@ describe('PubMatic adapter', function () { }); it('Request params should not have valid native bid request for non native request', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].native).to.not.exist; }); it('Request params should have valid native bid request with valid required param values for all valid params', function () { - let request = spec.buildRequests(nativeBidRequestsWithRequiredParam); + let request = spec.buildRequests(nativeBidRequestsWithRequiredParam, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].native).to.exist; expect(data.imp[0].native['request']).to.exist; @@ -1939,12 +2033,16 @@ describe('PubMatic adapter', function () { }); it('should not have valid native request if assets are not defined with minimum required params and only native is the slot', function () { - let request = spec.buildRequests(nativeBidRequestsWithoutAsset); + let request = spec.buildRequests(nativeBidRequestsWithoutAsset, { + auctionId: 'new-auction-id' + }); expect(request).to.deep.equal(undefined); }); it('Request params should have valid native bid request for all native params', function () { - let request = spec.buildRequests(nativeBidRequestsWithAllParams); + let request = spec.buildRequests(nativeBidRequestsWithAllParams, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.imp[0].native).to.exist; expect(data.imp[0].native['request']).to.exist; @@ -1954,7 +2052,9 @@ describe('PubMatic adapter', function () { }); it('Request params - should handle banner and video format in single adunit', function() { - let request = spec.buildRequests(bannerAndVideoBidRequests); + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.banner).to.exist; @@ -1965,7 +2065,9 @@ describe('PubMatic adapter', function () { // Case: when size is not present in adslo bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; - request = spec.buildRequests(bannerAndVideoBidRequests); + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); data = data.imp[0]; expect(data.banner).to.exist; @@ -1987,7 +2089,9 @@ describe('PubMatic adapter', function () { */ bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; - let request = spec.buildRequests(bannerAndVideoBidRequests); + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2006,7 +2110,9 @@ describe('PubMatic adapter', function () { bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; - request = spec.buildRequests(bannerAndVideoBidRequests); + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); data = data.imp[0]; @@ -2024,7 +2130,9 @@ describe('PubMatic adapter', function () { */ bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [[728, 90], ['fluid'], [300, 250]]; - request = spec.buildRequests(bannerAndVideoBidRequests); + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); data = data.imp[0]; @@ -2042,7 +2150,9 @@ describe('PubMatic adapter', function () { */ bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid']]; - request = spec.buildRequests(bannerAndVideoBidRequests); + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); data = data.imp[0]; @@ -2054,14 +2164,18 @@ describe('PubMatic adapter', function () { delete bannerAndVideoBidRequests[0].mediaTypes.banner; bannerAndVideoBidRequests[0].params.sizes = [300, 250]; - let request = spec.buildRequests(bannerAndVideoBidRequests); + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; expect(data.banner).to.not.exist; }); it('Request params - should handle banner and native format in single adunit', function() { - let request = spec.buildRequests(bannerAndNativeBidRequests); + let request = spec.buildRequests(bannerAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2076,7 +2190,9 @@ describe('PubMatic adapter', function () { }); it('Request params - should handle video and native format in single adunit', function() { - let request = spec.buildRequests(videoAndNativeBidRequests); + let request = spec.buildRequests(videoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2089,7 +2205,9 @@ describe('PubMatic adapter', function () { }); it('Request params - should handle banner, video and native format in single adunit', function() { - let request = spec.buildRequests(bannerVideoAndNativeBidRequests); + let request = spec.buildRequests(bannerVideoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2111,7 +2229,9 @@ describe('PubMatic adapter', function () { delete bannerAndNativeBidRequests[0].mediaTypes.banner; bannerAndNativeBidRequests[0].sizes = [729, 90]; - let request = spec.buildRequests(bannerAndNativeBidRequests); + let request = spec.buildRequests(bannerAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2134,7 +2254,9 @@ describe('PubMatic adapter', function () { sponsoredBy: { required: true }, clickUrl: { required: true } } - let request = spec.buildRequests(bannerAndNativeBidRequests); + let request = spec.buildRequests(bannerAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2155,7 +2277,9 @@ describe('PubMatic adapter', function () { sponsoredBy: { required: true }, clickUrl: { required: true } } - let request = spec.buildRequests(videoAndNativeBidRequests); + let request = spec.buildRequests(videoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data = data.imp[0]; @@ -2218,7 +2342,9 @@ describe('PubMatic adapter', function () { } ]; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); /* case 1 - @@ -2232,7 +2358,9 @@ describe('PubMatic adapter', function () { dctr not present in adunit[0] */ delete multipleBidRequests[0].params.dctr; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.site.ext).to.not.exist; @@ -2241,7 +2369,9 @@ describe('PubMatic adapter', function () { dctr is present in adunit[0], but is not a string value */ multipleBidRequests[0].params.dctr = 123; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.site.ext).to.not.exist; @@ -2301,7 +2431,9 @@ describe('PubMatic adapter', function () { } ]; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); // case 1 - deals are passed as expected, ['', ''] , in both adUnits expect(data.imp[0].pmp).to.deep.equal({ @@ -2329,19 +2461,25 @@ describe('PubMatic adapter', function () { // case 2 - deals not present in adunit[0] delete multipleBidRequests[0].params.deals; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].pmp).to.not.exist; // case 3 - deals is present in adunit[0], but is not an array multipleBidRequests[0].params.deals = 123; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].pmp).to.not.exist; // case 4 - deals is present in adunit[0] as an array but one of the value is not a string multipleBidRequests[0].params.deals = [123, 'deal-id-1']; - request = spec.buildRequests(multipleBidRequests); + request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); data = JSON.parse(request.data); expect(data.imp[0].pmp).to.deep.equal({ 'private_auction': 0, @@ -2409,21 +2547,27 @@ describe('PubMatic adapter', function () { it('bcat: pass only strings', function() { multipleBidRequests[0].params.bcat = [1, 2, 3, 'IAB1', 'IAB2']; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); }); it('bcat: pass strings with length greater than 3', function() { multipleBidRequests[0].params.bcat = ['AB', 'CD', 'IAB1', 'IAB2']; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); }); it('bcat: trim the strings', function() { multipleBidRequests[0].params.bcat = [' IAB1 ', ' IAB2 ']; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2']); }); @@ -2432,7 +2576,9 @@ describe('PubMatic adapter', function () { // multi slot multipleBidRequests[0].params.bcat = ['IAB1', 'IAB2', 'IAB1', 'IAB2', 'IAB1', 'IAB2']; multipleBidRequests[1].params.bcat = ['IAB1', 'IAB2', 'IAB1', 'IAB2', 'IAB1', 'IAB3']; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.bcat).to.exist.and.to.deep.equal(['IAB1', 'IAB2', 'IAB3']); }); @@ -2441,7 +2587,9 @@ describe('PubMatic adapter', function () { // multi slot multipleBidRequests[0].params.bcat = ['', 'IAB', 'IAB']; multipleBidRequests[1].params.bcat = [' ', 22, 99999, 'IA']; - let request = spec.buildRequests(multipleBidRequests); + let request = spec.buildRequests(multipleBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); expect(data.bcat).to.deep.equal(undefined); }); @@ -2449,7 +2597,9 @@ describe('PubMatic adapter', function () { describe('Response checking', function () { it('should check for valid response values', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); let response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').with.length.above(0); @@ -2503,7 +2653,9 @@ describe('PubMatic adapter', function () { }); it('should check for dealChannel value selection', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(bidResponses, request); expect(response).to.be.an('array').with.length.above(0); expect(response[0].dealChannel).to.equal('PMPG'); @@ -2511,7 +2663,9 @@ describe('PubMatic adapter', function () { }); it('should check for unexpected dealChannel value selection', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let updateBiResponse = bidResponses; updateBiResponse.body.seatbid[0].bid[0].ext.deal_channel = 11; @@ -2522,7 +2676,9 @@ describe('PubMatic adapter', function () { }); it('should have a valid native bid response', function() { - let request = spec.buildRequests(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests, { + auctionId: 'new-auction-id' + }); let data = JSON.parse(request.data); data.imp[0].id = '2a5571261281d4'; request.data = JSON.stringify(data); @@ -2540,20 +2696,26 @@ describe('PubMatic adapter', function () { }); it('should check for valid banner mediaType in case of multiformat request', function() { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(bannerBidResponse, request); expect(response[0].mediaType).to.equal('banner'); }); it('should check for valid video mediaType in case of multiformat request', function() { - let request = spec.buildRequests(videoBidRequests); + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(videoBidResponse, request); expect(response[0].mediaType).to.equal('video'); }); it('should check for valid native mediaType in case of multiformat request', function() { - let request = spec.buildRequests(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(nativeBidResponse, request); expect(response[0].mediaType).to.equal('native'); @@ -2566,25 +2728,33 @@ describe('PubMatic adapter', function () { }); it('should not assign renderer if bidderRequest is not present', function() { - let request = spec.buildRequests(outstreamBidRequest); + let request = spec.buildRequests(outstreamBidRequest, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(outstreamVideoBidResponse, request); expect(response[0].renderer).to.not.exist; }); it('should not assign renderer if bid is video and request is for instream', function() { - let request = spec.buildRequests(videoBidRequests); + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(videoBidResponse, request); expect(response[0].renderer).to.not.exist; }); it('should not assign renderer if bid is native', function() { - let request = spec.buildRequests(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(nativeBidResponse, request); expect(response[0].renderer).to.not.exist; }); it('should not assign renderer if bid is of banner', function() { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); let response = spec.interpretResponse(bidResponses, request); expect(response[0].renderer).to.not.exist; }); From 30a069ccd46ff2acf51d2794ab2c1adace0d7e88 Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Tue, 15 Sep 2020 02:23:44 -0500 Subject: [PATCH 0185/1476] Audigent HaloID User Id System Module (#5524) * audigentRtdProvider * fix line error * fix tests * deep equal test fix * conditionally set data in html5 local storage, add markdown readme * add integration test instructions to audigent readme * halo id update * add halo id to userid hpt example, submoduleparams check * haloId submodule update * remove script tag from haloid submodule * add eid tests * update docs * merge upstream master * name update * url update, name update * eid test update * remove getIds * remove unused import * style update * remove erroneous chars * remove comments * fix updated tests * fix id count check placement * style update * style update in eids.js * trailing space fix * documentation update --- integrationExamples/gpt/userId_example.html | 13 +++- modules/.submodules.json | 4 +- modules/haloIdSystem.js | 63 +++++++++++++++ modules/haloIdSystem.md | 32 ++++++++ modules/userId/eids.js | 8 ++ modules/userId/eids.md | 7 ++ test/spec/modules/eids_spec.js | 16 ++++ test/spec/modules/userId_spec.js | 86 +++++++++++++++++---- 8 files changed, 209 insertions(+), 20 deletions(-) create mode 100644 modules/haloIdSystem.js create mode 100644 modules/haloIdSystem.md diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 51b9f2aef90..521eb8bc9fa 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -230,10 +230,19 @@ name: "_li_pbid", expires: 28 } + }, + { + name: "zeotapIdPlus" }, { - name: "zeotapIdPlus" - }], + name: 'haloId', + storage: { + type: "cookie", + name: "haloId", + expires: 28 + } + } + ], syncDelay: 5000, auctionDelay: 1000 } diff --git a/modules/.submodules.json b/modules/.submodules.json index bacc543401f..575c3294bf1 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -13,7 +13,8 @@ "identityLinkIdSystem", "sharedIdSystem", "intentIqIdSystem", - "zeotapIdPlusIdSystem" + "zeotapIdPlusIdSystem", + "haloIdSystem" ], "adpod": [ "freeWheelAdserverVideo", @@ -21,6 +22,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "audigentRtdProvider", "jwplayerRtdProvider" ] } diff --git a/modules/haloIdSystem.js b/modules/haloIdSystem.js new file mode 100644 index 00000000000..237b502f6a7 --- /dev/null +++ b/modules/haloIdSystem.js @@ -0,0 +1,63 @@ +/** + * This module adds HaloID to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/haloIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js'; +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; + +const MODULE_NAME = 'haloId'; + +/** @type {Submodule} */ +export const haloIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{value:string}} value + * @returns {{haloId:Object}} + */ + decode(value) { + return (value && typeof value['haloId'] === 'string') ? { 'haloId': value['haloId'] } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @returns {IdResponse|undefined} + */ + getId(configParams) { + const url = `https://id.halo.ad.gt/api/v1/pbhid`; + + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, + error: error => { + utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, {method: 'GET'}); + }; + return {callback: resp}; + } +}; + +submodule('userId', haloIdSubmodule); diff --git a/modules/haloIdSystem.md b/modules/haloIdSystem.md new file mode 100644 index 00000000000..0be0be27f5d --- /dev/null +++ b/modules/haloIdSystem.md @@ -0,0 +1,32 @@ +## Audigent Halo User ID Submodule + +Audigent Halo ID Module. For assistance setting up your module please contact us at [prebid@audigent.com](prebid@audigent.com). + +### Prebid Params + +Individual params may be set for the Audigent Halo ID Submodule. At least one identifier must be set in the params. + +``` +pbjs.setConfig({ + usersync: { + userIds: [{ + name: 'haloId', + storage: { + name: 'haloId', + type: 'html5' + } + }] + } +}); +``` +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the HaloID User ID Module integration. + +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the HaloID module - `"haloId"` | `"haloId"` | +| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | | +| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` | +| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"haloid"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. | `365` | +| value | Optional | Object | Used only if the page has a separate mechanism for storing the Halo 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 | `{"haloId": "eb33b0cb-8d35-4722-b9c0-1a31d4064888"}` | diff --git a/modules/userId/eids.js b/modules/userId/eids.js index e6c3dbd5bd8..65907370ad6 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -111,6 +111,7 @@ const USER_IDS_CONFIG = { source: 'netid.de', atype: 1 }, + // sharedid 'sharedid': { source: 'sharedid.org', @@ -124,10 +125,17 @@ const USER_IDS_CONFIG = { } : undefined; } }, + // zeotapIdPlus 'IDP': { source: 'zeotap.com', atype: 1 + }, + + // haloId + 'haloId': { + source: 'audigent.com', + atype: 1 } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index fc46fef7b97..e5dca014172 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -103,5 +103,12 @@ userIdAsEids = [ atype: 1 }] }, + { + source: 'audigent.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + } ] ``` diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index a0bc0a84ee3..fdb5fc8005d 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -192,6 +192,7 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('zeotapIdPlus', function() { const userId = { IDP: 'some-random-id-value' @@ -206,6 +207,21 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + + it('haloId', function() { + const userId = { + haloId: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'audigent.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }); + }); }); describe('Negative case', function() { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 167187a281f..28a2286abc6 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -28,6 +28,7 @@ import {netIdSubmodule} from 'modules/netIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; +import {haloIdSubmodule} from 'modules/haloIdSystem.js'; import {server} from 'test/mocks/xhr.js'; let assert = require('chai').assert; @@ -36,7 +37,7 @@ const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const CONSENT_LOCAL_STORAGE_NAME = '_pbjs_userid_consent_data'; describe('User ID', function() { - function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8, configArr9) { + function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8, configArr9, configArr10) { return { userSync: { syncDelay: 0, @@ -49,7 +50,8 @@ describe('User ID', function() { (configArr6 && configArr6.length >= 3) ? getStorageMock.apply(null, configArr6) : null, (configArr7 && configArr7.length >= 3) ? getStorageMock.apply(null, configArr7) : null, (configArr8 && configArr8.length >= 3) ? getStorageMock.apply(null, configArr8) : null, - (configArr9 && configArr9.length >= 3) ? getStorageMock.apply(null, configArr9) : null + (configArr9 && configArr9.length >= 3) ? getStorageMock.apply(null, configArr9) : null, + (configArr10 && configArr10.length >= 3) ? getStorageMock.apply(null, configArr10) : null ].filter(i => i) } } @@ -405,8 +407,8 @@ describe('User ID', function() { expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 10 configurations should result in 10 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + it('config with 10 configurations should result in 11 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -437,16 +439,19 @@ describe('User ID', function() { }, { name: 'intentIqId', storage: { name: 'intentIqId', type: 'cookie' } + }, { + name: 'haloId', + storage: { name: 'haloId', type: 'cookie' } }, { name: 'zeotapIdPlus' }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 10 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 11 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -461,7 +466,7 @@ describe('User ID', function() { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -476,7 +481,7 @@ describe('User ID', function() { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -1121,6 +1126,32 @@ describe('User ID', function() { }, {adUnits}); }); + it('test hook from haloId html5', function(done) { + // simulate existing browser local storage values + localStorage.setItem('haloId', JSON.stringify({'haloId': 'random-ls-identifier'})); + localStorage.setItem('haloId_exp', ''); + + setSubmoduleRegistry([haloIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['haloId', 'haloId', 'html5'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('random-ls-identifier'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'audigent.com', + uids: [{id: 'random-ls-identifier', atype: 1}] + }); + }); + }); + localStorage.removeItem('haloId'); + localStorage.removeItem('haloId_exp', ''); + done(); + }, {adUnits}); + }); + it('test hook from merkleId cookies', function(done) { // simulate existing browser local storage values coreStorage.setCookie('merkleId', JSON.stringify({'ppid': {'id': 'testmerkleId'}}), (new Date(Date.now() + 5000).toUTCString())); @@ -1169,7 +1200,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId and netId have data to pass', function(done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have data to pass', function(done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1179,8 +1210,9 @@ describe('User ID', function() { coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1190,7 +1222,8 @@ describe('User ID', function() { ['netId', 'netId', 'cookie'], ['sharedId', 'sharedid', 'cookie'], ['intentIqId', 'intentIqId', 'cookie'], - ['zeotapIdPlus', 'IDP', 'cookie'])); + ['zeotapIdPlus', 'IDP', 'cookie'], + ['haloId', 'haloId', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -1224,7 +1257,10 @@ describe('User ID', function() { // also check that zeotapIdPlus id data was copied to bid expect(bid).to.have.deep.nested.property('userId.IDP'); expect(bid.userId.IDP).to.equal('zeotapId'); - expect(bid.userIdAsEids.length).to.equal(9); + // also check that haloId id was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userIdAsEids.length).to.equal(10); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1236,11 +1272,12 @@ describe('User ID', function() { coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId and netId have their modules added before and after init', function(done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have their modules added before and after init', function(done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1250,6 +1287,7 @@ describe('User ID', function() { coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1267,6 +1305,7 @@ describe('User ID', function() { attachIdSystem(sharedIdSubmodule); attachIdSystem(intentIqIdSubmodule); attachIdSystem(zeotapIdPlusSubmodule); + attachIdSystem(haloIdSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1276,7 +1315,8 @@ describe('User ID', function() { ['netId', 'netId', 'cookie'], ['sharedId', 'sharedid', 'cookie'], ['intentIqId', 'intentIqId', 'cookie'], - ['zeotapIdPlus', 'IDP', 'cookie'])); + ['zeotapIdPlus', 'IDP', 'cookie'], + ['haloId', 'haloId', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -1307,10 +1347,14 @@ describe('User ID', function() { // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); + // also check that zeotapIdPlus id data was copied to bid expect(bid).to.have.deep.nested.property('userId.IDP'); expect(bid.userId.IDP).to.equal('zeotapId'); - expect(bid.userIdAsEids.length).to.equal(9); + // also check that haloId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userIdAsEids.length).to.equal(10); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1322,6 +1366,7 @@ describe('User ID', function() { coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -1357,9 +1402,10 @@ describe('User ID', function() { coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); init(config); config.setConfig({ @@ -1383,6 +1429,8 @@ describe('User ID', function() { name: 'intentIqId', storage: { name: 'intentIqId', type: 'cookie' } }, { name: 'zeotapIdPlus' + }, { + name: 'haloId', storage: { name: 'haloId', type: 'cookie' } }, { name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} }] @@ -1439,7 +1487,10 @@ describe('User ID', function() { // also check that zeotapIdPlus id data was copied to bid expect(bid).to.have.deep.nested.property('userId.IDP'); expect(bid.userId.IDP).to.equal('zeotapId'); - expect(bid.userIdAsEids.length).to.equal(9); + // also check that haloId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userIdAsEids.length).to.equal(10); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1451,6 +1502,7 @@ describe('User ID', function() { coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); From 7da0521e53c087fadd9982d02a757be89ccbbb89 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Tue, 15 Sep 2020 17:21:19 +0300 Subject: [PATCH 0186/1476] Fix typo in TheMediaGrid Bid Adapter (#5589) * 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 --- modules/gridBidAdapter.js | 6 ++--- test/spec/modules/gridBidAdapter_spec.js | 28 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index b4b741ac783..32274fed2e6 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -351,10 +351,10 @@ function buildNewRequest(validBidRequests, bidderRequest) { } if (realTimeData && realTimeData.jwTargeting) { if (!jwpseg && realTimeData.jwTargeting.segments) { - jwpseg = realTimeData.segments; + jwpseg = realTimeData.jwTargeting.segments; } - if (!content && realTimeData.content) { - content = realTimeData.content; + if (!content && realTimeData.jwTargeting.content) { + content = realTimeData.jwTargeting.content; } } let impObj = { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 344f1764c05..650712e435f 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -578,6 +578,34 @@ describe('TheMediaGrid Adapter', function () { expect(payload.source.ext).to.have.property('schain'); expect(payload.source.ext.schain).to.deep.equal(schain); }); + + it('if content and segment is present in realTimeData.jwTargeting, payload must have right params', function () { + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + realTimeData: { + jwTargeting: { + segments: jsSegments, + content: jsContent + } + } + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.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); + }); }); describe('interpretResponse', function () { From 183ff52bd62f53bda4b5f6929bcf4be94207a026 Mon Sep 17 00:00:00 2001 From: Hugo Duthil Date: Tue, 15 Sep 2020 16:34:37 +0200 Subject: [PATCH 0187/1476] Check localstorage availability before accessing it (#5616) * Check localstorage availability before accessint it * Fix UTest for IE11 * Restore window.localStorage property descriptor after each test Co-authored-by: Hugo Duthil --- src/storageManager.js | 6 ++--- test/spec/unit/core/storageManager_spec.js | 27 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/storageManager.js b/src/storageManager.js index 0d88a8ccea1..60e5a7706d0 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -154,7 +154,7 @@ export function newStorageManager({gvlid, moduleName, moduleType} = {}) { */ const setDataInLocalStorage = function (key, value, done) { let cb = function (result) { - if (result && result.valid) { + if (result && result.valid && hasLocalStorage()) { window.localStorage.setItem(key, value); } } @@ -174,7 +174,7 @@ export function newStorageManager({gvlid, moduleName, moduleType} = {}) { */ const getDataFromLocalStorage = function (key, done) { let cb = function (result) { - if (result && result.valid) { + if (result && result.valid && hasLocalStorage()) { return window.localStorage.getItem(key); } return null; @@ -194,7 +194,7 @@ export function newStorageManager({gvlid, moduleName, moduleType} = {}) { */ const removeDataFromLocalStorage = function (key, done) { let cb = function (result) { - if (result && result.valid) { + if (result && result.valid && hasLocalStorage()) { window.localStorage.removeItem(key); } } diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index 0b406242f90..de09df5b196 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -42,5 +42,32 @@ describe('storage manager', function() { storage.setCookie('foo1', 'baz1'); expect(deviceAccessSpy.calledOnce).to.equal(true); deviceAccessSpy.restore(); + }); + + describe('localstorage forbidden access in 3rd-party context', function() { + let errorLogSpy; + const originalLocalStorage = { get: () => window.localStorage }; + const localStorageMock = { get: () => { throw Error } }; + + beforeEach(function() { + Object.defineProperty(window, 'localStorage', localStorageMock); + errorLogSpy = sinon.spy(utils, 'logError'); + }); + + afterEach(function() { + Object.defineProperty(window, 'localStorage', originalLocalStorage); + errorLogSpy.restore(); + }) + + it('should not throw if the localstorage is not accessible when setting/getting/removing from localstorage', function() { + const coreStorage = getStorageManager(); + + coreStorage.setDataInLocalStorage('key', 'value'); + const val = coreStorage.getDataFromLocalStorage('key'); + coreStorage.removeDataFromLocalStorage('key'); + + expect(val).to.be.null; + sinon.assert.calledThrice(errorLogSpy); + }) }) }); From f61be0c11dff0de23c2cdc5527940eb9cda9e187 Mon Sep 17 00:00:00 2001 From: Mirko Feddern <3244291+mirkorean@users.noreply.github.com> Date: Tue, 15 Sep 2020 18:33:20 +0200 Subject: [PATCH 0188/1476] Add Supply Supply Chain Object Module support for Yieldlab Adapter (#5521) * Add Supply Chain Support for Yieldlab Adapter * Add minor changes based on PR comments * Make createSchainString method leaner (additional input from PR review) --- modules/yieldlabBidAdapter.js | 34 +++++++++++++++++++- test/spec/modules/yieldlabBidAdapter_spec.js | 23 ++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 1d1636bda69..b252c0db2ee 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -47,6 +47,9 @@ export const spec = { query[prop] = bid.params.customParams[prop] } } + if (bid.schain && utils.isPlainObject(bid.schain) && Array.isArray(bid.schain.nodes)) { + query.schain = createSchainString(bid.schain) + } }) if (bidderRequest) { @@ -202,7 +205,12 @@ function createQueryString (obj) { let str = [] for (var p in obj) { if (obj.hasOwnProperty(p)) { - str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])) + let val = obj[p] + if (p !== 'schain') { + str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val)) + } else { + str.push(p + '=' + val) + } } } return str.join('&') @@ -225,6 +233,30 @@ function createTargetingString (obj) { return str.join('&') } +/** + * Creates a string out of a schain object + * @param {Object} schain + * @returns {String} + */ +function createSchainString (schain) { + const ver = schain.ver || '' + const complete = 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}` +} + +/** + * Encodes URI Component with exlamation mark included. Needed for schain object. + * @param {String} str + * @returns {String} + */ +function encodeURIComponentWithBangIncluded(str) { + return encodeURIComponent(str).replace(/!/g, '%21') +} + /** * Handles an outstream response after the library is loaded * @param {Object} bid diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index e7a9285cb48..90fa26fa823 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -30,7 +30,24 @@ const REQUEST = { 'id': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', 'atype': 1 }] - }] + }], + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '1', + 'hp': 1 + }, + { + 'asi': 'indirectseller2.com', + 'name': 'indirectseller2 name with comma , and bang !', + 'sid': '2', + 'hp': 1 + } + ] + } } const RESPONSE = { @@ -107,6 +124,10 @@ describe('yieldlabBidAdapter', function () { expect(request.url).to.include('extraParam=true&foo=bar') }) + it('passes unencoded schain string to bid request', function () { + 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,,') + }) + const refererRequest = spec.buildRequests(bidRequests, { refererInfo: { canonicalUrl: undefined, From cb733da7ff1aa25938266408bcc55a46ba3b4cfd Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Tue, 15 Sep 2020 19:45:21 +0200 Subject: [PATCH 0189/1476] Livewrapped support for video (#5724) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter * Collect info on ad units receiving any valid bid * Support for ID5 Pass metadata from adapter * Typo in test + eids on wrong level * Fix for Prebid 3.0 * Fix get referer * http -> https in tests * Native support * Read sizes from mediatype.banner * Revert accidental commit * Support native data collection + minor refactorings * Set analytics endpoint * Support for app parameters * Fix issue where adunits with bids were not counted on reload * Send debug info from adapter to external debugger * SChain support * Send GDPR data in analytics request * video support Video support --- modules/livewrappedAnalyticsAdapter.js | 2 +- modules/livewrappedBidAdapter.js | 17 ++- modules/livewrappedBidAdapter.md | 2 +- .../modules/livewrappedBidAdapter_spec.js | 113 ++++++++++++++---- 4 files changed, 107 insertions(+), 27 deletions(-) diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 4b1c162c67c..9f571cb5ae0 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -62,7 +62,7 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE bidResponse.cpm = args.cpm; bidResponse.ttr = args.timeToRespond; bidResponse.readyToSend = 1; - bidResponse.mediaType = args.mediaType == 'native' ? 2 : 1; + bidResponse.mediaType = args.mediaType == 'native' ? 2 : (args.mediaType == 'video' ? 4 : 1); if (!bidResponse.ttr) { bidResponse.ttr = time - bidResponse.start; } diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 78b29ab5016..50e9eea768b 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -2,18 +2,18 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; export const storage = getStorageManager(); const BIDDER_CODE = 'livewrapped'; export const URL = 'https://lwadm.com/ad'; -const VERSION = '1.3'; +const VERSION = '1.4'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -129,7 +129,12 @@ export const spec = { if (ad.native) { bidResponse.native = ad.native; - bidResponse.mediaType = NATIVE + bidResponse.mediaType = NATIVE; + } + + if (ad.video) { + bidResponse.mediaType = VIDEO; + bidResponse.vastXml = ad.tag; } bidResponses.push(bidResponse); @@ -218,7 +223,9 @@ function bidToAdRequest(bid) { adRequest.native = utils.deepAccess(bid, 'mediaTypes.native'); - if (adRequest.native && utils.deepAccess(bid, 'mediaTypes.banner')) { + adRequest.video = utils.deepAccess(bid, 'mediaTypes.video'); + + if ((adRequest.native || adRequest.video) && utils.deepAccess(bid, 'mediaTypes.banner')) { adRequest.banner = true; } diff --git a/modules/livewrappedBidAdapter.md b/modules/livewrappedBidAdapter.md index 15e3e8d533a..c5d867af8fe 100644 --- a/modules/livewrappedBidAdapter.md +++ b/modules/livewrappedBidAdapter.md @@ -8,7 +8,7 @@ Connects to Livewrapped Header Bidding wrapper for bids. -Livewrapped supports banner. +Livewrapped supports banner, native and video. # Test Parameters diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index fb676f75343..053e5102cbf 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -2,7 +2,7 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/livewrappedBidAdapter.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; -import { BANNER, NATIVE } from 'src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; describe('Livewrapped adapter tests', function () { let sandbox, @@ -102,7 +102,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -139,7 +139,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -177,7 +177,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -208,7 +208,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -238,7 +238,7 @@ describe('Livewrapped adapter tests', function () { let expectedQuery = { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -270,7 +270,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, deviceId: 'deviceid', @@ -303,7 +303,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, tid: 'tracking id', @@ -335,7 +335,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -366,7 +366,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -397,7 +397,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -428,7 +428,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -445,6 +445,37 @@ describe('Livewrapped adapter tests', function () { expect(data).to.deep.equal(expectedQuery); }); + it('should make a well-formed single request object with video only parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + testbidRequest.bids[0].mediaTypes = {'video': {'videodata': 'content parsed serverside only'}}; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'https://www.domain.com', + version: '1.4', + width: 100, + height: 100, + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}], + video: {'videodata': 'content parsed serverside only'} + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + it('should use app objects', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); @@ -474,7 +505,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://appdomain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 300, height: 200, ifa: 'ifa', @@ -507,7 +538,7 @@ describe('Livewrapped adapter tests', function () { auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', publisherId: '26947112-2289-405D-88C1-A7340C57E63E', url: 'https://www.domain.com', - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -541,7 +572,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -577,7 +608,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -608,7 +639,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: false, @@ -638,7 +669,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: false, @@ -701,7 +732,7 @@ describe('Livewrapped adapter tests', function () { userId: 'pubcid 123', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -733,7 +764,7 @@ describe('Livewrapped adapter tests', function () { userId: 'user id', url: 'https://www.domain.com', seats: {'dsp': ['seat 1']}, - version: '1.3', + version: '1.4', width: 100, height: 100, cookieSupport: true, @@ -895,6 +926,48 @@ describe('Livewrapped adapter tests', function () { expect(bids).to.deep.equal(expectedResponse); }) + it('should handle single video success response', function() { + let lwResponse = { + ads: [ + { + id: '28e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_0', + tag: 'VAST XML', + video: {}, + width: 300, + height: 250, + cpmBid: 2.565917, + bidId: '32e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120, + meta: undefined + } + ], + currency: 'USD' + }; + + let expectedResponse = [{ + requestId: '32e50fad901ae89', + bidderCode: 'livewrapped', + cpm: 2.565917, + width: 300, + height: 250, + ad: 'VAST XML', + ttl: 120, + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + netRevenue: true, + currency: 'USD', + meta: undefined, + vastXml: 'VAST XML', + mediaType: VIDEO + }]; + + let bids = spec.interpretResponse({body: lwResponse}); + + expect(bids).to.deep.equal(expectedResponse); + }) + it('should handle multiple success response', function() { let lwResponse = { ads: [ From 04ea60324e3750c442fb98ed2f0d665b3c2969e9 Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Wed, 16 Sep 2020 11:47:38 +0300 Subject: [PATCH 0190/1476] Intentiq id value change (#5746) * Intent iq id to use plain id string * getId from http resp as a string instead of as a json object * getId from http resp as a string instead of as a json object --- modules/intentIqIdSystem.js | 128 +++++++++++++++---------------- test/spec/modules/userId_spec.js | 8 +- 2 files changed, 64 insertions(+), 72 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 7d497ea9b1a..242b227f89f 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -1,68 +1,60 @@ -/** - * This module adds IntentIqId to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/intentIqIdSystem - * @requires module:modules/userId - */ - -import * as utils from '../src/utils.js' -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js' - -const MODULE_NAME = 'intentIqId'; - -/** @type {Submodule} */ -export const intentIqIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - /** - * decode the stored id value for passing to bid requests - * @function - * @param {{ctrid:string}} value - * @returns {{intentIqId:string}} - */ - decode(value) { - return (value && typeof value['ctrid'] === 'string') ? { 'intentIqId': value['ctrid'] } : undefined; - }, - /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleParams} [configParams] - * @returns {IdResponse|undefined} - */ - getId(configParams) { - if (!configParams || typeof configParams.partner !== 'number') { - utils.logError('User ID - intentIqId submodule requires a valid partner to be defined'); - return; - } - - // use protocol relative urls for http or https - const url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - utils.logError(error); - } - } - callback(responseObj); - }, - error: error => { - utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); - callback(); - } - }; - ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); - }; - return {callback: resp}; - } -}; - -submodule('userId', intentIqIdSubmodule); +/** + * This module adds IntentIqId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/intentIqIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js' + +const MODULE_NAME = 'intentIqId'; + +/** @type {Submodule} */ +export const intentIqIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{string}} value + * @returns {{intentIqId:string}} + */ + decode(value) { + return (value && value != '') ? { 'intentIqId': value } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @returns {IdResponse|undefined} + */ + getId(configParams) { + if (!configParams || typeof configParams.partner !== 'number') { + utils.logError('User ID - intentIqId submodule requires a valid partner to be defined'); + return; + } + + // use protocol relative urls for http or https + const url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; + const resp = function (callback) { + const callbacks = { + success: response => { + callback(response); + }, + error: error => { + utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); + }; + return {callback: resp}; + } +}; + +submodule('userId', intentIqIdSubmodule); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 28a2286abc6..e9057ee26d9 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1104,7 +1104,7 @@ describe('User ID', function() { it('test hook from intentIqId cookies', function(done) { // simulate existing browser local storage values - coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'abcdefghijk'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'abcdefghijk', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([intentIqIdSubmodule]); init(config); @@ -1207,7 +1207,7 @@ describe('User ID', function() { coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1285,7 +1285,7 @@ describe('User ID', function() { coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1400,7 +1400,7 @@ describe('User ID', function() { coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('intentIqId', JSON.stringify({'ctrid': 'testintentIqId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); From 298139f0ad4c81f85164a8e97f7b2c0b4a993ef4 Mon Sep 17 00:00:00 2001 From: Mehmet Can Kurt Date: Wed, 16 Sep 2020 05:15:38 -0700 Subject: [PATCH 0191/1476] add quantcast ID submodule (#5727) * add quantcast ID submodule * use setCookie in test * add comment for consent signals --- integrationExamples/gpt/userId_example.html | 16 +++++--- modules/.submodules.json | 3 +- modules/quantcastIdSystem.js | 44 +++++++++++++++++++++ modules/userId/eids.js | 6 +++ modules/userId/eids.md | 7 ++++ test/spec/modules/eids_spec.js | 15 +++++++ test/spec/modules/quantcastIdSystem_spec.js | 19 +++++++++ 7 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 modules/quantcastIdSystem.js create mode 100644 test/spec/modules/quantcastIdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 521eb8bc9fa..8e69bc6c6a7 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -83,9 +83,10 @@ var adUnits = [ { code: 'test-div', - sizes: [[300,250],[300,600],[728,90]], mediaTypes: { - banner: {} + banner: { + sizes: [[300,250],[300,600],[728,90]] + } }, bids: [ { @@ -216,10 +217,10 @@ name: "sharedid", expires: 28 } - }, + }, { name: 'lotamePanoramaId' - }, + }, { name: "liveIntentId", params: { @@ -230,7 +231,7 @@ name: "_li_pbid", expires: 28 } - }, + }, { name: "zeotapIdPlus" }, @@ -241,7 +242,10 @@ name: "haloId", expires: 28 } - } + }, + { + name: "quantcastId" + } ], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/.submodules.json b/modules/.submodules.json index 575c3294bf1..18e75dd1794 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -14,7 +14,8 @@ "sharedIdSystem", "intentIqIdSystem", "zeotapIdPlusIdSystem", - "haloIdSystem" + "haloIdSystem", + "quantcastIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js new file mode 100644 index 00000000000..e86c130dc5b --- /dev/null +++ b/modules/quantcastIdSystem.js @@ -0,0 +1,44 @@ +/** + * This module adds QuantcastID to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/quantcastIdSystem + * @requires module:modules/userId + */ + +import {submodule} from '../src/hook.js' +import { getStorageManager } from '../src/storageManager.js'; + +const QUANTCAST_FPA = '__qca'; + +export const storage = getStorageManager(); + +/** @type {Submodule} */ +export const quantcastIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'quantcastId', + + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{quantcastId: string} | undefined} + */ + decode(value) { + return value; + }, + + /** + * read Quantcast first party cookie and pass it along in quantcastId + * @function + * @returns {{id: {quantcastId: string} | undefined}}} + */ + getId() { + // Consent signals are currently checked on the server side. + let fpa = storage.getCookie(QUANTCAST_FPA); + return { id: fpa ? { quantcastId: fpa } : undefined } + } +}; + +submodule('userId', quantcastIdSubmodule); diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 65907370ad6..eebd0146d50 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -136,6 +136,12 @@ const USER_IDS_CONFIG = { 'haloId': { source: 'audigent.com', atype: 1 + }, + + // quantcastId + 'quantcastId': { + source: 'quantcast.com', + atype: 1 } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index e5dca014172..03aec46cf48 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -109,6 +109,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 1 }] + }, + { + source: 'quantcast.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] } ] ``` diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index fdb5fc8005d..a6a44f9296d 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -222,6 +222,21 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + + it('quantcastId', function() { + const userId = { + quantcastId: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'quantcast.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }); + }); }); describe('Negative case', function() { diff --git a/test/spec/modules/quantcastIdSystem_spec.js b/test/spec/modules/quantcastIdSystem_spec.js new file mode 100644 index 00000000000..12c8689fd3f --- /dev/null +++ b/test/spec/modules/quantcastIdSystem_spec.js @@ -0,0 +1,19 @@ +import { quantcastIdSubmodule, storage } from 'modules/quantcastIdSystem.js'; + +describe('QuantcastId module', function () { + beforeEach(function() { + storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); + }); + + it('getId() should return a quantcast id when the Quantcast first party cookie exists', function () { + storage.setCookie('__qca', 'P0-TestFPA'); + + const id = quantcastIdSubmodule.getId(); + expect(id).to.be.deep.equal({id: {quantcastId: 'P0-TestFPA'}}); + }); + + it('getId() should return an empty id when the Quantcast first party cookie is missing', function () { + const id = quantcastIdSubmodule.getId(); + expect(id).to.be.deep.equal({id: undefined}); + }); +}); From bce32dcd59f6130fcffaa7d603e66f04a953ac0b Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 16 Sep 2020 19:01:26 +0530 Subject: [PATCH 0192/1476] upgrade ci resource (#5725) --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ea5e87218c..ea5fb916a91 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ aliases: docker: # specify the version you desire here - image: circleci/node:12.16.1 - + resource_class: xlarge # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ @@ -94,4 +94,4 @@ workflows: - e2etest experimental: - pipelines: true \ No newline at end of file + pipelines: true From 140a67cc8dc3d6a8edfec7566bb1ba4f714074db Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 16 Sep 2020 08:03:52 -0700 Subject: [PATCH 0193/1476] Rubicon analytics v2 (#5698) * temp check in * add * dev almost done * add rule name to payload * update rubi analytics tests * add functionality to mock gpt * revert analyticsAdapter update * minor cleanup * handle edge cases more gracefully * logic for when cookies not enabled new param `channel` tests * use timeToRespond if available on bidResponse * Updating with review comments --- modules/rubiconAnalyticsAdapter.js | 153 ++++++- test/spec/integration/faker/googletag.js | 64 ++- .../modules/rubiconAnalyticsAdapter_spec.js | 392 +++++++++++++++++- test/spec/modules/rubiconAnalyticsSchema.json | 78 +++- 4 files changed, 651 insertions(+), 36 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 00fcd6ba8ff..54aa108ed78 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -5,7 +5,15 @@ import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; +import { getStorageManager } from '../src/storageManager.js'; +const RUBICON_GVL_ID = 52; +export const storage = getStorageManager(RUBICON_GVL_ID, 'rubicon'); +const COOKIE_NAME = 'rpaSession'; +const LAST_SEEN_EXPIRE_TIME = 1800000; // 30 mins +const END_EXPIRE_TIME = 21600000; // 6 hours + +let prebidGlobal = getGlobal(); const { EVENTS: { AUCTION_INIT, @@ -38,8 +46,11 @@ const cache = { auctions: {}, targeting: {}, timeouts: {}, + gpt: {}, }; +const BID_REJECTED_IPF = 'rejected-ipf'; + export function getHostNameFromReferer(referer) { try { rubiconAdapter.referrerHostname = utils.parseUrl(referer, {noDecodeWholeURL: true}).hostname; @@ -126,13 +137,15 @@ function sendMessage(auctionId, bidWonId) { }); } let auctionCache = cache.auctions[auctionId]; - let referrer = config.getConfig('pageUrl') || auctionCache.referrer; + let referrer = config.getConfig('pageUrl') || (auctionCache && auctionCache.referrer); let message = { eventTimeMillis: Date.now(), integration: config.getConfig('rubicon.int_type') || DEFAULT_INTEGRATION, + ruleId: config.getConfig('rubicon.rule_name'), version: '$prebid.version$', referrerUri: referrer, - referrerHostname: rubiconAdapter.referrerHostname || getHostNameFromReferer(referrer) + referrerHostname: rubiconAdapter.referrerHostname || getHostNameFromReferer(referrer), + channel: 'web' }; const wrapperName = config.getConfig('rubicon.wrapperName'); if (wrapperName) { @@ -149,7 +162,8 @@ function sendMessage(auctionId, bidWonId) { 'mediaTypes', 'dimensions', 'adserverTargeting', () => stringProperties(cache.targeting[bid.adUnit.adUnitCode] || {}), - 'adSlot' + 'gam', + 'pbAdSlot' ]); adUnit.bids = []; adUnit.status = 'no-bid'; // default it to be no bid @@ -215,6 +229,30 @@ function sendMessage(auctionId, bidWonId) { } } + // gather gdpr info + if (auctionCache.gdprConsent) { + auction.gdpr = utils.pick(auctionCache.gdprConsent, [ + 'gdprApplies as applies', + 'consentString', + 'apiVersion as version' + ]); + } + + // gather session info + if (auctionCache.session) { + message.session = utils.pick(auctionCache.session, [ + 'id', + 'pvid', + 'start', + 'expires' + ]); + if (!utils.isEmpty(auctionCache.session.fpkvs)) { + message.fpkvs = Object.keys(auctionCache.session.fpkvs).map(key => { + return { key, value: auctionCache.session.fpkvs[key] }; + }); + } + } + if (serverConfig) { auction.serverTimeoutMillis = serverConfig.timeout; } @@ -272,7 +310,7 @@ function getBidPrice(bid) { } // otherwise we convert and return try { - return Number(getGlobal().convertCurrency(cpm, currency, 'USD')); + return Number(prebidGlobal.convertCurrency(cpm, currency, 'USD')); } catch (err) { utils.logWarn('Rubicon Analytics Adapter: Could not determine the bidPriceUSD of the bid ', bid); } @@ -300,6 +338,19 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { ]); } +function getDynamicKvps() { + if (prebidGlobal.rp && typeof prebidGlobal.rp.getCustomTargeting === 'function') { + return prebidGlobal.rp.getCustomTargeting(); + } + return {}; +} + +function getPageViewId() { + if (prebidGlobal.rp && typeof prebidGlobal.rp.generatePageViewId === 'function') { + return prebidGlobal.rp.generatePageViewId(false); + } +} + let samplingFactor = 1; let accountId; // List of known rubicon aliases @@ -318,6 +369,74 @@ function setRubiconAliases(aliasRegistry) { }); } +function getRpaCookie() { + let encodedCookie = storage.getDataFromLocalStorage(COOKIE_NAME); + if (encodedCookie) { + try { + return JSON.parse(window.atob(encodedCookie)); + } catch (e) { + utils.logError(`Rubicon Analytics: Unable to decode ${COOKIE_NAME} value: `, e); + } + } + return {}; +} + +function setRpaCookie(decodedCookie) { + try { + storage.setDataInLocalStorage(COOKIE_NAME, window.btoa(JSON.stringify(decodedCookie))); + } catch (e) { + utils.logError(`Rubicon Analytics: Unable to encode ${COOKIE_NAME} value: `, e); + } +} + +function updateRpaCookie() { + const currentTime = Date.now(); + let decodedRpaCookie = getRpaCookie(); + if ( + !Object.keys(decodedRpaCookie).length || + (currentTime - decodedRpaCookie.lastSeen) > LAST_SEEN_EXPIRE_TIME || + decodedRpaCookie.expires < currentTime + ) { + decodedRpaCookie = { + id: utils.generateUUID(), + start: currentTime, + expires: currentTime + END_EXPIRE_TIME, // six hours later, + } + } + // possible that decodedRpaCookie is undefined, and if it is, we probably are blocked by storage or some other exception + if (Object.keys(decodedRpaCookie).length) { + decodedRpaCookie.lastSeen = currentTime; + decodedRpaCookie.fpkvs = {...decodedRpaCookie.fpkvs, ...getDynamicKvps()}; + decodedRpaCookie.pvid = getPageViewId(); + setRpaCookie(decodedRpaCookie) + } + return decodedRpaCookie; +} + +function subscribeToGamSlots() { + window.googletag.pubads().addEventListener('slotRenderEnded', event => { + const isMatchingAdSlot = utils.isAdUnitCodeMatchingSlot(event.slot); + // loop through auctions and adUnits and mark the info + Object.keys(cache.auctions).forEach(auctionId => { + (Object.keys(cache.auctions[auctionId].bids) || []).forEach(bidId => { + let bid = cache.auctions[auctionId].bids[bidId]; + // if this slot matches this bids adUnit, add the adUnit info + if (isMatchingAdSlot(bid.adUnit.adUnitCode)) { + bid.adUnit.gam = utils.pick(event, [ + // these come in as `null` from Gpt, which when stringified does not get removed + // so set explicitly to undefined when not a number + 'advertiserId', advertiserId => utils.isNumber(advertiserId) ? advertiserId : undefined, + 'creativeId', creativeId => utils.isNumber(creativeId) ? creativeId : undefined, + 'lineItemId', lineItemId => utils.isNumber(lineItemId) ? lineItemId : undefined, + 'adSlot', () => event.slot.getAdUnitPath(), + 'isSlotEmpty', () => event.isEmpty || undefined + ]); + } + }); + }); + }); +} + let baseAdapter = adapter({analyticsType: 'endpoint'}); let rubiconAdapter = Object.assign({}, baseAdapter, { referrerHostname: '', @@ -363,6 +482,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { disableAnalytics() { this.getUrl = baseAdapter.getUrl; accountId = null; + cache.gpt.registered = false; baseAdapter.disableAnalytics.apply(this, arguments); }, track({eventType, args}) { @@ -376,12 +496,19 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { ]); cacheEntry.bids = {}; cacheEntry.bidsWon = {}; - cacheEntry.referrer = args.bidderRequests[0].refererInfo.referer; + cacheEntry.referrer = utils.deepAccess(args, 'bidderRequests.0.refererInfo.referer'); const floorData = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData'); if (floorData) { cacheEntry.floorData = {...floorData}; } + cacheEntry.gdprConsent = utils.deepAccess(args, 'bidderRequests.0.gdprConsent'); + cacheEntry.session = storage.localStorageIsEnabled() && updateRpaCookie(); cache.auctions[args.auctionId] = cacheEntry; + // register to listen to gpt events if not done yet + if (!cache.gpt.registered && utils.isGptPubadsDefined()) { + subscribeToGamSlots(); + cache.gpt.registered = true; + } break; case BID_REQUESTED: Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => { @@ -452,6 +579,12 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { } return ['banner']; }, + 'gam', () => { + if (utils.deepAccess(bid, 'fpd.context.adServer.name') === 'gam') { + return {adSlot: bid.fpd.context.adServer.adSlot} + } + }, + 'pbAdSlot', () => utils.deepAccess(bid, 'fpd.context.pbAdSlot') ]) ]); return memo; @@ -461,8 +594,8 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { let auctionEntry = cache.auctions[args.auctionId]; let bid = auctionEntry.bids[args.requestId]; // If floor resolved gptSlot but we have not yet, then update the adUnit to have the adSlot name - if (!utils.deepAccess(bid, 'adUnit.adSlot') && utils.deepAccess(args, 'floorData.matchedFields.gptSlot')) { - bid.adUnit.adSlot = args.floorData.matchedFields.gptSlot; + if (!utils.deepAccess(bid, 'adUnit.gam.adSlot') && utils.deepAccess(args, 'floorData.matchedFields.gptSlot')) { + utils.deepSetValue(bid, 'adUnit.gam.adSlot', args.floorData.matchedFields.gptSlot); } // if we have not set enforcements yet set it if (!utils.deepAccess(auctionEntry, 'floorData.enforcements') && utils.deepAccess(args, 'floorData.enforcements')) { @@ -479,7 +612,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { delete bid.error; // it's possible for this to be set by a previous timeout break; case NO_BID: - bid.status = args.status === BID_REJECTED ? 'rejected' : 'no-bid'; + bid.status = args.status === BID_REJECTED ? BID_REJECTED_IPF : 'no-bid'; delete bid.error; break; default: @@ -488,7 +621,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { code: 'request-error' }; } - bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp; + bid.clientLatencyMillis = bid.timeToRespond || Date.now() - cache.auctions[args.auctionId].timestamp; bid.bidResponse = parseBidResponse(args, bid.bidResponse); break; case BIDDER_DONE: @@ -549,7 +682,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { adapterManager.registerAnalyticsAdapter({ adapter: rubiconAdapter, code: 'rubicon', - gvlid: 52 + gvlid: RUBICON_GVL_ID }); export default rubiconAdapter; diff --git a/test/spec/integration/faker/googletag.js b/test/spec/integration/faker/googletag.js index a0ce04402f7..9d91bf315d9 100644 --- a/test/spec/integration/faker/googletag.js +++ b/test/spec/integration/faker/googletag.js @@ -43,18 +43,52 @@ export function makeSlot() { return slot; } -window.googletag = { - _slots: [], - pubads: function () { - var self = this; - return { - getSlots: function () { - return self._slots; - }, - - setSlots: function (slots) { - self._slots = slots; - } - }; - } -}; +export function emitEvent(eventName, params) { + (window.googletag._callbackMap[eventName] || []).forEach(eventCb => eventCb({...params, eventName})); +} + +export function enable() { + window.googletag = { + _slots: [], + _callbackMap: {}, + pubads: function () { + var self = this; + return { + getSlots: function () { + return self._slots; + }, + + setSlots: function (slots) { + self._slots = slots; + }, + + setTargeting: function(key, arrayOfValues) { + self._targeting[key] = Array.isArray(arrayOfValues) ? arrayOfValues : [arrayOfValues]; + }, + + getTargeting: function(key) { + return self._targeting[key] || []; + }, + + getTargetingKeys: function() { + return Object.getOwnPropertyNames(self._targeting); + }, + + clearTargeting: function() { + self._targeting = {}; + }, + + addEventListener: function (eventName, cb) { + self._callbackMap[eventName] = self._callbackMap[eventName] || []; + self._callbackMap[eventName].push(cb); + } + }; + } + }; +} + +export function disable() { + window.googletag = undefined; +} + +enable(); diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 0c2c83a4b37..a2999dfed8c 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -2,16 +2,19 @@ import rubiconAnalyticsAdapter, { SEND_TIMEOUT, parseBidResponse, getHostNameFromReferer, + storage, } from 'modules/rubiconAnalyticsAdapter.js'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; - +import * as mockGpt from '../integration/faker/googletag.js'; import { setConfig, addBidResponseHook, } from 'modules/currency.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +let prebidGlobal = getGlobal(); let Ajv = require('ajv'); let schema = require('./rubiconAnalyticsSchema.json'); let ajv = new Ajv({ @@ -272,11 +275,19 @@ const MOCK = { ] }; +const STUBBED_UUID = '12345678-1234-1234-1234-123456789abc'; + const ANALYTICS_MESSAGE = { + 'channel': 'web', 'eventTimeMillis': 1519767013781, 'integration': 'pbjs', 'version': '$prebid.version$', 'referrerUri': 'http://www.test.com/page.html', + 'session': { + 'expires': 1519788613781, + 'id': STUBBED_UUID, + 'start': 1519767013781 + }, 'referrerHostname': 'www.test.com', 'auctions': [ { @@ -466,13 +477,18 @@ const ANALYTICS_MESSAGE = { 'wrapperName': '10000_fakewrapper_test' }; -function performStandardAuction() { +function performStandardAuction(gptEvents) { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); events.emit(AUCTION_END, MOCK.AUCTION_END); + + if (gptEvents && gptEvents.length) { + gptEvents.forEach(gptEvent => mockGpt.emitEvent(gptEvent.eventName, gptEvent.params)); + } + events.emit(SET_TARGETING, MOCK.SET_TARGETING); events.emit(BID_WON, MOCK.BID_WON[0]); events.emit(BID_WON, MOCK.BID_WON[1]); @@ -481,12 +497,20 @@ function performStandardAuction() { describe('rubicon analytics adapter', function () { let sandbox; let clock; - + let getDataFromLocalStorageStub, setDataInLocalStorageStub, localStorageIsEnabledStub; beforeEach(function () { + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + setDataInLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + mockGpt.disable(); sandbox = sinon.sandbox.create(); + localStorageIsEnabledStub.returns(true); + sandbox.stub(events, 'getEvents').returns([]); + sandbox.stub(utils, 'generateUUID').returns(STUBBED_UUID); + clock = sandbox.useFakeTimers(1519767013781); rubiconAnalyticsAdapter.referrerHostname = ''; @@ -505,6 +529,10 @@ describe('rubicon analytics adapter', function () { afterEach(function () { sandbox.restore(); config.resetConfig(); + mockGpt.enable(); + getDataFromLocalStorageStub.restore(); + setDataInLocalStorageStub.restore(); + localStorageIsEnabledStub.restore(); }); it('should require accountId', function () { @@ -747,17 +775,17 @@ describe('rubicon analytics adapter', function () { provider: 'rubicon' }); // first adUnit's adSlot - expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports'); + expect(message.auctions[0].adUnits[0].gam.adSlot).to.equal('12345/sports'); // since no other bids, we set adUnit status to no-bid expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); // first adUnits bid is rejected - expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected'); + expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected-ipf'); expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.equal(4); // if bid rejected should take cpmAfterAdjustments val expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); // second adUnit's adSlot - expect(message.auctions[0].adUnits[1].adSlot).to.equal('12345/news'); + expect(message.auctions[0].adUnits[1].gam.adSlot).to.equal('12345/news'); // top level adUnit status is success expect(message.auctions[0].adUnits[1].status).to.equal('success'); // second adUnits bid is success @@ -767,7 +795,7 @@ describe('rubicon analytics adapter', function () { }); it('should still send floor info if provider is not rubicon', function () { - let message = performFloorAuction('randomProvider') + let message = performFloorAuction('randomProvider'); // verify our floor stuff is passed // top level floor info @@ -782,17 +810,17 @@ describe('rubicon analytics adapter', function () { provider: 'randomProvider' }); // first adUnit's adSlot - expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports'); + expect(message.auctions[0].adUnits[0].gam.adSlot).to.equal('12345/sports'); // since no other bids, we set adUnit status to no-bid expect(message.auctions[0].adUnits[0].status).to.equal('no-bid'); // first adUnits bid is rejected - expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected'); + expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected-ipf'); expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.equal(4); // if bid rejected should take cpmAfterAdjustments val expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1); // second adUnit's adSlot - expect(message.auctions[0].adUnits[1].adSlot).to.equal('12345/news'); + expect(message.auctions[0].adUnits[1].gam.adSlot).to.equal('12345/news'); // top level adUnit status is success expect(message.auctions[0].adUnits[1].status).to.equal('success'); // second adUnits bid is success @@ -801,6 +829,350 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); }); + describe('with session handling', function () { + let pvid, kvps; + beforeEach(function () { + // custom dm stuff + prebidGlobal.rp = { + getCustomTargeting: () => kvps, + generatePageViewId: () => pvid + } + }); + + afterEach(function () { + prebidGlobal.rp = pvid = kvps = undefined; + }); + + it('should not log any session data if local storage is not enabled', function () { + localStorageIsEnabledStub.returns(false); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + delete expectedMessage.session; + delete expectedMessage.fpkvs; + + performStandardAuction(); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + + expect(request.url).to.equal('//localhost:9999/event'); + + let message = JSON.parse(request.requestBody); + validate(message); + + expect(message).to.deep.equal(expectedMessage); + }); + + it('should should pass along custom rubicon kv and pvid when defined', function () { + pvid = '1a2b3c'; + kvps = { + source: 'fb', + link: 'email' + }; + + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.session.pvid = '1a2b3c'; + expectedMessage.fpkvs = [ + {key: 'source', value: 'fb'}, + {key: 'link', value: 'email'} + ] + expect(message).to.deep.equal(expectedMessage); + }); + + it('should pick up existing localStorage and use its values', function () { + // set some localStorage + let inputlocalStorage = { + id: '987654', + start: 1519766113781, // 15 mins before "now" + expires: 1519787713781, // six hours later + lastSeen: 1519766113781, + fpkvs: { source: 'tw' } + }; + getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); + + pvid = '1a2b3c'; + kvps = { + link: 'email' // should merge this with what is in the localStorage! + }; + + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.session = { + id: '987654', + start: 1519766113781, + expires: 1519787713781, + pvid: '1a2b3c' + } + expectedMessage.fpkvs = [ + {key: 'source', value: 'tw'}, + {key: 'link', value: 'email'} + ] + expect(message).to.deep.equal(expectedMessage); + + let calledWith; + try { + calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); + } catch (e) { + calledWith = {}; + } + + expect(calledWith).to.deep.equal({ + id: '987654', // should have stayed same + start: 1519766113781, // should have stayed same + expires: 1519787713781, // should have stayed same + lastSeen: 1519767013781, // lastSeen updated to our "now" + fpkvs: { source: 'tw', link: 'email' }, // link merged in + pvid: '1a2b3c' // new pvid stored + }); + }); + + it('should throw out session if lastSeen > 30 mins ago and create new one', function () { + // set some localStorage + let inputlocalStorage = { + id: '987654', + start: 1519764313781, // 45 mins before "now" + expires: 1519785913781, // six hours later + lastSeen: 1519764313781, // 45 mins before "now" + fpkvs: { source: 'tw' } + }; + getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); + + pvid = '1a2b3c'; + kvps = { + link: 'email' // should merge this with what is in the localStorage! + }; + + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid + expectedMessage.session.pvid = '1a2b3c'; + + // the saved fpkvs should have been thrown out since session expired + expectedMessage.fpkvs = [ + {key: 'link', value: 'email'} + ] + expect(message).to.deep.equal(expectedMessage); + + let calledWith; + try { + calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); + } catch (e) { + calledWith = {}; + } + + expect(calledWith).to.deep.equal({ + id: STUBBED_UUID, // should have stayed same + start: 1519767013781, // should have stayed same + expires: 1519788613781, // should have stayed same + lastSeen: 1519767013781, // lastSeen updated to our "now" + fpkvs: { link: 'email' }, // link merged in + pvid: '1a2b3c' // new pvid stored + }); + }); + + it('should throw out session if past expires time and create new one', function () { + // set some localStorage + let inputlocalStorage = { + id: '987654', + start: 1519745353781, // 6 hours before "expires" + expires: 1519766953781, // little more than six hours ago + lastSeen: 1519767008781, // 5 seconds ago + fpkvs: { source: 'tw' } + }; + getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); + + pvid = '1a2b3c'; + kvps = { + link: 'email' // should merge this with what is in the localStorage! + }; + + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid + expectedMessage.session.pvid = '1a2b3c'; + + // the saved fpkvs should have been thrown out since session expired + expectedMessage.fpkvs = [ + {key: 'link', value: 'email'} + ] + expect(message).to.deep.equal(expectedMessage); + + let calledWith; + try { + calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); + } catch (e) { + calledWith = {}; + } + + expect(calledWith).to.deep.equal({ + id: STUBBED_UUID, // should have stayed same + start: 1519767013781, // should have stayed same + expires: 1519788613781, // should have stayed same + lastSeen: 1519767013781, // lastSeen updated to our "now" + fpkvs: { link: 'email' }, // link merged in + pvid: '1a2b3c' // new pvid stored + }); + }); + }); + describe('with googletag enabled', function () { + let gptSlot0, gptSlot1, gptEvent0, gptEvent1; + beforeEach(function () { + mockGpt.enable(); + gptSlot0 = mockGpt.makeSlot({code: '/19968336/header-bid-tag-0'}); + gptSlot1 = mockGpt.makeSlot({code: '/19968336/header-bid-tag1'}); + gptEvent0 = { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot0, + isEmpty: false, + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333 + } + }; + gptEvent1 = { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot1, + isEmpty: false, + advertiserId: 4444, + creativeId: 5555, + lineItemId: 6666 + } + }; + }); + + afterEach(function () { + mockGpt.disable(); + }); + + it('should add necessary gam information if gpt is enabled and slotRender event emmited', function () { + performStandardAuction([gptEvent0, gptEvent1]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + advertiserId: 4444, + creativeId: 5555, + lineItemId: 6666, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + + it('should handle empty gam renders', function () { + performStandardAuction([gptEvent0, { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot1, + isEmpty: true + } + }]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + isSlotEmpty: true, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + + it('should still add gam ids if falsy', function () { + performStandardAuction([gptEvent0, { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot1, + isEmpty: false, + advertiserId: 0, + creativeId: 0, + lineItemId: 0 + } + }]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + advertiserId: 0, + creativeId: 0, + lineItemId: 0, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + + it('should handle empty gam renders', function () { + performStandardAuction([gptEvent0, gptEvent1]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + advertiserId: 4444, + creativeId: 5555, + lineItemId: 6666, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + }); + it('should correctly overwrite bidId if seatBidId is on the bidResponse', function () { // Only want one bid request in our mock auction let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index 16cca629d8c..407dfd18be3 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -34,6 +34,53 @@ "type": "string", "description": "Version of Prebid.js responsible for the auctions contained within." }, + "fpkvs": { + "type": "array", + "description": "List of any dynamic key value pairs set by publisher.", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "session": { + "type": "object", + "description": "The session information for a given event", + "required": [ + "id", + "start", + "expires" + ], + "properties": { + "id": { + "type": "string", + "description": "UUID of session." + }, + "start": { + "type": "integer", + "description": "Unix timestamp of time of creation for this session in milliseconds." + }, + "expires": { + "type": "integer", + "description": "Unix timestamp of the maximum allowed time in milliseconds of the session." + }, + "pvid": { + "type": "string", + "description": "id to track page view." + } + } + }, "auctions": { "type": "array", "minItems": 1, @@ -125,6 +172,9 @@ "zoneId": { "type": "number", "description": "The Rubicon zoneId associated with this adUnit - Removed if null" + }, + "gam": { + "$ref": "#/definitions/gam" } } } @@ -197,6 +247,31 @@ } }, "definitions": { + "gam": { + "type": "object", + "description": "The gam information for a given ad unit", + "required": [ + "adSlot" + ], + "properties": { + "adSlot": { + "type": "string" + }, + "advertiserId": { + "type": "integer" + }, + "creativeId": { + "type": "integer" + }, + "LineItemId": { + "type": "integer" + }, + "isSlotEmpty": { + "type": "boolean", + "enum": [true] + } + } + }, "adserverTargeting": { "type": "object", "description": "The adserverTargeting key/value pairs", @@ -293,7 +368,8 @@ "success", "no-bid", "error", - "rejected" + "rejected-gdpr", + "rejected-ipf" ] }, "error": { From fd9a0d4cf2a1dfffca231dd8a6c17835bb5d49a1 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 16 Sep 2020 17:05:40 +0200 Subject: [PATCH 0194/1476] Add gdpr support to ablida bid adapter (#5741) * add onBidWon function, add bidder adapter version to bid requests * add support for native * use triggerPxel instead of ajax, because ajax was called 3 times with native * add gdpr consent to bid requests * update tests --- modules/ablidaBidAdapter.js | 5 +- test/spec/modules/ablidaBidAdapter_spec.js | 70 +++++++++++++++------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index 470a845cd20..af9c206700f 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -46,8 +46,9 @@ export const spec = { referer: bidderRequest.refererInfo.referer, jaySupported: jaySupported, device: device, - adapterVersion: 3, - mediaTypes: bidRequest.mediaTypes + adapterVersion: 4, + mediaTypes: bidRequest.mediaTypes, + gdprConsent: bidderRequest.gdprConsent }; return { method: 'POST', diff --git a/test/spec/modules/ablidaBidAdapter_spec.js b/test/spec/modules/ablidaBidAdapter_spec.js index e32531b1eac..0743bf0a896 100644 --- a/test/spec/modules/ablidaBidAdapter_spec.js +++ b/test/spec/modules/ablidaBidAdapter_spec.js @@ -9,17 +9,23 @@ describe('ablidaBidAdapter', function () { const adapter = newBidder(spec); describe('isBidRequestValid', function () { let bid = { + adUnitCode: 'adunit-code', + auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0', + bidRequestsCount: 1, bidder: 'ablida', + bidderRequestId: '14d2939272a26a', + bidderRequestsCount: 1, + bidderWinsCount: 0, + bidId: '1234asdf1234', + mediaTypes: {banner: {sizes: [[300, 250]]}}, params: { placementId: 123 }, - adUnitCode: 'adunit-code', sizes: [ [300, 250] ], - bidId: '1234asdf1234', - bidderRequestId: '1234asdf1234asdf', - auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0' + src: 'client', + transactionId: '4781e6ac-93c4-42ba-86fe-ab5f278863cf' }; it('should return true where required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); @@ -28,22 +34,29 @@ describe('ablidaBidAdapter', function () { describe('buildRequests', function () { let bidRequests = [ { + adUnitCode: 'adunit-code', + auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0', + bidId: '23beaa6af6cdde', + bidRequestsCount: 1, bidder: 'ablida', + bidderRequestId: '14d2939272a26a', + bidderRequestsCount: 1, + bidderWinsCount: 0, + mediaTypes: {banner: {sizes: [[300, 250]]}}, params: { placementId: 123 }, sizes: [ [300, 250] ], - adUnitCode: 'adunit-code', - bidId: '23beaa6af6cdde', - bidderRequestId: '14d2939272a26a', - auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0', + src: 'client', + transactionId: '4781e6ac-93c4-42ba-86fe-ab5f278863cf' } ]; let bidderRequests = { refererInfo: { + canonicalUrl: '', numIframes: 0, reachedTop: true, referer: 'http://example.com', @@ -62,42 +75,53 @@ describe('ablidaBidAdapter', function () { method: 'POST', url: ENDPOINT_URL, data: { + adapterVersion: 4, + bidId: '2b8c4de0116e54', + categories: undefined, + device: 'desktop', + gdprConsent: undefined, + jaySupported: true, + mediaTypes: {banner: {sizes: [[300, 250]]}}, placementId: 'testPlacementId', width: 300, height: 200, - bidId: '2b8c4de0116e54', - jaySupported: true, - device: 'desktop', - referer: 'www.example.com', - adapterVersion: 2 + referer: 'www.example.com' } }; let serverResponse = { body: [{ - requestId: '2b8c4de0116e54', + ad: '', cpm: 1.00, - width: 300, - height: 250, creativeId: '2b8c4de0116e54', currency: 'EUR', + height: 250, + mediaType: 'banner', + meta: {}, netRevenue: true, + nurl: 'https://example.com/some-tracker', + originalCpm: '0.10', + originalCurrency: 'EUR', + requestId: '2b8c4de0116e54', ttl: 3000, - ad: '', - nurl: 'https://example.com/some-tracker' + width: 300 }] }; it('should get the correct bid response', function () { let expectedResponse = [{ - requestId: '2b8c4de0116e54', + ad: '', cpm: 1.00, - width: 300, - height: 250, creativeId: '2b8c4de0116e54', currency: 'EUR', + height: 250, + mediaType: 'banner', + meta: {}, netRevenue: true, + nurl: 'https://example.com/some-tracker', + originalCpm: '0.10', + originalCurrency: 'EUR', + requestId: '2b8c4de0116e54', ttl: 3000, - ad: '', - nurl: 'https://example.com/some-tracker' + width: 300 }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); From a3aa5f4d14e983385741e0610722435bb01a33a1 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 16 Sep 2020 17:08:01 +0200 Subject: [PATCH 0195/1476] added onBidWon event (#5679) * added onBidWon event * luponmedia onBidWon event + test * luponmedia onbidwon event + unit test * luponmedia onbidwon event + test * luponmedia onbidwon + unit testing * luponmedia onbidwon event + unit tests * luponmedia onbidwon event ajax * package-log revert --- modules/luponmediaBidAdapter.js | 14 ++++++ .../spec/modules/luponmediaBidAdapter_spec.js | 45 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 0a2eed0ad13..4f7fd2ae1a0 100644 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -2,6 +2,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'luponmedia'; const ENDPOINT_URL = 'https://rtb.adxpremium.services/openrtb2/auction'; @@ -113,6 +114,19 @@ export const spec = { hasSynced = true; return allUserSyncs; }, + onBidWon: bid => { + const bidString = JSON.stringify(bid); + spec.sendWinningsToServer(bidString); + }, + sendWinningsToServer: data => { + let mutation = `mutation {createWin(input: {win: {eventData: "${window.btoa(data)}"}}) {win {createTime } } }`; + let dataToSend = JSON.stringify({ query: mutation }); + + ajax('https://analytics.adxpremium.services/graphql', null, dataToSend, { + contentType: 'application/json', + method: 'POST' + }); + } }; function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { diff --git a/test/spec/modules/luponmediaBidAdapter_spec.js b/test/spec/modules/luponmediaBidAdapter_spec.js index 39c915e38b8..8aeecc87c98 100644 --- a/test/spec/modules/luponmediaBidAdapter_spec.js +++ b/test/spec/modules/luponmediaBidAdapter_spec.js @@ -364,4 +364,49 @@ describe('luponmediaBidAdapter', function () { expect(checkSchain).to.equal(false); }); }); + + describe('onBidWon', function () { + const bidWonEvent = { + 'bidderCode': 'luponmedia', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '105bbf8c54453ff', + 'requestId': '934b8752185955', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.364, + 'creativeId': '443801010', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 300, + 'referrer': '', + 'ad': '', + 'auctionId': '926a8ea3-3dd4-4bf2-95ab-c85c2ce7e99b', + 'responseTimestamp': 1598527728026, + 'requestTimestamp': 1598527727629, + 'bidder': 'luponmedia', + 'adUnitCode': 'div-gpt-ad-1533155193780-5', + 'timeToRespond': 397, + 'size': '300x250', + 'status': 'rendered' + }; + + let ajaxStub; + + beforeEach(() => { + ajaxStub = sinon.stub(spec, 'sendWinningsToServer') + }) + + afterEach(() => { + ajaxStub.restore() + }) + + it('calls luponmedia\'s callback endpoint', () => { + const result = spec.onBidWon(bidWonEvent); + expect(result).to.equal(undefined); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.deep.equal(JSON.stringify(bidWonEvent)); + }); + }); }); From 94520501afcd82282a8c7832a3c7946012f9b870 Mon Sep 17 00:00:00 2001 From: Danny Khatib Date: Wed, 16 Sep 2020 12:28:41 -0400 Subject: [PATCH 0196/1476] mapping spotx dealid to bid object (#5745) Co-authored-by: Danny Khatib --- modules/spotxBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 9a3779dc65c..6a80fd6dc0d 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -320,6 +320,7 @@ export const spec = { currency: serverResponseBody.cur || 'USD', cpm: spotxBid.price, creativeId: spotxBid.crid || '', + dealId: spotxBid.dealid || '', ttl: 360, netRevenue: true, channel_id: serverResponseBody.id, From bfb182a47cd2cce1fb7fbfe73513090786515b02 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 16 Sep 2020 11:15:21 -0700 Subject: [PATCH 0197/1476] fix broken unit tests for zeotap (#5758) --- modules/zeotapIdPlusIdSystem.js | 2 +- .../spec/modules/zeotapIdPlusIdSystem_spec.js | 51 +++++++------------ 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index c194a9b9679..ea1173cd61e 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -9,7 +9,7 @@ import {submodule} from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; const ZEOTAP_COOKIE_NAME = 'IDP'; -const storage = getStorageManager(); +export const storage = getStorageManager(); function readCookie() { return storage.cookiesAreEnabled ? storage.getCookie(ZEOTAP_COOKIE_NAME) : null; diff --git a/test/spec/modules/zeotapIdPlusIdSystem_spec.js b/test/spec/modules/zeotapIdPlusIdSystem_spec.js index 52698ecffc9..54082618120 100644 --- a/test/spec/modules/zeotapIdPlusIdSystem_spec.js +++ b/test/spec/modules/zeotapIdPlusIdSystem_spec.js @@ -1,11 +1,8 @@ import { expect } from 'chai'; import find from 'core-js-pure/features/array/find.js'; import { config } from 'src/config.js'; -import { newStorageManager } from 'src/storageManager.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; -import { zeotapIdPlusSubmodule } from 'modules/zeotapIdPlusIdSystem.js'; - -const storage = newStorageManager(); +import { storage, zeotapIdPlusSubmodule } from 'modules/zeotapIdPlusIdSystem.js'; const ZEOTAP_COOKIE_NAME = 'IDP'; const ZEOTAP_COOKIE = 'THIS-IS-A-DUMMY-COOKIE'; @@ -37,27 +34,26 @@ function getAdUnitMock(code = 'adUnit-code') { }; } -function unsetCookie() { - storage.setCookie(ZEOTAP_COOKIE_NAME, ''); -} +describe('Zeotap ID System', function() { + let getDataFromLocalStorageStub, localStorageIsEnabledStub; + let getCookieStub, cookiesAreEnabledStub; + beforeEach(function () { + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + }); -function unsetLocalStorage() { - storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ''); -} + afterEach(function () { + getDataFromLocalStorageStub.restore(); + localStorageIsEnabledStub.restore(); + getCookieStub.restore(); + cookiesAreEnabledStub.restore(); + }); -describe('Zeotap ID System', function() { describe('test method: getId', function() { - afterEach(() => { - unsetCookie(); - unsetLocalStorage(); - }) - it('provides the stored Zeotap id if a cookie exists', function() { - storage.setCookie( - ZEOTAP_COOKIE_NAME, - ENCODED_ZEOTAP_COOKIE, - (new Date(Date.now() + 5000).toUTCString()), - ); + getCookieStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); let id = zeotapIdPlusSubmodule.getId(); expect(id).to.deep.equal({ id: ENCODED_ZEOTAP_COOKIE @@ -65,7 +61,7 @@ describe('Zeotap ID System', function() { }); it('provides the stored Zeotap id if cookie is absent but present in local storage', function() { - storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ENCODED_ZEOTAP_COOKIE); + getDataFromLocalStorageStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); let id = zeotapIdPlusSubmodule.getId(); expect(id).to.deep.equal({ id: ENCODED_ZEOTAP_COOKIE @@ -103,19 +99,10 @@ describe('Zeotap ID System', function() { beforeEach(function() { adUnits = [getAdUnitMock()]; - storage.setCookie( - ZEOTAP_COOKIE_NAME, - ENCODED_ZEOTAP_COOKIE, - (new Date(Date.now() + 5000).toUTCString()), - ); setSubmoduleRegistry([zeotapIdPlusSubmodule]); init(config); config.setConfig(getConfigMock()); - }); - - afterEach(function() { - unsetCookie(); - unsetLocalStorage(); + getCookieStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); }); it('when a stored Zeotap ID exists it is added to bids', function(done) { From a2c6128ab1905188801bbefb8a9ee66bc8b84ce7 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Thu, 17 Sep 2020 16:54:07 +0530 Subject: [PATCH 0198/1476] Add guideline to check for GVL ID (#5757) --- PR_REVIEW.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 9a57539a0cd..d7703cf20ae 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -24,6 +24,7 @@ For modules and core platform updates, the initial reviewer should request an ad - If they support SChain, add `schain_supported: true` - If their bidder doesn't work well with safeframed creatives, add `safeframes_ok: false`. This will alert publishers to not use safeframed creatives when creating the ad server entries for their bidder. - If they're setting a deal ID in some scenarios, add `bidder_supports_deals: true` + - If they have an IAB Global Vendor List ID, add `gvl_id: ID`. There's no default. - If all above is good, add a `LGTM` comment and request 1 additional core member to review. - Once there is 2 `LGTM` on the PR, merge to master - Ask the submitter to add a PR for documentation if applicable. From 172980b2f1efcc4dc73adad618ca6061f23b96b2 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Thu, 17 Sep 2020 20:59:33 +0530 Subject: [PATCH 0199/1476] Added instream BID_WON tracking (#5481) Co-authored-by: monis.q --- modules/dfpAdServerVideo.js | 13 +- modules/instreamTracking.js | 114 +++++++++++ test/spec/modules/instreamTracking_spec.js | 221 +++++++++++++++++++++ 3 files changed, 344 insertions(+), 4 deletions(-) create mode 100644 modules/instreamTracking.js create mode 100644 test/spec/modules/instreamTracking_spec.js diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 9fe8c26e4f6..554c44aa708 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -8,6 +8,8 @@ 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 events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; /** * @typedef {Object} DfpVideoParams @@ -245,17 +247,20 @@ function getCustParams(bid, options) { allTargetingData = (allTargeting) ? allTargeting[adUnit.code] : {}; } - const optCustParams = deepAccess(options, 'params.cust_params'); - let customParams = Object.assign({}, + const prebidTargetingSet = Object.assign({}, // Why are we adding standard keys here ? Refer https://github.com/prebid/Prebid.js/issues/3664 { hb_uuid: bid && bid.videoCacheKey }, // hb_uuid will be deprecated and replaced by hb_cache_id { hb_cache_id: bid && bid.videoCacheKey }, allTargetingData, adserverTargeting, - optCustParams, ); - return encodeURIComponent(formatQS(customParams)); + events.emit(CONSTANTS.EVENTS.SET_TARGETING, {[adUnit.code]: prebidTargetingSet}); + + // merge the prebid + publisher targeting sets + const publisherTargetingSet = deepAccess(options, 'params.cust_params'); + const targetingSet = Object.assign({}, prebidTargetingSet, publisherTargetingSet); + return encodeURIComponent(formatQS(targetingSet)); } registerVideoSupport('dfp', { diff --git a/modules/instreamTracking.js b/modules/instreamTracking.js new file mode 100644 index 00000000000..68bb4be79de --- /dev/null +++ b/modules/instreamTracking.js @@ -0,0 +1,114 @@ +import { config } from '../src/config.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { INSTREAM } from '../src/video.js'; +import * as events from '../src/events.js'; +import * as utils from '../src/utils.js'; +import { BID_STATUS, EVENTS, TARGETING_KEYS } from '../src/constants.json'; + +const {CACHE_ID, UUID} = TARGETING_KEYS; +const {BID_WON, AUCTION_END} = EVENTS; +const {RENDERED} = BID_STATUS; + +const INSTREAM_TRACKING_DEFAULT_CONFIG = { + enabled: false, + maxWindow: 1000 * 60, // the time in ms after which polling for instream delivery stops + pollingFreq: 500 // the frequency of polling +}; + +// Set instreamTracking default values +config.setDefaults({ + 'instreamTracking': utils.deepClone(INSTREAM_TRACKING_DEFAULT_CONFIG) +}); + +const whitelistedResources = /video|fetch|xmlhttprequest|other/; + +/** + * Here the idea is + * find all network entries via performance.getEntriesByType() + * filter it by video cache key in the url + * and exclude the ad server urls so that we dont match twice + * eg: + * dfp ads call: https://securepubads.g.doubleclick.net/gampad/ads?...hb_cache_id%3D55e85cd3-6ea4-4469-b890-84241816b131%26... + * prebid cache url: https://prebid.adnxs.com/pbc/v1/cache?uuid=55e85cd3-6ea4-4469-b890-84241816b131 + * + * if the entry exists, emit the BID_WON + * + * Note: this is a workaround till a better approach is engineered. + * + * @param {Array} adUnits + * @param {Array} bidsReceived + * @param {Array} bidderRequests + * + * @return {boolean} returns TRUE if tracking started + */ +export function trackInstreamDeliveredImpressions({adUnits, bidsReceived, bidderRequests}) { + const instreamTrackingConfig = config.getConfig('instreamTracking') || {}; + // check if instreamTracking is enabled and performance api is available + if (!instreamTrackingConfig.enabled || !window.performance || !window.performance.getEntriesByType) { + return false; + } + + // filter for video bids + const instreamBids = bidsReceived.filter(bid => { + const bidderRequest = utils.getBidRequest(bid.requestId, bidderRequests); + return bidderRequest && utils.deepAccess(bidderRequest, 'mediaTypes.video.context') === INSTREAM && bid.videoCacheKey; + }); + if (!instreamBids.length) { + return false; + } + + // find unique instream ad units + const instreamAdUnitMap = {}; + adUnits.forEach(adUnit => { + if (!instreamAdUnitMap[adUnit.code] && utils.deepAccess(adUnit, 'mediaTypes.video.context') === INSTREAM) { + instreamAdUnitMap[adUnit.code] = true; + } + }); + const instreamAdUnitsCount = Object.keys(instreamAdUnitMap).length; + + const start = Date.now(); + const {maxWindow, pollingFreq, urlPattern} = instreamTrackingConfig; + + let instreamWinningBidsCount = 0; + let lastRead = 0; // offset for performance.getEntriesByType + + function poll() { + // get network entries using the last read offset + const entries = window.performance.getEntriesByType('resource').splice(lastRead); + for (const resource of entries) { + const url = resource.name; + // check if the resource is of whitelisted resource to avoid checking img or css or script urls + if (!whitelistedResources.test(resource.initiatorType)) { + continue; + } + + instreamBids.forEach((bid) => { + // match the video cache key excluding ad server call + const matches = !(url.indexOf(CACHE_ID) !== -1 || url.indexOf(UUID) !== -1) && url.indexOf(bid.videoCacheKey) !== -1; + if (urlPattern && urlPattern instanceof RegExp && !urlPattern.test(url)) { + return; + } + if (matches && bid.status !== RENDERED) { + // video found + instreamWinningBidsCount++; + auctionManager.addWinningBid(bid); + events.emit(BID_WON, bid); + } + }); + } + // update offset + lastRead += entries.length; + + const timeElapsed = Date.now() - start; + if (timeElapsed < maxWindow && instreamWinningBidsCount < instreamAdUnitsCount) { + setTimeout(poll, pollingFreq); + } + } + + // start polling for network entries + setTimeout(poll, pollingFreq); + + return true; +} + +events.on(AUCTION_END, trackInstreamDeliveredImpressions) diff --git a/test/spec/modules/instreamTracking_spec.js b/test/spec/modules/instreamTracking_spec.js new file mode 100644 index 00000000000..8c49da76ab6 --- /dev/null +++ b/test/spec/modules/instreamTracking_spec.js @@ -0,0 +1,221 @@ +import { assert } from 'chai'; +import { trackInstreamDeliveredImpressions } from 'modules/instreamTracking.js'; +import { config } from 'src/config.js'; +import * as events from 'src/events.js'; +import * as utils from 'src/utils.js'; +import * as sinon from 'sinon'; +import { INSTREAM, OUTSTREAM } from 'src/video.js'; + +const BIDDER_CODE = 'sampleBidder'; +const VIDEO_CACHE_KEY = '4cf395af-8fee-4960-af0e-88d44e399f14'; + +let sandbox; + +function enableInstreamTracking(regex) { + let configStub = sandbox.stub(config, 'getConfig'); + configStub.withArgs('instreamTracking').returns(Object.assign( + { + enabled: true, + maxWindow: 10, + pollingFreq: 0 + }, + regex && {urlPattern: regex}, + )); +} + +function mockPerformanceApi({adServerCallSent, videoPresent}) { + let performanceStub = sandbox.stub(window.performance, 'getEntriesByType'); + let entries = [{ + name: 'https://domain.com/img.png', + initiatorType: 'img' + }, { + name: 'https://domain.com/script.js', + initiatorType: 'script' + }, { + name: 'https://domain.com/xhr', + initiatorType: 'xmlhttprequest' + }, { + name: 'https://domain.com/fetch', + initiatorType: 'fetch' + }]; + + if (adServerCallSent || videoPresent) { + entries.push({ + name: 'https://adserver.com/ads?custom_params=hb_uuid%3D' + VIDEO_CACHE_KEY + '%26pos%3D' + VIDEO_CACHE_KEY, + initiatorType: 'xmlhttprequest' + }); + } + + if (videoPresent) { + entries.push({ + name: 'https://prebid-vast-cache.com/cache?key=' + VIDEO_CACHE_KEY, + initiatorType: 'xmlhttprequest' + }); + } + + performanceStub.withArgs('resource').returns(entries); +} + +function mockBidResponse(adUnit, requestId) { + const bid = { + 'adUnitCod': adUnit.code, + 'bidderCode': adUnit.bids[0].bidder, + 'width': adUnit.sizes[0][0], + 'height': adUnit.sizes[0][1], + 'statusMessage': 'Bid available', + 'adId': 'id', + 'requestId': requestId, + 'source': 'client', + 'no_bid': false, + 'cpm': '1.1495', + 'ttl': 180, + 'creativeId': 'id', + 'netRevenue': true, + 'currency': 'USD', + } + if (adUnit.mediaTypes.video) { + bid.videoCacheKey = VIDEO_CACHE_KEY; + } + return bid +} + +function mockBidRequest(adUnit, bidResponse) { + return { + 'bidderCode': bidResponse.bidderCode, + 'auctionId': '20882439e3238c', + 'bidderRequestId': 'bidderRequestId', + 'bids': [ + { + 'adUnitCode': adUnit.code, + 'mediaTypes': adUnit.mediaTypes, + 'bidder': bidResponse.bidderCode, + 'bidId': bidResponse.requestId, + 'sizes': adUnit.sizes, + 'params': adUnit.bids[0].params, + 'bidderRequestId': 'bidderRequestId', + 'auctionId': '20882439e3238c', + } + ], + 'auctionStart': 1505250713622, + 'timeout': 3000 + }; +} + +function getMockInput(mediaType) { + const bannerAdUnit = { + code: 'banner', + mediaTypes: {banner: {sizes: [[300, 250]]}}, + sizes: [[300, 250]], + bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + }; + const outStreamAdUnit = { + code: 'video-' + OUTSTREAM, + mediaTypes: {video: {playerSize: [640, 480], context: OUTSTREAM}}, + sizes: [[640, 480]], + bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + }; + const inStreamAdUnit = { + code: 'video-' + INSTREAM, + mediaTypes: {video: {playerSize: [640, 480], context: INSTREAM}}, + sizes: [[640, 480]], + bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + }; + + let adUnit; + switch (mediaType) { + default: + case 'banner': + adUnit = bannerAdUnit; + break; + case OUTSTREAM: + adUnit = outStreamAdUnit; + break; + case INSTREAM: + adUnit = inStreamAdUnit; + break; + } + + const bidResponse = mockBidResponse(adUnit, utils.getUniqueIdentifierStr()); + const bidderRequest = mockBidRequest(adUnit, bidResponse); + return { + adUnits: [adUnit], + bidsReceived: [bidResponse], + bidderRequests: [bidderRequest], + }; +} + +describe('Instream Tracking', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('gaurd checks', function () { + it('skip if tracking not enable', function () { + sandbox.stub(config, 'getConfig').withArgs('instreamTracking').returns(undefined); + assert.isNotOk(trackInstreamDeliveredImpressions({ + adUnits: [], + bidsReceived: [], + bidderRequests: [] + }), 'should not start tracking when tracking is disabled'); + }); + + it('run only if instream bids are present', function () { + enableInstreamTracking(); + assert.isNotOk(trackInstreamDeliveredImpressions({adUnits: [], bidsReceived: [], bidderRequests: []})); + }); + + it('checks for instream bids', function (done) { + enableInstreamTracking(); + assert.isNotOk(trackInstreamDeliveredImpressions(getMockInput('banner')), 'should not start tracking when banner bids are present') + assert.isNotOk(trackInstreamDeliveredImpressions(getMockInput(OUTSTREAM)), 'should not start tracking when outstream bids are present') + mockPerformanceApi({}); + assert.isOk(trackInstreamDeliveredImpressions(getMockInput(INSTREAM)), 'should start tracking when instream bids are present') + setTimeout(done, 10); + }); + }); + + describe('instream bids check', function () { + let spyEventsOn; + + beforeEach(function () { + spyEventsOn = sandbox.spy(events, 'emit'); + }); + + it('BID WON event is not emitted when no video cache key entries are present', function (done) { + enableInstreamTracking(); + trackInstreamDeliveredImpressions(getMockInput(INSTREAM)); + mockPerformanceApi({}); + setTimeout(function () { + assert.isNotOk(spyEventsOn.calledWith('bidWon')) + done() + }, 10); + }); + + it('BID WON event is not emitted when ad server call is sent', function (done) { + enableInstreamTracking(); + mockPerformanceApi({adServerCallSent: true}); + setTimeout(function () { + assert.isNotOk(spyEventsOn.calledWith('bidWon')) + done() + }, 10); + }); + + it('BID WON event is emitted when video cache key is present', function (done) { + enableInstreamTracking(/cache/); + const bidWonSpy = sandbox.spy(); + events.on('bidWon', bidWonSpy); + mockPerformanceApi({adServerCallSent: true, videoPresent: true}); + + trackInstreamDeliveredImpressions(getMockInput(INSTREAM)); + setTimeout(function () { + assert.isOk(spyEventsOn.calledWith('bidWon')) + assert(bidWonSpy.args[0][0].videoCacheKey, VIDEO_CACHE_KEY, 'Video cache key in bid won should be equal to video cache call'); + done() + }, 10); + }); + }); +}); From db225e90acbeed41fc519ae040b501391acc2ed7 Mon Sep 17 00:00:00 2001 From: gpolaert Date: Thu, 17 Sep 2020 17:56:30 +0200 Subject: [PATCH 0200/1476] feat: add getEvents method to the public API (#5703) --- src/prebid.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index bd85f416883..31e0140cfe2 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,7 +1,7 @@ /** @module pbjs */ import { getGlobal } from './prebidGlobal.js'; -import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, isArrayOfNums } from './utils.js'; +import { adUnitsFilter, flatten, isArrayOfNums, isGptPubadsDefined, uniques } from './utils.js'; import { listenMessagesFromCreative } from './secureCreatives.js'; import { userSync } from './userSync.js'; import { config } from './config.js'; @@ -11,7 +11,7 @@ import { hook } from './hook.js'; import { sessionLoader } from './debugging.js'; import includes from 'core-js-pure/features/array/includes.js'; import { adunitCounter } from './adUnits.js'; -import { isRendererRequired, executeRenderer } from './Renderer.js'; +import { executeRenderer, isRendererRequired } from './Renderer.js'; import { createBid } from './bidfactory.js'; import { storageCallbacks } from './storageManager.js'; @@ -540,6 +540,7 @@ export function executeCallbacks(fn, reqBidsConfigObj) { runAll(storageCallbacks); runAll(enableAnalyticsCallbacks); fn.call(this, reqBidsConfigObj); + function runAll(queue) { var queued; while ((queued = queue.shift())) { @@ -614,6 +615,16 @@ $$PREBID_GLOBAL$$.offEvent = function (event, handler, id) { events.off(event, handler, id); }; +/** + * Return a copy of all events emitted + * + * @alias module:pbjs.getEvents + */ +$$PREBID_GLOBAL$$.getEvents = function () { + utils.logInfo('Invoking $$PREBID_GLOBAL$$.getEvents'); + return events.getEvents(); +}; + /* * Wrapper to register bidderAdapter externally (adapterManager.registerBidAdapter()) * @param {Function} bidderAdaptor [description] @@ -730,12 +741,12 @@ $$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias, options) { * @property {string} adserverTargeting.hb_adid The ad ID of the creative, as understood by the ad server. * @property {string} adserverTargeting.hb_pb The price paid to show the creative, as logged in the ad server. * @property {string} adserverTargeting.hb_bidder The winning bidder whose ad creative will be served by the ad server. -*/ + */ /** * Get all of the bids that have been rendered. Useful for [troubleshooting your integration](http://prebid.org/dev-docs/prebid-troubleshooting-guide.html). * @return {Array} A list of bids that have been rendered. -*/ + */ $$PREBID_GLOBAL$$.getAllWinningBids = function () { return auctionManager.getAllWinningBids(); }; @@ -767,7 +778,7 @@ $$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) { * @property {string} adId The id representing the ad we want to mark * * @alias module:pbjs.markWinningBidAsUsed -*/ + */ $$PREBID_GLOBAL$$.markWinningBidAsUsed = function (markBidRequest) { let bids = []; From 5911c6eedfee71e7868a62df279d1b3f2b3c2903 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 17 Sep 2020 10:09:51 -0700 Subject: [PATCH 0201/1476] Prebid 4.8.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ad0781d648..9daa387991d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.8.0-pre", + "version": "4.8.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From cd4d0d84cc7913985b3503d49e3da08c615ac845 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 17 Sep 2020 10:32:22 -0700 Subject: [PATCH 0202/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9daa387991d..2a304f2a42b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.8.0", + "version": "4.9.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 89d829fd672bd5da74e438a87b2e7cbd8680b342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E6=80=9D=E6=95=8F?= <506374983@qq.com> Date: Sat, 19 Sep 2020 07:57:43 +0800 Subject: [PATCH 0203/1476] =?UTF-8?q?=E3=80=90MediaGoBidderAdapter?= =?UTF-8?q?=E3=80=91notify=20server=20if=20the=20page=20is=20secure=20and?= =?UTF-8?q?=20check=20match=20size=20(#5753)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * notify server if the page is secure * remove undefined initial Co-authored-by: fangsimin@baidu.com --- modules/mediagoBidAdapter.js | 69 +++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 68eb95ddae3..022d216a84a 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -9,7 +9,8 @@ import { } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'mediago'; -// const PROTOCOL = window.document.location.protocol; +const PROTOCOL = window.document.location.protocol; +const IS_SECURE = (PROTOCOL === 'https:') ? 1 : 0; const ENDPOINT_URL = // ((PROTOCOL === 'https:') ? 'https' : 'http') + 'https://rtb-us.mediago.io/api/bid?tn='; @@ -163,8 +164,9 @@ function transformSizes(requestSizes) { */ function getItems(validBidRequests, bidderRequest) { let items = []; - items = validBidRequests.map((req, i) => { - let ret = {}; + for (let i in validBidRequests) { + let req = validBidRequests[i]; + let ret; let mediaTypes = getProperty(req, 'mediaTypes'); let sizes = transformSizes(getProperty(req, 'sizes')); @@ -178,27 +180,33 @@ function getItems(validBidRequests, bidderRequest) { } } - // if (mediaTypes.native) {} - // banner广告类型 - if (mediaTypes.banner) { - let id = '' + (i + 1); - ret = { - id: id, - // bidFloor: 0, // todo - banner: { - h: matchSize.height, - w: matchSize.width, - pos: 1, - } - }; - itemMaps[id] = { - req, - ret - }; + // Continue only if there is a matching size + if (matchSize) { + // banner广告类型 + if (mediaTypes.banner) { + let id = '' + (+i + 1); + ret = { + id: id, + // bidFloor: 0, // todo + banner: { + h: matchSize.height, + w: matchSize.width, + pos: 1, + }, + secure: IS_SECURE // for server-side to check if it's secure page + }; + itemMaps[id] = { + req, + ret + }; + } } - return ret; - }); + if (ret) { + items.push(ret); + } + } + return items; } @@ -288,12 +296,17 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { let payload = getParam(validBidRequests, bidderRequest); - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: ENDPOINT_URL + globals['token'], - data: payloadString, - }; + // request ad only if there is a matching size + if (payload) { + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: ENDPOINT_URL + globals['token'], + data: payloadString, + }; + } else { + return null; + } }, /** From 64807152f352d97d87458e99642ce40feb5a7b44 Mon Sep 17 00:00:00 2001 From: rtuschkany <35923908+rtuschkany@users.noreply.github.com> Date: Sat, 19 Sep 2020 02:03:13 +0200 Subject: [PATCH 0204/1476] ConnectAd Update: gvlid, better bidfloor support, transform type for S2S (#5702) * ConnectAd Update: gvlid, better bidfloor support, transform type for S2S * ConnectAd Update: gvlid, better bidfloor support, transform type for S2S --- modules/connectadBidAdapter.js | 16 +++++++++++++--- test/spec/modules/connectadBidAdapter_spec.js | 13 +++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 04459ec1f09..d1811a1b7d1 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -11,6 +11,7 @@ const SUPPORTED_MEDIA_TYPES = [BANNER]; export const spec = { code: BIDDER_CODE, + gvlid: 138, aliases: [ BIDDER_CODE_ALIAS ], supportedMediaTypes: SUPPORTED_MEDIA_TYPES, @@ -81,8 +82,10 @@ export const spec = { pisze: bid.mediaTypes.banner.sizes[0] || bid.sizes[0], sizes: bid.mediaTypes.banner.sizes, adTypes: getSize(bid.mediaTypes.banner.sizes || bid.sizes), - floor: getBidFloor(bid) - }, bid.params); + bidfloor: getBidFloor(bid), + siteId: bid.params.siteId, + networkId: bid.params.networkId + }); if (placement.networkId && placement.siteId) { data.placements.push(placement); @@ -134,6 +137,13 @@ export const spec = { return bidResponses; }, + transformBidParams: function (params, isOpenRtb) { + return utils.convertTypes({ + 'siteId': 'number', + 'networkId': 'number' + }, params); + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { let syncEndpoint = 'https://cdn.connectad.io/connectmyusers.php?'; @@ -227,7 +237,7 @@ function getBidFloor(bidRequest) { }); } - let floor = floorInfo.floor || 0; + let floor = floorInfo.floor || bidRequest.params.bidfloor || bidRequest.params.floorprice || 0; return floor; } diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index aef4fb562a7..dbac3c0dc7c 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -84,7 +84,6 @@ describe('ConnectAd Adapter', function () { } }; const isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); }); @@ -162,13 +161,19 @@ describe('ConnectAd Adapter', function () { bidRequests[0].getFloor = () => floorInfo; const request = spec.buildRequests(bidRequests, bidderRequest); const requestparse = JSON.parse(request.data); - expect(requestparse.placements[0].floor).to.equal(5.20); + expect(requestparse.placements[0].bidfloor).to.equal(5.20); }); - it('should be 0 if no floormodule is available', function() { + it('should be bidfloor if no floormodule is available', function() { const request = spec.buildRequests(bidRequests, bidderRequest); const requestparse = JSON.parse(request.data); - expect(requestparse.placements[0].floor).to.equal(0); + expect(requestparse.placements[0].bidfloor).to.equal(0.50); + }); + + it('should have 0 bidfloor value', function() { + const request = spec.buildRequests(bidRequestsUserIds, bidderRequest); + const requestparse = JSON.parse(request.data); + expect(requestparse.placements[0].bidfloor).to.equal(0); }); it('should contain gdpr info', function () { From 743d6fc4ae8f1928f0a6f98b7cb3956beb0ca030 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Mon, 21 Sep 2020 01:54:24 -0700 Subject: [PATCH 0205/1476] No bid version 1.2.8 (#5630) * Enable supplyChain support * Added support for COPPA * Added support for TCF 2.0 * Fixed the test coverage. * Fixed lint issue. Co-authored-by: Reda Guermas --- modules/nobidBidAdapter.js | 34 ++++++----- test/spec/modules/nobidBidAdapter_spec.js | 71 ++++++++++++++++++++++- 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index f1a9092e31d..00cb14dc01d 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.6'; +window.nobidVersion = '1.2.8'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -24,6 +24,15 @@ function nobidSetCookie(cname, cvalue, hours) { function nobidGetCookie(cname) { return storage.getCookie(cname); } +function nobidHasPurpose1Consent(bidderRequest) { + let result = true; + if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies && bidderRequest.gdprConsent.apiVersion === 2) { + result = !!(utils.deepAccess(bidderRequest.gdprConsent, 'vendorData.purpose.consents.1') === true); + } + } + return result; +} function nobidBuildRequests(bids, bidderRequest) { var serializeState = function(divIds, siteId, adunits) { var filterAdUnitsByIds = function(divIds, adUnits) { @@ -296,19 +305,7 @@ window.addEventListener('message', function (event) { if (window.nobid && window.nobid.bidResponses) { var bid = window.nobid.bidResponses['' + adId]; if (bid && bid.adm2) { - var markup = null; - if (bid.is_combo && bid.adm_combo) { - for (var i in bid.adm_combo) { - var combo = bid.adm_combo[i]; - if (!combo.done) { - markup = combo.adm; - combo.done = true; - break; - } - } - } else { - markup = bid.adm2; - } + var markup = bid.adm2; if (markup) { event.source.postMessage('nbTagRenderer.renderAdInSafeFrame|' + markup, '*'); } @@ -358,11 +355,18 @@ export const spec = { window.nobid.refreshCount++; const payloadString = JSON.stringify(payload).replace(/'|&|#/g, '') const endpoint = buildEndpoint(); + + let options = {}; + if (!nobidHasPurpose1Consent(bidderRequest)) { + options = { withCredentials: false }; + } + return { method: 'POST', url: endpoint, data: payloadString, - bidderRequest + bidderRequest, + options }; }, /** diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 3b8fd32160b..346356e7d5b 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -118,7 +118,7 @@ describe('Nobid Adapter', function () { expect(payload.a).to.exist; expect(payload.t).to.exist; expect(payload.tz).to.exist; - expect(payload.r).to.exist; + expect(payload.r).to.exist.and.to.equal('100x100'); expect(payload.lang).to.exist; expect(payload.ref).to.exist; expect(payload.a[0].d).to.exist.and.to.equal('adunit-code'); @@ -264,6 +264,38 @@ describe('Nobid Adapter', function () { expect(payload.gdpr).to.exist; }); + it('sends bid request to ad size', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a.length).to.exist.and.to.equal(1); + expect(payload.a[0].z[0][0]).to.equal(300); + expect(payload.a[0].z[0][1]).to.equal(250); + }); + + it('sends bid request to div id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].d).to.equal('adunit-code'); + }); + + it('sends bid request to site id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].sid).to.equal(2); + expect(payload.a[0].at).to.equal('banner'); + expect(payload.a[0].params.siteId).to.equal(2); + }); + + it('sends bid request to ad type', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].at).to.equal('banner'); + }); + it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.contain('ads.servenobid.com/adreq'); @@ -291,6 +323,43 @@ describe('Nobid Adapter', function () { expect(payload.gdpr.consentString).to.exist.and.to.equal(consentString); expect(payload.gdpr.consentRequired).to.exist.and.to.be.true; }); + + it('should add gdpr consent information to the request', function () { + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + gdprApplies: false + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consentString).to.not.exist; + expect(payload.gdpr.consentRequired).to.exist.and.to.be.false; + }); + + it('should add usp consent information to the request', function () { + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': '1Y-N' + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.usp).to.exist; + expect(payload.usp).to.exist.and.to.equal('1Y-N'); + }); }); describe('buildRequestsRefreshCount', function () { From f1793d3031f15c7be851e5e12db4e007f71b3903 Mon Sep 17 00:00:00 2001 From: susyt Date: Mon, 21 Sep 2020 05:58:21 -0700 Subject: [PATCH 0206/1476] adds support for zone and pubId params (#5728) --- modules/gumgumBidAdapter.js | 107 ++++++++++++--------- test/spec/modules/gumgumBidAdapter_spec.js | 71 ++++++++++++-- 2 files changed, 123 insertions(+), 55 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index f8e17e0fe71..9714a3eeeca 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -135,7 +135,8 @@ function isBidRequestValid (bid) { params, adUnitCode } = bid; - const id = params.inScreen || params.inScreenPubID || params.inSlot || params.ICV || params.video || params.inVideo; + const legacyParamID = params.inScreen || params.inScreenPubID || params.inSlot || params.ICV || params.video || params.inVideo; + const id = legacyParamID || params.slot || params.native || params.zone || params.pubID; if (invalidRequestIds[id]) { utils.logWarn(`[GumGum] Please check the implementation for ${id} for the placement ${adUnitCode}`); @@ -143,6 +144,8 @@ function isBidRequestValid (bid) { } switch (true) { + case !!(params.zone): break; + case !!(params.pubId): break; case !!(params.inScreen): break; case !!(params.inScreenPubID): break; case !!(params.inSlot): break; @@ -217,8 +220,8 @@ function _getFloor (mediaTypes, bidfloor, bid) { }); if (typeof floorInfo === 'object' && - floorInfo.currency === 'USD' && - !isNaN(parseFloat(floorInfo.floor))) { + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { floor = Math.max(floor, parseFloat(floorInfo.floor)); } } @@ -255,6 +258,7 @@ function buildRequests (validBidRequests, bidderRequest) { sizes = mediaTypes.banner.sizes; } else if (mediaTypes.video) { sizes = mediaTypes.video.playerSize; + data = _getVidParams(mediaTypes.video); } if (pageViewId) { @@ -265,48 +269,27 @@ function buildRequests (validBidRequests, bidderRequest) { data.fp = bidFloor; } - if (params.inScreenPubID) { - data.pubId = params.inScreenPubID; - data.pi = 2; - } - if (params.inScreen) { - data.t = params.inScreen; - data.pi = 2; - } - if (params.inSlot) { - data.si = parseInt(params.inSlot, 10); - // check for sizes and type - if (params.sizes && Array.isArray(params.sizes)) { - const bf = params.sizes.reduce(function(r, i) { - // only push if it's an array of length 2 - if (Array.isArray(i) && i.length === 2) { - r.push(`${i[0]}x${i[1]}`); - } - return r; - }, []); - data.bf = bf.toString(); + if (params.zone) { + data.t = params.zone; + data.pi = 2; // inscreen + // override pi if the following is found + if (params.slot) { + data.si = parseInt(params.slot, 10); + data.pi = 3; + data.bf = sizes.reduce((acc, curSlotDim) => `${acc}${acc && ','}${curSlotDim[0]}x${curSlotDim[1]}`, ''); + } else if (params.native) { + data.ni = parseInt(params.native, 10); + data.pi = 5; + } else if (mediaTypes.video) { + data.pi = mediaTypes.video.linearity === 1 ? 7 : 6; // video : invideo } - data.pi = 3; - } - if (params.ICV) { - data.ni = parseInt(params.ICV, 10); - data.pi = 5; - } - if (params.videoPubID) { - data = Object.assign(data, _getVidParams(mediaTypes.video)); - data.pubId = params.videoPubID; - data.pi = 7; - } - if (params.video) { - data = Object.assign(data, _getVidParams(mediaTypes.video)); - data.t = params.video; - data.pi = 7; - } - if (params.inVideo) { - data = Object.assign(data, _getVidParams(mediaTypes.video)); - data.t = params.inVideo; - data.pi = 6; + } else if (params.pubId) { + data.pubId = params.pubId + data.pi = mediaTypes.video ? 7 : 2; // video : inscreen + } else { // legacy params + data = { ...data, ...handleLegacyParams(params, sizes) } } + if (gdprConsent) { data.gdprApplies = gdprConsent.gdprApplies ? 1 : 0; } @@ -335,6 +318,40 @@ function buildRequests (validBidRequests, bidderRequest) { return bids; } +function handleLegacyParams (params, sizes) { + const data = {}; + if (params.inScreenPubID) { + data.pubId = params.inScreenPubID; + data.pi = 2; + } + if (params.inScreen) { + data.t = params.inScreen; + data.pi = 2; + } + if (params.inSlot) { + data.si = parseInt(params.inSlot, 10); + data.pi = 3; + data.bf = sizes.reduce((acc, curSlotDim) => `${acc}${acc && ','}${curSlotDim[0]}x${curSlotDim[1]}`, ''); + } + if (params.ICV) { + data.ni = parseInt(params.ICV, 10); + data.pi = 5; + } + if (params.videoPubID) { + data.pubId = params.videoPubID; + data.pi = 7; + } + if (params.video) { + data.t = params.video; + data.pi = 7; + } + if (params.inVideo) { + data.t = params.inVideo; + data.pi = 6; + } + return data; +} + /** * Unpack the response from the server into a list of bids. * @@ -347,7 +364,7 @@ function interpretResponse (serverResponse, bidRequest) { if (!serverResponseBody || serverResponseBody.err) { const data = bidRequest.data || {} - const id = data.t || data.si || data.ni || data.pubId; + const id = data.si || data.ni || data.t || data.pubId; const delayTime = serverResponseBody ? serverResponseBody.err.drt : DELAY_REQUEST_TIME; invalidRequestIds[id] = { productId: data.pi, timestamp: new Date().getTime() }; @@ -403,7 +420,7 @@ function interpretResponse (serverResponse, bidRequest) { bidResponses.push({ // dealId: DEAL_ID, // referrer: REFERER, - ...(product === 7 && { vastXml: markup }), + ...(product === 7 && { vastXml: markup, mediaType: VIDEO }), ad: wrapper ? getWrapperCode(wrapper, Object.assign({}, serverResponseBody, { bidRequest })) : markup, ...(product === 6 && {ad: markup}), cpm: isTestUnit ? 0.1 : cpm, diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index cf672d89e22..f24fdc87e45 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -33,7 +33,11 @@ describe('gumgumAdapter', function () { }; it('should return true when required params found', function () { + const zoneBid = { ...bid, params: { 'zone': '123' } }; + const pubIdBid = { ...bid, params: { 'pubId': '123' } }; expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(spec.isBidRequestValid(zoneBid)).to.equal(true); + expect(spec.isBidRequestValid(pubIdBid)).to.equal(true); }); it('should return true when required params found', function () { @@ -139,20 +143,68 @@ describe('gumgumAdapter', function () { } }; + describe('zone param', function () { + const zoneParam = { 'zone': '123a' }; + + it('should set t and pi param', function () { + const request = { ...bidRequests[0], params: zoneParam }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.t).to.equal(zoneParam.zone); + expect(bidRequest.data.pi).to.equal(2); + }); + it('should set the correct pi param if slot param is found', function () { + const request = { ...bidRequests[0], params: { ...zoneParam, 'slot': 1 } }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(3); + }); + it('should set the correct pi param if native param is found', function () { + const request = { ...bidRequests[0], params: { ...zoneParam, 'native': 2 } }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(5); + }); + it('should set the correct pi param for video', function () { + const request = { ...bidRequests[0], params: zoneParam, mediaTypes: vidMediaTypes }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(7); + }); + it('should set the correct pi param for invideo', function () { + const invideo = { video: { ...vidMediaTypes.video, linearity: 2 } }; + const request = { ...bidRequests[0], params: zoneParam, mediaTypes: invideo }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(6); + }); + }); + + describe('pubId zone', function () { + const pubIdParam = { 'pubId': 'abc' }; + + it('should set t param', function () { + const request = { ...bidRequests[0], params: pubIdParam }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pubId).to.equal(pubIdParam.pubId); + }); + + it('should set the correct pi depending on what is found in mediaTypes', function () { + const request = { ...bidRequests[0], params: pubIdParam }; + const bidRequest = spec.buildRequests([request])[0]; + const vidRequest = { ...bidRequests[0], mediaTypes: vidMediaTypes, params: { 'videoPubID': 123 } }; + const vidBidRequest = spec.buildRequests([vidRequest])[0]; + + expect(bidRequest.data.pi).to.equal(2); + expect(vidBidRequest.data.pi).to.equal(7); + }); + }); + it('should return a defined sizes field for video', function () { const request = { ...bidRequests[0], mediaTypes: vidMediaTypes, params: { 'videoPubID': 123 } }; const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.sizes).to.equal(vidMediaTypes.video.playerSize); }); it('should handle multiple sizes for inslot', function () { - const request = Object.assign({}, bidRequests[0]); - delete request.params; - request.params = { - 'inSlot': '123', - 'sizes': [[0, 1], [0, 2]] - }; + const mediaTypes = { banner: { sizes: [[300, 250], [300, 600]] } } + const request = { ...bidRequests[0], mediaTypes }; const bidRequest = spec.buildRequests([request])[0]; - expect(bidRequest.data.bf).to.equal('0x1,0x2'); + expect(bidRequest.data.bf).to.equal('300x250,300x600'); }); describe('floorModule', function () { const floorTestData = { @@ -163,9 +215,8 @@ describe('gumgumAdapter', function () { return floorTestData; }; it('should return the value from getFloor if present', function () { - const request = { ...bidRequests[0] }; - const bidRequest = spec.buildRequests([request])[0]; - expect(bidRequest.data.fp).to.equal(floorTestData.floor); + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data.fp).to.equal(floorTestData.floor); }); it('should return the getFloor.floor value if it is greater than bidfloor', function () { const bidfloor = 0.80; From e7501a92c1b898e39b562e1834101170e4b75a6f Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Mon, 21 Sep 2020 20:37:07 +0700 Subject: [PATCH 0207/1476] Change the data type of gdpr and schain object in Payload (#5770) I update a little bit of data type of object gdpr and object schain in Payload data sent to endpoint. This helps to normalize my endpoint data handling --- modules/quantumdexBidAdapter.js | 4 ++-- test/spec/modules/quantumdexBidAdapter_spec.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/quantumdexBidAdapter.js b/modules/quantumdexBidAdapter.js index 9b7a79755e3..738b6165f79 100644 --- a/modules/quantumdexBidAdapter.js +++ b/modules/quantumdexBidAdapter.js @@ -85,14 +85,14 @@ export const spec = { // Apply GDPR parameters to request. payload.gdpr = {}; if (bidderRequest && bidderRequest.gdprConsent) { - payload.gdpr.gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 'true' : 'false'; + payload.gdpr.gdprApplies = !!bidderRequest.gdprConsent.gdprApplies; if (bidderRequest.gdprConsent.consentString) { payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; } } // Apply schain. if (bids[0].schain) { - payload.schain = JSON.stringify(bids[0].schain) + payload.schain = bids[0].schain } // Apply us_privacy. if (bidderRequest && bidderRequest.uspConsent) { diff --git a/test/spec/modules/quantumdexBidAdapter_spec.js b/test/spec/modules/quantumdexBidAdapter_spec.js index 82429cbedae..d1817493b36 100644 --- a/test/spec/modules/quantumdexBidAdapter_spec.js +++ b/test/spec/modules/quantumdexBidAdapter_spec.js @@ -244,7 +244,7 @@ describe('QuantumdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') expect(bidRequests.method).to.equal('POST') - expect(bidRequests.data.gdpr.gdprApplies).to.equal('true') + expect(bidRequests.data.gdpr.gdprApplies).to.equal(true) expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') }) @@ -253,7 +253,7 @@ describe('QuantumdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') expect(bidRequests.method).to.equal('POST') - expect(bidRequests.data.gdpr.gdprApplies).to.equal('false') + expect(bidRequests.data.gdpr.gdprApplies).to.equal(false) expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') }) it('should return a properly formatted request with GDPR applies set to false with no consent_string param', function () { @@ -273,7 +273,7 @@ describe('QuantumdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') expect(bidRequests.method).to.equal('POST') - expect(bidRequests.data.gdpr.gdprApplies).to.equal('false') + expect(bidRequests.data.gdpr.gdprApplies).to.equal(false) expect(bidRequests.data.gdpr).to.not.include.keys('consentString') }) it('should return a properly formatted request with GDPR applies set to true with no consentString param', function () { @@ -293,12 +293,12 @@ describe('QuantumdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') expect(bidRequests.method).to.equal('POST') - expect(bidRequests.data.gdpr.gdprApplies).to.equal('true') + expect(bidRequests.data.gdpr.gdprApplies).to.equal(true) expect(bidRequests.data.gdpr).to.not.include.keys('consentString') }) it('should return a properly formatted request with schain defined', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); - expect(JSON.parse(bidRequests.data.schain)).to.deep.equal(bidRequest[0].schain) + expect(bidRequests.data.schain).to.deep.equal(bidRequest[0].schain) }); it('should return a properly formatted request with us_privacy included', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); From 8ef4f9d26fd0c134bcc2785d56c56747f9225133 Mon Sep 17 00:00:00 2001 From: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Date: Mon, 21 Sep 2020 08:59:33 -0700 Subject: [PATCH 0208/1476] Update Openx analytics adapter (#5761) * OpenX: track bidRequest.timeout in openxAnalyticsAdapter * OpenX: decode campaign data in openxAnalyticsAdapter --- modules/openxAnalyticsAdapter.js | 11 +++++++---- test/spec/modules/openxAnalyticsAdapter_spec.js | 12 ++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 3ee8ab588a9..07966e9e401 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -170,14 +170,15 @@ function isValidConfig({options: analyticsOptions}) { } function buildCampaignFromUtmCodes() { + const location = utils.getWindowLocation(); + const queryParams = utils.parseQS(location && location.search); let campaign = {}; - let queryParams = utils.parseQS(utils.getWindowLocation() && utils.getWindowLocation().search); UTM_TAGS.forEach(function(utmKey) { let utmValue = queryParams[utmKey]; if (utmValue) { let key = UTM_TO_CAMPAIGN_PROPERTIES[utmKey]; - campaign[key] = utmValue; + campaign[key] = decodeURIComponent(utmValue); } }); return campaign; @@ -326,7 +327,7 @@ function onAuctionInit({auctionId, timestamp: startTime, timeout, adUnitCodes}) * @param {PbBidRequest} bidRequest */ function onBidRequested(bidRequest) { - const {auctionId, bids: bidderRequests, start} = bidRequest; + const {auctionId, bids: bidderRequests, start, timeout} = bidRequest; const auction = auctionMap[auctionId]; const adUnitCodeToAdUnitMap = auction.adUnitCodeToAdUnitMap; @@ -341,6 +342,7 @@ function onBidRequested(bidRequest) { source: src, startTime: start, timedOut: false, + timeLimit: timeout, bids: {} }; }); @@ -640,13 +642,14 @@ function buildAuctionPayload(auction) { function buildBidRequestPayload(bidRequestsMap) { return utils._map(bidRequestsMap, (bidRequest) => { - let {bidder, source, bids, mediaTypes, timedOut} = 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: utils._map(bidRequest.bids, (bidderBidResponse) => { let { diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index be7ae6fcdc4..b946efe922d 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -78,6 +78,7 @@ describe('openx analytics adapter', function() { const bidRequestedOpenX = { auctionId: 'test-auction-id', auctionStart: CURRENT_TIME, + timeout: 2000, bids: [ { adUnitCode: AD_UNIT_CODE, @@ -100,6 +101,7 @@ describe('openx analytics adapter', function() { const bidRequestedCloseX = { auctionId: 'test-auction-id', auctionStart: CURRENT_TIME, + timeout: 1000, bids: [ { adUnitCode: AD_UNIT_CODE, @@ -334,7 +336,7 @@ describe('openx analytics adapter', function() { it('should track values from query params when they exist', function () { sinon.stub(utils, 'getWindowLocation').returns({search: '?' + - 'utm_campaign=test-campaign-name&' + + 'utm_campaign=test%20campaign-name&' + 'utm_source=test-source&' + 'utm_medium=test-medium&' }); @@ -348,7 +350,8 @@ describe('openx analytics adapter', function() { clock.tick(SLOT_LOAD_WAIT_TIME); auction = JSON.parse(server.requests[0].requestBody)[0]; - expect(auction.campaign.name).to.equal('test-campaign-name'); + // 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; @@ -466,6 +469,11 @@ describe('openx analytics adapter', 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 () { From 7c9c60dccb15cf59bd6f4b145280cdce405cc695 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Mon, 21 Sep 2020 13:45:17 -0700 Subject: [PATCH 0209/1476] Only set dimensions if can be resolved (#5769) --- modules/rubiconAnalyticsAdapter.js | 9 ++--- .../modules/rubiconAnalyticsAdapter_spec.js | 35 +++++++++++++++++++ test/spec/modules/rubiconAnalyticsSchema.json | 1 - 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 54aa108ed78..362c56b698a 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -328,10 +328,11 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { 'dealId', 'status', 'mediaType', - 'dimensions', () => utils.pick(bid, [ - 'width', - 'height' - ]), + 'dimensions', () => { + const width = bid.width || bid.playerWidth; + const height = bid.height || bid.playerHeight; + return (width && height) ? {width, height} : undefined; + }, 'seatBidId', 'floorValue', () => utils.deepAccess(bid, 'floorData.floorValue'), 'floorRule', () => utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index a2999dfed8c..344f08823f8 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -687,6 +687,41 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(ANALYTICS_MESSAGE); }); + it('should handle bidResponse dimensions correctly', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + + // mock bid response with playerWidth and playerHeight (NO width and height) + let bidResponse1 = utils.deepClone(MOCK.BID_RESPONSE[0]); + delete bidResponse1.width; + delete bidResponse1.height; + bidResponse1.playerWidth = 640; + bidResponse1.playerHeight = 480; + + // mock bid response with no width height or playerwidth playerheight + let bidResponse2 = utils.deepClone(MOCK.BID_RESPONSE[1]); + delete bidResponse2.width; + delete bidResponse2.height; + delete bidResponse2.playerWidth; + delete bidResponse2.playerHeight; + + events.emit(BID_RESPONSE, bidResponse1); + events.emit(BID_RESPONSE, bidResponse2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.dimensions).to.deep.equal({ + width: 640, + height: 480 + }); + expect(message.auctions[0].adUnits[1].bids[0].bidResponse.dimensions).to.equal(undefined); + }); + function performFloorAuction(provider) { let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); auctionInit.bidderRequests[0].bids[0].floorData = { diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index 407dfd18be3..13d39e46cd0 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -409,7 +409,6 @@ "bidResponse": { "type": "object", "required": [ - "dimensions", "mediaType", "bidPriceUSD" ], From b211409a47410b4b9601f420f5fd524b2a9bedb0 Mon Sep 17 00:00:00 2001 From: logicad Date: Tue, 22 Sep 2020 06:27:47 +0900 Subject: [PATCH 0210/1476] Native support for Logicad adapter (#5742) --- modules/logicadBidAdapter.js | 4 +- modules/logicadBidAdapter.md | 55 ++++++++++--- test/spec/modules/logicadBidAdapter_spec.js | 85 +++++++++++++++++++++ 3 files changed, 131 insertions(+), 13 deletions(-) diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js index 74c00ee39d6..0bf6b886dee 100644 --- a/modules/logicadBidAdapter.js +++ b/modules/logicadBidAdapter.js @@ -1,12 +1,12 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; const BIDDER_CODE = 'logicad'; const ENDPOINT_URL = 'https://pb.ladsp.com/adrequest/prebid'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], isBidRequestValid: function (bid) { return !!(bid.params && bid.params.tid); }, diff --git a/modules/logicadBidAdapter.md b/modules/logicadBidAdapter.md index 32d40a7d3cd..de439f3f8c2 100644 --- a/modules/logicadBidAdapter.md +++ b/modules/logicadBidAdapter.md @@ -11,15 +11,48 @@ Currently module supports only banner mediaType. # Test Parameters ``` - var adUnits = [{ - code: 'test-code', - sizes: [[300, 250],[300, 600]], - bids: [{ - bidder: 'logicad', - params: { - tid: 'test', - page: 'url', - } - }] - }]; +var adUnits = [ + // Banner adUnit + { + code: 'test-code', + sizes: [[300, 250], [300, 600]], + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bids: [{ + bidder: 'logicad', + params: { + tid: 'lfp-test-banner', + page: 'url' + } + }] + }, + // Native adUnit + { + code: 'test-native-code', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + } + }, + bids: [{ + bidder: 'logicad', + params: { + tid: 'lfp-test-native', + page: 'url' + } + }] + } +]; ``` diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js index eddcaecac7b..c4c06630a2b 100644 --- a/test/spec/modules/logicadBidAdapter_spec.js +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -21,6 +21,32 @@ describe('LogicadAdapter', function () { } } }]; + const nativeBidRequests = [{ + bidder: 'logicad', + bidId: '51ef8751f9aead', + params: { + tid: 'bgjD1', + page: 'https://www.logicad.com/' + }, + adUnitCode: 'div-gpt-ad-1460505748561-1', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + sizes: [[1, 1]], + bidderRequestId: '418b37f85e772c', + auctionId: '18fd8b8b0bd757', + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + } + } + }]; const bidderRequest = { refererInfo: { referer: 'fakeReferer', @@ -52,6 +78,40 @@ describe('LogicadAdapter', function () { } } }; + const nativeServerResponse = { + body: { + seatbid: + [{ + bid: { + requestId: '51ef8751f9aead', + cpm: 101.0234, + width: 1, + height: 1, + creativeId: '2019', + currency: 'JPY', + netRevenue: true, + ttl: 60, + native: { + clickUrl: 'https://www.logicad.com', + image: { + url: 'https://cd.ladsp.com/img.png', + width: '1200', + height: '628' + }, + impressionTrackers: [ + 'https://example.com' + ], + sponsoredBy: 'Logicad', + title: 'Native Creative', + } + } + }], + userSync: { + type: 'image', + url: 'https://cr-p31.ladsp.jp/cookiesender/31' + } + } + }; describe('isBidRequestValid', function () { it('should return true if the tid parameter is present', function () { @@ -69,6 +129,10 @@ describe('LogicadAdapter', function () { delete bidRequest[0].params; expect(spec.isBidRequestValid(bidRequest)).to.be.false; }); + + it('should return true if the tid parameter is present for native request', function () { + expect(spec.isBidRequestValid(nativeBidRequests[0])).to.be.true; + }); }); describe('buildRequests', function () { @@ -101,6 +165,27 @@ describe('LogicadAdapter', function () { expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.seatbid[0].bid.netRevenue); expect(interpretedResponse[0].ad).to.equal(serverResponse.body.seatbid[0].bid.ad); expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.seatbid[0].bid.ttl); + + // native + const nativeRequest = spec.buildRequests(nativeBidRequests, bidderRequest)[0]; + const interpretedResponseForNative = spec.interpretResponse(nativeServerResponse, nativeRequest); + + expect(interpretedResponseForNative).to.have.lengthOf(1); + + expect(interpretedResponseForNative[0].requestId).to.equal(nativeServerResponse.body.seatbid[0].bid.requestId); + expect(interpretedResponseForNative[0].cpm).to.equal(nativeServerResponse.body.seatbid[0].bid.cpm); + expect(interpretedResponseForNative[0].width).to.equal(nativeServerResponse.body.seatbid[0].bid.width); + expect(interpretedResponseForNative[0].height).to.equal(nativeServerResponse.body.seatbid[0].bid.height); + expect(interpretedResponseForNative[0].creativeId).to.equal(nativeServerResponse.body.seatbid[0].bid.creativeId); + expect(interpretedResponseForNative[0].currency).to.equal(nativeServerResponse.body.seatbid[0].bid.currency); + expect(interpretedResponseForNative[0].netRevenue).to.equal(nativeServerResponse.body.seatbid[0].bid.netRevenue); + expect(interpretedResponseForNative[0].ttl).to.equal(nativeServerResponse.body.seatbid[0].bid.ttl); + expect(interpretedResponseForNative[0].native.clickUrl).to.equal(nativeServerResponse.body.seatbid[0].bid.native.clickUrl); + expect(interpretedResponseForNative[0].native.image.url).to.equal(nativeServerResponse.body.seatbid[0].bid.native.image.url); + expect(interpretedResponseForNative[0].native.image.width).to.equal(nativeServerResponse.body.seatbid[0].bid.native.image.width); + expect(interpretedResponseForNative[0].native.impressionTrackers).to.equal(nativeServerResponse.body.seatbid[0].bid.native.impressionTrackers); + expect(interpretedResponseForNative[0].native.sponsoredBy).to.equal(nativeServerResponse.body.seatbid[0].bid.native.sponsoredBy); + expect(interpretedResponseForNative[0].native.title).to.equal(nativeServerResponse.body.seatbid[0].bid.native.title); }); }); From 9297612ae4eb7e4e7f4c43f9606ec7d248f4c564 Mon Sep 17 00:00:00 2001 From: Nick Duitz <42961155+nduitz@users.noreply.github.com> Date: Tue, 22 Sep 2020 12:17:36 +0200 Subject: [PATCH 0211/1476] welect: update parameters to match backend specs of tcf2.0 (#5613) * update parameters to match backend specs of tcf2.0 * add gvlid to spec --- modules/welectBidAdapter.js | 37 ++++++++++++---------- test/spec/modules/welectBidAdapter_spec.js | 8 ++--- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js index 3913cfde6a0..f9fc57a4834 100644 --- a/modules/welectBidAdapter.js +++ b/modules/welectBidAdapter.js @@ -7,6 +7,7 @@ const DEFAULT_DOMAIN = 'www.welect.de'; export const spec = { code: BIDDER_CODE, aliases: ['wlt'], + gvlid: 282, supportedMediaTypes: ['video'], // short code @@ -17,7 +18,10 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - return utils.deepAccess(bid, 'mediaTypes.video.context') === 'instream' && !!(bid.params.placementId); + return ( + utils.deepAccess(bid, 'mediaTypes.video.context') === 'instream' && + !!bid.params.placementId + ); }, /** * Make a server request from the list of BidRequests. @@ -26,9 +30,11 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests) { - return validBidRequests.map(bidRequest => { - let rawSizes = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize') || bidRequest.sizes; - let size = rawSizes[0] + return validBidRequests.map((bidRequest) => { + let rawSizes = + utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize') || + bidRequest.sizes; + let size = rawSizes[0]; let domain = bidRequest.params.domain || DEFAULT_DOMAIN; @@ -38,20 +44,19 @@ export const spec = { if (bidRequest && bidRequest.gdprConsent) { gdprConsent = { - gdpr_consent: - { - gdpr_applies: bidRequest.gdprConsent.gdprApplies, - gdpr_consent: bidRequest.gdprConsent.gdprConsent - } - } + gdpr_consent: { + gdprApplies: bidRequest.gdprConsent.gdprApplies, + tcString: bidRequest.gdprConsent.gdprConsent, + }, + }; } const data = { width: size[0], height: size[1], bid_id: bidRequest.bidId, - ...gdprConsent - } + ...gdprConsent, + }; return { method: 'POST', @@ -61,8 +66,8 @@ export const spec = { contentType: 'application/json', withCredentials: false, crossOrigin: true, - } - } + }, + }; }); }, /** @@ -82,6 +87,6 @@ export const spec = { const bidResponse = responseBody.bidResponse; bidResponses.push(bidResponse); return bidResponses; - } -} + }, +}; registerBidder(spec); diff --git a/test/spec/modules/welectBidAdapter_spec.js b/test/spec/modules/welectBidAdapter_spec.js index 2b929be5586..5f047904c76 100644 --- a/test/spec/modules/welectBidAdapter_spec.js +++ b/test/spec/modules/welectBidAdapter_spec.js @@ -95,8 +95,8 @@ describe('WelectAdapter', function () { width: 640, height: 360, gdpr_consent: { - gdpr_applies: 1, - gdpr_consent: 'some_string' + gdprApplies: 1, + tcString: 'some_string' } } @@ -166,8 +166,8 @@ describe('WelectAdapter', function () { width: 640, height: 320, gdpr_consent: { - gdpr_applies: 1, - gdpr_consent: 'some_string' + gdprApplies: 1, + tcString: 'some_string' } }, method: 'POST', From 52ff5e6dbc107decc6b6a8ec78d30d8cd745a302 Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Tue, 22 Sep 2020 23:44:46 +0800 Subject: [PATCH 0212/1476] Initial checkin jixie adapter files (#5751) * Initial checkin jixie adapter files * Update jixieBidAdapter.js as per suggestion by reviewers, for cookie access, using instead the storagemanager from prebid. Will also fix up the unit test too. * Update jixieBidAdapter_spec.js as per suggestion by reviewers, for cookie access, using instead the storagemanager from prebid. the adaptor was updated and now this unit test also updated. --- modules/jixieBidAdapter.js | 254 +++++++++ modules/jixieBidAdapter.md | 73 +++ test/spec/modules/jixieBidAdapter_spec.js | 597 ++++++++++++++++++++++ 3 files changed, 924 insertions(+) create mode 100644 modules/jixieBidAdapter.js create mode 100644 modules/jixieBidAdapter.md create mode 100644 test/spec/modules/jixieBidAdapter_spec.js diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js new file mode 100644 index 00000000000..d3ae090964c --- /dev/null +++ b/modules/jixieBidAdapter.js @@ -0,0 +1,254 @@ +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { Renderer } from '../src/Renderer.js'; +export const storage = getStorageManager(); + +const BIDDER_CODE = 'jixie'; +const EVENTS_URL = 'https://jxhbtrackers.azurewebsites.net/sync/evt?'; +const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.io/jxhboutstream.js'; +const REQUESTS_URL = 'https://hb.jixie.io/v2/hbpost'; +const sidTTLMins_ = 30; + +/** + * Own miscellaneous support functions: + */ + +function setIds_(clientId, sessionId) { + let dd = null; + try { + dd = window.location.hostname.match(/[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$/mg); + } catch (err1) {} + try { + 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('_jxs', sessionId, expS, 'None', null); + storage.setCookie('_jxs', sessionId, expS, 'None', dd); + + storage.setDataInLocalStorage('_jx', clientId); + storage.setDataInLocalStorage('_jxs', sessionId); + } catch (error) {} +} + +function fetchIds_() { + let ret = { + client_id_c: '', + client_id_ls: '', + session_id_c: '', + session_id_ls: '' + }; + try { + let tmp = storage.getCookie('_jx'); + if (tmp) ret.client_id_c = tmp; + tmp = storage.getCookie('_jxs'); + if (tmp) ret.session_id_c = tmp; + + tmp = storage.getDataFromLocalStorage('_jx'); + if (tmp) ret.client_id_ls = tmp; + tmp = storage.getDataFromLocalStorage('_jxs'); + if (tmp) ret.session_id_ls = tmp; + } catch (error) {} + return ret; +} + +function getDevice_() { + return ((/(ios|ipod|ipad|iphone|android|blackberry|iemobile|opera mini|webos)/i).test(navigator.userAgent) + ? 'mobile' : 'desktop'); +} + +function pingTracking_(endpointOverride, qpobj) { + internal.ajax((endpointOverride || EVENTS_URL), null, qpobj, { + withCredentials: true, + method: 'GET', + crossOrigin: true + }); +} + +function jxOutstreamRender_(bidAd) { + bidAd.renderer.push(() => { + window.JixieOutstreamVideo.init({ + sizes: [bidAd.width, bidAd.height], + width: bidAd.width, + height: bidAd.height, + targetId: bidAd.adUnitCode, + adResponse: bidAd.adResponse + }); + }); +} + +function createRenderer_(bidAd, scriptUrl, createFcn) { + const renderer = Renderer.install({ + id: bidAd.adUnitCode, + url: scriptUrl, + loaded: false, + config: {'player_height': bidAd.height, 'player_width': bidAd.width}, + adUnitCode: bidAd.adUnitCode + }); + try { + renderer.setRender(createFcn); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + return renderer; +} + +function getMiscDims_() { + let ret = { + pageurl: '', + domain: '', + device: 'unknown' + } + try { + let refererInfo_ = getRefererInfo(); + let url_ = ((refererInfo_ && refererInfo_.referer) ? refererInfo_.referer : window.location.href); + ret.pageurl = url_; + ret.domain = utils.parseUrl(url_).host; + ret.device = getDevice_(); + } catch (error) {} + return ret; +} + +// easier for replacement in the unit test +export const internal = { + getDevice: getDevice_, + getRefererInfo: getRefererInfo, + ajax: ajax, + getMiscDims: getMiscDims_ +}; + +export const spec = { + code: BIDDER_CODE, + EVENTS_URL: EVENTS_URL, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid: function(bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + if (typeof bid.params.unit === 'undefined') { + return false; + } + return true; + }, + buildRequests: function(validBidRequests, bidderRequest) { + const currencyObj = config.getConfig('currency'); + const currency = (currencyObj && currencyObj.adServerCurrency) || 'USD'; + + let bids = []; + validBidRequests.forEach(function(one) { + bids.push({ + bidId: one.bidId, + adUnitCode: one.adUnitCode, + mediaTypes: (one.mediaTypes === 'undefined' ? {} : one.mediaTypes), + sizes: (one.sizes === 'undefined' ? [] : one.sizes), + params: one.params, + }); + }); + + let jixieCfgBlob = config.getConfig('jixie'); + if (!jixieCfgBlob) { + jixieCfgBlob = {}; + } + + let ids = fetchIds_(); + let miscDims = internal.getMiscDims(); + let transformedParams = Object.assign({}, { + auctionid: bidderRequest.auctionId, + timeout: bidderRequest.timeout, + currency: currency, + timestamp: (new Date()).getTime(), + device: miscDims.device, + domain: miscDims.domain, + pageurl: miscDims.pageurl, + bids: bids, + cfg: jixieCfgBlob + }, ids); + return Object.assign({}, { + method: 'POST', + url: REQUESTS_URL, + data: JSON.stringify(transformedParams), + currency: currency + }); + }, + + onTimeout: function(timeoutData) { + let jxCfgBlob = config.getConfig('jixie'); + if (jxCfgBlob && jxCfgBlob.onTimeout == 'off') { + return; + } + let url = null;// default + if (jxCfgBlob && jxCfgBlob.onTimeoutUrl && typeof jxCfgBlob.onTimeoutUrl == 'string') { + url = jxCfgBlob.onTimeoutUrl; + } + let miscDims = internal.getMiscDims(); + pingTracking_(url, // no overriding ping URL . just use default + { + action: 'hbtimeout', + device: miscDims.device, + pageurl: encodeURIComponent(miscDims.pageurl), + domain: encodeURIComponent(miscDims.domain), + auctionid: utils.deepAccess(timeoutData, '0.auctionId'), + timeout: utils.deepAccess(timeoutData, '0.timeout'), + count: timeoutData.length + }); + }, + + onBidWon: function(bid) { + if (bid.notrack) { + return; + } + if (bid.trackingUrl) { + pingTracking_(bid.trackingUrl, {}); + } else { + let miscDims = internal.getMiscDims(); + pingTracking_((bid.trackingUrlBase ? bid.trackingUrlBase : null), { + action: 'hbbidwon', + device: miscDims.device, + pageurl: encodeURIComponent(miscDims.pageurl), + domain: encodeURIComponent(miscDims.domain), + cid: bid.cid, + cpid: bid.cpid, + jxbidid: bid.jxBidId, + auctionid: bid.auctionId, + cpm: bid.cpm, + requestid: bid.requestId + }); + } + }, + + interpretResponse: function(response, bidRequest) { + if (response && response.body && utils.isArray(response.body.bids)) { + const bidResponses = []; + response.body.bids.forEach(function(oneBid) { + let bnd = {}; + + Object.assign(bnd, oneBid); + if (oneBid.osplayer) { + bnd.adResponse = { + content: oneBid.vastXml, + parameters: oneBid.osparams, + height: oneBid.height, + width: oneBid.width + }; + let rendererScript = (oneBid.osparams.script ? oneBid.osparams.script : JX_OUTSTREAM_RENDERER_URL); + bnd.renderer = createRenderer_(oneBid, rendererScript, jxOutstreamRender_); + } + bidResponses.push(bnd); + }); + if (response.body.setids) { + setIds_(response.body.setids.client_id, + response.body.setids.session_id); + } + return bidResponses; + } else { return []; } + } +} + +registerBidder(spec); diff --git a/modules/jixieBidAdapter.md b/modules/jixieBidAdapter.md new file mode 100644 index 00000000000..d9c1f19541d --- /dev/null +++ b/modules/jixieBidAdapter.md @@ -0,0 +1,73 @@ +# Overview + +Module Name: Jixie Bidder Adapter +Module Type: Bidder Adapter +Maintainer: contact@jixie.io + +# Description + +Module that connects to Jixie demand source to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'demoslot1-div', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [ + { + bidder: 'jixie', + params: { + unit: "prebidsampleunit" + } + } + ] + }, + { + code: 'demoslot2-div', + sizes: [640, 360], + mediaTypes: { + video: { + playerSize: [ + [640, 360] + ], + context: "outstream" + } + }, + bids: [ + { + bidder: 'jixie', + params: { + unit: "prebidsampleunit" + } + } + ] + }, + { + code: 'demoslot3-div', + sizes: [640, 360], + mediaTypes: { + video: { + playerSize: [ + [640, 360] + ], + context: "instream" + } + }, + bids: [ + { + bidder: 'jixie', + params: { + unit: "prebidsampleunit" + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js new file mode 100644 index 00000000000..842f9e0ed30 --- /dev/null +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -0,0 +1,597 @@ +import { expect } from 'chai'; +import { spec, internal as jixieaux, storage } from 'modules/jixieBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; + +describe('jixie Adapter', function () { + const pageurl_ = 'https://testdomain.com/testpage.html'; + const domain_ = 'https://testdomain.com'; + const device_ = 'desktop'; + const timeout_ = 1000; + const currency_ = 'USD'; + + /** + * Basic + */ + 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'); + }); + }); + + /** + * isBidRequestValid + */ + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'jixie', + 'params': { + 'unit': 'prebidsampleunit' + }, + '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 required params obj does not exist', function () { + let bid0 = Object.assign({}, bid); + delete bid0.params; + expect(spec.isBidRequestValid(bid0)).to.equal(false); + }); + + it('should return false when params obj does not contain unit property', function () { + let bid1 = Object.assign({}, bid); + bid1.params = { rubbish: '' }; + expect(spec.isBidRequestValid(bid1)).to.equal(false); + }); + });// describe + + /** + * buildRequests + */ + describe('buildRequests', function () { + const adUnitCode0_ = 'adunit-code-0'; + const adUnitCode1_ = 'adunit-code-1'; + const adUnitCode2_ = 'adunit-code-2'; + + const bidId0_ = '22a9eb5004cf082'; + const bidId1_ = '230fceb12fd754f'; + const bidId2_ = '24dbe5c4fb80ed8'; + + const bidderRequestId_ = '2131ce076eeaa1b'; + const auctionId_ = '26d68819-d6ce-4a2c-a4d3-f1a97b159d66'; + + const clientIdTest1_ = '1aba6a40-f711-11e9-868c-53a2ae972xxx'; + const sessionIdTest1_ = '1594782644-1aba6a40-f711-11e9-868c-53a2ae972xxx'; + + // to serve as the object that prebid will call jixie buildRequest with: (param2) + const bidderRequest_ = { + refererInfo: {referer: pageurl_}, + auctionId: auctionId_, + timeout: timeout_ + }; + // to serve as the object that prebid will call jixie buildRequest with: (param1) + let bidRequests_ = [ + { + 'bidder': 'jixie', + 'params': { + 'unit': 'prebidsampleunit' + }, + 'sizes': [[300, 250], [300, 600]], + 'adUnitCode': adUnitCode0_, + 'bidId': bidId0_, + 'bidderRequestId': bidderRequestId_, + 'auctionId': auctionId_ + }, + { + 'bidder': 'jixie', + 'params': { + 'unit': 'prebidsampleunit' + }, + 'sizes': [[300, 250]], + 'mediaTypes': { + 'video': { + 'playerSize': [640, 360] + }, + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': adUnitCode1_, + 'bidId': bidId1_, + 'bidderRequestId': bidderRequestId_, + 'auctionId': auctionId_ + }, + { + 'bidder': 'jixie', + 'params': { + 'unit': 'prebidsampleunit' + }, + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'video': { + 'playerSize': [640, 360] + }, + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'adUnitCode': adUnitCode2_, + 'bidId': bidId2_, + 'bidderRequestId': bidderRequestId_, + 'auctionId': auctionId_ + } + ]; + + // To serve as a reference to check against the bids array portion of the blob that the call to + // buildRequest returns + const refBids_ = [ + { + 'bidId': bidId0_, + 'adUnitCode': adUnitCode0_, + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'unit': 'prebidsampleunit' + } + }, + { + 'bidId': bidId1_, + 'adUnitCode': adUnitCode1_, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 360] + }, + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'sizes': [[300, 250]], + 'params': { + 'unit': 'prebidsampleunit' + } + }, + { + 'bidId': bidId2_, + 'adUnitCode': adUnitCode2_, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 360] + }, + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'unit': 'prebidsampleunit' + } + } + ]; + + it('should attach valid params to the adserver endpoint (1)', function () { + // this one we do not intercept the cookie stuff so really don't know + // what will be in there. so we do not check here (using expect) + // The next next below we check + const request = spec.buildRequests(bidRequests_, bidderRequest_); + it('sends bid request to ENDPOINT via POST', function () { + expect(request.method).to.equal('POST') + }) + expect(request.data).to.be.an('string'); + const payload = JSON.parse(request.data); + expect(payload).to.have.property('auctionid', auctionId_); + expect(payload).to.have.property('timeout', timeout_); + expect(payload).to.have.property('currency', currency_); + expect(payload).to.have.property('bids').that.deep.equals(refBids_); + });// it + + it('should attach valid params to the adserver endpoint (2)', function () { + // similar to above test case but here we force some clientid sessionid values + // and domain, pageurl + // get the interceptors ready: + let getCookieStub = sinon.stub(storage, 'getCookie'); + let getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + getCookieStub + .withArgs('_jx') + .returns(clientIdTest1_); + getCookieStub + .withArgs('_jxs') + .returns(sessionIdTest1_); + getLocalStorageStub + .withArgs('_jx') + .returns(clientIdTest1_); + getLocalStorageStub + .withArgs('_jxs') + .returns(sessionIdTest1_ + ); + let miscDimsStub = sinon.stub(jixieaux, 'getMiscDims'); + miscDimsStub + .returns({ device: device_, pageurl: pageurl_, domain: domain_ }); + + // actual api call: + const request = spec.buildRequests(bidRequests_, bidderRequest_); + it('sends bid request to ENDPOINT via POST', function () { + expect(request.method).to.equal('POST') + }) + expect(request.data).to.be.an('string'); + const payload = JSON.parse(request.data); + expect(payload).to.have.property('auctionid', auctionId_); + expect(payload).to.have.property('client_id_c', clientIdTest1_); + 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('device', device_); + expect(payload).to.have.property('domain', domain_); + expect(payload).to.have.property('pageurl', pageurl_); + expect(payload).to.have.property('timeout', timeout_); + expect(payload).to.have.property('currency', currency_); + expect(payload).to.have.property('bids').that.deep.equals(refBids_); + + // unwire interceptors + getCookieStub.restore(); + getLocalStorageStub.restore(); + miscDimsStub.restore(); + });// it + });// describe + + /** + * interpretResponse: + */ + const JX_OTHER_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.io/dummyscript.js'; + const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.io/jxhboutstream.js'; + + const mockVastXml_ = `JXADSERVERAlway%20Live%20Prebid%20CreativeHybrid in-stream00:00:10`; + const responseBody_ = { + 'bids': [ + // video (vast tag url) returned here + { + 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', + 'jxBidId': '62847e4c696edcb-028d5dee-2c83-44e3-bed1-b75002475cdf', + 'requestId': '62847e4c696edcb', + 'cpm': 2.19, + 'width': 640, + 'height': 360, + 'ttl': 300, + 'adUnitCode': 'demoslot3-div', + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': 'jixie522', + 'meta': { + 'networkId': 123, + 'networkName': 'network123', + 'agencyId': 123, + 'agencyName': 'agency123', + 'advertiserId': 123, + 'advertiserName': 'advertiser123', + 'brandId': 123, + 'brandName': 'brand123', + 'primaryCatId': 1, + 'secondaryCatIds': [ + 2, + 3, + 4 + ], + 'mediaType': 'VIDEO' + }, + 'vastUrl': 'https://ad.jixie.io/v1/video?creativeid=522' + }, + // display ad returned here: + { + 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', + 'jxBidId': '600c9ae6fda1acb-028d5dee-2c83-44e3-bed1-b75002475cdf', + 'requestId': '600c9ae6fda1acb', + 'cpm': 1.999, + 'width': 300, + 'height': 250, + 'ttl': 300, + 'adUnitCode': 'demoslot1-div', + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': 'jixie520', + 'meta': { + 'networkId': 123, + 'networkName': 'network123', + 'agencyId': 123, + 'agencyName': 'agency123', + 'advertiserId': 123, + 'advertiserName': 'advertiser123', + 'advertiserDomains': [ + 'advdom1', + 'advdom2', + 'advdom3' + ], + 'brandId': 123, + 'brandName': 'brand123', + 'primaryCatId': 1, + 'secondaryCatIds': [ + 2, + 3, + 4 + ], + 'mediaType': 'BANNER' + }, + 'ad': '
' + }, + // outstream, jx non-default renderer specified: + { + 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', + 'jxBidId': '99bc539c81b00ce-028d5dee-2c83-44e3-bed1-b75002475cdf', + 'requestId': '99bc539c81b00ce', + 'cpm': 2.99, + 'width': 640, + 'height': 360, + 'ttl': 300, + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': 'jixie521', + 'adUnitCode': 'demoslot4-div', + 'osplayer': 'jixie', + 'osparams': { + 'script': JX_OTHER_OUTSTREAM_RENDERER_URL + }, + 'vastXml': mockVastXml_ + }, + // outstream, jx default renderer: + { + 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', + 'jxBidId': '61bc539c81b00ce-028d5dee-2c83-44e3-bed1-b75002475cdf', + 'requestId': '61bc539c81b00ce', + 'cpm': 1.99, + 'width': 640, + 'height': 360, + 'ttl': 300, + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': 'jixie521', + 'meta': { + 'networkId': 123, + 'networkName': 'network123', + 'agencyId': 123, + 'agencyName': 'agency123', + 'advertiserId': 123, + 'advertiserName': 'advertiser123', + 'brandId': 123, + 'brandName': 'brand123', + 'primaryCatId': 1, + 'secondaryCatIds': [ + 2, + 3, + 4 + ], + 'mediaType': 'VIDEO' + }, + 'adUnitCode': 'demoslot2-div', + 'osplayer': 'jixie', + 'osparams': {}, + 'vastXml': mockVastXml_ + } + ], + 'setids': { + 'client_id': '43aacc10-f643-11ea-8a10-c5fe2d394e7e', + 'session_id': '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e' + }, + }; + const requestObj_ = + { + 'method': 'POST', + 'url': 'http://localhost:8080/v2/hbpost', + 'data': '{"auctionid":"028d5dee-2c83-44e3-bed1-b75002475cdf","timeout":1000,"currency":"USD","timestamp":1600057934665,"device":"desktop","domain":"mock.com","pageurl":"https://mock.com/tests/jxprebidtest_pbjs.html","bids":[{"bidId":"600c9ae6fda1acb","adUnitCode":"demoslot1-div","mediaTypes":{"banner":{"sizes":[[300,250],[300,600],[728,90]]}},"params":{"unit":"prebidsampleunit"}},{"bidId":"61bc539c81b00ce","adUnitCode":"demoslot2-div","mediaTypes":{"video":{"playerSize":[[640,360]],"context":"outstream"}},"params":{"unit":"prebidsampleunit"}},{"bidId":"99bc539c81b00ce","adUnitCode":"demoslot4-div","mediaTypes":{"video":{"playerSize":[[640,360]],"context":"outstream"}},"params":{"unit":"prebidsampleunit"}},{"bidId":"62847e4c696edcb","adUnitCode":"demoslot3-div","mediaTypes":{"video":{"playerSize":[[640,360]],"context":"instream"}},"params":{"unit":"prebidsampleunit"}},{"bidId":"6360235ab01d2cd","adUnitCode":"woo-div","mediaTypes":{"video":{"context":"outstream","playerSize":[[640,360]]}},"params":{"unit":"80b76fc951e161d7c019d21b6639e408"}},{"bidId":"64d9724c7a5e512","adUnitCode":"test-div","mediaTypes":{"video":{"context":"outstream","playerSize":[[300,250]]}},"params":{"unit":"80b76fc951e161d7c019d21b6639e408"}},{"bidId":"65bea7e80fed44b","adUnitCode":"test-div","mediaTypes":{"banner":{"sizes":[[300,250],[300,600],[728,90]]}},"params":{"unit":"7854f723e932b951b6c51fc80b23a410"}},{"bidId":"6642054c4ba1b7f","adUnitCode":"div-banner-native-1","mediaTypes":{"banner":{"sizes":[[640,360]]},"video":{"context":"outstream","sizes":[[640,361]],"playerSize":[[640,360]]},"native":{"type":"image"}},"params":{"unit":"632e7695f0910ce0fa74c19859060a04"}},{"bidId":"675ecf4b44db228","adUnitCode":"div-banner-native-2","mediaTypes":{"banner":{"sizes":[[300,250]]},"native":{"title":{"required":true},"image":{"required":true},"sponsoredBy":{"required":true}}},"params":{"unit":"1000008-b1Q2UMQfZx"}},{"bidId":"68f2dbf5dc23f94","adUnitCode":"div-Top-MediumRectangle","mediaTypes":{"banner":{"sizes":[[300,250],[300,100],[320,50]]}},"params":{"unit":"1000008-b1Q2UMQfZx"}},{"bidId":"6991cf107bb7f1a","adUnitCode":"div-Middle-MediumRectangle","mediaTypes":{"banner":{"sizes":[[300,250],[300,100],[320,50]]}},"params":{"unit":"1000008-b1Q2UMQfZx"}},{"bidId":"706be1b011eac83","adUnitCode":"div-Inside-MediumRectangle","mediaTypes":{"banner":{"sizes":[[300,600],[300,250],[300,100],[320,480]]}},"params":{"unit":"1000008-b1Q2UMQfZx"}}],"client_id_c":"ebd0dea0-f5c8-11ea-a2c7-a5b37aa7fe95","client_id_ls":"ebd0dea0-f5c8-11ea-a2c7-a5b37aa7fe95","session_id_c":"","session_id_ls":"1600005388-ebd0dea0-f5c8-11ea-a2c7-a5b37aa7fe95"}', + 'currency': 'USD' + }; + + describe('interpretResponse', function () { + it('handles nobid responses', function () { + expect(spec.interpretResponse({body: {}}, {validBidRequests: []}).length).to.equal(0) + expect(spec.interpretResponse({body: []}, {validBidRequests: []}).length).to.equal(0) + }); + + it('should get correct bid response', 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); + + // video ad with vastUrl returned by adserver + expect(result[0].requestId).to.equal('62847e4c696edcb') + expect(result[0].cpm).to.equal(2.19) + expect(result[0].width).to.equal(640) + expect(result[0].height).to.equal(360) + expect(result[0].creativeId).to.equal('jixie522') + expect(result[0].currency).to.equal('USD') + expect(result[0].netRevenue).to.equal(true) + expect(result[0].ttl).to.equal(300) + expect(result[0].vastUrl).to.include('https://ad.jixie.io/v1/video?creativeid=') + expect(result[0].trackingUrlBase).to.include('sync') + + // display ad + expect(result[1].requestId).to.equal('600c9ae6fda1acb') + expect(result[1].cpm).to.equal(1.999) + expect(result[1].width).to.equal(300) + expect(result[1].height).to.equal(250) + expect(result[1].creativeId).to.equal('jixie520') + expect(result[1].currency).to.equal('USD') + expect(result[1].netRevenue).to.equal(true) + expect(result[1].ttl).to.equal(300) + expect(result[1].ad).to.include('jxoutstream') + expect(result[1].trackingUrlBase).to.include('sync') + + // should pick up about using alternative outstream renderer + expect(result[2].requestId).to.equal('99bc539c81b00ce') + expect(result[2].cpm).to.equal(2.99) + expect(result[2].width).to.equal(640) + expect(result[2].height).to.equal(360) + expect(result[2].creativeId).to.equal('jixie521') + expect(result[2].currency).to.equal('USD') + expect(result[2].netRevenue).to.equal(true) + expect(result[2].ttl).to.equal(300) + expect(result[2].vastXml).to.include('') + expect(result[2].trackingUrlBase).to.include('sync'); + expect(result[2].renderer.id).to.equal('demoslot4-div') + expect(result[2].renderer.url).to.equal(JX_OTHER_OUTSTREAM_RENDERER_URL); + + // should know to use default outstream renderer + expect(result[3].requestId).to.equal('61bc539c81b00ce') + expect(result[3].cpm).to.equal(1.99) + expect(result[3].width).to.equal(640) + expect(result[3].height).to.equal(360) + expect(result[3].creativeId).to.equal('jixie521') + expect(result[3].currency).to.equal('USD') + expect(result[3].netRevenue).to.equal(true) + expect(result[3].ttl).to.equal(300) + expect(result[3].vastXml).to.include('') + expect(result[3].trackingUrlBase).to.include('sync'); + expect(result[3].renderer.id).to.equal('demoslot2-div') + expect(result[3].renderer.url).to.equal(JX_OUTSTREAM_RENDERER_URL) + + setLocalStorageSpy.restore(); + setCookieSpy.restore(); + });// it + });// describe + + /** + * onBidWon + */ + describe('onBidWon', function() { + let ajaxStub; + let miscDimsStub; + beforeEach(function() { + miscDimsStub = sinon.stub(jixieaux, 'getMiscDims'); + ajaxStub = sinon.stub(jixieaux, 'ajax'); + + miscDimsStub + .returns({ device: device_, pageurl: pageurl_, domain: domain_ }); + }) + + afterEach(function() { + miscDimsStub.restore(); + ajaxStub.restore(); + }) + + let TRACKINGURL_ = 'https://abc.com/sync?action=bidwon'; + + it('Should fire if the adserver trackingUrl flag says so', function() { + spec.onBidWon({ trackingUrl: TRACKINGURL_ }) + expect(jixieaux.ajax.calledWith(TRACKINGURL_)).to.equal(true); + }) + + it('Should not fire if the adserver response indicates no firing', function() { + let called = false; + ajaxStub.callsFake(function fakeFn() { + called = true; + }); + spec.onBidWon({ notrack: 1 }) + expect(called).to.equal(false); + }); + + // A reference to check again: + const QPARAMS_ = { + action: 'hbbidwon', + device: device_, + pageurl: encodeURIComponent(pageurl_), + domain: encodeURIComponent(domain_), + cid: 121, + cpid: 99, + jxbidid: '62847e4c696edcb-028d5dee-2c83-44e3-bed1-b75002475cdf', + auctionid: '028d5dee-2c83-44e3-bed1-b75002475cdf', + cpm: 1.11, + requestid: '62847e4c696edcb' + }; + + it('check it is sending the correct ajax url and qparameters', function() { + spec.onBidWon({ + trackingUrlBase: 'https://mytracker.com/sync?', + cid: 121, + cpid: 99, + jxBidId: '62847e4c696edcb-028d5dee-2c83-44e3-bed1-b75002475cdf', + auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', + cpm: 1.11, + requestId: '62847e4c696edcb' + }) + expect(jixieaux.ajax.calledWith('https://mytracker.com/sync?', null, QPARAMS_)).to.equal(true); + }); + }); // describe + + /** + * onTimeout + */ + describe('onTimeout', function() { + let ajaxStub; + let miscDimsStub; + beforeEach(function() { + ajaxStub = sinon.stub(jixieaux, 'ajax'); + miscDimsStub = sinon.stub(jixieaux, 'getMiscDims'); + miscDimsStub + .returns({ device: device_, pageurl: pageurl_, domain: domain_ }); + }) + + afterEach(function() { + miscDimsStub.restore(); + ajaxStub.restore(); + }) + + // reference to check against: + const QPARAMS_ = { + action: 'hbtimeout', + device: device_, + pageurl: encodeURIComponent(pageurl_), + domain: encodeURIComponent(domain_), + auctionid: '028d5dee-2c83-44e3-bed1-b75002475cdf', + timeout: 1000, + count: 2 + }; + + it('check it is sending the correct ajax url and qparameters', function() { + spec.onTimeout([ + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000}, + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000} + ]) + expect(jixieaux.ajax.calledWith(spec.EVENTS_URL, null, QPARAMS_)).to.equal(true); + }) + + it('if turned off via config then dont do onTimeout sending of event', function() { + let getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.callsFake(function fakeFn(prop) { + if (prop == 'jixie') { + return { onTimeout: 'off' }; + } + return null; + }); + let called = false; + ajaxStub.callsFake(function fakeFn() { + called = true; + }); + spec.onTimeout([ + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000}, + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000} + ]) + expect(called).to.equal(false); + getConfigStub.restore(); + }) + + const otherUrl_ = 'https://other.azurewebsites.net/sync/evt?'; + it('if config specifies a different endpoint then should send there instead', function() { + let getConfigStub = sinon.stub(config, 'getConfig'); + getConfigStub.callsFake(function fakeFn(prop) { + if (prop == 'jixie') { + return { onTimeoutUrl: otherUrl_ }; + } + return null; + }); + spec.onTimeout([ + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000}, + {auctionId: '028d5dee-2c83-44e3-bed1-b75002475cdf', timeout: 1000} + ]) + expect(jixieaux.ajax.calledWith(otherUrl_, null, QPARAMS_)).to.equal(true); + getConfigStub.restore(); + }) + });// describe +}); From c210287507974f6772c4136f38d15932100e23c7 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 22 Sep 2020 12:32:13 -0700 Subject: [PATCH 0213/1476] Rubicon Bid Adapter: Let host be configurable (#5779) * let host be configurable * update with google meet code reviews --- modules/rubiconBidAdapter.js | 33 +++++++++---- test/spec/modules/rubiconBidAdapter_spec.js | 55 +++++++++++++++++++-- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 979cc430f15..9d4b6203a86 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -8,9 +8,24 @@ const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; // always use https, regardless of whether or not current page is secure -export const FASTLANE_ENDPOINT = 'https://fastlane.rubiconproject.com/a/api/fastlane.json'; -export const VIDEO_ENDPOINT = 'https://prebid-server.rubiconproject.com/openrtb2/auction'; -export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; +export let fastlaneEndpoint = `https://fastlane.rubiconproject.com/a/api/fastlane.json`; +export let videoEndpoint = `https://prebid-server.rubiconproject.com/openrtb2/auction`; +export let syncEndpoint = `https://eus.rubiconproject.com/usync.html`; +let returnVast = false; + +let bannerHost = 'fastlane'; +let videoHost = 'prebid-server'; +let syncHost = 'eus'; +config.getConfig('rubicon', config => { + let rubiConf = config.rubicon; + bannerHost = rubiConf.bannerHost || bannerHost; + fastlaneEndpoint = `https://${bannerHost}.rubiconproject.com/a/api/fastlane.json`; + videoHost = rubiConf.videoHost || videoHost; + videoEndpoint = `https://${videoHost}.rubiconproject.com/openrtb2/auction`; + syncHost = rubiConf.syncHost || syncHost; + syncEndpoint = `https://${syncHost}.rubiconproject.com/usync.html`; + returnVast = rubiConf.returnVast === true; // anything other than true is false +}); const GVLID = 52; const DIGITRUST_PROP_NAMES = { @@ -177,7 +192,7 @@ export const spec = { prebid: { cache: { vastxml: { - returnCreative: false // don't return the VAST + returnCreative: returnVast } }, targeting: { @@ -328,7 +343,7 @@ export const spec = { return { method: 'POST', - url: VIDEO_ENDPOINT, + url: videoEndpoint, data, bidRequest } @@ -340,7 +355,7 @@ export const spec = { const bidParams = spec.createSlotParams(bidRequest, bidderRequest); return { method: 'GET', - url: FASTLANE_ENDPOINT, + url: fastlaneEndpoint, data: spec.getOrderedParams(bidParams).reduce((paramString, key) => { const propValue = bidParams[key]; return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString; @@ -371,7 +386,7 @@ export const spec = { // SRA request returns grouped bidRequest arrays not a plain bidRequest aggregate.push({ method: 'GET', - url: FASTLANE_ENDPOINT, + url: fastlaneEndpoint, data: spec.getOrderedParams(combinedSlotParams).reduce((paramString, key) => { const propValue = combinedSlotParams[key]; return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString; @@ -795,7 +810,7 @@ export const spec = { }, getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { if (!hasSynced && syncOptions.iframeEnabled) { - // data is only assigned if params are available to pass to SYNC_ENDPOINT + // data is only assigned if params are available to pass to syncEndpoint let params = ''; if (gdprConsent && typeof gdprConsent.consentString === 'string') { @@ -814,7 +829,7 @@ export const spec = { hasSynced = true; return { type: 'iframe', - url: SYNC_ENDPOINT + params + url: syncEndpoint + params }; } }, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c5c0f644643..f4e35517636 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter.js'; +import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, fastlaneEndpoint} from 'modules/rubiconBidAdapter.js'; import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -1916,14 +1916,14 @@ describe('the rubicon adapter', function () { let requests = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); + expect(requests[0].url).to.equal(fastlaneEndpoint); bidderRequest.mediaTypes.video.context = 'instream'; requests = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); + expect(requests[0].url).to.equal(fastlaneEndpoint); }); it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () { @@ -1942,7 +1942,7 @@ describe('the rubicon adapter', function () { let requests = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); + expect(requests[0].url).to.equal(fastlaneEndpoint); }); it('should include coppa flag in video bid request', () => { @@ -3132,4 +3132,51 @@ describe('the rubicon adapter', function () { expect(request[0].data.source.ext.schain).to.deep.equal(schain); }); }); + + describe('configurable settings', function() { + afterEach(() => { + config.setConfig({ + rubicon: { + bannerHost: 'rubicon', + videoHost: 'prebid-server', + syncHost: 'eus', + returnVast: false + } + }); + config.resetConfig(); + }); + + beforeEach(function () { + resetUserSync(); + }); + + it('should update fastlane endpoint if', function () { + config.setConfig({ + rubicon: { + bannerHost: 'fastlane-qa', + videoHost: 'prebid-server-qa', + syncHost: 'eus-qa', + returnVast: true + } + }); + + // banner + let [bannerRequest] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(bannerRequest.url).to.equal('https://fastlane-qa.rubiconproject.com/a/api/fastlane.json'); + + // video and returnVast + createVideoBidderRequest(); + let [videoRequest] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = videoRequest.data; + expect(videoRequest.url).to.equal('https://prebid-server-qa.rubiconproject.com/openrtb2/auction'); + expect(post.ext.prebid.cache.vastxml).to.have.property('returnCreative').that.is.an('boolean'); + expect(post.ext.prebid.cache.vastxml.returnCreative).to.equal(true); + + // user sync + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + expect(syncs).to.deep.equal({type: 'iframe', url: 'https://eus-qa.rubiconproject.com/usync.html'}); + }); + }); }); From 4bddb0b2d0a6d9d1e8cd13685c84ea4a1b5f9236 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 22 Sep 2020 12:33:16 -0700 Subject: [PATCH 0214/1476] Rubicon Analytics Adapter: Custom Key Value reporting (#5778) * Setting fpkvs now in adapter * add logging for bad input * make sure it is an object * ooops * google meet code review --- modules/rubiconAnalyticsAdapter.js | 46 ++++++++++++------- .../modules/rubiconAnalyticsAdapter_spec.js | 45 +++++++++--------- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 362c56b698a..72648f8feb5 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -51,6 +51,29 @@ const cache = { const BID_REJECTED_IPF = 'rejected-ipf'; +let fpkvs = {}; +function updateFpkvs(fpkvs, newKvs) { + const isValid = typeof newKvs === 'object' && Object.keys(newKvs).every(key => typeof newKvs[key] === 'string'); + if (!isValid) { + utils.logError('Rubicon Analytics: fpkvs must be object with string keys and values'); + return fpkvs; + } else { + return {...fpkvs, ...newKvs}; + } +} + +let integration, ruleId, wrapperName; +// listen for any rubicon setConfig events and save them to appropriate fields! +// we are saving these as global to this module so that if a pub accidentally overwrites the entire +// rubicon object, then we do not lose other data +config.getConfig('rubicon', config => { + let rubiConf = config.rubicon; + integration = rubiConf.int_type || integration || DEFAULT_INTEGRATION; + ruleId = rubiConf.rule_name || ruleId; + wrapperName = rubiConf.wrapperName || wrapperName; + fpkvs = rubiConf.fpkvs ? updateFpkvs(fpkvs, rubiConf.fpkvs) : fpkvs +}); + export function getHostNameFromReferer(referer) { try { rubiconAdapter.referrerHostname = utils.parseUrl(referer, {noDecodeWholeURL: true}).hostname; @@ -140,17 +163,14 @@ function sendMessage(auctionId, bidWonId) { let referrer = config.getConfig('pageUrl') || (auctionCache && auctionCache.referrer); let message = { eventTimeMillis: Date.now(), - integration: config.getConfig('rubicon.int_type') || DEFAULT_INTEGRATION, - ruleId: config.getConfig('rubicon.rule_name'), + integration, + ruleId, version: '$prebid.version$', referrerUri: referrer, referrerHostname: rubiconAdapter.referrerHostname || getHostNameFromReferer(referrer), - channel: 'web' + channel: 'web', + wrapperName }; - const wrapperName = config.getConfig('rubicon.wrapperName'); - if (wrapperName) { - message.wrapperName = wrapperName; - } if (auctionCache && !auctionCache.sent) { let adUnitMap = Object.keys(auctionCache.bids).reduce((adUnits, bidId) => { let bid = auctionCache.bids[bidId]; @@ -339,13 +359,6 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { ]); } -function getDynamicKvps() { - if (prebidGlobal.rp && typeof prebidGlobal.rp.getCustomTargeting === 'function') { - return prebidGlobal.rp.getCustomTargeting(); - } - return {}; -} - function getPageViewId() { if (prebidGlobal.rp && typeof prebidGlobal.rp.generatePageViewId === 'function') { return prebidGlobal.rp.generatePageViewId(false); @@ -407,7 +420,7 @@ function updateRpaCookie() { // possible that decodedRpaCookie is undefined, and if it is, we probably are blocked by storage or some other exception if (Object.keys(decodedRpaCookie).length) { decodedRpaCookie.lastSeen = currentTime; - decodedRpaCookie.fpkvs = {...decodedRpaCookie.fpkvs, ...getDynamicKvps()}; + decodedRpaCookie.fpkvs = {...decodedRpaCookie.fpkvs, ...fpkvs}; decodedRpaCookie.pvid = getPageViewId(); setRpaCookie(decodedRpaCookie) } @@ -482,7 +495,8 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }, disableAnalytics() { this.getUrl = baseAdapter.getUrl; - accountId = null; + accountId = integration = ruleId = wrapperName = undefined; + fpkvs = {}; cache.gpt.registered = false; baseAdapter.disableAnalytics.apply(this, arguments); }, diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 344f08823f8..60b28b02361 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -900,11 +900,12 @@ describe('rubicon analytics adapter', function () { it('should should pass along custom rubicon kv and pvid when defined', function () { pvid = '1a2b3c'; - kvps = { - source: 'fb', - link: 'email' - }; - + config.setConfig({rubicon: { + fpkvs: { + source: 'fb', + link: 'email' + } + }}); performStandardAuction(); expect(server.requests.length).to.equal(1); let request = server.requests[0]; @@ -932,10 +933,11 @@ describe('rubicon analytics adapter', function () { getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); pvid = '1a2b3c'; - kvps = { - link: 'email' // should merge this with what is in the localStorage! - }; - + config.setConfig({rubicon: { + fpkvs: { + link: 'email' // should merge this with what is in the localStorage! + } + }}); performStandardAuction(); expect(server.requests.length).to.equal(1); let request = server.requests[0]; @@ -984,9 +986,11 @@ describe('rubicon analytics adapter', function () { getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); pvid = '1a2b3c'; - kvps = { - link: 'email' // should merge this with what is in the localStorage! - }; + config.setConfig({rubicon: { + fpkvs: { + link: 'email' // should merge this with what is in the localStorage! + } + }}); performStandardAuction(); expect(server.requests.length).to.equal(1); @@ -1033,9 +1037,11 @@ describe('rubicon analytics adapter', function () { getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); pvid = '1a2b3c'; - kvps = { - link: 'email' // should merge this with what is in the localStorage! - }; + config.setConfig({rubicon: { + fpkvs: { + link: 'email' // should merge this with what is in the localStorage! + } + }}); performStandardAuction(); expect(server.requests.length).to.equal(1); @@ -1370,12 +1376,9 @@ describe('rubicon analytics adapter', function () { describe('config with integration type', () => { it('should use the integration type provided in the config instead of the default', () => { - sandbox.stub(config, 'getConfig').callsFake(function (key) { - const config = { - 'rubicon.int_type': 'testType' - }; - return config[key]; - }); + config.setConfig({rubicon: { + int_type: 'testType' + }}) rubiconAnalyticsAdapter.enableAnalytics({ options: { From b3e81e40211f45786630ddb33c11c7763b3610bb Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 22 Sep 2020 15:39:13 -0400 Subject: [PATCH 0215/1476] Prebid 4.9.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a304f2a42b..6cca04d1c91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.9.0-pre", + "version": "4.9.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 540c7deff69364b98240f5426260eb1d6017232a Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 22 Sep 2020 15:52:05 -0400 Subject: [PATCH 0216/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cca04d1c91..fc5a0441ad1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.9.0", + "version": "4.10.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 1b932a0fec23322af7b8e74c113c694f44f2be2e Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 23 Sep 2020 06:45:10 -0400 Subject: [PATCH 0217/1476] Britepool user id module update (#5750) * adding britepool_pubparams dynamic variable lookup and merge into submodule params if exists * adding support for gdpr consent string in query params * adding tests for britepool_pubparams * adding doc block for consentData * adding pixel on success * - ensures id resolution pixel only fires when authoritative information is not present - adds tests for id resolution pixel --- modules/britepoolIdSystem.js | 17 ++++-- test/spec/modules/britepoolIdSystem_spec.js | 64 ++++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/modules/britepoolIdSystem.js b/modules/britepoolIdSystem.js index 17a39e96aad..90fd159571f 100644 --- a/modules/britepoolIdSystem.js +++ b/modules/britepoolIdSystem.js @@ -8,6 +8,7 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; +const PIXEL = 'https://px.britepool.com/new?partner_id=t'; /** @type {Submodule} */ export const britepoolIdSubmodule = { @@ -28,7 +29,8 @@ export const britepoolIdSubmodule = { /** * Performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleParams} [submoduleConfigParams] + * @param {ConsentData|undefined} consentData * @returns {function(callback:function)} */ getId(submoduleConfigParams, consentData) { @@ -44,6 +46,9 @@ export const britepoolIdSubmodule = { }; } } + if (utils.isEmpty(params)) { + utils.triggerPixel(PIXEL); + } // Return for async operation return { callback: function(callback) { @@ -79,13 +84,17 @@ export const britepoolIdSubmodule = { }, /** * Helper method to create params for our API call - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleParams} [submoduleConfigParams] + * @param {ConsentData|undefined} consentData * @returns {object} Object with parsed out params */ createParams(submoduleConfigParams, consentData) { + const hasGdprData = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies; + const gdprConsentString = hasGdprData ? consentData.consentString : undefined; let errors = []; const headers = {}; - let params = Object.assign({}, submoduleConfigParams); + const dynamicVars = typeof britepool_pubparams !== 'undefined' ? britepool_pubparams : {}; // eslint-disable-line camelcase, no-undef + let params = Object.assign({}, submoduleConfigParams, dynamicVars); if (params.getter) { // Custom getter will not require other params if (typeof params.getter !== 'function') { @@ -98,7 +107,7 @@ export const britepoolIdSubmodule = { headers['x-api-key'] = params.api_key; } } - const url = params.url || 'https://api.britepool.com/v1/britepool/id'; + const url = params.url || `https://api.britepool.com/v1/britepool/id${gdprConsentString ? '?gdprString=' + encodeURIComponent(gdprConsentString) : ''}`; const getter = params.getter; delete params.api_key; delete params.url; diff --git a/test/spec/modules/britepoolIdSystem_spec.js b/test/spec/modules/britepoolIdSystem_spec.js index 2c6dd234a90..f2dd2ef533f 100644 --- a/test/spec/modules/britepoolIdSystem_spec.js +++ b/test/spec/modules/britepoolIdSystem_spec.js @@ -1,5 +1,5 @@ -import { expect } from 'chai'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; +import * as utils from '../../../src/utils.js'; describe('BritePool Submodule', () => { const api_key = '1111'; @@ -16,6 +16,32 @@ describe('BritePool Submodule', () => { }; }; + let triggerPixelStub; + + beforeEach(function (done) { + triggerPixelStub = sinon.stub(utils, 'triggerPixel'); + done(); + }); + + afterEach(function () { + triggerPixelStub.restore(); + }); + + it('trigger id resolution pixel when no identifiers set', () => { + britepoolIdSubmodule.getId({}); + expect(triggerPixelStub.called).to.be.true; + }); + + it('trigger id resolution pixel when no identifiers set with api_key param', () => { + britepoolIdSubmodule.getId({ api_key }); + expect(triggerPixelStub.called).to.be.true; + }); + + it('does not trigger id resolution pixel when identifiers set', () => { + britepoolIdSubmodule.getId({ api_key, aaid }); + expect(triggerPixelStub.called).to.be.false; + }); + it('sends x-api-key in header and one identifier', () => { const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }); assert(errors.length === 0, errors); @@ -43,6 +69,42 @@ describe('BritePool Submodule', () => { expect(params.url).to.be.undefined; }); + it('test gdpr consent string in url', () => { + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }, { gdprApplies: true, consentString: 'expectedConsentString' }); + expect(url).to.equal('https://api.britepool.com/v1/britepool/id?gdprString=expectedConsentString'); + }); + + it('test gdpr consent string not in url if gdprApplies false', () => { + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }, { gdprApplies: false, consentString: 'expectedConsentString' }); + expect(url).to.equal('https://api.britepool.com/v1/britepool/id'); + }); + + it('test gdpr consent string not in url if consent string undefined', () => { + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }, { gdprApplies: true, consentString: undefined }); + expect(url).to.equal('https://api.britepool.com/v1/britepool/id'); + }); + + it('dynamic pub params should be added to params', () => { + window.britepool_pubparams = { ppid: '12345' }; + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }); + expect(params).to.eql({ aaid, ppid: '12345' }); + window.britepool_pubparams = undefined; + }); + + it('dynamic pub params should override submodule params', () => { + window.britepool_pubparams = { ppid: '67890' }; + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, ppid: '12345' }); + expect(params).to.eql({ ppid: '67890' }); + window.britepool_pubparams = undefined; + }); + + it('if dynamic pub params undefined do nothing', () => { + window.britepool_pubparams = undefined; + const { params, headers, url, errors } = britepoolIdSubmodule.createParams({ api_key, aaid }); + expect(params).to.eql({ aaid }); + window.britepool_pubparams = undefined; + }); + it('test getter override with value', () => { const { params, headers, url, getter, errors } = britepoolIdSubmodule.createParams({ api_key, aaid, url: url_override, getter: getter_override }); expect(getter).to.equal(getter_override); From 5f598b42894e805017fda7a88c78d52518129e27 Mon Sep 17 00:00:00 2001 From: Rigel Cheng Date: Wed, 23 Sep 2020 22:34:28 +0800 Subject: [PATCH 0218/1476] Add a new param cid to bridgewellBidAdapter (#5764) * pass a new param cid to bridgewellBidAdapter * update the markdown file for bridgewellBidAdpter --- modules/bridgewellBidAdapter.js | 33 +++++++++---- modules/bridgewellBidAdapter.md | 6 +-- .../spec/modules/bridgewellBidAdapter_spec.js | 48 ++++++++++++++++++- 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index a2d3a2e70a2..5fca9acc0b3 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -38,15 +38,29 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { const adUnits = []; utils._each(validBidRequests, function (bid) { - adUnits.push({ - ChannelID: bid.params.ChannelID, - adUnitCode: bid.adUnitCode, - mediaTypes: bid.mediaTypes || { - banner: { - sizes: bid.sizes + if (bid.params.cid) { + adUnits.push({ + cid: bid.params.cid, + adUnitCode: bid.adUnitCode, + requestId: bid.bidId, + mediaTypes: bid.mediaTypes || { + banner: { + sizes: bid.sizes + } } - } - }); + }); + } else { + adUnits.push({ + ChannelID: bid.params.ChannelID, + adUnitCode: bid.adUnitCode, + requestId: bid.bidId, + mediaTypes: bid.mediaTypes || { + banner: { + sizes: bid.sizes + } + } + }); + } }); let topUrl = ''; @@ -65,7 +79,8 @@ export const spec = { inIframe: utils.inIframe(), url: topUrl, referrer: getTopWindowReferrer(), - adUnits: adUnits + adUnits: adUnits, + refererInfo: bidderRequest.refererInfo, }, validBidRequests: validBidRequests }; diff --git a/modules/bridgewellBidAdapter.md b/modules/bridgewellBidAdapter.md index 6bcab4b8820..97e11f6eaf9 100644 --- a/modules/bridgewellBidAdapter.md +++ b/modules/bridgewellBidAdapter.md @@ -20,7 +20,7 @@ Module that connects to Bridgewell demand source to fetch bids. bids: [{ bidder: 'bridgewell', params: { - ChannelID: 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ' + cid: 12345 } }] }, { @@ -33,7 +33,7 @@ Module that connects to Bridgewell demand source to fetch bids. bids: [{ bidder: 'bridgewell', params: { - ChannelID: 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ' + cid: 56789 } }] }, { @@ -70,7 +70,7 @@ Module that connects to Bridgewell demand source to fetch bids. bids: [{ bidder: 'bridgewell', params: { - ChannelID: 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ' + cid: 2394 } }] }]; diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index fea2454393d..24ec523ac67 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -143,12 +143,58 @@ describe('bridgewellBidAdapter', function () { expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { expect(payload.adUnits[i]).to.have.property('ChannelID').that.is.a('string'); + expect(payload.adUnits[i]).to.not.have.property('cid'); expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); + } + }); + + it('should attach valid params to the tag, part2', function() { + const bidderRequest = { + refererInfo: { + referer: 'https://www.bridgewell.com/' + } + } + const bidRequests2 = [ + { + 'bidder': 'bridgewell', + 'params': { + 'cid': 1234, + }, + 'adUnitCode': 'adunit-code-2', + 'mediaTypes': { + 'banner': { + 'sizes': [728, 90] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + ]; + + const request = spec.buildRequests(bidRequests2, bidderRequest); + const payload = request.data; + + expect(payload).to.be.an('object'); + expect(payload.adUnits).to.be.an('array'); + expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); + for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { + expect(payload.adUnits[i]).to.have.property('cid').that.is.a('number'); + expect(payload.adUnits[i]).to.not.have.property('ChannelID'); + expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); } }); it('should attach validBidRequests to the tag', function () { - const request = spec.buildRequests(bidRequests); + const bidderRequest = { + refererInfo: { + referer: 'https://www.bridgewell.com/' + } + } + + const request = spec.buildRequests(bidRequests, bidderRequest); const validBidRequests = request.validBidRequests; expect(validBidRequests).to.deep.equal(bidRequests); }); From 8b9b86a632b9c366fd3e5d9bfe3825a7dfd45c58 Mon Sep 17 00:00:00 2001 From: Michael Griego Date: Wed, 23 Sep 2020 17:03:06 -0500 Subject: [PATCH 0219/1476] Refactor refererDetection to allow for URL discovery on AMP pages. (#4846) * Refactor refererDetection to allow for URL discovery on AMP pages. * Update import to include extension. --- src/refererDetection.js | 256 ++++++++--------- test/spec/refererDetection_spec.js | 422 +++++++++++++++++++++++------ 2 files changed, 463 insertions(+), 215 deletions(-) diff --git a/src/refererDetection.js b/src/refererDetection.js index 60198678666..da68313736b 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.js @@ -10,153 +10,48 @@ import { logWarn } from './utils.js'; +/** + * @param {Window} win Window + * @returns {Function} + */ export function detectReferer(win) { - /** - * Returns number of frames to reach top from current frame where prebid.js sits - * @returns {Array} levels - */ - function getLevels() { - let levels = walkUpWindows(); - let ancestors = getAncestorOrigins(); - - if (ancestors) { - for (let i = 0, l = ancestors.length; i < l; i++) { - levels[i].ancestor = ancestors[i]; - } - } - return levels; - } - /** * This function would return a read-only array of hostnames for all the parent frames. * win.location.ancestorOrigins is only supported in webkit browsers. For non-webkit browsers it will return undefined. + * + * @param {Window} win Window object * @returns {(undefined|Array)} Ancestor origins or undefined */ - function getAncestorOrigins() { + function getAncestorOrigins(win) { try { if (!win.location.ancestorOrigins) { return; } + return win.location.ancestorOrigins; } catch (e) { // Ignore error } } - /** - * This function would try to get referer and urls for all parent frames in case of win.location.ancestorOrigins undefined. - * @param {Array} levels - * @returns {Object} urls for all parent frames and top most detected referer url - */ - function getPubUrlStack(levels) { - let stack = []; - let defUrl = null; - let frameLocation = null; - let prevFrame = null; - let prevRef = null; - let ancestor = null; - let detectedRefererUrl = null; - - let i; - for (i = levels.length - 1; i >= 0; i--) { - try { - frameLocation = levels[i].location; - } catch (e) { - // Ignore error - } - - if (frameLocation) { - stack.push(frameLocation); - if (!detectedRefererUrl) { - detectedRefererUrl = frameLocation; - } - } else if (i !== 0) { - prevFrame = levels[i - 1]; - try { - prevRef = prevFrame.referrer; - ancestor = prevFrame.ancestor; - } catch (e) { - // Ignore error - } - - if (prevRef) { - stack.push(prevRef); - if (!detectedRefererUrl) { - detectedRefererUrl = prevRef; - } - } else if (ancestor) { - stack.push(ancestor); - if (!detectedRefererUrl) { - detectedRefererUrl = ancestor; - } - } else { - stack.push(defUrl); - } - } else { - stack.push(defUrl); - } - } - return { - stack, - detectedRefererUrl - }; - } - /** * This function returns canonical URL which refers to an HTML link element, with the attribute of rel="canonical", found in the element of your webpage + * * @param {Object} doc document + * @returns {string|null} */ function getCanonicalUrl(doc) { try { - let element = doc.querySelector("link[rel='canonical']"); + const element = doc.querySelector("link[rel='canonical']"); + if (element !== null) { return element.href; } } catch (e) { + // Ignore error } - return null; - } - /** - * Walk up to the top of the window to detect origin, number of iframes, ancestor origins and canonical url - */ - function walkUpWindows() { - let acc = []; - let currentWindow; - do { - try { - currentWindow = currentWindow ? currentWindow.parent : win; - try { - let isTop = (currentWindow == win.top); - let refData = { - referrer: currentWindow.document.referrer || null, - location: currentWindow.location.href || null, - isTop - } - if (isTop) { - refData = Object.assign(refData, { - canonicalUrl: getCanonicalUrl(currentWindow.document) - }) - } - acc.push(refData); - } catch (e) { - acc.push({ - referrer: null, - location: null, - isTop: (currentWindow == win.top) - }); - logWarn('Trying to access cross domain iframe. Continuing without referrer and location'); - } - } catch (e) { - acc.push({ - referrer: null, - location: null, - isTop: false - }); - return acc; - } - } while (currentWindow != win.top); - return acc; + return null; } /** @@ -170,31 +65,114 @@ export function detectReferer(win) { */ /** - * Get referer info + * Walk up the windows to get the origin stack and best available referrer, canonical URL, etc. + * * @returns {refererInfo} */ function refererInfo() { - try { - let levels = getLevels(); - let numIframes = levels.length - 1; - let reachedTop = (levels[numIframes].location !== null || - (numIframes > 0 && levels[numIframes - 1].referrer !== null)); - let stackInfo = getPubUrlStack(levels); - let canonicalUrl; - if (levels[levels.length - 1].canonicalUrl) { - canonicalUrl = levels[levels.length - 1].canonicalUrl; + const stack = []; + const ancestors = getAncestorOrigins(win); + let currentWindow; + let bestReferrer; + let bestCanonicalUrl; + let reachedTop = false; + let level = 0; + let valuesFromAmp = false; + let inAmpFrame = false; + + do { + const previousWindow = currentWindow; + const wasInAmpFrame = inAmpFrame; + let currentLocation; + let crossOrigin = false; + let foundReferrer = null; + + inAmpFrame = false; + currentWindow = currentWindow ? currentWindow.parent : win; + + try { + currentLocation = currentWindow.location.href || null; + } catch (e) { + crossOrigin = true; } - return { - referer: stackInfo.detectedRefererUrl, - reachedTop, - numIframes, - stack: stackInfo.stack, - canonicalUrl - }; - } catch (e) { - // Ignore error - } + if (crossOrigin) { + if (wasInAmpFrame) { + const context = previousWindow.context; + + try { + foundReferrer = context.sourceUrl; + bestReferrer = foundReferrer; + + valuesFromAmp = true; + + if (currentWindow === win.top) { + reachedTop = true; + } + + if (context.canonicalUrl) { + bestCanonicalUrl = context.canonicalUrl; + } + } catch (e) { /* Do nothing */ } + } else { + logWarn('Trying to access cross domain iframe. Continuing without referrer and location'); + + try { + const referrer = previousWindow.document.referrer; + + if (referrer) { + foundReferrer = referrer; + + if (currentWindow === win.top) { + reachedTop = true; + } + } + } catch (e) { /* Do nothing */ } + + if (!foundReferrer && ancestors && ancestors[level - 1]) { + foundReferrer = ancestors[level - 1]; + } + + if (foundReferrer && !valuesFromAmp) { + bestReferrer = foundReferrer; + } + } + } else { + if (currentLocation) { + foundReferrer = currentLocation; + bestReferrer = foundReferrer; + valuesFromAmp = false; + + if (currentWindow === win.top) { + reachedTop = true; + + const canonicalUrl = getCanonicalUrl(currentWindow.document); + + if (canonicalUrl) { + bestCanonicalUrl = canonicalUrl; + } + } + } + + if (currentWindow.context && currentWindow.context.sourceUrl) { + inAmpFrame = true; + } + } + + stack.push(foundReferrer); + level++; + } while (currentWindow !== win.top); + + stack.reverse(); + + return { + referer: bestReferrer || null, + reachedTop, + isAmp: valuesFromAmp, + numIframes: level - 1, + stack, + canonicalUrl: bestCanonicalUrl || null + }; } return refererInfo; diff --git a/test/spec/refererDetection_spec.js b/test/spec/refererDetection_spec.js index 90892d915fe..46990ae841f 100644 --- a/test/spec/refererDetection_spec.js +++ b/test/spec/refererDetection_spec.js @@ -1,87 +1,357 @@ import { detectReferer } from 'src/refererDetection.js'; import { expect } from 'chai'; -var mocks = { - createFakeWindow: function (referrer, href) { - return { - document: { - referrer: referrer - }, - location: { - href: href, - // TODO: add ancestorOrigins to increase test coverage - }, - parent: null, - top: null - }; +/** + * 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 = '', 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; } -} -describe('referer detection', () => { - it('should return referer details in nested friendly iframes', function() { - // Fake window object to test friendly iframes - // - Main page http://example.com/page.html - // - - Iframe1 http://example.com/iframe1.html - // - - - Iframe2 http://example.com/iframe2.html - let mockIframe2WinObject = mocks.createFakeWindow('http://example.com/iframe1.html', 'http://example.com/iframe2.html'); - let mockIframe1WinObject = mocks.createFakeWindow('http://example.com/page.html', 'http://example.com/iframe1.html'); - let mainWinObject = mocks.createFakeWindow('http://example.com/page.html', 'http://example.com/page.html'); - mainWinObject.document.querySelector = function() { - return { - href: 'http://prebid.org' + let previousWindow; + const myOrigin = getOrigin(urls[urls.length - 1]); + + const windowList = urls.map((url, index) => { + const theirOrigin = getOrigin(url), + sameOrigin = (myOrigin === theirOrigin); + + const win = {}; + + if (sameOrigin) { + win.location = { + href: url + }; + + if (ancestorOrigins) { + win.location.ancestorOrigins = urls.slice(0, index).reverse().map(getOrigin); + } + + if (index === 0) { + win.document = { + referrer: topReferrer + }; + + if (canonicalUrl) { + win.document.querySelector = function(selector) { + if (selector === "link[rel='canonical']") { + return { + href: canonicalUrl + }; + } + + return null; + }; + } + } else { + win.document = { + referrer: urls[index - 1] + }; } } - mockIframe2WinObject.parent = mockIframe1WinObject; - mockIframe2WinObject.top = mainWinObject; - mockIframe1WinObject.parent = mainWinObject; - mockIframe1WinObject.top = mainWinObject; - mainWinObject.top = mainWinObject; - - const getRefererInfo = detectReferer(mockIframe2WinObject); - let result = getRefererInfo(); - let expectedResult = { - 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' - ], - canonicalUrl: 'http://prebid.org' - }; - expect(result).to.deep.equal(expectedResult); + + previousWindow = win; + + return win; + }); + + const topWindow = windowList[0]; + + previousWindow = null; + + windowList.forEach((win) => { + win.top = topWindow; + win.parent = previousWindow || topWindow; + previousWindow = win; + }); + + return windowList[windowList.length - 1]; +} + +describe('Referer detection', () => { + describe('Non cross-origin scenarios', () => { + describe('No iframes', () => { + it('Should return the current window location and no canonical URL', () => { + const testWindow = buildWindowTree(['https://example.com/some/page'], 'https://othersite.com/'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: ['https://example.com/some/page'], + canonicalUrl: null + }); + }); + + it('Should return the current window location and a canonical URL', () => { + const testWindow = buildWindowTree(['https://example.com/some/page'], 'https://othersite.com/', 'https://example.com/canonical/page'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: ['https://example.com/some/page'], + canonicalUrl: 'https://example.com/canonical/page' + }); + }); + }); + + describe('Friendly iframes', () => { + it('Should return the top window location and no canonical URL', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://example.com/other/page', 'https://example.com/third/page'], 'https://othersite.com/'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 2, + stack: [ + 'https://example.com/some/page', + 'https://example.com/other/page', + 'https://example.com/third/page' + ], + canonicalUrl: null + }); + }); + + it('Should return the top window location and a canonical URL', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://example.com/other/page', 'https://example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 2, + stack: [ + 'https://example.com/some/page', + 'https://example.com/other/page', + 'https://example.com/third/page' + ], + canonicalUrl: 'https://example.com/canonical/page' + }); + }); + }); }); - it('should return referer details in nested cross domain iframes', function() { - // Fake window object to test cross domain iframes. - // - Main page http://example.com/page.html - // - - Iframe1 http://aaa.com/iframe1.html - // - - - Iframe2 http://bbb.com/iframe2.html - let mockIframe2WinObject = mocks.createFakeWindow('http://aaa.com/iframe1.html', 'http://bbb.com/iframe2.html'); - // Sinon cannot throw exception when accessing a propery so passing null to create cross domain - // environment for refererDetection module - let mockIframe1WinObject = mocks.createFakeWindow(null, null); - let mainWinObject = mocks.createFakeWindow(null, null); - mockIframe2WinObject.parent = mockIframe1WinObject; - mockIframe2WinObject.top = mainWinObject; - mockIframe1WinObject.parent = mainWinObject; - mockIframe1WinObject.top = mainWinObject; - mainWinObject.top = mainWinObject; - - const getRefererInfo = detectReferer(mockIframe2WinObject); - let result = getRefererInfo(); - let expectedResult = { - referer: 'http://aaa.com/iframe1.html', - reachedTop: false, - numIframes: 2, - stack: [ - null, - 'http://aaa.com/iframe1.html', - 'http://bbb.com/iframe2.html' - ], - canonicalUrl: undefined - }; - expect(result).to.deep.equal(expectedResult); + describe('Cross-origin scenarios', () => { + it('Should return the top URL and no canonical URL with one cross-origin iframe', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://safe.frame/ad'], 'https://othersite.com/', 'https://canonical.example.com/'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 1, + stack: [ + 'https://example.com/some/page', + 'https://safe.frame/ad' + ], + canonicalUrl: null + }); + }); + + it('Should return the top URL and no canonical URL with one cross-origin iframe and one friendly iframe', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://safe.frame/ad', 'https://safe.frame/ad'], 'https://othersite.com/', 'https://canonical.example.com/'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 2, + stack: [ + 'https://example.com/some/page', + 'https://safe.frame/ad', + 'https://safe.frame/ad' + ], + canonicalUrl: null + }); + }); + + it('Should return the second iframe location with three cross-origin windows and no ancessorOrigins', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://safe.frame/ad', 'https://otherfr.ame/ad'], 'https://othersite.com/', 'https://canonical.example.com/'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://safe.frame/ad', + reachedTop: false, + isAmp: false, + numIframes: 2, + stack: [ + null, + 'https://safe.frame/ad', + 'https://otherfr.ame/ad' + ], + canonicalUrl: null + }); + }); + + it('Should return the top window origin with three cross-origin windows with ancessorOrigins', () => { + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://safe.frame/ad', 'https://otherfr.ame/ad'], 'https://othersite.com/', 'https://canonical.example.com/', true), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/', + reachedTop: false, + isAmp: false, + numIframes: 2, + stack: [ + 'https://example.com/', + 'https://safe.frame/ad', + 'https://otherfr.ame/ad' + ], + canonicalUrl: null + }); + }); + }); + + describe('Cross-origin AMP page scenarios', () => { + it('Should return the AMP page source and canonical URLs in an amp-ad iframe for a non-cached AMP page', () => { + const testWindow = buildWindowTree(['https://example.com/some/page/amp/', 'https://ad-iframe.ampproject.org/ad']); + + testWindow.context = { + sourceUrl: 'https://example.com/some/page/amp/', + canonicalUrl: 'https://example.com/some/page/' + }; + + const result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page/amp/', + reachedTop: true, + isAmp: true, + numIframes: 1, + stack: [ + 'https://example.com/some/page/amp/', + 'https://ad-iframe.ampproject.org/ad' + ], + canonicalUrl: 'https://example.com/some/page/' + }); + }); + + it('Should return the AMP page source and canonical URLs in an amp-ad iframe for a cached AMP page on top', () => { + const testWindow = buildWindowTree(['https://example-com.amp-cache.example.com/some/page/amp/', 'https://ad-iframe.ampproject.org/ad']); + + testWindow.context = { + sourceUrl: 'https://example.com/some/page/amp/', + canonicalUrl: 'https://example.com/some/page/' + }; + + const result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page/amp/', + reachedTop: true, + isAmp: true, + numIframes: 1, + stack: [ + 'https://example.com/some/page/amp/', + 'https://ad-iframe.ampproject.org/ad' + ], + canonicalUrl: 'https://example.com/some/page/' + }); + }); + + describe('Cached AMP page in iframed search result', () => { + it('Should return the AMP source and canonical URLs but with a null top-level stack location Without ancesorOrigins', () => { + const testWindow = buildWindowTree(['https://google.com/amp/example-com/some/page/amp/', 'https://example-com.amp-cache.example.com/some/page/amp/', 'https://ad-iframe.ampproject.org/ad']); + + testWindow.context = { + sourceUrl: 'https://example.com/some/page/amp/', + canonicalUrl: 'https://example.com/some/page/' + }; + + const result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page/amp/', + reachedTop: false, + isAmp: true, + numIframes: 2, + stack: [ + null, + 'https://example.com/some/page/amp/', + 'https://ad-iframe.ampproject.org/ad' + ], + canonicalUrl: 'https://example.com/some/page/' + }); + }); + + it('Should return the AMP source and canonical URLs and include the top window origin in the stack with ancesorOrigins', () => { + const testWindow = buildWindowTree(['https://google.com/amp/example-com/some/page/amp/', 'https://example-com.amp-cache.example.com/some/page/amp/', 'https://ad-iframe.ampproject.org/ad'], null, null, true); + + testWindow.context = { + sourceUrl: 'https://example.com/some/page/amp/', + canonicalUrl: 'https://example.com/some/page/' + }; + + const result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page/amp/', + reachedTop: false, + isAmp: true, + numIframes: 2, + stack: [ + 'https://google.com/', + 'https://example.com/some/page/amp/', + 'https://ad-iframe.ampproject.org/ad' + ], + canonicalUrl: 'https://example.com/some/page/' + }); + }); + + it('Should return the AMP source and canonical URLs and include the top window origin in the stack with ancesorOrigins and a friendly iframe under the amp-ad iframe', () => { + const testWindow = buildWindowTree(['https://google.com/amp/example-com/some/page/amp/', 'https://example-com.amp-cache.example.com/some/page/amp/', 'https://ad-iframe.ampproject.org/ad', 'https://ad-iframe.ampproject.org/ad'], null, null, true); + + testWindow.parent.context = { + sourceUrl: 'https://example.com/some/page/amp/', + canonicalUrl: 'https://example.com/some/page/' + }; + + const result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page/amp/', + reachedTop: false, + isAmp: true, + numIframes: 3, + stack: [ + 'https://google.com/', + 'https://example.com/some/page/amp/', + 'https://ad-iframe.ampproject.org/ad', + 'https://ad-iframe.ampproject.org/ad' + ], + canonicalUrl: 'https://example.com/some/page/' + }); + }); + }); }); }); From 6ea04f97acb0403f31214eaa5366d811dd98aaac Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Thu, 24 Sep 2020 12:40:47 +0300 Subject: [PATCH 0220/1476] Intentiq id add url params (#5771) * Add new url params from config * Add intentIqIdSystem_spec.js tests class --- modules/intentIqIdSystem.js | 5 +- test/spec/modules/intentIqIdSystem_spec.js | 143 +++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 test/spec/modules/intentIqIdSystem_spec.js diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 242b227f89f..88da7d44481 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -40,7 +40,10 @@ export const intentIqIdSubmodule = { } // use protocol relative urls for http or https - const url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; + let url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; + url += configParams.pcid ? '&pcid=' + encodeURIComponent(configParams.pcid) : ''; + url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; + const resp = function (callback) { const callbacks = { success: response => { diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js new file mode 100644 index 00000000000..2dccde18855 --- /dev/null +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -0,0 +1,143 @@ +import { expect } from 'chai'; +import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; +import * as utils from 'src/utils.js'; +import {server} from 'test/mocks/xhr.js'; + +const partner = 10; +const pai = '11'; +const pcid = '12'; +const defaultConfigParams = {partner: partner}; +const paiConfigParams = {partner: partner, pai: pai}; +const pcidConfigParams = {partner: partner, pcid: pcid}; +const allConfigParams = {partner: partner, pai: pai, pcid: pcid}; +const responseHeader = {'Content-Type': 'application/json'} + +describe('IntentIQ tests', function () { + let logErrorStub; + + beforeEach(function () { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + logErrorStub.restore(); + }); + + it('should log an error if no configParams were passed when getId', function () { + let submodule = intentIqIdSubmodule.getId(); + expect(logErrorStub.calledOnce).to.be.true; + expect(submodule).to.be.undefined; + }); + + it('should log an error if partner configParam was not passed when getId', function () { + let submodule = intentIqIdSubmodule.getId({}); + expect(logErrorStub.calledOnce).to.be.true; + expect(submodule).to.be.undefined; + }); + + it('should log an error if partner configParam was not a numeric value', function () { + let submodule = intentIqIdSubmodule.getId({partner: '10'}); + expect(logErrorStub.calledOnce).to.be.true; + expect(submodule).to.be.undefined; + }); + + it('should call the IntentIQ endpoint with only partner', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should call the IntentIQ endpoint with only partner, pai', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(paiConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should call the IntentIQ endpoint with only partner, pcid', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(pcidConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should call the IntentIQ endpoint with partner, pcid, pai', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should not throw Uncaught TypeError when IntentIQ endpoint returns empty response', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + request.respond( + 204, + responseHeader, + '' + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(request.response).to.equal(''); + expect(logErrorStub.calledOnce).to.not.be.true; + }); + + it('should log an error and continue to callback if ajax request errors', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + request.respond( + 503, + responseHeader, + 'Unavailable' + ); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should log an error and continue to callback if ajax request errors', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + request.respond( + 503, + responseHeader, + 'Unavailable' + ); + expect(callBackSpy.calledOnce).to.be.true; + }); +}); From 7f2d81a87bebdd2d2d03efbba51726116f8f069c Mon Sep 17 00:00:00 2001 From: Drilon Kastrati Date: Thu, 24 Sep 2020 12:16:38 +0200 Subject: [PATCH 0221/1476] added instream video ad support (#5766) * added adapters for gjirafa and malltv * interpretResponse fix for empty result * updated testing propertyId and placementId * added instream video ad support * Single request for multple bids --- modules/gjirafaBidAdapter.js | 64 ++++++++++++++------- modules/gjirafaBidAdapter.md | 14 ++--- modules/malltvBidAdapter.js | 64 ++++++++++++++------- modules/malltvBidAdapter.md | 14 ++--- test/spec/modules/gjirafaBidAdapter_spec.js | 12 +++- test/spec/modules/malltvBidAdapter_spec.js | 10 +++- 6 files changed, 109 insertions(+), 69 deletions(-) diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index ca7fb4af32d..48496b52c05 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -1,4 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'gjirafa'; const ENDPOINT_URL = 'https://central.gjirafa.com/bid'; @@ -7,6 +8,7 @@ const SIZE_SEPARATOR = ';'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. * @@ -23,31 +25,48 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - let response = validBidRequests.map(bidRequest => { - let sizes = generateSizeParam(bidRequest.sizes); - let propertyId = bidRequest.params.propertyId; - let placementId = bidRequest.params.placementId; + let propertyId = ''; + let pageViewGuid = ''; + let storageId = ''; + let bidderRequestId = ''; + let url = ''; + let contents = []; + + let placements = validBidRequests.map(bidRequest => { + if (!propertyId) { propertyId = bidRequest.params.propertyId; } + if (!pageViewGuid && bidRequest.params) { pageViewGuid = bidRequest.params.pageViewGuid || ''; } + if (!storageId && bidRequest.params) { storageId = bidRequest.params.storageId || ''; } + if (!bidderRequestId) { bidderRequestId = bidRequest.bidderRequestId; } + if (!url && bidderRequest) { url = bidderRequest.refererInfo.referer; } + if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents } + let adUnitId = bidRequest.adUnitCode; - let pageViewGuid = bidRequest.params.pageViewGuid || ''; - let contents = bidRequest.params.contents || []; - const body = { + let placementId = bidRequest.params.placementId; + let sizes = generateSizeParam(bidRequest.sizes); + + return { sizes: sizes, adUnitId: adUnitId, placementId: placementId, - propertyId: propertyId, - pageViewGuid: pageViewGuid, - url: bidderRequest ? bidderRequest.refererInfo.referer : '', - requestid: bidRequest.bidderRequestId, bidid: bidRequest.bidId, - contents: contents - }; - return { - method: 'POST', - url: ENDPOINT_URL, - data: body }; }); - return response + + let body = { + propertyId: propertyId, + pageViewGuid: pageViewGuid, + storageId: storageId, + url: url, + requestid: bidderRequestId, + placements: placements, + contents: contents + } + + return [{ + method: 'POST', + url: ENDPOINT_URL, + data: body + }]; }, /** * Unpack the response from the server into a list of bids. @@ -55,13 +74,12 @@ 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, bidRequest) { - window.adnResponse = serverResponse; + interpretResponse: function (serverResponse) { const responses = serverResponse.body; const bidResponses = []; for (var i = 0; i < responses.length; i++) { const bidResponse = { - requestId: bidRequest.data.bidid, + requestId: responses[i].BidId, cpm: responses[i].CPM, width: responses[i].Width, height: responses[i].Height, @@ -70,7 +88,9 @@ export const spec = { netRevenue: responses[i].NetRevenue, ttl: responses[i].TTL, referrer: responses[i].Referrer, - ad: responses[i].Ad + ad: responses[i].Ad, + vastUrl: responses[i].VastUrl, + mediaType: responses[i].MediaType }; bidResponses.push(bidResponse); } diff --git a/modules/gjirafaBidAdapter.md b/modules/gjirafaBidAdapter.md index 53d3a76c5ed..deb06e74a27 100644 --- a/modules/gjirafaBidAdapter.md +++ b/modules/gjirafaBidAdapter.md @@ -28,22 +28,16 @@ var adUnits = [ { code: 'test-div', mediaTypes: { - banner: { - sizes: [[300, 250]] - } + video: { + context: 'instream' + } }, bids: [ { bidder: 'gjirafa', params: { propertyId: '105227', - placementId: '846848', - contents: [ //optional - { - type: 'article', - id: '123' - } - ] + placementId: '846836' } } ] diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 4cdb5d45328..846935a5522 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -1,4 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'malltv'; const ENDPOINT_URL = 'https://central.mall.tv/bid'; @@ -7,6 +8,7 @@ const SIZE_SEPARATOR = ';'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. * @@ -23,31 +25,48 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - let response = validBidRequests.map(bidRequest => { - let sizes = generateSizeParam(bidRequest.sizes); - let propertyId = bidRequest.params.propertyId; - let placementId = bidRequest.params.placementId; + let propertyId = ''; + let pageViewGuid = ''; + let storageId = ''; + let bidderRequestId = ''; + let url = ''; + let contents = []; + + let placements = validBidRequests.map(bidRequest => { + if (!propertyId) { propertyId = bidRequest.params.propertyId; } + if (!pageViewGuid && bidRequest.params) { pageViewGuid = bidRequest.params.pageViewGuid || ''; } + if (!storageId && bidRequest.params) { storageId = bidRequest.params.storageId || ''; } + if (!bidderRequestId) { bidderRequestId = bidRequest.bidderRequestId; } + if (!url && bidderRequest) { url = bidderRequest.refererInfo.referer; } + if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents } + let adUnitId = bidRequest.adUnitCode; - let pageViewGuid = bidRequest.params.pageViewGuid || ''; - let contents = bidRequest.params.contents || []; - const body = { + let placementId = bidRequest.params.placementId; + let sizes = generateSizeParam(bidRequest.sizes); + + return { sizes: sizes, adUnitId: adUnitId, placementId: placementId, - propertyId: propertyId, - pageViewGuid: pageViewGuid, - url: bidderRequest ? bidderRequest.refererInfo.referer : '', - requestid: bidRequest.bidderRequestId, bidid: bidRequest.bidId, - contents: contents - }; - return { - method: 'POST', - url: ENDPOINT_URL, - data: body }; }); - return response + + let body = { + propertyId: propertyId, + pageViewGuid: pageViewGuid, + storageId: storageId, + url: url, + requestid: bidderRequestId, + placements: placements, + contents: contents + } + + return [{ + method: 'POST', + url: ENDPOINT_URL, + data: body + }]; }, /** * Unpack the response from the server into a list of bids. @@ -55,13 +74,12 @@ 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, bidRequest) { - window.adnResponse = serverResponse; + interpretResponse: function (serverResponse) { const responses = serverResponse.body; const bidResponses = []; for (var i = 0; i < responses.length; i++) { const bidResponse = { - requestId: bidRequest.data.bidid, + requestId: responses[i].BidId, cpm: responses[i].CPM, width: responses[i].Width, height: responses[i].Height, @@ -70,7 +88,9 @@ export const spec = { netRevenue: responses[i].NetRevenue, ttl: responses[i].TTL, referrer: responses[i].Referrer, - ad: responses[i].Ad + ad: responses[i].Ad, + vastUrl: responses[i].VastUrl, + mediaType: responses[i].MediaType }; bidResponses.push(bidResponse); } diff --git a/modules/malltvBidAdapter.md b/modules/malltvBidAdapter.md index 72db0cef6c7..3d419fa0916 100644 --- a/modules/malltvBidAdapter.md +++ b/modules/malltvBidAdapter.md @@ -28,22 +28,16 @@ var adUnits = [ { code: 'test-div', mediaTypes: { - banner: { - sizes: [[300, 250], [300, 300]] - } + video: { + context: 'instream' + } }, bids: [ { bidder: 'malltv', params: { propertyId: '105134', - placementId: '846832', - contents: [ //optional - { - type: 'video', - id: '123' - } - ] + placementId: '846841' } } ] diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index 566b1243f62..db9b82e0a10 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -31,7 +31,7 @@ describe('gjirafaAdapterTest', () => { })).to.equal(false); }); - it('bidRequest without propertyId orplacementId', () => { + it('bidRequest without propertyId or placementId', () => { expect(spec.isBidRequestValid({ bidder: 'gjirafa', params: { @@ -80,7 +80,11 @@ describe('gjirafaAdapterTest', () => { it('bidRequest sizes', () => { const requests = spec.buildRequests(bidRequests); - expect(requests[0].data.sizes).to.equal('728x90'); + requests.forEach(function (requestItem) { + expect(requestItem.data.placements).to.exist; + expect(requestItem.data.placements.length).to.equal(1); + expect(requestItem.data.placements[0].sizes).to.equal('728x90'); + }); }); }); @@ -128,7 +132,9 @@ describe('gjirafaAdapterTest', () => { 'netRevenue', 'ttl', 'referrer', - 'ad' + 'ad', + 'vastUrl', + 'mediaType' ]; let resultKeys = Object.keys(result[0]); diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js index 10161b319c5..e1e9ad867e7 100644 --- a/test/spec/modules/malltvBidAdapter_spec.js +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -80,7 +80,11 @@ describe('malltvAdapterTest', () => { it('bidRequest sizes', () => { const requests = spec.buildRequests(bidRequests); - expect(requests[0].data.sizes).to.equal('300x250'); + requests.forEach(function (requestItem) { + expect(requestItem.data.placements).to.exist; + expect(requestItem.data.placements.length).to.equal(1); + expect(requestItem.data.placements[0].sizes).to.equal('300x250'); + }); }); }); @@ -128,7 +132,9 @@ describe('malltvAdapterTest', () => { 'netRevenue', 'ttl', 'referrer', - 'ad' + 'ad', + 'vastUrl', + 'mediaType' ]; let resultKeys = Object.keys(result[0]); From 80fc5b6c16fb32ed84691d3119e4d01d2c9061d7 Mon Sep 17 00:00:00 2001 From: fgcloutier Date: Thu, 24 Sep 2020 13:14:23 +0200 Subject: [PATCH 0222/1476] feat(sublimeBidAdapter): updating sublimeBidAdapter module (#5726) - handle new notifyId parameter; - bumping version to 0.6.0. --- modules/sublimeBidAdapter.js | 11 +++++++---- modules/sublimeBidAdapter.md | 9 ++++++--- test/spec/modules/sublimeBidAdapter_spec.js | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 1f8cb59f442..e9f7cf19033 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.5.2'; +const SUBLIME_VERSION = '0.6.0'; /** * Debug log message @@ -23,7 +23,8 @@ export function log(msg, obj) { // Default state export const state = { zoneId: '', - transactionId: '' + transactionId: '', + notifyId: '' }; /** @@ -47,8 +48,8 @@ export function sendEvent(eventName) { z: state.zoneId, e: eventName, src: 'pa', - puid: state.transactionId, - trId: state.transactionId, + puid: state.transactionId || state.notifyId, + trId: state.transactionId || state.notifyId, ver: SUBLIME_VERSION, }; @@ -101,6 +102,7 @@ function buildRequests(validBidRequests, bidderRequest) { setState({ transactionId: bid.transactionId, + notifyId: bid.params.notifyId, zoneId: bid.params.zoneId, debug: bid.params.debug || false, }); @@ -117,6 +119,7 @@ function buildRequests(validBidRequests, bidderRequest) { h: size[1], })), transactionId: bid.transactionId, + notifyId: bid.params.notifyId, zoneId: bid.params.zoneId, }; diff --git a/modules/sublimeBidAdapter.md b/modules/sublimeBidAdapter.md index e57f4a1fdb0..5cd1c95b682 100644 --- a/modules/sublimeBidAdapter.md +++ b/modules/sublimeBidAdapter.md @@ -9,7 +9,7 @@ Maintainer: pbjs@sublimeskinz.com # Description Connects to Sublime for bids. -Sublime bid adapter supports Skinz and M-Skinz formats. +Sublime bid adapter supports Skinz. # Nota Bene @@ -53,10 +53,13 @@ var adUnits = [{ bids: [{ bidder: 'sublime', params: { - zoneId: + zoneId: , + notifyId: } }] }]; ``` -Where you replace `` by your Sublime Zone id +Where you replace: +- `` by your Sublime Zone id; +- `` by your Sublime Notify id diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index ae9e293a757..008f24730bc 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -149,7 +149,7 @@ describe('Sublime Adapter', function() { currency: 'USD', netRevenue: true, ttl: 600, - pbav: '0.5.2', + pbav: '0.6.0', ad: '', }, ]; @@ -191,7 +191,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.5.2', + pbav: '0.6.0', }; expect(result[0]).to.deep.equal(expectedResponse); @@ -241,7 +241,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.5.2', + pbav: '0.6.0', }; expect(result[0]).to.deep.equal(expectedResponse); From 27fe52aacd95dcc9548da646976ed6c7539914da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alberto=20Polo=20Garc=C3=ADa?= Date: Thu, 24 Sep 2020 15:44:47 +0200 Subject: [PATCH 0223/1476] Add GVL ID and bidder code to CriteoId module (#5781) * Add GVL ID and bidder code to CriteoId module * Add gvlid as property to CriteoIdSubmodule Co-authored-by: Jesus Alberto Polo Garcia --- modules/criteoIdSystem.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index c44f0c843ae..017194d0e86 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -11,7 +11,9 @@ import { getRefererInfo } from '../src/refererDetection.js' import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; -export const storage = getStorageManager(); +const gvlid = 91; +const bidderCode = 'criteo'; +export const storage = getStorageManager(gvlid, bidderCode); const bididStorageKey = 'cto_bidid'; const bundleStorageKey = 'cto_bundle'; @@ -111,7 +113,8 @@ export const criteoIdSubmodule = { * used to link submodule with config * @type {string} */ - name: 'criteo', + name: bidderCode, + gvlid: gvlid, /** * decode the stored id value for passing to bid requests * @function From 3612308e6fd679ba4e94ac4419da157f11e8c19e Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:25:40 +0530 Subject: [PATCH 0224/1476] Update BrightMountainMedia cookie sync URL (#5740) --- modules/brightMountainMediaBidAdapter.js | 2 +- test/spec/modules/brightMountainMediaBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 5a285be71c0..aa1076e798a 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -79,7 +79,7 @@ export const spec = { if (syncOptions.iframeEnabled) { return [{ type: 'iframe', - url: 'https://console.brightmountainmedia.com:4444/cookieSync' + url: 'https://console.brightmountainmedia.com:8443/cookieSync' }]; } }, diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 3b7e46e55a7..f6312253722 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -133,7 +133,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.exist; expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.exist; expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe') - expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('https://console.brightmountainmedia.com:4444/cookieSync') + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('https://console.brightmountainmedia.com:8443/cookieSync') }); }); }); From f1ea594016d761c66ddd3ed0a190c9f1bb501927 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 24 Sep 2020 18:06:47 +0200 Subject: [PATCH 0225/1476] Convert id5id to an object to support passing additional data points to platforms (#5756) * move id5id to an object to support passing linkType and other data in the future * update bid adapters supporting the ID5 ID to use the new object instead of a string * remove `.only` from test --- modules/adheseBidAdapter.js | 4 +- modules/adxcgBidAdapter.js | 4 +- modules/avocetBidAdapter.js | 4 +- modules/colossussspBidAdapter.js | 2 +- modules/districtmDMXBidAdapter.js | 2 +- modules/gamoshiBidAdapter.js | 2 +- modules/gridBidAdapter.js | 4 +- modules/id5IdSystem.js | 17 +++++++- modules/livewrappedBidAdapter.js | 2 +- modules/openxBidAdapter.js | 3 ++ modules/ozoneBidAdapter.js | 8 +++- modules/pulsepointBidAdapter.js | 2 +- modules/richaudienceBidAdapter.js | 2 +- modules/spotxBidAdapter.js | 7 ++-- modules/userId/eids.js | 10 ++++- modules/vidazooBidAdapter.js | 3 ++ modules/visxBidAdapter.js | 4 +- test/spec/modules/adheseBidAdapter_spec.js | 2 +- test/spec/modules/adxcgBidAdapter_spec.js | 2 +- test/spec/modules/amxBidAdapter_spec.js | 2 +- test/spec/modules/avocetBidAdapter_spec.js | 4 +- .../modules/colossussspBidAdapter_spec.js | 2 +- .../modules/districtmDmxBidAdapter_spec.js | 4 +- test/spec/modules/eids_spec.js | 42 +++++++++++++++---- test/spec/modules/gamoshiBidAdapter_spec.js | 2 +- test/spec/modules/gridBidAdapter_spec.js | 2 +- test/spec/modules/id5IdSystem_spec.js | 19 +++++---- .../modules/justpremiumBidAdapter_spec.js | 6 ++- .../modules/livewrappedBidAdapter_spec.js | 2 +- .../modules/mediasquareBidAdapter_spec.js | 2 +- test/spec/modules/openxBidAdapter_spec.js | 5 ++- test/spec/modules/ozoneBidAdapter_spec.js | 8 ++-- .../modules/prebidServerBidAdapter_spec.js | 11 ++++- test/spec/modules/pubmaticBidAdapter_spec.js | 10 ++--- .../spec/modules/pulsepointBidAdapter_spec.js | 2 +- .../modules/richaudienceBidAdapter_spec.js | 16 +++++-- .../modules/smartadserverBidAdapter_spec.js | 2 +- test/spec/modules/spotxBidAdapter_spec.js | 5 ++- test/spec/modules/undertoneBidAdapter_spec.js | 4 +- test/spec/modules/userId_spec.js | 12 +++--- test/spec/modules/vidazooBidAdapter_spec.js | 1 + test/spec/modules/visxBidAdapter_spec.js | 2 +- .../yuktamediaAnalyticsAdapter_spec.js | 2 +- 43 files changed, 172 insertions(+), 79 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 606e56c13d5..80758668a95 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -150,8 +150,8 @@ function getAccount(validBidRequests) { } function getId5Id(validBidRequests) { - if (validBidRequests[0] && validBidRequests[0].userId && validBidRequests[0].userId.id5id) { - return validBidRequests[0].userId.id5id; + if (validBidRequests[0] && validBidRequests[0].userId && validBidRequests[0].userId.id5id && validBidRequests[0].userId.id5id.uid) { + return validBidRequests[0].userId.id5id.uid; } } diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index 2d5c64dfe53..e61792288ed 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -170,8 +170,8 @@ export const spec = { beaconParams.tdid = validBidRequests[0].userId.tdid; } - if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.id5id'))) { - beaconParams.id5id = validBidRequests[0].userId.id5id; + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.id5id.uid'))) { + beaconParams.id5id = validBidRequests[0].userId.id5id.uid; } if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.idl_env'))) { diff --git a/modules/avocetBidAdapter.js b/modules/avocetBidAdapter.js index 1163ac830ba..7a9e5062c0f 100644 --- a/modules/avocetBidAdapter.js +++ b/modules/avocetBidAdapter.js @@ -77,8 +77,8 @@ export const spec = { // ID5 identifier let id5id; - if (bidRequests[0].userId && bidRequests[0].userId.id5id) { - id5id = bidRequests[0].userId.id5id; + if (bidRequests[0].userId && bidRequests[0].userId.id5id && bidRequests[0].userId.id5id.uid) { + id5id = bidRequests[0].userId.id5id.uid; } // Build the avocet ext object diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index baa60a76a0d..a3beb723528 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -102,7 +102,7 @@ export const spec = { if (bid.userId) { getUserId(placement.eids, bid.userId.britepoolid, 'britepool.com'); getUserId(placement.eids, bid.userId.idl_env, 'identityLink'); - getUserId(placement.eids, bid.userId.id5id, 'id5-sync.com') + getUserId(placement.eids, utils.deepAccess(bid, 'userId.id5id.uid'), 'id5-sync.com', utils.deepAccess(bid, 'userId.id5id.ext')); getUserId(placement.eids, bid.userId.tdid, 'adserver.org', { rtiPartner: 'TDID' }); diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index 21f3b7b3586..bcb2bb97210 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -106,7 +106,7 @@ export const spec = { let eids = []; if (bidRequest[0] && bidRequest[0].userId) { bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.idl_env`), 'liveramp.com', 1); - bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.id5id`), 'id5-sync.com', 1); + bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.id5id.uid`), 'id5-sync.com', 1); bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.pubcid`), 'pubcid.org', 1); bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.tdid`), 'adserver.org', 1); bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.criteoId`), 'criteo.com', 1); diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 1316d74e430..2e09cf55d0a 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -157,7 +157,7 @@ export const spec = { let eids = []; if (bidRequest && bidRequest.userId) { - addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 'ID5ID'); + addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 'ID5ID'); addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.tdid`), 'adserver.org', 'TDID'); } if (eids.length > 0) { diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 32274fed2e6..378fc5a7efe 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -431,8 +431,8 @@ function buildNewRequest(validBidRequests, bidderRequest) { if (userId.tdid) { userExt.unifiedid = userId.tdid; } - if (userId.id5id) { - userExt.id5id = userId.id5id; + if (userId.id5id && userId.id5id.uid) { + userExt.id5id = userId.id5id.uid; } if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { userExt.digitrustid = userId.digitrustid.data.id; diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index ae3c7e55863..47064e0a1a9 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -39,14 +39,27 @@ export const id5IdSubmodule = { * @returns {(Object|undefined)} */ decode(value) { + let uid; + let linkType = 0; + if (value && typeof value.ID5ID === 'string') { // don't lose our legacy value from cache - return { 'id5id': value.ID5ID }; + uid = value.ID5ID; } else if (value && typeof value.universal_uid === 'string') { - return { 'id5id': value.universal_uid }; + uid = value.universal_uid; + linkType = value.link_type || linkType; } else { return undefined; } + + return { + 'id5id': { + 'uid': uid, + 'ext': { + 'linkType': linkType + } + } + }; }, /** diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 50e9eea768b..0a5464fd21f 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -277,7 +277,7 @@ function handleEids(bidRequests) { const bidRequest = bidRequests[0]; if (bidRequest && bidRequest.userId) { AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcommon', 1); // Also add this to eids - AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 1); + AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 1); } if (eids.length > 0) { return {user: {ext: {eids}}}; diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index f4b6288cd55..bbf3d6fdea1 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -281,6 +281,9 @@ function appendUserIdsToQueryParams(queryParams, userIds) { case 'parrableId': queryParams[key] = userIdObjectOrValue.eid; break; + case 'id5id': + queryParams[key] = userIdObjectOrValue.uid; + break; default: queryParams[key] = userIdObjectOrValue; } diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 451ab654c53..4246a39bc69 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -539,7 +539,7 @@ export const spec = { */ findAllUserIds(bidRequest) { var ret = {}; - let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'digitrustid', 'criteortus']; + let searchKeysSingle = ['pubcid', 'tdid', 'parrableId', 'idl_env', 'digitrustid', 'criteortus']; if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; @@ -551,6 +551,10 @@ export const spec = { if (lipbid) { ret['lipb'] = {'lipbid': lipbid}; } + var id5id = utils.deepAccess(bidRequest.userId, 'id5id.uid'); + if (id5id) { + ret['id5id'] = id5id; + } } if (!ret.hasOwnProperty('pubcid')) { var pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); @@ -675,7 +679,7 @@ export const spec = { if (bidRequest && bidRequest.userId) { this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcid', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcommon', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 1); + this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteortus.${BIDDER_CODE}.userid`), 'criteortus', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 1); this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.lipb.lipbid`), 'liveintent.com', 1); diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 33fdaa44100..12937dbec9d 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -420,7 +420,7 @@ function user(bidRequest, bidderRequest) { addExternalUserId(ext.eids, bidRequest.userId.britepoolid, 'britepool.com'); addExternalUserId(ext.eids, bidRequest.userId.criteoId, 'criteo'); addExternalUserId(ext.eids, bidRequest.userId.idl_env, 'identityLink'); - addExternalUserId(ext.eids, bidRequest.userId.id5id, 'id5-sync.com'); + addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.id5id.uid'), 'id5-sync.com', utils.deepAccess(bidRequest, 'userId.id5id.ext')); addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.parrableId.eid'), 'parrable.com'); // liveintent if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index e51cc79eb82..3b899e2179d 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -201,7 +201,7 @@ function raiSetEids(bid) { let eids = []; if (bid && bid.userId) { - raiSetUserId(bid, eids, 'id5-sync.com', utils.deepAccess(bid, `userId.id5id`)); + raiSetUserId(bid, eids, 'id5-sync.com', utils.deepAccess(bid, `userId.id5id.uid`)); raiSetUserId(bid, eids, 'pubcommon', utils.deepAccess(bid, `userId.pubcid`)); raiSetUserId(bid, eids, 'criteo.com', utils.deepAccess(bid, `userId.criteoId`)); raiSetUserId(bid, eids, 'liveramp.com', utils.deepAccess(bid, `userId.idl_env`)); diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 6a80fd6dc0d..7c4e2e3ef63 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -228,14 +228,15 @@ export const spec = { } // ID5 fied - if (bid && bid.userId && bid.userId.id5id) { + if (utils.deepAccess(bid, 'userId.id5id.uid')) { userExt.eids = userExt.eids || []; userExt.eids.push( { source: 'id5-sync.com', uids: [{ - id: bid.userId.id5id - }] + id: bid.userId.id5id.uid + }], + ext: bid.userId.id5id.ext || {} } ) } diff --git a/modules/userId/eids.js b/modules/userId/eids.js index eebd0146d50..9e39b548a57 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -30,8 +30,16 @@ const USER_IDS_CONFIG = { // id5Id 'id5id': { + getValue: function(data) { + return data.uid + }, source: 'id5-sync.com', - atype: 1 + atype: 1, + getEidExt: function(data) { + if (data.ext) { + return data.ext; + } + } }, // parrableId diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 0718f22d0d2..4b3b1767cec 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -117,6 +117,9 @@ function appendUserIdsToRequestPayload(payloadRef, userIds) { case 'parrableId': payloadRef[key] = userId.eid; break; + case 'id5id': + payloadRef[key] = userId.uid; + break; default: payloadRef[key] = userId; } diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 511e658c947..725482d07c3 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -100,8 +100,8 @@ export const spec = { if (payloadUserId.tdid) { payload.tdid = payloadUserId.tdid; } - if (payloadUserId.id5id) { - payload.id5 = payloadUserId.id5id; + if (payloadUserId.id5id && payloadUserId.id5id.uid) { + payload.id5 = payloadUserId.id5id.uid; } if (payloadUserId.digitrustid && payloadUserId.digitrustid.data && payloadUserId.digitrustid.data.id) { payload.dtid = payloadUserId.digitrustid.data.id; diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index aa4872641b4..4d888db269d 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -116,7 +116,7 @@ describe('AdheseAdapter', function () { }); it('should include id5 id as /x5 param', function () { - let req = spec.buildRequests([ bidWithParams({}, { 'id5id': 'ID5-1234567890' }) ], bidderRequest); + let req = spec.buildRequests([ bidWithParams({}, { 'id5id': { 'uid': 'ID5-1234567890' } }) ], bidderRequest); expect(JSON.parse(req.data).parameters).to.deep.include({ 'x5': [ 'ID5-1234567890' ] }); }); diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 306914960c3..f9aaea308a7 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -281,7 +281,7 @@ describe('AdxcgAdapter', function () { let bid = deepClone([bidBanner]); let bidderRequests = {}; - bid[0].userId = {id5id: 'id5idsample'}; + bid[0].userId = {id5id: {uid: 'id5idsample'}}; it('should send pubcid if available', function () { let request = spec.buildRequests(bid, bidderRequests); diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 4b21244501d..91315da8801 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -224,7 +224,7 @@ describe('AmxBidAdapter', () => { britepoolid: 'sample-britepool', criteoId: 'sample-criteo', digitrustid: {data: {id: 'sample-digitrust'}}, - id5id: 'sample-id5', + id5id: {uid: 'sample-id5'}, idl_env: 'sample-liveramp', lipb: {lipbid: 'sample-liveintent'}, netId: 'sample-netid', diff --git a/test/spec/modules/avocetBidAdapter_spec.js b/test/spec/modules/avocetBidAdapter_spec.js index 4cfd8ab89d4..2a2f29e48d2 100644 --- a/test/spec/modules/avocetBidAdapter_spec.js +++ b/test/spec/modules/avocetBidAdapter_spec.js @@ -69,7 +69,9 @@ describe('Avocet adapter', function () { placement: '012345678901234567890123', }, userId: { - id5id: 'test' + id5id: { + uid: 'test' + } } }, { diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index df9bdcbd47b..a10a7590677 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -108,7 +108,7 @@ describe('ColossussspAdapter', function () { bid.userId.britepoolid = 'britepoolid123'; bid.userId.idl_env = 'idl_env123'; bid.userId.tdid = 'tdid123'; - bid.userId.id5id = 'id5id123' + bid.userId.id5id = { uid: 'id5id123' }; let serverRequest = spec.buildRequests([bid], bidderRequest); it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js index 9b65ab855d8..90e6957fc2c 100644 --- a/test/spec/modules/districtmDmxBidAdapter_spec.js +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -103,7 +103,9 @@ const bidRequest = [{ id: {} } }, - id5id: {}, + id5id: { + uid: '' + }, pubcid: {}, tdid: {}, criteoId: {}, diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index a6a44f9296d..830f542ef40 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -29,15 +29,39 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('id5Id', function() { - const userId = { - id5id: 'some-random-id-value' - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'id5-sync.com', - uids: [{id: 'some-random-id-value', atype: 1}] + describe('id5Id', function() { + it('does not include an ext if not provided', function() { + const userId = { + id5id: { + uid: 'some-random-id-value' + } + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'id5-sync.com', + uids: [{ id: 'some-random-id-value', atype: 1 }] + }); + }); + + it('includes ext if provided', function() { + const userId = { + id5id: { + uid: 'some-random-id-value', + ext: { + linkType: 0 + } + } + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'id5-sync.com', + uids: [{ id: 'some-random-id-value', atype: 1 }], + ext: { + linkType: 0 + } + }); }); }); diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js index 79f58470cb3..2996b853046 100644 --- a/test/spec/modules/gamoshiBidAdapter_spec.js +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -423,7 +423,7 @@ describe('GamoshiAdapter', () => { it('build request with ID5 Id', () => { const bidRequestClone = utils.deepClone(bidRequest); bidRequestClone.userId = {}; - bidRequestClone.userId.id5id = 'id5-user-id'; + bidRequestClone.userId.id5id = { uid: 'id5-user-id' }; let request = spec.buildRequests([bidRequestClone], bidRequestClone)[0]; expect(request.data.user.ext.eids).to.deep.equal([{ 'source': 'id5-sync.com', diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 650712e435f..1cfca4779ad 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -536,7 +536,7 @@ describe('TheMediaGrid Adapter', function () { const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ userId: { - id5id: 'id5id_1', + id5id: { uid: 'id5id_1' }, tdid: 'tdid_1', digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, lipb: {lipbid: 'lipb_1'} diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 05ecec8dc36..cea6bdf92b9 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -50,7 +50,9 @@ describe('ID5 ID System', function() { return { name: ID5_MODULE_NAME, value: { - id5id: value + id5id: { + uid: value + } } } } @@ -238,10 +240,13 @@ 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).to.equal(ID5_STORED_ID); + expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); expect(bid.userIdAsEids[0]).to.deep.equal({ source: ID5_SOURCE, - uids: [{ id: ID5_STORED_ID, atype: 1 }] + uids: [{ id: ID5_STORED_ID, atype: 1 }], + ext: { + linkType: 0 + } }); }); }); @@ -258,7 +263,7 @@ 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).to.equal(ID5_STORED_ID); + expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); expect(bid.userIdAsEids[0]).to.deep.equal({ source: ID5_SOURCE, uids: [{ id: ID5_STORED_ID, atype: 1 }] @@ -368,13 +373,13 @@ describe('ID5 ID System', function() { }); describe('Decode stored object', function() { - const decodedObject = { 'id5id': ID5_STORED_ID }; + const expectedDecodedObject = { id5id: { uid: ID5_STORED_ID, ext: { linkType: 0 } } }; it('should properly decode from a stored object', function() { - expect(id5IdSubmodule.decode(ID5_STORED_OBJ)).to.deep.equal(decodedObject); + expect(id5IdSubmodule.decode(ID5_STORED_OBJ)).to.deep.equal(expectedDecodedObject); }); it('should properly decode from a legacy stored object', function() { - expect(id5IdSubmodule.decode(ID5_LEGACY_STORED_OBJ)).to.deep.equal(decodedObject); + expect(id5IdSubmodule.decode(ID5_LEGACY_STORED_OBJ)).to.deep.equal(expectedDecodedObject); }); it('should return undefined if passed a string', function() { expect(id5IdSubmodule.decode('somestring')).to.eq(undefined); diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index c162b785aed..7cc154254ff 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -21,7 +21,9 @@ describe('justpremium adapter', function () { }, userId: { tdid: '1111111', - id5id: '2222222', + id5id: { + uid: '2222222' + }, digitrustid: { data: { id: '3333333' @@ -84,7 +86,7 @@ describe('justpremium adapter', function () { expect(jpxRequest.version.jp_adapter).to.equal('1.7') expect(jpxRequest.pubcid).to.equal('0000000') expect(jpxRequest.uids.tdid).to.equal('1111111') - expect(jpxRequest.uids.id5id).to.equal('2222222') + expect(jpxRequest.uids.id5id.uid).to.equal('2222222') expect(jpxRequest.uids.digitrustid.data.id).to.equal('3333333') expect(jpxRequest.us_privacy).to.equal('1YYN') }) diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 053e5102cbf..2d5ba3f48df 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -787,7 +787,7 @@ describe('Livewrapped adapter tests', function () { let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; testbidRequest.bids[0].userId = {}; - testbidRequest.bids[0].userId.id5id = 'id5-user-id'; + testbidRequest.bids[0].userId.id5id = { uid: 'id5-user-id' }; let result = spec.buildRequests(testbidRequest.bids, testbidRequest); let data = JSON.parse(result.data); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 351fbb40228..5d930f2b6ac 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -53,7 +53,7 @@ describe('MediaSquare bid adapter tests', function () { canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' }, uspConsent: '111222333', - userId: {'id5id': '1111'}, + userId: { 'id5id': { uid: '1111' } }, schain: { 'ver': '1.0', 'complete': 1, diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 0808d78c0aa..121f8e76a07 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1043,7 +1043,7 @@ describe('OpenxAdapter', function () { britepoolid: '1111-britepoolid', criteoId: '1111-criteoId', digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - id5id: '1111-id5id', + id5id: {uid: '1111-id5id'}, idl_env: '1111-idl_env', lipb: {lipbid: '1111-lipb'}, netId: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', @@ -1096,6 +1096,9 @@ describe('OpenxAdapter', function () { case 'parrableId': userIdValue = EXAMPLE_DATA_BY_ATTR.parrableId.eid; break; + case 'id5id': + userIdValue = EXAMPLE_DATA_BY_ATTR.id5id.uid; + break; default: userIdValue = EXAMPLE_DATA_BY_ATTR[userIdProviderKey]; } diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index f20e75dfedd..c1022608b4a 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -66,7 +66,7 @@ var validBidRequestsWithUserIdData = [ params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, 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', 'id5id': 'ID5-someId', 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {eid: 'parrableid123'}} + userId: {'pubcid': '12345678', 'id5id': { 'uid': 'ID5-someId' }, 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {eid: 'parrableid123'}} } ]; var validBidRequestsMinimal = [ @@ -297,7 +297,7 @@ var validBidderRequest1OutstreamVideo2020 = { } }, 'userId': { - 'id5id': 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA', + 'id5id': { uid: 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA' }, 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' }, 'userIdAsEids': [ @@ -2118,7 +2118,7 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': '2222', + 'id5id': {'uid': '2222'}, 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, @@ -2138,7 +2138,7 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': '2222', + 'id5id': {'uid': '2222'}, 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d6f755914e5..d069bb74944 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1178,7 +1178,13 @@ describe('S2S Adapter', function () { lipbid: 'li-xyz', segments: ['segA', 'segB'] }, - idl_env: '0000-1111-2222-3333' + idl_env: '0000-1111-2222-3333', + id5id: { + uid: '11111', + ext: { + linkType: 'some-link-type' + } + } }; userIdBidRequest[0].bids[0].userIdAsEids = createEidsArray(userIdBidRequest[0].bids[0].userId); @@ -1199,6 +1205,9 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments.length).is.equal(2); expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[0]).is.equal('segA'); expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[1]).is.equal('segB'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].uids[0].id).is.equal('11111'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].ext.linkType).is.equal('some-link-type'); // LiveRamp should exist expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveramp.com')[0].uids[0].id).is.equal('0000-1111-2222-3333'); }); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index c1f62ee9f2b..37deb0bca9c 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1605,7 +1605,7 @@ describe('PubMatic adapter', function () { describe('ID5 Id', function() { it('send the id5 id if it is present', function() { bidRequests[0].userId = {}; - bidRequests[0].userId.id5id = 'id5-user-id'; + bidRequests[0].userId.id5id = { uid: 'id5-user-id' }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); @@ -1620,22 +1620,22 @@ describe('PubMatic adapter', function () { it('do not pass if not string', function() { bidRequests[0].userId = {}; - bidRequests[0].userId.id5id = 1; + bidRequests[0].userId.id5id = { uid: 1 }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = []; + bidRequests[0].userId.id5id = { uid: [] }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); request = spec.buildRequests(bidRequests, {}); data = JSON.parse(request.data); expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = null; + bidRequests[0].userId.id5id = { uid: null }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); request = spec.buildRequests(bidRequests, {}); data = JSON.parse(request.data); expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = {}; + bidRequests[0].userId.id5id = { uid: {} }; bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); request = spec.buildRequests(bidRequests, {}); data = JSON.parse(request.data); diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index d71bd018ab3..a6f5ff2a0dc 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -630,7 +630,7 @@ describe('PulsePoint Adapter Tests', function () { britepoolid: 'britepool_id123', criteoId: 'criteo_id234', idl_env: 'idl_id123', - id5id: 'id5id_234', + id5id: { uid: 'id5id_234' }, parrableId: { eid: 'parrable_id234' }, lipb: { lipbid: 'liveintent_id123' diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index f18e67a3ac5..1c710c46ea2 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -357,7 +357,7 @@ describe('Richaudience adapter tests', function () { }); it('Verify build id5', function () { DEFAULT_PARAMS_WO_OPTIONAL[0].userId = {}; - DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = 'id5-user-id'; + DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: 'id5-user-id' }; var request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); var requestContent = JSON.parse(request[0].data); @@ -369,13 +369,23 @@ describe('Richaudience adapter tests', function () { var request; DEFAULT_PARAMS_WO_OPTIONAL[0].userId = {}; - DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = 1; + DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: 1 }; request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); var requestContent = JSON.parse(request[0].data); expect(requestContent.user.eids).to.equal(undefined); - DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = []; + DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: [] }; + request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); + requestContent = JSON.parse(request[0].data); + expect(requestContent.user.eids).to.equal(undefined); + + DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: null }; + request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); + requestContent = JSON.parse(request[0].data); + expect(requestContent.user.eids).to.equal(undefined); + + DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: {} }; request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); requestContent = JSON.parse(request[0].data); expect(requestContent.user.eids).to.equal(undefined); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 2faad47aca8..2de7cb2c9ff 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -71,7 +71,7 @@ describe('Smart bid adapter tests', function () { britepoolid: '1111', criteoId: '1111', digitrustid: { data: { id: 'DTID', keyv: 4, privacy: { optout: false }, producer: 'ABC', version: 2 } }, - id5id: '1111', + id5id: { uid: '1111' }, idl_env: '1111', lipbid: '1111', parrableid: 'eidVersion.encryptionKeyReference.encryptedValue', diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index baece710ee1..cf651670518 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -152,7 +152,7 @@ describe('the spotx adapter', function () { }; bid.userId = { - id5id: 'id5id_1', + id5id: { uid: 'id5id_1' }, tdid: 'tdid_1' }; @@ -202,7 +202,8 @@ describe('the spotx adapter', function () { source: 'id5-sync.com', uids: [{ id: 'id5id_1' - }] + }], + ext: {} }, { source: 'adserver.org', diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index 72321ce415b..e8729965c50 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -94,7 +94,8 @@ const bidReqUserIds = [{ userId: { idl_env: '1111', tdid: '123456', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} + digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + id5id: { uid: '1111' } } }, { @@ -316,6 +317,7 @@ describe('Undertone Adapter', () => { expect(bidCommons.uids.tdid).to.equal('123456'); expect(bidCommons.uids.idl_env).to.equal('1111'); expect(bidCommons.uids.digitrustid.data.id).to.equal('DTID'); + expect(bidCommons.uids.id5id.uid).to.equal('1111'); }); it('should send page sizes sizes correctly', function () { const request = spec.buildRequests(bidReqUserIds, bidderReq); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index e9057ee26d9..749dd653865 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1235,8 +1235,8 @@ describe('User ID', function() { expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('testunifiedid'); // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id'); - expect(bid.userId.id5id).to.equal('testid5id'); + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); // check that identityLink id data was copied to bid expect(bid).to.have.deep.nested.property('userId.idl_env'); expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); @@ -1328,8 +1328,8 @@ describe('User ID', function() { expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id'); - expect(bid.userId.id5id).to.equal('testid5id'); + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); // also check that identityLink id data was copied to bid expect(bid).to.have.deep.nested.property('userId.idl_env'); expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); @@ -1461,8 +1461,8 @@ describe('User ID', function() { expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id'); - expect(bid.userId.id5id).to.equal('testid5id'); + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); // also check that identityLink id data was copied to bid expect(bid).to.have.deep.nested.property('userId.idl_env'); expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 1a503e46b5c..d7f20c434ca 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -244,6 +244,7 @@ describe('VidazooBidAdapter', function () { case 'digitrustid': return { data: { id: id } }; case 'lipb': return { lipbid: id }; case 'parrableId': return { eid: id }; + case 'id5id': return { uid: id }; default: return id; } })(); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 2a4f8e4c7b5..a06f530e145 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -231,7 +231,7 @@ describe('VisxAdapter', function () { const schainBidRequests = [ Object.assign({userId: { tdid: '111', - id5id: '222', + id5id: { uid: '222' }, digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} }}, bidRequests[0]), bidRequests[1], diff --git a/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js index c8643c547e0..e8eb4ab73be 100644 --- a/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js +++ b/test/spec/modules/yuktamediaAnalyticsAdapter_spec.js @@ -30,7 +30,7 @@ let prebidAuction = { } }, 'userId': { - 'id5id': 'ID5-ZHMOxXeRXPe3inZKGD-Lj0g7y8UWdDbsYXQ_n6aWMQ', + 'id5id': { uid: 'ID5-ZHMOxXeRXPe3inZKGD-Lj0g7y8UWdDbsYXQ_n6aWMQ' }, 'parrableid': '01.1595590997.46d951017bdc272ca50b88dbcfb0545cfc636bec3e3d8c02091fb1b413328fb2fd3baf65cb4114b3f782895fd09f82f02c9042b85b42c4654d08ba06dc77f0ded936c8ea3fc4085b4a99', 'pubcid': '100a8bc9-f588-4c22-873e-a721cb68bc34' }, From 7aff389003092410fb722326e4c6042cf7fefbff Mon Sep 17 00:00:00 2001 From: Stephan Brosinski Date: Fri, 25 Sep 2020 15:03:32 +0200 Subject: [PATCH 0226/1476] Smaato: Support in-app use cases (#5765) --- modules/smaatoBidAdapter.js | 9 +++- test/spec/modules/smaatoBidAdapter_spec.js | 57 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index ce0edb1e19c..49b4ed6aa34 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -110,6 +110,13 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + if (utils.deepAccess(validBidRequests[0], 'params.app')) { + const geo = utils.deepAccess(validBidRequests[0], 'params.app.geo'); + utils.deepSetValue(request, 'device.geo', geo); + const ifa = utils.deepAccess(validBidRequests[0], 'params.app.ifa') + utils.deepSetValue(request, 'device.ifa', ifa); + } + utils.logInfo('[SMAATO] OpenRTB Request:', request); return JSON.stringify(request); } @@ -185,7 +192,7 @@ export const spec = { ttl: ttlSec, creativeId: b.crid, dealId: b.dealid || null, - netRevenue: true, + netRevenue: utils.deepAccess(b, 'ext.net', true), currency: res.cur, meta: { advertiserDomains: b.adomain, diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 95eb36d8a0d..13716b51436 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -291,6 +291,36 @@ const combinedBannerAndVideoBidRequest = { bidderWinsCount: 0 }; +const inAppBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId', + app: { + ifa: 'aDeviceId', + geo: { + lat: 33.3, + lon: -88.8 + } + } + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 +}; + describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { it('has valid params', () => { @@ -462,6 +492,27 @@ describe('smaatoBidAdapterTest', () => { }); }); + describe('in-app requests', () => { + it('add geo and ifa info to device object', () => { + let req = JSON.parse(spec.buildRequests([inAppBidRequest], defaultBidderRequest).data); + expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); + expect(req.device.ifa).to.equal('aDeviceId'); + }); + it('add only ifa to device object', () => { + let inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); + delete inAppBidRequestWithoutGeo.params.app.geo + let req = JSON.parse(spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest).data); + + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.equal('aDeviceId'); + }); + it('add no specific device info if param does not exist', () => { + let req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.not.exist; + }); + }); + describe('interpretResponse', () => { it('single image reponse', () => { const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); @@ -501,5 +552,11 @@ describe('smaatoBidAdapterTest', () => { expect(bids[0].ttl).to.equal(400); clock.restore(); }); + it('uses net revenue flag send from server', () => { + let resp = openRtbBidResponse(ADTYPE_IMG); + resp.body.seatbid[0].bid[0].ext = {net: false}; + const bids = spec.interpretResponse(resp, request); + expect(bids[0].netRevenue).to.equal(false); + }) }); }); From 0fd72056f44a2af9dead69d19d194eac02aed089 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Fri, 25 Sep 2020 19:14:03 +0530 Subject: [PATCH 0227/1476] Added GVLID to Media.net Analytics Adapter (#5789) Co-authored-by: monis.q --- modules/medianetAnalyticsAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 62958cfbfd2..c76f695cc2b 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -728,7 +728,8 @@ medianetAnalytics.enableAnalytics = function (configuration) { adapterManager.registerAnalyticsAdapter({ adapter: medianetAnalytics, - code: 'medianetAnalytics' + code: 'medianetAnalytics', + gvlid: 142, }); export default medianetAnalytics; From c8176d7b093c54d42fd6660ede6dfceb246b0c1c Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 25 Sep 2020 16:45:26 +0200 Subject: [PATCH 0228/1476] Add video ad support to ablida bid adapter (#5782) * add onBidWon function, add bidder adapter version to bid requests * add support for native * use triggerPxel instead of ajax, because ajax was called 3 times with native * add gdpr consent to bid requests * update tests * add video ad support --- modules/ablidaBidAdapter.js | 8 +++++--- modules/ablidaBidAdapter.md | 16 ++++++++++++++++ test/spec/modules/ablidaBidAdapter_spec.js | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index af9c206700f..2400952367f 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -1,14 +1,14 @@ import * as utils from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'ablida'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -35,6 +35,8 @@ export const spec = { let sizes = [] if (bidRequest.mediaTypes && bidRequest.mediaTypes[BANNER] && bidRequest.mediaTypes[BANNER].sizes) { sizes = bidRequest.mediaTypes[BANNER].sizes; + } else if (bidRequest.mediaTypes[VIDEO] && bidRequest.mediaTypes[VIDEO].playerSize) { + sizes = bidRequest.mediaTypes[VIDEO].playerSize } const jaySupported = 'atob' in window && 'currentScript' in document; const device = getDevice(); @@ -46,7 +48,7 @@ export const spec = { referer: bidderRequest.refererInfo.referer, jaySupported: jaySupported, device: device, - adapterVersion: 4, + adapterVersion: 5, mediaTypes: bidRequest.mediaTypes, gdprConsent: bidderRequest.gdprConsent }; diff --git a/modules/ablidaBidAdapter.md b/modules/ablidaBidAdapter.md index 001bee4f35c..e0a9f3f9405 100644 --- a/modules/ablidaBidAdapter.md +++ b/modules/ablidaBidAdapter.md @@ -51,6 +51,22 @@ Module that connects to Ablida's bidder for bids. } } ] + }, { + code: 'video-ad', + mediaTypes: { + video: { + playerSize: [[640, 360]], + context: 'instream' + } + }, + bids: [ + { + bidder: 'ablida', + params: { + placementId: 'instream-demo' + } + } + ] } ]; ``` diff --git a/test/spec/modules/ablidaBidAdapter_spec.js b/test/spec/modules/ablidaBidAdapter_spec.js index 0743bf0a896..73109d8cf16 100644 --- a/test/spec/modules/ablidaBidAdapter_spec.js +++ b/test/spec/modules/ablidaBidAdapter_spec.js @@ -75,7 +75,7 @@ describe('ablidaBidAdapter', function () { method: 'POST', url: ENDPOINT_URL, data: { - adapterVersion: 4, + adapterVersion: 5, bidId: '2b8c4de0116e54', categories: undefined, device: 'desktop', From 05a5a969b8f3a679308c3546386565838ae472d7 Mon Sep 17 00:00:00 2001 From: ghguo Date: Fri, 25 Sep 2020 14:40:13 -0400 Subject: [PATCH 0229/1476] Add adrelevantis adapter (#5735) * Update adrelevantis adapter * Update Adrelevantis Bid Adapter and Add Unit Tests Commit changes suggested by @jsnellbaker on pull request #5735 --- modules/adrelevantisBidAdapter.js | 603 ++++++++++++++ modules/adrelevantisBidAdapter.md | 120 +++ .../modules/adrelevantisBidAdapter_spec.js | 769 ++++++++++++++++++ 3 files changed, 1492 insertions(+) create mode 100644 modules/adrelevantisBidAdapter.js create mode 100644 modules/adrelevantisBidAdapter.md create mode 100644 test/spec/modules/adrelevantisBidAdapter_spec.js diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js new file mode 100644 index 00000000000..5da941c65ca --- /dev/null +++ b/modules/adrelevantisBidAdapter.js @@ -0,0 +1,603 @@ +import { Renderer } from '../src/Renderer.js'; +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import { OUTSTREAM, INSTREAM } from '../src/video.js'; + +const BIDDER_CODE = 'adrelevantis'; +const URL = 'https://ssp.adrelevantis.com/prebid'; +const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', + 'startdelay', 'skippable', 'playback_method', 'frameworks']; +const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; +const APP_DEVICE_PARAMS = ['geo', 'device_id']; // appid is collected separately +const SOURCE = 'pbjs'; +const MAX_IMPS_PER_REQUEST = 15; + +const NATIVE_MAPPING = { + body: 'description', + body2: 'desc2', + cta: 'ctatext', + image: { + serverName: 'main_image', + requiredParams: { required: true } + }, + icon: { + serverName: 'icon', + requiredParams: { required: true } + }, + sponsoredBy: 'sponsored_by', + privacyLink: 'privacy_link', + salePrice: 'saleprice', + displayUrl: 'displayurl' +}; + +export const spec = { + code: BIDDER_CODE, + aliases: ['adr', 'adsmart', 'compariola'], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + /** + * 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.placementId); + }, + + /** + * 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(bidRequests, bidderRequest) { + const tags = bidRequests.map(bidToTag); + const userObjBid = find(bidRequests, hasUserInfo); + let userObj; + if (config.getConfig('coppa') === true) { + userObj = {'coppa': true}; + } + if (userObjBid) { + userObj = {}; + Object.keys(userObjBid.params.user) + .filter(param => includes(USER_PARAMS, param)) + .forEach(param => userObj[param] = userObjBid.params.user[param]); + } + + const appDeviceObjBid = find(bidRequests, hasAppDeviceInfo); + let appDeviceObj; + if (appDeviceObjBid && appDeviceObjBid.params && appDeviceObjBid.params.app) { + appDeviceObj = {}; + Object.keys(appDeviceObjBid.params.app) + .filter(param => includes(APP_DEVICE_PARAMS, param)) + .forEach(param => appDeviceObj[param] = appDeviceObjBid.params.app[param]); + } + + const appIdObjBid = find(bidRequests, hasAppId); + let appIdObj; + if (appIdObjBid && appIdObjBid.params && appDeviceObjBid.params.app && appDeviceObjBid.params.app.id) { + appIdObj = { + appid: appIdObjBid.params.app.id + }; + } + + const payload = { + tags: [...tags], + user: userObj, + sdk: { + source: SOURCE, + version: '$prebid.version$' + } + }; + + if (appDeviceObjBid) { + payload.device = appDeviceObj + } + if (appIdObjBid) { + payload.app = appIdObj; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + // note - objects for impbus use underscore instead of camelCase + payload.gdpr_consent = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + } + + if (bidderRequest && bidderRequest.refererInfo) { + let refererinfo = { + rd_ref: encodeURIComponent(bidderRequest.refererInfo.referer), + rd_top: bidderRequest.refererInfo.reachedTop, + rd_ifs: bidderRequest.refererInfo.numIframes, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + } + payload.referrer_detection = refererinfo; + } + + let fpdcfg = config.getConfig('fpd') + if (fpdcfg && fpdcfg.context) { + let fdata = { + keywords: fpdcfg.context.keywords, + category: fpdcfg.context.data.category + } + payload.fpd = fdata; + } + + const request = formatRequest(payload, bidderRequest); + return request; + }, + + /** + * 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, {bidderRequest}) { + serverResponse = serverResponse.body; + const bids = []; + if (!serverResponse || serverResponse.error) { + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; + if (serverResponse && serverResponse.error) { errorMessage += `: ${serverResponse.error}`; } + utils.logError(errorMessage); + return bids; + } + + if (serverResponse.tags) { + serverResponse.tags.forEach(serverBid => { + const rtbBid = getRtbBid(serverBid); + if (rtbBid) { + if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) { + const bid = newBid(serverBid, rtbBid, bidderRequest); + bid.mediaType = parseMediaType(rtbBid); + bids.push(bid); + } + } + }); + } + + return bids; + }, + + transformBidParams: function(params, isOpenRtb) { + params = utils.convertTypes({ + 'placementId': 'number', + 'keywords': utils.transformBidderParamKeywords + }, params); + + if (isOpenRtb) { + params.use_pmt_rule = (typeof params.usePaymentRule === 'boolean') ? params.usePaymentRule : false; + if (params.usePaymentRule) { delete params.usePaymentRule; } + + if (isPopulatedArray(params.keywords)) { + params.keywords.forEach(deleteValues); + } + + Object.keys(params).forEach(paramKey => { + let convertedKey = utils.convertCamelToUnderscore(paramKey); + if (convertedKey !== paramKey) { + params[convertedKey] = params[paramKey]; + delete params[paramKey]; + } + }); + } + + return params; + } +} + +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + +function formatRequest(payload, bidderRequest) { + let request = []; + + if (payload.tags.length > MAX_IMPS_PER_REQUEST) { + const clonedPayload = utils.deepClone(payload); + + utils.chunk(payload.tags, MAX_IMPS_PER_REQUEST).forEach(tags => { + clonedPayload.tags = tags; + const payloadString = JSON.stringify(clonedPayload); + request.push({ + method: 'POST', + url: URL, + data: payloadString, + bidderRequest + }); + }); + } else { + const payloadString = JSON.stringify(payload); + request = { + method: 'POST', + url: URL, + data: payloadString, + bidderRequest + }; + } + + return request; +} + +function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { + const renderer = Renderer.install({ + id: rtbBid.renderer_id, + url: rtbBid.renderer_url, + config: rendererOptions, + loaded: false, + adUnitCode + }); + + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + renderer.setEventHandlers({ + impression: () => utils.logMessage('AdRelevantis outstream video impression event'), + loaded: () => utils.logMessage('AdRelevantis outstream video loaded event'), + ended: () => { + utils.logMessage('AdRelevantis outstream renderer video event'); + document.querySelector(`#${adUnitCode}`).style.display = 'none'; + } + }); + return renderer; +} + +/** + * This function hides google div container for outstream bids to remove unwanted space on page. Appnexus renderer creates a new iframe outside of google iframe to render the outstream creative. + * @param {string} elementId element id + */ +function hidedfpContainer(elementId) { + var el = document.getElementById(elementId).querySelectorAll("div[id^='google_ads']"); + if (el[0]) { + el[0].style.setProperty('display', 'none'); + } +} + +function outstreamRender(bid) { + // push to render queue because ANOutstreamVideo may not be loaded yet + hidedfpContainer(bid.adUnitCode); + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + tagId: bid.adResponse.tag_id, + sizes: [bid.getSize().split('x')], + targetId: bid.adUnitCode, // target div id to render video + uuid: bid.adResponse.uuid, + adResponse: bid.adResponse, + rendererOptions: bid.renderer.getConfig() + }, handleOutstreamRendererEvents.bind(null, bid)); + }); +} + +function handleOutstreamRendererEvents(bid, id, eventName) { + bid.renderer.handleVideoEvent({ id, eventName }); +} + +/** + * Unpack the Server's Bid into a Prebid-compatible one. + * @param serverBid + * @param rtbBid + * @param bidderRequest + * @return Bid + */ +function newBid(serverBid, rtbBid, bidderRequest) { + const bidRequest = utils.getBidRequest(serverBid.uuid, [bidderRequest]); + const bid = { + requestId: serverBid.uuid, + cpm: rtbBid.cpm, + creativeId: rtbBid.creative_id, + dealId: rtbBid.deal_id, + currency: 'USD', + netRevenue: true, + ttl: 300, + adUnitCode: bidRequest.adUnitCode, + adrelevantis: { + buyerMemberId: rtbBid.buyer_member_id, + dealPriority: rtbBid.deal_priority, + dealCode: rtbBid.deal_code + } + }; + + if (rtbBid.advertiser_id) { + bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); + } + + if (rtbBid.rtb.video) { + Object.assign(bid, { + width: rtbBid.rtb.video.player_width, + height: rtbBid.rtb.video.player_height, + vastImpUrl: rtbBid.notify_url, + ttl: 3600 + }); + + const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); + switch (videoContext) { + case OUTSTREAM: + bid.adResponse = serverBid; + bid.adResponse.ad = bid.adResponse.ads[0]; + bid.adResponse.ad.video = bid.adResponse.ad.rtb.video; + bid.vastXml = rtbBid.rtb.video.content; + + if (rtbBid.renderer_url) { + const videoBid = find(bidderRequest.bids, bid => bid.bidId === serverBid.uuid); + const rendererOptions = utils.deepAccess(videoBid, 'renderer.options'); + bid.renderer = newRenderer(bid.adUnitCode, rtbBid, rendererOptions); + } + break; + case INSTREAM: + bid.vastUrl = rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url); + break; + } + } else if (rtbBid.rtb[NATIVE]) { + const nativeAd = rtbBid.rtb[NATIVE]; + + // setting up the jsTracker: + // we put it as a data-src attribute so that the tracker isn't called + // until we have the adId (see onBidWon) + let jsTrackerDisarmed = rtbBid.viewability.config.replace('src=', 'data-src='); + + let jsTrackers = nativeAd.javascript_trackers; + + if (jsTrackers == undefined) { + jsTrackers = jsTrackerDisarmed; + } else if (utils.isStr(jsTrackers)) { + jsTrackers = [jsTrackers, jsTrackerDisarmed]; + } else { + jsTrackers.push(jsTrackerDisarmed); + } + + bid[NATIVE] = { + title: nativeAd.title, + body: nativeAd.desc, + body2: nativeAd.desc2, + cta: nativeAd.ctatext, + rating: nativeAd.rating, + sponsoredBy: nativeAd.sponsored, + privacyLink: nativeAd.privacy_link, + address: nativeAd.address, + downloads: nativeAd.downloads, + likes: nativeAd.likes, + phone: nativeAd.phone, + price: nativeAd.price, + salePrice: nativeAd.saleprice, + clickUrl: nativeAd.link.url, + displayUrl: nativeAd.displayurl, + clickTrackers: nativeAd.link.click_trackers, + impressionTrackers: nativeAd.impression_trackers, + javascriptTrackers: jsTrackers + }; + if (nativeAd.main_img) { + bid['native'].image = { + url: nativeAd.main_img.url, + height: nativeAd.main_img.height, + width: nativeAd.main_img.width, + }; + } + if (nativeAd.icon) { + bid['native'].icon = { + url: nativeAd.icon.url, + height: nativeAd.icon.height, + width: nativeAd.icon.width, + }; + } + } else { + Object.assign(bid, { + width: rtbBid.rtb.banner.width, + height: rtbBid.rtb.banner.height, + ad: rtbBid.rtb.banner.content + }); + try { + const url = rtbBid.rtb.trackers[0].impression_urls[0]; + const tracker = utils.createTrackPixelHtml(url); + bid.ad += tracker; + } catch (error) { + utils.logError('Error appending tracking pixel', error); + } + } + + return bid; +} + +function bidToTag(bid) { + const tag = {}; + tag.sizes = transformSizes(bid.sizes); + tag.primary_size = tag.sizes[0]; + tag.ad_types = []; + tag.uuid = bid.bidId; + if (bid.params.placementId) { + tag.id = parseInt(bid.params.placementId, 10); + } + if (bid.params.cpm) { + tag.cpm = bid.params.cpm; + } + tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; + tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.prebid = true; + tag.disable_psa = true; + if (bid.params.reserve) { + tag.reserve = bid.params.reserve; + } + if (bid.params.position) { + tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; + } + if (bid.params.trafficSourceCode) { + tag.traffic_source_code = bid.params.trafficSourceCode; + } + if (bid.params.privateSizes) { + tag.private_sizes = transformSizes(bid.params.privateSizes); + } + if (bid.params.supplyType) { + tag.supply_type = bid.params.supplyType; + } + if (bid.params.pubClick) { + tag.pubclick = bid.params.pubClick; + } + if (bid.params.extInvCode) { + tag.ext_inv_code = bid.params.extInvCode; + } + if (bid.params.externalImpId) { + tag.external_imp_id = bid.params.externalImpId; + } + if (!utils.isEmpty(bid.params.keywords)) { + let keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + tag.keywords = keywords; + } + if (bid.params.category) { + tag.category = bid.params.category; + } + + if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) { + tag.ad_types.push(NATIVE); + if (tag.sizes.length === 0) { + tag.sizes = transformSizes([1, 1]); + } + + if (bid.nativeParams) { + const nativeRequest = buildNativeRequest(bid.nativeParams); + tag[NATIVE] = {layouts: [nativeRequest]}; + } + } + + const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`); + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + + tag.hb_source = 1; + if (bid.mediaType === VIDEO || videoMediaType) { + tag.ad_types.push(VIDEO); + } + + // instream gets vastUrl, outstream gets vastXml + if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { + tag.require_asset_url = true; + } + + if (bid.params.video) { + tag.video = {}; + // place any valid video params on the tag + Object.keys(bid.params.video) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => tag.video[param] = bid.params.video[param]); + } + + if (bid.renderer) { + tag.video = Object.assign({}, tag.video, {custom_renderer_present: true}); + } + + if ( + (utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) || + (bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER])) + ) { + tag.ad_types.push(BANNER); + } + + return tag; +} + +/* Turn bid request sizes into ut-compatible format */ +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; +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +function hasAppDeviceInfo(bid) { + if (bid.params) { + return !!bid.params.app + } +} + +function hasAppId(bid) { + if (bid.params && bid.params.app) { + return !!bid.params.app.id + } + return !!bid.params.app +} + +function getRtbBid(tag) { + return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb); +} + +function buildNativeRequest(params) { + const request = {}; + + // map standard prebid native asset identifier to /ut parameters + // e.g., tag specifies `body` but /ut only knows `description`. + // mapping may be in form {tag: ''} or + // {tag: {serverName: '', requiredParams: {...}}} + Object.keys(params).forEach(key => { + // check if one of the forms is used, otherwise + // a mapping wasn't specified so pass the key straight through + const requestKey = + (NATIVE_MAPPING[key] && NATIVE_MAPPING[key].serverName) || + NATIVE_MAPPING[key] || + key; + + // required params are always passed on request + const requiredParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].requiredParams; + request[requestKey] = Object.assign({}, requiredParams, params[key]); + + // convert the sizes of image/icon assets to proper format (if needed) + const isImageAsset = !!(requestKey === NATIVE_MAPPING.image.serverName || requestKey === NATIVE_MAPPING.icon.serverName); + if (isImageAsset && request[requestKey].sizes) { + let sizes = request[requestKey].sizes; + if (utils.isArrayOfNums(sizes) || (utils.isArray(sizes) && sizes.length > 0 && sizes.every(sz => utils.isArrayOfNums(sz)))) { + request[requestKey].sizes = transformSizes(request[requestKey].sizes); + } + } + + if (requestKey === NATIVE_MAPPING.privacyLink) { + request.privacy_supported = true; + } + }); + + return request; +} + +function parseMediaType(rtbBid) { + const adType = rtbBid.ad_type; + if (adType === VIDEO) { + return VIDEO; + } else { + return BANNER; + } +} + +registerBidder(spec); diff --git a/modules/adrelevantisBidAdapter.md b/modules/adrelevantisBidAdapter.md new file mode 100644 index 00000000000..a60a47508ff --- /dev/null +++ b/modules/adrelevantisBidAdapter.md @@ -0,0 +1,120 @@ +# Overview + +``` +Module Name: Adrelevantis Bid Adapter +Module Type: Bidder Adapter +Maintainer: info@adrelevantis.com +``` + +# Description + +Connects to Adrelevantis exchange for bids. + +Adrelevantis bid adapter supports Banner, Video (outstream) and Native. + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'adrelevantis', + params: { + placementId: 13144370, + cpm: 0.50 + } + }] + }, + // Native adUnit + { + code: 'native-div', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + }, + icon: { + required: false + } + } + }, + bids: [{ + bidder: 'adrelevantis', + params: { + placementId: 13232354, + allowSmallerSizes: true + } + }] + }, + // Video outstream adUnit + { + code: 'video-outstream', + mediaTypes: { + video: { + playerSize: [[640, 360]], + context: 'outstream' + } + }, + bids: [ + { + bidder: 'adrelevantis', + params: { + placementId: 13232385, + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + } + ] + }, + + // Banner adUnit in a App Webview + // Only use this for situations where prebid.js is in a webview of an App + // See Prebid Mobile for displaying ads via an SDK + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + } + bids: [{ + bidder: 'adrelevantis', + params: { + placementId: 13144370, + app: { + id: "B1O2W3M4AN.com.prebid.webview", + geo: { + lat: 40.0964439, + lng: -75.3009142 + }, + device_id: { + idfa: "4D12078D-3246-4DA4-AD5E-7610481E7AE", // Apple advertising identifier + aaid: "38400000-8cf0-11bd-b23e-10b96e40000d", // Android advertising identifier + md5udid: "5756ae9022b2ea1e47d84fead75220c8", // MD5 hash of the ANDROID_ID + sha1udid: "4DFAA92388699AC6539885AEF1719293879985BF", // SHA1 hash of the ANDROID_ID + windowsadid: "750c6be243f1c4b5c9912b95a5742fc5" // Windows advertising identifier + } + } + } + }] + } +]; +``` diff --git a/test/spec/modules/adrelevantisBidAdapter_spec.js b/test/spec/modules/adrelevantisBidAdapter_spec.js new file mode 100644 index 00000000000..11a6a14a353 --- /dev/null +++ b/test/spec/modules/adrelevantisBidAdapter_spec.js @@ -0,0 +1,769 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adrelevantisBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; + +const ENDPOINT = 'https://ssp.adrelevantis.com/prebid'; + +describe('AdrelevantisAdapter', 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': 'adrelevantis', + 'params': { + 'placementId': '10433394' + }, + '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 required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'adrelevantis', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should parse out private sizes', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + privateSizes: [300, 250] + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].private_sizes).to.exist; + expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); + }); + + it('should add source and verison to the tag', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.sdk).to.exist; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + }); + + it('should populate the ad_types array on all requests', function () { + ['banner', 'video', 'native'].forEach(type => { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes[type] = {}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal([type]); + }); + }); + + it('should populate the ad_types array on outstream requests', function () { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes.video = {context: 'outstream'}; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.deep.equal(['video']); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should attach valid video params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + video: { + id: 123, + minduration: 100, + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + id: 123, + minduration: 100 + }); + }); + + it('should add video property when adUnit includes a renderer', function () { + const videoData = { + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + }, + params: { + placementId: '10433394', + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + }; + + let bidRequest1 = deepClone(bidRequests[0]); + bidRequest1 = Object.assign({}, bidRequest1, videoData, { + renderer: { + url: 'http://test.renderer.url', + render: function () {} + } + }); + + let bidRequest2 = deepClone(bidRequests[0]); + bidRequest2.adUnitCode = 'adUnit_code_2'; + bidRequest2 = Object.assign({}, bidRequest2, videoData); + + const request = spec.buildRequests([bidRequest1, bidRequest2]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'], + custom_renderer_present: true + }); + expect(payload.tags[1].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'] + }); + }); + + it('should attach valid user params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + user: { + externalUid: '123', + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.user).to.exist; + expect(payload.user).to.deep.equal({ + externalUid: '123', + }); + }); + + it('should contain hb_source value for other media', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'banner', + params: { + sizes: [[300, 250], [300, 600]], + placementId: 10433394 + } + } + ); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].hb_source).to.deep.equal(1); + }); + + it('adds context data (category and keywords) to request when set', function() { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon + .stub(config, 'getConfig') + .withArgs('fpd') + .returns({ + context: { + keywords: 'US Open', + data: { + category: 'sports/tennis' + } + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.fpd.keywords).to.equal('US Open'); + expect(payload.fpd.category).to.equal('sports/tennis'); + + config.getConfig.restore(); + }); + + it('should attach native params to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + title: {required: true}, + body: {required: true}, + body2: {required: true}, + image: {required: true, sizes: [100, 100]}, + icon: {required: true}, + cta: {required: false}, + rating: {required: true}, + sponsoredBy: {required: true}, + privacyLink: {required: true}, + displayUrl: {required: true}, + address: {required: true}, + downloads: {required: true}, + likes: {required: true}, + phone: {required: true}, + price: {required: true}, + salePrice: {required: true} + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].native.layouts[0]).to.deep.equal({ + title: {required: true}, + description: {required: true}, + desc2: {required: true}, + main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, + icon: {required: true}, + ctatext: {required: false}, + rating: {required: true}, + sponsored_by: {required: true}, + privacy_link: {required: true}, + displayurl: {required: true}, + address: {required: true}, + downloads: {required: true}, + likes: {required: true}, + phone: {required: true}, + price: {required: true}, + saleprice: {required: true}, + privacy_supported: true + }); + expect(payload.tags[0].hb_source).to.equal(1); + }); + + it('should always populated tags[].sizes with 1,1 for native if otherwise not defined', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'native', + nativeParams: { + image: { required: true } + } + } + ); + bidRequest.sizes = [[150, 100], [300, 250]]; + + let request = spec.buildRequests([bidRequest]); + let payload = JSON.parse(request.data); + expect(payload.tags[0].sizes).to.deep.equal([{width: 150, height: 100}, {width: 300, height: 250}]); + + delete bidRequest.sizes; + + request = spec.buildRequests([bidRequest]); + payload = JSON.parse(request.data); + + expect(payload.tags[0].sizes).to.deep.equal([{width: 1, height: 1}]); + }); + + it('should convert keyword params to proper form and attaches to request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + emptyStr: '', + emptyArr: [''], + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + }]); + }); + + it('should add payment rules to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + usePaymentRule: true + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].use_pmt_rule).to.equal(true); + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'adrelevantis', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + 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; + }); + + it('supports sending hybrid mobile app parameters', function () { + let appRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + app: { + id: 'B1O2W3M4AN.com.prebid.webview', + geo: { + lat: 40.0964439, + lng: -75.3009142 + }, + device_id: { + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', // Apple advertising identifier + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', // Android advertising identifier + md5udid: '5756ae9022b2ea1e47d84fead75220c8', // MD5 hash of the ANDROID_ID + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', // SHA1 hash of the ANDROID_ID + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' // Windows advertising identifier + } + } + } + } + ); + const request = spec.buildRequests([appRequest]); + const payload = JSON.parse(request.data); + expect(payload.app).to.exist; + expect(payload.app).to.deep.equal({ + appid: 'B1O2W3M4AN.com.prebid.webview' + }); + expect(payload.device.device_id).to.exist; + expect(payload.device.device_id).to.deep.equal({ + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', + md5udid: '5756ae9022b2ea1e47d84fead75220c8', + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' + }); + expect(payload.device.geo).to.exist; + expect(payload.device.geo).to.deep.equal({ + lat: 40.0964439, + lng: -75.3009142 + }); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + 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 request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer_detection).to.exist; + expect(payload.referrer_detection).to.deep.equal({ + rd_ref: 'http%3A%2F%2Fexample.com%2Fpage.html', + rd_top: true, + rd_ifs: 2, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + }); + }); + + it('should populate coppa if set in config', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.user.coppa).to.equal(true); + + config.getConfig.restore(); + }); + }) + + describe('interpretResponse', function () { + let bfStub; + before(function() { + bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); + }); + + after(function() { + bfStub.restore(); + }); + + let response = { + 'version': '3.0.0', + 'tags': [ + { + 'uuid': '3db3773286ee59', + 'tag_id': 10433394, + 'auction_id': '4534722592064951574', + 'nobid': false, + 'no_ad_url': 'http://lax1-ib.adnxs.com/no-ad', + 'timeout_ms': 10000, + 'ad_profile_id': 27079, + 'ads': [ + { + 'content_source': 'rtb', + 'ad_type': 'banner', + 'buyer_member_id': 958, + 'creative_id': 29681110, + 'media_type_id': 1, + 'media_subtype_id': 1, + 'cpm': 0.5, + 'cpm_publisher_currency': 0.5, + 'publisher_currency_code': '$', + 'client_initiated_ad_counting': true, + 'viewability': { + 'config': '' + }, + 'rtb': { + 'banner': { + 'content': '', + 'width': 300, + 'height': 250 + }, + 'trackers': [ + { + 'impression_urls': [ + 'http://lax1-ib.adnxs.com/impression' + ], + 'video_events': {} + } + ] + } + } + ] + } + ] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '3db3773286ee59', + 'cpm': 0.5, + 'creativeId': 29681110, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true, + 'adUnitCode': 'code', + 'adrelevantis': { + 'buyerMemberId': 958 + } + } + ]; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let response = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + + it('handles outstream video responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'content': '' + } + }, + 'javascriptTrackers': '' + }] + }] + }; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'outstream' + } + } + }] + } + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles instream video responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'asset_url': 'https://sample.vastURL.com/here/vid' + } + }, + 'javascriptTrackers': '' + }] + }] + }; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'instream' + } + } + }] + } + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastUrl'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles native responses', function () { + let response1 = deepClone(response); + response1.tags[0].ads[0].ad_type = 'native'; + response1.tags[0].ads[0].rtb.native = { + 'title': 'Native Creative', + 'desc': 'Cool description great stuff', + 'desc2': 'Additional body text', + 'ctatext': 'Do it', + 'sponsored': 'AppNexus', + 'icon': { + 'width': 0, + 'height': 0, + 'url': 'https://cdn.adnxs.com/icon.png' + }, + 'main_img': { + 'width': 2352, + 'height': 1516, + 'url': 'https://cdn.adnxs.com/img.png' + }, + 'link': { + 'url': 'https://www.appnexus.com', + 'fallback_url': '', + 'click_trackers': ['https://nym1-ib.adnxs.com/click'] + }, + 'impression_trackers': ['https://example.com'], + 'rating': '5', + 'displayurl': 'https://AppNexus.com/?url=display_url', + 'likes': '38908320', + 'downloads': '874983', + 'price': '9.99', + 'saleprice': 'FREE', + 'phone': '1234567890', + 'address': '28 W 23rd St, New York, NY 10010', + 'privacy_link': 'https://appnexus.com/?url=privacy_url', + 'javascriptTrackers': '' + }; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + + let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + expect(result[0].native.title).to.equal('Native Creative'); + 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'); + }); + + it('supports configuring outstream renderers', function () { + const outstreamResponse = deepClone(response); + outstreamResponse.tags[0].ads[0].rtb.video = {}; + outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; + + const bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + renderer: { + options: { + adText: 'configured' + } + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }] + }; + + const result = spec.interpretResponse({ body: outstreamResponse }, {bidderRequest}); + expect(result[0].renderer.config).to.deep.equal( + bidderRequest.bids[0].renderer.options + ); + }); + + it('should add deal_priority and deal_code', function() { + let responseWithDeal = deepClone(response); + responseWithDeal.tags[0].ads[0].deal_priority = 'high'; + responseWithDeal.tags[0].ads[0].deal_code = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); + expect(Object.keys(result[0].adrelevantis)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); + }); + + it('should add advertiser id', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); + }) + }); +}); From ecd05a322eda3d9e1cfaba3a28feed834b28c71d Mon Sep 17 00:00:00 2001 From: vingood Date: Fri, 25 Sep 2020 21:55:58 +0300 Subject: [PATCH 0230/1476] Adnow bidder (#5738) * Add AdNow bid Adaptor * Fix problems by PR comments. * PR comments: - Use only secure endpoint. - Use adUnit mediaTypes instead of mediaType param in buildRequests. - Pass correct sizes to the endpoint for banner and native. - Fix adnowBidAdaper.md examples. - Fix and add new tests in adnowBidAdaper_spec.js * rename test * Restore package-lock.json from master * Fix sizes of bid response object for banners. * Fix adapters tests. --- modules/adnowBidAdapter.js | 178 ++++++++++++++++++++++ modules/adnowBidAdapter.md | 46 ++++++ test/spec/modules/adnowBidAdapter_spec.js | 163 ++++++++++++++++++++ 3 files changed, 387 insertions(+) create mode 100644 modules/adnowBidAdapter.js create mode 100644 modules/adnowBidAdapter.md create mode 100644 test/spec/modules/adnowBidAdapter_spec.js diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js new file mode 100644 index 00000000000..7412db8d7b6 --- /dev/null +++ b/modules/adnowBidAdapter.js @@ -0,0 +1,178 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { NATIVE, BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const BIDDER_CODE = 'adnow'; +const ENDPOINT = 'https://n.ads3-adnow.com/a'; + +/** + * @typedef {object} CommonBidData + * + * @property {string} requestId The specific BidRequest which this bid is aimed at. + * This should match the BidRequest.bidId which this Bid targets. + * @property {string} currency The currency code for the cpm value + * @property {number} cpm The bid price, in US cents per thousand impressions. + * @property {string} creativeId The id of ad content + * @property {number} ttl Time-to-live - how long (in seconds) Prebid can use this bid. + * @property {boolean} netRevenue Boolean defining whether the bid is Net or Gross. The default is true (Net). + * @property {object} [meta] Object for storing bid meta data + * @property {string} [meta.mediaType] banner or native + */ + +/** @type {BidderSpec} */ +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ NATIVE, BANNER ], + + /** + * @param {object} bid + * @return {boolean} + */ + isBidRequestValid(bid) { + if (!bid || !bid.params) return false; + + const codeId = parseInt(bid.params.codeId, 10); + if (!codeId) { + return false; + } + + const mediaType = bid.params.mediaType || NATIVE; + + return includes(this.supportedMediaTypes, mediaType); + }, + + /** + * @param {BidRequest[]} validBidRequests + * @param {*} bidderRequest + * @return {ServerRequest} + */ + buildRequests(validBidRequests, bidderRequest) { + return validBidRequests.map(req => { + const mediaType = this._isBannerRequest(req) ? BANNER : NATIVE; + const codeId = parseInt(req.params.codeId, 10); + + const data = { + Id: codeId, + mediaType: mediaType, + out: 'prebid', + d_user_agent: navigator.userAgent, + requestid: req.bidId + }; + + if (mediaType === BANNER) { + data.sizes = utils.parseSizesInput( + req.mediaTypes && req.mediaTypes.banner && req.mediaTypes.banner.sizes + ).join('|') + } else { + data.width = data.height = 200; + + let sizes = utils.deepAccess(req, 'mediaTypes.native.image.sizes', []); + + if (sizes.length > 0) { + const size = Array.isArray(sizes[0]) ? sizes[0] : sizes; + + data.width = size[0] || data.width; + data.height = size[1] || data.height; + } + } + + /** @type {ServerRequest} */ + return { + method: 'GET', + url: ENDPOINT, + data: utils.parseQueryStringParameters(data), + options: { + withCredentials: false, + crossOrigin: true + }, + bidRequest: req + }; + }); + }, + + /** + * @param {*} response + * @param {ServerRequest} request + * @return {Bid[]} + */ + interpretResponse(response, request) { + const bidObj = request.bidRequest; + let bid = response.body; + + if (!bid || !bid.currency || !bid.cpm) { + return []; + } + + const mediaType = bid.meta.mediaType || NATIVE; + if (!includes(this.supportedMediaTypes, mediaType)) { + return []; + } + + bid.requestId = bidObj.bidId; + + if (mediaType === BANNER) { + return [ this._getBannerBid(bid) ]; + } + + if (mediaType === NATIVE) { + return [ this._getNativeBid(bid) ]; + } + + return []; + }, + + /** + * @private + * @param {object} bid + * @return {CommonBidData} + */ + _commonBidData(bid) { + return { + requestId: bid.requestId, + currency: bid.currency || 'USD', + cpm: bid.cpm || 0.00, + creativeId: bid.creativeId || 'undefined-creative', + netRevenue: bid.netRevenue || true, + ttl: bid.ttl || 360, + meta: bid.meta || {} + }; + }, + + /** + * @param {BidRequest} req + * @return {boolean} + * @private + */ + _isBannerRequest(req) { + return !!(req.mediaTypes && req.mediaTypes.banner); + }, + + /** + * @private + * @param {object} bid + * @return {Bid} + */ + _getBannerBid(bid) { + return { + ...this._commonBidData(bid), + width: bid.width || 300, + height: bid.height || 250, + ad: bid.ad || '
Empty Ad
' + }; + }, + + /** + * @private + * @param {object} bid + * @return {Bid} + */ + _getNativeBid(bid) { + return { + ...this._commonBidData(bid), + native: bid.native || {} + }; + } +} + +registerBidder(spec); diff --git a/modules/adnowBidAdapter.md b/modules/adnowBidAdapter.md new file mode 100644 index 00000000000..9ad99a67fc5 --- /dev/null +++ b/modules/adnowBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +``` +Module Name: AdNow Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@adnow.com +``` + +# Description + +AdNow Bidder Adapter for Prebid.js. +Banner and Native format are supported. +Please use ```adnow``` as the bidder code. + +# Test Parameters +```javascript + const adUnits = [{ + code: 'test', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'adnow', + params: { + codeId: 794934 + } + }] + }, { + code: 'test', + mediaTypes: { + native: { + image: { + sizes: [200, 200] + } + } + }, + bids: [{ + bidder: 'adnow', + params: { + codeId: 794934 + } + }] + }]; +``` diff --git a/test/spec/modules/adnowBidAdapter_spec.js b/test/spec/modules/adnowBidAdapter_spec.js new file mode 100644 index 00000000000..a8013e3fa04 --- /dev/null +++ b/test/spec/modules/adnowBidAdapter_spec.js @@ -0,0 +1,163 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adnowBidAdapter.js'; + +describe('adnowBidAdapter', function () { + describe('isBidRequestValid', function () { + it('Should return true', function() { + expect(spec.isBidRequestValid({ + bidder: 'adnow', + params: { + codeId: 12345 + } + })).to.equal(true); + }); + + it('Should return false when required params is not passed', function() { + expect(spec.isBidRequestValid({ + bidder: 'adnow', + params: {} + })).to.equal(false); + }); + }); + + describe('buildRequests', function() { + it('Common settings', function() { + const bidRequestData = [{ + bidId: 'bid12345', + params: { + codeId: 12345 + } + }]; + + const req = spec.buildRequests(bidRequestData); + const reqData = req[0].data; + + expect(reqData) + .to.match(/Id=12345/) + .to.match(/mediaType=native/) + .to.match(/out=prebid/) + .to.match(/requestid=bid12345/) + .to.match(/d_user_agent=.+/); + }); + + it('Banner sizes', function () { + const bidRequestData = [{ + bidId: 'bid12345', + params: { + codeId: 12345 + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }]; + + const req = spec.buildRequests(bidRequestData); + const reqData = req[0].data; + + expect(reqData).to.match(/sizes=300x250/); + }); + + it('Native sizes', function () { + const bidRequestData = [{ + bidId: 'bid12345', + params: { + codeId: 12345 + }, + mediaTypes: { + native: { + image: { + sizes: [100, 100] + } + } + } + }]; + + const req = spec.buildRequests(bidRequestData); + const reqData = req[0].data; + + expect(reqData) + .to.match(/width=100/) + .to.match(/height=100/); + }); + }); + + describe('interpretResponse', function() { + const request = { + bidRequest: { + bidId: 'bid12345' + } + }; + + it('Response with native bid', function() { + const response = { + currency: 'USD', + cpm: 0.5, + native: { + title: 'Title', + body: 'Body', + sponsoredBy: 'AdNow', + clickUrl: '//click.url', + image: { + url: '//img.url', + height: 200, + width: 200 + } + }, + meta: { + mediaType: 'native' + } + }; + + const bids = spec.interpretResponse({ body: response }, request); + expect(bids).to.be.an('array').that.is.not.empty; + + const bid = bids[0]; + expect(bid).to.have.keys('requestId', 'cpm', 'currency', 'native', 'creativeId', 'netRevenue', 'meta', 'ttl'); + + const nativePart = bid.native; + + expect(nativePart.title).to.be.equal('Title'); + expect(nativePart.body).to.be.equal('Body'); + expect(nativePart.clickUrl).to.be.equal('//click.url'); + expect(nativePart.image.url).to.be.equal('//img.url'); + expect(nativePart.image.height).to.be.equal(200); + expect(nativePart.image.width).to.be.equal(200); + }); + + it('Response with banner bid', function() { + const response = { + currency: 'USD', + cpm: 0.5, + ad: '
Banner
', + meta: { + mediaType: 'banner' + } + }; + + const bids = spec.interpretResponse({ body: response }, request); + expect(bids).to.be.an('array').that.is.not.empty; + + const bid = bids[0]; + expect(bid).to.have.keys( + 'requestId', 'cpm', 'currency', 'ad', 'creativeId', 'netRevenue', 'meta', 'ttl', 'width', 'height' + ); + + expect(bid.ad).to.be.equal('
Banner
'); + }); + + it('Response with no bid should return an empty array', function() { + const noBidResponses = [ + false, + {}, + {body: false}, + {body: {}} + ]; + + noBidResponses.forEach(response => { + return expect(spec.interpretResponse(response, request)).to.be.an('array').that.is.empty; + }); + }); + }); +}); From b2f0c6d26417e74d16cd7785917bfbb31318485f Mon Sep 17 00:00:00 2001 From: jsut Date: Fri, 25 Sep 2020 15:39:08 -0400 Subject: [PATCH 0231/1476] Improve error and documentation for publisherId (#5788) - The error message you get if you use a publisherId that is a JS numeric instead of a JS string is not super helpful if you aren't familiar with JS internals. Update the warning message to give a suggestion on a solution, and update the markdown documentation to explictly state that the ID needs to be wrapped in quotes. --- modules/pubmaticBidAdapter.js | 2 +- modules/pubmaticBidAdapter.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 1651f1f5cc0..b5514ab0344 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -847,7 +847,7 @@ export const spec = { isBidRequestValid: bid => { if (bid && bid.params) { if (!utils.isStr(bid.params.publisherId)) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: publisherId is mandatory and cannot be numeric. Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); + utils.logWarn(LOG_WARN_PREFIX + 'Error: publisherId is mandatory and cannot be numeric (wrap it in quotes in your config). Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); return false; } // video ad validation diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index cd9398477f4..0ef89a22cbd 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -24,7 +24,7 @@ var adUnits = [ bids: [{ bidder: 'pubmatic', params: { - publisherId: '156209', // required + publisherId: '156209', // required, must be wrapped in quotes oustreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream'. This value can be get by BlueBillyWig Team. adSlot: 'pubmatic_test2', // optional pmzoneid: 'zone1, zone11', // optional From 280e9577f24c370979c2712c5657ae8fda145e04 Mon Sep 17 00:00:00 2001 From: Amanda Dillon <41923726+agdillon@users.noreply.github.com> Date: Mon, 28 Sep 2020 04:32:09 -0600 Subject: [PATCH 0232/1476] SpotX bid adapter: add page parameter (#5784) --- modules/spotxBidAdapter.js | 23 ++++++++--- test/spec/modules/spotxBidAdapter_spec.js | 47 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 7c4e2e3ef63..6104fce1d97 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -1,4 +1,5 @@ import * as utils 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'; @@ -19,7 +20,7 @@ export const spec = { * From Prebid.js: isBidRequestValid - Verify the the AdUnits.bids, respond with true (valid) or false (invalid). * * @param {object} bid The bid to validate. - * @return boolean True if this is a valid bid, and false otherwise. + * @return {boolean} True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { if (bid && typeof bid.params !== 'object') { @@ -64,14 +65,24 @@ export const spec = { * from Prebid.js: buildRequests - Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. * * @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. + * @param {object} bidderRequest - The master bidRequest object. + * @return {ServerRequest} Info describing the request to the server. */ buildRequests: function(bidRequests, bidderRequest) { - const page = bidderRequest.refererInfo.referer; - const isPageSecure = !!page.match(/^https:/) + const referer = bidderRequest.refererInfo.referer; + const isPageSecure = !!referer.match(/^https:/); const siteId = ''; const spotxRequests = bidRequests.map(function(bid) { + let page; + if (utils.getBidIdParameter('page', bid.params)) { + page = utils.getBidIdParameter('page', bid.params); + } else if (config.getConfig('pageUrl')) { + page = config.getConfig('pageUrl'); + } else { + page = referer; + } + const channelId = utils.getBidIdParameter('channel_id', bid.params); let pubcid = null; @@ -436,11 +447,11 @@ function createOutstreamScript(bid) { const customOverride = utils.getBidIdParameter('custom_override', bid.renderer.config.outstream_options); if (customOverride && utils.isPlainObject(customOverride)) { - utils.logMessage('[SPOTX][renderer] Custom beahavior.'); + utils.logMessage('[SPOTX][renderer] Custom behavior.'); for (let name in customOverride) { if (customOverride.hasOwnProperty(name)) { if (name === 'channel_id' || name === 'vast_url' || name === 'content_page_url' || name === 'ad_unit') { - utils.logWarn('[SPOTX][renderer] Custom beahavior: following option cannot be overrided: ' + name); + utils.logWarn('[SPOTX][renderer] Custom behavior: following option cannot be overridden: ' + name); } else { dataSpotXParams['data-spotx_' + name] = customOverride[name]; } diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index cf651670518..798fb3eec10 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -1,4 +1,5 @@ import {expect} from 'chai'; +import {config} from 'src/config.js'; import {spec, GOOGLE_CONSENT} from 'modules/spotxBidAdapter.js'; describe('the spotx adapter', function () { @@ -89,6 +90,7 @@ describe('the spotx adapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); + describe('buildRequests', function() { var bid, bidRequestObj; @@ -125,6 +127,7 @@ describe('the spotx adapter', function () { page: 'prebid.js' }); }); + it('should change request parameters based on options sent', function() { var request = spec.buildRequests([bid], bidRequestObj)[0]; expect(request.data.imp.video.ext).to.deep.equal({ @@ -332,6 +335,50 @@ describe('the spotx adapter', function () { expect(request.data.imp.video.ext.placement).to.equal(2); expect(request.data.imp.video.ext.pos).to.equal(5); }); + + it('should pass page param and override refererInfo.referer', function() { + var request; + + bid.params.page = 'https://example.com'; + + var origGetConfig = config.getConfig; + sinon.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'pageUrl') { + return 'https://www.spotx.tv'; + } + return origGetConfig.apply(config, arguments); + }); + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.site.page).to.equal('https://example.com'); + config.getConfig.restore(); + }); + + it('should use pageUrl from config if page param is not passed', function() { + var request; + + var origGetConfig = config.getConfig; + sinon.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'pageUrl') { + return 'https://www.spotx.tv'; + } + return origGetConfig.apply(config, arguments); + }); + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.site.page).to.equal('https://www.spotx.tv'); + config.getConfig.restore(); + }); + + it('should use refererInfo.referer if no page or pageUrl are passed', function() { + var request; + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.site.page).to.equal('prebid.js'); + }); }); describe('interpretResponse', function() { From ebb51fbdca68643e7f966e99c4bb8e9474b397b2 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Mon, 28 Sep 2020 20:04:25 +0530 Subject: [PATCH 0233/1476] Media.net Analytics improvements (#5755) * medianetAnalyticsAdapter improvements * medianetAnalyticsAdapter improvements * review changes * fixed eslint Co-authored-by: monis.q --- modules/medianetAnalyticsAdapter.js | 281 +++++++++++++----- .../modules/medianetAnalyticsAdapter_spec.js | 39 +-- 2 files changed, 236 insertions(+), 84 deletions(-) diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index c76f695cc2b..35c9273f951 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -11,6 +11,7 @@ const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics const CONFIG_URL = 'https://prebid.media.net/rtb/prebid/analytics/config'; const EVENT_PIXEL_URL = 'https://qsearch-a.akamaihd.net/log'; const DEFAULT_LOGGING_PERCENT = 50; +const ANALYTICS_VERSION = '1.0.0'; const PRICE_GRANULARITY = { 'auto': 'pbAg', @@ -39,6 +40,18 @@ const CONFIG_ERROR = 3; const VALID_URL_KEY = ['canonical_url', 'og_url', 'twitter_url']; const DEFAULT_URL_KEY = 'page'; +const LOG_TYPE = { + AP: 'AP', + PR: 'PR', + APPR: 'APPR', + RA: 'RA' +}; + +const BATCHING = { + SINGLE: 'SINGLE', + MULTI: 'MULTI' +} + let auctions = {}; let config; let pageDetails; @@ -73,7 +86,14 @@ class Configure { this.urlToConsume = DEFAULT_URL_KEY; this.debug = false; this.gdprConsent = undefined; + this.gdprApplies = undefined; this.uspConsent = undefined; + this.pixelWaitTime = 0; + this.apLoggingPct = 0; + this.prLoggingPct = 0; + this.batching = BATCHING.SINGLE; + this.shouldBeLogged = {}; + this.mnetDebugConfig = ''; } set publisherLper(plper) { @@ -85,10 +105,12 @@ class Configure { cid: this.cid, lper: Math.round(100 / this.loggingPercent), plper: this.pubLper, - gdpr: this.gdprConsent, + gdpr: this.gdprApplies ? '1' : '0', + gdprConsent: this.gdprConsent, ccpa: this.uspConsent, ajx: this.ajaxState, pbv: PREBID_VERSION, + pbav: ANALYTICS_VERSION, flt: 1, } } @@ -100,10 +122,9 @@ class Configure { _parseResponse(response) { try { response = JSON.parse(response); - if (isNaN(response.percentage)) { - throw new Error('not a number'); - } - this.loggingPercent = response.percentage; + this.setDataFromResponse(response); + this.overrideDomainLevelData(response); + this.overrideToDebug(this.mnetDebugConfig); this.urlToConsume = VALID_URL_KEY.includes(response.urlKey) ? response.urlKey : this.urlToConsume; this.ajaxState = CONFIG_PASS; } catch (e) { @@ -113,6 +134,41 @@ class Configure { } } + setDataFromResponse(response) { + if (!isNaN(parseInt(response.percentage, 10))) { + this.loggingPercent = response.percentage; + } + + if (!isNaN(parseInt(response.pixelwaittime, 10))) { + this.pixelWaitTime = response.pixelwaittime; + } + + if (!isNaN(parseInt(response.aplper, 10))) { + this.apLoggingPct = response.aplper; + this.batching = BATCHING.MULTI; + } + + if (!isNaN(parseInt(response.prlper, 10))) { + this.prLoggingPct = response.prlper; + this.batching = BATCHING.MULTI; + } + } + + overrideDomainLevelData(response) { + const domain = utils.deepAccess(response, 'domain.' + pageDetails.domain); + if (domain) { + this.setDataFromResponse(domain); + } + } + + overrideToDebug(response) { + if (response === '') return; + try { + this.setDataFromResponse(JSON.parse(decodeURIComponent(response))); + } catch (e) { + } + } + _errorFetch() { this.ajaxState = CONFIG_ERROR; /* eslint no-new: "error" */ @@ -128,6 +184,9 @@ class Configure { this.debug = true; return; } + if (utils.deepAccess(urlObj, 'search.mnet_setconfig')) { + this.mnetDebugConfig = utils.deepAccess(urlObj, 'search.mnet_setconfig'); + } ajax( this._configURL(), { @@ -145,7 +204,7 @@ class PageDetail { const twitterUrl = this._getUrlFromSelector('meta[name="twitter:url"]', 'content'); const refererInfo = getRefererInfo(); - this.domain = URL.parseUrl(refererInfo.referer).host; + this.domain = URL.parseUrl(refererInfo.referer).hostname; this.page = refererInfo.referer; this.is_top = refererInfo.reachedTop; this.referrer = this._getTopWindowReferrer(); @@ -202,39 +261,34 @@ class PageDetail { } class AdSlot { - constructor(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize) { - this.mediaTypes = mediaTypes; - this.allMediaTypeSizes = allMediaTypeSizes; + constructor(tmax, supplyAdCode, context, adext) { this.tmax = tmax; this.supplyAdCode = supplyAdCode; + this.context = context; this.adext = adext; - this.logged = false; + this.logged = {}; + this.logged[LOG_TYPE.PR] = false; + this.logged[LOG_TYPE.AP] = false; + this.logged[LOG_TYPE.APPR] = false; this.targeting = undefined; this.medianetPresent = 0; - // shouldBeLogged is assigned when requested, - // since we are waiting for logging percent response - this.shouldBeLogged = undefined; - this.context = context; - this.adSize = adSize; // old ad unit sizes } - getShouldBeLogged() { - if (this.shouldBeLogged === undefined) { - this.shouldBeLogged = isSampled(); + getShouldBeLogged(logType) { + if (!config.shouldBeLogged.hasOwnProperty(logType)) { + config.shouldBeLogged[logType] = isSampled(logType); } - return this.shouldBeLogged; + config.shouldBeLogged[logType] = isSampled(logType); + return config.shouldBeLogged[logType]; } getLoggingData() { return Object.assign({ supcrid: this.supplyAdCode, - mediaTypes: this.mediaTypes && this.mediaTypes.join('|'), - szs: this.allMediaTypeSizes.map(sz => sz.join('x')).join('|'), tmax: this.tmax, targ: JSON.stringify(this.targeting), ismn: this.medianetPresent, vplcmtt: this.context, - sz2: this.adSize.map(sz => sz.join('x')).join('|'), }, this.adext && {'adext': JSON.stringify(this.adext)}, ); @@ -242,12 +296,13 @@ class AdSlot { } class Bid { - constructor(bidId, bidder, src, start, supplyAdCode) { + constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes) { this.bidId = bidId; this.bidder = bidder; this.src = src; this.start = start; - this.supplyAdCode = supplyAdCode; + this.adUnitCode = adUnitCode; + this.allMediaTypeSizes = allMediaTypeSizes; this.iwb = 0; this.winner = 0; this.status = bidder === DUMMY_BIDDER ? BID_SUCCESS : BID_TIMEOUT; @@ -257,7 +312,7 @@ class Bid { this.dfpbd = undefined; this.width = undefined; this.height = undefined; - this.mediaType = undefined; + this.mediaType = mediaType; this.timeToRespond = undefined; this.dealId = undefined; this.creativeId = undefined; @@ -268,6 +323,7 @@ class Bid { this.mpvid = undefined; this.floorPrice = undefined; this.floorRule = undefined; + this.serverLatencyMillis = undefined; } get size() { @@ -286,6 +342,7 @@ class Bid { bdp: this.cpm, cbdp: this.dfpbd, dfpbd: this.dfpbd, + szs: this.allMediaTypeSizes.map(sz => sz.join('x')).join('|'), size: this.size, mtype: this.mediaType, dId: this.dealId, @@ -299,7 +356,8 @@ class Bid { mpvid: this.mpvid, bidflr: this.floorPrice, flrrule: this.floorRule, - ext: JSON.stringify(this.ext) + ext: JSON.stringify(this.ext), + rtime: this.serverLatencyMillis, } } } @@ -342,12 +400,10 @@ class Auction { } } - addSlot(supplyAdCode, { mediaTypes, allMediaTypeSizes, tmax, adext, context, adSize }) { - if (supplyAdCode && this.adSlots[supplyAdCode] === undefined) { - this.adSlots[supplyAdCode] = new AdSlot(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize); - this.addBid( - new Bid('-1', DUMMY_BIDDER, 'client', '-1', supplyAdCode) - ); + addSlot({ adUnitCode, supplyAdCode, mediaTypes, allMediaTypeSizes, tmax, adext, context }) { + if (adUnitCode && this.adSlots[adUnitCode] === undefined) { + this.adSlots[adUnitCode] = new AdSlot(tmax, supplyAdCode, context, adext); + this.addBid(new Bid('-1', DUMMY_BIDDER, 'client', '-1', adUnitCode, mediaTypes, allMediaTypeSizes)); } } @@ -363,7 +419,7 @@ class Auction { getAdslotBids(adslot) { return this.bids - .filter((bid) => bid.supplyAdCode === adslot) + .filter((bid) => bid.adUnitCode === adslot) .map((bid) => bid.getLoggingData()); } @@ -382,46 +438,68 @@ class Auction { } } -function auctionInitHandler({auctionId, timestamp, bidderRequests}) { +function auctionInitHandler({auctionId, adUnits, timeout, timestamp, bidderRequests}) { if (auctionId && auctions[auctionId] === undefined) { auctions[auctionId] = new Auction(auctionId); auctions[auctionId].auctionInitTime = timestamp; } + addAddSlots(auctionId, adUnits, timeout); const floorData = utils.deepAccess(bidderRequests, '0.bids.0.floorData'); if (floorData) { auctions[auctionId].floorData = {...floorData}; } } -function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, uspConsent, gdpr }) { +function addAddSlots(auctionId, adUnits, tmax) { + adUnits = adUnits || []; + const groupedAdUnits = utils.groupBy(adUnits, 'code'); + Object.keys(groupedAdUnits).forEach((adUnitCode) => { + const adUnits = groupedAdUnits[adUnitCode]; + const supplyAdCode = utils.deepAccess(adUnits, '0.adUnitCode') || adUnitCode; + let context = ''; + let adext = {}; + + const mediaTypeMap = {}; + const oSizes = {banner: [], video: []}; + adUnits.forEach(({mediaTypes, sizes, ext}) => { + mediaTypes = mediaTypes || {}; + adext = Object.assign(adext, ext || utils.deepAccess(mediaTypes, 'banner.ext')); + context = utils.deepAccess(mediaTypes, 'video.context') || context; + Object.keys(mediaTypes).forEach((mediaType) => mediaTypeMap[mediaType] = 1); + const sizeObject = _getSizes(mediaTypes, sizes); + sizeObject.banner.forEach(size => oSizes.banner.push(size)); + sizeObject.video.forEach(size => oSizes.video.push(size)); + }); + + adext = utils.isEmpty(adext) ? undefined : adext; + oSizes.banner = oSizes.banner.filter(utils.uniques); + oSizes.video = oSizes.video.filter(utils.uniques); + oSizes.native = mediaTypeMap.native === 1 ? [[1, 1]] : []; + const allMediaTypeSizes = [].concat(oSizes.banner, oSizes.native, oSizes.video); + const mediaTypes = Object.keys(mediaTypeMap).join('|'); + + auctions[auctionId].addSlot({adUnitCode, supplyAdCode, mediaTypes, allMediaTypeSizes, context, tmax, adext}); + }); +} + +function bidRequestedHandler({ auctionId, auctionStart, bids, start, uspConsent, gdpr }) { if (!(auctions[auctionId] instanceof Auction)) { return; } - if (gdpr && gdpr.gdprApplies) { + config.gdprApplies = !!(gdpr && gdpr.gdprApplies); + if (config.gdprApplies) { config.gdprConsent = gdpr.consentString || ''; } config.uspConsent = config.uspConsent || uspConsent; + auctions[auctionId].auctionStartTime = auctionStart; bids.forEach(bid => { - const { adUnitCode, bidder, mediaTypes, sizes, bidId, src } = bid; - if (!auctions[auctionId].adSlots[adUnitCode]) { - auctions[auctionId].auctionStartTime = auctionStart; - const sizeObject = _getSizes(mediaTypes, sizes); - auctions[auctionId].addSlot( - adUnitCode, - Object.assign({}, - (mediaTypes instanceof Object) && { mediaTypes: Object.keys(mediaTypes) }, - { allMediaTypeSizes: [].concat(sizeObject.banner, sizeObject.native, sizeObject.video) }, - { adext: utils.deepAccess(mediaTypes, 'banner.ext') || '' }, - { tmax: timeout }, - { context: utils.deepAccess(mediaTypes, 'video.context') || '' }, - { adSize: sizeObject.banner } - ) - ); - } - let bidObj = new Bid(bidId, bidder, src, start, adUnitCode); + const { adUnitCode, bidder, bidId, src, mediaTypes, sizes } = bid; + const sizeObject = _getSizes(mediaTypes, sizes); + const requestSizes = [].concat(sizeObject.banner, sizeObject.native, sizeObject.video); + const bidObj = new Bid(bidId, bidder, src, start, adUnitCode, mediaTypes && Object.keys(mediaTypes).join('|'), requestSizes); auctions[auctionId].addBid(bidObj); if (bidder === MEDIANET_BIDDER_CODE) { bidObj.crid = utils.deepAccess(bid, 'params.crid'); @@ -482,6 +560,9 @@ function bidResponseHandler(bid) { bid.ext.crid && { 'crid': bid.ext.crid } ); } + if (typeof bid.serverResponseTimeMs !== 'undefined') { + bidObj.serverLatencyMillis = bid.serverResponseTimeMs; + } } function noBidResponseHandler({ auctionId, bidId }) { @@ -511,12 +592,18 @@ function bidTimeoutHandler(timedOutBids) { }) } -function auctionEndHandler({ auctionId, auctionEnd }) { +function auctionEndHandler({auctionId, auctionEnd, adUnitCodes}) { if (!(auctions[auctionId] instanceof Auction)) { return; } auctions[auctionId].status = AUCTION_COMPLETED; auctions[auctionId].auctionEndTime = auctionEnd; + + if (config.batching === BATCHING.MULTI) { + adUnitCodes.forEach(function (adUnitCode) { + sendEvent(auctionId, adUnitCode, LOG_TYPE.PR); + }); + } } function setTargetingHandler(params) { @@ -530,20 +617,51 @@ function setTargetingHandler(params) { adunitObj.targeting = params[adunit]; auctionObj.setTargetingTime = Date.now(); let targetingObj = Object.keys(params[adunit]).reduce((result, key) => { - if (key.indexOf('hb_adid') !== -1) { + if (key.indexOf(CONSTANTS.TARGETING_KEYS.AD_ID) !== -1) { result[key] = params[adunit][key] } return result; }, {}); + const winnerAdId = params[adunit][CONSTANTS.TARGETING_KEYS.AD_ID]; + let winningBid; let bidAdIds = Object.keys(targetingObj).map(k => targetingObj[k]); auctionObj.bids.filter((bid) => bidAdIds.indexOf(bid.adId) !== -1).map(function(bid) { bid.iwb = 1; + if (bid.adId === winnerAdId) { + winningBid = bid; + } + }); + auctionObj.bids.forEach(bid => { + if (bid.bidder === DUMMY_BIDDER && bid.adUnitCode === adunit) { + bid.iwb = bidAdIds.length === 0 ? 0 : 1; + bid.width = utils.deepAccess(winningBid, 'width'); + bid.height = utils.deepAccess(winningBid, 'height'); + } }); - sendEvent(auctionId, adunit, false); + sendEvent(auctionId, adunit, getLogType()); } } } +function getLogType() { + if (config.batching === BATCHING.SINGLE) { + return LOG_TYPE.APPR; + } + return LOG_TYPE.AP; +} + +function setBidderDone(params) { + if (config.pixelWaitTime != null && config.pixelWaitTime > 0) { + setTimeout(fireApAfterWait, config.pixelWaitTime, params) + } +} + +function fireApAfterWait(params) { + params.bids.forEach(function (adUnit) { + sendEvent(params.auctionId, adUnit.adUnitCode, LOG_TYPE.AP); + }); +} + function bidWonHandler(bid) { const { requestId, auctionId, adUnitCode } = bid; if (!(auctions[auctionId] instanceof Auction)) { @@ -555,26 +673,50 @@ function bidWonHandler(bid) { } auctions[auctionId].bidWonTime = Date.now(); bidObj.winner = 1; - sendEvent(auctionId, adUnitCode, true); + sendEvent(auctionId, adUnitCode, LOG_TYPE.RA); +} + +function isSampled(logType) { + return Math.random() * 100 < parseFloat(getLogPercentage(logType)); } -function isSampled() { - return Math.random() * 100 < parseFloat(config.loggingPercent); +function getLogPercentage(logType) { + let logPercentage = config.loggingPercent; + if (config.batching === BATCHING.MULTI) { + if (logType === LOG_TYPE.AP) { + logPercentage = config.apLoggingPct; + } else if (logType === LOG_TYPE.PR) { + logPercentage = config.prLoggingPct; + } + } + return logPercentage; } function isValidAuctionAdSlot(acid, adtag) { return (auctions[acid] instanceof Auction) && (auctions[acid].adSlots[adtag] instanceof AdSlot); } -function sendEvent(id, adunit, isBidWonEvent) { +function sendEvent(id, adunit, logType) { if (!isValidAuctionAdSlot(id, adunit)) { return; } - if (isBidWonEvent) { - fireAuctionLog(id, adunit, isBidWonEvent); - } else if (auctions[id].adSlots[adunit].getShouldBeLogged() && !auctions[id].adSlots[adunit].logged) { - auctions[id].adSlots[adunit].logged = true; - fireAuctionLog(id, adunit, isBidWonEvent); + if (logType === LOG_TYPE.RA) { + fireAuctionLog(id, adunit, logType); + } else { + fireApPrLog(id, adunit, logType) + } +} + +function fireApPrLog(auctionId, adUnitName, logType) { + const adSlot = auctions[auctionId].adSlots[adUnitName]; + if (adSlot.getShouldBeLogged(logType) && !adSlot.logged[logType]) { + if (config.batching === BATCHING.SINGLE) { + adSlot.logged[LOG_TYPE.AP] = true; + adSlot.logged[LOG_TYPE.PR] = true; + } else { + adSlot.logged[logType] = true; + } + fireAuctionLog(auctionId, adUnitName, logType); } } @@ -585,8 +727,9 @@ function getCommonLoggingData(acid, adtag) { return Object.assign(commonParams, adunitParams, auctionParams); } -function fireAuctionLog(acid, adtag, isBidWonEvent) { +function fireAuctionLog(acid, adtag, logType) { let commonParams = getCommonLoggingData(acid, adtag); + commonParams.lgtp = logType; let targeting = utils.deepAccess(commonParams, 'targ'); Object.keys(commonParams).forEach((key) => (commonParams[key] == null) && delete commonParams[key]); @@ -594,7 +737,7 @@ function fireAuctionLog(acid, adtag, isBidWonEvent) { let bidParams; - if (isBidWonEvent) { + if (logType === LOG_TYPE.RA) { bidParams = auctions[acid].getWinnerAdslotBid(adtag); commonParams.lper = 1; } else { @@ -700,6 +843,10 @@ let medianetAnalytics = Object.assign(adapter({URL, analyticsType}), { setTargetingHandler(args); break; } + case CONSTANTS.EVENTS.BIDDER_DONE : { + setBidderDone(args); + break; + } case CONSTANTS.EVENTS.BID_WON: { bidWonHandler(args); break; diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index 97b45cef00c..41a6338225e 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -9,20 +9,23 @@ const { } = CONSTANTS; const MOCK = { - AUCTION_INIT: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739}, - AUCTION_INIT_WITH_FLOOR: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739, 'bidderRequests': [{'bids': [{ 'floorData': {'enforcements': {'enforceJS': true}} }]}]}, + Ad_Units: [{'code': 'div-gpt-ad-1460505748561-0', 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'bids': [], 'ext': {'prop1': 'value1'}}], + MULTI_FORMAT_TWIN_AD_UNITS: [{'code': 'div-gpt-ad-1460505748561-0', 'mediaTypes': {'banner': {'sizes': [[300, 250]]}, 'native': {'image': {'required': true, 'sizes': [150, 50]}}}, 'bids': [], 'ext': {'prop1': 'value1'}}, {'code': 'div-gpt-ad-1460505748561-0', 'mediaTypes': {'video': {'playerSize': [640, 480], 'context': 'instream'}}, 'bids': [], 'ext': {'prop1': 'value1'}}], + AUCTION_INIT: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739, 'timeout': 6000}, + AUCTION_INIT_WITH_FLOOR: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739, 'timeout': 6000, 'bidderRequests': [{'bids': [{ 'floorData': {'enforcements': {'enforceJS': true}} }]}]}, BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, - MULTI_FORMAT_BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}, 'video': {'playerSize': [640, 480], 'context': 'instream'}, 'native': {'image': {'required': true, 'sizes': [150, 50]}, 'title': {'required': true, 'len': 80}}, 'ext': ['asdads']}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, + MULTI_FORMAT_BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}, 'video': {'playerSize': [640, 480], 'context': 'instream'}, 'native': {'image': {'required': true, 'sizes': [150, 50]}, 'title': {'required': true, 'len': 80}}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, BID_RESPONSE: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, AUCTION_END: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'auctionEnd': 1584563605739}, SET_TARGETING: {'div-gpt-ad-1460505748561-0': {'prebid_test': '1', 'hb_format': 'banner', 'hb_source': 'client', 'hb_size': '300x250', 'hb_pb': '2.00', 'hb_adid': '3e6e4bce5c8fb3', 'hb_bidder': 'medianet', 'hb_format_medianet': 'banner', 'hb_source_medianet': 'client', 'hb_size_medianet': '300x250', 'hb_pb_medianet': '2.00', 'hb_adid_medianet': '3e6e4bce5c8fb3', 'hb_bidder_medianet': 'medianet'}}, + NO_BID_SET_TARGETING: {'div-gpt-ad-1460505748561-0': {}}, BID_WON: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, NO_BID: {'bidder': 'medianet', 'params': {'cid': 'test123', 'crid': '451466393', 'site': {}}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '303fa0c6-682f-4aea-8e4a-dc68f0d5c7d5', 'sizes': [[300, 250], [300, 600]], 'bidId': '28248b0e6aece2', 'bidderRequestId': '13fccf3809fe43', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}, BID_TIMEOUT: [{'bidId': '28248b0e6aece2', 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'params': [{'cid': 'test123', 'crid': '451466393', 'site': {}}, {'cid': '8CUX0H51P', 'crid': '451466393', 'site': {}}], 'timeout': 6}] } function performAuctionWithFloorConfig() { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT_WITH_FLOOR); + events.emit(AUCTION_INIT, {...MOCK.AUCTION_INIT_WITH_FLOOR, adUnits: MOCK.Ad_Units}); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE); events.emit(AUCTION_END, MOCK.AUCTION_END); @@ -31,7 +34,7 @@ function performAuctionWithFloorConfig() { } function performStandardAuctionWithWinner() { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, {...MOCK.AUCTION_INIT, adUnits: MOCK.Ad_Units}); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.BID_RESPONSE); events.emit(AUCTION_END, MOCK.AUCTION_END); @@ -40,27 +43,27 @@ function performStandardAuctionWithWinner() { } function performMultiFormatAuctionWithNoBid() { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, {...MOCK.AUCTION_INIT, adUnits: MOCK.MULTI_FORMAT_TWIN_AD_UNITS}); events.emit(BID_REQUESTED, MOCK.MULTI_FORMAT_BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE); + events.emit(NO_BID, MOCK.NO_BID); events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(SET_TARGETING, MOCK.NO_BID_SET_TARGETING); } function performStandardAuctionWithNoBid() { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, {...MOCK.AUCTION_INIT, adUnits: MOCK.Ad_Units}); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(NO_BID, MOCK.NO_BID); events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(SET_TARGETING, MOCK.NO_BID_SET_TARGETING); } function performStandardAuctionWithTimeout() { - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, {...MOCK.AUCTION_INIT, adUnits: MOCK.Ad_Units}); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(SET_TARGETING, MOCK.NO_BID_SET_TARGETING); } function getQueryData(url) { @@ -123,8 +126,10 @@ describe('Media.net Analytics Adapter', function() { cid: 'test123' } }); + medianetAnalytics.clearlogsQueue(); }); afterEach(function () { + medianetAnalytics.clearlogsQueue(); medianetAnalytics.disableAnalytics(); }); @@ -139,10 +144,9 @@ describe('Media.net Analytics Adapter', function() { performMultiFormatAuctionWithNoBid(); const noBidLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log))[0]; medianetAnalytics.clearlogsQueue(); - - expect(noBidLog.szs).to.equal(encodeURIComponent('300x250|1x1|640x480')); + expect(noBidLog.mtype).to.have.ordered.members([encodeURIComponent('banner|native|video'), encodeURIComponent('banner|video|native')]); + expect(noBidLog.szs).to.have.ordered.members([encodeURIComponent('300x250|1x1|640x480'), encodeURIComponent('300x250|1x1|640x480')]); expect(noBidLog.vplcmtt).to.equal('instream'); - expect(noBidLog.sz2).to.equal(encodeURIComponent('300x250')); }); it('should have winner log in standard auction', function() { @@ -167,6 +171,7 @@ describe('Media.net Analytics Adapter', function() { src: 'client', size: '300x250', mtype: 'banner', + gdpr: '0', cid: 'test123', lper: '1', ogbdp: '1.1495', @@ -205,7 +210,7 @@ describe('Media.net Analytics Adapter', function() { expect(noBidLog.status).to.have.ordered.members(['1', '2']); expect(noBidLog.src).to.have.ordered.members(['client', 'client']); expect(noBidLog.curr).to.have.ordered.members(['', '']); - expect(noBidLog.mtype).to.have.ordered.members(['', '']); + expect(noBidLog.mtype).to.have.ordered.members(['banner', 'banner']); expect(noBidLog.ogbdp).to.have.ordered.members(['', '']); expect(noBidLog.mpvid).to.have.ordered.members(['', '']); expect(noBidLog.crid).to.have.ordered.members(['', '451466393']); @@ -223,7 +228,7 @@ describe('Media.net Analytics Adapter', function() { expect(timeoutLog.status).to.have.ordered.members(['1', '3']); expect(timeoutLog.src).to.have.ordered.members(['client', 'client']); expect(timeoutLog.curr).to.have.ordered.members(['', '']); - expect(timeoutLog.mtype).to.have.ordered.members(['', '']); + expect(timeoutLog.mtype).to.have.ordered.members(['banner', 'banner']); expect(timeoutLog.ogbdp).to.have.ordered.members(['', '']); expect(timeoutLog.mpvid).to.have.ordered.members(['', '']); expect(timeoutLog.crid).to.have.ordered.members(['', '451466393']); From c696e0019d3120e1c2b4d96ba16f263c3ee1c0f2 Mon Sep 17 00:00:00 2001 From: Olivier Date: Mon, 28 Sep 2020 17:45:57 +0200 Subject: [PATCH 0234/1476] adagio Bid Adapter: add support for CCPA, COPPA (#5749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clément besse --- modules/adagioBidAdapter.js | 21 ++++++- test/spec/modules/adagioBidAdapter_spec.js | 71 ++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index b4c2a6ac0d6..65a35284d1b 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1,5 +1,6 @@ import find from 'core-js-pure/features/array/find.js'; import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { loadExternalScript } from '../src/adloader.js' import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; @@ -9,7 +10,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.3.0'; +export const VERSION = '2.4.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; export const SUPPORTED_MEDIA_TYPES = ['banner']; @@ -555,6 +556,16 @@ function _getGdprConsent(bidderRequest) { return consent; } +function _getCoppa() { + return { + required: config.getConfig('coppa') === true ? 1 : 0 + }; +} + +function _getUspConsent(bidderRequest) { + return (utils.deepAccess(bidderRequest, 'uspConsent')) ? { uspConsent: bidderRequest.uspConsent } : false; +} + function _getSchain(bidRequest) { if (utils.deepAccess(bidRequest, 'schain')) { return bidRequest.schain; @@ -643,6 +654,8 @@ export const spec = { const site = internal.getSite(bidderRequest); const pageviewId = internal.getPageviewId(); const gdprConsent = _getGdprConsent(bidderRequest) || {}; + const uspConsent = _getUspConsent(bidderRequest) || {}; + const coppa = _getCoppa(); const schain = _getSchain(validBidRequests[0]); const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); @@ -672,7 +685,11 @@ export const spec = { site: site, pageviewId: pageviewId, adUnits: groupedAdUnits[organizationId], - gdpr: gdprConsent, + regs: { + gdpr: gdprConsent, + coppa: coppa, + ccpa: uspConsent + }, schain: schain, prebidVersion: '$prebid.version$', adapterVersion: VERSION, diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 52b99f274d5..a18cd797d68 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -3,6 +3,7 @@ import { expect } from 'chai'; import { _features, internal as adagio, adagioScriptFromLocalStorageCb, getAdagioScript, storage, spec, ENDPOINT, VERSION } from '../../../modules/adagioBidAdapter.js'; import { loadExternalScript } from '../../../src/adloader.js'; import * as utils from '../../../src/utils.js'; +import { config } from 'src/config.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -285,7 +286,7 @@ describe('Adagio bid adapter', () => { 'site', 'pageviewId', 'adUnits', - 'gdpr', + 'regs', 'schain', 'prebidVersion', 'adapterVersion', @@ -450,7 +451,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.gdpr).to.deep.equal(expected); + expect(requests[0].data.regs.gdpr).to.deep.equal(expected); }); it('send data.gdpr object to the server from TCF v.2 cmp', function() { @@ -466,7 +467,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.gdpr).to.deep.equal(expected); + expect(requests[0].data.regs.gdpr).to.deep.equal(expected); }); }); @@ -485,7 +486,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.gdpr).to.deep.equal(expected); + expect(requests[0].data.regs.gdpr).to.deep.equal(expected); }); it('send data.gdpr object to the server from TCF v.2 cmp', function() { @@ -501,7 +502,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.gdpr).to.deep.equal(expected); + expect(requests[0].data.regs.gdpr).to.deep.equal(expected); }); }); @@ -510,10 +511,68 @@ describe('Adagio bid adapter', () => { const bidderRequest = new BidderRequestBuilder().build(); const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.gdpr).to.be.empty; + expect(requests[0].data.regs.gdpr).to.be.empty; }); }); }); + + describe('with COPPA', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + const bidderRequest = new BidderRequestBuilder().build(); + + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.regs.coppa.required).to.equal(1); + + config.getConfig.restore(); + }); + }); + + describe('without COPPA', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + + it('should send the Coppa "required" flag set to "0" in the request', function () { + const bidderRequest = new BidderRequestBuilder().build(); + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.regs.coppa.required).to.equal(0); + }); + }); + + describe('with USPrivacy', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + + const consent = 'Y11N' + + it('should send the USPrivacy "ccpa.uspConsent" in the request', function () { + const bidderRequest = new BidderRequestBuilder({ + uspConsent: consent + }).build(); + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.regs.ccpa.uspConsent).to.equal(consent); + }); + }); + + describe('without USPrivacy', function() { + const bid01 = new BidRequestBuilder().withParams().build(); + + it('should have an empty "ccpa" field in the request', function () { + const bidderRequest = new BidderRequestBuilder().build(); + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.regs.ccpa).to.be.empty; + }); + }); }); describe('interpretResponse()', function() { From a054aa09b35cc1bd19ccdb2201d5e83d23848e10 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 28 Sep 2020 10:56:10 -0700 Subject: [PATCH 0235/1476] PubMatic analytics adapter: Not passing GDPR information (#5791) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * not passing GDPR data in analytics --- modules/pubmaticAnalyticsAdapter.js | 8 -------- test/spec/modules/pubmaticAnalyticsAdapter_spec.js | 10 ++++------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index d94c098db5d..04c6606a299 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -228,13 +228,6 @@ function executeBidsLoggerCall(e, highestCpmBids) { return 0; })(); - // GDPR support - if (auctionCache.gdprConsent) { - outputObj['cns'] = auctionCache.gdprConsent.consentString || ''; - outputObj['gdpr'] = auctionCache.gdprConsent.gdprApplies === true ? 1 : 0; - pixelURL += '&gdEn=1'; - } - outputObj.s = Object.keys(auctionCache.adUnitCodes).reduce(function(slotsArray, adUnitId) { let adUnit = auctionCache.adUnitCodes[adUnitId]; let slotObject = { @@ -307,7 +300,6 @@ function auctionInitHandler(args) { } function bidRequestedHandler(args) { - cache.auctions[args.auctionId].gdprConsent = args.gdprConsent || undefined; args.bids.forEach(function(bid) { if (!cache.auctions[args.auctionId].adUnitCodes.hasOwnProperty(bid.adUnitCode)) { cache.auctions[args.auctionId].adUnitCodes[bid.adUnitCode] = { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 537ba4483cf..d38037f40a1 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -316,7 +316,7 @@ describe('pubmatic analytics adapter', function () { clock.tick(2000 + 1000); expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1'); + expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); let data = getLoggerJsonFromRequest(request.requestBody); expect(data.pubid).to.equal('9999'); expect(data.pid).to.equal('1111'); @@ -326,8 +326,6 @@ describe('pubmatic analytics adapter', function () { expect(data.purl).to.equal('http://www.test.com/page.html'); expect(data.orig).to.equal('www.test.com'); expect(data.tst).to.equal(1519767016); - expect(data.cns).to.equal('here-goes-gdpr-consent-string'); - expect(data.gdpr).to.equal(1); expect(data.tgid).to.equal(15); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); @@ -423,7 +421,7 @@ describe('pubmatic analytics adapter', function () { clock.tick(2000 + 1000); expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1'); + expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); let data = getLoggerJsonFromRequest(request.requestBody); expect(data.pubid).to.equal('9999'); expect(data.pid).to.equal('1111'); @@ -490,7 +488,7 @@ describe('pubmatic analytics adapter', function () { clock.tick(2000 + 1000); expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1'); + expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); let data = getLoggerJsonFromRequest(request.requestBody); expect(data.pubid).to.equal('9999'); expect(data.pid).to.equal('1111'); @@ -672,7 +670,7 @@ describe('pubmatic analytics adapter', function () { clock.tick(2000 + 1000); expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1'); + 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].sz).to.deep.equal(['1000x300', '970x250', '728x90']); From 8168453bc4fd6433c0b21dae0edafa91685f7f70 Mon Sep 17 00:00:00 2001 From: susyt Date: Mon, 28 Sep 2020 21:07:29 -0700 Subject: [PATCH 0236/1476] GumGum: adds support for new field - iriscat (#5790) * adds support for zone and pubId params * adds support for iriscat field --- modules/gumgumBidAdapter.js | 4 ++++ test/spec/modules/gumgumBidAdapter_spec.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 9714a3eeeca..3206b7e1727 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -269,6 +269,10 @@ function buildRequests (validBidRequests, bidderRequest) { data.fp = bidFloor; } + if (params.iriscat && typeof params.iriscat === 'string') { + data.iriscat = params.iriscat; + } + if (params.zone) { data.t = params.zone; data.pi = 2; // inscreen diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index f24fdc87e45..701ce9a7e81 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -252,6 +252,25 @@ describe('gumgumAdapter', function () { expect(bidRequest.data).to.include.any.keys('t'); expect(bidRequest.data).to.include.any.keys('fp'); }); + it('should set iriscat parameter if iriscat param is found and is of type string', function () { + const iriscat = 'segment'; + const request = { ...bidRequests[0] }; + request.params = { ...request.params, iriscat }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.iriscat).to.equal(iriscat); + }); + it('should not send iriscat parameter if iriscat param is not found', function () { + const request = { ...bidRequests[0] }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.iriscat).to.be.undefined; + }); + it('should not send iriscat parameter if iriscat param is not of type string', function () { + const iriscat = 123; + const request = { ...bidRequests[0] }; + request.params = { ...request.params, iriscat }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.iriscat).to.be.undefined; + }); it('should send pubId if inScreenPubID param is specified', function () { const request = Object.assign({}, bidRequests[0]); delete request.params; From eb9cf3f3fbd39bf468a847c7fa547c0c1cc52e09 Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 29 Sep 2020 06:36:21 +0200 Subject: [PATCH 0237/1476] fix a few id5 docs (#5793) * update id5 eids value and add html storage example * html5, not html --- modules/userId/eids.md | 8 ++++++-- modules/userId/userId.md | 42 ++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 03aec46cf48..7dc149cd47a 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -1,7 +1,8 @@ ## Example of eids array generated by UserId Module. + ``` userIdAsEids = [ - { + { source: 'pubcid.org', uids: [{ id: 'some-random-id-value', @@ -25,6 +26,9 @@ userIdAsEids = [ uids: [{ id: 'some-random-id-value', atype: 1 + }, + ext: { + linkType: 2 }] }, @@ -91,7 +95,7 @@ userIdAsEids = [ uids: [{ id: 'some-random-id-value', atype: 1, - ext: { + ext: { third: 'some-random-id-value' } }] diff --git a/modules/userId/userId.md b/modules/userId/userId.md index a47ecd9f08c..a9ab6ccc483 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -26,7 +26,7 @@ pbjs.setConfig({ name: "id5Id", params: { partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: "some-pd-string" // See https://wiki.id5.io/display/PD/Prebid.js+UserId+Module for details + pd: "some-pd-string" // See https://wiki.id5.io/x/BIAZ for details }, storage: { type: "cookie", @@ -43,7 +43,7 @@ pbjs.setConfig({ }, { name: 'identityLink', params: { - pid: '999' // Set your real identityLink placement ID here + pid: '999' // Set your real identityLink placement ID here }, storage: { type: 'cookie', @@ -53,7 +53,7 @@ pbjs.setConfig({ }, { name: 'liveIntentId', params: { - publisherId: '7798696' // Set an identifier of a publisher know to your systems + publisherId: '7798696' // Set an identifier of a publisher know to your systems }, storage: { type: 'cookie', @@ -102,7 +102,7 @@ pbjs.setConfig({ }, { name: 'identityLink', params: { - pid: '999' // Set your real identityLink placement ID here + pid: '999' // Set your real identityLink placement ID here }, storage: { type: 'html5', @@ -110,25 +110,37 @@ pbjs.setConfig({ expires: 30 } }, { - name: 'liveIntentId', - params: { - publisherId: '7798696' // Set an identifier of a publisher know to your systems - }, - storage: { - type: 'html5', - name: '_li_pbid', - expires: 60 - } + name: 'liveIntentId', + params: { + publisherId: '7798696' // Set an identifier of a publisher know to your systems + }, + storage: { + type: 'html5', + name: '_li_pbid', + expires: 60 + } }, { - name: 'sharedId', + name: 'sharedId', params: { syncTime: 60 // in seconds, default is 24 hours }, storage: { - type: 'cookie', + type: 'html5', name: 'sharedid', expires: 28 } + }, { + name: 'id5Id', + params: { + partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id + pd: 'some-pd-string' // See https://wiki.id5.io/x/BIAZ for details + }, + storage: { + type: 'html5', + name: 'id5id.1st', + expires: 90, // Expiration of cookies in days + refreshInSeconds: 8*3600 // User Id cache lifetime in seconds, defaulting to 'expires' + }, }], syncDelay: 5000 } From 739bad87a5cd46786e57cd082d342e5330f4cf2b Mon Sep 17 00:00:00 2001 From: YerkovichM <48519843+YerkovichM@users.noreply.github.com> Date: Tue, 29 Sep 2020 18:26:41 +0300 Subject: [PATCH 0238/1476] New PubProvided Id UserId Submodule (#5767) * PubProvided Module * - * formatting * formatting * Added rubiconBidAdapter support Added unit tests * formatting * formatting * formatting * formatting * commit to rerun build * type changes * type changes * type changes * Revert "type changes" This reverts commit af408b0a * Revert "type changes" This reverts commit af408b0a * formatting * formatting * formatting * formatting * formatting * Revert "type changes" This reverts commit 114005a5 * formatting * formatting * formatting * formatting * commit to rerun build * commit to rerun build * commit to rerun build * rubiconBidAdapter changes * rubiconBidAdapter changes * rubiconBidAdapter changes * trigger build * fix * fix * fix * rebuild Co-authored-by: myerkovich --- integrationExamples/gpt/userId_example.html | 24 ++ modules/pubProvidedSystem.js | 53 +++ modules/rubiconBidAdapter.js | 11 + modules/userId/eids.js | 10 +- test/spec/modules/eids_spec.js | 36 +- test/spec/modules/rubiconBidAdapter_spec.js | 164 +++++--- test/spec/modules/userId_spec.js | 416 +++++++++++++------- 7 files changed, 513 insertions(+), 201 deletions(-) create mode 100644 modules/pubProvidedSystem.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 8e69bc6c6a7..dddc0915db2 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -134,6 +134,30 @@ // }, userSync: { userIds: [{ + name: "pubProvidedId", + params: { + eids: [{ + source: "domain.com", + uids:[{ + id: "value read from cookie or local storage", + atype: 1, + ext: { + stype: "ppuid" // allowable options are sha256email, DMP, ppuid for now + } + }] + },{ + source: "3rdpartyprovided.com", + uids:[{ + id: "value read from cookie or local storage", + atype: 3, + ext: { + stype: "sha256email" + } + }] + }], + eidsFunction: getHashedEmail // any user defined function that exists in the page + } + },{ name: "unifiedId", params: { partner: "prebid", diff --git a/modules/pubProvidedSystem.js b/modules/pubProvidedSystem.js new file mode 100644 index 00000000000..575633e622f --- /dev/null +++ b/modules/pubProvidedSystem.js @@ -0,0 +1,53 @@ +/** + * This module adds Publisher Provided ids support to the User ID module + * The {@link module:modules/userId} module is required. + * @module modules/pubProvidedSystem + * @requires module:modules/userId + */ + +import {submodule} from '../src/hook.js'; +import * as utils from '../src/utils.js'; + +const MODULE_NAME = 'pubProvidedId'; + +/** @type {Submodule} */ +export const pubProvidedIdSubmodule = { + + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * decode the stored id value for passing to bid request + * @function + * @param {string} value + * @returns {{pubProvidedId: array}} or undefined if value doesn't exists + */ + decode(value) { + const res = value ? {pubProvidedId: value} : undefined; + utils.logInfo('PubProvidedId: Decoded value ' + JSON.stringify(res)); + return res; + }, + + /** + * performs action to obtain id and return a value. + * @function + * @param {SubmoduleParams} [configParams] + * @returns {{id: array}} + */ + getId(configParams) { + let res = []; + if (utils.isArray(configParams.eids)) { + res = res.concat(configParams.eids); + } + if (typeof configParams.eidsFunction === 'function') { + res = res.concat(configParams.eidsFunction()); + } + return {id: res}; + } +}; + +// Register submodule for userId +submodule('userId', pubProvidedIdSubmodule); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9d4b6203a86..18d7973d9fa 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -559,6 +559,17 @@ export const spec = { const configUserId = config.getConfig('user.id'); if (configUserId) { data['ppuid'] = configUserId; + } else { + // if config.getConfig('user.id') doesn't return anything, then look for the first eid.uids[*].ext.stype === 'ppuid' + for (let i = 0; bidRequest.userIdAsEids && i < bidRequest.userIdAsEids.length; i++) { + if (bidRequest.userIdAsEids[i].uids) { + const pubProvidedId = find(bidRequest.userIdAsEids[i].uids, uid => uid.ext && uid.ext.stype === 'ppuid'); + if (pubProvidedId && pubProvidedId.id) { + data['ppuid'] = pubProvidedId.id; + break; + } + } + } } if (bidderRequest.gdprConsent) { diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 9e39b548a57..4e4576dbb14 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -190,9 +190,13 @@ export function createEidsArray(bidRequestUserId) { let eids = []; for (const subModuleKey in bidRequestUserId) { if (bidRequestUserId.hasOwnProperty(subModuleKey)) { - const eid = createEidObject(bidRequestUserId[subModuleKey], subModuleKey); - if (eid) { - eids.push(eid); + if (subModuleKey === 'pubProvidedId') { + eids = eids.concat(bidRequestUserId['pubProvidedId']); + } else { + const eid = createEidObject(bidRequestUserId[subModuleKey], subModuleKey); + if (eid) { + eids.push(eid); + } } } } diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 830f542ef40..26dbad2f153 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -261,8 +261,42 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('pubProvidedId', function() { + const userId = { + pubProvidedId: [{ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: 'value read from cookie or local storage' + }] + }] + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(2); + expect(newEids[0]).to.deep.equal({ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }); + expect(newEids[1]).to.deep.equal({ + source: 'id-partner.com', + uids: [{ + id: 'value read from cookie or local storage' + }] + }); + }); }); - describe('Negative case', function() { it('eids array generation for UN-known sub-module', function() { // UnknownCommonId diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index f4e35517636..9417303da0e 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -4,7 +4,7 @@ import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import find from 'core-js-pure/features/array/find.js'; -import { createEidsArray } from 'modules/userId/eids.js'; +import {createEidsArray} from 'modules/userId/eids.js'; const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid const PBS_INTEGRATION = 'pbjs'; @@ -137,7 +137,7 @@ describe('the rubicon adapter', function () { 'targeting': [ { 'key': getProp('targeting_key', `rpfl_${i}`), - 'values': [ '43_tier_all_test' ] + 'values': ['43_tier_all_test'] } ] }; @@ -220,11 +220,25 @@ describe('the rubicon adapter', function () { 'size_id': 201, }; bid.userId = { - lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, + lipb: {lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB']}, idl_env: '1111-2222-3333-4444', - sharedid: { id: '1111', third: '2222' }, + sharedid: {id: '1111', third: '2222'}, tdid: '3000', - pubcid: '4000' + pubcid: '4000', + pubProvidedId: [{ + source: 'example.com', + uids: [{ + id: '333333', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: '4444444' + }] + }] }; bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; @@ -461,29 +475,29 @@ describe('the rubicon adapter', function () { data = parseQuery(request.data); expect(data.rp_hard_floor).to.equal('1.23'); }); - it('should not send p_pos to AE if not params.position specified', function() { - var noposRequest = utils.deepClone(bidderRequest); - delete noposRequest.bids[0].params.position; + it('should not send p_pos to AE if not params.position specified', function () { + var noposRequest = utils.deepClone(bidderRequest); + delete noposRequest.bids[0].params.position; - let [request] = spec.buildRequests(noposRequest.bids, noposRequest); - let data = parseQuery(request.data); + let [request] = spec.buildRequests(noposRequest.bids, noposRequest); + let data = parseQuery(request.data); - expect(data['site_id']).to.equal('70608'); - expect(data['p_pos']).to.equal(undefined); + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal(undefined); }); - it('should not send p_pos to AE if not params.position is invalid', function() { - var badposRequest = utils.deepClone(bidderRequest); - badposRequest.bids[0].params.position = 'bad'; + it('should not send p_pos to AE if not params.position is invalid', function () { + var badposRequest = utils.deepClone(bidderRequest); + badposRequest.bids[0].params.position = 'bad'; - let [request] = spec.buildRequests(badposRequest.bids, badposRequest); - let data = parseQuery(request.data); + let [request] = spec.buildRequests(badposRequest.bids, badposRequest); + let data = parseQuery(request.data); - expect(data['site_id']).to.equal('70608'); - expect(data['p_pos']).to.equal(undefined); + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal(undefined); }); - it('should correctly send p_pos in sra fashion', function() { + it('should correctly send p_pos in sra fashion', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { const config = { 'rubicon.singleRequest': true @@ -519,11 +533,11 @@ describe('the rubicon adapter', function () { expect(data['p_pos']).to.equal('atf;;btf;;'); }); - it('should not send x_source.pchain to AE if params.pchain is not specified', function() { - var noPchainRequest = utils.deepClone(bidderRequest); - delete noPchainRequest.bids[0].params.pchain; + it('should not send x_source.pchain to AE if params.pchain is not specified', function () { + var noPchainRequest = utils.deepClone(bidderRequest); + delete noPchainRequest.bids[0].params.pchain; - let [request] = spec.buildRequests(noPchainRequest.bids, noPchainRequest); + let [request] = spec.buildRequests(noPchainRequest.bids, noPchainRequest); expect(request.data).to.contain('&site_id=70608&'); expect(request.data).to.not.contain('x_source.pchain'); }); @@ -624,7 +638,7 @@ describe('the rubicon adapter', function () { expect(parseQuery(request.data).rf).to.equal('localhost'); delete bidderRequest.bids[0].params.referrer; - let refererInfo = { referer: 'https://www.prebid.org' }; + let refererInfo = {referer: 'https://www.prebid.org'}; bidderRequest = Object.assign({refererInfo}, bidderRequest); [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(parseQuery(request.data).rf).to.equal('https://www.prebid.org'); @@ -1041,7 +1055,7 @@ describe('the rubicon adapter', function () { keywords: ['d'], gender: 'M', yob: '1984', - geo: { country: 'ca' } + geo: {country: 'ca'} }; sandbox.stub(config, 'getConfig').callsFake(key => { @@ -1315,7 +1329,7 @@ describe('the rubicon adapter', function () { }); }); - describe('user id config', function() { + describe('user id config', function () { it('should send tpid_tdid when userIdAsEids contains unifiedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { @@ -1391,9 +1405,36 @@ describe('the rubicon adapter', function () { }); }); + describe('pubProvidedId support', function () { + it('should send pubProvidedId when userIdAsEids contains pubProvidedId ids', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + pubProvidedId: [{ + source: 'example.com', + uids: [{ + id: '11111', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: '222222' + }] + }] + }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['ppuid']).to.equal('11111'); + }); + }); + describe('Config user.id support', function () { it('should send ppuid when config defines user.id', function () { - config.setConfig({ user: { id: '123' } }); + config.setConfig({user: {id: '123'}}); const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { sharedid: { @@ -1710,7 +1751,7 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].bidfloor).to.be.undefined; }); - it('should add alias name to PBS Request', function() { + it('should add alias name to PBS Request', function () { createVideoBidderRequest(); bidderRequest.bidderCode = 'superRubicon'; @@ -1726,7 +1767,7 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext).to.not.haveOwnProperty('rubicon'); }); - it('should send correct bidfloor to PBS', function() { + it('should send correct bidfloor to PBS', function () { createVideoBidderRequest(); bidderRequest.bids[0].params.floor = 0.1; @@ -1974,7 +2015,7 @@ describe('the rubicon adapter', function () { keywords: ['d'], gender: 'M', yob: '1984', - geo: { country: 'ca' } + geo: {country: 'ca'} }; sandbox.stub(config, 'getConfig').callsFake(key => { @@ -1988,7 +2029,7 @@ describe('the rubicon adapter', function () { }); const expected = [{ - bidders: [ 'rubicon' ], + bidders: ['rubicon'], config: { fpd: { site: Object.assign({}, bidderRequest.bids[0].params.inventory, context), @@ -2066,7 +2107,7 @@ describe('the rubicon adapter', function () { }); it('should pass the user.id provided in the config', function () { - config.setConfig({ user: { id: '123' } }); + config.setConfig({user: {id: '123'}}); createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => @@ -2120,7 +2161,11 @@ describe('the rubicon adapter', function () { it('should combine an array of slot url params', function () { expect(spec.combineSlotUrlParams([])).to.deep.equal({}); - expect(spec.combineSlotUrlParams([{p1: 'foo', p2: 'test', p3: ''}])).to.deep.equal({p1: 'foo', p2: 'test', p3: ''}); + expect(spec.combineSlotUrlParams([{p1: 'foo', p2: 'test', p3: ''}])).to.deep.equal({ + p1: 'foo', + p2: 'test', + p3: '' + }); expect(spec.combineSlotUrlParams([{}, {p1: 'foo', p2: 'test'}])).to.deep.equal({p1: ';foo', p2: ';test'}); @@ -2676,7 +2721,7 @@ describe('the rubicon adapter', function () { }] }; - let bids = spec.interpretResponse({ body: response }, { + let bids = spec.interpretResponse({body: response}, { bidRequest: [utils.deepClone(bidderRequest.bids[0])] }); @@ -2687,7 +2732,7 @@ describe('the rubicon adapter', function () { describe('singleRequest enabled', function () { it('handles bidRequest of type Array and returns associated adUnits', function () { const overrideMap = []; - overrideMap[0] = { impression_id: '1' }; + overrideMap[0] = {impression_id: '1'}; const stubAds = []; for (let i = 0; i < 10; i++) { @@ -2709,7 +2754,8 @@ describe('the rubicon adapter', function () { 'tracking': '', 'inventory': {}, 'ads': stubAds - }}, { bidRequest: stubBids }); + } + }, {bidRequest: stubBids}); expect(bids).to.be.a('array').with.lengthOf(10); bids.forEach((bid) => { @@ -2742,7 +2788,7 @@ describe('the rubicon adapter', function () { it('handles incorrect adUnits length by returning all bids with matching ads', function () { const overrideMap = []; - overrideMap[0] = { impression_id: '1' }; + overrideMap[0] = {impression_id: '1'}; const stubAds = []; for (let i = 0; i < 6; i++) { @@ -2764,7 +2810,8 @@ describe('the rubicon adapter', function () { 'tracking': '', 'inventory': {}, 'ads': stubAds - }}, { bidRequest: stubBids }); + } + }, {bidRequest: stubBids}); // no bids expected because response didn't match requested bid number expect(bids).to.be.a('array').with.lengthOf(6); @@ -2775,11 +2822,11 @@ describe('the rubicon adapter', function () { // Create overrides to break associations between bids and ads // Each override should cause one less bid to be returned by interpretResponse const overrideMap = []; - overrideMap[0] = { impression_id: '1' }; - overrideMap[2] = { status: 'error' }; - overrideMap[4] = { status: 'error' }; - overrideMap[7] = { status: 'error' }; - overrideMap[8] = { status: 'error' }; + overrideMap[0] = {impression_id: '1'}; + overrideMap[2] = {status: 'error'}; + overrideMap[4] = {status: 'error'}; + overrideMap[7] = {status: 'error'}; + overrideMap[8] = {status: 'error'}; for (let i = 0; i < 10; i++) { stubAds.push(createResponseAdByIndex(i, sizeMap[i].sizeId, overrideMap)); @@ -2800,7 +2847,8 @@ describe('the rubicon adapter', function () { 'tracking': '', 'inventory': {}, 'ads': stubAds - }}, { bidRequest: stubBids }); + } + }, {bidRequest: stubBids}); expect(bids).to.be.a('array').with.lengthOf(6); bids.forEach((bid) => { @@ -2931,7 +2979,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdpr params if consent is true', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { gdprApplies: true, consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr=1&gdpr_consent=foo` @@ -2939,7 +2987,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdpr params if consent is false', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { gdprApplies: false, consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr=0&gdpr_consent=foo` @@ -2947,7 +2995,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr_consent=foo` @@ -2955,13 +3003,13 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is not defined', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {})).to.deep.equal({ + expect(spec.getUserSyncs({iframeEnabled: true}, {}, {})).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); it('should pass no params if gdpr consentString is a number', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { consentString: 0 })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -2969,7 +3017,7 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is null', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { consentString: null })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -2977,7 +3025,7 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is a object', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { consentString: {} })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -2985,19 +3033,19 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr is not defined', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined)).to.deep.equal({ + expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined)).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); it('should pass us_privacy if uspConsent is defined', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal({ + expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined, '1NYN')).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?us_privacy=1NYN` }); }); it('should pass us_privacy after gdpr if both are present', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { consentString: 'foo' }, '1NYN')).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr_consent=foo&us_privacy=1NYN` @@ -3005,8 +3053,8 @@ describe('the rubicon adapter', function () { }); }); - describe('get price granularity', function() { - it('should return correct buckets for all price granularity values', function() { + describe('get price granularity', function () { + it('should return correct buckets for all price granularity values', function () { const CUSTOM_PRICE_BUCKET_ITEM = {max: 5, increment: 0.5}; const mockConfig = { @@ -3039,7 +3087,7 @@ describe('the rubicon adapter', function () { }); }); - describe('Supply Chain Support', function() { + describe('Supply Chain Support', function () { const nodePropsOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; let bidRequests; let schainConfig; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 749dd653865..c4e52d9a121 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1,13 +1,13 @@ import { attachIdSystem, auctionDelay, + coreStorage, init, requestBidsHook, - setSubmoduleRegistry, - syncDelay, - coreStorage, + setStoredConsentData, setStoredValue, - setStoredConsentData + setSubmoduleRegistry, + syncDelay } from 'modules/userId/index.js'; import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; @@ -15,8 +15,11 @@ import * as utils from 'src/utils.js'; import events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import {getGlobal} from 'src/prebidGlobal.js'; -import {setConsentConfig, requestBidsHook as consentManagementRequestBidsHook, resetConsentData} from 'modules/consentManagement.js'; -import {gdprDataHandler} from 'src/adapterManager.js'; +import { + requestBidsHook as consentManagementRequestBidsHook, + resetConsentData, + setConsentConfig +} from 'modules/consentManagement.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem.js'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; @@ -30,13 +33,14 @@ import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; import {haloIdSubmodule} from 'modules/haloIdSystem.js'; import {server} from 'test/mocks/xhr.js'; +import {pubProvidedIdSubmodule} from 'modules/pubProvidedSystem.js'; let assert = require('chai').assert; let expect = require('chai').expect; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const CONSENT_LOCAL_STORAGE_NAME = '_pbjs_userid_consent_data'; -describe('User ID', function() { +describe('User ID', function () { function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8, configArr9, configArr10) { return { userSync: { @@ -90,35 +94,35 @@ describe('User ID', function() { return cfg; } - before(function() { + before(function () { coreStorage.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); localStorage.removeItem('_pbjs_id_optout'); localStorage.removeItem('_pubcid_optout'); }); - beforeEach(function() { + beforeEach(function () { coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); }); - describe('Decorate Ad Units', function() { - beforeEach(function() { + describe('Decorate Ad Units', function () { + beforeEach(function () { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); sinon.spy(coreStorage, 'setCookie'); }); - afterEach(function() { + afterEach(function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); config.resetConfig(); coreStorage.setCookie.restore(); }); - after(function() { + after(function () { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('pubcid_alt', '', EXPIRED_COOKIE_DATE); }); - it('Check same cookie behavior', function() { + it('Check same cookie behavior', function () { let adUnits1 = [getAdUnitMock()]; let adUnits2 = [getAdUnitMock()]; let innerAdUnits1; @@ -153,7 +157,7 @@ describe('User ID', function() { assert.deepEqual(innerAdUnits1, innerAdUnits2); }); - it('Check different cookies', function() { + it('Check different cookies', function () { let adUnits1 = [getAdUnitMock()]; let adUnits2 = [getAdUnitMock()]; let innerAdUnits1; @@ -204,7 +208,7 @@ describe('User ID', function() { expect(pubcid1).to.not.equal(pubcid2); }); - it('Use existing cookie', function() { + it('Use existing cookie', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; @@ -229,7 +233,7 @@ describe('User ID', function() { expect(coreStorage.setCookie.callCount).to.equal(1); }); - it('Extend cookie', function() { + it('Extend cookie', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; let customConfig = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); @@ -256,7 +260,7 @@ describe('User ID', function() { expect(coreStorage.setCookie.callCount).to.equal(2); }); - it('Disable auto create', function() { + it('Disable auto create', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; let customConfig = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); @@ -278,7 +282,7 @@ describe('User ID', function() { expect(coreStorage.setCookie.callCount).to.equal(1); }); - it('pbjs.getUserIds', function() { + it('pbjs.getUserIds', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig({ @@ -293,7 +297,7 @@ describe('User ID', function() { expect((getGlobal()).getUserIds()).to.deep.equal({pubcid: '11111'}); }); - it('pbjs.getUserIdsAsEids', function() { + it('pbjs.getUserIdsAsEids', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig({ @@ -309,16 +313,16 @@ describe('User ID', function() { }); }); - describe('Opt out', function() { - before(function() { + describe('Opt out', function () { + before(function () { coreStorage.setCookie('_pbjs_id_optout', '1', (new Date(Date.now() + 5000).toUTCString())); }); - beforeEach(function() { + beforeEach(function () { sinon.stub(utils, 'logInfo'); }); - afterEach(function() { + afterEach(function () { // removed cookie coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); $$PREBID_GLOBAL$$.requestBids.removeAll(); @@ -326,18 +330,18 @@ describe('User ID', function() { config.resetConfig(); }); - after(function() { + after(function () { coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); }); - it('fails initialization if opt out cookie exists', function() { + it('fails initialization if opt out cookie exists', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); 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'); }); - it('initializes if no opt out cookie exists', function() { + it('initializes if no opt out cookie exists', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); @@ -345,19 +349,19 @@ describe('User ID', function() { }); }); - describe('Handle variations of config values', function() { - beforeEach(function() { + describe('Handle variations of config values', function () { + beforeEach(function () { sinon.stub(utils, 'logInfo'); }); - afterEach(function() { + afterEach(function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); utils.logInfo.restore(); config.resetConfig(); }); - it('handles config with no usersync object', function() { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + it('handles config with no usersync object', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -365,14 +369,14 @@ describe('User ID', function() { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -383,7 +387,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -400,20 +404,22 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 10 configurations should result in 11 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + it('config with 11 configurations should result in 12 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { syncDelay: 0, userIds: [{ + name: 'pubProvidedId' + }, { name: 'pubCommonId', value: {'pubcid': '11111'} }, { name: 'unifiedId', @@ -438,20 +444,20 @@ describe('User ID', function() { storage: {name: 'sharedid', type: 'cookie'} }, { name: 'intentIqId', - storage: { name: 'intentIqId', type: 'cookie' } + storage: {name: 'intentIqId', type: 'cookie'} }, { name: 'haloId', - storage: { name: 'haloId', type: 'cookie' } + storage: {name: 'haloId', type: 'cookie'} }, { name: 'zeotapIdPlus' }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 11 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 12 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -466,7 +472,7 @@ describe('User ID', function() { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -481,7 +487,7 @@ describe('User ID', function() { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -496,13 +502,13 @@ describe('User ID', function() { }); }); - describe('auction and user sync delays', function() { + describe('auction and user sync delays', function () { let sandbox; let adUnits; let mockIdCallback; let auctionSpy; - beforeEach(function() { + beforeEach(function () { sandbox = sinon.createSandbox(); sandbox.stub(global, 'setTimeout').returns(2); sandbox.stub(events, 'on'); @@ -517,12 +523,12 @@ describe('User ID', function() { mockIdCallback = sandbox.stub(); const mockIdSystem = { name: 'mockId', - decode: function(value) { + decode: function (value) { return { 'mid': value['MOCKID'] }; }, - getId: function() { + getId: function () { const storedId = coreStorage.getCookie('MOCKID'); if (storedId) { return {id: {'MOCKID': storedId}}; @@ -536,13 +542,13 @@ describe('User ID', function() { attachIdSystem(mockIdSystem, true); }); - afterEach(function() { + afterEach(function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); config.resetConfig(); sandbox.restore(); }); - it('delays auction if auctionDelay is set, timing out at auction delay', function() { + it('delays auction if auctionDelay is set, timing out at auction delay', function () { config.setConfig({ userSync: { auctionDelay: 33, @@ -575,7 +581,7 @@ describe('User ID', function() { events.on.called.should.equal(false); }); - it('delays auction if auctionDelay is set, continuing auction if ids are fetched before timing out', function(done) { + it('delays auction if auctionDelay is set, continuing auction if ids are fetched before timing out', function (done) { config.setConfig({ userSync: { auctionDelay: 33, @@ -614,7 +620,7 @@ describe('User ID', function() { events.on.called.should.equal(false); }); - it('does not delay auction if not set, delays id fetch after auction ends with syncDelay', function() { + it('does not delay auction if not set, delays id fetch after auction ends with syncDelay', function () { config.setConfig({ userSync: { syncDelay: 77, @@ -650,7 +656,7 @@ describe('User ID', function() { mockIdCallback.calledOnce.should.equal(true); }); - it('does not delay user id sync after auction ends if set to 0', function() { + it('does not delay user id sync after auction ends if set to 0', function () { config.setConfig({ userSync: { syncDelay: 0, @@ -679,7 +685,7 @@ describe('User ID', function() { mockIdCallback.calledOnce.should.equal(true); }); - it('does not delay auction if there are no ids to fetch', function() { + it('does not delay auction if there are no ids to fetch', function () { coreStorage.getCookie.withArgs('MOCKID').returns('123456778'); config.setConfig({ userSync: { @@ -702,21 +708,21 @@ describe('User ID', function() { }); }); - describe('Request bids hook appends userId to bid objs in adapters', function() { + describe('Request bids hook appends userId to bid objs in adapters', function () { let adUnits; - beforeEach(function() { + beforeEach(function () { adUnits = [getAdUnitMock()]; }); - it('test hook from pubcommonid cookie', function(done) { + it('test hook from pubcommonid cookie', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.pubcid'); @@ -732,12 +738,12 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from pubcommonid config value object', function(done) { + it('test hook from pubcommonid config value object', function (done) { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig(getConfigValueMock('pubCommonId', {'pubcidvalue': 'testpubcidvalue'})); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); @@ -749,7 +755,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from pubcommonid html5', function(done) { + it('test hook from pubcommonid html5', function (done) { // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); @@ -758,7 +764,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid_alt', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.tdid'); @@ -775,7 +781,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from identityLink html5', function(done) { + it('test hook from identityLink html5', function (done) { // simulate existing browser local storage values localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); localStorage.setItem('idl_env_exp', ''); @@ -783,7 +789,7 @@ describe('User ID', function() { setSubmoduleRegistry([identityLinkSubmodule]); init(config); config.setConfig(getConfigMock(['identityLink', 'idl_env', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.idl_env'); @@ -800,14 +806,14 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from identityLink cookie', function(done) { + it('test hook from identityLink cookie', function (done) { coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([identityLinkSubmodule]); init(config); config.setConfig(getConfigMock(['identityLink', 'idl_env', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.idl_env'); @@ -823,7 +829,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from liveIntentId html5', function(done) { + it('test hook from liveIntentId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier'})); localStorage.setItem('_li_pbid_exp', ''); @@ -831,7 +837,7 @@ describe('User ID', function() { setSubmoduleRegistry([liveIntentIdSubmodule]); init(config); config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.lipb'); @@ -848,14 +854,14 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from liveIntentId cookie', function(done) { + it('test hook from liveIntentId cookie', function (done) { coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([liveIntentIdSubmodule]); init(config); config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.lipb'); @@ -871,7 +877,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from sharedId html5', function(done) { + it('test hook from sharedId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611})); localStorage.setItem('sharedid_exp', ''); @@ -879,7 +885,7 @@ describe('User ID', function() { setSubmoduleRegistry([sharedIdSubmodule]); init(config); config.setConfig(getConfigMock(['sharedId', 'sharedid', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.sharedid'); @@ -907,7 +913,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from sharedId html5 (id not synced)', function(done) { + it('test hook from sharedId html5 (id not synced)', function (done) { // simulate existing browser local storage values localStorage.setItem('sharedid', JSON.stringify({'id': 'test_sharedId', 'ns': true, 'ts': 1590525289611})); localStorage.setItem('sharedid_exp', ''); @@ -915,7 +921,7 @@ describe('User ID', function() { setSubmoduleRegistry([sharedIdSubmodule]); init(config); config.setConfig(getConfigMock(['sharedId', 'sharedid', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.sharedid'); @@ -937,14 +943,17 @@ describe('User ID', function() { done(); }, {adUnits}); }); - it('test hook from sharedId cookie', function(done) { - coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 100000).toUTCString())); + it('test hook from sharedId cookie', function (done) { + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test_sharedId', + 'ts': 1590525289611 + }), (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([sharedIdSubmodule]); init(config); config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.sharedid'); @@ -970,14 +979,18 @@ describe('User ID', function() { done(); }, {adUnits}); }); - it('test hook from sharedId cookie (id not synced) ', function(done) { - coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ns': true, 'ts': 1590525289611}), (new Date(Date.now() + 100000).toUTCString())); + it('test hook from sharedId cookie (id not synced) ', function (done) { + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test_sharedId', + 'ns': true, + 'ts': 1590525289611 + }), (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([sharedIdSubmodule]); init(config); config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.sharedid'); @@ -999,7 +1012,104 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from liveIntentId html5', function(done) { + it('test hook from pubProvidedId config params', function (done) { + setSubmoduleRegistry([pubProvidedIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'pubProvidedId', + params: { + eids: [{ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'dmp' + } + }] + }], + eidsFunction: function () { + return [{ + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } + }] + }] + } + } + } + ] + } + }); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubProvidedId'); + expect(bid.userId.pubProvidedId).to.deep.equal([{ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'dmp' + } + }] + }, { + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } + }] + }]); + + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }); + expect(bid.userIdAsEids[2]).to.deep.equal({ + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } + }] + }); + }); + }); + done(); + }, {adUnits}); + }); + + it('test hook from liveIntentId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier', 'segments': ['123']})); localStorage.setItem('_li_pbid_exp', ''); @@ -1007,7 +1117,7 @@ describe('User ID', function() { setSubmoduleRegistry([liveIntentIdSubmodule]); init(config); config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.lipb'); @@ -1026,7 +1136,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from liveIntentId cookie', function(done) { + it('test hook from liveIntentId cookie', function (done) { coreStorage.setCookie('_li_pbid', JSON.stringify({ 'unifiedId': 'random-cookie-identifier', 'segments': ['123'] @@ -1036,7 +1146,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.lipb'); @@ -1054,7 +1164,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from britepoolid cookies', function(done) { + it('test hook from britepoolid cookies', function (done) { // simulate existing browser local storage values coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1062,7 +1172,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['britepoolId', 'britepoolid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.britepoolid'); @@ -1078,7 +1188,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from netId cookies', function(done) { + it('test hook from netId cookies', function (done) { // simulate existing browser local storage values coreStorage.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1086,7 +1196,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['netId', 'netId', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.netId'); @@ -1102,7 +1212,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from intentIqId cookies', function(done) { + it('test hook from intentIqId cookies', function (done) { // simulate existing browser local storage values coreStorage.setCookie('intentIqId', 'abcdefghijk', (new Date(Date.now() + 5000).toUTCString())); @@ -1110,7 +1220,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['intentIqId', 'intentIqId', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.intentIqId'); @@ -1126,7 +1236,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from haloId html5', function(done) { + it('test hook from haloId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('haloId', JSON.stringify({'haloId': 'random-ls-identifier'})); localStorage.setItem('haloId_exp', ''); @@ -1135,7 +1245,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['haloId', 'haloId', 'html5'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.haloId'); @@ -1152,7 +1262,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from merkleId cookies', function(done) { + it('test hook from merkleId cookies', function (done) { // simulate existing browser local storage values coreStorage.setCookie('merkleId', JSON.stringify({'ppid': {'id': 'testmerkleId'}}), (new Date(Date.now() + 5000).toUTCString())); @@ -1160,7 +1270,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['merkleId', 'merkleId', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.merkleId'); @@ -1176,7 +1286,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from zeotapIdPlus cookies', function(done) { + it('test hook from zeotapIdPlus cookies', function (done) { // simulate existing browser local storage values coreStorage.setCookie('IDP', btoa(JSON.stringify('abcdefghijk')), (new Date(Date.now() + 5000).toUTCString())); @@ -1184,7 +1294,7 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['zeotapIdPlus', 'IDP', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.IDP'); @@ -1200,7 +1310,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have data to pass', function(done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1209,7 +1319,10 @@ describe('User ID', function() { coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test_sharedId', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); @@ -1225,7 +1338,7 @@ describe('User ID', function() { ['zeotapIdPlus', 'IDP', 'cookie'], ['haloId', 'haloId', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { // verify that the PubCommonId id data was copied to bid @@ -1277,14 +1390,17 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have their modules added before and after init', function(done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have their modules added before and after init', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test_sharedId', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1318,7 +1434,7 @@ describe('User ID', function() { ['zeotapIdPlus', 'IDP', 'cookie'], ['haloId', 'haloId', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { // verify that the PubCommonId id data was copied to bid @@ -1371,8 +1487,11 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when sharedId(opted out) have their modules added before and after init', function(done) { - coreStorage.setCookie('sharedid', JSON.stringify({'id': '00000000000000000000000000', 'ts': 1590525289611}), (new Date(Date.now() + 5000).toUTCString())); + it('test hook when sharedId(opted out) have their modules added before and after init', function (done) { + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': '00000000000000000000000000', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); init(config); @@ -1381,7 +1500,7 @@ describe('User ID', function() { config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid.userIdAsEids).to.be.undefined; @@ -1392,14 +1511,17 @@ describe('User ID', function() { }, {adUnits}); }); - it('should add new id system ', function(done) { + it('should add new id system ', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test_sharedId', + 'ts': 1590525289611 + }), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1426,11 +1548,11 @@ describe('User ID', function() { }, { name: 'sharedId', storage: {name: 'sharedid', type: 'cookie'} }, { - name: 'intentIqId', storage: { name: 'intentIqId', type: 'cookie' } + name: 'intentIqId', storage: {name: 'intentIqId', type: 'cookie'} }, { name: 'zeotapIdPlus' }, { - name: 'haloId', storage: { name: 'haloId', type: 'cookie' } + name: 'haloId', storage: {name: 'haloId', type: 'cookie'} }, { name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} }] @@ -1440,18 +1562,18 @@ describe('User ID', function() { // Add new submodule named 'mockId' attachIdSystem({ name: 'mockId', - decode: function(value) { + decode: function (value) { return { 'mid': value['MOCKID'] }; }, - getId: function(params, storedId) { + getId: function (params, storedId) { if (storedId) return {}; return {id: {'MOCKID': '1234'}}; } }); - requestBidsHook(function() { + requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { // check PubCommonId id data was copied to bid @@ -1509,8 +1631,8 @@ describe('User ID', function() { }); }); - describe('callbacks at the end of auction', function() { - beforeEach(function() { + describe('callbacks at the end of auction', function () { + beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); sinon.stub(utils, 'triggerPixel'); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1518,7 +1640,7 @@ describe('User ID', function() { coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); - afterEach(function() { + afterEach(function () { events.getEvents.restore(); utils.triggerPixel.restore(); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1526,7 +1648,7 @@ describe('User ID', function() { coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); - it('pubcid callback with url', function() { + it('pubcid callback with url', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; let customCfg = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); @@ -1544,7 +1666,7 @@ describe('User ID', function() { expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); }); - it('unifiedid callback with url', function() { + it('unifiedid callback with url', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); @@ -1562,7 +1684,7 @@ describe('User ID', function() { expect(server.requests[0].url).to.equal('/any/unifiedid/url'); }); - it('unifiedid callback with partner', function() { + it('unifiedid callback with partner', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); @@ -1581,7 +1703,7 @@ describe('User ID', function() { }); }); - describe('Set cookie behavior', function() { + describe('Set cookie behavior', function () { let coreStorageSpy; beforeEach(function () { coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); @@ -1620,7 +1742,7 @@ describe('User ID', function() { }); }); - describe('Consent changes determine getId refreshes', function() { + describe('Consent changes determine getId refreshes', function () { let expStr; let adUnits; @@ -1656,7 +1778,7 @@ describe('User ID', function() { allowAuctionWithoutConsent: false }; - const sharedBeforeFunction = function() { + const sharedBeforeFunction = function () { // clear cookies expStr = (new Date(Date.now() + 25000).toUTCString()); coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); @@ -1682,7 +1804,7 @@ describe('User ID', function() { delete window.__tcfapi; }; - describe('TCF v1', function() { + describe('TCF v1', function () { testConsentData = { gdprApplies: true, consentData: 'xyz', @@ -1693,7 +1815,8 @@ describe('User ID', function() { sharedBeforeFunction(); // init v1 consent management - window.__cmp = function () { }; + window.__cmp = function () { + }; delete window.__tcfapi; cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); @@ -1701,17 +1824,20 @@ describe('User ID', function() { setConsentConfig(consentConfig); }); - afterEach(function() { + afterEach(function () { sharedAfterFunction(); }); it('does not call getId if no stored consent data and refresh is not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); let innerAdUnits; - consentManagementRequestBidsHook(() => { }, {}); - requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); sinon.assert.notCalled(mockGetId); sinon.assert.calledOnce(mockDecode); @@ -1719,12 +1845,15 @@ describe('User ID', function() { }); it('calls getId if no stored consent data but refresh is needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); let innerAdUnits; - consentManagementRequestBidsHook(() => { }, {}); - requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); @@ -1732,14 +1861,17 @@ describe('User ID', function() { }); it('calls getId if empty stored consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); setStoredConsentData(); let innerAdUnits; - consentManagementRequestBidsHook(() => { }, {}); - requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); @@ -1747,7 +1879,7 @@ describe('User ID', function() { }); it('calls getId if stored consent does not match current consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); setStoredConsentData({ @@ -1757,8 +1889,11 @@ describe('User ID', function() { }); let innerAdUnits; - consentManagementRequestBidsHook(() => { }, {}); - requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); @@ -1766,7 +1901,7 @@ describe('User ID', function() { }); it('does not call getId if stored consent matches current consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({ id: '1234' }), expStr); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); setStoredConsentData({ @@ -1776,8 +1911,11 @@ describe('User ID', function() { }); let innerAdUnits; - consentManagementRequestBidsHook(() => { }, {}); - requestBidsHook((config) => { innerAdUnits = config.adUnits }, { adUnits }); + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); sinon.assert.notCalled(mockGetId); sinon.assert.calledOnce(mockDecode); From 92e234c4f266ac21003ba7adb3652eae412e4ac4 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 29 Sep 2020 10:54:30 -0700 Subject: [PATCH 0239/1476] standardize rubicon get config calls (#5780) --- modules/rubiconAnalyticsAdapter.js | 46 +++----- modules/rubiconBidAdapter.js | 48 +++------ .../modules/rubiconAnalyticsAdapter_spec.js | 101 +++++++++++++----- test/spec/modules/rubiconBidAdapter_spec.js | 69 ++++-------- 4 files changed, 132 insertions(+), 132 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 72648f8feb5..f6d30e06e9a 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -51,27 +51,16 @@ const cache = { const BID_REJECTED_IPF = 'rejected-ipf'; -let fpkvs = {}; -function updateFpkvs(fpkvs, newKvs) { - const isValid = typeof newKvs === 'object' && Object.keys(newKvs).every(key => typeof newKvs[key] === 'string'); - if (!isValid) { - utils.logError('Rubicon Analytics: fpkvs must be object with string keys and values'); - return fpkvs; - } else { - return {...fpkvs, ...newKvs}; - } -} - -let integration, ruleId, wrapperName; -// listen for any rubicon setConfig events and save them to appropriate fields! +export let rubiConf = { + pvid: utils.generateUUID().slice(0, 8) +}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire // rubicon object, then we do not lose other data config.getConfig('rubicon', config => { - let rubiConf = config.rubicon; - integration = rubiConf.int_type || integration || DEFAULT_INTEGRATION; - ruleId = rubiConf.rule_name || ruleId; - wrapperName = rubiConf.wrapperName || wrapperName; - fpkvs = rubiConf.fpkvs ? updateFpkvs(fpkvs, rubiConf.fpkvs) : fpkvs + utils.mergeDeep(rubiConf, config.rubicon); + if (utils.deepAccess(config, 'rubicon.updatePageView') === true) { + rubiConf.pvid = utils.generateUUID().slice(0, 8) + } }); export function getHostNameFromReferer(referer) { @@ -163,13 +152,13 @@ function sendMessage(auctionId, bidWonId) { let referrer = config.getConfig('pageUrl') || (auctionCache && auctionCache.referrer); let message = { eventTimeMillis: Date.now(), - integration, - ruleId, + integration: rubiConf.int_type || DEFAULT_INTEGRATION, + ruleId: rubiConf.rule_name, version: '$prebid.version$', referrerUri: referrer, referrerHostname: rubiconAdapter.referrerHostname || getHostNameFromReferer(referrer), channel: 'web', - wrapperName + wrapperName: rubiConf.wrapperName }; if (auctionCache && !auctionCache.sent) { let adUnitMap = Object.keys(auctionCache.bids).reduce((adUnits, bidId) => { @@ -359,10 +348,9 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { ]); } -function getPageViewId() { - if (prebidGlobal.rp && typeof prebidGlobal.rp.generatePageViewId === 'function') { - return prebidGlobal.rp.generatePageViewId(false); - } +function getFpkvs() { + const isValid = rubiConf.fpkvs && typeof rubiConf.fpkvs === 'object' && Object.keys(rubiConf.fpkvs).every(key => typeof rubiConf.fpkvs[key] === 'string'); + return isValid ? rubiConf.fpkvs : {}; } let samplingFactor = 1; @@ -420,8 +408,8 @@ function updateRpaCookie() { // possible that decodedRpaCookie is undefined, and if it is, we probably are blocked by storage or some other exception if (Object.keys(decodedRpaCookie).length) { decodedRpaCookie.lastSeen = currentTime; - decodedRpaCookie.fpkvs = {...decodedRpaCookie.fpkvs, ...fpkvs}; - decodedRpaCookie.pvid = getPageViewId(); + decodedRpaCookie.fpkvs = {...decodedRpaCookie.fpkvs, ...getFpkvs()}; + decodedRpaCookie.pvid = rubiConf.pvid; setRpaCookie(decodedRpaCookie) } return decodedRpaCookie; @@ -495,8 +483,8 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }, disableAnalytics() { this.getUrl = baseAdapter.getUrl; - accountId = integration = ruleId = wrapperName = undefined; - fpkvs = {}; + accountId = undefined; + rubiConf = {}; cache.gpt.registered = false; baseAdapter.disableAnalytics.apply(this, arguments); }, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 18d7973d9fa..829fd208887 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -7,24 +7,11 @@ import find from 'core-js-pure/features/array/find.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; -// always use https, regardless of whether or not current page is secure -export let fastlaneEndpoint = `https://fastlane.rubiconproject.com/a/api/fastlane.json`; -export let videoEndpoint = `https://prebid-server.rubiconproject.com/openrtb2/auction`; -export let syncEndpoint = `https://eus.rubiconproject.com/usync.html`; -let returnVast = false; - -let bannerHost = 'fastlane'; -let videoHost = 'prebid-server'; -let syncHost = 'eus'; +let rubiConf = {}; +// we are saving these as global to this module so that if a pub accidentally overwrites the entire +// rubicon object, then we do not lose other data config.getConfig('rubicon', config => { - let rubiConf = config.rubicon; - bannerHost = rubiConf.bannerHost || bannerHost; - fastlaneEndpoint = `https://${bannerHost}.rubiconproject.com/a/api/fastlane.json`; - videoHost = rubiConf.videoHost || videoHost; - videoEndpoint = `https://${videoHost}.rubiconproject.com/openrtb2/auction`; - syncHost = rubiConf.syncHost || syncHost; - syncEndpoint = `https://${syncHost}.rubiconproject.com/usync.html`; - returnVast = rubiConf.returnVast === true; // anything other than true is false + utils.mergeDeep(rubiConf, config.rubicon); }); const GVLID = 52; @@ -192,7 +179,7 @@ export const spec = { prebid: { cache: { vastxml: { - returnCreative: returnVast + returnCreative: rubiConf.returnVast === true } }, targeting: { @@ -203,7 +190,7 @@ export const spec = { }, bidders: { rubicon: { - integration: config.getConfig('rubicon.int_type') || DEFAULT_PBS_INTEGRATION + integration: rubiConf.int_type || DEFAULT_PBS_INTEGRATION } } } @@ -218,7 +205,7 @@ export const spec = { } let bidFloor; - if (typeof bidRequest.getFloor === 'function' && !config.getConfig('rubicon.disableFloors')) { + if (typeof bidRequest.getFloor === 'function' && !rubiConf.disableFloors) { let floorInfo; try { floorInfo = bidRequest.getFloor({ @@ -343,19 +330,19 @@ export const spec = { return { method: 'POST', - url: videoEndpoint, + url: `https://${rubiConf.videoHost || 'prebid-server'}.rubiconproject.com/openrtb2/auction`, data, bidRequest } }); - if (config.getConfig('rubicon.singleRequest') !== true) { + if (rubiConf.singleRequest !== true) { // bids are not grouped if single request mode is not enabled requests = videoRequests.concat(bidRequests.filter(bidRequest => bidType(bidRequest) === 'banner').map(bidRequest => { const bidParams = spec.createSlotParams(bidRequest, bidderRequest); return { method: 'GET', - url: fastlaneEndpoint, + url: `https://${rubiConf.bannerHost || 'fastlane'}.rubiconproject.com/a/api/fastlane.json`, data: spec.getOrderedParams(bidParams).reduce((paramString, key) => { const propValue = bidParams[key]; return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString; @@ -386,7 +373,7 @@ export const spec = { // SRA request returns grouped bidRequest arrays not a plain bidRequest aggregate.push({ method: 'GET', - url: fastlaneEndpoint, + url: `https://${rubiConf.bannerHost || 'fastlane'}.rubiconproject.com/a/api/fastlane.json`, data: spec.getOrderedParams(combinedSlotParams).reduce((paramString, key) => { const propValue = combinedSlotParams[key]; return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString; @@ -493,8 +480,6 @@ export const spec = { const [latitude, longitude] = params.latLong || []; - const configIntType = config.getConfig('rubicon.int_type'); - const data = { 'account_id': params.accountId, 'site_id': params.siteId, @@ -503,7 +488,7 @@ export const spec = { 'alt_size_ids': parsedSizes.slice(1).join(',') || undefined, 'rp_floor': (params.floor = parseFloat(params.floor)) > 0.01 ? params.floor : 0.01, 'rp_secure': '1', - 'tk_flint': `${configIntType || DEFAULT_INTEGRATION}_v$prebid.version$`, + 'tk_flint': `${rubiConf.int_type || DEFAULT_INTEGRATION}_v$prebid.version$`, 'x_source.tid': bidRequest.transactionId, 'x_source.pchain': params.pchain, 'p_screen_res': _getScreenResolution(), @@ -515,7 +500,7 @@ export const spec = { }; // If floors module is enabled and we get USD floor back, send it in rp_hard_floor else undfined - if (typeof bidRequest.getFloor === 'function' && !config.getConfig('rubicon.disableFloors')) { + if (typeof bidRequest.getFloor === 'function' && !rubiConf.disableFloors) { let floorInfo; try { floorInfo = bidRequest.getFloor({ @@ -698,7 +683,7 @@ export const spec = { cpm: bid.price || 0, bidderCode: seatbid.seat, ttl: 300, - netRevenue: config.getConfig('rubicon.netRevenue') !== false, // If anything other than false, netRev is true + netRevenue: rubiConf.netRevenue !== false, // If anything other than false, netRev is true width: bid.w || utils.deepAccess(bidRequest, 'mediaTypes.video.w') || utils.deepAccess(bidRequest, 'params.video.playerWidth'), height: bid.h || utils.deepAccess(bidRequest, 'mediaTypes.video.h') || utils.deepAccess(bidRequest, 'params.video.playerHeight'), }; @@ -778,7 +763,7 @@ export const spec = { cpm: ad.cpm || 0, dealId: ad.deal, ttl: 300, // 5 minutes - netRevenue: config.getConfig('rubicon.netRevenue') !== false, // If anything other than false, netRev is true + netRevenue: rubiConf.netRevenue !== false, // If anything other than false, netRev is true rubicon: { advertiserId: ad.advertiser, networkId: ad.network }, @@ -840,7 +825,7 @@ export const spec = { hasSynced = true; return { type: 'iframe', - url: syncEndpoint + params + url: `https://${rubiConf.syncHost || 'eus'}.rubiconproject.com/usync.html` + params }; } }, @@ -1086,6 +1071,7 @@ function bidType(bid, log = false) { } } +export const resetRubiConf = () => rubiConf = {}; export function masSizeOrdering(sizes) { const MAS_SIZE_PRIORITY = [15, 2, 9]; diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 60b28b02361..2bbab506b34 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -3,6 +3,7 @@ import rubiconAnalyticsAdapter, { parseBidResponse, getHostNameFromReferer, storage, + rubiConf, } from 'modules/rubiconAnalyticsAdapter.js'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config.js'; @@ -12,9 +13,6 @@ import { setConfig, addBidResponseHook, } from 'modules/currency.js'; -import { getGlobal } from 'src/prebidGlobal.js'; - -let prebidGlobal = getGlobal(); let Ajv = require('ajv'); let schema = require('./rubiconAnalyticsSchema.json'); let ajv = new Ajv({ @@ -559,6 +557,73 @@ describe('rubicon analytics adapter', function () { expect(utils.logError.called).to.equal(true); }); + describe('config subscribe', function() { + it('should update the pvid if user asks', function () { + expect(utils.generateUUID.called).to.equal(false); + config.setConfig({rubicon: {updatePageView: true}}); + expect(utils.generateUUID.called).to.equal(true); + }); + it('should merge in and preserve older set configs', function () { + config.setConfig({ + rubicon: { + wrapperName: '1001_general', + int_type: 'dmpbjs', + fpkvs: { + source: 'fb' + } + } + }); + expect(rubiConf).to.deep.equal({ + pvid: '12345678', + wrapperName: '1001_general', + int_type: 'dmpbjs', + fpkvs: { + source: 'fb' + }, + updatePageView: true + }); + + // update it with stuff + config.setConfig({ + rubicon: { + fpkvs: { + link: 'email' + } + } + }); + expect(rubiConf).to.deep.equal({ + pvid: '12345678', + wrapperName: '1001_general', + int_type: 'dmpbjs', + fpkvs: { + source: 'fb', + link: 'email' + }, + updatePageView: true + }); + + // overwriting specific edge keys should update them + config.setConfig({ + rubicon: { + fpkvs: { + link: 'iMessage', + source: 'twitter' + } + } + }); + expect(rubiConf).to.deep.equal({ + pvid: '12345678', + wrapperName: '1001_general', + int_type: 'dmpbjs', + fpkvs: { + link: 'iMessage', + source: 'twitter' + }, + updatePageView: true + }); + }); + }); + describe('sampling', function () { beforeEach(function () { sandbox.stub(Math, 'random').returns(0.08); @@ -865,17 +930,9 @@ describe('rubicon analytics adapter', function () { }); describe('with session handling', function () { - let pvid, kvps; + const expectedPvid = STUBBED_UUID.slice(0, 8); beforeEach(function () { - // custom dm stuff - prebidGlobal.rp = { - getCustomTargeting: () => kvps, - generatePageViewId: () => pvid - } - }); - - afterEach(function () { - prebidGlobal.rp = pvid = kvps = undefined; + config.setConfig({rubicon: {updatePageView: true}}); }); it('should not log any session data if local storage is not enabled', function () { @@ -899,7 +956,6 @@ describe('rubicon analytics adapter', function () { }); it('should should pass along custom rubicon kv and pvid when defined', function () { - pvid = '1a2b3c'; config.setConfig({rubicon: { fpkvs: { source: 'fb', @@ -913,7 +969,7 @@ describe('rubicon analytics adapter', function () { validate(message); let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); - expectedMessage.session.pvid = '1a2b3c'; + expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8); expectedMessage.fpkvs = [ {key: 'source', value: 'fb'}, {key: 'link', value: 'email'} @@ -932,7 +988,6 @@ describe('rubicon analytics adapter', function () { }; getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - pvid = '1a2b3c'; config.setConfig({rubicon: { fpkvs: { link: 'email' // should merge this with what is in the localStorage! @@ -949,7 +1004,7 @@ describe('rubicon analytics adapter', function () { id: '987654', start: 1519766113781, expires: 1519787713781, - pvid: '1a2b3c' + pvid: expectedPvid } expectedMessage.fpkvs = [ {key: 'source', value: 'tw'}, @@ -970,7 +1025,7 @@ describe('rubicon analytics adapter', function () { expires: 1519787713781, // should have stayed same lastSeen: 1519767013781, // lastSeen updated to our "now" fpkvs: { source: 'tw', link: 'email' }, // link merged in - pvid: '1a2b3c' // new pvid stored + pvid: expectedPvid // new pvid stored }); }); @@ -985,7 +1040,6 @@ describe('rubicon analytics adapter', function () { }; getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - pvid = '1a2b3c'; config.setConfig({rubicon: { fpkvs: { link: 'email' // should merge this with what is in the localStorage! @@ -1000,7 +1054,7 @@ describe('rubicon analytics adapter', function () { let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid - expectedMessage.session.pvid = '1a2b3c'; + expectedMessage.session.pvid = expectedPvid; // the saved fpkvs should have been thrown out since session expired expectedMessage.fpkvs = [ @@ -1021,7 +1075,7 @@ describe('rubicon analytics adapter', function () { expires: 1519788613781, // should have stayed same lastSeen: 1519767013781, // lastSeen updated to our "now" fpkvs: { link: 'email' }, // link merged in - pvid: '1a2b3c' // new pvid stored + pvid: expectedPvid // new pvid stored }); }); @@ -1036,7 +1090,6 @@ describe('rubicon analytics adapter', function () { }; getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); - pvid = '1a2b3c'; config.setConfig({rubicon: { fpkvs: { link: 'email' // should merge this with what is in the localStorage! @@ -1051,7 +1104,7 @@ describe('rubicon analytics adapter', function () { let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid - expectedMessage.session.pvid = '1a2b3c'; + expectedMessage.session.pvid = expectedPvid; // the saved fpkvs should have been thrown out since session expired expectedMessage.fpkvs = [ @@ -1072,7 +1125,7 @@ describe('rubicon analytics adapter', function () { expires: 1519788613781, // should have stayed same lastSeen: 1519767013781, // lastSeen updated to our "now" fpkvs: { link: 'email' }, // link merged in - pvid: '1a2b3c' // new pvid stored + pvid: expectedPvid // new pvid stored }); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 9417303da0e..dd25fafb149 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,5 +1,12 @@ import {expect} from 'chai'; -import {spec, getPriceGranularity, masSizeOrdering, resetUserSync, hasVideoMediaType, fastlaneEndpoint} from 'modules/rubiconBidAdapter.js'; +import { + spec, + getPriceGranularity, + masSizeOrdering, + resetUserSync, + hasVideoMediaType, + resetRubiConf +} from 'modules/rubiconBidAdapter.js'; import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -361,6 +368,8 @@ describe('the rubicon adapter', function () { afterEach(function () { sandbox.restore(); utils.logError.restore(); + config.resetConfig(); + resetRubiConf(); }); describe('MAS mapping / ordering', function () { @@ -497,13 +506,8 @@ describe('the rubicon adapter', function () { expect(data['p_pos']).to.equal(undefined); }); - it('should correctly send p_pos in sra fashion', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'rubicon.singleRequest': true - }; - return config[key]; - }); + it('should correctly send p_pos in sra fashion', function() { + config.setConfig({rubicon: {singleRequest: true}}); // first one is atf var sraPosRequest = utils.deepClone(bidderRequest); @@ -1096,12 +1100,7 @@ describe('the rubicon adapter', function () { it('should group all bid requests with the same site id', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'rubicon.singleRequest': true - }; - return config[key]; - }); + config.setConfig({rubicon: {singleRequest: true}}); const expectedQuery = { 'account_id': '14062', @@ -1209,13 +1208,7 @@ describe('the rubicon adapter', function () { }); it('should not send more than 10 bids in a request (split into separate requests with <= 10 bids each)', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'rubicon.singleRequest': true - }; - return config[key]; - }); - + config.setConfig({rubicon: {singleRequest: true}}); let serverRequests; let data; @@ -1257,12 +1250,7 @@ describe('the rubicon adapter', function () { }); it('should not group bid requests if singleRequest does not equal true', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'rubicon.singleRequest': false - }; - return config[key]; - }); + config.setConfig({rubicon: {singleRequest: false}}); const bidCopy = utils.deepClone(bidderRequest.bids[0]); bidderRequest.bids.push(bidCopy); @@ -1280,12 +1268,7 @@ describe('the rubicon adapter', function () { }); it('should not group video bid requests', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'rubicon.singleRequest': true - }; - return config[key]; - }); + config.setConfig({rubicon: {singleRequest: true}}); const bidCopy = utils.deepClone(bidderRequest.bids[0]); bidderRequest.bids.push(bidCopy); @@ -1957,14 +1940,14 @@ describe('the rubicon adapter', function () { let requests = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(fastlaneEndpoint); + expect(requests[0].url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); bidderRequest.mediaTypes.video.context = 'instream'; requests = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(fastlaneEndpoint); + expect(requests[0].url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); }); it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () { @@ -1983,7 +1966,7 @@ describe('the rubicon adapter', function () { let requests = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(fastlaneEndpoint); + expect(requests[0].url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); }); it('should include coppa flag in video bid request', () => { @@ -2096,12 +2079,7 @@ describe('the rubicon adapter', function () { it('should use the integration type provided in the config instead of the default', () => { createVideoBidderRequest(); - sandbox.stub(config, 'getConfig').callsFake(function (key) { - const config = { - 'rubicon.int_type': 'testType' - }; - return config[key]; - }); + config.setConfig({rubicon: {int_type: 'testType'}}); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.ext.prebid.bidders.rubicon.integration).to.equal('testType'); }); @@ -2939,12 +2917,7 @@ describe('the rubicon adapter', function () { describe('config with integration type', () => { it('should use the integration type provided in the config instead of the default', () => { - sandbox.stub(config, 'getConfig').callsFake(function (key) { - const config = { - 'rubicon.int_type': 'testType' - }; - return config[key]; - }); + config.setConfig({rubicon: {int_type: 'testType'}}); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(parseQuery(request.data).tk_flint).to.equal('testType_v$prebid.version$'); }); From 242efdcd81abce2083ea279d46cc84cccce02d94 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Tue, 29 Sep 2020 16:29:18 -0400 Subject: [PATCH 0240/1476] Prebid 4.10.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fc5a0441ad1..20e1cd6be19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.10.0-pre", + "version": "4.10.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 08e2bf2bfae40ea8583794e3ced6caf07f57aec5 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Tue, 29 Sep 2020 17:01:39 -0400 Subject: [PATCH 0241/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20e1cd6be19..35b0f6925e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.10.0", + "version": "4.11.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 61e1485e269560779e4bd89cb34cfd9b8b9fcba2 Mon Sep 17 00:00:00 2001 From: Zak Andree Date: Tue, 29 Sep 2020 18:33:53 -0700 Subject: [PATCH 0242/1476] Add Inmar bidder adapter (#5674) * Add Inmar bidder adapter * Update Inmar adapter * Small fix * Update Inmar params * Remove domain and bidFloor, add meta * Remove unused data * Fix unit tests --- modules/inmarBidAdapter.js | 113 ++++++++++ modules/inmarBidAdapter.md | 46 ++++ test/spec/modules/inmarBidAdapter_spec.js | 251 ++++++++++++++++++++++ 3 files changed, 410 insertions(+) create mode 100755 modules/inmarBidAdapter.js create mode 100644 modules/inmarBidAdapter.md create mode 100644 test/spec/modules/inmarBidAdapter_spec.js diff --git a/modules/inmarBidAdapter.js b/modules/inmarBidAdapter.js new file mode 100755 index 00000000000..b5ab72266fc --- /dev/null +++ b/modules/inmarBidAdapter.js @@ -0,0 +1,113 @@ +import * as utils 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 = 'inmar'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['inm'], + 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 && bid.params.partnerId && bid.params.adnetId); + }, + + /** + * Build a server request from the list of valid BidRequests + * @param {validBidRequests} is an array of the valid bids + * @param {bidderRequest} bidder request object + * @returns {ServerRequest} Info describing the request to the server + */ + buildRequests: function(validBidRequests, bidderRequest) { + var payload = { + bidderCode: bidderRequest.bidderCode, + auctionId: bidderRequest.auctionId, + bidderRequestId: bidderRequest.bidderRequestId, + bidRequests: validBidRequests, + auctionStart: bidderRequest.auctionStart, + timeout: bidderRequest.timeout, + refererInfo: bidderRequest.refererInfo, + start: bidderRequest.start, + gdprConsent: bidderRequest.gdprConsent, + uspConsent: bidderRequest.uspConsent, + currencyCode: config.getConfig('currency.adServerCurrency'), + coppa: config.getConfig('coppa'), + firstPartyData: config.getConfig('fpd'), + prebidVersion: '$prebid.version$' + }; + + var payloadString = JSON.stringify(payload); + + return { + method: 'POST', + url: 'https://prebid.owneriq.net:8443/bidder/pb/bid', + options: { + withCredentials: false + }, + data: payloadString, + }; + }, + + /** + * Read the response from the server and build a list of bids + * @param {serverResponse} Response from the server. + * @param {bidRequest} Bid request object + * @returns {bidResponses} Array of bids which were nested inside the server + */ + interpretResponse: function(serverResponse, bidRequest) { + const bidResponses = []; + var response = serverResponse.body; + + try { + if (response) { + var bidResponse = { + requestId: response.requestId, + cpm: response.cpm, + currency: response.currency, + width: response.width, + height: response.height, + ad: response.ad, + ttl: response.ttl, + creativeId: response.creativeId, + netRevenue: response.netRevenue, + vastUrl: response.vastUrl, + dealId: response.dealId, + meta: response.meta + }; + + bidResponses.push(bidResponse); + } + } catch (error) { + utils.logError('Error while parsing inmar response', error); + } + return bidResponses; + }, + + /** + * User Syncs + * + * @param {syncOptions} Publisher prebid configuration + * @param {serverResponses} Response from the server + * @returns {Array} + */ + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: 'https://px.owneriq.net/eucm/p/pb' + }); + } + return syncs; + } +}; + +registerBidder(spec); diff --git a/modules/inmarBidAdapter.md b/modules/inmarBidAdapter.md new file mode 100644 index 00000000000..1bacb30f2dd --- /dev/null +++ b/modules/inmarBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +``` +Module Name: Inmar Bidder Adapter +Module Type: Bidder Adapter +Maintainer: oiq_rtb@inmar.com +``` + +# Description + +Connects to Inmar for bids. This adapter supports Display and Video. + +The Inmar adapter requires setup and approval from the Inmar team. +Please reach out to your account manager for more information. + +# Test Parameters + +## Web +``` + var adUnits = [ + { + code: 'test-div1', + sizes: [[300, 250],[300, 600]], + bids: [{ + bidder: 'inmar', + params: { + partnerId: 12345, + adnetId: 'ADb1f40rmi', + position: 1 + } + }] + }, + { + code: 'test-div2', + sizes: [[728, 90],[970, 250]], + bids: [{ + bidder: 'inmar', + params: { + partnerId: 12345, + adnetId: 'ADb1f40rmo', + position: 0 + } + }] + } + ]; +``` diff --git a/test/spec/modules/inmarBidAdapter_spec.js b/test/spec/modules/inmarBidAdapter_spec.js new file mode 100644 index 00000000000..86b7ab3a8af --- /dev/null +++ b/test/spec/modules/inmarBidAdapter_spec.js @@ -0,0 +1,251 @@ +// import or require modules necessary for the test, e.g.: +import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' +import { + spec +} from 'modules/inmarBidAdapter.js'; +import {config} from 'src/config.js'; + +describe('Inmar adapter tests', function () { + var DEFAULT_PARAMS_NEW_SIZES = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + mediaTypes: { + banner: { + sizes: [ + [300, 250], [300, 600], [728, 90], [970, 250]] + } + }, + bidder: 'inmar', + params: { + adnetId: 'ADb1f40rmi', + partnerId: 12345 + }, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6', + user: {} + }]; + + var DEFAULT_PARAMS_VIDEO = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + mediaTypes: { + video: { + context: 'instream', // or 'outstream' + playerSize: [640, 480], + mimes: ['video/mp4'] + } + }, + bidder: 'inmar', + params: { + adnetId: 'ADb1f40rmi', + partnerId: 12345 + }, + auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6', + user: {} + }]; + + var DEFAULT_PARAMS_WO_OPTIONAL = [{ + adUnitCode: 'test-div', + bidId: '2c7c8e9c900244', + sizes: [ + [300, 250], + [300, 600], + [728, 90], + [970, 250] + ], + bidder: 'inmar', + params: { + adnetId: 'ADb1f40rmi', + partnerId: 12345, + }, + auctionId: '851adee7-d843-48f9-a7e9-9ff00573fcbf', + bidRequestsCount: 1, + bidderRequestId: '1858b7382993ca', + transactionId: '29df2112-348b-4961-8863-1b33684d95e6' + }]; + + var BID_RESPONSE = { + body: { + cpm: 1.50, + ad: '', + meta: { + mediaType: 'banner', + }, + width: 300, + height: 250, + creativeId: '189198063', + netRevenue: true, + currency: 'USD', + ttl: 300, + dealId: 'dealId' + + } + }; + + var BID_RESPONSE_VIDEO = { + body: { + cpm: 1.50, + meta: { + mediaType: 'video', + }, + width: 1, + height: 1, + creativeId: '189198063', + netRevenue: true, + currency: 'USD', + ttl: 300, + vastUrl: 'https://vast.com/vast.xml', + dealId: 'dealId' + } + }; + + it('Verify build request to prebid 3.0 display test', function() { + const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'https://domain.com', + numIframes: 0 + } + }); + + expect(request).to.have.property('method').and.to.equal('POST'); + const requestContent = JSON.parse(request.data); + expect(requestContent.bidRequests[0].params).to.have.property('adnetId').and.to.equal('ADb1f40rmi'); + expect(requestContent.bidRequests[0].params).to.have.property('partnerId').and.to.equal(12345); + expect(requestContent.bidRequests[0]).to.have.property('auctionId').and.to.equal('0cb3144c-d084-4686-b0d6-f5dbe917c563'); + expect(requestContent.bidRequests[0]).to.have.property('bidId').and.to.equal('2c7c8e9c900244'); + expect(requestContent.bidRequests[0]).to.have.property('bidRequestsCount').and.to.equal(1); + expect(requestContent.bidRequests[0]).to.have.property('bidder').and.to.equal('inmar'); + expect(requestContent.bidRequests[0]).to.have.property('bidderRequestId').and.to.equal('1858b7382993ca'); + expect(requestContent.bidRequests[0]).to.have.property('adUnitCode').and.to.equal('test-div'); + expect(requestContent.refererInfo).to.have.property('referer').and.to.equal('https://domain.com'); + expect(requestContent.bidRequests[0].mediaTypes.banner).to.have.property('sizes'); + expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[0]).to.have.ordered.members([300, 250]); + expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[1]).to.have.ordered.members([300, 600]); + expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[2]).to.have.ordered.members([728, 90]); + expect(requestContent.bidRequests[0].mediaTypes.banner.sizes[3]).to.have.ordered.members([970, 250]); + expect(requestContent.bidRequests[0]).to.have.property('transactionId').and.to.equal('29df2112-348b-4961-8863-1b33684d95e6'); + expect(requestContent.refererInfo).to.have.property('numIframes').and.to.equal(0); + }) + + it('Verify interprete response', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'https://domain.com', + numIframes: 0 + } + }); + + const bids = spec.interpretResponse(BID_RESPONSE, request); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); + expect(bid.ad).to.equal(''); + expect(bid.meta.mediaType).to.equal('banner'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); + }); + + it('no banner media response', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'https://domain.com', + numIframes: 0 + } + }); + + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request); + const bid = bids[0]; + expect(bid.vastUrl).to.equal('https://vast.com/vast.xml'); + }); + + it('Verifies bidder_code', function () { + expect(spec.code).to.equal('inmar'); + }); + + it('Verifies bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('inm'); + }); + + it('Verifies if bid request is valid', function () { + expect(spec.isBidRequestValid(DEFAULT_PARAMS_NEW_SIZES[0])).to.equal(true); + expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ + params: {} + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + adnetId: 'ADb1f40rmi' + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + partnerId: 12345 + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + adnetId: 'ADb1f40rmi', + partnerId: 12345 + } + })).to.equal(true); + }); + + it('Verifies user syncs image', function () { + var syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: '', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [], { + consentString: null, + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + }); +}); From 083d76e46a2fe2bf36b1add0170bf03e88c4225c Mon Sep 17 00:00:00 2001 From: Ignat Khaylov Date: Wed, 30 Sep 2020 04:37:37 +0300 Subject: [PATCH 0243/1476] added detect referer (#5759) Co-authored-by: Ignat Khaylov --- modules/betweenBidAdapter.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index c435a5a993e..fb3fcdb8d89 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,5 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getAdUnitSizes, parseSizesInput } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; + const BIDDER_CODE = 'between'; export const spec = { @@ -24,6 +26,7 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { let requests = []; const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + const refInfo = getRefererInfo(); validBidRequests.forEach(i => { let params = { @@ -56,6 +59,8 @@ export const spec = { } } + if (refInfo && refInfo.referer) params.ref = refInfo.referer; + if (gdprConsent) { if (typeof gdprConsent.gdprApplies !== 'undefined') { params.gdprApplies = !!gdprConsent.gdprApplies; From 0c9bbf9ad9543ee27f4d817a11f44e08b1e44fa0 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Wed, 30 Sep 2020 18:03:22 +0700 Subject: [PATCH 0244/1476] Qwarry bid adapter (#5662) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 75 +++++++++++++ modules/qwarryBidAdapter.md | 28 +++++ test/spec/modules/qwarryBidAdapter_spec.js | 122 +++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 modules/qwarryBidAdapter.js create mode 100644 modules/qwarryBidAdapter.md create mode 100644 test/spec/modules/qwarryBidAdapter_spec.js diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js new file mode 100644 index 00000000000..36c1562324a --- /dev/null +++ b/modules/qwarryBidAdapter.js @@ -0,0 +1,75 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { deepClone } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'qwarry'; +export const ENDPOINT = 'https://ui-bidder.kantics.co/bid/adtag?prebid=true' + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: ['banner', 'video'], + + isBidRequestValid: function (bid) { + return !!(bid.params && bid.params.zoneToken); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + let bids = []; + validBidRequests.forEach(bidRequest => { + bids.push({ + bidId: bidRequest.bidId, + zoneToken: bidRequest.params.zoneToken + }) + }) + + return { + method: 'POST', + url: ENDPOINT, + data: { requestId: bidderRequest.bidderRequestId, bids }, + options: { + contentType: 'application/json', + customHeaders: { + 'Rtb-Direct': true + } + } + }; + }, + + interpretResponse: function (serverResponse, request) { + const serverBody = serverResponse.body; + if (!serverBody || typeof serverBody !== 'object') { + return []; + } + + const { prebidResponse } = serverBody; + if (!prebidResponse || typeof prebidResponse !== 'object') { + return []; + } + + let bids = []; + prebidResponse.forEach(bidResponse => { + let bid = deepClone(bidResponse); + bid.cpm = parseFloat(bidResponse.cpm); + + // banner or video + if (VIDEO === bid.format) { + bid.vastXml = bid.ad; + } + + bids.push(bid); + }) + + return bids; + }, + + onBidWon: function (bid) { + if (bid.winUrl) { + ajax(bid.winUrl, null); + return true; + } + return false; + } +} + +registerBidder(spec); diff --git a/modules/qwarryBidAdapter.md b/modules/qwarryBidAdapter.md new file mode 100644 index 00000000000..056ccb51293 --- /dev/null +++ b/modules/qwarryBidAdapter.md @@ -0,0 +1,28 @@ +# Overview + +``` +Module Name: Qwarry Bidder Adapter +Module Type: Bidder Adapter +Maintainer: akascheev@asteriosoft.com +``` + +# Description + +Connects to Qwarry Bidder for bids. +Qwarry bid adapter supports Banner and Video ads. + +# Test Parameters +``` +const adUnits = [ + { + bids: [ + { + bidder: 'qwarry', + params: { + zoneToken: '?????????????????????', // zoneToken provided by Qwarry + } + } + ] + } +]; +``` diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js new file mode 100644 index 00000000000..a5bb438f384 --- /dev/null +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -0,0 +1,122 @@ +import { expect } from 'chai' +import { ENDPOINT, spec } from 'modules/qwarryBidAdapter.js' +import { newBidder } from 'src/adapters/bidderFactory.js' + +const REQUEST = { + 'bidId': '456', + 'bidder': 'qwarry', + 'params': { + zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f' + } +} + +const BIDDER_BANNER_RESPONSE = {'prebidResponse': [{ + 'ad': '
test
', + 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46d', + 'cpm': 900.5, + 'currency': 'USD', + 'width': 640, + 'height': 480, + 'ttl': 300, + 'creativeId': 1, + 'netRevenue': true, + 'winUrl': 'http://test.com', + 'format': 'banner' +}]} + +const BIDDER_VIDEO_RESPONSE = {'prebidResponse': [{ + 'ad': 'vast', + 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46z', + 'cpm': 800.4, + 'currency': 'USD', + 'width': 1024, + 'height': 768, + 'ttl': 200, + 'creativeId': 2, + 'netRevenue': true, + 'winUrl': 'http://test.com', + 'format': 'video' +}]} + +const BIDDER_NO_BID_RESPONSE = '' + +describe('qwarryBidAdapter', 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 () { + expect(spec.isBidRequestValid(REQUEST)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, REQUEST) + delete bid.params.zoneToken + expect(spec.isBidRequestValid(bid)).to.equal(false) + delete bid.params + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + let bidRequests = [REQUEST] + const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123' }) + + it('sends bid request to ENDPOINT via POST', function () { + expect(bidderRequest.method).to.equal('POST') + expect(bidderRequest.data.requestId).to.equal('123') + expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f' }) + expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) + expect(bidderRequest.options.contentType).to.equal('application/json') + expect(bidderRequest.url).to.equal(ENDPOINT) + }) + }) + + describe('interpretResponse', function () { + it('handles banner request : should get correct bid response', function () { + const result = spec.interpretResponse({ body: BIDDER_BANNER_RESPONSE }, {}) + + expect(result[0]).to.have.property('ad').equal('
test
') + expect(result[0]).to.have.property('requestId').equal('e64782a4-8e68-4c38-965b-80ccf115d46d') + expect(result[0]).to.have.property('cpm').equal(900.5) + expect(result[0]).to.have.property('currency').equal('USD') + expect(result[0]).to.have.property('width').equal(640) + expect(result[0]).to.have.property('height').equal(480) + expect(result[0]).to.have.property('ttl').equal(300) + expect(result[0]).to.have.property('creativeId').equal(1) + expect(result[0]).to.have.property('netRevenue').equal(true) + expect(result[0]).to.have.property('winUrl').equal('http://test.com') + expect(result[0]).to.have.property('format').equal('banner') + }) + + it('handles video request : should get correct bid response', function () { + const result = spec.interpretResponse({ body: BIDDER_VIDEO_RESPONSE }, {}) + + expect(result[0]).to.have.property('ad').equal('vast') + expect(result[0]).to.have.property('requestId').equal('e64782a4-8e68-4c38-965b-80ccf115d46z') + expect(result[0]).to.have.property('cpm').equal(800.4) + expect(result[0]).to.have.property('currency').equal('USD') + expect(result[0]).to.have.property('width').equal(1024) + expect(result[0]).to.have.property('height').equal(768) + expect(result[0]).to.have.property('ttl').equal(200) + expect(result[0]).to.have.property('creativeId').equal(2) + expect(result[0]).to.have.property('netRevenue').equal(true) + expect(result[0]).to.have.property('winUrl').equal('http://test.com') + expect(result[0]).to.have.property('format').equal('video') + expect(result[0]).to.have.property('vastXml').equal('vast') + }) + + it('handles no bid response : should get empty array', function () { + let result = spec.interpretResponse({ body: undefined }, {}) + expect(result).to.deep.equal([]) + + result = spec.interpretResponse({ body: BIDDER_NO_BID_RESPONSE }, {}) + expect(result).to.deep.equal([]) + }) + }) +}) From 9573a428dee71f7aa1ba6d7c9a3605b3b9350d32 Mon Sep 17 00:00:00 2001 From: mimenet <64042452+mimenet@users.noreply.github.com> Date: Wed, 30 Sep 2020 08:59:25 -0700 Subject: [PATCH 0245/1476] Allow selection of supported default targeting keys at configuration time. (#5763) * initial check-in: add ability to selectively allow default keys into GAM KV targeting. * add more descriptive test documentation to explain that the default targeting keys is checking against the key prefix to accomodate bid landscape. collate and remove targeting surrounding the key removal process. --- src/targeting.js | 47 +++++++++++++++++++++++++++ test/spec/unit/core/targeting_spec.js | 40 +++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/targeting.js b/src/targeting.js index 1b1e14fd4a6..8176bc9caff 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -181,6 +181,48 @@ export function newTargeting(auctionManager) { return []; }; + /** + * Returns filtered ad server targeting for custom and allowed keys. + * @param {targetingArray} targeting + * @param {string[]} allowedKeys + * @return {targetingArray} filtered targeting + */ + function getAllowedTargetingKeyValues(targeting, allowedKeys) { + const defaultKeyring = Object.assign({}, CONSTANTS.TARGETING_KEYS, CONSTANTS.NATIVE_KEYS); + const defaultKeys = Object.keys(defaultKeyring); + const keyDispositions = {}; + logInfo(`allowTargetingKeys - allowed keys [ ${allowedKeys.map(k => defaultKeyring[k]).join(', ')} ]`); + targeting.map(adUnit => { + const adUnitCode = Object.keys(adUnit)[0]; + const keyring = adUnit[adUnitCode]; + const keys = keyring.filter(kvPair => { + const key = Object.keys(kvPair)[0]; + // check if key is in default keys, if not, it's custom, we won't remove it. + const isCustom = defaultKeys.filter(defaultKey => key.indexOf(defaultKeyring[defaultKey]) === 0).length === 0; + // check if key explicitly allowed, if not, we'll remove it. + const found = isCustom || allowedKeys.find(allowedKey => { + const allowedKeyName = defaultKeyring[allowedKey]; + // we're looking to see if the key exactly starts with one of our default keys. + // (which hopefully means it's not custom) + const found = key.indexOf(allowedKeyName) === 0; + return found; + }); + keyDispositions[key] = !found; + return found; + }); + adUnit[adUnitCode] = keys; + }); + const removedKeys = Object.keys(keyDispositions).filter(d => keyDispositions[d]); + logInfo(`allowTargetingKeys - removed keys [ ${removedKeys.join(', ')} ]`); + // remove any empty targeting objects, as they're unnecessary. + const filteredTargeting = targeting.filter(adUnit => { + const adUnitCode = Object.keys(adUnit)[0]; + const keyring = adUnit[adUnitCode]; + return keyring.length > 0; + }); + return filteredTargeting + } + /** * Returns all ad server targeting for all ad units. * @param {string=} adUnitCode @@ -206,6 +248,11 @@ export function newTargeting(auctionManager) { }); }); + const allowedKeys = config.getConfig('targetingControls.allowTargetingKeys'); + if (Array.isArray(allowedKeys) && allowedKeys.length > 0) { + targeting = getAllowedTargetingKeyValues(targeting, allowedKeys); + } + targeting = flattenTargeting(targeting); const auctionKeysThreshold = config.getConfig('targetingControls.auctionKeyMaxChars'); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 3aae6e3c33a..5d43ed48266 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -416,6 +416,46 @@ describe('targeting tests', function () { }); }); + describe('targetingControls.allowTargetingKeys', function () { + let bid4; + + beforeEach(function() { + bid4 = utils.deepClone(bid1); + bid4.adserverTargeting = { + hb_deal: '4321', + hb_pb: '0.1', + hb_adid: '567891011', + hb_bidder: 'appnexus', + }; + bid4.bidder = bid4.bidderCode = 'appnexus'; + bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false + bid4.dealId = '4321'; + enableSendAllBids = true; + config.setConfig({ + targetingControls: { + allowTargetingKeys: ['BIDDER', 'AD_ID', 'PRICE_BUCKET'] + } + }); + bidsReceived.push(bid4); + }); + + it('targeting should include custom keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('foobar'); + }); + + it('targeting should include keys prefixed by allowed default targeting keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder_rubicon', 'hb_adid_rubicon', 'hb_pb_rubicon'); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder_appnexus', 'hb_adid_appnexus', 'hb_pb_appnexus'); + }); + + it('targeting should not include keys prefixed by disallowed default targeting keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.not.have.all.keys(['hb_deal_appnexus', 'hb_deal_rubicon']); + }); + }); + describe('targetingControls.alwaysIncludeDeals', function () { let bid4; From 1df6a22650037fdfb4606e8296dc86e56441b5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Thu, 1 Oct 2020 17:59:57 +0300 Subject: [PATCH 0246/1476] cointrafficBidAdapter: added support responding in different currencies (#5800) * New adapter "Cointraffic" added * removed mobile detection * The sizes property has been updated, added supportedMediaTypes. * feat: added support responding in different currencies * change: module description --- modules/cointrafficBidAdapter.js | 20 ++- modules/cointrafficBidAdapter.md | 5 +- .../modules/cointrafficBidAdapter_spec.js | 138 ++++++++++++++++-- 3 files changed, 149 insertions(+), 14 deletions(-) diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index aa6860d1fc6..43f5cc01ccf 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -1,9 +1,15 @@ import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js' +import { config } from '../src/config.js' const BIDDER_CODE = 'cointraffic'; const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; +const DEFAULT_CURRENCY = 'EUR'; +const ALLOWED_CURRENCIES = [ + 'EUR', 'USD', 'JPY', 'BGN', 'CZK', 'DKK', 'GBP', 'HUF', 'PLN', 'RON', 'SEK', 'CHF', 'ISK', 'NOK', 'HRK', 'RUB', 'TRY', + 'AUD', 'BRL', 'CAD', 'CNY', 'HKD', 'IDR', 'ILS', 'INR', 'KRW', 'MXN', 'MYR', 'NZD', 'PHP', 'SGD', 'THB', 'ZAR', +]; export const spec = { code: BIDDER_CODE, @@ -29,9 +35,19 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { const sizes = utils.parseSizesInput(bidRequest.params.size || bidRequest.sizes); + const currency = + config.getConfig(`currency.bidderCurrencyDefault.${BIDDER_CODE}`) || + config.getConfig('currency.adServerCurrency') || + DEFAULT_CURRENCY; + + if (ALLOWED_CURRENCIES.indexOf(currency) === -1) { + utils.logError('Currency is not supported - ' + currency); + return; + } const payload = { placementId: bidRequest.params.placementId, + currency: currency, sizes: sizes, bidId: bidRequest.bidId, referer: bidderRequest.refererInfo.referer, diff --git a/modules/cointrafficBidAdapter.md b/modules/cointrafficBidAdapter.md index ad608a1319e..fcbcad1cad7 100644 --- a/modules/cointrafficBidAdapter.md +++ b/modules/cointrafficBidAdapter.md @@ -7,7 +7,10 @@ Maintainer: tech@cointraffic.io ``` # Description -The Cointraffic client module makes it easy to implement Cointraffic directly into your website. To get started, simply replace the ``placementId`` with your assigned tracker key. This is dependent on the size required by your account dashboard. For additional information on this module, please contact us at ``support@cointraffic.io``. +The Cointraffic client module makes it easy to implement Cointraffic directly into your website. To get started, simply replace the ``placementId`` with your assigned tracker key. This is dependent on the size required by your account dashboard. +We support response in different currencies. Supported currencies listed [here](https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html). + +For additional information on this module, please contact us at ``support@cointraffic.io``. # Test Parameters ``` diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index 6d948e36cb9..a2ce4cedea0 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -1,5 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/cointrafficBidAdapter.js'; +import { config } from 'src/config.js' +import * as utils from 'src/utils.js' const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; @@ -37,7 +39,7 @@ describe('cointrafficBidAdapter', function () { ], bidId: 'bidId12345', bidderRequestId: 'bidderRequestId12345', - auctionId: 'auctionId12345', + auctionId: 'auctionId12345' }, { bidder: 'cointraffic', @@ -50,7 +52,7 @@ describe('cointrafficBidAdapter', function () { ], bidId: 'bidId67890"', bidderRequestId: 'bidderRequestId67890', - auctionId: 'auctionId12345', + auctionId: 'auctionId12345' } ]; @@ -65,34 +67,71 @@ describe('cointrafficBidAdapter', function () { } }; - const request = spec.buildRequests(bidRequests, bidderRequests); + it('replaces currency with EUR if there is no currency provided', function () { + const request = spec.buildRequests(bidRequests, bidderRequests); + + expect(request[0].data.currency).to.equal('EUR'); + expect(request[1].data.currency).to.equal('EUR'); + }); + + it('replaces currency with EUR if there is no currency provided', function () { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'currency.bidderCurrencyDefault.cointraffic' ? 'USD' : 'EUR' + ); + + const request = spec.buildRequests(bidRequests, bidderRequests); + + expect(request[0].data.currency).to.equal('USD'); + expect(request[1].data.currency).to.equal('USD'); + + getConfigStub.restore(); + }); + + it('throws an error if currency provided in params is not allowed', function () { + const utilsMock = sinon.mock(utils).expects('logError').twice() + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'currency.bidderCurrencyDefault.cointraffic' ? 'BTC' : 'EUR' + ); + + const request = spec.buildRequests(bidRequests, bidderRequests); + + expect(request[0]).to.undefined; + expect(request[1]).to.undefined; + + utilsMock.restore() + getConfigStub.restore(); + }); it('sends bid request to our endpoint via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequests); + expect(request[0].method).to.equal('POST'); expect(request[1].method).to.equal('POST'); }); + it('attaches source and version to endpoint URL as query params', function () { + const request = spec.buildRequests(bidRequests, bidderRequests); + expect(request[0].url).to.equal(ENDPOINT_URL); expect(request[1].url).to.equal(ENDPOINT_URL); }); }); describe('interpretResponse', function () { - let bidRequest = [ - { + it('should get the correct bid response', function () { + let bidRequest = [{ method: 'POST', url: ENDPOINT_URL, data: { placementId: 'testPlacementId', device: 'desktop', + currency: 'EUR', sizes: ['300x250'], bidId: 'bidId12345', referer: 'www.example.com' } - } - ]; + }]; - it('should get the correct bid response', function () { let serverResponse = { body: { requestId: 'bidId12345', @@ -103,7 +142,7 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

', + ad: '

I am an ad

' } }; @@ -118,22 +157,99 @@ describe('cointrafficBidAdapter', function () { ttl: 90, ad: '

I am an ad

' }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); }); - it('should get empty bid response if server response body is empty', function () { + it('should get the correct bid response with different currency', function () { + let bidRequest = [{ + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'testPlacementId', + device: 'desktop', + currency: 'USD', + sizes: ['300x250'], + bidId: 'bidId12345', + referer: 'www.example.com' + } + }]; + let serverResponse = { - body: {} + body: { + requestId: 'bidId12345', + cpm: 3.9, + currency: 'USD', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

' + } }; + let expectedResponse = [{ + requestId: 'bidId12345', + cpm: 3.9, + currency: 'USD', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

' + }]; + + const getConfigStub = sinon.stub(config, 'getConfig').returns('USD'); + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + + getConfigStub.restore(); + }); + + it('should get empty bid response requested currency is not available', function () { + let bidRequest = [{ + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'testPlacementId', + device: 'desktop', + currency: 'BTC', + sizes: ['300x250'], + bidId: 'bidId12345', + referer: 'www.example.com' + } + }]; + + let serverResponse = {}; + let expectedResponse = []; + const getConfigStub = sinon.stub(config, 'getConfig').returns('BTC'); + let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + + getConfigStub.restore(); }); it('should get empty bid response if no server response', function () { + let bidRequest = [{ + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'testPlacementId', + device: 'desktop', + currency: 'EUR', + sizes: ['300x250'], + bidId: 'bidId12345', + referer: 'www.example.com' + } + }]; + let serverResponse = {}; let expectedResponse = []; From 303ccaee3e190b3bea50a50b8eb75460e9922818 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Thu, 1 Oct 2020 21:09:47 +0530 Subject: [PATCH 0247/1476] Send proper slot info in case of adUnitPath (#5810) - using `getGptSlotInfoForAdUnitCode` to get `divId` in case of `adUnitPath` - added test case for visibility via `adUnitPath` Co-authored-by: monis.q --- modules/medianetBidAdapter.js | 11 +++++++-- test/spec/modules/medianetBidAdapter_spec.js | 26 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 5decaa148e3..49e2bdc225f 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -113,8 +113,15 @@ function getWindowSize() { } } -function getCoordinates(id) { - const element = document.getElementById(id); +function getCoordinates(adUnitCode) { + let element = document.getElementById(adUnitCode); + if (!element && adUnitCode.indexOf('/') !== -1) { + // now it means that adUnitCode is GAM AdUnitPath + const {divId} = utils.getGptSlotInfoForAdUnitCode(adUnitCode); + if (utils.isStr(divId)) { + element = document.getElementById(divId); + } + } if (element && element.getBoundingClientRect) { const rect = element.getBoundingClientRect(); let coordinates = {}; diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 649929056fa..1b2207de842 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -1,7 +1,9 @@ import {expect} from 'chai'; import {spec} from 'modules/medianetBidAdapter.js'; +import { makeSlot } from '../integration/faker/googletag.js'; import { config } from 'src/config.js'; +$$PREBID_GLOBAL$$.version = $$PREBID_GLOBAL$$.version || 'version'; let VALID_BID_REQUEST = [{ 'bidder': 'medianet', 'params': { @@ -1306,6 +1308,30 @@ describe('Media.net bid adapter', function () { expect(data.imp[1].ext).to.not.have.ownPropertyDescriptor('viewability'); expect(data.imp[1].ext.visibility).to.equal(0); }); + it('slot visibility should be calculable even in case of adUnitPath', function () { + const code = '/19968336/header-bid-tag-0'; + const divId = 'div-gpt-ad-1460505748561-0'; + window.googletag.pubads().setSlots([makeSlot({ code, divId })]); + + let boundingRect = { + top: 1010, + left: 1010, + bottom: 1050, + right: 1050 + }; + documentStub.withArgs(divId).returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + + const bidRequest = [{...VALID_BID_REQUEST[0], adUnitCode: code}] + const bidReq = spec.buildRequests(bidRequest, VALID_AUCTIONDATA); + const data = JSON.parse(bidReq.data); + expect(data.imp[0].ext.visibility).to.equal(2); + expect(data.imp[0].ext.viewability).to.equal(0); + }); }); describe('getUserSyncs', function () { From c1c6aaad95c51761aa00c21c0b6d733c31c76bb9 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Thu, 1 Oct 2020 12:02:47 -0400 Subject: [PATCH 0248/1476] Update to rubiconBidAdapter to include criteoId support (#5806) --- modules/rubiconBidAdapter.js | 6 +++++- test/spec/modules/rubiconBidAdapter_spec.js | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 829fd208887..17e64a7b76c 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -253,7 +253,7 @@ export const spec = { const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); if (eids && eids.length) { // filter out unsupported id systems - utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); + utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org', 'criteo.com'].indexOf(eid.source) !== -1)); // liveintent requires additional props to be set const liveIntentEid = find(data.user.ext.eids, eid => eid.source === 'liveintent.com'); @@ -538,6 +538,10 @@ export const spec = { if (sharedId) { data['eid_sharedid.org'] = `${sharedId.uids[0].id}^${sharedId.uids[0].atype}^${sharedId.uids[0].ext.third}`; } + const criteoId = find(bidRequest.userIdAsEids, eid => eid.source === 'criteo.com'); + if (criteoId) { + data['eid_criteo.com'] = `${criteoId.uids[0].id}^${criteoId.uids[0].atype}`; + } } // set ppuid value from config value diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index dd25fafb149..159632eee8a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -245,7 +245,8 @@ describe('the rubicon adapter', function () { uids: [{ id: '4444444' }] - }] + }], + criteoId: '1111' }; bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; @@ -1371,6 +1372,20 @@ describe('the rubicon adapter', function () { }); }); + describe('Criteo support', function () { + it('should send eid_criteo.com when userIdAsEids contains criteo', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + criteoId: '1111' + }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_criteo.com']).to.equal('1111^1'); + }); + }); + describe('SharedID support', function () { it('should send sharedid when userIdAsEids contains sharedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); @@ -1654,6 +1669,10 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[4].source).to.equal('pubcid.org'); expect(post.user.ext.eids[4].uids[0].atype).to.equal(1); expect(post.user.ext.eids[4].uids[0].id).to.equal('4000'); + // CriteoId should exist + expect(post.user.ext.eids[5].source).to.equal('criteo.com'); + expect(post.user.ext.eids[5].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[5].uids[0].atype).to.equal(1); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); From 804ff2fadff746d98e3484051852c65fb5769b7f Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 1 Oct 2020 15:07:40 -0400 Subject: [PATCH 0249/1476] appnexus bid adapter: criteo back to tpuids (#5808) --- modules/appnexusBidAdapter.js | 10 ++++++---- test/spec/modules/appnexusBidAdapter_spec.js | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 12bc6a8105c..f3ebf9bf4f6 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -220,15 +220,17 @@ export const spec = { }); } - let eids = []; const criteoId = utils.deepAccess(bidRequests[0], `userId.criteoId`); if (criteoId) { - eids.push({ - source: 'criteo.com', - id: criteoId + let tpuids = []; + tpuids.push({ + 'provider': 'criteo', + 'user_id': criteoId }); + payload.tpuids = tpuids; } + let eids = []; const tdid = utils.deepAccess(bidRequests[0], `userId.tdid`); if (tdid) { eids.push({ diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 7e985045c45..7b563c4ec1d 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -808,7 +808,7 @@ describe('AppNexusAdapter', function () { expect(request.options).to.deep.equal({withCredentials: false}); }); - it('should populate eids array when ttd id and criteo is available', function () { + it('should populate eids and tpuids when ttd id and criteo is available', function () { const bidRequest = Object.assign({}, bidRequests[0], { userId: { tdid: 'sample-userid', @@ -824,9 +824,9 @@ describe('AppNexusAdapter', function () { rti_partner: 'TDID' }); - expect(payload.eids).to.deep.include({ - source: 'criteo.com', - id: 'sample-criteo-userid', + expect(payload.tpuids).to.deep.include({ + provider: 'criteo', + user_id: 'sample-criteo-userid', }); }); }) From 907a9c7bac5f3f400d68ee7e980356d879f5d0d3 Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Fri, 2 Oct 2020 00:39:35 +0300 Subject: [PATCH 0250/1476] Intentiq id add validation (#5797) * Add validity check to ignore not-available response * Added tests * Added error log --- modules/intentIqIdSystem.js | 36 ++++++++++++++++++++-- test/spec/modules/intentIqIdSystem_spec.js | 8 +++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 88da7d44481..bb74b1210cb 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -11,6 +11,32 @@ import {submodule} from '../src/hook.js' const MODULE_NAME = 'intentIqId'; +const NOT_AVAILABLE = 'NA'; + +/** + * Verify the id is valid - Id value or Not Found (ignore not available response) + * @param id + * @returns {boolean|*|boolean} + */ +function isValidId(id) { + return id && id != '' && id != NOT_AVAILABLE && isValidResponse(id); +} + +/** + * Ignore not available response JSON + * @param obj + * @returns {boolean} + */ +function isValidResponse(obj) { + try { + obj = JSON.parse(obj); + return obj && obj['RESULT'] != NOT_AVAILABLE; + } catch (error) { + utils.logError(error); + return true; + } +} + /** @type {Submodule} */ export const intentIqIdSubmodule = { /** @@ -22,10 +48,10 @@ export const intentIqIdSubmodule = { * decode the stored id value for passing to bid requests * @function * @param {{string}} value - * @returns {{intentIqId:string}} + * @returns {{intentIqId: {string}}|undefined} */ decode(value) { - return (value && value != '') ? { 'intentIqId': value } : undefined; + return isValidId(value) ? { 'intentIqId': value } : undefined; }, /** * performs action to obtain id and return a value in the callback's response argument @@ -47,7 +73,11 @@ export const intentIqIdSubmodule = { const resp = function (callback) { const callbacks = { success: response => { - callback(response); + if (isValidId(response)) { + callback(response); + } else { + callback(); + } }, error: error => { utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 2dccde18855..951cfab5f50 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -55,6 +55,14 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); + it('should ignore NA and invalid responses', function () { + let resp = JSON.stringify({'RESULT': 'NA'}); + expect(intentIqIdSubmodule.decode(resp)).to.equal(undefined); + expect(intentIqIdSubmodule.decode('NA')).to.equal(undefined); + expect(intentIqIdSubmodule.decode('')).to.equal(undefined); + expect(intentIqIdSubmodule.decode(undefined)).to.equal(undefined); + }); + it('should call the IntentIQ endpoint with only partner, pai', function () { let callBackSpy = sinon.spy(); let submoduleCallback = intentIqIdSubmodule.getId(paiConfigParams).callback; From b5cf481ec8ba56c43915b78b1a67c6717bf4c676 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Sun, 4 Oct 2020 21:01:25 -0700 Subject: [PATCH 0251/1476] remove digitrust from rubicon bid adapter (#5798) --- modules/rubiconBidAdapter.js | 55 ----- test/spec/modules/rubiconBidAdapter_spec.js | 227 -------------------- 2 files changed, 282 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 17e64a7b76c..36b3de8403d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -15,17 +15,6 @@ config.getConfig('rubicon', config => { }); const GVLID = 52; -const DIGITRUST_PROP_NAMES = { - FASTLANE: { - id: 'dt.id', - keyv: 'dt.keyv', - pref: 'dt.pref' - }, - PREBID_SERVER: { - id: 'id', - keyv: 'keyv' - } -}; var sizeMap = { 1: '468x60', @@ -230,11 +219,6 @@ export const spec = { addVideoParameters(data, bidRequest); - const digiTrust = _getDigiTrustQueryParams(bidRequest, 'PREBID_SERVER'); - if (digiTrust) { - utils.deepSetValue(data, 'user.ext.digitrust', digiTrust); - } - if (bidderRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module let gdprApplies; @@ -405,9 +389,6 @@ export const spec = { 'tpid_tdid', 'tpid_liveintent.com', 'tg_v.LIseg', - 'dt.id', - 'dt.keyv', - 'dt.pref', 'rf', 'p_geo.latitude', 'p_geo.longitude', @@ -617,10 +598,6 @@ export const spec = { data['tg_i.dfp_ad_unit_code'] = gamAdUnit.replace(/^\/+/, ''); } - // digitrust properties - const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); - Object.assign(data, digitrustParams); - if (config.getConfig('coppa') === true) { data['coppa'] = 1; } @@ -852,38 +829,6 @@ function _getScreenResolution() { return [window.screen.width, window.screen.height].join('x'); } -function _getDigiTrustQueryParams(bidRequest = {}, endpointName) { - if (!endpointName || !DIGITRUST_PROP_NAMES[endpointName]) { - return null; - } - const propNames = DIGITRUST_PROP_NAMES[endpointName]; - - function getDigiTrustId() { - const bidRequestDigitrust = utils.deepAccess(bidRequest, 'userId.digitrustid.data'); - if (bidRequestDigitrust) { - return bidRequestDigitrust; - } - - let digiTrustUser = (window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}))); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; - } - - let digiTrustId = getDigiTrustId(); - // Verify there is an ID and this user has not opted out - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return null; - } - - const digiTrustQueryParams = { - [propNames.id]: digiTrustId.id, - [propNames.keyv]: digiTrustId.keyv - }; - if (propNames.pref) { - digiTrustQueryParams[propNames.pref] = 0; - } - return digiTrustQueryParams; -} - /** * @param {BidRequest} bidRequest * @param bidderRequest diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 159632eee8a..aee792714cb 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -702,233 +702,6 @@ describe('the rubicon adapter', function () { expect(data['rp_floor']).to.equal('2'); }); - it('should send digitrust params', function () { - window.DigiTrust = { - getUser: function () { - } - }; - sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => - ({ - success: true, - identity: { - privacy: {optout: false}, - id: 'testId', - keyv: 'testKeyV' - } - }) - ); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let expectedQuery = { - 'dt.id': 'testId', - 'dt.keyv': 'testKeyV', - 'dt.pref': '0' - }; - - // test that all values above are both present and correct - Object.keys(expectedQuery).forEach(key => { - let value = expectedQuery[key]; - expect(data[key]).to.equal(value); - }); - - delete window.DigiTrust; - }); - - it('should not send digitrust params when DigiTrust not loaded', function () { - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - }); - - it('should not send digitrust params due to optout', function () { - window.DigiTrust = { - getUser: function () { - } - }; - sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => - ({ - success: true, - identity: { - privacy: {optout: true}, - id: 'testId', - keyv: 'testKeyV' - } - }) - ); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - - delete window.DigiTrust; - }); - - it('should not send digitrust params due to failure', function () { - window.DigiTrust = { - getUser: function () { - } - }; - sandbox.stub(window.DigiTrust, 'getUser').callsFake(() => - ({ - success: false, - identity: { - privacy: {optout: false}, - id: 'testId', - keyv: 'testKeyV' - } - }) - ); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - - delete window.DigiTrust; - }); - - describe('digiTrustId config', function () { - beforeEach(function () { - window.DigiTrust = { - getUser: sandbox.spy() - }; - }); - - afterEach(function () { - delete window.DigiTrust; - }); - - it('should send digiTrustId config params', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - var config = { - digiTrustId: { - success: true, - identity: { - privacy: {optout: false}, - id: 'testId', - keyv: 'testKeyV' - } - } - }; - return config[key]; - }); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let expectedQuery = { - 'dt.id': 'testId', - 'dt.keyv': 'testKeyV' - }; - - // test that all values above are both present and correct - Object.keys(expectedQuery).forEach(key => { - let value = expectedQuery[key]; - expect(data[key]).to.equal(value); - }); - - // should not have called DigiTrust.getUser() - expect(window.DigiTrust.getUser.notCalled).to.equal(true); - }); - - it('should not send digiTrustId config params due to optout', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - var config = { - digiTrustId: { - success: true, - identity: { - privacy: {optout: true}, - id: 'testId', - keyv: 'testKeyV' - } - } - } - return config[key]; - }); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - - // should not have called DigiTrust.getUser() - expect(window.DigiTrust.getUser.notCalled).to.equal(true); - }); - - it('should not send digiTrustId config params due to failure', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - var config = { - digiTrustId: { - success: false, - identity: { - privacy: {optout: false}, - id: 'testId', - keyv: 'testKeyV' - } - } - } - return config[key]; - }); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - - // should not have called DigiTrust.getUser() - expect(window.DigiTrust.getUser.notCalled).to.equal(true); - }); - - it('should not send digiTrustId config params if they do not exist', function () { - sandbox.stub(config, 'getConfig').callsFake((key) => { - var config = {}; - return config[key]; - }); - - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - let undefinedKeys = ['dt.id', 'dt.keyv']; - - // Test that none of the DigiTrust keys are part of the query - undefinedKeys.forEach(key => { - expect(typeof data[key]).to.equal('undefined'); - }); - - // should have called DigiTrust.getUser() once - expect(window.DigiTrust.getUser.calledOnce).to.equal(true); - }); - }); - describe('GDPR consent config', function () { it('should send "gdpr" and "gdpr_consent", when gdprConsent defines consentString and gdprApplies', function () { createGdprBidderRequest(true); From ca22a45991077d4dc5fa87582568b3eb94236982 Mon Sep 17 00:00:00 2001 From: Maxime Lequain Date: Mon, 5 Oct 2020 11:14:38 +0200 Subject: [PATCH 0252/1476] add native preset handling and automatic price macro replacement (#5807) Co-authored-by: Maxime Lequain --- modules/adotBidAdapter.js | 97 ++++++++++++++++++------ modules/adotBidAdapter.md | 64 ++++++++-------- test/spec/modules/adotBidAdapter_spec.js | 74 +++++++++++------- 3 files changed, 149 insertions(+), 86 deletions(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 911da416cfe..3d0af864a31 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -1,14 +1,26 @@ import {Renderer} from '../src/Renderer.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {isStr, isArray, isNumber, isPlainObject, isBoolean, logError} from '../src/utils.js'; +import {isStr, isArray, isNumber, isPlainObject, isBoolean, logError, replaceAuctionPrice} from '../src/utils.js'; import find from 'core-js-pure/features/array/find.js'; const ADAPTER_VERSION = 'v1.0.0'; const BID_METHOD = 'POST'; const BIDDER_URL = 'https://dsp.adotmob.com/headerbidding/bidrequest'; +const FIRST_PRICE = 1; +const NET_REVENUE = true; +// eslint-disable-next-line no-template-curly-in-string +const AUCTION_PRICE = '${AUCTION_PRICE}'; +const TTL = 10; + const SUPPORTED_VIDEO_CONTEXTS = ['instream', 'outstream']; const SUPPORTED_INSTREAM_CONTEXTS = ['pre-roll', 'mid-roll', 'post-roll']; +const SUPPORTED_VIDEO_MIMES = ['video/mp4']; +const BID_SUPPORTED_MEDIA_TYPES = ['banner', 'video', 'native']; + +const DOMAIN_REGEX = new RegExp('//([^/]*)'); +const OUTSTREAM_VIDEO_PLAYER_URL = 'https://adserver.adotmob.com/video/player.min.js'; + const NATIVE_PLACEMENTS = { title: {id: 1, name: 'title'}, icon: {id: 2, type: 1, name: 'img'}, @@ -18,15 +30,9 @@ const NATIVE_PLACEMENTS = { cta: {id: 6, type: 12, name: 'data'} }; const NATIVE_ID_MAPPING = {1: 'title', 2: 'icon', 3: 'image', 4: 'sponsoredBy', 5: 'body', 6: 'cta'}; -const SUPPORTED_VIDEO_MIMES = ['video/mp4']; -const DOMAIN_REGEX = new RegExp('//([^/]*)'); -const FIRST_PRICE = 1; -const BID_SUPPORTED_MEDIA_TYPES = ['banner', 'video', 'native']; -const TTL = 10; -const NET_REVENUE = true; -// eslint-disable-next-line no-template-curly-in-string -const AUCTION_PRICE = '${AUCTION_PRICE}'; -const OUTSTREAM_VIDEO_PLAYER_URL = 'https://adserver.adotmob.com/video/player.min.js'; +const NATIVE_PRESET_FORMATTERS = { + image: formatNativePresetImage +} function isNone(value) { return (value === null) || (value === undefined); @@ -183,6 +189,7 @@ function generateImpressionsFromAdUnit(acc, adUnit) { const {bidId, mediaTypes, params} = adUnit; const {placementId} = params; const pmp = {}; + const ext = {placementId}; if (placementId) pmp.deals = [{id: placementId}] @@ -193,8 +200,8 @@ function generateImpressionsFromAdUnit(acc, adUnit) { const impId = `${bidId}_${index}`; if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params)); - if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp}); - if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data, params), pmp}); + if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp, ext}); + if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data, params), pmp, ext}); }, []); return acc.concat(imps); @@ -208,10 +215,11 @@ function generateBannerFromAdUnit(impId, data, params) { const {position, placementId} = params; const pos = position || 0; const pmp = {}; + const ext = {placementId}; if (placementId) pmp.deals = [{id: placementId}] - return data.sizes.map(([w, h], index) => ({id: `${impId}_${index}`, banner: {format: [{w, h}], w, h, pos}, pmp})); + return data.sizes.map(([w, h], index) => ({id: `${impId}_${index}`, banner: {format: [{w, h}], w, h, pos}, pmp, ext})); } function generateVideoFromAdUnit(data, params) { @@ -254,19 +262,31 @@ function computeStartDelay(data, params) { } function generateNativeFromAdUnit(data, params) { - const placements = NATIVE_PLACEMENTS; + const {type} = data; + const presetFormatter = type && NATIVE_PRESET_FORMATTERS[data.type]; + const nativeFields = presetFormatter ? presetFormatter(data) : data; + const assets = Object - .keys(data) + .keys(nativeFields) .reduce((acc, placement) => { - const placementData = data[placement]; - const assetInfo = placements[placement]; + const placementData = nativeFields[placement]; + const assetInfo = NATIVE_PLACEMENTS[placement]; if (!assetInfo) return acc; const {id, name, type} = assetInfo; - const {required, len, sizes} = placementData; - const wmin = sizes && sizes[0]; - const hmin = sizes && sizes[1]; + const {required, len, sizes = []} = placementData; + let wmin; + let hmin; + + if (isArray(sizes[0])) { + wmin = sizes[0][0]; + hmin = sizes[0][1]; + } else { + wmin = sizes[0]; + hmin = sizes[1]; + } + const content = {}; if (type) content.type = type; @@ -284,6 +304,32 @@ function generateNativeFromAdUnit(data, params) { }; } +function formatNativePresetImage(data) { + const sizes = data.sizes; + + return { + image: { + required: true, + sizes + }, + title: { + required: true + }, + sponsoredBy: { + required: true + }, + body: { + required: false + }, + cta: { + required: false + }, + icon: { + required: false + } + }; +} + function generateSiteFromAdUnitContext(adUnitContext) { if (!adUnitContext || !adUnitContext.refererInfo) return null; @@ -448,7 +494,7 @@ function generateAdFromBid(bid, bidResponse, serverRequest) { mediaType: bid.ext.adot.media_type, }; - if (isBidANative(bid)) return {...base, native: formatNativeData(bid.adm)}; + if (isBidANative(bid)) return {...base, native: formatNativeData(bid)}; const size = getSizeFromBid(bid, impressionData); const creative = getCreativeFromBid(bid, impressionData); @@ -465,7 +511,7 @@ function generateAdFromBid(bid, bidResponse, serverRequest) { }; } -function formatNativeData(adm) { +function formatNativeData({adm, price}) { const parsedAdm = tryParse(adm); const {assets, link: {url, clicktrackers}, imptrackers, jstracker} = parsedAdm.native; const placements = NATIVE_PLACEMENTS; @@ -480,7 +526,7 @@ function formatNativeData(adm) { }, { clickUrl: url, clickTrackers: clicktrackers, - impressionTrackers: imptrackers, + impressionTrackers: imptrackers && imptrackers.map(impTracker => replaceAuctionPrice(impTracker, price)), javascriptTrackers: jstracker && [jstracker] }); } @@ -503,10 +549,11 @@ function getSizeFromBid(bid, impressionData) { function getCreativeFromBid(bid, impressionData) { const shouldUseAdMarkup = !!bid.adm; + const price = bid.price; return { - markup: shouldUseAdMarkup ? bid.adm : null, - markupUrl: !shouldUseAdMarkup ? bid.nurl : null, + markup: shouldUseAdMarkup ? replaceAuctionPrice(bid.adm, price) : null, + markupUrl: !shouldUseAdMarkup ? replaceAuctionPrice(bid.nurl, price) : null, renderer: getRendererFromBid(bid, impressionData) }; } diff --git a/modules/adotBidAdapter.md b/modules/adotBidAdapter.md index 88c8fb0b936..7fc1d84ee60 100644 --- a/modules/adotBidAdapter.md +++ b/modules/adotBidAdapter.md @@ -114,39 +114,37 @@ const adUnit = { code: 'test-div', mediaTypes: { native: { - native: { - image: { - // Field required status - required: false, - // Image dimensions supported by the native ad unit. - // Each ad unit size is formatted as follows: [width, height]. - sizes: [100, 50] - }, - title: { - // Field required status - required: false, - // Maximum length of the title - len: 140 - }, - sponsoredBy: { - // Field required status - required: false - }, - clickUrl: { - // Field required status - required: false - }, - body: { - // Field required status - required: false - }, - icon: { - // Field required status - required: false, - // Icon dimensions supported by the native ad unit. - // Each ad unit size is formatted as follows: [width, height]. - sizes: [50, 50] - } + image: { + // Field required status + required: false, + // Image dimensions supported by the native ad unit. + // Each ad unit size is formatted as follows: [width, height]. + sizes: [100, 50] + }, + title: { + // Field required status + required: false, + // Maximum length of the title + len: 140 + }, + sponsoredBy: { + // Field required status + required: false + }, + clickUrl: { + // Field required status + required: false + }, + body: { + // Field required status + required: false + }, + icon: { + // Field required status + required: false, + // Icon dimensions supported by the native ad unit. + // Each ad unit size is formatted as follows: [width, height]. + sizes: [50, 50] } } }, diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index 594fc4ac7b7..d580cd763a4 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -2062,10 +2062,11 @@ describe('Adot Adapter', function () { serverResponse.body.cur = 'USD'; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); expect(ads[0].vastXml).to.equal(null); expect(ads[0].vastUrl).to.equal(null); @@ -2087,10 +2088,13 @@ describe('Adot Adapter', function () { const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); + const adm2WithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[1].adm, serverResponse.body.seatbid[0].bid[1].price); + expect(ads).to.be.an('array').and.to.have.length(2); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); expect(ads[0].vastXml).to.equal(null); expect(ads[0].vastUrl).to.equal(null); @@ -2104,7 +2108,7 @@ describe('Adot Adapter', function () { expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); expect(ads[0].renderer).to.equal(null); expect(ads[1].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[1].bidId); - expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[1].adm); + expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(adm2WithAuctionPriceReplaced); expect(ads[1].adUrl).to.equal(null); expect(ads[1].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].crid); expect(ads[1].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].price); @@ -2592,10 +2596,11 @@ describe('Adot Adapter', function () { const serverResponse = examples.serverResponse_banner; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); expect(ads[0].vastXml).to.equal(null); expect(ads[0].vastUrl).to.equal(null); @@ -2617,10 +2622,11 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].nurl = undefined; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.equal(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); expect(ads[0].vastXml).to.equal(null); expect(ads[0].vastUrl).to.equal(null); @@ -2642,11 +2648,12 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].adm = undefined; const ads = spec.interpretResponse(serverResponse, serverRequest); + const nurlWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].nurl, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); expect(ads[0].ad).to.equal(null); - expect(ads[0].adUrl).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].nurl); + expect(ads[0].adUrl).to.exist.and.to.be.a('string').and.to.equal(nurlWithAuctionPriceReplaced); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); @@ -2721,12 +2728,13 @@ describe('Adot Adapter', function () { const serverResponse = examples.serverResponse_video_instream; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2745,12 +2753,13 @@ describe('Adot Adapter', function () { const serverResponse = examples.serverResponse_video_outstream; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2769,12 +2778,14 @@ describe('Adot Adapter', function () { const serverResponse = examples.serverResponse_video_instream_outstream; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); + const adm2WithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[1].adm, serverResponse.body.seatbid[0].bid[1].price); expect(ads).to.be.an('array').and.to.have.length(2); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2786,9 +2797,9 @@ describe('Adot Adapter', function () { expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('video'); expect(ads[0].renderer).to.equal(null); expect(ads[1].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[1].bidId); - expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[1].adm); + expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(adm2WithAuctionPriceReplaced); expect(ads[1].adUrl).to.equal(null); - expect(ads[1].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[1].vastXml).to.equal(adm2WithAuctionPriceReplaced); expect(ads[1].vastUrl).to.equal(null); expect(ads[1].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].crid); expect(ads[1].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].price); @@ -2808,12 +2819,13 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].nurl = undefined; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2833,13 +2845,14 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].adm = undefined; const ads = spec.interpretResponse(serverResponse, serverRequest); + const nurlWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].nurl, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); expect(ads[0].ad).to.equal(null); - expect(ads[0].adUrl).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].nurl); + expect(ads[0].adUrl).to.exist.and.to.be.a('string').and.to.have.string(nurlWithAuctionPriceReplaced); expect(ads[0].vastXml).to.equal(null); - expect(ads[0].vastUrl).to.equal(serverResponse.body.seatbid[0].bid[0].nurl); + expect(ads[0].vastUrl).to.equal(nurlWithAuctionPriceReplaced); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); @@ -2858,12 +2871,13 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].h = 500; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2883,12 +2897,13 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].w = 500; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2909,12 +2924,13 @@ describe('Adot Adapter', function () { serverResponse.body.seatbid[0].bid[0].h = 400; const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2934,12 +2950,13 @@ describe('Adot Adapter', function () { const serverResponse = utils.deepClone(examples.serverResponse_video_instream); const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); @@ -2959,12 +2976,13 @@ describe('Adot Adapter', function () { const serverResponse = utils.deepClone(examples.serverResponse_video_instream); const ads = spec.interpretResponse(serverResponse, serverRequest); + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); - expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); expect(ads[0].adUrl).to.equal(null); - expect(ads[0].vastXml).to.equal(serverResponse.body.seatbid[0].bid[0].adm); + expect(ads[0].vastXml).to.equal(admWithAuctionPriceReplaced); expect(ads[0].vastUrl).to.equal(null); expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); From 664ec3bbc72d06d20e3f0cdc581cb386ef89ca23 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Mon, 5 Oct 2020 16:04:40 -0700 Subject: [PATCH 0253/1476] fix some video request params (#5799) --- modules/rubiconBidAdapter.js | 4 +-- test/spec/modules/rubiconBidAdapter_spec.js | 36 +++++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 36b3de8403d..a446cbc3ce0 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -154,9 +154,9 @@ export const spec = { source: { tid: bidRequest.transactionId }, - tmax: config.getConfig('TTL') || 1000, + tmax: bidderRequest.timeout, imp: [{ - exp: 300, + exp: config.getConfig('s2sConfig.defaultTtl'), id: bidRequest.adUnitCode, secure: 1, ext: { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index aee792714cb..b2adc1f39ff 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1381,11 +1381,11 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; - expect(post).to.have.property('imp') + expect(post).to.have.property('imp'); // .with.length.of(1); let imp = post.imp[0]; expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(imp.exp).to.equal(300); + expect(imp.exp).to.equal(undefined); // now undefined expect(imp.video.w).to.equal(640); expect(imp.video.h).to.equal(480); expect(imp.video.pos).to.equal(1); @@ -1542,6 +1542,36 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext).to.not.haveOwnProperty('rubicon'); }); + it('should send video exp param correctly when set', function () { + createVideoBidderRequest(); + config.setConfig({s2sConfig: {defaultTtl: 600}}); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = request.data; + + // should exp set to the right value according to config + let imp = post.imp[0]; + expect(imp.exp).to.equal(600); + }); + + it('should not send video exp at all if not set in s2sConfig config', function () { + createVideoBidderRequest(); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = request.data; + + // should exp set to the right value according to config + let imp = post.imp[0]; + // bidderFactory stringifies request body before sending so removes undefined attributes: + expect(imp.exp).to.equal(undefined); + }); + + it('should send tmax as the bidderRequest timeout value', function () { + createVideoBidderRequest(); + bidderRequest.timeout = 3333; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let post = request.data; + expect(post.tmax).to.equal(3333); + }); + it('should send correct bidfloor to PBS', function () { createVideoBidderRequest(); @@ -1891,7 +1921,7 @@ describe('the rubicon adapter', function () { // .with.length.of(1); let imp = post.imp[0]; expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(imp.exp).to.equal(300); + expect(imp.exp).to.equal(undefined); expect(imp.video.w).to.equal(640); expect(imp.video.h).to.equal(480); expect(imp.video.pos).to.equal(1); From aa1e5e3b1f9025c97d18a633c147c91af15e9b6e Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 6 Oct 2020 16:17:55 +0200 Subject: [PATCH 0254/1476] expose full user id config (including storage) to user id modules (#5803) * expose full user id config (including storage) to user id modules, rather than just the params object * update docs to `SubmoduleConfig` * more doc fixes * missed one doc --- modules/britepoolIdSystem.js | 5 +- modules/criteoIdSystem.js | 4 +- modules/haloIdSystem.js | 4 +- modules/id5IdSystem.js | 12 +++-- modules/identityLinkIdSystem.js | 5 +- modules/intentIqIdSystem.js | 5 +- modules/liveIntentIdSystem.js | 10 ++-- modules/lotamePanoramaIdSystem.js | 7 +-- modules/merkleIdSystem.js | 5 +- modules/netIdSystem.js | 4 +- modules/parrableIdSystem.js | 5 +- modules/pubCommonIdSystem.js | 8 +-- modules/pubProvidedSystem.js | 5 +- modules/sharedIdSystem.js | 9 ++-- modules/unifiedIdSystem.js | 5 +- modules/userId/index.js | 19 +++---- modules/zeotapIdPlusIdSystem.js | 2 +- test/spec/modules/britepoolIdSystem_spec.js | 10 ++-- test/spec/modules/id5IdSystem_spec.js | 18 ++++--- .../spec/modules/identityLinkIdSystem_spec.js | 6 +-- test/spec/modules/intentIqIdSystem_spec.js | 14 ++--- test/spec/modules/liveIntentIdSystem_spec.js | 28 +++++----- test/spec/modules/parrableIdSystem_spec.js | 54 +++++++++---------- test/spec/modules/userId_spec.js | 2 +- 24 files changed, 131 insertions(+), 115 deletions(-) diff --git a/modules/britepoolIdSystem.js b/modules/britepoolIdSystem.js index 90fd159571f..3bf416957d2 100644 --- a/modules/britepoolIdSystem.js +++ b/modules/britepoolIdSystem.js @@ -29,11 +29,12 @@ export const britepoolIdSubmodule = { /** * Performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [submoduleConfigParams] + * @param {SubmoduleConfig} [submoduleConfig] * @param {ConsentData|undefined} consentData * @returns {function(callback:function)} */ - getId(submoduleConfigParams, consentData) { + getId(submoduleConfig, consentData) { + const submoduleConfigParams = (submoduleConfig && submoduleConfig.params) || {}; const { params, headers, url, getter, errors } = britepoolIdSubmodule.createParams(submoduleConfigParams, consentData); let getterResponse = null; if (typeof getter === 'function') { diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 017194d0e86..64aaf61ef23 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -126,11 +126,11 @@ export const criteoIdSubmodule = { /** * get the Criteo Id from local storages and initiate a new user sync * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] * @returns {{id: {criteoId: string} | undefined}}} */ - getId(configParams, consentData) { + getId(config, consentData) { const hasGdprData = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies; const gdprConsentString = hasGdprData ? consentData.consentString : undefined; diff --git a/modules/haloIdSystem.js b/modules/haloIdSystem.js index 237b502f6a7..d0eb79d4ac2 100644 --- a/modules/haloIdSystem.js +++ b/modules/haloIdSystem.js @@ -30,10 +30,10 @@ export const haloIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(config) { const url = `https://id.halo.ad.gt/api/v1/pbhid`; const resp = function (callback) { diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 47064e0a1a9..f8ff50f52a3 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -65,12 +65,13 @@ export const id5IdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function getId - * @param {SubmoduleParams} [configParams] - * @param {ConsentData} [consentData] + * @param {SubmoduleConfig} config + * @param {ConsentData} consentData * @param {(Object|undefined)} cacheIdObj * @returns {IdResponse|undefined} */ - getId(configParams, consentData, cacheIdObj) { + getId(config, consentData, cacheIdObj) { + const configParams = (config && config.params) || {}; if (!hasRequiredParams(configParams)) { return undefined; } @@ -123,11 +124,12 @@ export const id5IdSubmodule = { * If IdResponse#callback is defined, then it'll called at the end of auction. * It's permissible to return neither, one, or both fields. * @function extendId - * @param {SubmoduleParams} configParams + * @param {SubmoduleConfig} config * @param {Object} cacheIdObj - existing id, if any * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ - extendId(configParams, cacheIdObj) { + extendId(config, cacheIdObj) { + const configParams = (config && config.params) || {}; incrementNb(configParams); return cacheIdObj; } diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index 14c33329b2d..73fc6408581 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -34,10 +34,11 @@ export const identityLinkSubmodule = { * performs action to obtain id and return a value in the callback's response argument * @function * @param {ConsentData} [consentData] - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(configParams, consentData) { + getId(config, consentData) { + const configParams = (config && config.params) || {}; if (!configParams || typeof configParams.pid !== 'string') { utils.logError('identityLink submodule requires partner id to be defined'); return; diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index bb74b1210cb..c22a6dbc7aa 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -56,10 +56,11 @@ export const intentIqIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(config) { + const configParams = (config && config.params) || {}; if (!configParams || typeof configParams.partner !== 'number') { utils.logError('User ID - intentIqId submodule requires a valid partner to be defined'); return; diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index bd2638e5936..7981b62dc51 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -100,10 +100,11 @@ export const liveIntentIdSubmodule = { * `publisherId` params. * @function * @param {{unifiedId:string}} value - * @param {SubmoduleParams|undefined} [configParams] + * @param {SubmoduleConfig|undefined} config * @returns {{lipb:Object}} */ - decode(value, configParams) { + decode(value, config) { + const configParams = (config && config.params) || {}; function composeIdObject(value) { const base = { 'lipbid': value['unifiedId'] }; delete value.unifiedId; @@ -121,10 +122,11 @@ export const liveIntentIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(config) { + const configParams = (config && config.params) || {}; const liveConnect = initializeLiveConnect(configParams); if (!liveConnect) { return; diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index cdf9131dd68..5c3a9a16b3a 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -152,21 +152,22 @@ export const lotamePanoramaIdSubmodule = { * Decode the stored id value for passing to bid requests * @function decode * @param {(Object|string)} value + * @param {SubmoduleConfig|undefined} config * @returns {(Object|undefined)} */ - decode(value, configParams) { + decode(value, config) { return utils.isStr(value) ? { 'lotamePanoramaId': value } : undefined; }, /** * Retrieve the Lotame Panorama Id * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] * @param {(Object|undefined)} cacheIdObj * @returns {IdResponse|undefined} */ - getId(configParams, consentData, cacheIdObj) { + getId(config, consentData, cacheIdObj) { let localCache = getLotameLocalCache(); let refreshNeeded = Date.now() > localCache.expiryTimestampMs; diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index d6bf96618df..c55233af6a0 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -31,11 +31,12 @@ export const merkleIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] * @returns {IdResponse|undefined} */ - getId(configParams, consentData) { + getId(config, consentData) { + const configParams = (config && config.params) || {}; if (!configParams || typeof configParams.pubid !== 'string') { utils.logError('User ID - merkleId submodule requires a valid pubid to be defined'); return; diff --git a/modules/netIdSystem.js b/modules/netIdSystem.js index cfe78d9f488..90c8735c993 100644 --- a/modules/netIdSystem.js +++ b/modules/netIdSystem.js @@ -26,12 +26,12 @@ export const netIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] * @param {(Object|undefined)} cacheIdObj * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(config) { /* currently not possible */ return {}; } diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 2d6a2d6d6e5..7587962c62b 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -261,11 +261,12 @@ export const parrableIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] * @returns {function(callback:function), id:ParrableId} */ - getId(configParams, gdprConsentData, currentStoredId) { + getId(config, gdprConsentData, currentStoredId) { + const configParams = (config && config.params) || {}; return fetchId(configParams); } }; diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index 9516934de42..cb60a1b559c 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -54,10 +54,10 @@ export const pubCommonIdSubmodule = { /** * performs action to obtain id * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse} */ - getId: function ({create = true, pixelUrl} = {}) { + getId: function ({params: {create = true, pixelUrl} = {}} = {}) { try { if (typeof window[PUB_COMMON_ID] === 'object') { // If the page includes its own pubcid module, then save a copy of id. @@ -75,11 +75,11 @@ export const pubCommonIdSubmodule = { /** * performs action to extend an id * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleParams} [config] * @param {Object} storedId existing id * @returns {IdResponse|undefined} */ - extendId: function({extend = false, pixelUrl} = {}, storedId) { + extendId: function({params: {extend = false, pixelUrl} = {}} = {}, storedId) { try { if (typeof window[PUB_COMMON_ID] === 'object') { // If the page includes its onw pubcid module, then there is nothing to do. diff --git a/modules/pubProvidedSystem.js b/modules/pubProvidedSystem.js index 575633e622f..0b2175f57cb 100644 --- a/modules/pubProvidedSystem.js +++ b/modules/pubProvidedSystem.js @@ -34,10 +34,11 @@ export const pubProvidedIdSubmodule = { /** * performs action to obtain id and return a value. * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {{id: array}} */ - getId(configParams) { + getId(config) { + const configParams = (config && config.params) || {}; let res = []; if (utils.isArray(configParams.eids)) { res = res.concat(configParams.eids); diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 5c2a3df0595..cdd840c4f54 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -296,10 +296,10 @@ export const sharedIdSubmodule = { /** * performs action to obtain id and return a value. * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {sharedId} */ - getId(configParams) { + getId(config) { const resp = function (callback) { utils.logInfo('SharedId: Sharedid doesnt exists, new cookie creation'); ajax(ID_SVC, idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true}); @@ -309,11 +309,12 @@ export const sharedIdSubmodule = { /** * performs actions even if the id exists and returns a value - * @param configParams + * @param config * @param storedId * @returns {{callback: *}} */ - extendId(configParams, storedId) { + extendId(config, storedId) { + const configParams = (config && config.params) || {}; utils.logInfo('SharedId: Existing shared id ' + storedId.id); const resp = function (callback) { const needSync = isIdSynced(configParams, storedId); diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index f916030d643..3db4003c424 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -30,10 +30,11 @@ export const unifiedIdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} [configParams] + * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(config) { + const configParams = (config && config.params) || {}; if (!configParams || (typeof configParams.partner !== 'string' && typeof configParams.url !== 'string')) { utils.logError('User ID - unifiedId submodule requires either partner or url to be defined'); return; diff --git a/modules/userId/index.js b/modules/userId/index.js index 14f7ad3599b..ce442d4a2d3 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -14,7 +14,7 @@ * If IdResponse#callback is defined, then it'll called at the end of auction. * It's permissible to return neither, one, or both fields. * @name Submodule#getId - * @param {SubmoduleParams} configParams + * @param {SubmoduleConfig} config * @param {ConsentData|undefined} consentData * @param {(Object|undefined)} cacheIdObj * @return {(IdResponse|undefined)} A response object that contains id and/or callback. @@ -27,7 +27,7 @@ * If IdResponse#callback is defined, then it'll called at the end of auction. * It's permissible to return neither, one, or both fields. * @name Submodule#extendId - * @param {SubmoduleParams} configParams + * @param {SubmoduleConfig} config * @param {Object} storedId - existing id, if any * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ @@ -37,7 +37,7 @@ * @summary decode a stored value for passing to bid requests * @name Submodule#decode * @param {Object|string} value - * @param {SubmoduleParams|undefined} configParams + * @param {SubmoduleConfig|undefined} config * @return {(Object|undefined)} */ @@ -85,6 +85,7 @@ * @property {(array|undefined)} identifiersToResolve - the identifiers from either ls|cookie to be attached to the getId query * @property {(string|undefined)} providedIdentifierName - defines the name of an identifier that can be found in local storage or in the cookie jar that can be sent along with the getId request. This parameter should be used whenever a customer is able to provide the most stable identifier possible * @property {(LiveIntentCollectConfig|undefined)} liCollectConfig - the config for LiveIntent's collect requests + * @property {(string|undefined)} pd - publisher provided data for reconciling ID5 IDs */ /** @@ -322,7 +323,7 @@ function processSubmoduleCallbacks(submodules, cb) { setStoredValue(submodule, idObj); } // cache decoded value (this is copied to every adUnit bid) - submodule.idObj = submodule.submodule.decode(idObj); + submodule.idObj = submodule.submodule.decode(idObj, submodule.config); } else { utils.logInfo(`${MODULE_NAME}: ${submodule.submodule.name} - request id responded with an empty value`); } @@ -505,10 +506,10 @@ function initSubmodules(submodules, consentData) { if (!storedId || refreshNeeded || !storedConsentDataMatchesConsentData(storedConsentData, consentData)) { // No id previously saved, or a refresh is needed, or consent has changed. Request a new id from the submodule. - response = submodule.submodule.getId(submodule.config.params, consentData, storedId); + response = submodule.submodule.getId(submodule.config, consentData, storedId); } else if (typeof submodule.submodule.extendId === 'function') { // If the id exists already, give submodule a chance to decide additional actions that need to be taken - response = submodule.submodule.extendId(submodule.config.params, storedId); + response = submodule.submodule.extendId(submodule.config, storedId); } if (utils.isPlainObject(response)) { @@ -526,16 +527,16 @@ function initSubmodules(submodules, consentData) { if (storedId) { // cache decoded value (this is copied to every adUnit bid) - submodule.idObj = submodule.submodule.decode(storedId, submodule.config.params); + submodule.idObj = submodule.submodule.decode(storedId, submodule.config); } } else if (submodule.config.value) { // cache decoded value (this is copied to every adUnit bid) submodule.idObj = submodule.config.value; } else { - const response = submodule.submodule.getId(submodule.config.params, consentData, undefined); + const response = submodule.submodule.getId(submodule.config, consentData, undefined); if (utils.isPlainObject(response)) { if (typeof response.callback === 'function') { submodule.callback = response.callback; } - if (response.id) { submodule.idObj = submodule.submodule.decode(response.id, submodule.config.params); } + if (response.id) { submodule.idObj = submodule.submodule.decode(response.id, submodule.config); } } } carry.push(submodule); diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index ea1173cd61e..d800286b00e 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -41,7 +41,7 @@ export const zeotapIdPlusSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function - * @param {SubmoduleParams} configParams + * @param {SubmoduleConfig} config * @return {{id: string | undefined} | undefined} */ getId() { diff --git a/test/spec/modules/britepoolIdSystem_spec.js b/test/spec/modules/britepoolIdSystem_spec.js index f2dd2ef533f..ddb61806006 100644 --- a/test/spec/modules/britepoolIdSystem_spec.js +++ b/test/spec/modules/britepoolIdSystem_spec.js @@ -28,17 +28,17 @@ describe('BritePool Submodule', () => { }); it('trigger id resolution pixel when no identifiers set', () => { - britepoolIdSubmodule.getId({}); + britepoolIdSubmodule.getId({ params: {} }); expect(triggerPixelStub.called).to.be.true; }); it('trigger id resolution pixel when no identifiers set with api_key param', () => { - britepoolIdSubmodule.getId({ api_key }); + britepoolIdSubmodule.getId({ params: { api_key } }); expect(triggerPixelStub.called).to.be.true; }); it('does not trigger id resolution pixel when identifiers set', () => { - britepoolIdSubmodule.getId({ api_key, aaid }); + britepoolIdSubmodule.getId({ params: { api_key, aaid } }); expect(triggerPixelStub.called).to.be.false; }); @@ -110,7 +110,7 @@ describe('BritePool Submodule', () => { expect(getter).to.equal(getter_override); // Making sure it did not become part of params expect(params.getter).to.be.undefined; - const response = britepoolIdSubmodule.getId({ api_key, aaid, url: url_override, getter: getter_override }); + const response = britepoolIdSubmodule.getId({ params: { api_key, aaid, url: url_override, getter: getter_override } }); assert.deepEqual(response, { id: { 'primaryBPID': bpid } }); }); @@ -119,7 +119,7 @@ describe('BritePool Submodule', () => { expect(getter).to.equal(getter_callback_override); // Making sure it did not become part of params expect(params.getter).to.be.undefined; - const response = britepoolIdSubmodule.getId({ api_key, aaid, url: url_override, getter: getter_callback_override }); + const response = britepoolIdSubmodule.getId({ params: { api_key, aaid, url: url_override, getter: getter_callback_override } }); expect(response.callback).to.not.be.undefined; response.callback(result => { assert.deepEqual(result, { 'primaryBPID': bpid }); diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index cea6bdf92b9..ac000c1e6dd 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -95,10 +95,12 @@ describe('ID5 ID System', function() { it('should fail if no partner is provided in the config', function() { expect(id5IdSubmodule.getId()).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ params: { } })).to.be.eq(undefined); }); it('should call the ID5 server with 1puid field for legacy storedObj format', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig().params, undefined, ID5_LEGACY_STORED_OBJ).callback; + let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_LEGACY_STORED_OBJ).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -115,7 +117,7 @@ describe('ID5 ID System', function() { }); it('should call the ID5 server with signature field for new storedObj format', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig().params, undefined, ID5_STORED_OBJ).callback; + let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -134,8 +136,8 @@ describe('ID5 ID System', function() { it('should call the ID5 server with pd field when pd config is set', function () { const pubData = 'b50ca08271795a8e7e4012813f23d505193d75c0f2e2bb99baa63aa822f66ed3'; - let config = getId5FetchConfig().params; - config.pd = pubData; + let config = getId5FetchConfig(); + config.params.pd = pubData; let submoduleCallback = id5IdSubmodule.getId(config, undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); @@ -154,8 +156,8 @@ describe('ID5 ID System', function() { }); it('should call the ID5 server with empty pd field when pd config is not set', function () { - let config = getId5FetchConfig().params; - config.pd = undefined; + let config = getId5FetchConfig(); + config.params.pd = undefined; let submoduleCallback = id5IdSubmodule.getId(config, undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); @@ -174,7 +176,7 @@ describe('ID5 ID System', function() { it('should call the ID5 server with nb=1 when no stored value exists', function () { coreStorage.setCookie(ID5_NB_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig().params, undefined, ID5_STORED_OBJ).callback; + let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -194,7 +196,7 @@ describe('ID5 ID System', function() { let expStr = (new Date(Date.now() + 25000).toUTCString()); coreStorage.setCookie(ID5_NB_COOKIE_NAME, '1', expStr); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig().params, undefined, ID5_STORED_OBJ).callback; + let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index 9f36ba92558..c729be4c1d6 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -3,7 +3,7 @@ import * as utils from 'src/utils.js'; import {server} from 'test/mocks/xhr.js'; const pid = '14'; -const defaultConfigParams = {pid: pid}; +const defaultConfigParams = { params: {pid: pid} }; const responseHeader = {'Content-Type': 'application/json'} describe('IdentityLinkId tests', function () { @@ -18,12 +18,12 @@ describe('IdentityLinkId tests', function () { }); it('should log an error if no configParams were passed when getId', function () { - identityLinkSubmodule.getId(); + identityLinkSubmodule.getId({ params: {} }); expect(logErrorStub.calledOnce).to.be.true; }); it('should log an error if pid configParam was not passed when getId', function () { - identityLinkSubmodule.getId({}); + identityLinkSubmodule.getId({ params: {} }); expect(logErrorStub.calledOnce).to.be.true; }); diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 951cfab5f50..4b45342cb6f 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -6,10 +6,10 @@ import {server} from 'test/mocks/xhr.js'; const partner = 10; const pai = '11'; const pcid = '12'; -const defaultConfigParams = {partner: partner}; -const paiConfigParams = {partner: partner, pai: pai}; -const pcidConfigParams = {partner: partner, pcid: pcid}; -const allConfigParams = {partner: partner, pai: pai, pcid: pcid}; +const defaultConfigParams = { params: {partner: partner} }; +const paiConfigParams = { params: {partner: partner, pai: pai} }; +const pcidConfigParams = { params: {partner: partner, pcid: pcid} }; +const allConfigParams = { params: {partner: partner, pai: pai, pcid: pcid} }; const responseHeader = {'Content-Type': 'application/json'} describe('IntentIQ tests', function () { @@ -24,19 +24,19 @@ describe('IntentIQ tests', function () { }); it('should log an error if no configParams were passed when getId', function () { - let submodule = intentIqIdSubmodule.getId(); + let submodule = intentIqIdSubmodule.getId({ params: {} }); expect(logErrorStub.calledOnce).to.be.true; expect(submodule).to.be.undefined; }); it('should log an error if partner configParam was not passed when getId', function () { - let submodule = intentIqIdSubmodule.getId({}); + let submodule = intentIqIdSubmodule.getId({ params: {} }); expect(logErrorStub.calledOnce).to.be.true; expect(submodule).to.be.undefined; }); it('should log an error if partner configParam was not a numeric value', function () { - let submodule = intentIqIdSubmodule.getId({partner: '10'}); + let submodule = intentIqIdSubmodule.getId({ params: {partner: '10'} }); expect(logErrorStub.calledOnce).to.be.true; expect(submodule).to.be.undefined; }); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index b19d38d5859..80f776168c4 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -4,7 +4,7 @@ import {uspDataHandler} from '../../../src/adapterManager.js'; import {server} from 'test/mocks/xhr.js'; const PUBLISHER_ID = '89899'; -const defaultConfigParams = {publisherId: PUBLISHER_ID}; +const defaultConfigParams = { params: {publisherId: PUBLISHER_ID} }; const responseHeader = {'Content-Type': 'application/json'} describe('LiveIntentId', function () { @@ -55,8 +55,8 @@ describe('LiveIntentId', function () { }); it('should initialize LiveConnect with the config params when decode and emit an event', function () { - liveIntentIdSubmodule.decode({}, { - ...defaultConfigParams, + liveIntentIdSubmodule.decode({}, { params: { + ...defaultConfigParams.params, ...{ url: 'https://dummy.liveintent.com', liCollectConfig: { @@ -64,7 +64,7 @@ describe('LiveIntentId', function () { collectorUrl: 'https://collector.liveintent.com' } } - }); + } }); expect(pixel.src).to.match(/https:\/\/collector.liveintent.com\/p\?aid=a-0001&wpn=prebid.*/) }); @@ -95,7 +95,7 @@ describe('LiveIntentId', function () { it('should call the Custom URL of the LiveIntent Identity Exchange endpoint', function () { getCookieStub.returns(null); let callBackSpy = sinon.spy(); - let submoduleCallback = liveIntentIdSubmodule.getId({...defaultConfigParams, ...{'url': 'https://dummy.liveintent.com/idex'}}).callback; + let submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899'); @@ -110,13 +110,13 @@ describe('LiveIntentId', function () { it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function () { getCookieStub.returns(null); let callBackSpy = sinon.spy(); - let submoduleCallback = liveIntentIdSubmodule.getId({ - ...defaultConfigParams, + let submoduleCallback = liveIntentIdSubmodule.getId({ params: { + ...defaultConfigParams.params, ...{ 'url': 'https://dummy.liveintent.com/idex', 'partner': 'rubicon' } - }).callback; + } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899'); @@ -179,12 +179,12 @@ describe('LiveIntentId', function () { const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000' getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie); getDataFromLocalStorageStub.withArgs('_thirdPC').returns('third-pc'); - const configParams = { - ...defaultConfigParams, + const configParams = { params: { + ...defaultConfigParams.params, ...{ 'identifiersToResolve': ['_thirdPC'] } - }; + }}; let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); @@ -201,12 +201,12 @@ describe('LiveIntentId', function () { it('should include an additional identifier value to resolve even if it is an object', function () { getCookieStub.returns(null); getDataFromLocalStorageStub.withArgs('_thirdPC').returns({'key': 'value'}); - const configParams = { - ...defaultConfigParams, + const configParams = { params: { + ...defaultConfigParams.params, ...{ 'identifiersToResolve': ['_thirdPC'] } - }; + }}; let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 1cc89240bc3..5e62af9b2fa 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -92,7 +92,7 @@ describe('Parrable ID System', function() { }) it('creates xhr to Parrable that synchronizes the ID', function() { - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); getIdResult.callback(callbackSpy); @@ -128,7 +128,7 @@ describe('Parrable ID System', function() { let uspString = '1YNN'; uspDataHandler.setConsentData(uspString); parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, + P_CONFIG_MOCK, null, null ).callback(callbackSpy); @@ -138,7 +138,7 @@ describe('Parrable ID System', function() { it('should log an error and continue to callback if ajax request errors', function () { let callBackSpy = sinon.spy(); - let submoduleCallback = parrableIdSubmodule.getId({partner: 'prebid'}).callback; + let submoduleCallback = parrableIdSubmodule.getId({ params: {partner: 'prebid'} }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; expect(request.url).to.contain('h.parrable.com'); @@ -155,7 +155,7 @@ describe('Parrable ID System', function() { describe('response id', function() { it('provides the stored Parrable values if a cookie exists', function() { writeParrableCookie({ eid: P_COOKIE_EID }); - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); removeParrableCookie(); expect(getIdResult.id).to.deep.equal({ @@ -171,7 +171,7 @@ describe('Parrable ID System', function() { storage.setCookie(oldEidCookieName, oldEid); storage.setCookie(oldOptoutCookieName, 'true'); - let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); expect(getIdResult.id).to.deep.equal({ eid: oldEid, ibaOptout: true @@ -212,9 +212,9 @@ describe('Parrable ID System', function() { }); it('permits an impression when no timezoneFilter is configured', function() { - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', - })).to.have.property('callback'); + } })).to.have.property('callback'); }); it('permits an impression from a blocked timezone when a cookie exists', function() { @@ -224,12 +224,12 @@ describe('Parrable ID System', function() { writeParrableCookie({ eid: P_COOKIE_EID }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } - })).to.have.property('callback'); + } })).to.have.property('callback'); expect(resolvedOptions.called).to.equal(false); removeParrableCookie(); @@ -240,12 +240,12 @@ describe('Parrable ID System', function() { const resolvedOptions = sinon.stub().returns({ timeZone: allowedZone }); Intl.DateTimeFormat.returns({ resolvedOptions }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { allowedZones: [ allowedZone ] } - })).to.have.property('callback'); + } })).to.have.property('callback'); expect(resolvedOptions.called).to.equal(true); }); @@ -254,12 +254,12 @@ describe('Parrable ID System', function() { const resolvedOptions = sinon.stub().returns({ timeZone: 'Iceland' }); Intl.DateTimeFormat.returns({ resolvedOptions }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } - })).to.have.property('callback'); + } })).to.have.property('callback'); expect(resolvedOptions.called).to.equal(true); }); @@ -268,12 +268,12 @@ describe('Parrable ID System', function() { const resolvedOptions = sinon.stub().returns({ timeZone: blockedZone }); Intl.DateTimeFormat.returns({ resolvedOptions }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } - })).to.equal(null); + } })).to.equal(null); expect(resolvedOptions.called).to.equal(true); }); @@ -282,13 +282,13 @@ describe('Parrable ID System', function() { const resolvedOptions = sinon.stub().returns({ timeZone: timezone }); Intl.DateTimeFormat.returns({ resolvedOptions }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { allowedZones: [ timezone ], blockedZones: [ timezone ] } - })).to.equal(null); + } })).to.equal(null); expect(resolvedOptions.called).to.equal(true); }); }); @@ -312,12 +312,12 @@ describe('Parrable ID System', function() { writeParrableCookie({ eid: P_COOKIE_EID }); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } - })).to.have.property('callback'); + } })).to.have.property('callback'); removeParrableCookie(); }); @@ -326,12 +326,12 @@ describe('Parrable ID System', function() { const allowedOffset = -5; Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { allowedOffsets: [ allowedOffset ] } - })).to.have.property('callback'); + } })).to.have.property('callback'); expect(Date.prototype.getTimezoneOffset.called).to.equal(true); }); @@ -340,12 +340,12 @@ describe('Parrable ID System', function() { const blockedOffset = 5; Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } - })).to.have.property('callback'); + }})).to.have.property('callback'); expect(Date.prototype.getTimezoneOffset.called).to.equal(true); }); @@ -353,12 +353,12 @@ describe('Parrable ID System', function() { const blockedOffset = -5; Date.prototype.getTimezoneOffset.returns(blockedOffset * 60); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } - })).to.equal(null); + } })).to.equal(null); expect(Date.prototype.getTimezoneOffset.called).to.equal(true); }); @@ -366,13 +366,13 @@ describe('Parrable ID System', function() { const offset = -5; Date.prototype.getTimezoneOffset.returns(offset * 60); - expect(parrableIdSubmodule.getId({ + expect(parrableIdSubmodule.getId({ params: { partner: 'prebid-test', timezoneFilter: { allowedOffset: [ offset ], blockedOffsets: [ offset ] } - })).to.equal(null); + } })).to.equal(null); expect(Date.prototype.getTimezoneOffset.called).to.equal(true); }); }); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index c4e52d9a121..5dac5db7b54 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1567,7 +1567,7 @@ describe('User ID', function () { 'mid': value['MOCKID'] }; }, - getId: function (params, storedId) { + getId: function (config, storedId) { if (storedId) return {}; return {id: {'MOCKID': '1234'}}; } From ea498f97d0a0f874d1df5e32c50f868600ada2d8 Mon Sep 17 00:00:00 2001 From: Baptiste HAUDEGAND Date: Tue, 6 Oct 2020 17:50:31 +0200 Subject: [PATCH 0255/1476] Fix timeToFirstByte unit test (#5820) * Debug timeToFirstByte unit test * review --- modules/teadsBidAdapter.js | 8 ++++---- test/spec/modules/teadsBidAdapter_spec.js | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 08ae1854669..6d55aabbfb5 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -133,8 +133,8 @@ function getTimeToFirstByte(win) { performance.getEntriesByType('navigation')[0] && performance.getEntriesByType('navigation')[0].responseStart && performance.getEntriesByType('navigation')[0].requestStart && - performance.getEntriesByType('navigation')[0].responseStart >= 0 && - performance.getEntriesByType('navigation')[0].requestStart >= 0 && + performance.getEntriesByType('navigation')[0].responseStart > 0 && + performance.getEntriesByType('navigation')[0].requestStart > 0 && Math.round( performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart ); @@ -146,8 +146,8 @@ function getTimeToFirstByte(win) { const ttfbWithTimingV1 = performance && performance.timing.responseStart && performance.timing.requestStart && - performance.timing.responseStart >= 0 && - performance.timing.requestStart >= 0 && + performance.timing.responseStart > 0 && + performance.timing.requestStart > 0 && performance.timing.responseStart - performance.timing.requestStart; return ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : ''; diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 17841b271d4..a6ae17ec8ff 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -200,8 +200,8 @@ describe('teadsBidAdapter', () => { performance.getEntriesByType('navigation')[0] && performance.getEntriesByType('navigation')[0].responseStart && performance.getEntriesByType('navigation')[0].requestStart && - performance.getEntriesByType('navigation')[0].responseStart >= 0 && - performance.getEntriesByType('navigation')[0].requestStart >= 0 && + performance.getEntriesByType('navigation')[0].responseStart > 0 && + performance.getEntriesByType('navigation')[0].requestStart > 0 && Math.round( performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart ); @@ -211,9 +211,15 @@ describe('teadsBidAdapter', () => { if (ttfbExpectedV2) { expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV2.toString()); } else { - const ttfbExpectedV1 = performance.timing.responseStart - performance.timing.requestStart; - - expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV1.toString()); + const ttfbWithTimingV1 = performance && + performance.timing.responseStart && + performance.timing.requestStart && + performance.timing.responseStart > 0 && + performance.timing.requestStart > 0 && + performance.timing.responseStart - performance.timing.requestStart; + const ttfbExpectedV1 = ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : ''; + + expect(payload.timeToFirstByte).to.deep.equal(ttfbExpectedV1); } }); From 8982e0904fde99454f4c10985ea46d019b683e5c Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 6 Oct 2020 15:08:39 -0400 Subject: [PATCH 0256/1476] rubicon: adding pubcid support (#5824) * rubicon: adding pubcid support * adding to orderedParams * removed eids filter so all eids will be supported * fix eids test * fixed eids assertions Co-authored-by: Isaac A. Dettman --- modules/rubiconBidAdapter.js | 11 +++++++-- test/spec/modules/rubiconBidAdapter_spec.js | 26 ++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index a446cbc3ce0..d4411a82e8f 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -236,8 +236,7 @@ export const spec = { const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); if (eids && eids.length) { - // filter out unsupported id systems - utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org', 'criteo.com'].indexOf(eid.source) !== -1)); + utils.deepSetValue(data, 'user.ext.eids', eids); // liveintent requires additional props to be set const liveIntentEid = find(data.user.ext.eids, eid => eid.source === 'liveintent.com'); @@ -389,6 +388,10 @@ export const spec = { 'tpid_tdid', 'tpid_liveintent.com', 'tg_v.LIseg', + 'ppuid', + 'eid_pubcid.org', + 'eid_sharedid.org', + 'eid_criteo.com', 'rf', 'p_geo.latitude', 'p_geo.longitude', @@ -519,6 +522,10 @@ export const spec = { if (sharedId) { data['eid_sharedid.org'] = `${sharedId.uids[0].id}^${sharedId.uids[0].atype}^${sharedId.uids[0].ext.third}`; } + const pubcid = find(bidRequest.userIdAsEids, eid => eid.source === 'pubcid.org'); + if (pubcid) { + data['eid_pubcid.org'] = `${pubcid.uids[0].id}^${pubcid.uids[0].atype}`; + } const criteoId = find(bidRequest.userIdAsEids, eid => eid.source === 'criteo.com'); if (criteoId) { data['eid_criteo.com'] = `${criteoId.uids[0].id}^${criteoId.uids[0].atype}`; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index b2adc1f39ff..a38743d634a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1145,6 +1145,20 @@ describe('the rubicon adapter', function () { }); }); + describe('pubcid support', function () { + it('should send eid_pubcid.org when userIdAsEids contains pubcid', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + pubcid: '1111' + }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_pubcid.org']).to.equal('1111^1'); + }); + }); + describe('Criteo support', function () { it('should send eid_criteo.com when userIdAsEids contains criteo', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); @@ -1442,10 +1456,16 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[4].source).to.equal('pubcid.org'); expect(post.user.ext.eids[4].uids[0].atype).to.equal(1); expect(post.user.ext.eids[4].uids[0].id).to.equal('4000'); + // example should exist + expect(post.user.ext.eids[5].source).to.equal('example.com'); + expect(post.user.ext.eids[5].uids[0].id).to.equal('333333'); + // id-partner.com + expect(post.user.ext.eids[6].source).to.equal('id-partner.com'); + expect(post.user.ext.eids[6].uids[0].id).to.equal('4444444'); // CriteoId should exist - expect(post.user.ext.eids[5].source).to.equal('criteo.com'); - expect(post.user.ext.eids[5].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[5].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[7].source).to.equal('criteo.com'); + expect(post.user.ext.eids[7].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[7].uids[0].atype).to.equal(1); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); From 16fabf4cb78daaa16ac16c3ea6c0def06fa8df9b Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:14:56 +0530 Subject: [PATCH 0257/1476] Appnexus: Add omid support (#5821) * basic implementation complete * add unit tests * remove redundant field tags[].video.frameworks --- modules/appnexusBidAdapter.js | 32 ++++++++++++++ test/spec/modules/appnexusBidAdapter_spec.js | 46 ++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index f3ebf9bf4f6..ff0e3230007 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -157,6 +157,7 @@ export const spec = { const memberIdBid = find(bidRequests, hasMemberId); const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; const schain = bidRequests[0].schain; + const omidSupport = find(bidRequests, hasOmidSupport); const payload = { tags: [...tags], @@ -168,6 +169,13 @@ export const spec = { schain: schain }; + if (omidSupport) { + payload['iab_support'] = { + omidpn: 'Appnexus', + omidpv: '$prebid.version$' + } + } + if (member > 0) { payload.member_id = member; } @@ -766,16 +774,27 @@ function bidToTag(bid) { type = (utils.isArray(type)) ? type[0] : type; tag.video[param] = VIDEO_MAPPING[param][type]; break; + // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks + case 'frameworks': + break; default: tag.video[param] = bid.params.video[param]; } }); + + if (bid.params.video.frameworks && utils.isArray(bid.params.video.frameworks)) { + tag['video_frameworks'] = bid.params.video.frameworks; + } } if (bid.renderer) { tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); } + if (bid.params.frameworks && utils.isArray(bid.params.frameworks)) { + tag['banner_frameworks'] = bid.params.frameworks; + } + let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { tag.ad_types.push(BANNER); @@ -844,6 +863,19 @@ function hasAdPod(bid) { ); } +function hasOmidSupport(bid) { + let hasOmid = false; + const bidderParams = bid.params; + const videoParams = bid.params.video; + if (bidderParams.frameworks && utils.isArray(bidderParams.frameworks)) { + hasOmid = includes(bid.params.frameworks, 6); + } + if (!hasOmid && videoParams && videoParams.frameworks && utils.isArray(videoParams.frameworks)) { + hasOmid = includes(bid.params.video.frameworks, 6); + } + return hasOmid; +} + /** * Expand an adpod placement into a set of request objects according to the * total adpod duration and the range of duration seconds. Sets minduration/ diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 7b563c4ec1d..4102896ba94 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -829,6 +829,52 @@ describe('AppNexusAdapter', function () { user_id: 'sample-criteo-userid', }); }); + + it('should populate iab_support object at the root level if omid support is detected', function () { + // with bid.params.frameworks + let bidRequest_A = Object.assign({}, bidRequests[0], { + params: { + frameworks: [1, 2, 5, 6], + video: { + frameworks: [1, 2, 5, 6] + } + } + }); + let request = spec.buildRequests([bidRequest_A]); + let payload = JSON.parse(request.data); + expect(payload.iab_support).to.be.an('object'); + expect(payload.iab_support).to.deep.equal({ + omidpn: 'Appnexus', + omidpv: '$prebid.version$' + }); + expect(payload.tags[0].banner_frameworks).to.be.an('array'); + expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]); + expect(payload.tags[0].video_frameworks).to.be.an('array'); + expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]); + expect(payload.tags[0].video.frameworks).to.not.exist; + + // without bid.params.frameworks + const bidRequest_B = Object.assign({}, bidRequests[0]); + request = spec.buildRequests([bidRequest_B]); + payload = JSON.parse(request.data); + expect(payload.iab_support).to.not.exist; + expect(payload.tags[0].banner_frameworks).to.not.exist; + expect(payload.tags[0].video_frameworks).to.not.exist; + + // with video.frameworks but it is not an array + const bidRequest_C = Object.assign({}, bidRequests[0], { + params: { + video: { + frameworks: "'1', '2', '3', '6'" + } + } + }); + request = spec.buildRequests([bidRequest_C]); + payload = JSON.parse(request.data); + expect(payload.iab_support).to.not.exist; + expect(payload.tags[0].banner_frameworks).to.not.exist; + expect(payload.tags[0].video_frameworks).to.not.exist; + }); }) describe('interpretResponse', function () { From f907ee6f4312c889bc41b22bd7b44026c7b40302 Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Wed, 7 Oct 2020 10:19:37 -0400 Subject: [PATCH 0258/1476] new userId module - neustar's fabrick (#5802) * submitting userId module for neustar's fabrick - https://www.home.neustar/fabrick * fixing 'gulp test' errors * fixing another test issue (related to ie) * removing another (last) repeat * - expose full user id config (including storage) to user id modules (#5803 - removing TODO from test * - updates to test Co-authored-by: Anderson, Ben --- modules/.submodules.json | 3 +- modules/fabrickIdSystem.js | 147 ++++++++++++++++++++++ modules/fabrickIdSystem.md | 24 ++++ test/mocks/fabrickId.json | 3 + test/spec/modules/fabrickIdSystem_spec.js | 106 ++++++++++++++++ 5 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 modules/fabrickIdSystem.js create mode 100644 modules/fabrickIdSystem.md create mode 100644 test/mocks/fabrickId.json create mode 100644 test/spec/modules/fabrickIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 18e75dd1794..0e5fdba4807 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -15,7 +15,8 @@ "intentIqIdSystem", "zeotapIdPlusIdSystem", "haloIdSystem", - "quantcastIdSystem" + "quantcastIdSystem", + "fabrickIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js new file mode 100644 index 00000000000..e61b377eefa --- /dev/null +++ b/modules/fabrickIdSystem.js @@ -0,0 +1,147 @@ +/** + * This module adds neustar's fabrickId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/fabrickIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; + +/** @type {Submodule} */ +export const fabrickIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'fabrickId', + + /** + * decode the stored id value for passing to bid requests + * @function decode + * @param {(Object|string)} value + * @returns {(Object|undefined)} + */ + decode(value) { + if (value && value.fabrickId) { + return { 'fabrickId': value.fabrickId }; + } else { + return undefined; + } + }, + + /** + * performs action to obtain id and return a value in the callback's response argument + * @function getId + * @param {SubmoduleConfig} [config] + * @param {ConsentData} + * @param {Object} cacheIdObj - existing id, if any consentData] + * @returns {IdResponse|undefined} + */ + getId(config, consentData, cacheIdObj) { + try { + const configParams = (config && config.params) || {}; + if (window.fabrickMod1) { + window.fabrickMod1(configParams, consentData, cacheIdObj); + } + if (!configParams || typeof configParams.apiKey !== 'string') { + utils.logError('fabrick submodule requires an apiKey.'); + return; + } + try { + let url = _getBaseUrl(configParams); + let keysArr = Object.keys(configParams); + for (let i in keysArr) { + let k = keysArr[i]; + if (k === 'url' || k === 'refererInfo') { + continue; + } + let v = configParams[k]; + if (Array.isArray(v)) { + for (let j in v) { + url += `${k}=${v[j]}&`; + } + } else { + url += `${k}=${v}&`; + } + } + // pull off the trailing & + url = url.slice(0, -1) + const referer = _getRefererInfo(configParams); + const urls = new Set(); + url = truncateAndAppend(urls, url, 'r', referer.referer); + if (referer.stack && referer.stack[0]) { + url = truncateAndAppend(urls, url, 'r', referer.stack[0]); + } + url = truncateAndAppend(urls, url, 'r', referer.canonicalUrl); + url = truncateAndAppend(urls, url, 'r', window.location.href); + + const resp = function (callback) { + const callbacks = { + success: response => { + if (window.fabrickMod2) { + return window.fabrickMod2( + callback, response, configParams, consentData, cacheIdObj); + } else { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + responseObj = {}; + } + } + callback(responseObj); + } + }, + error: error => { + utils.logError(`fabrickId fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, null, {method: 'GET', withCredentials: true}); + }; + return {callback: resp}; + } catch (e) { + utils.logError(`fabrickIdSystem encountered an error`, e); + } + } catch (e) { + utils.logError(`fabrickIdSystem encountered an error`, e); + } + } +}; + +function _getRefererInfo(configParams) { + if (configParams.refererInfo) { + return configParams.refererInfo; + } else { + return getRefererInfo(); + } +} + +function _getBaseUrl(configParams) { + if (configParams.url) { + return configParams.url; + } else { + return `https://fid.agkn.com/f?`; + } +} + +function truncateAndAppend(urls, url, paramName, s) { + if (s && url.length < 2000) { + if (s.length > 200) { + s = s.substring(0, 200); + } + // Don't send the same url in multiple params + if (!urls.has(s)) { + urls.add(s); + return `${url}&${paramName}=${s}` + } + } + return url; +} + +submodule('userId', fabrickIdSubmodule); diff --git a/modules/fabrickIdSystem.md b/modules/fabrickIdSystem.md new file mode 100644 index 00000000000..268c861710a --- /dev/null +++ b/modules/fabrickIdSystem.md @@ -0,0 +1,24 @@ +## Neustar Fabrick User ID Submodule + +Fabrick ID Module - https://www.home.neustar/fabrick +Product and Sales Inquiries: 1-855-898-0036 + +## Example configuration for publishers: +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'fabrickId', + storage: { + name: 'pbjs_fabrickId', + type: 'cookie', + expires: 7 + }, + params: { + apiKey: 'your apiKey', // provided to you by Neustar + e: '31c5543c1734d25c7206f5fd591525d0295bec6fe84ff82f946a34fe970a1e66' // example hash identifier (sha256) + } + }] + } +}); +``` diff --git a/test/mocks/fabrickId.json b/test/mocks/fabrickId.json new file mode 100644 index 00000000000..a8723ec88ec --- /dev/null +++ b/test/mocks/fabrickId.json @@ -0,0 +1,3 @@ +{ + "fabrickId": 1980 +} diff --git a/test/spec/modules/fabrickIdSystem_spec.js b/test/spec/modules/fabrickIdSystem_spec.js new file mode 100644 index 00000000000..cbd538816ab --- /dev/null +++ b/test/spec/modules/fabrickIdSystem_spec.js @@ -0,0 +1,106 @@ +import * as utils from '../../../src/utils.js'; +import {server} from '../../mocks/xhr.js'; + +import * as fabrickIdSystem from 'modules/fabrickIdSystem.js'; + +const defaultConfigParams = { + apiKey: '123', + e: 'abc', + p: ['def', 'hij'], + url: 'http://localhost:9999/test/mocks/fabrickId.json?' +}; +const responseHeader = {'Content-Type': 'application/json'} +const fabrickIdSubmodule = fabrickIdSystem.fabrickIdSubmodule; + +describe('Fabrick ID System', function() { + let logErrorStub; + + beforeEach(function () { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + logErrorStub.restore(); + fabrickIdSubmodule.getRefererInfoOverride = null; + }); + + it('should log an error if no configParams were passed into getId', function () { + fabrickIdSubmodule.getId(); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should error on json parsing', function() { + let submoduleCallback = fabrickIdSubmodule.getId({ + name: 'fabrickId', + params: defaultConfigParams + }).callback; + let callBackSpy = sinon.spy(); + submoduleCallback(callBackSpy); + let request = server.requests[0]; + request.respond( + 200, + responseHeader, + '] this is not json {' + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should truncate the params', function() { + let r = ''; + for (let i = 0; i < 300; i++) { + r += 'r'; + } + let configParams = Object.assign({}, defaultConfigParams, { + refererInfo: { + referer: r, + stack: ['s-0'], + canonicalUrl: 'cu-0' + } + }); + let submoduleCallback = fabrickIdSubmodule.getId({ + name: 'fabrickId', + params: configParams + }).callback; + let callBackSpy = sinon.spy(); + submoduleCallback(callBackSpy); + let request = server.requests[0]; + r = ''; + for (let i = 0; i < 200; i++) { + r += 'r'; + } + expect(request.url).to.match(new RegExp(`r=${r}&r=`)); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(logErrorStub.calledOnce).to.be.false; + }); + + it('should complete successfully', function() { + let configParams = Object.assign({}, defaultConfigParams, { + refererInfo: { + referer: 'r-0', + stack: ['s-0'], + canonicalUrl: 'cu-0' + } + }); + let submoduleCallback = fabrickIdSubmodule.getId({ + name: 'fabrickId', + params: configParams + }).callback; + let callBackSpy = sinon.spy(); + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.match(/r=r-0&r=s-0&r=cu-0&r=http/); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(logErrorStub.calledOnce).to.be.false; + }); +}); From c57bab724b683bbe0efc9dd8de87ad8db332c946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Baudisch?= Date: Wed, 7 Oct 2020 18:01:45 +0200 Subject: [PATCH 0259/1476] Integrate option to pass clickThrough urls to renderAd method (#5796) * adding options to renderAd method * adding replaceClickThrough method to utils * implemented replaceClickThrough method in render ad to enable ssps adding url param clickthrough for publisher side counting * update to cover some validation and unit tests as requested by harpere * adding unit test for clickthrough implementation; --- src/prebid.js | 10 +++++++++- src/utils.js | 5 +++++ test/spec/unit/pbjs_api_spec.js | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/prebid.js b/src/prebid.js index 31e0140cfe2..8d1d7ca1253 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -342,7 +342,7 @@ function emitAdRenderFail({ reason, message, bid, id }) { * @param {string} id bid id to locate the ad * @alias module:pbjs.renderAd */ -$$PREBID_GLOBAL$$.renderAd = function (doc, id) { +$$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); utils.logMessage('Calling renderAd with adId :' + id); @@ -354,6 +354,14 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { // replace macros according to openRTB with price paid = bid.cpm bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); + + // replacing clickthrough if submitted + if (options && options.clickThrough) { + const { clickThrough } = options; + bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); + bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + } + // save winning bids auctionManager.addWinningBid(bid); diff --git a/src/utils.js b/src/utils.js index 9426308daf4..8af7a25668d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -718,6 +718,11 @@ export function replaceAuctionPrice(str, cpm) { return str.replace(/\$\{AUCTION_PRICE\}/g, cpm); } +export function replaceClickThrough(str, clicktag) { + if (!str || !clicktag || typeof clicktag !== 'string') return; + return str.replace(/\${CLICKTHROUGH}/g, clicktag); +} + export function timestamp() { return new Date().getTime(); } diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 960ccf08c92..efb1338ac5d 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1197,6 +1197,14 @@ describe('Unit: Prebid Module', function () { assert.deepEqual($$PREBID_GLOBAL$$.getAllWinningBids()[0], adResponse); }); + it('should replace ${CLICKTHROUGH} macro in winning bids response', function () { + pushBidResponseToAuction({ + ad: "" + }); + $$PREBID_GLOBAL$$.renderAd(doc, bidId, {clickThrough: 'https://someadserverclickurl.com'}); + expect(adResponse).to.have.property('ad').and.to.match(/https:\/\/someadserverclickurl\.com/i); + }); + it('fires billing url if present on s2s bid', function () { const burl = 'http://www.example.com/burl'; pushBidResponseToAuction({ From ba1a35c56405d1d1ee4a901ba173cba32578652c Mon Sep 17 00:00:00 2001 From: Hugo Duthil Date: Wed, 7 Oct 2020 18:16:56 +0200 Subject: [PATCH 0260/1476] Add credentials and explicit options to CriteoIdSystem (#5822) Co-authored-by: Hugo Duthil --- modules/criteoIdSystem.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 64aaf61ef23..30641b6c225 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -103,7 +103,9 @@ function callCriteoUserSync(parsedCriteoData, gdprString) { } else if (jsonResponse.bundle) { saveOnAllStorages(bundleStorageKey, jsonResponse.bundle); } - } + }, + undefined, + { method: 'GET', contentType: 'application/json', withCredentials: true } ); } From e082c83adce4fd198fa0c8d4be6d1fe6caf7cb29 Mon Sep 17 00:00:00 2001 From: guiann Date: Wed, 7 Oct 2020 18:21:46 +0200 Subject: [PATCH 0261/1476] AdYouLike bidAdapter - Add information in bid request (#5828) * Remove useless bidderCode in bid response * send all the available sizes in the bid request * Use the banner sizes if given * avoid compatibility issue with old bid format * ad iframe and publisher domain paramters to bid requests * add publisher domain info in ad request * add a check in unit tests for publisherDomain * encode uri components Co-authored-by: Guillaume --- modules/adyoulikeBidAdapter.js | 26 ++++++++++--------- test/spec/modules/adyoulikeBidAdapter_spec.js | 1 + 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 725ee0f5626..40d3cf84369 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -1,5 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; const VERSION = '1.0'; @@ -104,15 +105,6 @@ function getHostname(bidderRequest) { return ''; } -/* Get current page referrer url */ -function getReferrerUrl(bidderRequest) { - let referer = ''; - if (bidderRequest && bidderRequest.refererInfo) { - referer = bidderRequest.refererInfo.referer; - } - return referer; -} - /* Get current page canonical url */ function getCanonicalUrl() { let link; @@ -155,9 +147,14 @@ function createEndpoint(bidRequests, bidderRequest) { function createEndpointQS(bidderRequest) { const qs = {}; - const ref = getReferrerUrl(bidderRequest); - if (ref) { - qs.RefererUrl = encodeURIComponent(ref); + if (bidderRequest) { + const ref = bidderRequest.refererInfo; + if (ref) { + qs.RefererUrl = encodeURIComponent(ref.referer); + if (ref.numIframes > 0) { + qs.SafeFrame = true; + } + } } const can = getCanonicalUrl(); @@ -165,6 +162,11 @@ function createEndpointQS(bidderRequest) { qs.CanonicalUrl = encodeURIComponent(can); } + const domain = config.getConfig('publisherDomain'); + if (domain) { + qs.PublisherDomain = encodeURIComponent(domain); + } + return qs; } diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index d2d4e10c17f..ec8f2f00923 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -306,6 +306,7 @@ describe('Adyoulike Adapter', function () { expect(request.method).to.equal('POST'); expect(request.url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); expect(request.url).to.contains('RefererUrl=' + encodeURIComponent(referrerUrl)); + expect(request.url).to.contains('PublisherDomain=http%3A%2F%2Flocalhost%3A9876'); expect(payload.Version).to.equal('1.0'); expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); From 84e7121f2b025d049eb5da39f31755feb94a5591 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 7 Oct 2020 14:48:32 -0400 Subject: [PATCH 0262/1476] 4.11.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35b0f6925e0..b3652fc288c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.11.0-pre", + "version": "4.11.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 752bd2713d746543b8e66d572459e8b6c1b43089 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 7 Oct 2020 15:52:43 -0400 Subject: [PATCH 0263/1476] 4.12.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3652fc288c..686d47f53e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.11.0", + "version": "4.12.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7d2709523afafad9e3ca91c2bd9477006df9f971 Mon Sep 17 00:00:00 2001 From: Renzo Toscani <7190938+rtoscani@users.noreply.github.com> Date: Thu, 8 Oct 2020 13:16:15 -0300 Subject: [PATCH 0264/1476] IDx user id submodule (#5826) * add idx user id * Update modules/idxIdSystem.js to match new SubmoduleConfig param Co-authored-by: Scott Co-authored-by: Scott --- modules/.submodules.json | 1 + modules/idxIdSystem.js | 61 ++++++++++++++ modules/idxIdSystem.md | 22 +++++ modules/userId/eids.js | 8 +- test/spec/modules/idxIdSystem_spec.js | 117 ++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 modules/idxIdSystem.js create mode 100644 modules/idxIdSystem.md create mode 100644 test/spec/modules/idxIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 0e5fdba4807..91cda9d95ad 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -16,6 +16,7 @@ "zeotapIdPlusIdSystem", "haloIdSystem", "quantcastIdSystem", + "idxIdSystem", "fabrickIdSystem" ], "adpod": [ diff --git a/modules/idxIdSystem.js b/modules/idxIdSystem.js new file mode 100644 index 00000000000..00e8a8bc5e5 --- /dev/null +++ b/modules/idxIdSystem.js @@ -0,0 +1,61 @@ +/** + * This module adds IDx to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/idxIdSystem + * @requires module:modules/userId + */ +import * as utils from '../src/utils.js' +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const IDX_MODULE_NAME = 'idx'; +const IDX_COOKIE_NAME = '_idx'; +export const storage = getStorageManager(); + +function readIDxFromCookie() { + return storage.cookiesAreEnabled ? storage.getCookie(IDX_COOKIE_NAME) : null; +} + +function readIDxFromLocalStorage() { + return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(IDX_COOKIE_NAME) : null; +} + +/** @type {Submodule} */ +export const idxIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: IDX_MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param { Object | string | undefined } value + * @return { Object | string | undefined } + */ + decode(value) { + const idxVal = value ? utils.isStr(value) ? value : utils.isPlainObject(value) ? value.id : undefined : undefined; + return idxVal ? { + 'idx': idxVal + } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} config + * @return {{id: string | undefined } | undefined} + */ + getId() { + const idxString = readIDxFromLocalStorage() || readIDxFromCookie(); + if (typeof idxString == 'string' && idxString) { + try { + const idxObj = JSON.parse(idxString); + return idxObj && idxObj.idx ? { id: idxObj.idx } : undefined; + } catch (error) { + utils.logError(error); + } + } + return undefined; + } +}; +submodule('userId', idxIdSubmodule); diff --git a/modules/idxIdSystem.md b/modules/idxIdSystem.md new file mode 100644 index 00000000000..363120899cb --- /dev/null +++ b/modules/idxIdSystem.md @@ -0,0 +1,22 @@ +## IDx User ID Submodule + +For assistance setting up your module please contact us at [prebid@idx.lat](prebid@idx.lat). + +### Prebid Params + +Individual params may be set for the IDx Submodule. +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'idx', + }] + } +}); +``` +## Parameter Descriptions for the `userSync` Configuration Section +The below parameters apply only to the IDx integration. + +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID of the module - `"idx"` | `"idx"` | diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 4e4576dbb14..f6c58a5a0bf 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -150,7 +150,13 @@ const USER_IDS_CONFIG = { 'quantcastId': { source: 'quantcast.com', atype: 1 - } + }, + + // IDx + 'idx': { + source: 'idx.lat', + atype: 1 + }, }; // this function will create an eid object for the given UserId sub-module diff --git a/test/spec/modules/idxIdSystem_spec.js b/test/spec/modules/idxIdSystem_spec.js new file mode 100644 index 00000000000..14cd9a88d13 --- /dev/null +++ b/test/spec/modules/idxIdSystem_spec.js @@ -0,0 +1,117 @@ +import { expect } from 'chai'; +import find from 'core-js-pure/features/array/find.js'; +import { config } from 'src/config.js'; +import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { storage, idxIdSubmodule } from 'modules/idxIdSystem.js'; + +const IDX_COOKIE_NAME = '_idx'; +const IDX_DUMMY_VALUE = 'idx value for testing'; +const IDX_COOKIE_STORED = '{ "idx": "' + IDX_DUMMY_VALUE + '" }'; +const ID_COOKIE_OBJECT = { id: IDX_DUMMY_VALUE }; +const IDX_COOKIE_OBJECT = { idx: IDX_DUMMY_VALUE }; + +function getConfigMock() { + return { + userSync: { + syncDelay: 0, + userIds: [{ + name: 'idx' + }] + } + } +} + +function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: {banner: {}, native: {}}, + sizes: [ + [300, 200], + [300, 600] + ], + bids: [{ + bidder: 'sampleBidder', + params: { placementId: 'banner-only-bidder' } + }] + }; +} + +describe('IDx ID System', () => { + let getDataFromLocalStorageStub, localStorageIsEnabledStub; + let getCookieStub, cookiesAreEnabledStub; + + beforeEach(() => { + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + }); + + afterEach(() => { + getDataFromLocalStorageStub.restore(); + localStorageIsEnabledStub.restore(); + getCookieStub.restore(); + cookiesAreEnabledStub.restore(); + }); + + describe('IDx: test "getId" method', () => { + it('provides the stored IDx if a cookie exists', () => { + getCookieStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED); + let idx = idxIdSubmodule.getId(); + expect(idx).to.deep.equal(ID_COOKIE_OBJECT); + }); + + it('provides the stored IDx if cookie is absent but present in local storage', () => { + getDataFromLocalStorageStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED); + let idx = idxIdSubmodule.getId(); + expect(idx).to.deep.equal(ID_COOKIE_OBJECT); + }); + + it('returns undefined if both cookie and local storage are empty', () => { + let idx = idxIdSubmodule.getId(); + expect(idx).to.be.undefined; + }) + }); + + describe('IDx: test "decode" method', () => { + it('provides the IDx from a stored object', () => { + expect(idxIdSubmodule.decode(ID_COOKIE_OBJECT)).to.deep.equal(IDX_COOKIE_OBJECT); + }); + + it('provides the IDx from a stored string', () => { + expect(idxIdSubmodule.decode(IDX_DUMMY_VALUE)).to.deep.equal(IDX_COOKIE_OBJECT); + }); + }); + + describe('requestBids hook', () => { + let adUnits; + + beforeEach(() => { + adUnits = [getAdUnitMock()]; + setSubmoduleRegistry([idxIdSubmodule]); + init(config); + config.setConfig(getConfigMock()); + getCookieStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED); + }); + + it('when a stored IDx exists it is added to bids', (done) => { + requestBidsHook(() => { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.idx'); + expect(bid.userId.idx).to.equal(IDX_DUMMY_VALUE); + const idxIdAsEid = find(bid.userIdAsEids, e => e.source == 'idx.lat'); + expect(idxIdAsEid).to.deep.equal({ + source: 'idx.lat', + uids: [{ + id: IDX_DUMMY_VALUE, + atype: 1, + }] + }); + }); + }); + done(); + }, { adUnits }); + }); + }); +}); From ab4f80a9f129614f9d23c4fc2c0a686dde58ab44 Mon Sep 17 00:00:00 2001 From: liranbaruch Date: Fri, 9 Oct 2020 00:32:20 +0300 Subject: [PATCH 0265/1476] Adding Test mode for the IronSource bidder (#5831) * Change ironsource to be lower case all over code * Add test mode to the IronSource bidder --- modules/ironsourceBidAdapter.js | 20 ++++++++++++++-- modules/ironsourceBidAdapter.md | 2 ++ .../spec/modules/ironsourceBidAdapter_spec.js | 24 +++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js index 34650d46a0f..922e7b94c5b 100644 --- a/modules/ironsourceBidAdapter.js +++ b/modules/ironsourceBidAdapter.js @@ -7,7 +7,11 @@ const SUPPORTED_AD_TYPES = [VIDEO]; const BIDDER_CODE = 'ironsource'; const BIDDER_VERSION = '4.0.0'; const TTL = 360; -const SELLER_ENDPOINT = 'https://hb.yellowblue.io/hb'; +const SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; +const MODES = { + PRODUCTION: 'hb', + TEST: 'hb-test' +} const SUPPORTED_SYNC_METHODS = { IFRAME: 'iframe', PIXEL: 'pixel' @@ -86,9 +90,10 @@ registerBidder(spec); */ function buildVideoRequest(bid, bidderRequest) { const sellerParams = generateParameters(bid, bidderRequest); + const {params} = bid; return { method: 'GET', - url: SELLER_ENDPOINT, + url: getEndpoint(params.testMode), data: sellerParams }; } @@ -169,6 +174,17 @@ function isSyncMethodAllowed(syncRule, bidderCode) { return isInclude && utils.contains(bidders, bidderCode); } +/** + * Get the seller endpoint + * @param testMode {boolean} + * @returns {string} + */ +function getEndpoint(testMode) { + return testMode + ? SELLER_ENDPOINT + MODES.TEST + : SELLER_ENDPOINT + MODES.PRODUCTION; +} + /** * Generate query parameters for the request * @param bid {bid} diff --git a/modules/ironsourceBidAdapter.md b/modules/ironsourceBidAdapter.md index 2f9e38b69e8..86756b08809 100644 --- a/modules/ironsourceBidAdapter.md +++ b/modules/ironsourceBidAdapter.md @@ -23,6 +23,7 @@ The adapter supports Video(instream). For the integration, IronSource returns co | `isOrg` | required | String | IronSource publisher Id provided by your IronSource representative | "56f91cd4d3e3660002000033" | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `ifa` | optional | String | The ID for advertisers (also referred to as "IDFA") | "XXX-XXX" +| `testMode` | optional | Boolean | This activates the test mode | false # Test Parameters ```javascript @@ -42,6 +43,7 @@ var adUnits = [ isOrg: '56f91cd4d3e3660002000033', // Required floorPrice: 2.00, // Optional ifa: 'XXX-XXX', // Optional + testMode: false // Optional } }] } diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js index cfdc51e0235..0c59dfef14b 100644 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -5,6 +5,7 @@ import { config } from 'src/config.js'; import { VIDEO } from '../../../src/mediaTypes.js'; const ENDPOINT = 'https://hb.yellowblue.io/hb'; +const TEST_ENDPOINT = 'https://hb.yellowblue.io/hb-test'; const TTL = 360; describe('ironsourceAdapter', function () { @@ -55,6 +56,21 @@ describe('ironsourceAdapter', function () { } ]; + const testModeBidRequests = [ + { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [[640, 480]], + 'params': { + 'isOrg': 'jdye8weeyirk00000001', + 'testMode': true + }, + 'bidId': '299ffc8cca0b87', + 'bidderRequestId': '1144f487e563f9', + 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', + } + ]; + const bidderRequest = { bidderCode: 'ironsource', } @@ -67,6 +83,14 @@ describe('ironsourceAdapter', function () { } }); + it('sends bid request to test ENDPOINT via GET', function () { + const requests = spec.buildRequests(testModeBidRequests, bidderRequest); + for (const request of requests) { + expect(request.url).to.equal(TEST_ENDPOINT); + expect(request.method).to.equal('GET'); + } + }); + it('should send the correct bid Id', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { From 32495f9b91f3e0eb29fc8c8fc2ea226ec9c9bedd Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 9 Oct 2020 14:04:29 +0300 Subject: [PATCH 0266/1476] Adtelligent: Add new alias (#5825) --- modules/adtelligentBidAdapter.js | 25 +++++++++++++------ .../modules/adtelligentBidAdapter_spec.js | 14 ++++++++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index e22aafb73fc..51138a2cac7 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -6,12 +6,23 @@ import { Renderer } from '../src/Renderer.js'; import find from 'core-js-pure/features/array/find.js'; const subdomainSuffixes = ['', 1, 2]; -const getUri = (function () { - let num = 0; - return function () { - return 'https://ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com/v2/auction/' +const AUCTION_PATH = '/v2/auction/'; +const PROTOCOL = 'https://'; +const HOST_GETTERS = { + default: (function () { + let num = 0; + return function () { + return 'ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com' + } + }()), + appaloosa: function () { + return 'hb.appaloosa.media' } -}()) +} +const getUri = function (bidderCode) { + let getter = HOST_GETTERS[bidderCode] || HOST_GETTERS['default']; + return PROTOCOL + getter() + AUCTION_PATH +} const OUTSTREAM_SRC = 'https://player.adtelligent.com/outstream-unit/2.01/outstream.min.js'; const BIDDER_CODE = 'adtelligent'; const OUTSTREAM = 'outstream'; @@ -21,7 +32,7 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, gvlid: 410, - aliases: ['onefiftytwomedia', 'selectmedia'], + aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { return !!utils.deepAccess(bid, 'params.aid'); @@ -82,7 +93,7 @@ export const spec = { data: Object.assign({}, tag, { BidRequests: bids }), adapterRequest, method: 'POST', - url: getUri() + url: getUri(adapterRequest.bidderCode) }; }) }, diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 9c694668703..62449771416 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -2,6 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/adtelligentBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; const EXPECTED_ENDPOINTS = [ 'https://ghb.adtelligent.com/v2/auction/', @@ -9,7 +10,9 @@ const EXPECTED_ENDPOINTS = [ 'https://ghb2.adtelligent.com/v2/auction/', 'https://ghb.adtelligent.com/v2/auction/' ]; - +const aliasEP = { + appaloosa: 'https://hb.appaloosa.media/v2/auction/' +}; const DISPLAY_REQUEST = { 'bidder': 'adtelligent', 'params': { @@ -250,6 +253,15 @@ describe('adtelligentBidAdapter', () => { expect(bidReqUrls).to.deep.equal(EXPECTED_ENDPOINTS); }) + it('makes correct host for aliases', () => { + for (const alias in aliasEP) { + const bidReq = deepClone(DISPLAY_REQUEST) + bidReq.bidder = alias; + const [bidderRequest] = spec.buildRequests([bidReq], { bidderCode: alias }); + expect(bidderRequest.url).to.equal(aliasEP[alias]); + } + }) + it('building requests as arrays', () => { expect(videoRequest).to.be.a('array'); expect(displayRequest).to.be.a('array'); From 844999459c5ee0ec2836a6056f2ed07071d8089e Mon Sep 17 00:00:00 2001 From: hamper Date: Fri, 9 Oct 2020 14:30:49 +0300 Subject: [PATCH 0267/1476] Add vuukle adapter (#5773) * add vuukle adapter * add readme * doc: add email --- modules/vuukleBidAdapter.js | 63 ++++++++++++++++++++++ modules/vuukleBidAdapter.md | 26 +++++++++ test/spec/modules/vuukleBidAdapter_spec.js | 59 ++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 modules/vuukleBidAdapter.js create mode 100644 modules/vuukleBidAdapter.md create mode 100644 test/spec/modules/vuukleBidAdapter_spec.js diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js new file mode 100644 index 00000000000..e9770b5e62e --- /dev/null +++ b/modules/vuukleBidAdapter.js @@ -0,0 +1,63 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'vuukle'; +const URL = 'https://pb.vuukle.com/adapter'; +const TIME_TO_LIVE = 360; + +export const spec = { + code: BIDDER_CODE, + + isBidRequestValid: function(bid) { + return true + }, + + buildRequests: function(bidRequests) { + const requests = bidRequests.map(function (bid) { + const parseSized = utils.parseSizesInput(bid.sizes); + const arrSize = parseSized[0].split('x'); + const params = { + url: encodeURIComponent(window.location.href), + sizes: JSON.stringify(parseSized), + width: arrSize[0], + height: arrSize[1], + params: JSON.stringify(bid.params), + rnd: Math.random(), + bidId: bid.bidId, + source: 'pbjs', + version: '$prebid.version$', + v: 1, + }; + + return { + method: 'GET', + url: URL, + data: params, + options: {withCredentials: false} + } + }); + + return requests; + }, + + interpretResponse: function(serverResponse, bidRequest) { + if (!serverResponse || !serverResponse.body || !serverResponse.body.ad) { + return []; + } + + const res = serverResponse.body; + const bidResponse = { + requestId: bidRequest.data.bidId, + cpm: res.cpm, + width: res.width, + height: res.height, + creativeId: res.creative_id, + currency: res.currency || 'USD', + netRevenue: true, + ttl: TIME_TO_LIVE, + ad: res.ad + }; + return [bidResponse]; + }, +} +registerBidder(spec); diff --git a/modules/vuukleBidAdapter.md b/modules/vuukleBidAdapter.md new file mode 100644 index 00000000000..ee7b54c6262 --- /dev/null +++ b/modules/vuukleBidAdapter.md @@ -0,0 +1,26 @@ +# Overview +``` +Module Name: Vuukle Bid Adapter +Module Type: Bidder Adapter +Maintainer: support@vuukle.com +``` + +# Description +Module that connects to Vuukle's server for bids. +Currently module supports only banner mediaType. + +# Test Parameters +``` + var adUnits = [{ + code: '/test/div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'vuukle', + params: {} + }] + }]; +``` diff --git a/test/spec/modules/vuukleBidAdapter_spec.js b/test/spec/modules/vuukleBidAdapter_spec.js new file mode 100644 index 00000000000..fdca4085c8e --- /dev/null +++ b/test/spec/modules/vuukleBidAdapter_spec.js @@ -0,0 +1,59 @@ +import { expect } from 'chai'; +import { spec } from 'modules/vuukleBidAdapter.js'; + +describe('vuukleBidAdapterTests', function() { + let bidRequestData = { + bids: [ + { + bidId: 'testbid', + bidder: 'vuukle', + params: { + test: 1 + }, + sizes: [[300, 250]] + } + ] + }; + let request = []; + + it('validate_pub_params', function() { + expect( + spec.isBidRequestValid({ + bidder: 'vuukle', + params: { + test: 1 + } + }) + ).to.equal(true); + }); + + it('validate_generated_params', function() { + request = spec.buildRequests(bidRequestData.bids); + let req_data = request[0].data; + + expect(req_data.bidId).to.equal('testbid'); + }); + + it('validate_response_params', function() { + let serverResponse = { + body: { + 'cpm': 0.01, + 'width': 300, + 'height': 250, + 'creative_id': '12345', + 'ad': 'test ad' + } + }; + + request = spec.buildRequests(bidRequestData.bids); + let bids = spec.interpretResponse(serverResponse, request[0]); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + expect(bid.ad).to.equal('test ad'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('12345'); + }); +}); From b6bfbef1db6c4533a0a40cf61490d47022df2b37 Mon Sep 17 00:00:00 2001 From: tadam75 Date: Fri, 9 Oct 2020 21:04:38 +0200 Subject: [PATCH 0268/1476] Handling video outstream in smartadserver adapter. (#5739) * Handling video outstream in smartadserver adapter. * Fixing the outstream example with the queue handler. Co-authored-by: tadam --- modules/smartadserverBidAdapter.js | 8 +- modules/smartadserverBidAdapter.md | 39 +++++++ .../modules/smartadserverBidAdapter_spec.js | 104 ++++++++++++++++++ 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 97dd43fc5ba..8462e749b91 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -83,10 +83,11 @@ export const spec = { w: size[0], h: size[1] })); - } else if (videoMediaType && videoMediaType.context === 'instream') { + } else if (videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream')) { // Specific attributes for instream. let playerSize = videoMediaType.playerSize[0]; - payload.isVideo = true; + payload.isVideo = videoMediaType.context === 'instream'; + payload.mediaType = VIDEO; payload.videoData = { videoProtocol: bid.params.video.protocol, playerWidth: playerSize[0], @@ -146,10 +147,11 @@ export const spec = { ttl: response.ttl }; - if (bidRequest.isVideo) { + if (bidRequest.mediaType === VIDEO) { bidResponse.mediaType = VIDEO; bidResponse.vastUrl = response.adUrl; bidResponse.vastXml = response.ad; + bidResponse.content = response.ad; } else { bidResponse.adUrl = response.adUrl; bidResponse.ad = response.ad; diff --git a/modules/smartadserverBidAdapter.md b/modules/smartadserverBidAdapter.md index c6f68363d7c..05e29359fd2 100644 --- a/modules/smartadserverBidAdapter.md +++ b/modules/smartadserverBidAdapter.md @@ -94,4 +94,43 @@ Please reach out to your Technical account manager for more information. } }] }; +``` + +## Outstream Video + +``` + var outstreamVideoAdUnit = { + code: 'test-div', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + renderer: { + url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: function (bid) { + bid.renderer.push(() => { + ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, + adResponse: bid + }); + }); + } + }, + bids: [{ + bidder: "smart", + params: { + domain: 'https://prg.smartadserver.com', + siteId: 207435, + pageId: 896536, + formatId: 85089, + bidfloor: 5, + video: { + protocol: 6, + startDelay: 1 + } + } + }] + }; ``` \ No newline at end of file diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 2de7cb2c9ff..e3bca240a47 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -425,6 +425,7 @@ describe('Smart bid adapter tests', function () { expect(bid.mediaType).to.equal('video'); expect(bid.vastUrl).to.equal('http://awesome.fake-vast.url'); expect(bid.vastXml).to.equal(''); + expect(bid.content).to.equal(''); expect(bid.width).to.equal(640); expect(bid.height).to.equal(480); expect(bid.creativeId).to.equal('zioeufg'); @@ -473,6 +474,109 @@ describe('Smart bid adapter tests', function () { }); }); + describe('Outstream video tests', function () { + afterEach(function () { + config.resetConfig(); + $$PREBID_GLOBAL$$.requestBids.removeAll(); + }); + + const OUTSTREAM_DEFAULT_PARAMS = [{ + adUnitCode: 'sas_43', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[800, 600]] // It seems prebid.js transforms the player size array into an array of array... + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '91', + target: 'test=prebid-outstream', + bidfloor: 0.430, + buId: '7579', + appName: 'Mozilla', + ckId: 43, + video: { + protocol: 7 + } + }, + requestId: 'efgh5679', + transactionId: 'zsfgzzga' + }]; + + var OUTSTREAM_BID_RESPONSE = { + body: { + cpm: 14, + width: 800, + height: 600, + creativeId: 'zioeufga', + currency: 'USD', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake-vast2.url', + ad: '', + cSyncUrl: 'http://awesome.fake2.csync.url' + } + }; + + it('Verify outstream video build request', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests(OUTSTREAM_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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('siteid').and.to.equal('1234'); + expect(requestContent).to.have.property('pageid').and.to.equal('5678'); + expect(requestContent).to.have.property('formatid').and.to.equal('91'); + expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR'); + expect(requestContent).to.have.property('bidfloor').and.to.equal(0.43); + expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid-outstream'); + expect(requestContent).to.have.property('tagId').and.to.equal('sas_43'); + expect(requestContent).to.not.have.property('pageDomain'); + expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; + 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('isVideo').and.to.equal(false); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(7); + expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(800); + expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(600); + }); + + it('Verify outstream parse response', function () { + const request = spec.buildRequests(OUTSTREAM_DEFAULT_PARAMS); + const bids = spec.interpretResponse(OUTSTREAM_BID_RESPONSE, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(14); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastUrl).to.equal('http://awesome.fake-vast2.url'); + expect(bid.vastXml).to.equal(''); + expect(bid.content).to.equal(''); + expect(bid.width).to.equal(800); + expect(bid.height).to.equal(600); + expect(bid.creativeId).to.equal('zioeufga'); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.requestId).to.equal(OUTSTREAM_DEFAULT_PARAMS[0].bidId); + + expect(function () { + spec.interpretResponse(OUTSTREAM_BID_RESPONSE, { + data: 'invalid Json' + }) + }).to.not.throw(); + }); + }); + describe('External ids tests', function () { it('Verify external ids in request and ids found', function () { config.setConfig({ From e39a812bb7c151b7ffeb835034bf055d041ba2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dlouh=C3=BD?= Date: Mon, 12 Oct 2020 11:12:27 +0200 Subject: [PATCH 0269/1476] add stroeerCoreBidAdapter (#5830) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add stroeerCoreBidAdapter * test correction * refactroring * add gvl id to spec Co-authored-by: Jakub Dlouhý Co-authored-by: karel koule Co-authored-by: Lukáš Havrlant --- modules/stroeerCoreBidAdapter.js | 196 +++++++ modules/stroeerCoreBidAdapter.md | 31 + .../modules/stroeerCoreBidAdapter_spec.js | 532 ++++++++++++++++++ 3 files changed, 759 insertions(+) create mode 100644 modules/stroeerCoreBidAdapter.js create mode 100644 modules/stroeerCoreBidAdapter.md create mode 100644 test/spec/modules/stroeerCoreBidAdapter_spec.js diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js new file mode 100644 index 00000000000..ec442f5125a --- /dev/null +++ b/modules/stroeerCoreBidAdapter.js @@ -0,0 +1,196 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const GVL_ID = 136; +const BIDDER_CODE = 'stroeerCore'; +const DEFAULT_HOST = 'hb.adscale.de'; +const DEFAULT_PATH = '/dsh'; +const DEFAULT_PORT = ''; +const FIVE_MINUTES_IN_SECONDS = 300; +const USER_SYNC_IFRAME_URL = 'https://js.adscale.de/pbsync.html'; + +const isSecureWindow = () => utils.getWindowSelf().location.protocol === 'https:'; +const isMainPageAccessible = () => getMostAccessibleTopWindow() === utils.getWindowTop(); + +function getTopWindowReferrer() { + try { + return utils.getWindowTop().document.referrer; + } catch (e) { + return utils.getWindowSelf().referrer; + } +} + +function getMostAccessibleTopWindow() { + let res = utils.getWindowSelf(); + + try { + while (utils.getWindowTop().top !== res && res.parent.location.href.length) { + res = res.parent; + } + } catch (ignore) { + } + + return res; +} + +function elementInView(elementId) { + const resolveElement = (elId) => { + const win = utils.getWindowSelf(); + + return win.document.getElementById(elId); + }; + + const visibleInWindow = (el, win) => { + const rect = el.getBoundingClientRect(); + const inView = (rect.top + rect.height >= 0) && (rect.top <= win.innerHeight); + + if (win !== win.parent) { + return inView && visibleInWindow(win.frameElement, win.parent); + } + + return inView; + }; + + try { + return visibleInWindow(resolveElement(elementId), utils.getWindowSelf()); + } catch (e) { + // old browser, element not found, cross-origin etc. + } + return undefined; +} + +function buildUrl({host: hostname = DEFAULT_HOST, port = DEFAULT_PORT, securePort, path: pathname = DEFAULT_PATH}) { + if (securePort) { + port = securePort; + } + + return utils.buildUrl({protocol: 'https', hostname, port, pathname}); +} + +function getGdprParams(gdprConsent) { + if (gdprConsent) { + const consentString = encodeURIComponent(gdprConsent.consentString || '') + const isGdpr = gdprConsent.gdprApplies ? 1 : 0; + + return `?gdpr=${isGdpr}&gdpr_consent=${consentString}` + } else { + return ''; + } +} + +export const spec = { + code: BIDDER_CODE, + gvlid: GVL_ID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: (function () { + const validators = []; + + const createValidator = (checkFn, errorMsgFn) => { + return (bidRequest) => { + if (checkFn(bidRequest)) { + return true; + } else { + utils.logError(`invalid bid: ${errorMsgFn(bidRequest)}`, 'ERROR'); + return false; + } + } + }; + + function isBanner(bidReq) { + return (!bidReq.mediaTypes && !bidReq.mediaType) || + (bidReq.mediaTypes && bidReq.mediaTypes.banner) || + bidReq.mediaType === BANNER; + } + + validators.push(createValidator((bidReq) => isBanner(bidReq), + bidReq => `bid request ${bidReq.bidId} is not a banner`)); + validators.push(createValidator((bidReq) => typeof bidReq.params === 'object', + bidReq => `bid request ${bidReq.bidId} does not have custom params`)); + validators.push(createValidator((bidReq) => utils.isStr(bidReq.params.sid), + bidReq => `bid request ${bidReq.bidId} does not have a sid string field`)); + + return function (bidRequest) { + return validators.every(f => f(bidRequest)); + } + }()), + + buildRequests: function (validBidRequests = [], bidderRequest) { + const anyBid = bidderRequest.bids[0]; + + const payload = { + id: bidderRequest.auctionId, + bids: [], + ref: getTopWindowReferrer(), + ssl: isSecureWindow(), + mpa: isMainPageAccessible(), + timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart) + }; + + const userIds = anyBid.userId; + + if (!utils.isEmpty(userIds)) { + payload.user = { + euids: userIds + }; + } + + const gdprConsent = bidderRequest.gdprConsent; + + if (gdprConsent && gdprConsent.consentString != null && gdprConsent.gdprApplies != null) { + payload.gdpr = { + consent: bidderRequest.gdprConsent.consentString, applies: bidderRequest.gdprConsent.gdprApplies + }; + } + + function bidSizes(bid) { + return utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes /* for prebid < 3 */ || []; + } + + validBidRequests.forEach(bid => { + payload.bids.push({ + bid: bid.bidId, sid: bid.params.sid, siz: bidSizes(bid), viz: elementInView(bid.adUnitCode) + }); + }); + + return { + method: 'POST', url: buildUrl(anyBid.params), data: payload + } + }, + + interpretResponse: function (serverResponse) { + const bids = []; + + if (serverResponse.body && typeof serverResponse.body === 'object') { + serverResponse.body.bids.forEach(bidResponse => { + bids.push({ + requestId: bidResponse.bidId, + cpm: bidResponse.cpm || 0, + width: bidResponse.width || 0, + height: bidResponse.height || 0, + ad: bidResponse.ad, + ttl: FIVE_MINUTES_IN_SECONDS, + currency: 'EUR', + netRevenue: true, + creativeId: '', + }); + }); + } + + return bids; + }, + + getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { + if (serverResponses.length > 0 && syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: USER_SYNC_IFRAME_URL + getGdprParams(gdprConsent) + }]; + } + + return []; + } +}; + +registerBidder(spec); diff --git a/modules/stroeerCoreBidAdapter.md b/modules/stroeerCoreBidAdapter.md new file mode 100644 index 00000000000..fe6e92057c6 --- /dev/null +++ b/modules/stroeerCoreBidAdapter.md @@ -0,0 +1,31 @@ +## Overview + +``` +Module Name: Stroeer Bidder Adapter +Module Type: Bidder Adapter +Maintainer: help@cz.stroeer-labs.com +``` + + +## Ad unit configuration for publishers + +```javascript +const adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'stroeerCore', + params: { + sid: "06b782cc-091b-4f53-9cd2-0291679aa1ac" + } + }] +}]; +``` +### Config Notes + +* Slot id (`sid`) is required. The adapter will ignore bid requests from prebid if `sid` is not provided. This must be in the decoded form. For example, "1234" as opposed to "MTM0ODA=". +* The server ignores dimensions that are not supported by the slot or by the platform (such as 987x123). diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js new file mode 100644 index 00000000000..dd02ebb7c8e --- /dev/null +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -0,0 +1,532 @@ +import {assert} from 'chai'; +import {spec} from 'modules/stroeerCoreBidAdapter.js'; +import * as utils from 'src/utils.js'; +import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; + +describe('stroeerCore bid adapter', function () { + let sandbox; + let fakeServer; + let bidderRequest; + let clock; + + beforeEach(() => { + bidderRequest = buildBidderRequest(); + sandbox = sinon.sandbox.create(); + fakeServer = sandbox.useFakeServer(); + clock = sandbox.useFakeTimers(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + function assertStandardFieldsOnBid(bidObject, bidId, ad, width, height, cpm) { + assert.propertyVal(bidObject, 'requestId', bidId); + assert.propertyVal(bidObject, 'ad', ad); + assert.propertyVal(bidObject, 'width', width); + assert.propertyVal(bidObject, 'height', height); + assert.propertyVal(bidObject, 'cpm', cpm); + assert.propertyVal(bidObject, 'currency', 'EUR'); + assert.propertyVal(bidObject, 'netRevenue', true); + assert.propertyVal(bidObject, 'creativeId', ''); + } + + const AUCTION_ID = utils.getUniqueIdentifierStr(); + + // Vendor user ids and associated data + const userIds = Object.freeze({ + criteoId: 'criteo-user-id', + digitrustid: { + data: { + id: 'encrypted-user-id==', + keyv: 4, + privacy: {optout: false}, + producer: 'ABC', + version: 2 + } + }, + lipb: { + lipbid: 'T7JiRRvsRAmh88', + segments: ['999'] + } + }); + + const buildBidderRequest = () => ({ + auctionId: AUCTION_ID, + bidderRequestId: 'bidder-request-id-123', + bidderCode: 'stroeerCore', + timeout: 5000, + auctionStart: 10000, + bids: [{ + bidId: 'bid1', + bidder: 'stroeerCore', + adUnitCode: 'div-1', + mediaTypes: { + banner: { + sizes: [[300, 600], [160, 60]] + } + }, + params: { + sid: 'NDA=' + }, + userId: userIds + }, { + bidId: 'bid2', + bidder: 'stroeerCore', + adUnitCode: 'div-2', + mediaTypes: { + banner: { + sizes: [[728, 90]], + } + }, + params: { + sid: 'ODA=' + }, + userId: userIds + }], + }); + + const buildBidderRequestPreVersion3 = () => { + const request = buildBidderRequest(); + request.bids.forEach((bid) => { + bid.sizes = bid.mediaTypes.banner.sizes; + delete bid.mediaTypes; + bid.mediaType = 'banner'; + }); + return request; + }; + + const buildBidderResponse = () => ({ + 'bids': [{ + 'bidId': 'bid1', 'cpm': 4.0, 'width': 300, 'height': 600, 'ad': '
tag1
', 'tracking': {'brandId': 123} + }, { + 'bidId': 'bid2', 'cpm': 7.3, 'width': 728, 'height': 90, 'ad': '
tag2
' + }] + }); + + const createWindow = (href, params = {}) => { + let {parent, referrer, top, frameElement, placementElements = []} = params; + + const protocol = (href.indexOf('https') === 0) ? 'https:' : 'http:'; + const win = { + frameElement, + parent, + top, + location: { + protocol, href + }, + document: { + createElement: function () { + return { + setAttribute: function () { + } + } + }, + referrer, + getElementById: id => placementElements.find(el => el.id === id) + } + }; + + win.self = win; + + if (!parent) { + win.parent = win; + } + + if (!top) { + win.top = win; + } + + return win; + }; + + function createElement(id, offsetTop = 0) { + return { + id, + getBoundingClientRect: function () { + return { + top: offsetTop, height: 1 + } + } + } + } + + function setupSingleWindow(sandBox, placementElements = [createElement('div-1', 17), createElement('div-2', 54)]) { + const win = createWindow('http://www.xyz.com/', { + parent: win, top: win, frameElement: createElement(undefined, 304), placementElements: placementElements + }); + + win.innerHeight = 200; + + sandBox.stub(utils, 'getWindowSelf').returns(win); + sandBox.stub(utils, 'getWindowTop').returns(win); + + return win; + } + + 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'}); + topWin.innerHeight = 800; + + const midWin = createWindow('http://www.abc.org/', {parent: topWin, top: topWin, frameElement: createElement()}); + midWin.innerHeight = 400; + + const win = createWindow('http://www.xyz.com/', { + parent: midWin, top: topWin, frameElement: createElement(undefined, 304), placementElements + }); + + win.innerHeight = 200; + + sandBox.stub(utils, 'getWindowSelf').returns(win); + sandBox.stub(utils, 'getWindowTop').returns(topWin); + + return {topWin, midWin, win}; + } + + it('should only support BANNER mediaType', function () { + assert.deepEqual(spec.supportedMediaTypes, [BANNER]); + }); + + describe('bid validation entry point', () => { + let bidRequest; + + beforeEach(() => { + bidRequest = buildBidderRequest().bids[0]; + }); + + it('should have \"isBidRequestValid\" function', () => { + assert.isFunction(spec.isBidRequestValid); + }); + + it('should pass a valid bid', () => { + assert.isTrue(spec.isBidRequestValid(bidRequest)); + }); + + it('should exclude bids without slot id param', () => { + bidRequest.params.sid = undefined; + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + + it('should exclude non-banner bids', () => { + delete bidRequest.mediaTypes.banner; + bidRequest.mediaTypes.video = { + playerSize: [640, 480] + }; + + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + + it('should exclude non-banner, pre-version 3 bids', () => { + delete bidRequest.mediaTypes; + bidRequest.mediaType = VIDEO; + assert.isFalse(spec.isBidRequestValid(bidRequest)); + }); + }); + + describe('build request entry point', () => { + it('should have \"buildRequests\" function', () => { + assert.isFunction(spec.buildRequests); + }); + + describe('url on server request info object', () => { + let win; + beforeEach(() => { + win = setupSingleWindow(sandbox); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should use hardcoded url as default endpoint', () => { + const bidReq = buildBidderRequest(); + let serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + assert.equal(serverRequestInfo.method, 'POST'); + assert.isObject(serverRequestInfo.data); + assert.equal(serverRequestInfo.url, 'https://hb.adscale.de/dsh'); + }); + + describe('should use custom url if provided', () => { + const samples = [{ + protocol: 'http:', params: {sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz'}, expected: 'https://other.com:234/xyz' + }, { + protocol: 'https:', params: {sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz'}, expected: 'https://other.com:234/xyz' + }, { + protocol: 'https:', + params: {sid: 'ODA=', host: 'other.com', port: '234', securePort: '871', path: '/xyz'}, + expected: 'https://other.com:871/xyz' + }, { + protocol: 'http:', params: {sid: 'ODA=', port: '234', path: '/xyz'}, expected: 'https://hb.adscale.de:234/xyz' + }, ]; + + samples.forEach(sample => { + it(`should use ${sample.expected} as endpoint when given params ${JSON.stringify(sample.params)} and protocol ${sample.protocol}`, + function () { + win.location.protocol = sample.protocol; + + const bidReq = buildBidderRequest(); + bidReq.bids[0].params = sample.params; + bidReq.bids.length = 1; + + let serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + assert.equal(serverRequestInfo.method, 'POST'); + assert.isObject(serverRequestInfo.data); + assert.equal(serverRequestInfo.url, sample.expected); + }); + }); + }); + }); + + describe('payload on server request info object', () => { + let topWin; + let win; + + let placementElements; + beforeEach(() => { + placementElements = [createElement('div-1', 17), createElement('div-2', 54)]; + ({ topWin, win } = setupNestedWindows(sandbox, placementElements)); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should have expected JSON structure', () => { + clock.tick(13500); + const bidReq = buildBidderRequest(); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const expectedTimeout = bidderRequest.timeout - (13500 - bidderRequest.auctionStart); + + assert.equal(expectedTimeout, 1500); + + const expectedJsonPayload = { + 'id': AUCTION_ID, + 'timeout': expectedTimeout, + 'ref': topWin.document.referrer, + 'mpa': true, + 'ssl': false, + 'bids': [{ + 'sid': 'NDA=', 'bid': 'bid1', 'siz': [[300, 600], [160, 60]], 'viz': true + }, { + 'sid': 'ODA=', 'bid': 'bid2', 'siz': [[728, 90]], 'viz': true + }], + 'user': { + 'euids': userIds + } + }; + + // trim away fields with undefined + const actualJsonPayload = JSON.parse(JSON.stringify(serverRequestInfo.data)); + + assert.deepEqual(actualJsonPayload, expectedJsonPayload); + }); + + it('should handle banner sizes for pre version 3', () => { + // Version 3 changes the way how banner sizes are accessed. + // We can support backwards compatibility with version 2.x + const bidReq = buildBidderRequestPreVersion3(); + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + assert.deepEqual(serverRequestInfo.data.bids[0].siz, [[300, 600], [160, 60]]); + assert.deepEqual(serverRequestInfo.data.bids[1].siz, [[728, 90]]); + }); + + describe('optional fields', () => { + it('should skip viz field when unable to determine visibility of placement', () => { + placementElements.length = 0; + const bidReq = buildBidderRequest(); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + assert.lengthOf(serverRequestInfo.data.bids, 2); + + for (let bid of serverRequestInfo.data.bids) { + assert.isUndefined(bid.viz); + } + }); + + it('should skip ref field when unable to determine document referrer', () => { + // i.e., empty if user came from bookmark, or web page using 'rel="noreferrer" on link, etc + buildBidderRequest(); + + const serverRequestInfo = spec.buildRequests(bidderRequest.bids, bidderRequest); + assert.lengthOf(serverRequestInfo.data.bids, 2); + + for (let bid of serverRequestInfo.data.bids) { + assert.isUndefined(bid.ref); + } + }); + + const gdprSamples = [{consentString: 'RG9ua2V5IEtvbmc=', gdprApplies: true}, {consentString: 'UGluZyBQb25n', gdprApplies: false}]; + gdprSamples.forEach((sample) => { + it(`should add GDPR info ${JSON.stringify(sample)} when provided`, () => { + const bidReq = buildBidderRequest(); + bidReq.gdprConsent = sample; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const actualGdpr = serverRequestInfo.data.gdpr; + assert.propertyVal(actualGdpr, 'applies', sample.gdprApplies); + assert.propertyVal(actualGdpr, 'consent', sample.consentString); + }); + }); + + const skippableGdprSamples = [{consentString: null, gdprApplies: true}, // + {consentString: 'UGluZyBQb25n', gdprApplies: null}, // + {consentString: null, gdprApplies: null}, // + {consentString: undefined, gdprApplies: true}, // + {consentString: 'UGluZyBQb25n', gdprApplies: undefined}, // + {consentString: undefined, gdprApplies: undefined}]; + skippableGdprSamples.forEach((sample) => { + it(`should not add GDPR info ${JSON.stringify(sample)} when one or more values are missing`, () => { + const bidReq = buildBidderRequest(); + bidReq.gdprConsent = sample; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const actualGdpr = serverRequestInfo.data.gdpr; + assert.isUndefined(actualGdpr); + }); + }); + + it('should be able to build without third party user id data', () => { + const bidReq = buildBidderRequest(); + bidReq.bids.forEach(bid => delete bid.userId); + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + assert.lengthOf(serverRequestInfo.data.bids, 2); + assert.notProperty(serverRequestInfo, 'uids'); + }); + }); + }); + }); + + describe('interpret response entry point', () => { + it('should have \"interpretResponse\" function', () => { + assert.isFunction(spec.interpretResponse); + }); + + const invalidResponses = ['', ' ', ' ', undefined, null]; + invalidResponses.forEach(sample => { + it('should ignore invalid responses (\"' + sample + '\") response', () => { + const result = spec.interpretResponse({body: sample}); + assert.isArray(result); + assert.lengthOf(result, 0); + }); + }); + + it('should interpret a standard response', () => { + const bidderResponse = buildBidderResponse(); + + const result = spec.interpretResponse({body: bidderResponse}); + assertStandardFieldsOnBid(result[0], 'bid1', '
tag1
', 300, 600, 4); + assertStandardFieldsOnBid(result[1], 'bid2', '
tag2
', 728, 90, 7.3); + }); + + it('should return empty array, when response contains no bids', () => { + const result = spec.interpretResponse({body: {bids: []}}); + assert.deepStrictEqual(result, []); + }); + }); + + describe('get user syncs entry point', () => { + let win; + beforeEach(() => { + win = setupSingleWindow(sandbox); + + // fake + win.document.createElement = function () { + const attrs = {}; + return { + setAttribute: (name, value) => { + attrs[name] = value + }, + getAttribute: (name) => attrs[name], + hasAttribute: (name) => attrs[name] !== undefined, + tagName: 'SCRIPT', + } + } + }); + + it('should have \"getUserSyncs\" function', () => { + assert.isFunction(spec.getUserSyncs); + }); + + describe('when iframe option is enabled', () => { + it('should perform user connect when there was a response', () => { + const expectedUrl = 'https://js.adscale.de/pbsync.html'; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, ['']); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + + it('should not perform user connect when there was no response', () => { + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, []); + + assert.deepStrictEqual(userSyncResponse, []); + }); + + describe('and gdpr consent is defined', () => { + describe('and gdpr applies', () => { + it('should place gdpr query param to the user sync url with value of 1', () => { + const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent='; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true}); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + }); + + describe('and gdpr does not apply', () => { + it('should place gdpr query param to the user sync url with zero value', () => { + const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=0&gdpr_consent='; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: false}); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + + describe('because consent does not specify it', () => { + it('should place gdpr query param to the user sync url with zero value', () => { + const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=0&gdpr_consent='; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {}); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + }); + }); + + describe('and consent string is defined', () => { + it('should pass consent string to gdpr consent query param', () => { + const consentString = 'consent_string'; + const expectedUrl = `https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent=${consentString}`; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true, consentString}); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + + it('should correctly escape invalid characters', () => { + const consentString = 'consent ?stri&ng'; + const expectedUrl = `https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent=consent%20%3Fstri%26ng`; + const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true, consentString}); + + assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + }); + }); + }); + }); + + describe('when iframe option is disabled', () => { + it('should not perform user connect even when there was a response', () => { + const userSyncResponse = spec.getUserSyncs({iframeEnabled: false}, ['']); + + assert.deepStrictEqual(userSyncResponse, []); + }); + + it('should not perform user connect when there was no response', () => { + const userSyncResponse = spec.getUserSyncs({iframeEnabled: false}, []); + + assert.deepStrictEqual(userSyncResponse, []); + }); + }); + }); +}); From ad41a193d05957f42eac56dd8b6772c328647cbd Mon Sep 17 00:00:00 2001 From: Niksok Date: Mon, 12 Oct 2020 16:41:56 +0300 Subject: [PATCH 0270/1476] Added the ability to send multiple bids in one ad request for mediaforce bid adapter (#5834) * Added the ability to send multiple bids in one ad request for mediaforce bid adapter * Fixes after review for mediaforce bid adapter --- modules/mediaforceBidAdapter.js | 78 +++++--- .../spec/modules/mediaforceBidAdapter_spec.js | 169 +++++++++++++++++- 2 files changed, 216 insertions(+), 31 deletions(-) diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index 7e5c06b1b48..7e29bb519b5 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -114,18 +114,28 @@ export const spec = { } const referer = bidderRequest && bidderRequest.refererInfo ? encodeURIComponent(bidderRequest.refererInfo.referer) : ''; + const auctionId = bidderRequest && bidderRequest.auctionId; + const timeout = bidderRequest && bidderRequest.timeout; const dnt = utils.getDNT() ? 1 : 0; - let requests = []; + const requestsMap = {}; + const requests = []; + let isTest = false; validBidRequests.forEach(bid => { + isTest = isTest || bid.params.is_test; let tagid = bid.params.placement_id; let bidfloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : 0; - let imp = []; let validImp = false; let impObj = { id: bid.bidId, tagid: tagid, - secure: 1, + secure: window.location.protocol === 'https' ? 1 : 0, bidfloor: bidfloor, + ext: { + mediaforce: { + transactionId: bid.transactionId + } + } + }; for (let mediaTypes in bid.mediaTypes) { switch (mediaTypes) { @@ -140,31 +150,47 @@ export const spec = { default: return; } } - validImp && imp.push(impObj); - let request = { - id: bid.transactionId, - site: { - page: referer, - ref: referer, - id: bid.params.publisher_id, - publisher: { - id: bid.params.publisher_id + let request = requestsMap[bid.params.publisher_id]; + if (!request) { + request = { + id: Math.round(Math.random() * 1e16).toString(16), + site: { + page: window.location.href, + ref: referer, + id: bid.params.publisher_id, + publisher: { + id: bid.params.publisher_id + }, }, - }, - device: { - ua: navigator.userAgent, - js: 1, - dnt: dnt, - language: getLanguage() - }, - imp - }; - requests.push({ - method: 'POST', - url: bid.params.is_test ? TEST_ENDPOINT_URL : ENDPOINT_URL, - data: JSON.stringify(request) - }); + device: { + ua: navigator.userAgent, + js: 1, + dnt: dnt, + language: getLanguage() + }, + ext: { + mediaforce: { + hb_key: auctionId + } + }, + tmax: timeout, + imp: [] + }; + requestsMap[bid.params.publisher_id] = request; + requests.push({ + method: 'POST', + url: ENDPOINT_URL, + data: request + }); + } + validImp && request.imp.push(impObj); + }); + requests.forEach((req) => { + if (isTest) { + req.url = TEST_ENDPOINT_URL; + } + req.data = JSON.stringify(req.data); }); return requests; }, diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index ee478acbc83..0b3271da770 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -100,6 +100,37 @@ describe('mediaforce bid adapter', function () { transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', }; + const multiBid = [ + { + publisher_id: 'pub123', + placement_id: '202', + }, + { + publisher_id: 'pub123', + placement_id: '203', + }, + { + publisher_id: 'pub124', + placement_id: '202', + }, + { + publisher_id: 'pub123', + placement_id: '203', + transactionId: '8df76688-1618-417a-87b1-60ad046841c9' + } + ].map(({publisher_id, placement_id, transactionId}) => { + return { + bidder: 'mediaforce', + params: {publisher_id, placement_id}, + mediaTypes: { + banner: { + sizes: [[300, 250], [600, 400]] + } + }, + transactionId: transactionId || 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + }); + const refererInfo = { referer: 'https://www.prebid.org', reachedTop: true, @@ -111,7 +142,9 @@ describe('mediaforce bid adapter', function () { const requestUrl = `${baseUrl}/header_bid`; const dnt = utils.getDNT() ? 1 : 0; - const secure = 1; + const secure = window.location.protocol === 'https' ? 1 : 0; + const pageUrl = window.location.href; + const timeout = 1500; it('should return undefined if no validBidRequests passed', function () { assert.equal(spec.buildRequests([]), undefined); @@ -127,18 +160,29 @@ describe('mediaforce bid adapter', function () { bid.params.bidfloor = 0.5; let bidRequests = [bid]; - let bidderRequest = {bids: bidRequests, refererInfo: refererInfo}; + let bidderRequest = { + bids: bidRequests, + refererInfo: refererInfo, + timeout: timeout, + auctionId: '210a474e-88f0-4646-837f-4253b7cf14fb' + }; let [request] = spec.buildRequests(bidRequests, bidderRequest); let data = JSON.parse(request.data); assert.deepEqual(data, { - id: bid.transactionId, + id: data.id, + tmax: timeout, + ext: { + mediaforce: { + hb_key: bidderRequest.auctionId + } + }, site: { id: bid.params.publisher_id, publisher: {id: bid.params.publisher_id}, ref: encodeURIComponent(refererInfo.referer), - page: encodeURIComponent(refererInfo.referer), + page: pageUrl, }, device: { ua: navigator.userAgent, @@ -150,6 +194,11 @@ describe('mediaforce bid adapter', function () { tagid: bid.params.placement_id, secure: secure, bidfloor: bid.params.bidfloor, + ext: { + mediaforce: { + transactionId: bid.transactionId + } + }, banner: {w: 300, h: 250}, native: { ver: '1.2', @@ -170,7 +219,7 @@ describe('mediaforce bid adapter', function () { assert.deepEqual(request, { method: 'POST', url: requestUrl, - data: '{"id":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b","site":{"page":"https%3A%2F%2Fwww.prebid.org","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"imp":[{"tagid":"202","secure":1,"bidfloor":0.5,"banner":{"w":300,"h":250},"native":{"ver":"1.2","request":{"assets":[{"required":1,"id":1,"title":{"len":800}},{"required":1,"id":3,"img":{"type":3,"w":300,"h":250}},{"required":1,"id":5,"data":{"type":1}}],"context":1,"plcmttype":1,"ver":"1.2"}}}]}', + data: '{"id":"' + data.id + '","site":{"page":"' + pageUrl + '","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"ext":{"mediaforce":{"hb_key":"210a474e-88f0-4646-837f-4253b7cf14fb"}},"tmax":1500,"imp":[{"tagid":"202","secure":' + secure + ',"bidfloor":0.5,"ext":{"mediaforce":{"transactionId":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b"}},"banner":{"w":300,"h":250},"native":{"ver":"1.2","request":{"assets":[{"required":1,"id":1,"title":{"len":800}},{"required":1,"id":3,"img":{"type":3,"w":300,"h":250}},{"required":1,"id":5,"data":{"type":1}}],"context":1,"plcmttype":1,"ver":"1.2"}}}]}', }); }); @@ -186,6 +235,116 @@ describe('mediaforce bid adapter', function () { let data = JSON.parse(request.data); assert.deepEqual(data.imp[0].banner, {w: 300, h: 600, format: [{w: 300, h: 250}]}); }); + + it('should return proper requests for multiple imps', function () { + let bidderRequest = { + bids: multiBid, + refererInfo: refererInfo, + timeout: timeout, + auctionId: '210a474e-88f0-4646-837f-4253b7cf14fb' + }; + + let requests = spec.buildRequests(multiBid, bidderRequest); + assert.equal(requests.length, 2); + requests.forEach((req) => { + req.data = JSON.parse(req.data); + }); + + assert.deepEqual(requests, [ + { + method: 'POST', + url: requestUrl, + data: { + id: requests[0].data.id, + tmax: timeout, + ext: { + mediaforce: { + hb_key: bidderRequest.auctionId + } + }, + site: { + id: 'pub123', + publisher: {id: 'pub123'}, + ref: encodeURIComponent(refererInfo.referer), + page: pageUrl, + }, + device: { + ua: navigator.userAgent, + dnt: dnt, + js: 1, + language: language, + }, + imp: [{ + tagid: '202', + secure: secure, + bidfloor: 0, + ext: { + mediaforce: { + transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + }, + banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + }, { + tagid: '203', + secure: secure, + bidfloor: 0, + ext: { + mediaforce: { + transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + }, + banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + }, { + tagid: '203', + secure: secure, + bidfloor: 0, + ext: { + mediaforce: { + transactionId: '8df76688-1618-417a-87b1-60ad046841c9' + } + }, + banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + }] + } + }, + { + method: 'POST', + url: requestUrl, + data: { + id: requests[1].data.id, + tmax: timeout, + ext: { + mediaforce: { + hb_key: bidderRequest.auctionId + } + }, + site: { + id: 'pub124', + publisher: {id: 'pub124'}, + ref: encodeURIComponent(refererInfo.referer), + page: pageUrl, + }, + device: { + ua: navigator.userAgent, + dnt: dnt, + js: 1, + language: language, + }, + imp: [{ + tagid: '202', + secure: secure, + bidfloor: 0, + ext: { + mediaforce: { + transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + }, + banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + }] + } + } + ]); + }); }); describe('interpretResponse() banner', function () { From 77293a49808ee328ef6847e1f43c95f7495c49de Mon Sep 17 00:00:00 2001 From: Thomas Ladd Date: Tue, 13 Oct 2020 03:05:55 -0500 Subject: [PATCH 0271/1476] Force refresh userId (#5819) * Added global function for refreshing user id's * Refactored submodule initialization to allow for refresh * Added submodule initialization when refreshing user id's * Refactored refresh parameter to be optional Refactored refresh user id's parameter to be optional where an empty list will result in all modules being refreshed. * Added unit tests for refresh user id's * Added single module refresh test * Test callback in refreshUserIds test * Remove zeotapIdPlus expiration on cookie in test because it caused it to intermittently fail Co-authored-by: chammon --- modules/userId/index.js | 154 ++++++++++++------ test/spec/modules/userId_spec.js | 102 ++++++++++++ .../spec/modules/zeotapIdPlusIdSystem_spec.js | 41 +++-- 3 files changed, 231 insertions(+), 66 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index ce442d4a2d3..b934fa2d8eb 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -109,6 +109,11 @@ * @property {(function|undefined)} callback - function that will return an id */ +/** + * @typedef {Object} RefreshUserIdsOptions + * @property {(string[]|undefined)} submoduleNames - submodules to refresh + */ + import find from 'core-js-pure/features/array/find.js'; import {config} from '../../src/config.js'; import events from '../../src/events.js'; @@ -466,6 +471,54 @@ function getUserIdsAsEids() { return createEidsArray(getCombinedSubmoduleIds(initializedSubmodules)); } +/** +* This function will be exposed in the global-name-space so that userIds can be refreshed after initialization. +* @param {RefreshUserIdsOptions} options +*/ +function refreshUserIds(options, callback) { + let submoduleNames = options ? options.submoduleNames : null; + if (!submoduleNames) { + submoduleNames = []; + } + + initializeSubmodulesAndExecuteCallbacks(function() { + let consentData = gdprDataHandler.getConsentData() + + const storedConsentData = getStoredConsentData(); + setStoredConsentData(consentData); + + // gdpr consent with purpose one is required, otherwise exit immediately + let {userIdModules, hasValidated} = validateGdprEnforcement(submodules, consentData); + if (!hasValidated && !hasGDPRConsent(consentData)) { + utils.logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); + return; + } + + let callbackSubmodules = []; + for (let submodule of userIdModules) { + if (submoduleNames.length > 0 && + submoduleNames.indexOf(submodule.submodule.name) === -1) { + continue; + } + + utils.logInfo(`${MODULE_NAME} - refreshing ${submodule.submodule.name}`); + populateSubmoduleId(submodule, consentData, storedConsentData, true); + + if (utils.isFn(submodule.callback)) { + callbackSubmodules.push(submodule); + } + } + + if (callbackSubmodules.length > 0) { + processSubmoduleCallbacks(callbackSubmodules); + } + + if (callback) { + callback(); + } + }); +} + /** * This hook returns updated list of submodules which are allowed to do get user id based on TCF 2 enforcement rules configured */ @@ -473,6 +526,57 @@ export const validateGdprEnforcement = hook('sync', function (submodules, consen return {userIdModules: submodules, hasValidated: consentData && consentData.hasValidated}; }, 'validateGdprEnforcement'); +function populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh) { + // There are two submodule configuration types to handle: storage or value + // 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method + // 2. value: pass directly to bids + if (submodule.config.storage) { + let storedId = getStoredValue(submodule.config.storage); + let response; + + let refreshNeeded = false; + if (typeof submodule.config.storage.refreshInSeconds === 'number') { + const storedDate = new Date(getStoredValue(submodule.config.storage, 'last')); + refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000); + } + + if (!storedId || refreshNeeded || forceRefresh || !storedConsentDataMatchesConsentData(storedConsentData, consentData)) { + // No id previously saved, or a refresh is needed, or consent has changed. Request a new id from the submodule. + response = submodule.submodule.getId(submodule.config, consentData, storedId); + } else if (typeof submodule.submodule.extendId === 'function') { + // If the id exists already, give submodule a chance to decide additional actions that need to be taken + response = submodule.submodule.extendId(submodule.config, storedId); + } + + if (utils.isPlainObject(response)) { + if (response.id) { + // A getId/extendId result assumed to be valid user id data, which should be saved to users local storage or cookies + setStoredValue(submodule, response.id); + storedId = response.id; + } + + if (typeof response.callback === 'function') { + // Save async callback to be invoked after auction + submodule.callback = response.callback; + } + } + + if (storedId) { + // cache decoded value (this is copied to every adUnit bid) + submodule.idObj = submodule.submodule.decode(storedId, submodule.config); + } + } else if (submodule.config.value) { + // cache decoded value (this is copied to every adUnit bid) + submodule.idObj = submodule.config.value; + } else { + const response = submodule.submodule.getId(submodule.config, consentData, undefined); + if (utils.isPlainObject(response)) { + if (typeof response.callback === 'function') { submodule.callback = response.callback; } + if (response.id) { submodule.idObj = submodule.submodule.decode(response.id, submodule.config); } + } + } +} + /** * @param {SubmoduleContainer[]} submodules * @param {ConsentData} consentData @@ -491,54 +595,7 @@ function initSubmodules(submodules, consentData) { } return userIdModules.reduce((carry, submodule) => { - // There are two submodule configuration types to handle: storage or value - // 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method - // 2. value: pass directly to bids - if (submodule.config.storage) { - let storedId = getStoredValue(submodule.config.storage); - let response; - - let refreshNeeded = false; - if (typeof submodule.config.storage.refreshInSeconds === 'number') { - const storedDate = new Date(getStoredValue(submodule.config.storage, 'last')); - refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000); - } - - if (!storedId || refreshNeeded || !storedConsentDataMatchesConsentData(storedConsentData, consentData)) { - // No id previously saved, or a refresh is needed, or consent has changed. Request a new id from the submodule. - response = submodule.submodule.getId(submodule.config, consentData, storedId); - } else if (typeof submodule.submodule.extendId === 'function') { - // If the id exists already, give submodule a chance to decide additional actions that need to be taken - response = submodule.submodule.extendId(submodule.config, storedId); - } - - if (utils.isPlainObject(response)) { - if (response.id) { - // A getId/extendId result assumed to be valid user id data, which should be saved to users local storage or cookies - setStoredValue(submodule, response.id); - storedId = response.id; - } - - if (typeof response.callback === 'function') { - // Save async callback to be invoked after auction - submodule.callback = response.callback; - } - } - - if (storedId) { - // cache decoded value (this is copied to every adUnit bid) - submodule.idObj = submodule.submodule.decode(storedId, submodule.config); - } - } else if (submodule.config.value) { - // cache decoded value (this is copied to every adUnit bid) - submodule.idObj = submodule.config.value; - } else { - const response = submodule.submodule.getId(submodule.config, consentData, undefined); - if (utils.isPlainObject(response)) { - if (typeof response.callback === 'function') { submodule.callback = response.callback; } - if (response.id) { submodule.idObj = submodule.submodule.decode(response.id, submodule.config); } - } - } + populateSubmoduleId(submodule, consentData, storedConsentData, false); carry.push(submodule); return carry; }, []); @@ -661,6 +718,7 @@ export function init(config) { // 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()).refreshUserIds = refreshUserIds; } // init config update listener to start the application diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 5dac5db7b54..ef393df82d5 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -311,6 +311,108 @@ describe('User ID', function () { expect(typeof (getGlobal()).getUserIdsAsEids).to.equal('function'); expect((getGlobal()).getUserIdsAsEids()).to.deep.equal(createEidsArray((getGlobal()).getUserIds())); }); + + it('pbjs.refreshUserIds refreshes', function() { + let sandbox = sinon.createSandbox(); + + let mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); + + let mockIdSystem = { + name: 'mockId', + decode: function(value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: mockIdCallback + }; + + setSubmoduleRegistry([mockIdSystem]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'mockId', + value: {id: {mockId: '1111'}} + }] + } + }); + expect(typeof (getGlobal()).refreshUserIds).to.equal('function'); + + getGlobal().getUserIds(); // force initialization + + // update config so that getId will be called + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'mockId', + storage: {name: 'mockid', type: 'cookie'}, + }] + } + }); + + getGlobal().refreshUserIds(); + expect(mockIdCallback.callCount).to.equal(1); + }); + + it('pbjs.refreshUserIds refreshes single', function() { + coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('REFRESH', '', EXPIRED_COOKIE_DATE); + + let sandbox = sinon.createSandbox(); + let mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); + let refreshUserIdsCallback = sandbox.stub(); + + let mockIdSystem = { + name: 'mockId', + decode: function(value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: mockIdCallback + }; + + let refreshedIdCallback = sandbox.stub().returns({id: {'REFRESH': '1111'}}); + + let refreshedIdSystem = { + name: 'refreshedId', + decode: function(value) { + return { + 'refresh': value['REFRESH'] + }; + }, + getId: refreshedIdCallback + }; + + setSubmoduleRegistry([refreshedIdSystem, mockIdSystem]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [ + { + name: 'mockId', + storage: {name: 'MOCKID', type: 'cookie'}, + }, + { + name: 'refreshedId', + storage: {name: 'refreshedid', type: 'cookie'}, + } + ] + } + }); + + getGlobal().getUserIds(); // force initialization + + getGlobal().refreshUserIds({submoduleNames: 'refreshedId'}, refreshUserIdsCallback); + + expect(refreshedIdCallback.callCount).to.equal(2); + expect(mockIdCallback.callCount).to.equal(1); + expect(refreshUserIdsCallback.callCount).to.equal(1); + }); }); describe('Opt out', function () { diff --git a/test/spec/modules/zeotapIdPlusIdSystem_spec.js b/test/spec/modules/zeotapIdPlusIdSystem_spec.js index 54082618120..4f9e691f12e 100644 --- a/test/spec/modules/zeotapIdPlusIdSystem_spec.js +++ b/test/spec/modules/zeotapIdPlusIdSystem_spec.js @@ -34,26 +34,23 @@ function getAdUnitMock(code = 'adUnit-code') { }; } -describe('Zeotap ID System', function() { - let getDataFromLocalStorageStub, localStorageIsEnabledStub; - let getCookieStub, cookiesAreEnabledStub; - beforeEach(function () { - getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - getCookieStub = sinon.stub(storage, 'getCookie'); - cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); - }); +function unsetCookie() { + storage.setCookie(ZEOTAP_COOKIE_NAME, ''); +} - afterEach(function () { - getDataFromLocalStorageStub.restore(); - localStorageIsEnabledStub.restore(); - getCookieStub.restore(); - cookiesAreEnabledStub.restore(); - }); +function unsetLocalStorage() { + storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ''); +} +describe('Zeotap ID System', function() { describe('test method: getId', function() { + afterEach(() => { + unsetCookie(); + unsetLocalStorage(); + }); + it('provides the stored Zeotap id if a cookie exists', function() { - getCookieStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); + storage.setCookie(ZEOTAP_COOKIE_NAME, ENCODED_ZEOTAP_COOKIE); let id = zeotapIdPlusSubmodule.getId(); expect(id).to.deep.equal({ id: ENCODED_ZEOTAP_COOKIE @@ -61,7 +58,7 @@ describe('Zeotap ID System', function() { }); it('provides the stored Zeotap id if cookie is absent but present in local storage', function() { - getDataFromLocalStorageStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); + storage.setDataInLocalStorage(ZEOTAP_COOKIE_NAME, ENCODED_ZEOTAP_COOKIE); let id = zeotapIdPlusSubmodule.getId(); expect(id).to.deep.equal({ id: ENCODED_ZEOTAP_COOKIE @@ -99,10 +96,18 @@ describe('Zeotap ID System', function() { beforeEach(function() { adUnits = [getAdUnitMock()]; + storage.setCookie( + ZEOTAP_COOKIE_NAME, + ENCODED_ZEOTAP_COOKIE + ); setSubmoduleRegistry([zeotapIdPlusSubmodule]); init(config); config.setConfig(getConfigMock()); - getCookieStub.withArgs(ZEOTAP_COOKIE_NAME).returns(ENCODED_ZEOTAP_COOKIE); + }); + + afterEach(function() { + unsetCookie(); + unsetLocalStorage(); }); it('when a stored Zeotap ID exists it is added to bids', function(done) { From 511e16a0fdeb93bf18a9f9d6b19c1c0d0b0f2e29 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 13 Oct 2020 14:50:26 +0300 Subject: [PATCH 0272/1476] Hybrid adapter. Added support In-Image format (#5754) * Added Hybrid.ai adapter * Is used 'find' from 'core-js/library/fn/array/find' instead Array.find * Fixed missing file extensions for imports * Typo fixed * Fixed missing file extensions for imports * Added support In-Image format * Added more test * Fixed errors of lint * Deleted debug line Co-authored-by: s.shevtsov --- modules/hybridBidAdapter.js | 59 ++++++- modules/hybridBidAdapter.md | 184 +++++++++++++++++++++ test/spec/modules/hybridBidAdapter_spec.js | 70 +++++++- 3 files changed, 307 insertions(+), 6 deletions(-) diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 5671756e0b2..dd55483ef33 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -10,12 +10,14 @@ const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb'; const TRAFFIC_TYPE_WEB = 1; const PLACEMENT_TYPE_BANNER = 1; const PLACEMENT_TYPE_VIDEO = 2; +const PLACEMENT_TYPE_IN_IMAGE = 3; const TTL = 60; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const placementTypes = { 'banner': PLACEMENT_TYPE_BANNER, - 'video': PLACEMENT_TYPE_VIDEO + 'video': PLACEMENT_TYPE_VIDEO, + 'inImage': PLACEMENT_TYPE_IN_IMAGE }; function buildBidRequests(validBidRequests) { @@ -26,7 +28,8 @@ function buildBidRequests(validBidRequests) { transactionId: validBidRequest.transactionId, sizes: validBidRequest.sizes, placement: placementTypes[params.placement], - placeId: params.placeId + placeId: params.placeId, + imageUrl: params.imageUrl || '' }; return bidRequest; @@ -94,6 +97,33 @@ function buildBid(bidData) { bid.renderer = createRenderer(bid); } } + } else if (bidData.placement === PLACEMENT_TYPE_IN_IMAGE) { + bid.mediaType = BANNER; + bid.inImageContent = { + content: { + content: bidData.content, + actionUrls: {} + } + }; + let actionUrls = bid.inImageContent.content.actionUrls; + actionUrls.loadUrls = bidData.inImage.loadtrackers || []; + actionUrls.impressionUrls = bidData.inImage.imptrackers || []; + actionUrls.scrollActUrls = bidData.inImage.startvisibilitytrackers || []; + actionUrls.viewUrls = bidData.inImage.viewtrackers || []; + actionUrls.stopAnimationUrls = bidData.inImage.stopanimationtrackers || []; + actionUrls.closeBannerUrls = bidData.inImage.closebannertrackers || []; + + if (bidData.inImage.but) { + let inImageOptions = bid.inImageContent.content.inImageOptions = {}; + inImageOptions.hasButton = true; + inImageOptions.buttonLogoUrl = bidData.inImage.but_logo; + inImageOptions.buttonProductUrl = bidData.inImage.but_prod; + inImageOptions.buttonHead = bidData.inImage.but_head; + inImageOptions.buttonHeadColor = bidData.inImage.but_head_colour; + inImageOptions.dynparams = bidData.inImage.dynparams || {}; + } + + bid.ad = wrapAd(bid, bidData); } else { bid.ad = bidData.content; bid.mediaType = BANNER; @@ -116,6 +146,30 @@ function hasVideoMandatoryParams(mediaTypes) { return isHasVideoContext && isPlayerSize; } +function wrapAd(bid, bidData) { + return ` + + + + + + + + +
+ + + `; +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], @@ -133,6 +187,7 @@ export const spec = { !!bid.params.placement && ( (getMediaTypeFromBid(bid) === BANNER && bid.params.placement === 'banner') || + (getMediaTypeFromBid(bid) === BANNER && bid.params.placement === 'inImage' && !!bid.params.imageUrl) || (getMediaTypeFromBid(bid) === VIDEO && bid.params.placement === 'video' && hasVideoMandatoryParams(bid.mediaTypes)) ) ); diff --git a/modules/hybridBidAdapter.md b/modules/hybridBidAdapter.md index 245f010970a..098d8642415 100644 --- a/modules/hybridBidAdapter.md +++ b/modules/hybridBidAdapter.md @@ -52,3 +52,187 @@ var adUnits = [{ }]; ``` +# Sample In-Image Ad Unit + +```js +var adUnits = [{ + code: 'test-div', + mediaTypes: { + banner: { + sizes: [0, 0] + } + }, + bids: [{ + bidder: "hybrid", + params: { + placement: "inImage", + placeId: "102030405060708090000020", + imageUrl: "https://hybrid.ai/images/image.jpg" + } + }] +}]; +``` + +# Example page with In-Image + +```html + + + + + Prebid.js Banner Example + + + + + +

Prebid.js InImage Banner Test

+
+ + +
+ + +``` + +# Example page with In-Image and GPT + +```html + + + + + Prebid.js Banner Example + + + + + + +

Prebid.js Banner Ad Unit Test

+
+ + +
+ + +``` diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index e5ad878c9b1..d1899ef3d81 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -25,16 +25,22 @@ describe('Hybrid.ai Adapter', function() { placeId: PLACE_ID, placement: 'video' } + const inImageMandatoryParams = { + placeId: PLACE_ID, + placement: 'inImage', + imageUrl: 'https://hybrid.ai/images/image.jpg' + } const validBidRequests = [ getSlotConfigs({ banner: {} }, bannerMandatoryParams), - getSlotConfigs({ video: {playerSize: [[640, 480]]} }, videoMandatoryParams) + getSlotConfigs({ video: {playerSize: [[640, 480]], context: 'outstream'} }, videoMandatoryParams), + getSlotConfigs({ banner: {sizes: [0, 0]} }, inImageMandatoryParams) ] describe('isBidRequestValid method', function() { describe('returns true', function() { describe('when banner slot config has all mandatory params', () => { - describe('and placement has the correct value', function() { + describe('and banner placement has the correct value', function() { const slotConfig = getSlotConfigs( - { banner: {} }, + {banner: {}}, { placeId: PLACE_ID, placement: 'banner' @@ -43,6 +49,22 @@ describe('Hybrid.ai Adapter', function() { const isBidRequestValid = spec.isBidRequestValid(slotConfig) expect(isBidRequestValid).to.equal(true) }) + describe('and In-Image placement has the correct value', function() { + const slotConfig = getSlotConfigs( + { + banner: { + sizes: [[0, 0]] + } + }, + { + placeId: PLACE_ID, + placement: 'inImage', + imageUrl: 'imageUrl' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) describe('when video slot has all mandatory params.', function() { it('should return true, when video mediatype object are correct.', function() { const slotConfig = getSlotConfigs( @@ -84,6 +106,15 @@ describe('Hybrid.ai Adapter', function() { ) expect(isBidRequestValid).to.equal(false) }) + it('does not have the imageUrl.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placeId: PLACE_ID, + placement: 'inImage' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) it('does not have a the correct placement.', function() { const isBidRequestValid = spec.isBidRequestValid( createSlotconfig({ @@ -240,6 +271,36 @@ describe('Hybrid.ai Adapter', function() { expect(bids[0].netRevenue).to.equal(true) expect(typeof bids[0].ad).to.equal('string') }) + it('should return a In-Image bid', function() { + const request = spec.buildRequests([validBidRequests[2]], bidderRequest) + const serverResponse = { + body: { + bids: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: 'html', + inImage: { + actionUrls: {} + }, + width: 100, + height: 100, + ttl: 360 + } + ] + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2df8c0733f284e') + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(100) + expect(bids[0].height).to.equal(100) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(typeof bids[0].ad).to.equal('string') + }) }) describe('the bid is a video', function() { it('should return a video bid', function() { @@ -253,7 +314,8 @@ describe('Hybrid.ai Adapter', function() { currency: 'USD', content: 'html', width: 100, - height: 100 + height: 100, + transactionId: '31a58515-3634-4e90-9c96-f86196db1459' } ] } From a2c63e1b0217314ad1e379623d2b73a8e4be8430 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Tue, 13 Oct 2020 06:03:28 -0700 Subject: [PATCH 0273/1476] PubMatic Analytics: internal kgpv param support in analytics (#5849) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * not passing GDPR data in analytics * adding support for OpenWrap regex support * added unit test cases --- modules/pubmaticAnalyticsAdapter.js | 17 ++- .../modules/pubmaticAnalyticsAdapter_spec.js | 101 ++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 04c6606a299..dfe183076f4 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -152,6 +152,7 @@ function parseBidResponse(bid) { 'mediaType', 'params', 'mi', + 'regexPattern', () => bid.regexPattern || undefined, 'partnerImpId', // partner impression ID 'dimensions', () => utils.pick(bid, [ 'width', @@ -166,6 +167,18 @@ function getDomainFromUrl(url) { return a.hostname; } +function getValueForKgpv(bid, adUnitId) { + if (bid.params.regexPattern) { + return bid.params.regexPattern; + } else if (bid.bidResponse && bid.bidResponse.regexPattern) { + return bid.bidResponse.regexPattern; + } else if (bid.params.kgpv) { + return bid.params.kgpv; + } else { + return adUnitId; + } +} + function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { @@ -174,7 +187,7 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'pn': bid.bidder, 'bidid': bid.bidId, 'db': bid.bidResponse ? 0 : 1, - 'kgpv': bid.params.kgpv ? bid.params.kgpv : adUnitId, + 'kgpv': getValueForKgpv(bid, adUnitId), 'kgpsv': bid.params.kgpv ? bid.params.kgpv : adUnitId, 'psz': bid.bidResponse ? (bid.bidResponse.dimensions.width + 'x' + bid.bidResponse.dimensions.height) : '0x0', 'eg': bid.bidResponse ? bid.bidResponse.bidGrossCpmUSD : 0, @@ -268,7 +281,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += '&pn=' + enc(winningBid.bidder); pixelURL += '&en=' + enc(winningBid.bidResponse.bidPriceUSD); pixelURL += '&eg=' + enc(winningBid.bidResponse.bidGrossCpmUSD); - pixelURL += '&kgpv=' + enc(winningBid.params.kgpv || adUnitId); + pixelURL += '&kgpv=' + enc(getValueForKgpv(winningBid, adUnitId)); pixelURL += '&piid=' + enc(winningBid.bidResponse.partnerImpId || EMPTY_STRING); ajax( pixelURL, diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index d38037f40a1..91c6ec1379d 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -696,5 +696,106 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].ocpm).to.equal(100); expect(data.s[1].ps[0].ocry).to.equal('JPY'); }); + + it('Logger: regexPattern in bid.params', function() { + const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); + BID_REQUESTED_COPY.bids[1].params.regexPattern = '*'; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, BID_REQUESTED_COPY); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, BID2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker + let request = requests[2]; // logger is executed late, trackers execute first + 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].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); + expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); + expect(data.s[1].ps[0].db).to.equal(0); + expect(data.s[1].ps[0].kgpv).to.equal('*'); + expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].psz).to.equal('728x90'); + expect(data.s[1].ps[0].eg).to.equal(1.52); + expect(data.s[1].ps[0].en).to.equal(1.52); + expect(data.s[1].ps[0].di).to.equal('the-deal-id'); + expect(data.s[1].ps[0].dc).to.equal('PMP'); + expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].l1).to.equal(3214); + expect(data.s[1].ps[0].l2).to.equal(0); + expect(data.s[1].ps[0].ss).to.equal(1); + expect(data.s[1].ps[0].t).to.equal(0); + expect(data.s[1].ps[0].wb).to.equal(0); // bidPriceUSD is not getting set as currency module is not added, so unable to set wb to 1 + 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'); + // respective tracker slot + let firstTracker = requests[1].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + data = {}; + firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); + expect(data.kgpv).to.equal('*'); + }); + + it('Logger: regexPattern in bid.bidResponse', function() { + const BID2_COPY = utils.deepClone(BID2); + BID2_COPY.regexPattern = '*'; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, BID2_COPY); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, Object.assign({}, BID2_COPY, { + 'status': 'rendered' + })); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker + let request = requests[2]; // logger is executed late, trackers execute first + 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].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); + expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); + expect(data.s[1].ps[0].db).to.equal(0); + expect(data.s[1].ps[0].kgpv).to.equal('*'); + expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].psz).to.equal('728x90'); + expect(data.s[1].ps[0].eg).to.equal(1.52); + expect(data.s[1].ps[0].en).to.equal(1.52); + expect(data.s[1].ps[0].di).to.equal('the-deal-id'); + expect(data.s[1].ps[0].dc).to.equal('PMP'); + expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].l1).to.equal(3214); + expect(data.s[1].ps[0].l2).to.equal(0); + expect(data.s[1].ps[0].ss).to.equal(1); + expect(data.s[1].ps[0].t).to.equal(0); + expect(data.s[1].ps[0].wb).to.equal(0); // bidPriceUSD is not getting set as currency module is not added, so unable to set wb to 1 + 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'); + // respective tracker slot + let firstTracker = requests[1].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + data = {}; + firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); + expect(data.kgpv).to.equal('*'); + }); }); }); From 666c82fe44834043173d5201eca87a43a741f6a8 Mon Sep 17 00:00:00 2001 From: mmprebid <67095277+mmprebid@users.noreply.github.com> Date: Wed, 14 Oct 2020 02:23:49 +0530 Subject: [PATCH 0274/1476] TrueReach Bidder Adapter: Added User Sync Support (#5846) * 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 Co-authored-by: Nitin Kumar Co-authored-by: arnav Co-authored-by: arnav --- modules/truereachBidAdapter.js | 20 ++++++++++++++++++ test/spec/modules/truereachBidAdapter_spec.js | 21 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/modules/truereachBidAdapter.js b/modules/truereachBidAdapter.js index 2de7edbc04d..92f9cc9951e 100755 --- a/modules/truereachBidAdapter.js +++ b/modules/truereachBidAdapter.js @@ -79,6 +79,26 @@ export const spec = { return bidResponses; }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = [] + + var gdprParams = ''; + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `?gdpr_consent=${gdprConsent.consentString}`; + } + } + + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: 'http://ads.momagic.com/jsp/usersync.jsp' + gdprParams + }); + } + return syncs; + } }; diff --git a/test/spec/modules/truereachBidAdapter_spec.js b/test/spec/modules/truereachBidAdapter_spec.js index 5fe053ceb91..36441722648 100644 --- a/test/spec/modules/truereachBidAdapter_spec.js +++ b/test/spec/modules/truereachBidAdapter_spec.js @@ -81,4 +81,25 @@ describe('truereachBidAdapterTests', function () { expect(bid.netRevenue).to.equal(false); expect(bid.meta.advertiserDomains[0]).to.equal('https://www.momagic.com/'); }); + + describe('user_sync', function() { + const user_sync_url = 'http://ads.momagic.com/jsp/usersync.jsp'; + it('register_iframe_pixel_if_iframeEnabled_is_true', function() { + let syncs = spec.getUserSyncs( + {iframeEnabled: true} + ); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal(user_sync_url); + }); + + it('if_pixelEnabled_is_true', function() { + let syncs = spec.getUserSyncs( + {pixelEnabled: true} + ); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(0); + }); + }); }); From 1b76f45f4f380bfd3574dcabb06b5acfacae4ef6 Mon Sep 17 00:00:00 2001 From: kero75 <72736014+kero75@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:56:01 +0200 Subject: [PATCH 0275/1476] Don't parse the querystring when extracting the protocolHost (#5851) Co-authored-by: Karim El Shabrawy --- modules/criteoIdSystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 30641b6c225..83bc773cb30 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -31,7 +31,7 @@ function areCookiesWriteable() { } function extractProtocolHost (url, returnOnlyHost = false) { - const parsedUrl = utils.parseUrl(url) + const parsedUrl = utils.parseUrl(url, {noDecodeWholeURL: true}) return returnOnlyHost ? `${parsedUrl.hostname}` : `${parsedUrl.protocol}://${parsedUrl.hostname}${parsedUrl.port ? ':' + parsedUrl.port : ''}/`; From 3bc0bf9bf99642af954da18417190662b78eb193 Mon Sep 17 00:00:00 2001 From: Andrea Cannuni <57228257+ACannuniRP@users.noreply.github.com> Date: Tue, 13 Oct 2020 21:57:58 +0100 Subject: [PATCH 0276/1476] Add rubicon size 548 (#5853) * Rubicon Adapter: Add multiple sizes to sizeMap * Add new size 500x1000 (ID: 548) in Rubicon Adapter Co-authored-by: Bret Gorsline --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index d4411a82e8f..069a7e7ead5 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -102,7 +102,8 @@ var sizeMap = { 274: '1800x200', 278: '320x500', 282: '320x400', - 288: '640x380' + 288: '640x380', + 548: '500x1000' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From c251ad5207ed2260ffc8069d5101c7a8ee5bb351 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 13 Oct 2020 17:12:49 -0400 Subject: [PATCH 0277/1476] PR Review Process: Adding RTD, UserId. General modernization. (#5829) * Adding RTD, UserId. General modernization. * Update PR_REVIEW.md Co-authored-by: Scott Menzer Co-authored-by: Scott Menzer --- PR_REVIEW.md | 82 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index d7703cf20ae..1dd8fbcec0d 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -6,38 +6,28 @@ If the PR is for a standard bid adapter or a standard analytics adapter, just th For modules and core platform updates, the initial reviewer should request an additional team member to review as a sanity check. Merge should only happen when the PR has 2 `LGTM` from the core team and a documentation PR if required. ### General PR review Process +- All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. - Checkout the branch (these instructions are available on the github PR page as well). - Verify PR is a single change type. Example, refactor OR bugfix. If more than 1 type, ask submitter to break out requests. -- Verify code under review has at least 80% unit test coverage. If legacy code has no unit test coverage, ask for unit tests to be included in the PR. +- Verify code under review has at least 80% unit test coverage. If legacy code doesn't have enough unit test coverage, require that additional unit tests to be included in the PR. - Verify tests are green in Travis-ci + local build by running `gulp serve` | `gulp test` - Verify no code quality violations are present from linting (should be reported in terminal) +- Make sure the code is not setting cookies or localstorage directly -- it must use the `StorageManager`. - Review for obvious errors or bad coding practice / use best judgement here. - If the change is a new feature / change to core prebid.js - review the change with a Tech Lead on the project and make sure they agree with the nature of change. - If the change results in needing updates to docs (such as public API change, module interface etc), add a label for "needs docs" and inform the submitter they must submit a docs PR to update the appropriate area of Prebid.org **before the PR can merge**. Help them with finding where the docs are located on prebid.org if needed. - - Below are some examples of bidder specific updates that should require docs update (in their dev-docs/bidders/BIDDER.md file): - - If they support the GDPR consentManagement module and TCF1, add `gdpr_supported: true` - - If they support the GDPR consentManagement module and TCF2, add `tcf2_supported: true` - - If they support the US Privacy consentManagementUsp module, add `usp_supported: true` - - If they support one or more userId modules, add `userId: (list of supported vendors)` - - If they support video and/or native mediaTypes add `media_types: video, native`. Note that display is added by default. If you don't support display, add "no-display" as the first entry, e.g. `media_types: no-display, native` - - If they support COPPA, add `coppa_supported: true` - - If they support SChain, add `schain_supported: true` - - If their bidder doesn't work well with safeframed creatives, add `safeframes_ok: false`. This will alert publishers to not use safeframed creatives when creating the ad server entries for their bidder. - - If they're setting a deal ID in some scenarios, add `bidder_supports_deals: true` - - If they have an IAB Global Vendor List ID, add `gvl_id: ID`. There's no default. -- If all above is good, add a `LGTM` comment and request 1 additional core member to review. -- Once there is 2 `LGTM` on the PR, merge to master -- Ask the submitter to add a PR for documentation if applicable. +- If all above is good, add a `LGTM` comment and, if the change is in PBS-core or is an important module like the prebidServerBidAdapter, request 1 additional core member to review. +- Once there are 2 `LGTM` on the PR, merge to master - Add a line into the [draft release](https://github.com/prebid/Prebid.js/releases) notes for this submission. If no draft release is available, create one using [this template]( https://gist.github.com/mkendall07/c3af6f4691bed8a46738b3675cb5a479) -- Add the PR to the appropriate project board (I.E. 1.23.0 Release) for the week, [see](https://github.com/prebid/Prebid.js/projects) -### New Adapter or updates to adapter process -- Follow steps above for general review process. In addition, please verify the following: +### Reviewing a New or Updated Bid Adapter +Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/bidder-adaptor.html + +Follow steps above for general review process. In addition, please verify the following: - Verify that bidder has submitted valid bid params and that bids are being received. - Verify that bidder is not manipulating the prebid.js auction in any way or doing things that go against the principles of the project. If unsure check with the Tech Lead. - Verify that code re-use is being done properly and that changes introduced by a bidder don't impact other bidders. - If the adapter being submitted is an alias type, check with the bidder contact that is being aliased to make sure it's allowed. -- All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. - All bidder parameter conventions must be followed: - Video params must be read from AdUnit.mediaTypes.video when available; however bidder config can override the ad unit. - First party data must be read from [`fpd.context` and `fpd.user`](https://docs.prebid.org/dev-docs/publisher-api-reference.html#setConfig-fpd). @@ -45,9 +35,61 @@ For modules and core platform updates, the initial reviewer should request an ad - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidRequest.schain. - The bidRequest page referrer must checked in addition to any bidder-specific parameter. - If they're getting the COPPA flag, it must come from config.getConfig('coppa'); - +- Below are some examples of bidder specific updates that should require docs update (in their dev-docs/bidders/BIDDER.md file): + - If they support the GDPR consentManagement module and TCF1, add `gdpr_supported: true` + - If they support the GDPR consentManagement module and TCF2, add `tcf2_supported: true` + - If they support the US Privacy consentManagementUsp module, add `usp_supported: true` + - If they support one or more userId modules, add `userId: (list of supported vendors)` + - If they support video and/or native mediaTypes add `media_types: video, native`. Note that display is added by default. If you don't support display, add "no-display" as the first entry, e.g. `media_types: no-display, native` + - If they support COPPA, add `coppa_supported: true` + - If they support SChain, add `schain_supported: true` + - If their bidder doesn't work well with safeframed creatives, add `safeframes_ok: false`. This will alert publishers to not use safeframed creatives when creating the ad server entries for their bidder. + - If they're setting a deal ID in some scenarios, add `bidder_supports_deals: true` + - If they have an IAB Global Vendor List ID, add `gvl_id: ID`. There's no default. - After a new adapter is approved, let the submitter know they may open a PR in the [headerbid-expert repository](https://github.com/prebid/headerbid-expert) to have their adapter recognized by the [Headerbid Expert extension](https://chrome.google.com/webstore/detail/headerbid-expert/cgfkddgbnfplidghapbbnngaogeldmop). The PR should be to the [bidder patterns file](https://github.com/prebid/headerbid-expert/blob/master/bidderPatterns.js), adding an entry with their adapter's name and the url the adapter uses to send and receive bid responses. +### Reviewing a New or Updated Analytics Adapter +Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html + +No additional steps above the general review process and making sure it conforms to the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html). + +### Reviewing a New or Updated User ID Sub-Module +Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/modules/userId.html#id-providers + +Follow steps above for general review process. In addition: +- Try running the new user ID module with a basic config and confirm it hits the endpoint and stores the results. +- the filename should be camel case ending with `IdSystem` (e.g. `myCompanyIdSystem.js`) +- the `const MODULE_NAME` value should be camel case ending with `Id` (e.g. `myCompanyId` ) +- the response of the `decode` method should be an object with the key being ideally camel case similar to the module name and ending in `id` or `Id`, but in some cases this value is a shortened name and sometimes with the `id` part being all lowercase, provided there are no other uppercase letters. if there's no id or it's an invalid object, the response should be `undefined`. example "valid" values (although this is more style than a requirement) + - `mcid` + - `mcId` + - `myCompanyId` +- make sure they've added references of their new module everywhere required: + - modules/.submodules.json + - modules/userId/eids.js + - modules/userId/eids.md + - modules/userId/userId.md +- tests can go either within the userId_spec.js file or in their own _spec file if they wish +- GVLID is recommended in the *IdSystem file if they operate in EU +- make sure example configurations align to the actual code (some modules use the userId storage settings and allow pub configuration, while others handle reading/writing cookies on their own, so should not include the storage params in examples) +- the 3 available methods (getId, extendId, decode) should be used as they were intended + - decode (required method) should not be making requests to retrieve a new ID, it should just be decoding a response + - extendId (optional method) should not be making requests to retrieve a new ID, it should just be adding additional data to the id object + - getId (required method) should be the only method that gets a new ID (from ajax calls or a cookie/local storage). this ensures that decode and extend do not unnecessarily delay the auction in places where it is not expected. +- in the eids.js file, the source should be the actual domain of the provider, not a made up domain. +- in the eids.js file, the key in the array should be the same value as the key in the decode function +- make sure all supported config params align in the submodule js file and the docs / examples + +### Reviewing a New or Updated Real-Time-Data Sub-Module +Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/add-rtd-submodule.html + +Follow steps above for general review process. In addition: +- The RTD Provider must include a `providerRtdProvider.md` file. This file must have example parameters and document a sense of what to expect: what should change in the bidrequest, or what targeting data should be added? +- Try running the new sub-module and confirm the provided test parameters. +- Make sure the sub-module is "phoning home" as early as possible, but not more often than needed. +- Consider whether the kind of data the module is obtaining could have privacy implications. If so, make sure they're utilizing the `consent` data passed to them. + + ## Ticket Coordinator Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. Every Monday morning a reminder is From 916972b1686c1dff4d55ce5b5e5ad0ab40081eb6 Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Wed, 14 Oct 2020 11:18:10 +0200 Subject: [PATCH 0278/1476] ATS-analytics - add retry logic to not fire request for envelope every time, and cut down analytics requests to 1/10 (#5839) * ATS-analytics - add retry logic to not fire request for envelope every time, and cut down analytics requests to 1/10 * ATS-analytics - fix test naming --- modules/atsAnalyticsAdapter.js | 22 +++++++++----- modules/identityLinkIdSystem.js | 17 +++++++++-- test/spec/modules/atsAnalyticsAdapter_spec.js | 3 +- .../spec/modules/identityLinkIdSystem_spec.js | 30 +++++++++++++++++++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index 9811c306738..a5bb3797bbf 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -126,13 +126,16 @@ let atsAnalyticsAdapter = Object.assign(adapter( callHandler(eventType, args); } if (eventType === CONSTANTS.EVENTS.AUCTION_END) { - // send data to ats analytic endpoint - try { - let dataToSend = {'Data': atsAnalyticsAdapter.context.events}; - let strJSON = JSON.stringify(dataToSend); - ajax(atsAnalyticsAdapter.context.host, function () { - }, strJSON, {method: 'POST', contentType: 'application/json'}); - } catch (err) { + if (atsAnalyticsAdapter.shouldFireRequest()) { + // send data to ats analytic endpoint + try { + let dataToSend = {'Data': atsAnalyticsAdapter.context.events}; + let strJSON = JSON.stringify(dataToSend); + utils.logInfo('atsAnalytics tried to send analytics data!'); + ajax(atsAnalyticsAdapter.context.host, function () { + }, strJSON, {method: 'POST', contentType: 'application/json'}); + } catch (err) { + } } } } @@ -141,6 +144,11 @@ let atsAnalyticsAdapter = Object.assign(adapter( // save the base class function atsAnalyticsAdapter.originEnableAnalytics = atsAnalyticsAdapter.enableAnalytics; +// add check to not fire request every time, but instead to send 1/10 events +atsAnalyticsAdapter.shouldFireRequest = function () { + return (Math.floor((Math.random() * 11)) === 10); +} + // override enableAnalytics so we can get access to the config passed in from the page atsAnalyticsAdapter.enableAnalytics = function (config) { if (!config.options.pid) { diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index 73fc6408581..33dd74380ac 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -8,6 +8,9 @@ import * as utils from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; +import {getStorageManager} from '../src/storageManager.js'; + +export const storage = getStorageManager(); /** @type {Submodule} */ export const identityLinkSubmodule = { @@ -76,7 +79,6 @@ export const identityLinkSubmodule = { }; // return envelope from third party endpoint function getEnvelope(url, callback) { - utils.logInfo('A 3P retrieval is attempted!'); const callbacks = { success: response => { let responseObj; @@ -94,7 +96,18 @@ function getEnvelope(url, callback) { callback(); } }; - ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); + + if (!storage.getCookie('_lr_retry_request')) { + setRetryCookie(); + utils.logInfo('A 3P retrieval is attempted!'); + ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); + } +} + +function setRetryCookie() { + let now = new Date(); + now.setTime(now.getTime() + 3600000); + storage.setCookie('_lr_retry_request', 'true', now.toUTCString()); } submodule('userId', identityLinkSubmodule); diff --git a/test/spec/modules/atsAnalyticsAdapter_spec.js b/test/spec/modules/atsAnalyticsAdapter_spec.js index 849ccf88c51..84206337fad 100644 --- a/test/spec/modules/atsAnalyticsAdapter_spec.js +++ b/test/spec/modules/atsAnalyticsAdapter_spec.js @@ -18,7 +18,7 @@ describe('ats analytics adapter', function () { describe('track', function () { it('builds and sends request and response data', function () { - sinon.spy(atsAnalyticsAdapter, 'track'); + sinon.stub(atsAnalyticsAdapter, 'shouldFireRequest').returns(true); let initOptions = { pid: '10433394', @@ -134,7 +134,6 @@ describe('ats analytics adapter', function () { let requests = server.requests.filter(req => { return req.url.indexOf(initOptions.host) > -1; }); - expect(requests.length).to.equal(1); let realAfterBid = JSON.parse(requests[0].requestBody); diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index c729be4c1d6..3c544fa8d08 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -1,6 +1,9 @@ import {identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; import * as utils from 'src/utils.js'; import {server} from 'test/mocks/xhr.js'; +import {getStorageManager} from '../../../src/storageManager.js'; + +export const storage = getStorageManager(); const pid = '14'; const defaultConfigParams = { params: {pid: pid} }; @@ -11,6 +14,8 @@ describe('IdentityLinkId tests', function () { beforeEach(function () { logErrorStub = sinon.stub(utils, 'logError'); + // remove _lr_retry_request cookie before test + storage.setCookie('_lr_retry_request', 'true', 'Thu, 01 Jan 1970 00:00:01 GMT'); }); afterEach(function () { @@ -124,4 +129,29 @@ describe('IdentityLinkId tests', function () { ); expect(callBackSpy.calledOnce).to.be.true; }); + + it('should not call the LiveRamp envelope endpoint if cookie _lr_retry_request exist', function () { + let now = new Date(); + now.setTime(now.getTime() + 3000); + storage.setCookie('_lr_retry_request', 'true', now.toUTCString()); + let callBackSpy = sinon.spy(); + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request).to.be.eq(undefined); + }); + + it('should call the LiveRamp envelope endpoint if cookie _lr_retry_request does not exist', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://api.rlcdn.com/api/identity/envelope?pid=14'); + request.respond( + 200, + responseHeader, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; + }); }); From c4d0f99fa4c601b9cd13526178d172fbcd6f46d1 Mon Sep 17 00:00:00 2001 From: Hugo Duthil Date: Wed, 14 Oct 2020 12:12:55 +0200 Subject: [PATCH 0279/1476] Add examples and tests for criteo User Id Module (#5838) Co-authored-by: Hugo Duthil --- integrationExamples/gpt/userId_example.html | 5 +- modules/userId/userId.md | 18 ++++ test/spec/modules/userId_spec.js | 95 ++++++++++++++------- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index dddc0915db2..7375293fdf0 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -269,7 +269,10 @@ }, { name: "quantcastId" - } + }, + { + name: "criteo" + }, ], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/userId/userId.md b/modules/userId/userId.md index a9ab6ccc483..c46f67f9a9a 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -70,6 +70,13 @@ pbjs.setConfig({ name: 'sharedid', expires: 28 } + }, { + name: 'criteo', + storage: { // It is best not to specify this parameter since the module needs to be called as many times as possible + type: 'cookie', + name: '_criteoId', + expires: 1 + } }], syncDelay: 5000, auctionDelay: 1000 @@ -141,6 +148,13 @@ pbjs.setConfig({ expires: 90, // Expiration of cookies in days refreshInSeconds: 8*3600 // User Id cache lifetime in seconds, defaulting to 'expires' }, + }, { + name: 'criteo', + storage: { // It is best not to specify this parameter since the module needs to be called as many times as possible + type: 'html5', + name: '_criteoId', + expires: 1 + } }], syncDelay: 5000 } @@ -164,6 +178,10 @@ pbjs.setConfig({ { name: "netId", value: { "netId": "fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg" } + }, + { + name: "criteo", + value: { "criteoId": "wK-fkF8zaEIlMkZMbHl3eFo4NEtoNmZaeXJtYkFjZlVuWjBhcjJMaTRYd3pZNSUyQnlKRHNGRXlpdzdjd3pjVzhjcSUyQmY4eTFzN3VSZjV1ZyUyRlA0U2ZiR0UwN2I4bDZRJTNEJTNE" } }], syncDelay: 5000 } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index ef393df82d5..5b1faa46979 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -20,6 +20,7 @@ import { resetConsentData, setConsentConfig } from 'modules/consentManagement.js'; +import {server} from 'test/mocks/xhr.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem.js'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; @@ -32,8 +33,8 @@ import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; import {haloIdSubmodule} from 'modules/haloIdSystem.js'; -import {server} from 'test/mocks/xhr.js'; import {pubProvidedIdSubmodule} from 'modules/pubProvidedSystem.js'; +import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -41,22 +42,11 @@ const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const CONSENT_LOCAL_STORAGE_NAME = '_pbjs_userid_consent_data'; describe('User ID', function () { - function getConfigMock(configArr1, configArr2, configArr3, configArr4, configArr5, configArr6, configArr7, configArr8, configArr9, configArr10) { + function getConfigMock(...configArrays) { return { userSync: { syncDelay: 0, - userIds: [ - (configArr1 && configArr1.length >= 3) ? getStorageMock.apply(null, configArr1) : null, - (configArr2 && configArr2.length >= 3) ? getStorageMock.apply(null, configArr2) : null, - (configArr3 && configArr3.length >= 3) ? getStorageMock.apply(null, configArr3) : null, - (configArr4 && configArr4.length >= 3) ? getStorageMock.apply(null, configArr4) : null, - (configArr5 && configArr5.length >= 3) ? getStorageMock.apply(null, configArr5) : null, - (configArr6 && configArr6.length >= 3) ? getStorageMock.apply(null, configArr6) : null, - (configArr7 && configArr7.length >= 3) ? getStorageMock.apply(null, configArr7) : null, - (configArr8 && configArr8.length >= 3) ? getStorageMock.apply(null, configArr8) : null, - (configArr9 && configArr9.length >= 3) ? getStorageMock.apply(null, configArr9) : null, - (configArr10 && configArr10.length >= 3) ? getStorageMock.apply(null, configArr10) : null - ].filter(i => i) + userIds: configArrays.map(configArray => (configArray && configArray.length >= 3) ? getStorageMock.apply(null, configArray) : null) } } } @@ -463,7 +453,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -471,14 +461,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -489,7 +479,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -506,15 +496,15 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 11 configurations should result in 12 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); + it('config with 13 configurations should result in 13 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -552,14 +542,16 @@ describe('User ID', function () { storage: {name: 'haloId', type: 'cookie'} }, { name: 'zeotapIdPlus' + }, { + name: 'criteo' }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 12 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 13 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -574,7 +566,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -589,7 +581,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -931,6 +923,29 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from criteoIdModule cookie', function (done) { + coreStorage.setCookie('storage_bidid', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 100000).toUTCString())); + + setSubmoduleRegistry([criteoIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['criteo', 'storage_bidid', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'criteo.com', + uids: [{id: 'test_bidid', atype: 1}] + }); + }); + }); + coreStorage.setCookie('storage_bidid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + it('test hook from liveIntentId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier'})); @@ -1412,7 +1427,7 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have data to pass', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId and Criteo have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1426,8 +1441,9 @@ describe('User ID', function () { 'ts': 1590525289611 }), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1438,7 +1454,8 @@ describe('User ID', function () { ['sharedId', 'sharedid', 'cookie'], ['intentIqId', 'intentIqId', 'cookie'], ['zeotapIdPlus', 'IDP', 'cookie'], - ['haloId', 'haloId', 'cookie'])); + ['haloId', 'haloId', 'cookie'], + ['criteo', 'storage_criteo', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1475,7 +1492,11 @@ describe('User ID', function () { // also check that haloId id was copied to bid expect(bid).to.have.deep.nested.property('userId.haloId'); expect(bid.userId.haloId).to.equal('testHaloId'); - expect(bid.userIdAsEids.length).to.equal(10); + // also check that criteo id was copied to bid + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + + expect(bid.userIdAsEids.length).to.equal(11); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1488,11 +1509,12 @@ describe('User ID', function () { coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId and haloId have their modules added before and after init', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, criteo, netId and haloId have their modules added before and after init', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1506,6 +1528,7 @@ describe('User ID', function () { coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1524,6 +1547,7 @@ describe('User ID', function () { attachIdSystem(intentIqIdSubmodule); attachIdSystem(zeotapIdPlusSubmodule); attachIdSystem(haloIdSubmodule); + attachIdSystem(criteoIdSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1534,7 +1558,8 @@ describe('User ID', function () { ['sharedId', 'sharedid', 'cookie'], ['intentIqId', 'intentIqId', 'cookie'], ['zeotapIdPlus', 'IDP', 'cookie'], - ['haloId', 'haloId', 'cookie'])); + ['haloId', 'haloId', 'cookie'], + ['criteo', 'storage_criteo', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1572,7 +1597,12 @@ describe('User ID', function () { // also check that haloId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.haloId'); expect(bid.userId.haloId).to.equal('testHaloId'); - expect(bid.userIdAsEids.length).to.equal(10); + + // also check that criteo id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + + expect(bid.userIdAsEids.length).to.equal(11); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1585,6 +1615,7 @@ describe('User ID', function () { coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); From 681b4ea83168ea312b079dd5dc732eab35e85ce6 Mon Sep 17 00:00:00 2001 From: r-ishigami <62228392+r-ishigami@users.noreply.github.com> Date: Wed, 14 Oct 2020 22:35:35 +0900 Subject: [PATCH 0280/1476] Fix size validate (#5841) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * updated size validate Co-authored-by: cmertv-sishigami --- modules/relaidoBidAdapter.js | 30 ++++++++---- test/spec/modules/relaidoBidAdapter_spec.js | 54 +++++++++++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index b3b8a647137..bc2854de40b 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -68,7 +68,7 @@ function buildRequests(validBidRequests, bidderRequest) { payload.width = playerSize[0][0]; payload.height = playerSize[0][1]; } else if (hasBannerMediaType(bidRequest)) { - const sizes = utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes'); + const sizes = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')); payload.width = sizes[0][0]; payload.height = sizes[0][1]; } @@ -234,15 +234,9 @@ function isBannerValid(bid) { if (!isMobile()) { return false; } - const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes'); - if (sizes && utils.isArray(sizes)) { - if (utils.isArray(sizes[0])) { - const width = sizes[0][0]; - const height = sizes[0][1]; - if (width >= 300 && height >= 250) { - return true; - } - } + const sizes = getValidSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes')); + if (sizes.length > 0) { + return true; } return false; } @@ -291,6 +285,22 @@ function hasVideoMediaType(bid) { return !!utils.deepAccess(bid, 'mediaTypes.video'); } +function getValidSizes(sizes) { + let result = []; + if (sizes && utils.isArray(sizes) && sizes.length > 0) { + for (let i = 0; i < sizes.length; i++) { + if (utils.isArray(sizes[i]) && sizes[i].length == 2) { + const width = sizes[i][0]; + const height = sizes[i][1]; + if ((width >= 300 && height >= 250) || (width == 1 && height == 1)) { + result.push([width, height]); + } + } + } + } + return result; +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index cd4918460db..42818232cda 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -86,6 +86,60 @@ describe('RelaidoAdapter', function () { setUADefault(); }); + it('should return false when missing 300x250 over and 1x1 by banner', function () { + setUAMobile(); + bidRequest.mediaTypes = { + banner: { + sizes: [ + [100, 100], + [300, 100] + ] + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + setUADefault(); + }); + + it('should return true when 300x250 by banner', function () { + setUAMobile(); + bidRequest.mediaTypes = { + banner: { + sizes: [ + [300, 250] + ] + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + setUADefault(); + }); + + it('should return true when 1x1 by banner', function () { + setUAMobile(); + bidRequest.mediaTypes = { + banner: { + sizes: [ + [1, 1] + ] + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + setUADefault(); + }); + + it('should return true when 300x250 over by banner', function () { + setUAMobile(); + bidRequest.mediaTypes = { + banner: { + sizes: [ + [100, 100], + [300, 250] + ] + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + setUADefault(); + }); + it('should return false when the uuid are missing', function () { localStorage.removeItem(UUID_KEY); const result = !!(utils.isSafariBrowser()); From e9d1fb5f459d0ab786bb4cfc2f08e5b45c98a688 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:34:36 +0530 Subject: [PATCH 0281/1476] fix adunit.bid undefined edge case (#5827) --- modules/pubCommonId.js | 8 +++--- modules/sizeMappingV2.js | 9 ++++++- modules/userId/index.js | 36 +++++++++++++------------ src/prebid.js | 7 +++++ test/spec/modules/sizeMappingV2_spec.js | 18 ++++++++++--- test/spec/unit/pbjs_api_spec.js | 26 ++++++++++++++++++ 6 files changed, 80 insertions(+), 24 deletions(-) diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index 174fa6ffe6e..427f775c44b 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -212,9 +212,11 @@ export function requestBidHook(next, config) { // into bid requests later. if (adUnits && pubcid) { adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - Object.assign(bid, {crumbs: {pubcid}}); - }); + if (unit.bids && utils.isArray(unit.bids)) { + unit.bids.forEach((bid) => { + Object.assign(bid, {crumbs: {pubcid}}); + }); + } }); } diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 4df537e0eb3..ffd242e57ac 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -66,7 +66,7 @@ export function isUsingNewSizeMapping(adUnits) { }); // checks for the presence of sizeConfig property at the adUnit.bids[].bidder object - adUnit.bids.forEach(bidder => { + adUnit.bids && utils.isArray(adUnit.bids) && adUnit.bids.forEach(bidder => { if (bidder.sizeConfig) { if (isUsingSizeMappingBool === false) { isUsingSizeMappingBool = true; @@ -168,8 +168,15 @@ export function checkAdUnitSetupHook(adUnits) { } const validatedAdUnits = []; adUnits.forEach(adUnit => { + const bids = adUnit.bids; const mediaTypes = adUnit.mediaTypes; let validatedBanner, validatedVideo, validatedNative; + + if (!bids || !utils.isArray(bids)) { + utils.logError(`Detected adUnit.code '${adUnit.code}' did not have 'adUnit.bids' defined or 'adUnit.bids' is not an array. Removing adUnit from auction.`); + return; + } + if (!mediaTypes || Object.keys(mediaTypes).length === 0) { utils.logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); return; diff --git a/modules/userId/index.js b/modules/userId/index.js index b934fa2d8eb..83573be8682 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -115,14 +115,14 @@ */ import find from 'core-js-pure/features/array/find.js'; -import {config} from '../../src/config.js'; +import { config } from '../../src/config.js'; import events from '../../src/events.js'; import * as utils from '../../src/utils.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; -import {gdprDataHandler} from '../../src/adapterManager.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; +import { gdprDataHandler } from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; -import {module, hook} from '../../src/hook.js'; -import {createEidsArray} from './eids.js'; +import { module, hook } from '../../src/hook.js'; +import { createEidsArray } from './eids.js'; import { getCoreStorageManager } from '../../src/storageManager.js'; const MODULE_NAME = 'User ID'; @@ -319,8 +319,8 @@ function hasGDPRConsent(consentData) { * @param {function} cb - callback for after processing is done. */ function processSubmoduleCallbacks(submodules, cb) { - const done = cb ? utils.delayExecution(cb, submodules.length) : function() { }; - submodules.forEach(function(submodule) { + const done = cb ? utils.delayExecution(cb, submodules.length) : function () { }; + submodules.forEach(function (submodule) { submodule.callback(function callbackCompleted(idObj) { // if valid, id data should be saved to cookie/html storage if (idObj) { @@ -371,11 +371,13 @@ function addIdDataToAdUnitBids(adUnits, submodules) { const combinedSubmoduleIdsAsEids = createEidsArray(combinedSubmoduleIds); if (Object.keys(combinedSubmoduleIds).length) { adUnits.forEach(adUnit => { - adUnit.bids.forEach(bid => { - // create a User ID object on the bid, - bid.userId = combinedSubmoduleIds; - bid.userIdAsEids = combinedSubmoduleIdsAsEids; - }); + if (adUnit.bids && utils.isArray(adUnit.bids)) { + adUnit.bids.forEach(bid => { + // create a User ID object on the bid, + bid.userId = combinedSubmoduleIds; + bid.userIdAsEids = combinedSubmoduleIdsAsEids; + }); + } }); } } @@ -398,7 +400,7 @@ function initializeSubmodulesAndExecuteCallbacks(continueAuction) { // delay auction until ids are available delayed = true; let continued = false; - const continueCallback = function() { + const continueCallback = function () { if (!continued) { continued = true; continueAuction(); @@ -415,7 +417,7 @@ function initializeSubmodulesAndExecuteCallbacks(continueAuction) { // when syncDelay is zero, process callbacks now, otherwise delay process with a setTimeout if (syncDelay > 0) { - setTimeout(function() { + setTimeout(function () { processSubmoduleCallbacks(submodulesWithCallbacks); }, syncDelay); } else { @@ -443,7 +445,7 @@ function initializeSubmodulesAndExecuteCallbacks(continueAuction) { */ export function requestBidsHook(fn, reqBidsConfigObj) { // initialize submodules only when undefined - initializeSubmodulesAndExecuteCallbacks(function() { + initializeSubmodulesAndExecuteCallbacks(function () { // pass available user id data to bid adapters addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, initializedSubmodules); // calling fn allows prebid to continue processing @@ -523,7 +525,7 @@ function refreshUserIds(options, callback) { * This hook returns updated list of submodules which are allowed to do get user id based on TCF 2 enforcement rules configured */ export const validateGdprEnforcement = hook('sync', function (submodules, consentData) { - return {userIdModules: submodules, hasValidated: consentData && consentData.hasValidated}; + return { userIdModules: submodules, hasValidated: consentData && consentData.hasValidated }; }, 'validateGdprEnforcement'); function populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh) { @@ -588,7 +590,7 @@ function initSubmodules(submodules, consentData) { setStoredConsentData(consentData); // gdpr consent with purpose one is required, otherwise exit immediately - let {userIdModules, hasValidated} = validateGdprEnforcement(submodules, consentData); + let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData); if (!hasValidated && !hasGDPRConsent(consentData)) { utils.logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); return []; diff --git a/src/prebid.js b/src/prebid.js index 8d1d7ca1253..8bfb6024d7a 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -149,7 +149,14 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { adUnits.forEach(adUnit => { const mediaTypes = adUnit.mediaTypes; + const bids = adUnit.bids; let validatedBanner, validatedVideo, validatedNative; + + if (!bids || !utils.isArray(bids)) { + utils.logError(`Detected adUnit.code '${adUnit.code}' did not have 'adUnit.bids' defined or 'adUnit.bids' is not an array. Removing adUnit from auction.`); + return; + } + if (!mediaTypes || Object.keys(mediaTypes).length === 0) { utils.logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); return; diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 77e17ef360f..ab06ddc8f51 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -205,6 +205,18 @@ describe('sizeMappingV2', function () { expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); }); + it('should filter out adUnit if it does not contain the required property "bids"', function() { + let adUnits = utils.deepClone(AD_UNITS); + delete adUnits[0].mediaTypes; + // before checkAdUnitSetupHook is called, the length of the adUnits should equal '2' + expect(adUnits.length).to.equal(2); + adUnits = checkAdUnitSetupHook(adUnits); + + // after checkAdUnitSetupHook is called, the length of the adUnits should be '1' + expect(adUnits.length).to.equal(1); + expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); + }); + it('should filter out adUnit if it has declared property mediaTypes with an empty object', function () { let adUnits = utils.deepClone(AD_UNITS); adUnits[0].mediaTypes = {}; @@ -657,7 +669,7 @@ describe('sizeMappingV2', function () { }, native: {} }, - bids: [] + bids: [{bidder: 'appnexus', params: 1234}] }]; checkAdUnitSetupHook(adUnit); @@ -679,7 +691,7 @@ describe('sizeMappingV2', function () { }, native: {} }, - bids: [] + bids: [{bidder: 'appnexus', params: 1234}] }]; checkAdUnitSetupHook(adUnit); @@ -698,7 +710,7 @@ describe('sizeMappingV2', function () { mediaTypes: { native: {} }, - bids: [] + bids: [{bidder: 'appnexus', params: 1234}] }]; checkAdUnitSetupHook(adUnit); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index efb1338ac5d..afad1d665fd 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1773,6 +1773,32 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].mediaTypes.native.icon).to.exist; assert.ok(logErrorSpy.calledWith('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.')); }); + + it('should throw error message and remove adUnit if adUnit.bids is not defined correctly', function () { + const adUnits = [{ + code: 'ad-unit-1', + mediaTypes: { + banner: { + sizes: [300, 400] + } + }, + bids: [{code: 'appnexus', params: 1234}] + }, { + code: 'bad-ad-unit-2', + mediaTypes: { + banner: { + sizes: [300, 400] + } + } + }]; + + $$PREBID_GLOBAL$$.requestBids({ + adUnits: adUnits + }); + expect(auctionArgs.adUnits.length).to.equal(1); + expect(auctionArgs.adUnits[1]).to.not.exist; + assert.ok(logErrorSpy.calledWith("Detected adUnit.code 'bad-ad-unit-2' did not have 'adUnit.bids' defined or 'adUnit.bids' is not an array. Removing adUnit from auction.")); + }); }); }); }); From 4df3696faca76c78ee04317ce214b3947bc85d01 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Wed, 14 Oct 2020 08:33:49 -0700 Subject: [PATCH 0282/1476] PubMatic Analytics: pass device platform related information (#5855) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * not passing GDPR data in analytics * adding support for OpenWrap regex support * added unit test cases * passing device platform in logger call; test cases added --- modules/pubmaticAnalyticsAdapter.js | 18 +++ .../modules/pubmaticAnalyticsAdapter_spec.js | 113 ++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index dfe183076f4..c7eeaf87fdc 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -167,6 +167,23 @@ function getDomainFromUrl(url) { return a.hostname; } +function getDevicePlatform() { + var deviceType = 3; + try { + var ua = navigator.userAgent; + if (ua && utils.isStr(ua) && ua.trim() != '') { + ua = ua.toLowerCase().trim(); + var isMobileRegExp = new RegExp('(mobi|tablet|ios).*'); + if (ua.match(isMobileRegExp)) { + deviceType = 2; + } else { + deviceType = 1; + } + } + } catch (ex) {} + return deviceType; +} + function getValueForKgpv(bid, adUnitId) { if (bid.params.regexPattern) { return bid.params.regexPattern; @@ -233,6 +250,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { outputObj['tst'] = Math.round((new window.Date()).getTime() / 1000); outputObj['pid'] = '' + profileId; outputObj['pdvid'] = '' + profileVersionId; + outputObj['dvc'] = {'plt': getDevicePlatform()}; outputObj['tgid'] = (function() { var testGroupId = parseInt(config.getConfig('testGroupId') || 0); if (testGroupId <= 15 && testGroupId >= 0) { diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 91c6ec1379d..11de7b13208 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -11,6 +11,12 @@ let events = require('src/events'); let ajax = require('src/ajax'); let utils = require('src/utils'); +const DEFAULT_USER_AGENT = window.navigator.userAgent; +const MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1'; +const setUADefault = () => { window.navigator.__defineGetter__('userAgent', function () { return DEFAULT_USER_AGENT }) }; +const setUAMobile = () => { window.navigator.__defineGetter__('userAgent', function () { return MOBILE_USER_AGENT }) }; +const setUANull = () => { window.navigator.__defineGetter__('userAgent', function () { return null }) }; + const { EVENTS: { AUCTION_INIT, @@ -247,6 +253,7 @@ describe('pubmatic analytics adapter', function () { let clock; beforeEach(function () { + setUADefault(); sandbox = sinon.sandbox.create(); xhr = sandbox.useFakeXMLHttpRequest(); @@ -643,6 +650,7 @@ describe('pubmatic analytics adapter', function () { }); it('Logger: currency conversion check', function() { + setUANull(); setConfig({ adServerCurrency: 'JPY', rates: { @@ -695,6 +703,111 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(100); expect(data.s[1].ps[0].ocry).to.equal('JPY'); + expect(data.dvc).to.deep.equal({'plt': 3}); + }); + + it('Logger: regexPattern in bid.params', function() { + setUAMobile(); + const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); + BID_REQUESTED_COPY.bids[1].params.regexPattern = '*'; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, BID_REQUESTED_COPY); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, BID2); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker + let request = requests[2]; // logger is executed late, trackers execute first + 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].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); + expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); + expect(data.s[1].ps[0].db).to.equal(0); + expect(data.s[1].ps[0].kgpv).to.equal('*'); + expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].psz).to.equal('728x90'); + expect(data.s[1].ps[0].eg).to.equal(1.52); + expect(data.s[1].ps[0].en).to.equal(1.52); + expect(data.s[1].ps[0].di).to.equal('the-deal-id'); + expect(data.s[1].ps[0].dc).to.equal('PMP'); + expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].l1).to.equal(3214); + expect(data.s[1].ps[0].l2).to.equal(0); + expect(data.s[1].ps[0].ss).to.equal(1); + expect(data.s[1].ps[0].t).to.equal(0); + expect(data.s[1].ps[0].wb).to.equal(0); // bidPriceUSD is not getting set as currency module is not added, so unable to set wb to 1 + 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.dvc).to.deep.equal({'plt': 2}); + // respective tracker slot + let firstTracker = requests[1].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + data = {}; + firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); + expect(data.kgpv).to.equal('*'); + }); + + it('Logger: regexPattern in bid.bidResponse', function() { + const BID2_COPY = utils.deepClone(BID2); + BID2_COPY.regexPattern = '*'; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, BID2_COPY); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, Object.assign({}, BID2_COPY, { + 'status': 'rendered' + })); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker + let request = requests[2]; // logger is executed late, trackers execute first + 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].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); + expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); + expect(data.s[1].ps[0].db).to.equal(0); + expect(data.s[1].ps[0].kgpv).to.equal('*'); + expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].psz).to.equal('728x90'); + expect(data.s[1].ps[0].eg).to.equal(1.52); + expect(data.s[1].ps[0].en).to.equal(1.52); + expect(data.s[1].ps[0].di).to.equal('the-deal-id'); + expect(data.s[1].ps[0].dc).to.equal('PMP'); + expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].l1).to.equal(3214); + expect(data.s[1].ps[0].l2).to.equal(0); + expect(data.s[1].ps[0].ss).to.equal(1); + expect(data.s[1].ps[0].t).to.equal(0); + expect(data.s[1].ps[0].wb).to.equal(0); // bidPriceUSD is not getting set as currency module is not added, so unable to set wb to 1 + 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.dvc).to.deep.equal({'plt': 1}); + // respective tracker slot + let firstTracker = requests[1].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + data = {}; + firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); + expect(data.kgpv).to.equal('*'); }); it('Logger: regexPattern in bid.params', function() { From 2c1cd899aa1da7b91ed63e080b2d35667aacd6b9 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 14 Oct 2020 10:48:39 -0700 Subject: [PATCH 0283/1476] Prebid 4.12.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 686d47f53e9..0f0ee0cde08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.12.0-pre", + "version": "4.12.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 13a0425f82d93f453dca5e5de14ac5e60d2a78a9 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 14 Oct 2020 11:09:55 -0700 Subject: [PATCH 0284/1476] git commit -m "Increment pre version" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f0ee0cde08..6273d680e19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.12.0", + "version": "4.13.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f545a56b63b9e5138f08d6c2909a7981d24d2400 Mon Sep 17 00:00:00 2001 From: roygiladi <58809838+roygiladi@users.noreply.github.com> Date: Thu, 15 Oct 2020 13:10:33 +0300 Subject: [PATCH 0285/1476] add ooloAnalyticsAdapter (#5852) * oolo analytics adapter added * update md * fix startsWith undefined * adjust tests * update tests - replace .find with .filter * update .md description --- modules/ooloAnalyticsAdapter.js | 547 ++++++++++++ modules/ooloAnalyticsAdapter.md | 24 + .../spec/modules/ooloAnalyticsAdapter_spec.js | 793 ++++++++++++++++++ 3 files changed, 1364 insertions(+) create mode 100644 modules/ooloAnalyticsAdapter.js create mode 100644 modules/ooloAnalyticsAdapter.md create mode 100644 test/spec/modules/ooloAnalyticsAdapter_spec.js diff --git a/modules/ooloAnalyticsAdapter.js b/modules/ooloAnalyticsAdapter.js new file mode 100644 index 00000000000..7195d6ac177 --- /dev/null +++ b/modules/ooloAnalyticsAdapter.js @@ -0,0 +1,547 @@ +import adapter from '../src/AnalyticsAdapter.js' +import adapterManager from '../src/adapterManager.js' +import CONSTANTS from '../src/constants.json' +import * as utils from '../src/utils.js' +import { ajax } from '../src/ajax.js' +import { config } from '../src/config.js' + +const baseUrl = 'https://pbdata.oolo.io/' +const ENDPOINTS = { + CONFIG: 'https://config-pbdata.oolo.io', + PAGE_DATA: baseUrl + 'page', + AUCTION: baseUrl + 'auctionData', + BID_WON: baseUrl + 'bidWonData', + AD_RENDER_FAILED: baseUrl + 'adRenderFailedData', + RAW: baseUrl + 'raw', + HBCONFIG: baseUrl + 'hbconfig', +} + +const pbModuleVersion = '1.0.0' +const prebidVersion = '$prebid.version$' +const analyticsType = 'endpoint' +const ADAPTER_CODE = 'oolo' +const AUCTION_END_SEND_TIMEOUT = 1500 +export const PAGEVIEW_ID = +generatePageViewId() + +const { + AUCTION_INIT, + AUCTION_END, + BID_REQUESTED, + BID_RESPONSE, + NO_BID, + BID_WON, + BID_TIMEOUT, + AD_RENDER_FAILED +} = CONSTANTS.EVENTS + +const SERVER_EVENTS = { + AUCTION: 'auction', + WON: 'bidWon', + AD_RENDER_FAILED: 'adRenderFailed' +} + +const SERVER_BID_STATUS = { + NO_BID: 'noBid', + BID_REQUESTED: 'bidRequested', + BID_RECEIVED: 'bidReceived', + BID_TIMEDOUT: 'bidTimedOut', + BID_WON: 'bidWon' +} + +let auctions = {} +let initOptions = {} +let eventsQueue = [] + +const onAuctionInit = (args) => { + const { auctionId, adUnits, timestamp } = args + + let auction = auctions[auctionId] = { + ...args, + adUnits: {}, + auctionStart: timestamp, + _sentToServer: false + } + + handleCustomFields(auction, AUCTION_INIT, args) + + utils._each(adUnits, adUnit => { + auction.adUnits[adUnit.code] = { + ...adUnit, + auctionId, + adunid: adUnit.code, + bids: {}, + } + }) +} + +const onBidRequested = (args) => { + const { auctionId, bids, start, timeout } = args + const _start = start || Date.now() + const auction = auctions[auctionId] + const auctionAdUnits = auction.adUnits + + bids.forEach(bid => { + const { adUnitCode } = bid + const bidId = parseBidId(bid) + + auctionAdUnits[adUnitCode].bids[bidId] = { + ...bid, + timeout, + start: _start, + rs: _start - auction.auctionStart, + bidStatus: SERVER_BID_STATUS.BID_REQUESTED, + } + }) +} + +const onBidResponse = (args) => { + const { auctionId, adUnitCode } = args + const auction = auctions[auctionId] + const bidId = parseBidId(args) + let bid = auction.adUnits[adUnitCode].bids[bidId] + + Object.assign(bid, args, { + bidStatus: SERVER_BID_STATUS.BID_RECEIVED, + end: args.responseTimestamp, + re: args.responseTimestamp - auction.auctionStart + }) +} + +const onNoBid = (args) => { + const { auctionId, adUnitCode } = args + const bidId = parseBidId(args) + const end = Date.now() + const auction = auctions[auctionId] + let bid = auction.adUnits[adUnitCode].bids[bidId] + + Object.assign(bid, args, { + bidStatus: SERVER_BID_STATUS.NO_BID, + end, + re: end - auction.auctionStart + }) +} + +const onBidWon = (args) => { + const { auctionId, adUnitCode } = args + const bidId = parseBidId(args) + const bid = auctions[auctionId].adUnits[adUnitCode].bids[bidId] + + Object.assign(bid, args, { + bidStatus: SERVER_BID_STATUS.BID_WON, + isW: true, + isH: true + }) + + if (auctions[auctionId]._sentToServer) { + const payload = { + auctionId, + adunid: adUnitCode, + bid: mapBid(bid, BID_WON) + } + + sendEvent(SERVER_EVENTS.WON, payload) + } +} + +const onBidTimeout = (args) => { + utils._each(args, bid => { + const { auctionId, adUnitCode } = bid + const bidId = parseBidId(bid) + let bidCache = auctions[auctionId].adUnits[adUnitCode].bids[bidId] + + Object.assign(bidCache, bid, { + bidStatus: SERVER_BID_STATUS.BID_TIMEDOUT, + }) + }) +} + +const onAuctionEnd = (args) => { + const { auctionId, adUnits, ...restAuctionEnd } = args + + Object.assign(auctions[auctionId], restAuctionEnd, { + auctionEnd: args.auctionEnd || Date.now() + }) + + // wait for bidWon before sending to server + setTimeout(() => { + auctions[auctionId]._sentToServer = true + const finalAuctionData = buildAuctionData(auctions[auctionId]) + + sendEvent(SERVER_EVENTS.AUCTION, finalAuctionData) + }, initOptions.serverConfig.BID_WON_TIMEOUT || AUCTION_END_SEND_TIMEOUT) +} + +const onAdRenderFailed = (args) => { + const data = utils.deepClone(args) + data.timestamp = Date.now() + + if (data.bid) { + data.bid = mapBid(data.bid, AD_RENDER_FAILED) + } + + sendEvent(SERVER_EVENTS.AD_RENDER_FAILED, data) +} + +var ooloAdapter = Object.assign( + adapter({ analyticsType }), { + track({ eventType, args }) { + // wait for server configuration before processing the events + if (typeof initOptions.serverConfig !== 'undefined' && eventsQueue.length === 0) { + handleEvent(eventType, args) + } else { + eventsQueue.push({ eventType, args }) + } + } + } +) + +function handleEvent(eventType, args) { + try { + const { sendRaw } = initOptions.serverConfig.events[eventType] + if (sendRaw) { + sendEvent(eventType, args, true) + } + } catch (e) { } + + switch (eventType) { + case AUCTION_INIT: + onAuctionInit(args) + break + case BID_REQUESTED: + onBidRequested(args) + break + case NO_BID: + onNoBid(args) + break + case BID_RESPONSE: + onBidResponse(args) + break + case BID_WON: + onBidWon(args) + break + case BID_TIMEOUT: + onBidTimeout(args) + break + case AUCTION_END: + onAuctionEnd(args) + break + case AD_RENDER_FAILED: + onAdRenderFailed(args) + break + } +} + +function sendEvent(eventType, args, isRaw) { + let data = utils.deepClone(args) + + Object.assign(data, buildCommonDataProperties(), { + eventType + }) + + if (isRaw) { + let rawEndpoint + try { + const { endpoint, omitRawFields } = initOptions.serverConfig.events[eventType] + rawEndpoint = endpoint + handleCustomRawFields(data, omitRawFields) + } catch (e) { } + ajaxCall(rawEndpoint || ENDPOINTS.RAW, () => { }, JSON.stringify(data)) + } else { + let endpoint + if (eventType === SERVER_EVENTS.AD_RENDER_FAILED) { + endpoint = ENDPOINTS.AD_RENDER_FAILED + } else if (eventType === SERVER_EVENTS.WON) { + endpoint = ENDPOINTS.BID_WON + } else { + endpoint = ENDPOINTS.AUCTION + } + + ajaxCall(endpoint, () => { }, JSON.stringify(data)) + } +} + +function checkEventsQueue() { + while (eventsQueue.length) { + const event = eventsQueue.shift() + handleEvent(event.eventType, event.args) + } +} + +function buildAuctionData(auction) { + const auctionData = utils.deepClone(auction) + const keysToRemove = ['adUnitCodes', 'auctionStatus', 'bidderRequests', 'bidsReceived', 'noBids', 'winningBids', 'timestamp', 'config'] + + keysToRemove.forEach(key => { + delete auctionData[key] + }) + + handleCustomFields(auctionData, AUCTION_END, auction) + + // turn bids object into array of objects + Object.keys(auctionData.adUnits).forEach(adUnit => { + const adUnitObj = auctionData.adUnits[adUnit] + adUnitObj.bids = Object.keys(adUnitObj.bids).map(key => mapBid(adUnitObj.bids[key])) + delete adUnitObj['adUnitCode'] + delete adUnitObj['code'] + delete adUnitObj['transactionId'] + }) + + // turn adUnits objects into array of adUnits + auctionData.adUnits = Object.keys(auctionData.adUnits).map(key => auctionData.adUnits[key]) + + return auctionData +} + +function buildCommonDataProperties() { + return { + pvid: PAGEVIEW_ID, + pid: initOptions.pid, + pbModuleVersion + } +} + +function buildLogMessage(message) { + return `oolo: ${message}` +} + +function parseBidId(bid) { + return bid.bidId || bid.requestId +} + +function mapBid({ + bidStatus, + start, + end, + mediaType, + creativeId, + originalCpm, + originalCurrency, + source, + netRevenue, + currency, + width, + height, + timeToRespond, + responseTimestamp, + ...rest +}, eventType) { + const bidObj = { + bst: bidStatus, + s: start, + e: responseTimestamp || end, + mt: mediaType, + crId: creativeId, + oCpm: originalCpm, + oCur: originalCurrency, + src: source, + nrv: netRevenue, + cur: currency, + w: width, + h: height, + ttr: timeToRespond, + ...rest, + } + + delete bidObj['bidRequestsCount'] + delete bidObj['bidderRequestId'] + delete bidObj['bidderRequestsCount'] + delete bidObj['bidderWinsCount'] + delete bidObj['schain'] + delete bidObj['refererInfo'] + delete bidObj['statusMessage'] + delete bidObj['status'] + delete bidObj['adUrl'] + delete bidObj['ad'] + delete bidObj['usesGenericKeys'] + delete bidObj['requestTimestamp'] + + try { + handleCustomFields(bidObj, eventType || BID_RESPONSE, rest) + } catch (e) { } + + return bidObj +} + +function handleCustomFields(obj, eventType, args) { + try { + const { pickFields, omitFields } = initOptions.serverConfig.events[eventType] + + if (pickFields && obj && args) { + Object.assign(obj, utils.pick(args, pickFields)) + } + + if (omitFields && obj && args) { + omitFields.forEach(field => { + utils.deepSetValue(obj, field, undefined) + }) + } + } catch (e) { } +} + +function handleCustomRawFields(obj, omitRawFields) { + try { + if (omitRawFields && obj) { + omitRawFields.forEach(field => { + utils.deepSetValue(obj, field, undefined) + }) + } + } catch (e) { } +} + +function getServerConfig() { + const defaultConfig = { events: {} } + + ajaxCall( + ENDPOINTS.CONFIG + '?pid=' + initOptions.pid, + { + success: function (data) { + try { + initOptions.serverConfig = JSON.parse(data) || defaultConfig + } catch (e) { + initOptions.serverConfig = defaultConfig + } + checkEventsQueue() + }, + error: function () { + initOptions.serverConfig = defaultConfig + checkEventsQueue() + } + }, + null + ) +} + +function sendPage() { + setTimeout(() => { + const payload = { + timestamp: Date.now(), + screenWidth: window.screen.width, + screenHeight: window.screen.height, + url: window.location.href, + protocol: window.location.protocol, + origin: utils.getOrigin(), + referrer: getTopWindowReferrer(), + pbVersion: prebidVersion, + } + + Object.assign(payload, buildCommonDataProperties(), getPagePerformance()) + ajaxCall(ENDPOINTS.PAGE_DATA, () => { }, JSON.stringify(payload)) + }, 0) +} + +function sendHbConfigData() { + const conf = {} + const pbjsConfig = config.getConfig() + + Object.keys(pbjsConfig).forEach(key => { + if (key[0] !== '_') { + conf[key] = pbjsConfig[key] + } + }) + + ajaxCall(ENDPOINTS.HBCONFIG, () => { }, JSON.stringify(conf)) +} + +function getPagePerformance() { + let timing + + try { + timing = window.top.performance.timing + } catch (e) { } + + if (!timing) { + return + } + + const { navigationStart, domContentLoadedEventEnd, loadEventEnd } = timing + const domContentLoadTime = domContentLoadedEventEnd - navigationStart + const pageLoadTime = loadEventEnd - navigationStart + + return { + domContentLoadTime, + pageLoadTime, + } +} + +function getTopWindowReferrer() { + try { + return window.top.document.referrer + } catch (e) { + return '' + } +} + +function generatePageViewId(min = 10000, max = 90000) { + var randomNumber = Math.floor((Math.random() * max) + min) + var currentdate = new Date() + var currentTime = { + getDate: currentdate.getDate(), + getMonth: currentdate.getMonth(), + getFullYear: currentdate.getFullYear(), + getHours: currentdate.getHours(), + getMinutes: currentdate.getMinutes(), + getSeconds: currentdate.getSeconds(), + getMilliseconds: currentdate.getMilliseconds() + } + return ((currentTime.getDate <= 9) ? '0' + (currentTime.getDate) : (currentTime.getDate)) + '' + + (currentTime.getMonth + 1 <= 9 ? '0' + (currentTime.getMonth + 1) : (currentTime.getMonth + 1)) + '' + + currentTime.getFullYear % 100 + '' + + (currentTime.getHours <= 9 ? '0' + currentTime.getHours : currentTime.getHours) + '' + + (currentTime.getMinutes <= 9 ? '0' + currentTime.getMinutes : currentTime.getMinutes) + '' + + (currentTime.getSeconds <= 9 ? '0' + currentTime.getSeconds : currentTime.getSeconds) + '' + + (currentTime.getMilliseconds % 100 <= 9 ? '0' + (currentTime.getMilliseconds % 100) : (currentTime.getMilliseconds % 100)) + '' + + (randomNumber) +} + +function ajaxCall(endpoint, callback, data, options = {}) { + if (data) { + options.contentType = 'application/json' + } + + return ajax(endpoint, callback, data, options) +} + +ooloAdapter.originEnableAnalytics = ooloAdapter.enableAnalytics +ooloAdapter.enableAnalytics = function (config) { + ooloAdapter.originEnableAnalytics(config) + initOptions = config ? config.options : {} + + if (!initOptions.pid) { + utils.logError(buildLogMessage('enableAnalytics missing config object with "pid"')) + return + } + + getServerConfig() + sendHbConfigData() + + if (document.readyState === 'complete') { + sendPage() + } else { + window.addEventListener('load', sendPage) + } + + utils.logInfo(buildLogMessage('enabled analytics adapter'), config) + ooloAdapter.enableAnalytics = function () { + utils.logInfo(buildLogMessage('Analytics adapter already enabled..')) + } +} + +ooloAdapter.originDisableAnalytics = ooloAdapter.disableAnalytics +ooloAdapter.disableAnalytics = function () { + auctions = {} + initOptions = {} + ooloAdapter.originDisableAnalytics() +} + +adapterManager.registerAnalyticsAdapter({ + adapter: ooloAdapter, + code: ADAPTER_CODE +}) + +// export for testing +export { + buildAuctionData, + generatePageViewId +} + +export default ooloAdapter diff --git a/modules/ooloAnalyticsAdapter.md b/modules/ooloAnalyticsAdapter.md new file mode 100644 index 00000000000..1ffb9cbe050 --- /dev/null +++ b/modules/ooloAnalyticsAdapter.md @@ -0,0 +1,24 @@ +# Overview + +Module Name: oolo Analytics Adapter +Module Type: Analytics Adapter +Maintainer: admin@oolo.io + +# Description + +Analytics adapter for oolo. + +oolo is an anomaly detection based monitoring solution for web publishers, built by adops for adops. Its purpose is to eliminate most of the daily manual work that teams are required to do, while increasing the overall performance, due to full & faster detection of problems and opportunities. + +Contact admin@oolo.io for information. + +# Usage + +```javascript +pbjs.enableAnalytics({ + provider: 'oolo', + options: { + pid: 12345 // id provided by oolo + } +}) +``` diff --git a/test/spec/modules/ooloAnalyticsAdapter_spec.js b/test/spec/modules/ooloAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..f82f7856fb2 --- /dev/null +++ b/test/spec/modules/ooloAnalyticsAdapter_spec.js @@ -0,0 +1,793 @@ +import ooloAnalytics, { PAGEVIEW_ID } from 'modules/ooloAnalyticsAdapter.js'; +import {expect} from 'chai'; +import {server} from 'test/mocks/xhr.js'; +import constants from 'src/constants.json' +import events from 'src/events' +import { config } from 'src/config'; +import { buildAuctionData, generatePageViewId } from 'modules/ooloAnalyticsAdapter'; + +const auctionId = '0ea14159-2058-4b87-a966-9d7652176a56'; +const auctionStart = 1598513385415 +const timeout = 3000 +const adUnit1 = 'top_1'; +const adUnit2 = 'top_2'; +const bidId1 = '392b5a6b05d648' +const bidId2 = '392b5a6b05d649' +const bidId3 = '392b5a6b05d650' + +const auctionInit = { + timestamp: auctionStart, + auctionId: auctionId, + timeout: timeout, + auctionStart, + adUnits: [{ + code: adUnit1, + transactionId: 'abalksdkjfh-12sade' + }, { + code: adUnit2, + transactionId: 'abalksdkjfh-12sadf' + }] +}; + +const bidRequested = { + auctionId, + bidderCode: 'appnexus', + bidderRequestId: '2946b569352ef2', + start: 1598513405254, + bids: [ + { + auctionId, + bidId: bidId1, + bidderRequestId: '2946b569352ef2', + bidder: 'appnexus', + adUnitCode: adUnit1, + sizes: [[728, 90], [970, 90]], + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 90]], + }, + }, + params: { + placementId: '4799418', + }, + schain: {} + }, + { + auctionId, + bidId: bidId2, + bidderRequestId: '2946b569352ef3', + bidder: 'rubicon', + adUnitCode: adUnit2, + sizes: [[728, 90], [970, 90]], + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 90]], + }, + }, + params: { + placementId: '4799418', + }, + schain: {} + }, + { + auctionId, + bidId: bidId3, + bidderRequestId: '2946b569352ef4', + bidder: 'ix', + adUnitCode: adUnit2, + sizes: [[728, 90], [970, 90]], + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 90]], + }, + }, + params: { + placementId: '4799418', + }, + schain: {} + }, + ], +} + +const noBid = { + auctionId, + adUnitCode: adUnit2, + bidId: bidId2, + requestId: bidId2 +} + +const bidResponse = { + auctionId, + bidderCode: 'appnexus', + mediaType: 'banner', + width: 0, + height: 0, + statusMessage: 'Bid available', + adId: '222bb26f9e8bd', + cpm: 0.112256, + responseTimestamp: 1598513485254, + requestTimestamp: 1462919238936, + bidder: 'appnexus', + adUnitCode: adUnit1, + timeToRespond: 401, + pbLg: '0.00', + pbMg: '0.10', + pbHg: '0.11', + pbAg: '0.10', + size: '0x0', + requestId: bidId1, + creativeId: '123456', + adserverTargeting: { + hb_bidder: 'appnexus', + hb_adid: '222bb26f9e8bd', + hb_pb: '10.00', + hb_size: '0x0', + foobar: '0x0', + }, + netRevenue: true, + currency: 'USD', + ttl: 300, +} + +const auctionEnd = { + auctionId: auctionId +}; + +const bidTimeout = [ + { + adUnitCode: adUnit2, + auctionId: auctionId, + bidId: bidId3, + bidder: 'ix', + timeout: timeout + } +]; + +const bidWon = { + auctionId, + adUnitCode: adUnit1, + bidId: bidId1, + cpm: 0.5 +} + +function simulateAuction () { + events.emit(constants.EVENTS.AUCTION_INIT, auctionInit); + events.emit(constants.EVENTS.BID_REQUESTED, bidRequested); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + events.emit(constants.EVENTS.NO_BID, noBid); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); +} + +describe('oolo Prebid Analytic', () => { + let clock + + beforeEach(() => { + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers() + }); + + afterEach(() => { + ooloAnalytics.disableAnalytics(); + events.getEvents.restore() + clock.restore(); + }) + + describe('enableAnalytics init options', () => { + it('should not enable analytics if invalid config', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: undefined + } + }) + + expect(server.requests).to.have.length(0) + }) + + it('should send prebid config to the server', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + const conf = {} + const pbjsConfig = config.getConfig() + + Object.keys(pbjsConfig).forEach(key => { + if (key[0] !== '_') { + conf[key] = pbjsConfig[key] + } + }) + + expect(server.requests[1].url).to.contain('/hbconf') + expect(JSON.parse(server.requests[1].requestBody)).to.deep.equal(conf) + }) + + it('should request server config and send page data', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + expect(server.requests[0].url).to.contain('?pid=123') + + const pageData = JSON.parse(server.requests[2].requestBody) + + expect(pageData).to.have.property('timestamp') + expect(pageData).to.have.property('screenWidth') + expect(pageData).to.have.property('screenHeight') + expect(pageData).to.have.property('url') + expect(pageData).to.have.property('protocol') + expect(pageData).to.have.property('origin') + expect(pageData).to.have.property('referrer') + expect(pageData).to.have.property('pbVersion') + expect(pageData).to.have.property('pvid') + expect(pageData).to.have.property('pid') + expect(pageData).to.have.property('pbModuleVersion') + expect(pageData).to.have.property('domContentLoadTime') + expect(pageData).to.have.property('pageLoadTime') + }) + }) + + describe('data handling and sending events', () => { + it('should send an "auction" event to the server', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + server.requests[0].respond(500) + + simulateAuction() + + expect(server.requests.length).to.equal(3) + clock.tick(2000) + expect(server.requests.length).to.equal(4) + + const request = JSON.parse(server.requests[3].requestBody); + + expect(request).to.include({ + eventType: 'auction', + pid: 123, + auctionId, + auctionStart, + auctionEnd: 0, + timeout, + }) + expect(request.pvid).to.be.a('number') + expect(request.adUnits).to.have.length(2) + + const auctionAdUnit1 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit1)[0] + const auctionAdUnit2 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit2)[0] + + expect(auctionAdUnit1.auctionId).to.equal(auctionId) + expect(auctionAdUnit1.bids).to.be.an('array') + expect(auctionAdUnit1.bids).to.have.length(1) + + // bid response + expect(auctionAdUnit1.bids[0].bidId).to.equal(bidId1) + expect(auctionAdUnit1.bids[0].bst).to.equal('bidReceived') + expect(auctionAdUnit1.bids[0].bidder).to.equal('appnexus') + expect(auctionAdUnit1.bids[0].cpm).to.equal(0.112256) + expect(auctionAdUnit1.bids[0].cur).to.equal('USD') + expect(auctionAdUnit1.bids[0].s).to.equal(bidRequested.start) + expect(auctionAdUnit1.bids[0].e).to.equal(bidResponse.responseTimestamp) + expect(auctionAdUnit1.bids[0].rs).to.equal(bidRequested.start - auctionStart) + expect(auctionAdUnit1.bids[0].re).to.equal(bidResponse.responseTimestamp - auctionStart) + expect(auctionAdUnit1.bids[0].h).to.equal(0) + expect(auctionAdUnit1.bids[0].w).to.equal(0) + expect(auctionAdUnit1.bids[0].mt).to.equal('banner') + expect(auctionAdUnit1.bids[0].nrv).to.equal(true) + expect(auctionAdUnit1.bids[0].params).to.have.keys('placementId') + expect(auctionAdUnit1.bids[0].size).to.equal('0x0') + expect(auctionAdUnit1.bids[0].crId).to.equal('123456') + expect(auctionAdUnit1.bids[0].ttl).to.equal(bidResponse.ttl) + expect(auctionAdUnit1.bids[0].ttr).to.equal(bidResponse.timeToRespond) + + expect(auctionAdUnit2.auctionId).to.equal(auctionId) + expect(auctionAdUnit2.bids).to.be.an('array') + expect(auctionAdUnit2.bids).to.have.length(2) + + // no bid + expect(auctionAdUnit2.bids[0].bidId).to.equal(bidId2) + expect(auctionAdUnit2.bids[0].bst).to.equal('noBid') + expect(auctionAdUnit2.bids[0].bidder).to.equal('rubicon') + expect(auctionAdUnit2.bids[0].s).to.be.a('number') + expect(auctionAdUnit2.bids[0].e).to.be.a('number') + expect(auctionAdUnit2.bids[0].params).to.have.keys('placementId') + + // timeout + expect(auctionAdUnit2.bids[1].bidId).to.equal(bidId3) + expect(auctionAdUnit2.bids[1].bst).to.equal('bidTimedOut') + expect(auctionAdUnit2.bids[1].bidder).to.equal('ix') + expect(auctionAdUnit2.bids[1].s).to.be.a('number') + expect(auctionAdUnit2.bids[1].e).to.be.a('undefined') + }) + + it('should push events to a queue and process them once server configuration returns', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + events.emit(constants.EVENTS.AUCTION_INIT, auctionInit); + events.emit(constants.EVENTS.BID_REQUESTED, bidRequested); + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + + // configuration returned in an arbitrary moment + server.requests[0].respond(500) + + events.emit(constants.EVENTS.NO_BID, noBid); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + + clock.tick(1500) + + const request = JSON.parse(server.requests[3].requestBody); + + expect(request).to.include({ + eventType: 'auction', + pid: 123, + auctionId, + auctionStart, + auctionEnd: 0, + timeout, + }) + expect(request.pvid).to.be.a('number') + expect(request.adUnits).to.have.length(2) + + const auctionAdUnit1 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit1)[0] + const auctionAdUnit2 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit2)[0] + + expect(auctionAdUnit1.auctionId).to.equal(auctionId) + expect(auctionAdUnit1.bids).to.be.an('array') + expect(auctionAdUnit1.bids).to.have.length(1) + + // bid response + expect(auctionAdUnit1.bids[0].bidId).to.equal(bidId1) + expect(auctionAdUnit1.bids[0].bst).to.equal('bidReceived') + expect(auctionAdUnit1.bids[0].bidder).to.equal('appnexus') + expect(auctionAdUnit1.bids[0].cpm).to.equal(0.112256) + expect(auctionAdUnit1.bids[0].cur).to.equal('USD') + expect(auctionAdUnit1.bids[0].s).to.equal(bidRequested.start) + expect(auctionAdUnit1.bids[0].e).to.equal(bidResponse.responseTimestamp) + expect(auctionAdUnit1.bids[0].rs).to.equal(bidRequested.start - auctionStart) + expect(auctionAdUnit1.bids[0].re).to.equal(bidResponse.responseTimestamp - auctionStart) + expect(auctionAdUnit1.bids[0].h).to.equal(0) + expect(auctionAdUnit1.bids[0].w).to.equal(0) + expect(auctionAdUnit1.bids[0].mt).to.equal('banner') + expect(auctionAdUnit1.bids[0].nrv).to.equal(true) + expect(auctionAdUnit1.bids[0].params).to.have.keys('placementId') + expect(auctionAdUnit1.bids[0].size).to.equal('0x0') + expect(auctionAdUnit1.bids[0].crId).to.equal('123456') + expect(auctionAdUnit1.bids[0].ttl).to.equal(bidResponse.ttl) + expect(auctionAdUnit1.bids[0].ttr).to.equal(bidResponse.timeToRespond) + + expect(auctionAdUnit2.auctionId).to.equal(auctionId) + expect(auctionAdUnit2.bids).to.be.an('array') + expect(auctionAdUnit2.bids).to.have.length(2) + + // no bid + expect(auctionAdUnit2.bids[0].bidId).to.equal(bidId2) + expect(auctionAdUnit2.bids[0].bst).to.equal('noBid') + expect(auctionAdUnit2.bids[0].bidder).to.equal('rubicon') + expect(auctionAdUnit2.bids[0].s).to.be.a('number') + expect(auctionAdUnit2.bids[0].e).to.be.a('number') + expect(auctionAdUnit2.bids[0].params).to.have.keys('placementId') + + // timeout + expect(auctionAdUnit2.bids[1].bidId).to.equal(bidId3) + expect(auctionAdUnit2.bids[1].bst).to.equal('bidTimedOut') + expect(auctionAdUnit2.bids[1].bidder).to.equal('ix') + expect(auctionAdUnit2.bids[1].s).to.be.a('number') + expect(auctionAdUnit2.bids[1].e).to.be.a('undefined') + }) + + it('should send "auction" event without all the fields that were set to undefined', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(500) + + simulateAuction() + clock.tick(1500) + + const request = JSON.parse(server.requests[3].requestBody); + + const auctionAdUnit1 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit1)[0] + const auctionAdUnit2 = request.adUnits.filter(adUnit => adUnit.adunid === adUnit2)[0] + + expect(auctionAdUnit1.code).to.equal(undefined) + expect(auctionAdUnit1.transactionId).to.equal(undefined) + expect(auctionAdUnit1.adUnitCode).to.equal(undefined) + expect(auctionAdUnit1.bids[0].auctionStart).to.equal(undefined) + expect(auctionAdUnit1.bids[0].bids).to.equal(undefined) + expect(auctionAdUnit1.bids[0].refererInfo).to.equal(undefined) + expect(auctionAdUnit1.bids[0].bidRequestsCount).to.equal(undefined) + expect(auctionAdUnit1.bids[0].bidderRequestId).to.equal(undefined) + expect(auctionAdUnit1.bids[0].bidderRequestsCount).to.equal(undefined) + expect(auctionAdUnit1.bids[0].bidderWinsCount).to.equal(undefined) + expect(auctionAdUnit1.bids[0].schain).to.equal(undefined) + expect(auctionAdUnit1.bids[0].src).to.equal(undefined) + expect(auctionAdUnit1.bids[0].transactionId).to.equal(undefined) + + // no bid + expect(auctionAdUnit2.bids[0].schain).to.equal(undefined) + expect(auctionAdUnit2.bids[0].src).to.equal(undefined) + expect(auctionAdUnit2.bids[0].transactionId).to.equal(undefined) + }) + + it('should mark bid winner and send to the server along with the auction data', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(500) + simulateAuction() + events.emit(constants.EVENTS.BID_WON, bidWon); + clock.tick(1500) + + // no bidWon + expect(server.requests).to.have.length(4) + + const request = JSON.parse(server.requests[3].requestBody); + expect(request.adUnits[0].bids[0].isW).to.equal(true) + }) + + it('should take BID_WON_TIMEOUT from server config if exists', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': {}, + 'BID_WON_TIMEOUT': 500 + })) + + simulateAuction() + events.emit(constants.EVENTS.BID_WON, bidWon); + clock.tick(499) + + // no auction data + expect(server.requests).to.have.length(3) + + clock.tick(1) + + // auction data + expect(server.requests).to.have.length(4) + const request = JSON.parse(server.requests[3].requestBody); + expect(request.adUnits[0].bids[0].isW).to.equal(true) + }) + + it('should send a "bidWon" event to the server', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(500) + simulateAuction() + clock.tick(1500) + events.emit(constants.EVENTS.BID_WON, bidWon); + + expect(server.requests).to.have.length(5) + + const request = JSON.parse(server.requests[4].requestBody); + + expect(request.eventType).to.equal('bidWon') + expect(request.auctionId).to.equal(auctionId) + expect(request.adunid).to.equal(adUnit1) + expect(request.bid.bst).to.equal('bidWon') + expect(request.bid.cur).to.equal('USD') + expect(request.bid.cpm).to.equal(0.5) + }) + + it('should sent adRenderFailed to the server', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(500) + simulateAuction() + clock.tick(1500) + events.emit(constants.EVENTS.AD_RENDER_FAILED, { bidId: 'abcdef', reason: 'exception' }); + + expect(server.requests).to.have.length(5) + + const request = JSON.parse(server.requests[4].requestBody); + + expect(request.eventType).to.equal('adRenderFailed') + expect(request.pvid).to.equal(PAGEVIEW_ID) + expect(request.bidId).to.equal('abcdef') + expect(request.reason).to.equal('exception') + }) + + it('should pick fields according to server configuration', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'bidRequested': { + 'sendRaw': 0, + 'pickFields': ['transactionId'] + }, + 'noBid': { + 'sendRaw': 0, + 'pickFields': ['src'] + }, + 'bidResponse': { + 'sendRaw': 0, + 'pickFields': ['adUrl', 'statusMessage'] + }, + 'auctionEnd': { + 'sendRaw': 0, + 'pickFields': ['winningBids'] + }, + } + })) + + events.emit(constants.EVENTS.AUCTION_INIT, { ...auctionInit }); + events.emit(constants.EVENTS.BID_REQUESTED, { ...bidRequested, bids: bidRequested.bids.map(b => { b.transactionId = '123'; return b }) }); + events.emit(constants.EVENTS.NO_BID, { ...noBid, src: 'client' }); + events.emit(constants.EVENTS.BID_RESPONSE, { ...bidResponse, adUrl: '...' }); + events.emit(constants.EVENTS.AUCTION_END, { ...auctionEnd, winningBids: [] }); + events.emit(constants.EVENTS.BID_WON, { ...bidWon, statusMessage: 'msg2' }); + + clock.tick(1500) + + const request = JSON.parse(server.requests[3].requestBody) + + expect(request.adUnits[0].bids[0].transactionId).to.equal('123') + expect(request.adUnits[0].bids[0].adUrl).to.equal('...') + expect(request.adUnits[0].bids[0].statusMessage).to.equal('msg2') + expect(request.adUnits[1].bids[0].src).to.equal('client') + }) + + it('should omit fields according to server configuration', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'auctionInit': { + 'sendRaw': 0, + 'omitFields': ['custom_1'] + }, + 'bidResponse': { + 'sendRaw': 0, + 'omitFields': ['custom_2', 'custom_4', 'custom_5'] + } + } + })) + + events.emit(constants.EVENTS.AUCTION_INIT, { ...auctionInit, custom_1: true }); + events.emit(constants.EVENTS.BID_REQUESTED, { ...bidRequested, bids: bidRequested.bids.map(b => { b.custom_2 = true; return b }) }); + events.emit(constants.EVENTS.NO_BID, { ...noBid, custom_3: true }); + events.emit(constants.EVENTS.BID_RESPONSE, { ...bidResponse, custom_4: true }); + events.emit(constants.EVENTS.AUCTION_END, { ...auctionEnd }); + events.emit(constants.EVENTS.BID_WON, { ...bidWon, custom_5: true }); + + clock.tick(1500) + + const request = JSON.parse(server.requests[3].requestBody) + + expect(request.custom_1).to.equal(undefined) + expect(request.custom_6).to.equal(undefined) + expect(request.adUnits[0].bids[0].custom_2).to.equal(undefined) + expect(request.adUnits[0].bids[0].custom_4).to.equal(undefined) + expect(request.adUnits[0].bids[0].custom_5).to.equal(undefined) + expect(request.adUnits[0].bids[0].custom_7).to.equal(undefined) + }) + + it('should omit fields from raw data according to server configuration', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'auctionInit': { + 'sendRaw': 1, + 'omitRawFields': ['custom_1'] + }, + } + })) + + events.emit(constants.EVENTS.AUCTION_INIT, { ...auctionInit, custom_1: true }); + + clock.tick(1500) + + const request = JSON.parse(server.requests[3].requestBody) + + expect(request.eventType).to.equal('auctionInit') + expect(request.custom_1).to.equal(undefined) + }) + + it('should send raw data to custom endpoint if exists in server configuration', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'auctionInit': { + 'sendRaw': 1, + 'endpoint': 'https://pbjs.com' + }, + } + })) + + events.emit(constants.EVENTS.AUCTION_INIT, { ...auctionInit }); + + expect(server.requests[3].url).to.equal('https://pbjs.com') + }) + + it('should send raw events based on server configuration', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + clock.next() + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'auctionInit': { + 'sendRaw': 0, + }, + 'bidRequested': { + 'sendRaw': 1, + } + } + })) + + events.emit(constants.EVENTS.AUCTION_INIT, auctionInit) + events.emit(constants.EVENTS.BID_REQUESTED, bidRequested); + + const request = JSON.parse(server.requests[3].requestBody) + + expect(request).to.deep.equal({ + eventType: 'bidRequested', + pid: 123, + pvid: PAGEVIEW_ID, + pbModuleVersion: '1.0.0', + ...bidRequested + }) + }) + + it('should queue events and raw events until server configuration resolves', () => { + ooloAnalytics.enableAnalytics({ + provider: 'oolo', + options: { + pid: 123 + } + }) + + simulateAuction() + clock.tick(1500) + + expect(server.requests).to.have.length(3) + + server.requests[0].respond(200, {}, JSON.stringify({ + 'events': { + 'auctionInit': { + 'sendRaw': 1, + }, + 'bidRequested': { + 'sendRaw': 1, + } + } + })) + + expect(server.requests).to.have.length(5) + expect(JSON.parse(server.requests[3].requestBody).eventType).to.equal('auctionInit') + expect(JSON.parse(server.requests[4].requestBody).eventType).to.equal('bidRequested') + }) + }); + + describe('buildAuctionData', () => { + let auction = { + auctionId, + auctionStart, + auctionEnd, + adUnitCodes: ['mid_1'], + auctionStatus: 'running', + bidderRequests: [], + bidsReceived: [], + noBids: [], + winningBids: [], + timestamp: 1234567, + config: {}, + adUnits: { + mid_1: { + adUnitCode: 'mid_1', + code: 'mid_1', + transactionId: '123dsafasdf', + bids: { + [bidId1]: { + adUnitCode: 'mid_1', + cpm: 0.5 + } + } + } + } + } + + it('should turn adUnits and bids objects into arrays', () => { + const auctionData = buildAuctionData(auction, []) + + expect(auctionData.adUnits).to.be.an('array') + expect(auctionData.adUnits[0].bids).to.be.an('array') + }) + + it('should remove fields from the auction', () => { + const auctionData = buildAuctionData(auction, []) + const auctionFields = Object.keys(auctionData) + const adUnitFields = Object.keys(auctionData.adUnits[0]) + + expect(auctionFields).not.to.contain('adUnitCodes') + expect(auctionFields).not.to.contain('auctionStatus') + expect(auctionFields).not.to.contain('bidderRequests') + expect(auctionFields).not.to.contain('bidsReceived') + expect(auctionFields).not.to.contain('noBids') + expect(auctionFields).not.to.contain('winningBids') + expect(auctionFields).not.to.contain('timestamp') + expect(auctionFields).not.to.contain('config') + + expect(adUnitFields).not.to.contain('adUnitCoe') + expect(adUnitFields).not.to.contain('code') + expect(adUnitFields).not.to.contain('transactionId') + }) + }) + + describe('generatePageViewId', () => { + it('should generate a 19 digits number', () => { + expect(generatePageViewId()).length(19) + }) + }) +}); From 4662f060b42c55106d2099f25a4e5a480705b43e Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 15 Oct 2020 09:12:34 -0700 Subject: [PATCH 0286/1476] Add sharedid support to pubcommon (#5850) * Add sharedid support to pubcommon * Add sharedid support to pubcommon - fix typos * Add sharedid support to pubcommon - delete sharedid cookie when opt-out * Add sharedid support to pubcommon - disable sharedid by default --- modules/pubCommonIdSystem.js | 235 ++++++++++++++++++++--- test/spec/modules/userId_spec.js | 311 ++++++++++++++++++++++++++++++- 2 files changed, 517 insertions(+), 29 deletions(-) diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index cb60a1b559c..51b4335fe60 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -8,13 +8,150 @@ import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; +import {ajax} from '../src/ajax.js'; const PUB_COMMON_ID = 'PublisherCommonId'; - const MODULE_NAME = 'pubCommonId'; +const COOKIE = 'cookie'; +const LOCAL_STORAGE = 'html5'; +const SHAREDID_OPT_OUT_VALUE = '00000000000000000000000000'; +const SHAREDID_URL = 'https://id.sharedid.org/id'; +const SHAREDID_SUFFIX = '_sharedid'; +const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const SHAREDID_DEFAULT_STATE = false; + const storage = getStorageManager(null, 'pubCommonId'); +/** + * Store sharedid in either cookie or local storage + * @param {Object} config Need config.storage object to derive key, expiry time, and storage type. + * @param {string} value Shareid value to store + */ + +function storeData(config, value) { + try { + if (value) { + const key = config.storage.name + SHAREDID_SUFFIX; + const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); + + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + storage.setCookie(key, value, expiresStr, 'LAX', COOKIE_DOMAIN); + } + } else if (config.storage.type === LOCAL_STORAGE) { + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(`${key}_exp`, expiresStr); + storage.setDataInLocalStorage(key, value); + } + } + } + } catch (error) { + utils.logError(error); + } +} + +/** + * Read sharedid from cookie or local storage + * @param config Need config.storage to derive key and storage type + * @return {string} + */ +function readData(config) { + try { + const key = config.storage.name + SHAREDID_SUFFIX; + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + return storage.getCookie(key); + } + } else if (config.storage.type === LOCAL_STORAGE) { + if (storage.hasLocalStorage()) { + const expValue = storage.getDataFromLocalStorage(`${key}_exp`); + if (!expValue) { + return storage.getDataFromLocalStorage(key); + } else if ((new Date(expValue)).getTime() - Date.now() > 0) { + return storage.getDataFromLocalStorage(key) + } + } + } + } catch (error) { + utils.logError(error); + } +} + +/** + * Delete sharedid from cookie or local storage + * @param config Need config.storage to derive key and storage type + */ +function delData(config) { + try { + const key = config.storage.name + SHAREDID_SUFFIX; + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + storage.setCookie(key, '', EXPIRED_COOKIE_DATE); + } + } else if (config.storage.type === LOCAL_STORAGE) { + storage.removeDataFromLocalStorage(`${key}_exp`); + storage.removeDataFromLocalStorage(key); + } + } catch (error) { + utils.logError(error); + } +} + +/** + * setup success and error handler for sharedid callback thru ajax + * @param {string} pubcid Current pubcommon id + * @param {function} callback userId module callback. + * @param {Object} config Need config.storage to derive sharedid storage params + * @return {{success: success, error: error}} + */ + +function handleResponse(pubcid, callback, config) { + return { + success: function (responseBody) { + if (responseBody) { + try { + let responseObj = JSON.parse(responseBody); + utils.logInfo('PubCommonId: Generated SharedId: ' + responseObj.sharedId); + if (responseObj.sharedId) { + if (responseObj.sharedId !== SHAREDID_OPT_OUT_VALUE) { + // Store sharedId locally + storeData(config, responseObj.sharedId); + } else { + // Delete local copy if the user has opted out + delData(config); + } + } + // Pass pubcid even though there is no change in order to trigger decode + callback(pubcid); + } catch (error) { + utils.logError(error); + } + } + }, + error: function (statusText, responseBody) { + utils.logInfo('PubCommonId: failed to get sharedid'); + } + } +} + +/** + * Wraps pixelCallback in order to call sharedid sync + * @param {string} pubcid Pubcommon id value + * @param {function|undefined} pixelCallback fires a pixel to first party server + * @param {Object} config Need config.storage to derive sharedid storage params. + * @return {function(...[*]=)} + */ + +function getIdCallback(pubcid, pixelCallback, config) { + return function (callback) { + if (typeof pixelCallback === 'function') { + pixelCallback(); + } + ajax(SHAREDID_URL, handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); + } +} + /** @type {Submodule} */ export const pubCommonIdSubmodule = { /** @@ -22,6 +159,7 @@ export const pubCommonIdSubmodule = { * @type {string} */ name: MODULE_NAME, + /** * Return a callback function that calls the pixelUrl with id as a query parameter * @param pixelUrl @@ -46,52 +184,93 @@ export const pubCommonIdSubmodule = { * decode the stored id value for passing to bid requests * @function * @param {string} value + * @param {SubmoduleConfig} config * @returns {{pubcid:string}} */ - decode(value) { - return { 'pubcid': value } + decode(value, config) { + const idObj = {'pubcid': value}; + const {params: {enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + + if (enableSharedId) { + const sharedId = readData(config); + if (sharedId) idObj['sharedid'] = {id: sharedId}; + } + + return idObj; }, /** * performs action to obtain id * @function - * @param {SubmoduleConfig} [config] + * @param {SubmoduleConfig} [config] Config object with params and storage properties + * @param {Object} consentData + * @param {string} storedId Existing pubcommon id * @returns {IdResponse} */ - getId: function ({params: {create = true, pixelUrl} = {}} = {}) { - try { - if (typeof window[PUB_COMMON_ID] === 'object') { - // If the page includes its own pubcid module, then save a copy of id. - return {id: window[PUB_COMMON_ID].getId()}; + getId: function (config = {}, consentData, storedId) { + const {params: {create = true, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + let newId = storedId; + if (!newId) { + try { + if (typeof window[PUB_COMMON_ID] === 'object') { + // If the page includes its own pubcid module, then save a copy of id. + newId = window[PUB_COMMON_ID].getId(); + } + } catch (e) { } - } catch (e) { - } - const newId = (create && utils.hasDeviceAccess()) ? utils.generateUUID() : undefined; - return { - id: newId, - callback: this.makeCallback(pixelUrl, newId) + if (!newId) newId = (create && utils.hasDeviceAccess()) ? utils.generateUUID() : undefined; } + + const pixelCallback = this.makeCallback(pixelUrl, newId); + const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config) : pixelCallback; + + return {id: newId, callback: combinedCallback}; }, /** - * performs action to extend an id + * performs action to extend an id. There are generally two ways to extend the expiration time + * of stored id: using pixelUrl or return the id and let main user id module write it again with + * the new expiration time. + * + * PixelUrl, if defined, should point back to a first party domain endpoint. On the server + * side, there is either a plugin, or customized logic to read and write back the pubcid cookie. + * The extendId function itself should return only the callback, and not the id itself to avoid + * having the script-side overwriting server-side. This applies to both pubcid and sharedid. + * + * On the other hand, if there is no pixelUrl, then the extendId should return storedId so that + * its expiration time is updated. Sharedid, however, will have to be updated by this submodule + * separately. + * * @function * @param {SubmoduleParams} [config] * @param {Object} storedId existing id * @returns {IdResponse|undefined} */ - extendId: function({params: {extend = false, pixelUrl} = {}} = {}, storedId) { - try { - if (typeof window[PUB_COMMON_ID] === 'object') { - // If the page includes its onw pubcid module, then there is nothing to do. - return; - } - } catch (e) { - } + extendId: function(config = {}, storedId) { + const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; if (extend) { - // When extending, only one of response fields is needed - const callback = this.makeCallback(pixelUrl, storedId); - return callback ? {callback: callback} : {id: storedId}; + try { + if (typeof window[PUB_COMMON_ID] === 'object') { + if (enableSharedId) { + // If the page includes its own pubcid module, then there is nothing to do + // except to update sharedid's expiration time + storeData(config, readData(config)); + } + return; + } + } catch (e) { + } + + if (pixelUrl) { + const callback = this.makeCallback(pixelUrl, storedId); + return {callback: callback}; + } else { + if (enableSharedId) { + // Update with the same value to extend expiration time + storeData(config, readData(config)); + } + return {id: storedId}; + } } }, @@ -123,4 +302,6 @@ export const pubCommonIdSubmodule = { } }; +const COOKIE_DOMAIN = pubCommonIdSubmodule.domainOverride(); + submodule('userId', pubCommonIdSubmodule); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 5b1faa46979..df72e59e7e7 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -84,6 +84,12 @@ describe('User ID', function () { return cfg; } + function findEid(eids, source) { + return eids.find((eid) => { + if (eid.source === source) { return true; } + }); + } + before(function () { coreStorage.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); localStorage.removeItem('_pbjs_id_optout'); @@ -825,6 +831,10 @@ describe('User ID', function () { source: 'pubcid.org', uids: [{id: 'testpubcid', atype: 1}] }); + + // verify no sharedid was added + expect(bid.userId).to.not.have.property('sharedid'); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -832,6 +842,36 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from pubcommonid html5', function (done) { + // simulate existing browser local storage values + localStorage.setItem('pubcid', 'testpubcid'); + localStorage.setItem('pubcid_exp', new Date(Date.now() + 100000).toUTCString()); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'html5'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'pubcid.org', + uids: [{id: 'testpubcid', atype: 1}] + }); + + // verify no sharedid was added + expect(bid.userId).to.not.have.property('sharedid'); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; + }); + }); + localStorage.removeItem('pubcid'); + localStorage.removeItem('pubcid_exp'); + done(); + }, {adUnits}); + }); + it('test hook from pubcommonid config value object', function (done) { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); @@ -849,7 +889,7 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook from pubcommonid html5', function (done) { + it('test hook from UnifiedId html5', function (done) { // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); @@ -1644,6 +1684,192 @@ describe('User ID', function () { }, {adUnits}); }); + it('test sharedid enabled via pubcid cookie', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + + const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + addConfig(customCfg, 'params', {enableSharedId: true}); + config.setConfig(customCfg); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( + {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} + ); + // verify that the sharedid was also copied to bid + expect(bid).to.have.deep.nested.property('userId.sharedid'); + expect(bid.userId.sharedid).to.deep.equal({id: 'testsharedid'}); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.deep.equal( + {'source': 'sharedid.org', 'uids': [{'id': 'testsharedid', 'atype': 1}]} + ); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); + + done(); + }, {adUnits}); + }); + + it('test sharedid disabled via pubcid cookie', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + + // pubCommonId's support for sharedId is off by default + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( + {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} + ); + // verify that the sharedid was not added to bid + expect(bid.userId).to.not.have.property('sharedid'); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); + + done(); + }, {adUnits}); + }); + + it('test sharedid enabled via pubcid html5', function (done) { + coreStorage.setDataInLocalStorage('pubcid', 'testpubcid'); + coreStorage.setDataInLocalStorage('pubcid_exp', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setDataInLocalStorage('pubcid_sharedid', 'testsharedid'); + coreStorage.setDataInLocalStorage('pubcid_sharedid_exp', new Date(Date.now() + 5000).toUTCString()); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + + const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'html5']); + addConfig(customCfg, 'params', {enableSharedId: true}); + config.setConfig(customCfg); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( + {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} + ); + // verify that the sharedid was also copied to bid + expect(bid).to.have.deep.nested.property('userId.sharedid'); + expect(bid.userId.sharedid).to.deep.equal({id: 'testsharedid'}); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.deep.equal( + {'source': 'sharedid.org', 'uids': [{'id': 'testsharedid', 'atype': 1}]} + ); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); + + coreStorage.removeDataFromLocalStorage('pubcid'); + coreStorage.removeDataFromLocalStorage('pubcid_exp'); + coreStorage.removeDataFromLocalStorage('pubcid_sharedid'); + coreStorage.removeDataFromLocalStorage('pubcid_sharedid_exp'); + done(); + }, {adUnits}); + }); + + it('test expired sharedid via pubcid html5', function (done) { + coreStorage.setDataInLocalStorage('pubcid', 'testpubcid'); + coreStorage.setDataInLocalStorage('pubcid_exp', new Date(Date.now() + 5000).toUTCString()); + + // set sharedid to expired already + coreStorage.setDataInLocalStorage('pubcid_sharedid', 'testsharedid'); + coreStorage.setDataInLocalStorage('pubcid_sharedid_exp', new Date(Date.now() - 100).toUTCString()); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + + const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'html5']); + addConfig(customCfg, 'params', {enableSharedId: true}); + config.setConfig(customCfg); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( + {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} + ); + // verify that the sharedid was not added + expect(bid.userId).to.not.have.property('sharedid'); + expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; + }); + }); + + coreStorage.removeDataFromLocalStorage('pubcid'); + coreStorage.removeDataFromLocalStorage('pubcid_exp'); + coreStorage.removeDataFromLocalStorage('pubcid_sharedid'); + coreStorage.removeDataFromLocalStorage('pubcid_sharedid_exp'); + done(); + }, {adUnits}); + }); + + it('test pubcid coexisting with sharedid', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('pubcid_sharedid', 'test111', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test222', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); + + // When both pubcommon and sharedid are configured, pubcommon are invoked first due to + // module loading order. This mean the output from the primary sharedid module will overwrite + // the one in pubcommon. + + setSubmoduleRegistry([pubCommonIdSubmodule, sharedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['sharedId', 'sharedid', 'cookie'], + )); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( + {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} + ); + // verify that the sharedid was also copied to bid + expect(bid).to.have.deep.nested.property('userId.sharedid'); + expect(bid.userId.sharedid.id).to.equal('test222'); + expect(findEid(bid.userIdAsEids, 'sharedid.org').uids[0].id).to.equal('test222'); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); + + done(); + }, {adUnits}); + }); + it('should add new id system ', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); @@ -1769,6 +1995,7 @@ describe('User ID', function () { sinon.stub(events, 'getEvents').returns([]); sinon.stub(utils, 'triggerPixel'); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); @@ -1777,6 +2004,7 @@ describe('User ID', function () { events.getEvents.restore(); utils.triggerPixel.restore(); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); @@ -1784,7 +2012,7 @@ describe('User ID', function () { it('pubcid callback with url', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; - let customCfg = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); @@ -1834,6 +2062,85 @@ describe('User ID', function () { events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); }); + + it('verify sharedid callback via pubcid when enabled', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); + + server.respondWith('https://id.sharedid.org/id', function(xhr) { + xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); + }); + server.respondImmediately = true; + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + + expect(server.requests[0].url).to.equal('https://id.sharedid.org/id'); + expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid'); + }); + + it('verify no sharedid callback via pubcid when disabled', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); + + server.respondWith('https://id.sharedid.org/id', function(xhr) { + xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); + }); + server.respondImmediately = true; + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + + expect(server.requests).to.have.lengthOf(0); + expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null; + }); + + it('verify sharedid optout via pubcid when enabled', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); + coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); + + server.respondWith('https://id.sharedid.org/id', function(xhr) { + xhr.respond(200, {}, '{"sharedId":"00000000000000000000000000"}'); + }); + server.respondImmediately = true; + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + + expect(server.requests[0].url).to.equal('https://id.sharedid.org/id'); + expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null; + }); }); describe('Set cookie behavior', function () { From aaa7dc98b9a6de4cf06f533089b812340371b47e Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 16 Oct 2020 07:29:43 -0400 Subject: [PATCH 0287/1476] PR Review process tweaks (#5862) Incorporating feedback --- PR_REVIEW.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 1dd8fbcec0d..a8b68c7ab45 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -53,6 +53,8 @@ Documentation they're supposed to be following is https://docs.prebid.org/dev-do No additional steps above the general review process and making sure it conforms to the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html). +Make sure there's a docs pull request + ### Reviewing a New or Updated User ID Sub-Module Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/modules/userId.html#id-providers @@ -79,6 +81,7 @@ Follow steps above for general review process. In addition: - in the eids.js file, the source should be the actual domain of the provider, not a made up domain. - in the eids.js file, the key in the array should be the same value as the key in the decode function - make sure all supported config params align in the submodule js file and the docs / examples +- make sure there's a docs pull request ### Reviewing a New or Updated Real-Time-Data Sub-Module Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/add-rtd-submodule.html @@ -86,9 +89,9 @@ Documentation they're supposed to be following is https://docs.prebid.org/dev-do Follow steps above for general review process. In addition: - The RTD Provider must include a `providerRtdProvider.md` file. This file must have example parameters and document a sense of what to expect: what should change in the bidrequest, or what targeting data should be added? - Try running the new sub-module and confirm the provided test parameters. -- Make sure the sub-module is "phoning home" as early as possible, but not more often than needed. +- Make sure the sub-module is making HTTP requests as early as possible, but not more often than needed. - Consider whether the kind of data the module is obtaining could have privacy implications. If so, make sure they're utilizing the `consent` data passed to them. - +- make sure there's a docs pull request ## Ticket Coordinator From 368b6023edaa59ba2ec1ba253be8e841ee17d58a Mon Sep 17 00:00:00 2001 From: John Rosendahl Date: Fri, 16 Oct 2020 07:04:59 -0600 Subject: [PATCH 0288/1476] Added basic support for ID Module (#5835) Co-authored-by: John Rosendahl --- modules/sovrnBidAdapter.js | 37 +++++++++++++---------- test/spec/modules/sovrnBidAdapter_spec.js | 23 +++++++++----- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index f3260668b74..935b0ceb489 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -1,10 +1,12 @@ import * as utils from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' +import { createEidsArray } from './userId/eids.js'; export const spec = { code: 'sovrn', supportedMediaTypes: [BANNER], + gvlid: 13, /** * Check if the bid is a valid zone ID in either number or string form @@ -25,11 +27,21 @@ export const spec = { let sovrnImps = []; let iv; let schain; - let unifiedID; + let eids; + let tpid = [] + let criteoId; utils._each(bidReqs, function (bid) { - if (!unifiedID) { - unifiedID = utils.deepAccess(bid, 'userId.tdid'); + if (!eids && bid.userId) { + eids = createEidsArray(bid.userId) + eids.forEach(function (id) { + if (id.uids && id.uids[0]) { + if (id.source === 'criteo.com') { + criteoId = id.uids[0].id + } + tpid.push({source: id.source, uid: id.uids[0].id}) + } + }) } if (bid.schain) { @@ -84,19 +96,12 @@ export const spec = { utils.deepSetValue(sovrnBidReq, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (unifiedID) { - const idArray = [{ - source: 'adserver.org', - uids: [ - { - id: unifiedID, - ext: { - rtiPartner: 'TDID' - } - } - ] - }] - utils.deepSetValue(sovrnBidReq, 'user.ext.eids', idArray) + if (eids) { + utils.deepSetValue(sovrnBidReq, 'user.ext.eids', eids) + utils.deepSetValue(sovrnBidReq, 'user.ext.tpid', tpid) + if (criteoId) { + utils.deepSetValue(sovrnBidReq, 'user.ext.prebid_criteoid', criteoId) + } } let url = `https://ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$`; diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 54ccff870eb..321fed40d83 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -239,8 +239,8 @@ describe('sovrnBidAdapter', function() { expect(data.source.ext.schain.nodes.length).to.equal(1) }); - it('should add the unifiedID if present', function() { - const digitrustRequests = [{ + it('should add ids to the bid request', function() { + const criteoIdRequest = [{ 'bidder': 'sovrn', 'params': { 'tagid': 403370 @@ -254,6 +254,7 @@ describe('sovrnBidAdapter', function() { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'userId': { + 'criteoId': 'A_CRITEO_ID', 'tdid': 'SOMESORTOFID', } }].concat(bidRequests); @@ -263,12 +264,20 @@ describe('sovrnBidAdapter', function() { } }; - const data = JSON.parse(spec.buildRequests(digitrustRequests, bidderRequest).data); - expect(data.user.ext.eids[0].source).to.equal('adserver.org') - expect(data.user.ext.eids[0].uids[0].id).to.equal('SOMESORTOFID') - expect(data.user.ext.eids[0].uids[0].ext.rtiPartner).to.equal('TDID') - }) + const data = JSON.parse(spec.buildRequests(criteoIdRequest, bidderRequest).data); + expect(data.user.ext.eids[0].source).to.equal('criteo.com') + expect(data.user.ext.eids[0].uids[0].id).to.equal('A_CRITEO_ID') + expect(data.user.ext.eids[0].uids[0].atype).to.equal(1) + expect(data.user.ext.eids[1].source).to.equal('adserver.org') + expect(data.user.ext.eids[1].uids[0].id).to.equal('SOMESORTOFID') + expect(data.user.ext.eids[1].uids[0].ext.rtiPartner).to.equal('TDID') + expect(data.user.ext.eids[1].uids[0].atype).to.equal(1) + expect(data.user.ext.tpid[0].source).to.equal('criteo.com') + expect(data.user.ext.tpid[0].uid).to.equal('A_CRITEO_ID') + expect(data.user.ext.prebid_criteoid).to.equal('A_CRITEO_ID') + }); }); + describe('interpretResponse', function () { let response; beforeEach(function () { From 926ccc6ae96057bf64517ed4b8428a0dddb72a37 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Fri, 16 Oct 2020 10:05:12 -0400 Subject: [PATCH 0289/1476] Rename pubProvidedSystem.js to pubProvidedIdSystem.js (#5861) * Rename pubProvidedSystem.js to pubProvidedIdSystem.js * Update userId_spec.js --- modules/{pubProvidedSystem.js => pubProvidedIdSystem.js} | 0 test/spec/modules/userId_spec.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename modules/{pubProvidedSystem.js => pubProvidedIdSystem.js} (100%) diff --git a/modules/pubProvidedSystem.js b/modules/pubProvidedIdSystem.js similarity index 100% rename from modules/pubProvidedSystem.js rename to modules/pubProvidedIdSystem.js diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index df72e59e7e7..c5ab2e249fc 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -33,7 +33,7 @@ import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; import {haloIdSubmodule} from 'modules/haloIdSystem.js'; -import {pubProvidedIdSubmodule} from 'modules/pubProvidedSystem.js'; +import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js'; import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; let assert = require('chai').assert; From d8f43de3f8b1a3d74a90e43ebf143e0e397ce06f Mon Sep 17 00:00:00 2001 From: Rahul Shandilya <67756716+c3p-0@users.noreply.github.com> Date: Tue, 20 Oct 2020 17:54:37 +0530 Subject: [PATCH 0290/1476] Adding Medianet outstream renderer support (#5854) --- modules/medianetBidAdapter.js | 45 +++++++++++++++++++- test/spec/modules/medianetBidAdapter_spec.js | 41 ++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 49e2bdc225f..a30f6fc2627 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -3,9 +3,11 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getRefererInfo } from '../src/refererDetection.js'; +import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'medianet'; const BID_URL = 'https://prebid.media.net/rtb/prebid'; +const PLAYER_URL = 'https://prebid.media.net/video/bundle.js'; const SLOT_VISIBILITY = { NOT_DETERMINED: 0, ABOVE_THE_FOLD: 1, @@ -16,10 +18,14 @@ const EVENTS = { BID_WON_EVENT_NAME: 'client_bid_won' }; const EVENT_PIXEL_URL = 'qsearch-a.akamaihd.net/log'; - +const OUTSTREAM = 'outstream'; let refererInfo = getRefererInfo(); let mnData = {}; + +window.mnet = window.mnet || {}; +window.mnet.queue = window.mnet.queue || []; + mnData.urlData = { domain: utils.parseUrl(refererInfo.referer).hostname, page: refererInfo.referer, @@ -321,6 +327,40 @@ function clearMnData() { mnData = {}; } +function addRenderer(bid) { + const videoContext = utils.deepAccess(bid, 'context') || ''; + const vastTimeout = utils.deepAccess(bid, 'vto'); + /* Adding renderer only when the context is Outstream + and the provider has responded with a renderer. + */ + if (videoContext == OUTSTREAM && vastTimeout) { + bid.renderer = newVideoRenderer(bid); + } +} + +function newVideoRenderer(bid) { + const renderer = Renderer.install({ + url: PLAYER_URL, + }); + renderer.setRender(function (bid) { + window.mnet.queue.push(function () { + const obj = { + width: bid.width, + height: bid.height, + vastTimeout: bid.vto, + maxAllowedVastTagRedirects: bid.mavtr, + allowVpaid: bid.avp, + autoPlay: bid.ap, + preload: bid.pl, + mute: bid.mt + } + const adUnitCode = bid.dfp_id; + const divId = utils.getGptSlotInfoForAdUnitCode(adUnitCode).divId || adUnitCode; + window.mnet.mediaNetoutstreamPlayer(bid, divId, obj); + }); + }); + return renderer; +} export const spec = { code: BIDDER_CODE, @@ -387,9 +427,10 @@ export const spec = { } validBids = bids.filter(bid => isValidBid(bid)); + validBids.forEach(addRenderer); + return validBids; }, - getUserSyncs: function(syncOptions, serverResponses) { let cookieSyncUrls = fetchCookieSyncUrls(serverResponses); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 1b2207de842..1eeb167601e 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -952,6 +952,42 @@ let VALID_BID_REQUEST = [{ } } }, + SERVER_VIDEO_OUTSTREAM_RESPONSE_VALID_BID = { + body: { + 'id': 'd90ca32f-3877-424a-b2f2-6a68988df57a', + 'bidList': [{ + 'no_bid': false, + 'requestId': '27210feac00e96', + 'cpm': 12.00, + 'width': 640, + 'height': 480, + 'ttl': 180, + 'creativeId': '370637746', + 'netRevenue': true, + 'vastXml': '', + 'currency': 'USD', + 'dfp_id': 'video1', + 'mediaType': 'video', + 'vto': 5000, + 'mavtr': 10, + 'avp': true, + 'ap': true, + 'pl': true, + 'mt': true, + 'jslt': 3000, + 'context': 'outstream' + }], + 'ext': { + 'csUrl': [{ + 'type': 'image', + 'url': 'http://cs.media.net/cksync.php' + }, { + 'type': 'iframe', + 'url': 'http://contextual.media.net/checksync.php?&vsSync=1' + }] + } + } + }, SERVER_VALID_BIDS = [{ 'no_bid': false, 'requestId': '27210feac00e96', @@ -1405,4 +1441,9 @@ describe('Media.net bid adapter', function () { expect(response).to.deep.equal(undefined); }); }); + + it('context should be outstream', function () { + let bids = spec.interpretResponse(SERVER_VIDEO_OUTSTREAM_RESPONSE_VALID_BID, []); + expect(bids[0].context).to.equal('outstream'); + }); }); From 449a9fa33d910e07a6c26e63e505e47afaea7a89 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 20 Oct 2020 10:45:31 -0400 Subject: [PATCH 0291/1476] PR-review: fixed getFloor function name (#5876) --- PR_REVIEW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index a8b68c7ab45..a4bcc43b11a 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -31,7 +31,7 @@ Follow steps above for general review process. In addition, please verify the fo - All bidder parameter conventions must be followed: - Video params must be read from AdUnit.mediaTypes.video when available; however bidder config can override the ad unit. - First party data must be read from [`fpd.context` and `fpd.user`](https://docs.prebid.org/dev-docs/publisher-api-reference.html#setConfig-fpd). - - Adapters that accept a floor parameter must also support the [floors module](https://docs.prebid.org/dev-docs/modules/floors.html) -- look for a call to the `getFloors()` function. + - Adapters that accept a floor parameter must also support the [floors module](https://docs.prebid.org/dev-docs/modules/floors.html) -- look for a call to the `getFloor()` function. - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidRequest.schain. - The bidRequest page referrer must checked in addition to any bidder-specific parameter. - If they're getting the COPPA flag, it must come from config.getConfig('coppa'); From 5d9a0e62da6af5546d4dd3f33742de8e0dca28ce Mon Sep 17 00:00:00 2001 From: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Date: Tue, 20 Oct 2020 18:24:52 +0300 Subject: [PATCH 0292/1476] Real Time Data Module - Phase3 (#5783) * 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 * RTD module extend #4610 * add hook for submodule init variables naming * RTD bug fix * remove auction delay and related hooks * RTD phase 3 * design changes * fix loop continuation * proper fix this time * linter * reduce loops Co-authored-by: bretg --- modules/browsiRtdProvider.js | 122 +++----- modules/rtdModule/index.js | 306 +++++++++---------- src/targeting.js | 24 +- test/spec/modules/browsiRtdProvider_spec.js | 83 +++++ test/spec/modules/realTimeDataModule_spec.js | 160 ++++++++++ test/spec/modules/realTimeModule_spec.js | 274 ----------------- 6 files changed, 453 insertions(+), 516 deletions(-) create mode 100644 test/spec/modules/browsiRtdProvider_spec.js create mode 100644 test/spec/modules/realTimeDataModule_spec.js delete mode 100644 test/spec/modules/realTimeModule_spec.js diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 3aff3c6aac6..4ee338e94cc 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -13,30 +13,21 @@ * @property {string} pubKey * @property {string} url * @property {?string} keyName - * @property {?number} auctionDelay - * @property {?number} timeout */ -import {config} from '../src/config.js'; import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; import {ajaxBuilder} from '../src/ajax.js'; import {loadExternalScript} from '../src/adloader.js'; -import { getStorageManager } from '../src/storageManager.js'; +import {getStorageManager} from '../src/storageManager.js'; import find from 'core-js-pure/features/array/find.js'; const storage = getStorageManager(); -/** @type {string} */ -const MODULE_NAME = 'realTimeData'; -/** @type {number} */ -const DEF_TIMEOUT = 1000; /** @type {ModuleParams} */ let _moduleParams = {}; /** @type {null|Object} */ -let _data = null; -/** @type {null | function} */ -let _dataReadyCallback = null; +let _predictionsData = null; /** @type {string} */ const DEF_KEYNAME = 'browsiViewability'; @@ -63,7 +54,7 @@ export function addBrowsiTag(data) { * collect required data from page * send data to browsi server to get predictions */ -function collectData() { +export function collectData() { const win = window.top; const doc = win.document; let browsiData = null; @@ -88,59 +79,33 @@ function collectData() { } export function setData(data) { - _data = data; - - if (typeof _dataReadyCallback === 'function') { - _dataReadyCallback(_data); - _dataReadyCallback = null; - } -} - -/** - * wait for data from server - * call callback when data is ready - * @param {function} callback - */ -function waitForData(callback) { - if (_data) { - _dataReadyCallback = null; - callback(_data); - } else { - _dataReadyCallback = callback; - } + _predictionsData = data; } -/** - * filter server data according to adUnits received - * call callback (onDone) when data is ready - * @param {adUnit[]} adUnits - * @param {function} onDone callback function - */ -function sendDataToModule(adUnits, onDone) { +function sendDataToModule(adUnitsCodes) { try { - waitForData(_predictionsData => { - const _predictions = _predictionsData.p || {}; - let dataToReturn = adUnits.reduce((rp, cau) => { - const adUnitCode = cau && cau.code; - if (!adUnitCode) { return rp } - const adSlot = getSlotByCode(adUnitCode); - const identifier = adSlot ? getMacroId(_predictionsData.pmd, adSlot) : adUnitCode; - const predictionData = _predictions[identifier]; - rp[adUnitCode] = getKVObject(-1, _predictionsData.kn); - if (!predictionData) { return rp } - - if (predictionData.p) { - if (!isIdMatchingAdUnit(adSlot, predictionData.w)) { - return rp; - } - rp[adUnitCode] = getKVObject(predictionData.p, _predictionsData.kn); + const _predictions = (_predictionsData && _predictionsData.p) || {}; + return adUnitsCodes.reduce((rp, adUnitCode) => { + if (!adUnitCode) { + return rp + } + const adSlot = getSlotByCode(adUnitCode); + const identifier = adSlot ? getMacroId(_predictionsData['pmd'], adSlot) : adUnitCode; + const predictionData = _predictions[identifier]; + rp[adUnitCode] = getKVObject(-1, _predictionsData['kn']); + if (!predictionData) { + return rp + } + if (predictionData.p) { + if (!isIdMatchingAdUnit(adSlot, predictionData.w)) { + return rp; } - return rp; - }, {}); - return onDone(dataToReturn); - }); + rp[adUnitCode] = getKVObject(predictionData.p, _predictionsData.kn); + } + return rp; + }, {}); } catch (e) { - onDone({}); + return {}; } } @@ -231,7 +196,7 @@ function evaluate(macro, divId, adUnit, replacer) { * @param {string} url server url with query params */ function getPredictionsFromServer(url) { - let ajax = ajaxBuilder(_moduleParams.auctionDelay || _moduleParams.timeout); + let ajax = ajaxBuilder(); ajax(url, { @@ -283,38 +248,23 @@ export const browsiSubmodule = { /** * get data and send back to realTimeData module * @function - * @param {adUnit[]} adUnits - * @param {function} onDone + * @param {string[]} adUnitsCodes */ - getData: sendDataToModule, - init: init + getTargetingData: sendDataToModule, + init: init, }; -function init(config, gdpr, usp) { +function init(moduleConfig) { + _moduleParams = moduleConfig.params; + if (_moduleParams && _moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) { + collectData(); + } else { + utils.logError('missing params for Browsi provider'); + } return true; } -export function beforeInit(config) { - const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { - try { - _moduleParams = realTimeData.dataProviders && realTimeData.dataProviders.filter( - pr => pr.name && pr.name.toLowerCase() === 'browsi')[0].params; - confListener(); - _moduleParams.auctionDelay = realTimeData.auctionDelay; - _moduleParams.timeout = realTimeData.timeout || DEF_TIMEOUT; - } catch (e) { - _moduleParams = {}; - } - if (_moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) { - collectData(); - } else { - utils.logError('missing params for Browsi provider'); - } - }); -} - function registerSubModule() { submodule('realTimeData', browsiSubmodule); } registerSubModule(); -beforeInit(config); diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 9acd484cec8..e235868f791 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -3,16 +3,49 @@ * @module modules/realTimeData */ +/** + * @interface UserConsentData + */ +/** + * @property + * @summary gdpr consent + * @name UserConsentData#gdpr + * @type {Object} + */ +/** + * @property + * @summary usp consent + * @name UserConsentData#usp + * @type {Object} + */ +/** + * @property + * @summary coppa + * @name UserConsentData#coppa + * @type {boolean} + */ + /** * @interface RtdSubmodule */ /** - * @function + * @function? * @summary return real time data - * @name RtdSubmodule#getData - * @param {AdUnit[]} adUnits - * @param {function} onDone + * @name RtdSubmodule#getTargetingData + * @param {string[]} adUnitsCodes + * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent + */ + +/** + * @function? + * @summary modify bid request data + * @name RtdSubmodule#getBidRequestData + * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent + * @param {Object} reqBidsConfigObj + * @param {function} callback */ /** @@ -33,42 +66,36 @@ * @function * @summary init sub module * @name RtdSubmodule#init - * @param {Object} config - * @param {Object} gdpr settings - * @param {Object} usp settings + * @param {SubmoduleConfig} config + * @param {UserConsentData} user consent * @return {boolean} false to remove sub module */ /** * @function? * @summary on auction init event - * @name RtdSubmodule#auctionInit + * @name RtdSubmodule#onAuctionInitEvent * @param {Object} data * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent */ /** * @function? * @summary on auction end event - * @name RtdSubmodule#auctionEnd - * @param {Object} data - * @param {SubmoduleConfig} config - */ - -/** - * @function? - * @summary on bid request event - * @name RtdSubmodule#updateBidRequest + * @name RtdSubmodule#onAuctionEndEvent * @param {Object} data * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent */ /** * @function? * @summary on bid response event - * @name RtdSubmodule#updateBidResponse + * @name RtdSubmodule#onBidResponseEvent * @param {Object} data * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent */ /** @@ -82,13 +109,6 @@ * @type {number} */ -/** - * @property - * @summary timeout (if no auction dealy) - * @name ModuleConfig#timeout - * @type {number} - */ - /** * @property * @summary list of sub modules @@ -121,33 +141,34 @@ * @type {boolean} */ -import {getGlobal} from '../../src/prebidGlobal.js'; import {config} from '../../src/config.js'; -import {targeting} from '../../src/targeting.js'; -import {getHook, module} from '../../src/hook.js'; +import {module} from '../../src/hook.js'; import * as utils from '../../src/utils.js'; import events from '../../src/events.js'; import CONSTANTS from '../../src/constants.json'; import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; import find from 'core-js-pure/features/array/find.js'; +import {getGlobal} from '../../src/prebidGlobal.js'; /** @type {string} */ const MODULE_NAME = 'realTimeData'; -/** @type {number} */ -const DEF_TIMEOUT = 1000; +/** @type {RtdSubmodule[]} */ +let registeredSubModules = []; /** @type {RtdSubmodule[]} */ export let subModules = []; /** @type {ModuleConfig} */ let _moduleConfig; /** @type {SubmoduleConfig[]} */ let _dataProviders = []; +/** @type {UserConsentData} */ +let _userConsent; /** * enable submodule in User ID * @param {RtdSubmodule} submodule */ export function attachRealTimeDataProvider(submodule) { - subModules.push(submodule); + registeredSubModules.push(submodule); } export function init(config) { @@ -159,35 +180,35 @@ export function init(config) { confListener(); // unsubscribe config listener _moduleConfig = realTimeData; _dataProviders = realTimeData.dataProviders; - getHook('makeBidRequests').before(initSubModules); setEventsListeners(); - if (typeof (_moduleConfig.auctionDelay) === 'undefined') { - _moduleConfig.auctionDelay = 0; - } - // delay bidding process only if auctionDelay > 0 - if (!_moduleConfig.auctionDelay > 0) { - getHook('bidsBackCallback').before(setTargetsAfterRequestBids); - } else { - getGlobal().requestBids.before(requestBidsHook); - } + getGlobal().requestBids.before(setBidRequestsData, 40); + initSubModules(); }); } +function getConsentData() { + return { + gdpr: gdprDataHandler.getConsentData(), + usp: uspDataHandler.getConsentData(), + coppa: !!(config.getConfig('coppa')) + } +} + /** * call each sub module init function by config order * if no init function / init return failure / module not configured - remove it from submodules list */ -export function initSubModules(next, adUnits, auctionStart, auctionId, cbTimeout, labels) { +function initSubModules() { + _userConsent = getConsentData(); let subModulesByOrder = []; _dataProviders.forEach(provider => { - const sm = find(subModules, s => s.name === provider.name); - const initResponse = sm && sm.init && sm.init(provider, gdprDataHandler.getConsentData(), uspDataHandler.getConsentData()); + const sm = find(registeredSubModules, s => s.name === provider.name); + const initResponse = sm && sm.init && sm.init(provider, _userConsent); if (initResponse) { subModulesByOrder.push(Object.assign(sm, {config: provider})); } }); subModules = subModulesByOrder; - next(adUnits, auctionStart, auctionId, cbTimeout, labels) } /** @@ -195,94 +216,117 @@ export function initSubModules(next, adUnits, auctionStart, auctionId, cbTimeout */ function setEventsListeners() { events.on(CONSTANTS.EVENTS.AUCTION_INIT, (args) => { - subModules.forEach(sm => { sm.auctionInit && sm.auctionInit(args, sm.config) }) + subModules.forEach(sm => { sm.onAuctionInitEvent && sm.onAuctionInitEvent(args, sm.config, _userConsent) }) }); events.on(CONSTANTS.EVENTS.AUCTION_END, (args) => { - subModules.forEach(sm => { sm.auctionEnd && sm.auctionEnd(args, sm.config) }) - }); - events.on(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, (args) => { - subModules.forEach(sm => { sm.updateBidRequest && sm.updateBidRequest(args, sm.config) }) + getAdUnitTargeting(args); + subModules.forEach(sm => { sm.onAuctionEndEvent && sm.onAuctionEndEvent(args, sm.config, _userConsent) }) }); events.on(CONSTANTS.EVENTS.BID_RESPONSE, (args) => { - subModules.forEach(sm => { sm.updateBidResponse && sm.updateBidResponse(args, sm.config) }) + subModules.forEach(sm => { sm.onBidResponseEvent && sm.onBidResponseEvent(args, sm.config, _userConsent) }) }); } /** - * get data from sub module - * @param {AdUnit[]} adUnits received from auction - * @param {function} callback callback function on data received + * loop through configured data providers If the data provider has registered getBidRequestData, + * call it, providing reqBidsConfigObj, consent data and module params + * this allows submodules to modify bidders + * @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 getProviderData(adUnits, callback) { - /** - * invoke callback if one of the conditions met: - * timeout reached - * all submodules answered - * all sub modules configured "waitForIt:true" answered (as long as there is at least one configured) - */ - - const waitForSubModulesLength = subModules.filter(sm => sm.config && sm.config.waitForIt).length; - let callbacksExpected = waitForSubModulesLength || subModules.length; - const shouldWaitForAllSubModules = waitForSubModulesLength === 0; - let dataReceived = {}; - let processDone = false; - const dataWaitTimeout = setTimeout(done, _moduleConfig.auctionDelay || _moduleConfig.timeout || DEF_TIMEOUT); +export function setBidRequestsData(fn, reqBidsConfigObj) { + _userConsent = getConsentData(); + + const relevantSubModules = []; + const prioritySubModules = []; subModules.forEach(sm => { - sm.getData(adUnits, onDataReceived.bind(sm)); + if (typeof sm.getBidRequestData !== 'function') { + return; + } + relevantSubModules.push(sm); + const config = sm.config; + if (config && config.waitForIt) { + prioritySubModules.push(sm); + } }); - function onDataReceived(data) { - if (processDone) { - return + const shouldDelayAuction = prioritySubModules.length && _moduleConfig.auctionDelay && _moduleConfig.auctionDelay > 0; + let callbacksExpected = prioritySubModules.length; + let isDone = false; + let waitTimeout; + + if (!relevantSubModules.length) { + return exitHook(); + } + + if (shouldDelayAuction) { + waitTimeout = setTimeout(exitHook, _moduleConfig.auctionDelay); + } + + relevantSubModules.forEach(sm => { + sm.getBidRequestData(reqBidsConfigObj, onGetBidRequestDataCallback.bind(sm), sm.config, _userConsent) + }); + + if (!shouldDelayAuction) { + return exitHook(); + } + + function onGetBidRequestDataCallback() { + if (isDone) { + return; } - dataReceived[this.name] = data; - if (shouldWaitForAllSubModules || (this.config && this.config.waitForIt)) { - callbacksExpected-- + if (this.config && this.config.waitForIt) { + callbacksExpected--; } if (callbacksExpected <= 0) { - clearTimeout(dataWaitTimeout); - done(); + return exitHook(); } } - function done() { - processDone = true; - callback(dataReceived); + function exitHook() { + isDone = true; + clearTimeout(waitTimeout); + fn.call(this, reqBidsConfigObj); } } /** - * run hook after bids request and before callback - * get data from provider and set key values to primary ad server - * @param {function} next - next hook function - * @param {AdUnit[]} adUnits received from auction + * loop through configured data providers If the data provider has registered getTargetingData, + * call it, providing ad unit codes, consent data and module params + * the sub mlodle will return data to set on the ad unit + * this function used to place key values on primary ad server per ad unit + * @param {Object} auction object received on auction end event */ -export function setTargetsAfterRequestBids(next, adUnits) { - getProviderData(adUnits, (data) => { - if (data && Object.keys(data).length) { - const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); - if (Object.keys(_mergedData).length) { - setDataForPrimaryAdServer(_mergedData); - } - } - next(adUnits); - }); -} +export function getAdUnitTargeting(auction) { + const relevantSubModules = subModules.filter(sm => typeof sm.getTargetingData === 'function'); + if (!relevantSubModules.length) { + return; + } -/** - * return an array providers data in reverse order,so the data merge will be according to correct config order - * @param {Submodule[]} modules - * @param {Object} data - data retrieved from providers - * @return {array} reversed order ready for merge - */ -function setDataOrderByProvider(modules, data) { - let rd = []; - for (let i = modules.length; i--; i > 0) { - if (data[modules[i].name]) { - rd.push(data[modules[i].name]) + // get data + const adUnitCodes = auction.adUnitCodes; + if (!adUnitCodes) { + return; + } + let targeting = []; + for (let i = relevantSubModules.length - 1; i >= 0; i--) { + const smTargeting = relevantSubModules[i].getTargetingData(adUnitCodes, relevantSubModules[i].config, _userConsent); + if (smTargeting && typeof smTargeting === 'object') { + targeting.push(smTargeting); + } else { + utils.logWarn('invalid getTargetingData response for sub module', relevantSubModules[i].name); } } - return rd; + // place data on auction adUnits + const mergedTargeting = deepMerge(targeting); + auction.adUnits.forEach(adUnit => { + const kv = adUnit.code && mergedTargeting[adUnit.code]; + if (!kv) { + return + } + adUnit[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = Object.assign(adUnit[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] || {}, kv); + }); + return auction.adUnits; } /** @@ -311,53 +355,5 @@ export function deepMerge(arr) { }, {}); } -/** - * run hook before bids request - * get data from provider and set key values to primary ad server & bidders - * @param {function} fn - hook function - * @param {Object} reqBidsConfigObj - request bids object - */ -export function requestBidsHook(fn, reqBidsConfigObj) { - getProviderData(reqBidsConfigObj.adUnits || getGlobal().adUnits, (data) => { - if (data && Object.keys(data).length) { - const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); - if (Object.keys(_mergedData).length) { - setDataForPrimaryAdServer(_mergedData); - addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, _mergedData); - } - } - return fn.call(this, reqBidsConfigObj); - }); -} - -/** - * set data to primary ad server - * @param {Object} data - key values to set - */ -function setDataForPrimaryAdServer(data) { - if (utils.isGptPubadsDefined()) { - targeting.setTargetingForGPT(data, null) - } else { - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - window.googletag.cmd.push(() => { - targeting.setTargetingForGPT(data, null); - }); - } -} - -/** - * @param {AdUnit[]} adUnits - * @param {Object} data - key values to set - */ -function addIdDataToAdUnitBids(adUnits, data) { - adUnits.forEach(adUnit => { - adUnit.bids = adUnit.bids.map(bid => { - const rd = data[adUnit.code] || {}; - return Object.assign(bid, {realTimeData: rd}); - }) - }); -} - module('realTimeData', attachRealTimeDataProvider); init(config); diff --git a/src/targeting.js b/src/targeting.js index 8176bc9caff..b6a38bdbb61 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -235,7 +235,8 @@ export function newTargeting(auctionManager) { // `alwaysUseBid=true`. If sending all bids is enabled, add targeting for losing bids. var targeting = getWinningBidTargeting(adUnitCodes, bidsReceived) .concat(getCustomBidTargeting(adUnitCodes, bidsReceived)) - .concat(config.getConfig('enableSendAllBids') ? getBidLandscapeTargeting(adUnitCodes, bidsReceived) : getDealBids(adUnitCodes, bidsReceived)); + .concat(config.getConfig('enableSendAllBids') ? getBidLandscapeTargeting(adUnitCodes, bidsReceived) : getDealBids(adUnitCodes, bidsReceived)) + .concat(getAdUnitTargeting(adUnitCodes)); // store a reference of the targeting keys targeting.map(adUnitCode => { @@ -609,6 +610,27 @@ export function newTargeting(auctionManager) { }); } + function getAdUnitTargeting(adUnitCodes) { + function getTargetingObj(adUnit) { + return deepAccess(adUnit, CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING); + } + + function getTargetingValues(adUnit) { + const aut = getTargetingObj(adUnit); + + return Object.keys(aut) + .map(function(key) { + return {[key]: utils.isArray(aut[key]) ? aut[key] : aut[key].split(',')}; + }); + } + + return auctionManager.getAdUnits() + .filter(adUnit => includes(adUnitCodes, adUnit.code) && getTargetingObj(adUnit)) + .map(adUnit => { + return {[adUnit.code]: getTargetingValues(adUnit)} + }); + } + targeting.isApntagDefined = function() { if (window.apntag && utils.isFn(window.apntag.setKeywords)) { return true; diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js new file mode 100644 index 00000000000..ee37d16905b --- /dev/null +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -0,0 +1,83 @@ +import * as browsiRTD from '../../../modules/browsiRtdProvider.js'; +import {makeSlot} from '../integration/faker/googletag.js'; + +describe('browsi Real time data sub module', function () { + const conf = { + 'auctionDelay': 250, + dataProviders: [{ + 'name': 'browsi', + 'params': { + 'url': 'testUrl.com', + 'siteKey': 'testKey', + 'pubKey': 'testPub', + 'keyName': 'bv' + } + }] + }; + + it('should init and return true', function () { + browsiRTD.collectData(); + expect(browsiRTD.browsiSubmodule.init(conf.dataProviders[0])).to.equal(true) + }); + + it('should create browsi script', function () { + const script = browsiRTD.addBrowsiTag('scriptUrl.com'); + expect(script.getAttribute('data-sitekey')).to.equal('testKey'); + expect(script.getAttribute('data-pubkey')).to.equal('testPub'); + expect(script.async).to.equal(true); + expect(script.prebidData.kn).to.equal(conf.dataProviders[0].params.keyName); + }); + + it('should match placement with ad unit', function () { + const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'}); + + const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true + const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true + const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false + const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true + + expect(test1).to.equal(true); + expect(test2).to.equal(true); + expect(test3).to.equal(false); + expect(test4).to.equal(true); + }); + + it('should return correct macro values', function () { + const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'}); + + slot.setTargeting('test', ['test', 'value']); + // slot getTargeting doesn't act like GPT so we can't expect real value + const macroResult = browsiRTD.getMacroId({p: '/'}, slot); + expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); + + const macroResultB = browsiRTD.getMacroId({}, slot); + expect(macroResultB).to.equal('browsiAd_1'); + + const macroResultC = browsiRTD.getMacroId({p: '', s: {s: 0, e: 1}}, slot); + expect(macroResultC).to.equal('/'); + }); + + describe('should return data to RTD module', function () { + it('should return empty if no ad units defined', function () { + browsiRTD.setData({}); + expect(browsiRTD.browsiSubmodule.getTargetingData([])).to.eql({}); + }); + + it('should return NA if no prediction for ad unit', function () { + makeSlot({code: 'adMock', divId: 'browsiAd_2'}); + browsiRTD.setData({}); + expect(browsiRTD.browsiSubmodule.getTargetingData(['adMock'])).to.eql({adMock: {bv: 'NA'}}); + }); + + it('should return prediction from server', function () { + makeSlot({code: 'hasPrediction', divId: 'hasPrediction'}); + const data = { + p: {'hasPrediction': {p: 0.234}}, + kn: 'bv', + pmd: undefined + }; + browsiRTD.setData(data); + expect(browsiRTD.browsiSubmodule.getTargetingData(['hasPrediction'])).to.eql({hasPrediction: {bv: '0.20'}}); + }) + }) +}); diff --git a/test/spec/modules/realTimeDataModule_spec.js b/test/spec/modules/realTimeDataModule_spec.js new file mode 100644 index 00000000000..b84aef15feb --- /dev/null +++ b/test/spec/modules/realTimeDataModule_spec.js @@ -0,0 +1,160 @@ +import * as rtdModule from 'modules/rtdModule/index.js'; +import { config } from 'src/config.js'; +import * as sinon from 'sinon'; + +const getBidRequestDataSpy = sinon.spy(); + +const validSM = { + name: 'validSM', + init: () => { return true }, + getTargetingData: (adUnitsCodes) => { + return {'ad2': {'key': 'validSM'}} + }, + getBidRequestData: getBidRequestDataSpy +}; + +const validSMWait = { + name: 'validSMWait', + init: () => { return true }, + getTargetingData: (adUnitsCodes) => { + return {'ad1': {'key': 'validSMWait'}} + }, + getBidRequestData: getBidRequestDataSpy +}; + +const invalidSM = { + name: 'invalidSM' +}; + +const failureSM = { + name: 'failureSM', + init: () => { return false } +}; + +const nonConfSM = { + name: 'nonConfSM', + init: () => { return true } +}; + +const conf = { + 'realTimeData': { + 'auctionDelay': 100, + dataProviders: [ + { + 'name': 'validSMWait', + 'waitForIt': true, + }, + { + 'name': 'validSM', + 'waitForIt': false, + }, + { + 'name': 'invalidSM' + }, + { + 'name': 'failureSM' + }] + } +}; + +describe('Real time module', function () { + before(function () { + rtdModule.attachRealTimeDataProvider(validSM); + rtdModule.attachRealTimeDataProvider(invalidSM); + rtdModule.attachRealTimeDataProvider(failureSM); + rtdModule.attachRealTimeDataProvider(nonConfSM); + rtdModule.attachRealTimeDataProvider(validSMWait); + }); + + after(function () { + config.resetConfig(); + }); + + beforeEach(function () { + config.setConfig(conf); + }); + + it('should use only valid modules', function () { + rtdModule.init(config); + expect(rtdModule.subModules).to.eql([validSMWait, validSM]); + }); + + it('should be able to modify bid request', function (done) { + rtdModule.setBidRequestsData(() => { + assert(getBidRequestDataSpy.calledTwice); + assert(getBidRequestDataSpy.calledWith({bidRequest: {}})); + done(); + }, {bidRequest: {}}) + }); + + it('deep merge object', function () { + const obj1 = { + id1: { + key: 'value', + key2: 'value2' + }, + id2: { + k: 'v' + } + }; + const obj2 = { + id1: { + key3: 'value3' + } + }; + const obj3 = { + id3: { + key: 'value' + } + }; + const expected = { + id1: { + key: 'value', + key2: 'value2', + key3: 'value3' + }, + id2: { + k: 'v' + }, + id3: { + key: 'value' + } + }; + + const merged = rtdModule.deepMerge([obj1, obj2, obj3]); + assert.deepEqual(expected, merged); + }); + + it('sould place targeting on adUnits', function (done) { + const auction = { + adUnitCodes: ['ad1', 'ad2'], + adUnits: [ + { + code: 'ad1' + }, + { + code: 'ad2', + adserverTargeting: {preKey: 'preValue'} + } + ] + }; + + const expectedAdUnits = [ + { + code: 'ad1', + adserverTargeting: {key: 'validSMWait'} + }, + { + code: 'ad2', + adserverTargeting: { + preKey: 'preValue', + key: 'validSM' + } + } + ]; + + const adUnits = rtdModule.getAdUnitTargeting(auction); + assert.deepEqual(expectedAdUnits, adUnits) + done(); + }) +}); diff --git a/test/spec/modules/realTimeModule_spec.js b/test/spec/modules/realTimeModule_spec.js deleted file mode 100644 index f47068724d1..00000000000 --- a/test/spec/modules/realTimeModule_spec.js +++ /dev/null @@ -1,274 +0,0 @@ -import * as rtdModule from 'modules/rtdModule/index.js'; -import { config } from 'src/config.js'; -import {makeSlot} from '../integration/faker/googletag.js'; -import * as browsiRTD from '../../../modules/browsiRtdProvider.js'; - -const validSM = { - name: 'validSM', - init: () => { return true }, - getData: (adUnits, onDone) => { - setTimeout(() => { - return onDone({'key': 'validSM'}) - }, 500) - } -}; - -const validSMWait = { - name: 'validSMWait', - init: () => { return true }, - getData: (adUnits, onDone) => { - setTimeout(() => { - return onDone({'ad1': {'key': 'validSMWait'}}) - }, 50) - } -}; - -const invalidSM = { - name: 'invalidSM' -}; - -const failureSM = { - name: 'failureSM', - init: () => { return false } -}; - -const nonConfSM = { - name: 'nonConfSM', - init: () => { return true } -}; - -const conf = { - 'realTimeData': { - 'auctionDelay': 250, - dataProviders: [ - { - 'name': 'validSMWait', - 'waitForIt': true, - }, - { - 'name': 'validSM', - 'waitForIt': false, - }, - { - 'name': 'invalidSM' - }, - { - 'name': 'failureSM' - }] - } -}; - -function getAdUnitMock(code = 'adUnit-code') { - return { - code, - mediaTypes: { banner: {}, native: {} }, - sizes: [[300, 200], [300, 600]], - bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] - }; -} - -describe('Real time module', function () { - after(function () { - config.resetConfig(); - }); - - beforeEach(function () { - config.setConfig(conf); - }); - - it('should use only valid modules', function (done) { - rtdModule.attachRealTimeDataProvider(validSM); - rtdModule.attachRealTimeDataProvider(invalidSM); - rtdModule.attachRealTimeDataProvider(failureSM); - rtdModule.attachRealTimeDataProvider(nonConfSM); - rtdModule.attachRealTimeDataProvider(validSMWait); - rtdModule.initSubModules(afterInitSubModules); - function afterInitSubModules() { - expect(rtdModule.subModules).to.eql([validSMWait, validSM]); - done(); - } - rtdModule.init(config); - }); - - it('should only wait for must have sub modules', function (done) { - rtdModule.getProviderData([], (data) => { - expect(data).to.eql({validSMWait: {'ad1': {'key': 'validSMWait'}}}); - done(); - }) - }); - - it('deep merge object', function () { - const obj1 = { - id1: { - key: 'value', - key2: 'value2' - }, - id2: { - k: 'v' - } - }; - const obj2 = { - id1: { - key3: 'value3' - } - }; - const obj3 = { - id3: { - key: 'value' - } - }; - const expected = { - id1: { - key: 'value', - key2: 'value2', - key3: 'value3' - }, - id2: { - k: 'v' - }, - id3: { - key: 'value' - } - }; - - const merged = rtdModule.deepMerge([obj1, obj2, obj3]); - assert.deepEqual(expected, merged); - }); - - it('check module using bidsBackCallback', function (done) { - // set slot - const slot = makeSlot({ code: '/code1', divId: 'ad1' }); - window.googletag.pubads().setSlots([slot]); - - function afterBidHook() { - expect(slot.getTargeting().length).to.equal(1); - expect(slot.getTargeting()[0].key).to.equal('validSMWait'); - done(); - } - rtdModule.setTargetsAfterRequestBids(afterBidHook, []); - }); - - it('check module using requestBidsHook', function (done) { - // set slot - const slotsB = makeSlot({ code: '/code1', divId: 'ad1' }); - window.googletag.pubads().setSlots([slotsB]); - let adUnits = [getAdUnitMock('ad1')]; - - function afterBidHook(data) { - expect(slotsB.getTargeting().length).to.equal(1); - expect(slotsB.getTargeting()[0].key).to.equal('validSMWait'); - - data.adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.realTimeData).to.have.property('key'); - expect(bid.realTimeData.key).to.equal('validSMWait'); - }); - }); - done(); - } - rtdModule.requestBidsHook(afterBidHook, { adUnits: adUnits }); - }); -}); - -describe('browsi Real time data sub module', function () { - const conf = { - 'realTimeData': { - 'auctionDelay': 250, - dataProviders: [{ - 'name': 'browsi', - 'params': { - 'url': 'testUrl.com', - 'siteKey': 'testKey', - 'pubKey': 'testPub', - 'keyName': 'bv' - } - }] - } - }; - - beforeEach(function () { - config.setConfig(conf); - }); - - after(function () { - config.resetConfig(); - }); - - it('should init and return true', function () { - browsiRTD.beforeInit(config); - expect(browsiRTD.browsiSubmodule.init()).to.equal(true) - }); - - it('should create browsi script', function () { - const script = browsiRTD.addBrowsiTag('scriptUrl.com'); - expect(script.getAttribute('data-sitekey')).to.equal('testKey'); - expect(script.getAttribute('data-pubkey')).to.equal('testPub'); - expect(script.async).to.equal(true); - expect(script.prebidData.kn).to.equal(conf.realTimeData.dataProviders[0].params.keyName); - }); - - it('should match placement with ad unit', function () { - const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - - const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true - const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true - const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false - const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true - - expect(test1).to.equal(true); - expect(test2).to.equal(true); - expect(test3).to.equal(false); - expect(test4).to.equal(true); - }); - - it('should return correct macro values', function () { - const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - - slot.setTargeting('test', ['test', 'value']); - // slot getTargeting doesn't act like GPT so we can't expect real value - const macroResult = browsiRTD.getMacroId({p: '/'}, slot); - expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); - - const macroResultB = browsiRTD.getMacroId({}, slot); - expect(macroResultB).to.equal('browsiAd_1'); - - const macroResultC = browsiRTD.getMacroId({p: '', s: {s: 0, e: 1}}, slot); - expect(macroResultC).to.equal('/'); - }); - - describe('should return data to RTD module', function () { - it('should return empty if no ad units defined', function (done) { - browsiRTD.setData({}); - browsiRTD.browsiSubmodule.getData([], onDone); - function onDone(data) { - expect(data).to.eql({}); - done(); - } - }); - - it('should return NA if no prediction for ad unit', function (done) { - const adUnits = [getAdUnitMock('adMock')]; - browsiRTD.setData({}); - browsiRTD.browsiSubmodule.getData(adUnits, onDone); - function onDone(data) { - expect(data).to.eql({adMock: {bv: 'NA'}}); - done(); - } - }); - - it('should return prediction from server', function (done) { - const adUnits = [getAdUnitMock('hasPrediction')]; - const data = { - p: {'hasPrediction': {p: 0.234}}, - kn: 'bv', - pmd: undefined - }; - browsiRTD.setData(data); - browsiRTD.browsiSubmodule.getData(adUnits, onDone); - function onDone(data) { - expect(data).to.eql({hasPrediction: {bv: '0.20'}}); - done(); - } - }) - }) -}); From c4eb7428eec2ea10d12d1b3b8e637bdf9a313bfe Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Tue, 20 Oct 2020 10:25:46 -0500 Subject: [PATCH 0293/1476] Audigent RTD Provider HaloId Support & RTD Phase 3 Compliance (#5777) * 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 * RTD module extend #4610 * add hook for submodule init variables naming * RTD bug fix * remove auction delay and related hooks * update audigent rtd provider * style update * change onDone() logic * RTD phase 3 * return on data unavailable * api endpoint update * update audigent RTD provider for new spec * design changes * fix loop continuation * proper fix this time * linter * update rtd parameters, onDone semantics * reduce loops * documentation update * working update to rtd3 spec, update segment example, documentation * remove unused vars, reference module name * resolve haloid for segments * update documentation to markdown * update description in documentation * minify optimizations Co-authored-by: omerdotan Co-authored-by: bretg --- .../gpt/audigentSegments_example.html | 86 ++++++--- modules/audigentRtdProvider.js | 174 +++++++++--------- modules/audigentRtdProvider.md | 34 +++- package-lock.json | 47 ++--- 4 files changed, 202 insertions(+), 139 deletions(-) diff --git a/integrationExamples/gpt/audigentSegments_example.html b/integrationExamples/gpt/audigentSegments_example.html index 7739b558327..1536ece9ab7 100644 --- a/integrationExamples/gpt/audigentSegments_example.html +++ b/integrationExamples/gpt/audigentSegments_example.html @@ -84,11 +84,10 @@ { code: 'test-div', mediaTypes: { - banner: { - sizes: [[300,250],[300,600],[728,90]] - } + banner: { + sizes: [[300,250],[300,600],[728,90]] + } }, - bids: [ { bidder: 'rubicon', @@ -120,20 +119,9 @@ consentManagement: { cmpApi: 'iab', timeout: 1000, - allowAuctionWithoutConsent: true + defaultGdprScope: true }, - // consentManagement: { - // cmpApi: 'static', - // consentData: { - // consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA' - // vendorData: { - // purposeConsents: { - // '1': true - // } - // } - // } - // }, - usersync: { + userSync: { userIds: [{ name: "unifiedId", params: { @@ -145,6 +133,16 @@ name: "unifiedid", expires: 30 }, + }, { + name: "intentIqId", + params: { + partner: 0, //Set your real IntentIQ partner ID here for production. + }, + storage: { + type: "cookie", + name: "intentIqId", + expires: 30 + }, }, { name: "id5Id", params: { @@ -157,6 +155,18 @@ refreshInSeconds: 8*3600 // Refresh frequency of cookies, defaulting to 'expires' }, + }, { + name: "merkleId", + params: { + ptk: '12345678-aaaa-bbbb-cccc-123456789abc', //Set your real merkle partner key here + pubid: 'EXAMPLE' //Set your real merkle publisher id here + }, + storage: { + type: "html5", + name: "merkleId", + expires: 30 + }, + }, { name: "parrableId", params: { @@ -174,7 +184,7 @@ // foo: '9879878907987', // bar:'93939' // } - }, { + }, { name: 'identityLink', params: { pid: '14' // Set your real identityLink placement ID here @@ -184,13 +194,46 @@ name: 'idl_env', expires: 30 } + }, { + name: "sharedId", + params: { + syncTime: 60 // in seconds, default is 24 hours + }, + storage: { + type: "cookie", + name: "sharedid", + expires: 28 + } + }, { + name: 'lotamePanoramaId' + }, { + name: "liveIntentId", + params: { + publisherId: "9896876" + }, + storage: { + type: "cookie", + name: "_li_pbid", + expires: 28 + } + }, { + name: "zeotapIdPlus" + }, { + name: 'haloId', + storage: { + type: "cookie", + name: "haloId", + expires: 28 + } + }, { + name: "quantcastId" }], syncDelay: 5000, - auctionDelay: 1000 + auctionDelay: 1000 }, realTimeData: { auctionDelay: 1000, - dataProviders: [{name: "audigent"}] + dataProviders: [{name: "audigent", waitForIt: true}] } }); pbjs.addAdUnits(adUnits); @@ -199,7 +242,7 @@ function sendAdserverRequest() { document.getElementById('tdid').innerHTML = adUnits[0].bids[0].userId['tdid']; - document.getElementById('audigent_segments').innerHTML = JSON.stringify(adUnits[0].bids[0].realTimeData.audigent_segments); + document.getElementById('audigent_segments').innerHTML = JSON.stringify(adUnits[0].bids[0].audigent_segments); if (pbjs.adserverRequestSent) return; pbjs.adserverRequestSent = true; @@ -246,6 +289,7 @@

Audigent Segments Prebid

googletag.cmd.push(function() { googletag.display('test-div'); });
+ TDID:
diff --git a/modules/audigentRtdProvider.js b/modules/audigentRtdProvider.js index 0f32c84962f..09b76cac0df 100644 --- a/modules/audigentRtdProvider.js +++ b/modules/audigentRtdProvider.js @@ -5,17 +5,6 @@ * @module modules/audigentRtdProvider * @requires module:modules/realTimeData */ - -/** - * @typedef {Object} ModuleParams - * @property {string} siteKey - * @property {string} pubKey - * @property {string} url - * @property {?string} keyName - * @property {number} auctionDelay - */ - -import {config} from '../src/config.js'; import {getGlobal} from '../src/prebidGlobal.js'; import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; @@ -26,116 +15,131 @@ const storage = getStorageManager(); /** @type {string} */ const MODULE_NAME = 'realTimeData'; - -/** @type {ModuleParams} */ -let _moduleParams = {}; +const SUBMODULE_NAME = 'audigent'; +const HALOID_LOCAL_NAME = 'auHaloId'; +const SEG_LOCAL_NAME = '__adgntseg'; /** - * XMLHttpRequest to get data form audigent server - * @param {string} url server url with query params + * decorate adUnits with segment data + * @param {adUnit[]} adUnits + * @param {Object} data */ +function addSegmentData(adUnits, data) { + adUnits.forEach(adUnit => { + if (adUnit.hasOwnProperty('bids')) { + adUnit.bids.forEach(bid => { + bid.audigent_segments = data; + }) + } + }) -export function setData(data) { - storage.setDataInLocalStorage('__adgntseg', JSON.stringify(data)); + return adUnits; } -function getSegments(adUnits, onDone) { - try { - let jsonData = storage.getDataFromLocalStorage('__adgntseg'); - if (jsonData) { - let data = JSON.parse(jsonData); - if (data.audigent_segments) { - let dataToReturn = adUnits.reduce((rp, cau) => { - const adUnitCode = cau && cau.code; - if (!adUnitCode) { return rp } - rp[adUnitCode] = data; - return rp; - }, {}); - - onDone(dataToReturn); - return; - } +/** + * segment retrieval from audigent's backends + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} config + * @param {Object} userConsent + */ +function getSegments(reqBidsConfigObj, onDone, config, userConsent) { + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + + let jsonData = storage.getDataFromLocalStorage(SEG_LOCAL_NAME); + if (jsonData) { + let data = JSON.parse(jsonData); + if (data.audigent_segments) { + addSegmentData(adUnits, data.audigent_segments); + onDone(); + return; } - getSegmentsAsync(adUnits, onDone); - } catch (e) { - getSegmentsAsync(adUnits, onDone); } -} -function getSegmentsAsync(adUnits, onDone) { const userIds = (getGlobal()).getUserIds(); - let tdid = null; + if (typeof userIds == 'undefined' || userIds == null) { + onDone(); + return; + } - if (userIds && userIds['tdid']) { - tdid = userIds['tdid']; + let haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); + if (haloId) { + userIds.haloId = haloId; + getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); } else { - onDone({}); + var script = document.createElement('script') + script.type = 'text/javascript'; + + script.onload = function() { + userIds.haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); + getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); + } + + script.src = 'https://id.halo.ad.gt/api/v1/haloid'; + document.getElementsByTagName('head')[0].appendChild(script); } +} - const url = `https://seg.ad.gt/api/v1/rtb_segments?tdid=${tdid}`; +/** + * async segment retrieval from audigent's backends + * @param {adUnit[]} adUnits + * @param {function} onDone + * @param {Object} config + * @param {Object} userConsent + * @param {Object} userIds + */ +function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds) { + let reqParams = {} + if (typeof config == 'object' && config != null && Object.keys(config).length > 0) { + reqParams = config.params + } + const url = `https://seg.halo.ad.gt/api/v1/rtb_segments`; ajax(url, { success: function (response, req) { if (req.status === 200) { try { const data = JSON.parse(response); if (data && data.audigent_segments) { - setData(data); - let dataToReturn = adUnits.reduce((rp, cau) => { - const adUnitCode = cau && cau.code; - if (!adUnitCode) { return rp } - rp[adUnitCode] = data; - return rp; - }, {}); - - onDone(dataToReturn); + addSegmentData(adUnits, data.audigent_segments); + onDone(); + storage.setDataInLocalStorage(SEG_LOCAL_NAME, JSON.stringify(data)); } else { - onDone({}); + onDone(); } } catch (err) { utils.logError('unable to parse audigent segment data'); - onDone({}) + onDone(); } } else if (req.status === 204) { - // unrecognized site key - onDone({}); + // unrecognized partner config + onDone(); } }, error: function () { - onDone({}); + onDone(); utils.logError('unable to get audigent segment data'); } - } + }, + JSON.stringify({'userIds': userIds, 'config': reqParams}), + {contentType: 'application/json'} ); } +/** + * module init + * @param {Object} config + * @return {boolean} + */ +export function init(config) { + return true; +} + /** @type {RtdSubmodule} */ export const audigentSubmodule = { - /** - * used to link submodule with realTimeData - * @type {string} - */ - name: 'audigent', - /** - * get data and send back to realTimeData module - * @function - * @param {adUnit[]} adUnits - * @param {function} onDone - */ - getData: getSegments + name: SUBMODULE_NAME, + getBidRequestData: getSegments, + init: init }; -export function init(config) { - const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { - try { - _moduleParams = realTimeData.dataProviders && realTimeData.dataProviders.filter(pr => pr.name && pr.name.toLowerCase() === 'audigent')[0].params; - _moduleParams.auctionDelay = realTimeData.auctionDelay; - } catch (e) { - _moduleParams = {}; - } - confListener(); - }); -} - -submodule('realTimeData', audigentSubmodule); -init(config); +submodule(MODULE_NAME, audigentSubmodule); diff --git a/modules/audigentRtdProvider.md b/modules/audigentRtdProvider.md index 47bcbbbf951..03e647f651d 100644 --- a/modules/audigentRtdProvider.md +++ b/modules/audigentRtdProvider.md @@ -1,18 +1,40 @@ +## Audigent Real-time Data Submodule + Audigent is a next-generation data management platform and a first-of-a-kind "data agency" containing some of the most exclusive content-consuming audiences across desktop, mobile and social platforms. -This real-time data module provides first-party Audigent segments that can be +This real-time data module provides quality user segmentation that can be attached to bid request objects destined for different SSPs in order to optimize targeting. Audigent maintains a large database of first-party Tradedesk Unified -ID to third party segment mappings that can now be queried at bid-time. +ID, Audigent Halo ID and other id provider mappings to various third-party +segment types that are utilizable across different SSPs. With this module, +these segments can be retrieved and supplied to the SSP in real-time during +the bid request cycle. -Usage: +### Usage Compile the audigent RTD module into your Prebid build: `gulp build --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,rubiconBidAdapter` +Configure Prebid to add the Audigent RTD Segment Handler: +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "audigent", + waitForIt: true + } + ] + } + ... +} +``` + Audigent segments will then be attached to each bid request objects in `bid.realTimeData.audigent_segments` @@ -35,13 +57,15 @@ function addAudigentSegments() { for (i = 0; i < adUnits.length; i++) { let adUnit = adUnits[i]; for (j = 0; j < adUnit.bids.length; j++) { - adUnit.bids[j].userId.lipb.segments = adUnit.bids[j].realTimeData.audigent_segments['rubicon']; + adUnit.bids[j].userId.lipb.segments = adUnit.bids[j].audigent_segments['rubicon']; } } } ``` -To view an example of the segments returned by Audigent's backends: +### Testing + +To view an example of available segments returned by Audigent's backends: `gulp serve --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,rubiconBidAdapter` diff --git a/package-lock.json b/package-lock.json index 1784b885be9..81ff16fdc5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.8.0-pre", + "version": "4.11.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7796,13 +7796,22 @@ "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.0" + }, + "dependencies": { + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + } } }, "es-array-method-boxes-properly": { @@ -7838,6 +7847,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -12076,7 +12086,7 @@ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has": "^1.0.3" } }, "is-relative": { @@ -17484,7 +17494,8 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true }, "object-is": { "version": "1.1.2", @@ -20696,26 +20707,6 @@ "es-abstract": "^1.17.0-next.1" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", From 291caa15ac86c2ac0bd5e327e0632b8aa89db731 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Tue, 20 Oct 2020 12:21:18 -0400 Subject: [PATCH 0294/1476] [AD-963] - Update JW Player RTD Provider for compliance with RTD Module Phase 3 (#5844) * updates grid adapter * adds response to bids * separates responsibilities * refactos success block * renames functions * tests getCache and formatting * tests data enrichment * adds tests for bid enhancement * updates documentation * adds clarification that sample params are placeholders * adds instructions to replace placeholder ids in example Co-authored-by: karimJWP --- .../gpt/jwplayerRtdProvider_example.html | 5 +- modules/gridBidAdapter.js | 12 +- modules/jwplayerRtdProvider.js | 217 +++++---- modules/jwplayerRtdProvider.md | 72 +-- test/spec/modules/gridBidAdapter_spec.js | 10 +- test/spec/modules/jwplayerRtdProvider_spec.js | 429 ++++++++++++------ 6 files changed, 479 insertions(+), 266 deletions(-) diff --git a/integrationExamples/gpt/jwplayerRtdProvider_example.html b/integrationExamples/gpt/jwplayerRtdProvider_example.html index 3791ab42137..e47f3ca45ec 100644 --- a/integrationExamples/gpt/jwplayerRtdProvider_example.html +++ b/integrationExamples/gpt/jwplayerRtdProvider_example.html @@ -12,6 +12,7 @@ var adUnits = [{ code: 'div-gpt-ad-1460505748561-0', jwTargeting: { + // Note: the following Ids are placeholders and should be replaced with your Ids. playerID: '123', mediaID: 'abc' }, @@ -32,7 +33,6 @@ var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; - + + + Reconciliation RTD Provider Example + + + + + + + + +
Div-1
+
+ +
+ + diff --git a/modules/.submodules.json b/modules/.submodules.json index 91cda9d95ad..78fd9376dd1 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -26,6 +26,7 @@ "rtdModule": [ "browsiRtdProvider", "audigentRtdProvider", - "jwplayerRtdProvider" + "jwplayerRtdProvider", + "reconciliationRtdProvider" ] } diff --git a/modules/reconciliationRtdProvider.js b/modules/reconciliationRtdProvider.js new file mode 100644 index 00000000000..20acb6a535a --- /dev/null +++ b/modules/reconciliationRtdProvider.js @@ -0,0 +1,318 @@ +/** + * This module adds reconciliation provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will add custom targetings to ad units + * The module will listen to post messages from rendered creatives with Reconciliation Tag + * The module will call tracking pixels to log info needed for reconciliation matching + * @module modules/reconciliationRtdProvider + * @requires module:modules/realTimeData + */ + +/** + * @typedef {Object} ModuleParams + * @property {string} publisherMemberId + * @property {?string} initUrl + * @property {?string} impressionUrl + * @property {?boolean} allowAccess + */ + +import { submodule } from '../src/hook.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import * as utils from '../src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; + +/** @type {Object} */ +const MessageType = { + IMPRESSION_REQUEST: 'rsdk:impression:req', + IMPRESSION_RESPONSE: 'rsdk:impression:res', +}; +/** @type {ModuleParams} */ +const DEFAULT_PARAMS = { + initUrl: 'https://confirm.fiduciadlt.com/init', + impressionUrl: 'https://confirm.fiduciadlt.com/imp', + allowAccess: false, +}; +/** @type {ModuleParams} */ +let _moduleParams = {}; + +/** + * Handle postMesssage from ad creative, track impression + * and send response to reconciliation ad tag + * @param {Event} e + */ +function handleAdMessage(e) { + let data = {}; + let adUnitId = ''; + let adDeliveryId = ''; + + try { + data = JSON.parse(e.data); + } catch (e) { + return; + } + + if (data.type === MessageType.IMPRESSION_REQUEST) { + if (utils.isGptPubadsDefined()) { + // 1. Find the last iframed window before window.top where the tracker was injected + // (the tracker could be injected in nested iframes) + const adWin = getTopIFrameWin(e.source); + if (adWin && adWin !== window.top) { + // 2. Find the GPT slot for the iframed window + const adSlot = getSlotByWin(adWin); + // 3. Get AdUnit IDs for the selected slot + if (adSlot) { + adUnitId = adSlot.getAdUnitPath(); + adDeliveryId = adSlot.getTargeting('RSDK_ADID'); + adDeliveryId = adDeliveryId.length + ? adDeliveryId[0] + : utils.generateUUID(); + } + } + } + + // Call local impression callback + const args = Object.assign({}, data.args, { + publisherDomain: window.location.hostname, + publisherMemberId: _moduleParams.publisherMemberId, + adUnitId, + adDeliveryId, + }); + + track.trackGet(_moduleParams.impressionUrl, args); + + // Send response back to the Advertiser tag + let response = { + type: MessageType.IMPRESSION_RESPONSE, + id: data.id, + args: Object.assign( + { + publisherDomain: window.location.hostname, + }, + data.args + ), + }; + + // If access is allowed - add ad unit id to response + if (_moduleParams.allowAccess) { + Object.assign(response.args, { + adUnitId, + adDeliveryId, + }); + } + + e.source.postMessage(JSON.stringify(response), '*'); + } +} + +/** + * Get top iframe window for nested Window object + * - top + * -- iframe.window <-- top iframe window + * --- iframe.window + * ---- iframe.window <-- win + * + * @param {Window} win nested iframe window object + * @param {Window} topWin top window + */ +export function getTopIFrameWin(win, topWin) { + topWin = topWin || window; + + if (!win) { + return null; + } + + try { + while (win.parent !== topWin) { + win = win.parent; + } + return win; + } catch (e) { + return null; + } +} + +/** + * get all slots on page + * @return {Object[]} slot GoogleTag slots + */ +function getAllSlots() { + return utils.isGptPubadsDefined() && window.googletag.pubads().getSlots(); +} + +/** + * get GPT slot by placement id + * @param {string} code placement id + * @return {?Object} + */ +function getSlotByCode(code) { + const slots = getAllSlots(); + if (!slots || !slots.length) { + return null; + } + return ( + find( + slots, + (s) => s.getSlotElementId() === code || s.getAdUnitPath() === code + ) || null + ); +} + +/** + * get GPT slot by iframe window + * @param {Window} win + * @return {?Object} + */ +export function getSlotByWin(win) { + const slots = getAllSlots(); + + if (!slots || !slots.length) { + return null; + } + + return ( + find(slots, (s) => { + let slotElement = document.getElementById(s.getSlotElementId()); + + if (slotElement) { + let slotIframe = slotElement.querySelector('iframe'); + + if (slotIframe && slotIframe.contentWindow === win) { + return true; + } + } + + return false; + }) || null + ); +} + +/** + * serialize object and return query params string + * @param {Object} data + * @return {string} + */ +export function stringify(query) { + const parts = []; + + for (let key in query) { + if (query.hasOwnProperty(key)) { + let val = query[key]; + if (typeof query[key] !== 'object') { + parts.push(`${key}=${encodeURIComponent(val)}`); + } else { + parts.push(`${key}=${encodeURIComponent(stringify(val))}`); + } + } + } + return parts.join('&'); +} +/** + * Init Reconciliation post messages listeners to handle + * impressions messages from ad creative + */ +function initListeners() { + window.addEventListener('message', handleAdMessage, false); +} + +/** + * Send init event to log + * @param {Array} adUnits + */ +function trackInit(adUnits) { + track.trackPost( + _moduleParams.initUrl, + { + adUnits, + publisherDomain: window.location.hostname, + publisherMemberId: _moduleParams.publisherMemberId, + } + ); +} + +/** + * Track event via POST request + * wrap method to allow stubbing in tests + * @param {string} url + * @param {Object} data + */ +export const track = { + trackGet(url, data) { + utils.triggerPixel(`${url}?${stringify(data)}`); + }, + trackPost(url, data) { + const ajax = ajaxBuilder(); + + ajax( + url, + function() {}, + JSON.stringify(data), + { + method: 'POST', + } + ); + } +} + +/** + * Set custom targetings for provided adUnits + * @param {string[]} adUnitsCodes + * @return {Object} key-value object with custom targetings + */ +function getReconciliationData(adUnitsCodes) { + const dataToReturn = {}; + const adUnitsToTrack = []; + + adUnitsCodes.forEach((adUnitCode) => { + if (!adUnitCode) { + return; + } + + const adSlot = getSlotByCode(adUnitCode); + const adUnitId = adSlot ? adSlot.getAdUnitPath() : adUnitCode; + const adDeliveryId = utils.generateUUID(); + + dataToReturn[adUnitCode] = { + RSDK_AUID: adUnitId, + RSDK_ADID: adDeliveryId, + }; + + adUnitsToTrack.push({ + adUnitId, + adDeliveryId + }); + }, {}); + + // Track init event + trackInit(adUnitsToTrack); + + return dataToReturn; +} + +/** @type {RtdSubmodule} */ +export const reconciliationSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'reconciliation', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getTargetingData: getReconciliationData, + init: init, +}; + +function init(moduleConfig) { + const params = moduleConfig.params; + if (params && params.publisherMemberId) { + _moduleParams = Object.assign({}, DEFAULT_PARAMS, params); + initListeners(); + } else { + utils.logError('missing params for Reconciliation provider'); + } + return true; +} + +submodule('realTimeData', reconciliationSubmodule); diff --git a/modules/reconciliationRtdProvider.md b/modules/reconciliationRtdProvider.md new file mode 100644 index 00000000000..53883ad99eb --- /dev/null +++ b/modules/reconciliationRtdProvider.md @@ -0,0 +1,49 @@ +The purpose of this Real Time Data Provider is to allow publishers to match impressions accross the supply chain. + +**Reconciliation SDK** +The purpose of Reconciliation SDK module is to collect supply chain structure information and vendor-specific impression IDs from suppliers participating in ad creative delivery and report it to the Reconciliation Service, allowing publishers, advertisers and other supply chain participants to match and reconcile ad server, SSP, DSP and veritifation system log file records. Reconciliation SDK was created as part of TAG DLT initiative ( https://www.tagtoday.net/pressreleases/dlt_9_7_2020 ). + +**Usage for Publishers:** + +Compile the Reconciliation Provider into your Prebid build: + +`gulp build --modules=reconciliationRtdProvider` + +Add Reconciliation real time data provider configuration by setting up a Prebid Config: + +```javascript +const reconciliationDataProvider = { + name: "reconciliation", + params: { + publisherMemberId: "test_prebid_publisher", // required + allowAccess: true, //optional + } +}; + +pbjs.setConfig({ + ..., + realTimeData: { + dataProviders: [ + reconciliationDataProvider + ] + } +}); +``` + +where: +- `publisherMemberId` (required) - ID associated with the publisher +- `access` (optional) true/false - Whether ad markup will recieve Ad Unit Id's via Reconciliation Tag + +**Example:** + +To view an example: + +- in your cli run: + +`gulp serve --modules=reconciliationRtdProvider,appnexusBidAdapter` + +Your could also change 'appnexusBidAdapter' to another one. + +- in your browser, navigate to: + +`http://localhost:9999/integrationExamples/gpt/reconciliationRtdProvider_example.html` diff --git a/test/spec/modules/reconciliationRtdProvider_spec.js b/test/spec/modules/reconciliationRtdProvider_spec.js new file mode 100644 index 00000000000..8adca28248c --- /dev/null +++ b/test/spec/modules/reconciliationRtdProvider_spec.js @@ -0,0 +1,229 @@ +import { + reconciliationSubmodule, + track, + stringify, + getTopIFrameWin, + getSlotByWin +} from 'modules/reconciliationRtdProvider.js'; +import { makeSlot } from '../integration/faker/googletag.js'; +import * as utils from 'src/utils.js'; + +describe('Reconciliation Real time data submodule', function () { + const conf = { + dataProviders: [{ + 'name': 'reconciliation', + 'params': { + 'publisherMemberId': 'test_prebid_publisher' + }, + }] + }; + + let trackPostStub, trackGetStub; + + beforeEach(function () { + trackPostStub = sinon.stub(track, 'trackPost'); + trackGetStub = sinon.stub(track, 'trackGet'); + }); + + afterEach(function () { + trackPostStub.restore(); + trackGetStub.restore(); + }); + + describe('reconciliationSubmodule', function () { + describe('initialization', function () { + let utilsLogErrorSpy; + + before(function () { + utilsLogErrorSpy = sinon.spy(utils, 'logError'); + }); + + after(function () { + utils.logError.restore(); + }); + + it('successfully instantiates', function () { + expect(reconciliationSubmodule.init(conf.dataProviders[0])).to.equal(true); + }); + + it('should log error if initializied without parameters', function () { + expect(reconciliationSubmodule.init({'name': 'reconciliation', 'params': {}})).to.equal(true); + expect(utilsLogErrorSpy.calledOnce).to.be.true; + }); + }); + + describe('getData', function () { + it('should return data in proper format', function () { + makeSlot({code: '/reconciliationAdunit1', divId: 'reconciliationAd1'}); + + const targetingData = reconciliationSubmodule.getTargetingData(['/reconciliationAdunit1']); + expect(targetingData['/reconciliationAdunit1'].RSDK_AUID).to.eql('/reconciliationAdunit1'); + expect(targetingData['/reconciliationAdunit1'].RSDK_ADID).to.be.a('string'); + }); + + it('should return unit path if called with divId', function () { + makeSlot({code: '/reconciliationAdunit2', divId: 'reconciliationAd2'}); + + const targetingData = reconciliationSubmodule.getTargetingData(['reconciliationAd2']); + expect(targetingData['reconciliationAd2'].RSDK_AUID).to.eql('/reconciliationAdunit2'); + expect(targetingData['reconciliationAd2'].RSDK_ADID).to.be.a('string'); + }); + + it('should skip empty adUnit id', function () { + makeSlot({code: '/reconciliationAdunit3', divId: 'reconciliationAd3'}); + + const targetingData = reconciliationSubmodule.getTargetingData(['reconciliationAd3', '']); + expect(targetingData).to.have.all.keys('reconciliationAd3'); + }); + }); + + describe('track events', function () { + it('should track init event with data', function () { + const adUnit = { + code: '/adunit' + }; + + reconciliationSubmodule.getTargetingData([adUnit.code]); + + expect(trackPostStub.calledOnce).to.be.true; + expect(trackPostStub.getCalls()[0].args[0]).to.eql('https://confirm.fiduciadlt.com/init'); + expect(trackPostStub.getCalls()[0].args[1].adUnits[0].adUnitId).to.eql(adUnit.code); + expect(trackPostStub.getCalls()[0].args[1].adUnits[0].adDeliveryId).be.a('string'); + expect(trackPostStub.getCalls()[0].args[1].publisherMemberId).to.eql('test_prebid_publisher'); + }); + }); + + describe('stringify parameters', function () { + it('should return query for flat object', function () { + const parameters = { + adUnitId: '/adunit', + adDeliveryId: '12345' + }; + + expect(stringify(parameters)).to.eql('adUnitId=%2Fadunit&adDeliveryId=12345'); + }); + + it('should return query with nested parameters', function () { + const parameters = { + adUnitId: '/adunit', + adDeliveryId: '12345', + ext: { + adSize: '300x250', + adType: 'banner' + } + }; + + expect(stringify(parameters)).to.eql('adUnitId=%2Fadunit&adDeliveryId=12345&ext=adSize%3D300x250%26adType%3Dbanner'); + }); + }); + + describe('get topmost iframe', function () { + /** + * - top + * -- iframe.window <-- top iframe window + * --- iframe.window + * ---- iframe.window <-- win + */ + const mockFrameWin = (topWin, parentWin) => { + return { + top: topWin, + parent: parentWin + } + } + + it('should return null if called with null', function() { + expect(getTopIFrameWin(null)).to.be.null; + }); + + it('should return null if there is an error in frames chain', function() { + const topWin = {}; + const iframe1Win = mockFrameWin(topWin, null); // break chain + const iframe2Win = mockFrameWin(topWin, iframe1Win); + + expect(getTopIFrameWin(iframe1Win, topWin)).to.be.null; + }); + + it('should get the topmost iframe', function () { + const topWin = {}; + const iframe1Win = mockFrameWin(topWin, topWin); + const iframe2Win = mockFrameWin(topWin, iframe1Win); + + expect(getTopIFrameWin(iframe2Win, topWin)).to.eql(iframe1Win); + }); + }); + + describe('get slot by nested iframe window', function () { + it('should return the slot', function () { + const adSlotElement = document.createElement('div'); + const adSlotIframe = document.createElement('iframe'); + + adSlotElement.id = 'reconciliationAd'; + adSlotElement.appendChild(adSlotIframe); + document.body.appendChild(adSlotElement); + + const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + + expect(getSlotByWin(adSlotIframe.contentWindow)).to.eql(adSlot); + }); + + it('should return null if the slot is not found', function () { + const adSlotElement = document.createElement('div'); + const adSlotIframe = document.createElement('iframe'); + + adSlotElement.id = 'reconciliationAd'; + document.body.appendChild(adSlotElement); + document.body.appendChild(adSlotIframe); // iframe is not in ad slot + + const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + + expect(getSlotByWin(adSlotIframe.contentWindow)).to.be.null; + }); + }); + + describe('handle postMessage from Reconciliation Tag in ad iframe', function () { + it('should track impression pixel with parameters', function (done) { + const adSlotElement = document.createElement('div'); + const adSlotIframe = document.createElement('iframe'); + + adSlotElement.id = 'reconciliationAdMessage'; + adSlotElement.appendChild(adSlotIframe); + document.body.appendChild(adSlotElement); + + const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + // Fix targeting methods + adSlot.targeting = {}; + adSlot.setTargeting = function(key, value) { + this.targeting[key] = [value]; + }; + adSlot.getTargeting = function(key) { + return this.targeting[key]; + }; + + adSlot.setTargeting('RSDK_AUID', '/reconciliationAdunit'); + adSlot.setTargeting('RSDK_ADID', '12345'); + adSlotIframe.contentDocument.open(); + adSlotIframe.contentDocument.write(``); + adSlotIframe.contentDocument.close(); + + setTimeout(() => { + expect(trackGetStub.calledOnce).to.be.true; + expect(trackGetStub.getCalls()[0].args[0]).to.eql('https://confirm.fiduciadlt.com/imp'); + expect(trackGetStub.getCalls()[0].args[1].adUnitId).to.eql('/reconciliationAdunit'); + expect(trackGetStub.getCalls()[0].args[1].adDeliveryId).to.eql('12345'); + expect(trackGetStub.getCalls()[0].args[1].sourceMemberId).to.eql('test_member_id'); ; + expect(trackGetStub.getCalls()[0].args[1].sourceImpressionId).to.eql('123'); ; + expect(trackGetStub.getCalls()[0].args[1].publisherMemberId).to.eql('test_prebid_publisher'); + done(); + }, 100); + }); + }); + }); +}); From 01eb9534d2af678e129bd44d514bf859743638b6 Mon Sep 17 00:00:00 2001 From: sdao-tl <49252703+sdao-tl@users.noreply.github.com> Date: Wed, 21 Oct 2020 01:54:33 -0700 Subject: [PATCH 0296/1476] Update instream logic to account for multimp (#5872) * initial commit, instream poc done * push in poc changes * push in poc changes * restore instream.html * push in poc changes * restore instream.html * restore instream.html v2 * adding instream unit tests v1 * catch up to bidfloor changes * unit tests finalized! * update adapter md * add support for mediaTypes.video * merge in prebid master * add instream validation * add unit test for instream validation Co-authored-by: Sy Dao --- modules/tripleliftBidAdapter.js | 13 ++++++- .../spec/modules/tripleliftBidAdapter_spec.js | 37 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index b003de7785f..77a313a0f55 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -119,7 +119,8 @@ function _buildPostBody(bidRequests) { tagid: bidRequest.params.inventoryCode, floor: _getFloor(bidRequest) }; - if (bidRequest.mediaTypes.video) { + // remove the else to support multi-imp + if (_isInstreamBidRequest(bidRequest)) { imp.video = _getORTBVideo(bidRequest); } else if (bidRequest.mediaTypes.banner) { imp.banner = { format: _sizes(bidRequest.sizes) }; @@ -147,6 +148,16 @@ function _buildPostBody(bidRequests) { 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 _getORTBVideo(bidRequest) { // give precedent to mediaTypes.video let video = { ...bidRequest.params.video, ...bidRequest.mediaTypes.video }; diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 797b3fab0c1..96cab3d837c 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -164,6 +164,39 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + }, + { + 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] + }, + 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, } ]; @@ -228,6 +261,10 @@ describe('triplelift adapter', function () { 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[2]).to.not.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}]); }); it('should add tdid to the payload if included', function () { From 3423e7b12233a55e5b0382c03a657e86b6756932 Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Wed, 21 Oct 2020 10:24:27 +0100 Subject: [PATCH 0297/1476] Verizon Media user id module (#5786) * Initial work on Verizon Media User ID module * Submodule tests * Add sample eid object for Verizon Media * Documentation update * Switch to HTTP GET, update tests. * Remove single test restriction. * Documentation update * Addressing initial PR feedback. * Accept pixelId parameter to construct VMUID URL * Fix tests following API signature change * Add IAB vendor ID Co-authored-by: slimkrazy --- modules/.submodules.json | 3 +- modules/userId/eids.js | 6 + modules/userId/eids.md | 12 ++ modules/verizonMediaIdSystem.js | 103 ++++++++++ modules/verizonMediaSystemId.md | 33 ++++ .../spec/modules/verizonMediaIdSystem_spec.js | 182 ++++++++++++++++++ 6 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 modules/verizonMediaIdSystem.js create mode 100644 modules/verizonMediaSystemId.md create mode 100644 test/spec/modules/verizonMediaIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 78fd9376dd1..9b523a0c73a 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -17,7 +17,8 @@ "haloIdSystem", "quantcastIdSystem", "idxIdSystem", - "fabrickIdSystem" + "fabrickIdSystem", + "verizonMediaIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/userId/eids.js b/modules/userId/eids.js index f6c58a5a0bf..8118607fbde 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -157,6 +157,12 @@ const USER_IDS_CONFIG = { source: 'idx.lat', atype: 1 }, + + // Verizon Media + 'vmuid': { + source: 'verizonmedia.com', + atype: 1 + } }; // this function will create an eid object for the given UserId sub-module diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 7dc149cd47a..3e51eff3165 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -90,6 +90,7 @@ userIdAsEids = [ atype: 1 }] }, + { source: 'sharedid.org', uids: [{ @@ -100,6 +101,7 @@ userIdAsEids = [ } }] }, + { source: 'zeotap.com', uids: [{ @@ -107,6 +109,7 @@ userIdAsEids = [ atype: 1 }] }, + { source: 'audigent.com', uids: [{ @@ -114,12 +117,21 @@ userIdAsEids = [ atype: 1 }] }, + { source: 'quantcast.com', uids: [{ id: 'some-random-id-value', atype: 1 }] + }, + + { + source: 'verizonmedia.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] } ] ``` diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js new file mode 100644 index 00000000000..617561765cc --- /dev/null +++ b/modules/verizonMediaIdSystem.js @@ -0,0 +1,103 @@ +/** + * This module adds verizonMediaId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/verizonMediaIdSystem + * @requires module:modules/userId + */ + +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; +import * as utils from '../src/utils.js'; + +const MODULE_NAME = 'verizonMediaId'; +const VENDOR_ID = 25; +const PLACEHOLDER = '__PIXEL_ID__'; +const VMUID_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; + +function isEUConsentRequired(consentData) { + return !!(consentData && consentData.gdpr && consentData.gdpr.gdprApplies); +} + +/** @type {Submodule} */ +export const verizonMediaIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * Vendor id of Verizon Media EMEA Limited + * @type {Number} + */ + gvlid: VENDOR_ID, + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{vmuid: string} | undefined} + */ + decode(value) { + return (value && typeof value.vmuid === 'string') ? {vmuid: value.vmuid} : undefined; + }, + /** + * get the VerizonMedia Id + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ + getId(config, consentData) { + const params = config.params || {}; + if (!params || typeof params.he !== 'string' || + (typeof params.pixelId === 'undefined' && typeof params.endpoint === 'undefined')) { + utils.logError('The verizonMediaId submodule requires the \'he\' and \'pixelId\' parameters to be defined.'); + return; + } + + const data = { + '1p': [1, '1', true].includes(params['1p']) ? '1' : '0', + he: params.he, + gdpr: isEUConsentRequired(consentData) ? '1' : '0', + euconsent: isEUConsentRequired(consentData) ? consentData.gdpr.consentString : '', + us_privacy: consentData && consentData.uspConsent ? consentData.uspConsent : '' + }; + + if (params.pixelId) { + data.pixelId = params.pixelId + } + + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, + error: error => { + utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + const endpoint = VMUID_ENDPOINT.replace(PLACEHOLDER, params.pixelId); + let url = `${params.endpoint || endpoint}?${utils.formatQS(data)}`; + verizonMediaIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); + }; + return {callback: resp}; + }, + + /** + * Return the function used to perform XHR calls. + * Utilised for each of testing. + * @returns {Function} + */ + getAjaxFn() { + return ajax; + } +}; + +submodule('userId', verizonMediaIdSubmodule); diff --git a/modules/verizonMediaSystemId.md b/modules/verizonMediaSystemId.md new file mode 100644 index 00000000000..8d0e0bddaa9 --- /dev/null +++ b/modules/verizonMediaSystemId.md @@ -0,0 +1,33 @@ +## Verizon Media User ID Submodule + +Verizon Media User ID Module. + +### Prebid Params + +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'verizonMediaId', + storage: { + name: 'vmuid', + type: 'html5', + expires: 30 + }, + params: { + pixelId: 58776, + he: '0bef996248d63cea1529cb86de31e9547a712d9f380146e98bbd39beec70355a' + } + }] + } +}); +``` +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the Verizon Media User ID Module integration. + +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the Verizon Media module - `"verizonMediaId"` | `"verizonMediaId"` | +| params | Required | Object | Data for Verizon Media ID initialization. | | +| params.pixelId | Required | Number | The Verizon Media supplied publisher specific pixel Id | `8976` | +| params.he | Required | String | The SHA-256 hashed user email address | `"529cb86de31e9547a712d9f380146e98bbd39beec"` | diff --git a/test/spec/modules/verizonMediaIdSystem_spec.js b/test/spec/modules/verizonMediaIdSystem_spec.js new file mode 100644 index 00000000000..a30be5a2569 --- /dev/null +++ b/test/spec/modules/verizonMediaIdSystem_spec.js @@ -0,0 +1,182 @@ +import {expect} from 'chai'; +import * as utils from 'src/utils.js'; +import {verizonMediaIdSubmodule} from 'modules/verizonMediaIdSystem.js'; + +describe('Verizon Media ID Submodule', () => { + const HASHED_EMAIL = '6bda6f2fa268bf0438b5423a9861a2cedaa5dec163c03f743cfe05c08a8397b2'; + const PIXEL_ID = '1234'; + const PROD_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PIXEL_ID}/fed`; + const OVERRIDE_ENDPOINT = 'https://foo/bar'; + + it('should have the correct module name declared', () => { + expect(verizonMediaIdSubmodule.name).to.equal('verizonMediaId'); + }); + + it('should have the correct TCFv2 Vendor ID declared', () => { + expect(verizonMediaIdSubmodule.gvlid).to.equal(25); + }); + + describe('getId()', () => { + let ajaxStub; + let getAjaxFnStub; + let consentData; + beforeEach(() => { + ajaxStub = sinon.stub(); + getAjaxFnStub = sinon.stub(verizonMediaIdSubmodule, 'getAjaxFn'); + getAjaxFnStub.returns(ajaxStub); + + consentData = { + gdpr: { + gdprApplies: 1, + consentString: 'GDPR_CONSENT_STRING' + }, + uspConsent: 'USP_CONSENT_STRING' + }; + }); + + afterEach(() => { + getAjaxFnStub.restore(); + }); + + function invokeGetIdAPI(configParams, consentData) { + let result = verizonMediaIdSubmodule.getId({ + params: configParams + }, consentData); + if (typeof result === 'object') { + result.callback(sinon.stub()); + } + return result; + } + + it('returns undefined if he and pixelId params are not passed', () => { + expect(invokeGetIdAPI({}, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns undefined if the pixelId param is not passed', () => { + expect(invokeGetIdAPI({ + he: HASHED_EMAIL + }, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns undefined if the he param is not passed', () => { + expect(invokeGetIdAPI({ + pixelId: PIXEL_ID + }, consentData)).to.be.undefined; + expect(ajaxStub.callCount).to.equal(0); + }); + + it('returns an object with the callback function if the correct params are passed', () => { + let result = invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + expect(result).to.be.an('object').that.has.all.keys('callback'); + expect(result.callback).to.be.a('function'); + }); + + it('Makes an ajax GET request to the production API endpoint with query params', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + '1p': '0', + gdpr: '1', + euconsent: consentData.gdpr.consentString, + us_privacy: consentData.uspConsent + }; + const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('Makes an ajax GET request to the specified override API endpoint with query params', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + endpoint: OVERRIDE_ENDPOINT + }, consentData); + + const expectedParams = { + he: HASHED_EMAIL, + '1p': '0', + gdpr: '1', + euconsent: consentData.gdpr.consentString, + us_privacy: consentData.uspConsent + }; + const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + + expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); + expect(requestQueryParams).to.deep.equal(expectedParams); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + }); + + it('sets the callbacks param of the ajax function call correctly', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + expect(ajaxStub.firstCall.args[1]).to.be.an('object').that.has.all.keys(['success', 'error']); + }); + + it('sets GDPR consent data flag correctly when call is under GDPR jurisdiction.', () => { + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams.gdpr).to.equal('1'); + expect(requestQueryParams.euconsent).to.equal(consentData.gdpr.consentString); + }); + + it('sets GDPR consent data flag correctly when call is NOT under GDPR jurisdiction.', () => { + consentData.gdpr.gdprApplies = false; + + invokeGetIdAPI({ + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams.gdpr).to.equal('0'); + expect(requestQueryParams.euconsent).to.equal(''); + }); + + [1, '1', true].forEach(firstPartyParamValue => { + it(`sets 1p payload property to '1' for a config value of ${firstPartyParamValue}`, () => { + invokeGetIdAPI({ + '1p': firstPartyParamValue, + he: HASHED_EMAIL, + pixelId: PIXEL_ID, + }, consentData); + + const requestQueryParams = utils.parseQS(ajaxStub.firstCall.args[0].split('?')[1]); + expect(requestQueryParams['1p']).to.equal('1'); + }); + }); + }); + + describe('decode()', () => { + const VALID_API_RESPONSE = { + vmuid: '1234' + }; + it('should return a newly constructed object with the vmuid property', () => { + expect(verizonMediaIdSubmodule.decode(VALID_API_RESPONSE)).to.deep.equal(VALID_API_RESPONSE); + expect(verizonMediaIdSubmodule.decode(VALID_API_RESPONSE)).to.not.equal(VALID_API_RESPONSE); + }); + + [{}, '', {foo: 'bar'}].forEach((response) => { + it(`should return undefined for an invalid response "${JSON.stringify(response)}"`, () => { + expect(verizonMediaIdSubmodule.decode(response)).to.be.undefined; + }); + }); + }); +}); From 820ec1d80fa1592e6e4ceab42efb2db92a9bc0f2 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Wed, 21 Oct 2020 17:11:34 +0300 Subject: [PATCH 0298/1476] Use new ad request format by default in TheMediaGrid Bid Adapter (#5840) * 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 --- modules/gridBidAdapter.js | 583 ++++++++++------------- modules/gridBidAdapter.md | 3 +- test/spec/modules/gridBidAdapter_spec.js | 500 +++++++------------ 3 files changed, 413 insertions(+), 673 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index a3cd5b3cf56..5436a18c6cb 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -5,8 +5,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; -const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; -const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; +const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -44,19 +43,231 @@ export const spec = { * @return {ServerRequest[]} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const oldFormatBids = []; - const newFormatBids = []; + if (!validBidRequests.length) { + return null; + } + let pageKeywords = null; + let jwpseg = null; + let content = null; + let schain = null; + let userId = null; + let user = null; + let userExt = null; + let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; + const imp = []; + const bidsMap = {}; + validBidRequests.forEach((bid) => { - bid.params.useNewFormat ? newFormatBids.push(bid) : oldFormatBids.push(bid); + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userId) { + userId = bid.userId; + } + const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, jwTargeting} = bid; + bidsMap[bidId] = bid; + if (!pageKeywords && !utils.isEmpty(keywords)) { + pageKeywords = utils.transformBidderParamKeywords(keywords); + } + if (jwTargeting) { + if (!jwpseg && jwTargeting.segments) { + jwpseg = jwTargeting.segments; + } + if (!content && jwTargeting.content) { + content = jwTargeting.content; + } + } + let impObj = { + id: bidId, + tagid: uid.toString(), + ext: { + divid: adUnitCode + }, + bidfloor: _getFloor(mediaTypes || {}, bidFloor, bid) + }; + + if (!mediaTypes || mediaTypes[BANNER]) { + const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); + if (banner) { + impObj.banner = banner; + } + } + if (mediaTypes && mediaTypes[VIDEO]) { + const video = createVideoRequest(bid, mediaTypes[VIDEO]); + if (video) { + impObj.video = video; + } + } + + if (impObj.banner || impObj.video) { + imp.push(impObj); + } }); - const requests = []; - if (newFormatBids.length) { - requests.push(buildNewRequest(newFormatBids, bidderRequest)); + + const source = { + tid: auctionId, + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' + } + }; + + if (schain) { + source.ext.schain = schain; } - if (oldFormatBids.length) { - requests.push(buildOldRequest(oldFormatBids, bidderRequest)); + + const bidderTimeout = config.getConfig('bidderTimeout') || timeout; + const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + + let request = { + id: bidderRequestId, + site: { + page: referer + }, + tmax, + source, + imp + }; + + if (content) { + request.site.content = content; + } + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = {consent: gdprConsent.consentString}; + } + + if (userId) { + if (userId.tdid) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'adserver.org', // Unified ID + uids: [{ + id: userId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); + } + if (userId.id5id && userId.id5id.uid) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'id5-sync.com', + uids: [{ + id: userId.id5id.uid + }], + ext: userId.id5id.ext + }); + } + if (userId.lipb && userId.lipb.lipbid) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'liveintent.com', + uids: [{ + id: userId.lipb.lipbid + }] + }); + } + if (userId.idl_env) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'identityLink', + uids: [{ + id: userId.idl_env + }] + }); + } + if (userId.criteoId) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'criteo.com', + uids: [{ + id: userId.criteoId + }] + }); + } + + if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt = userExt || {}; + userExt.digitrust = Object.assign({}, userId.digitrustid.data); + } } - return requests; + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } + + if (user) { + request.user = user; + } + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } + + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + } + } + + if (uspConsent) { + if (!request.regs) { + request.regs = {ext: {}}; + } + request.regs.ext.us_privacy = uspConsent; + } + + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(request), + newFormat: true, + bidsMap + }; }, /** * Unpack the response from the server into a list of bids. @@ -108,6 +319,34 @@ export const spec = { } }; +/** + * Gets bidfloor + * @param {Object} mediaTypes + * @param {Number} bidfloor + * @param {Object} bid + * @returns {Number} floor + */ +function _getFloor (mediaTypes, bidfloor, bid) { + const curMediaType = mediaTypes.video ? 'video' : 'banner'; + let floor = bidfloor || 0; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: bid.sizes.map(([w, h]) => ({w, h})) + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + function isPopulatedArray(arr) { return !!(utils.isArray(arr) && arr.length > 0); } @@ -135,24 +374,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - let bid = null; - let slot = null; - const bidsMap = bidRequest.bidsMap; - if (bidRequest.newFormat) { - bid = bidsMap[serverBid.impid]; - } else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - slot = awaitingBids[sizeId][0]; - bid = slot.bids.shift(); - } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; - } - } - + const bid = bidRequest.bidsMap[serverBid.impid]; if (bid) { const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, @@ -184,21 +406,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { bidResponse.mediaType = BANNER; } bidResponses.push(bidResponse); - - if (slot && !slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } - }); - } } } if (errorMessage) { @@ -206,294 +413,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { } } -function buildOldRequest(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; - const bids = validBidRequests || []; - let pageKeywords = null; - let reqId; - - bids.forEach(bid => { - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode, mediaTypes} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); - } - - const addedSizes = {}; - sizesId.forEach((sizeId) => { - addedSizes[sizeId] = true; - }); - const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); - const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); - bannerSizesId.concat(videoSizesId).forEach((sizeId) => { - if (!addedSizes[sizeId]) { - addedSizes[sizeId] = true; - sizesId.push(sizeId); - } - }); - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); - } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); - }); - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null - }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); - } - - const payload = { - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); - } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } - } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap - } -} - -function buildNewRequest(validBidRequests, bidderRequest) { - let pageKeywords = null; - let jwpseg = null; - let content = null; - let schain = null; - let userId = null; - let user = null; - let userExt = null; - let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest; - - const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; - const imp = []; - const bidsMap = {}; - - validBidRequests.forEach((bid) => { - if (!bidderRequestId) { - bidderRequestId = bid.bidderRequestId; - } - if (!auctionId) { - auctionId = bid.auctionId; - } - if (!schain) { - schain = bid.schain; - } - if (!userId) { - userId = bid.userId; - } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, jwTargeting} = bid; - bidsMap[bidId] = bid; - if (!pageKeywords && !utils.isEmpty(keywords)) { - pageKeywords = utils.transformBidderParamKeywords(keywords); - } - if (jwTargeting) { - if (!jwpseg && jwTargeting.segments) { - jwpseg = jwTargeting.segments; - } - if (!content && jwTargeting.content) { - content = jwTargeting.content; - } - } - let impObj = { - id: bidId, - tagid: uid.toString(), - ext: { - divid: adUnitCode - } - }; - - if (!mediaTypes || mediaTypes[BANNER]) { - const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); - if (banner) { - impObj.banner = banner; - } - } - if (mediaTypes && mediaTypes[VIDEO]) { - const video = createVideoRequest(bid, mediaTypes[VIDEO]); - if (video) { - impObj.video = video; - } - } - - if (impObj.banner || impObj.video) { - imp.push(impObj); - } - }); - - const source = { - tid: auctionId, - ext: { - wrapper: 'Prebid_js', - wrapper_version: '$prebid.version$' - } - }; - - if (schain) { - source.ext.schain = schain; - } - - const tmax = config.getConfig('bidderTimeout') || timeout; - - let request = { - id: bidderRequestId, - site: { - page: referer - }, - tmax, - source, - imp - }; - - if (content) { - request.site.content = content; - } - - if (jwpseg && jwpseg.length) { - user = { - data: [{ - name: 'iow_labs_pub_data', - segment: jwpseg.map((seg) => { - return {name: 'jwpseg', value: seg}; - }) - }] - }; - } - - if (gdprConsent && gdprConsent.consentString) { - userExt = {consent: gdprConsent.consentString}; - } - - if (userId) { - userExt = userExt || {}; - if (userId.tdid) { - userExt.unifiedid = userId.tdid; - } - if (userId.id5id && userId.id5id.uid) { - userExt.id5id = userId.id5id.uid; - } - if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt.digitrustid = userId.digitrustid.data.id; - } - if (userId.lipb && userId.lipb.lipbid) { - userExt.liveintentid = userId.lipb.lipbid; - } - } - - if (userExt && Object.keys(userExt).length) { - user = user || {}; - user.ext = userExt; - } - - if (user) { - request.user = user; - } - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null - }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); - } - - if (pageKeywords) { - request.ext = { - keywords: pageKeywords - }; - } - - if (gdprConsent && gdprConsent.gdprApplies) { - request.regs = { - ext: { - gdpr: gdprConsent.gdprApplies ? 1 : 0 - } - } - } - - if (uspConsent) { - if (!request.regs) { - request.regs = {ext: {}}; - } - request.regs.ext.us_privacy = uspConsent; - } - - return { - method: 'POST', - url: NEW_ENDPOINT_URL, - data: JSON.stringify(request), - newFormat: true, - bidsMap - }; -} - function createVideoRequest(bid, mediaType) { const {playerSize, mimes, durationRangeSec} = mediaType; const size = (playerSize || bid.sizes || [])[0]; diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 77b9bbf0f36..6a7075ccb00 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -20,7 +20,7 @@ Grid bid adapter supports Banner and Video (instream and outstream). bidder: "grid", params: { uid: '1', - priceType: 'gross' // by default is 'net' + bidFloor: 0.5 } } ] @@ -32,7 +32,6 @@ Grid bid adapter supports Banner and Video (instream and outstream). bidder: "grid", params: { uid: 2, - priceType: 'gross', keywords: { brandsafety: ['disaster'], topic: ['stress', 'fear'] diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 72e49e5dbdf..9cd7d100318 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -40,229 +40,6 @@ describe('TheMediaGrid Adapter', function () { }); describe('buildRequests', function () { - function parseRequest(url) { - const res = {}; - url.split('&').forEach((it) => { - const couple = it.split('='); - res[couple[0]] = decodeURIComponent(couple[1]); - }); - return res; - } - const bidderRequest = {refererInfo: {referer: 'https://example.com'}}; - const referrer = bidderRequest.refererInfo.referer; - let bidRequests = [ - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'mediaTypes': { - 'video': { - 'playerSize': [400, 600] - }, - 'banner': { - 'sizes': [[728, 90]] - } - }, - 'bidId': '3150ccb55da321', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '2' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'mediaTypes': { - 'video': { - 'playerSize': [400, 600] - }, - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'bidId': '42dbe3a7168a6a', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should attach valid params to the tag', function () { - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - }); - - it('sizes must be added from mediaTypes', function () { - const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1,1'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('sizes must not be duplicated', function () { - const [request] = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1,1,2'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('if gdprConsent is present payload must have gdpr params', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if gdprApplies is false gdpr_applies must be 0', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '0'); - }); - - it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('us_privacy', '1YNN'); - }); - - it('should convert keyword params to proper form and attaches to request', function () { - const bidRequestWithKeywords = [].concat(bidRequests); - bidRequestWithKeywords[1] = Object.assign({}, - bidRequests[1], - { - params: { - uid: '1', - keywords: { - single: 'val', - singleArr: ['val'], - singleArrNum: [3], - multiValMixed: ['value1', 2, 'value3'], - singleValNum: 123, - emptyStr: '', - emptyArr: [''], - badValue: {'foo': 'bar'} // should be dropped - } - } - } - ); - - const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.keywords).to.be.an('string'); - payload.keywords = JSON.parse(payload.keywords); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['3'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); - }); - - it('should mix keyword param with keywords from config', function () { - const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'fpd.user' ? {'keywords': ['a', 'b']} : arg === 'fpd.context' ? {'keywords': ['any words']} : null); - - const bidRequestWithKeywords = [].concat(bidRequests); - bidRequestWithKeywords[1] = Object.assign({}, - bidRequests[1], - { - params: { - uid: '1', - keywords: { - single: 'val', - singleArr: ['val'], - multiValMixed: ['value1', 2, 'value3'] - } - } - } - ); - - const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.keywords).to.be.an('string'); - payload.keywords = JSON.parse(payload.keywords); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'user', - 'value': ['a', 'b'] - }, { - 'key': 'context', - 'value': ['any words'] - }]); - - getConfigStub.restore(); - }); - }); - - describe('buildRequests in new format', function () { function parseRequest(data) { return JSON.parse(data); } @@ -278,7 +55,7 @@ describe('TheMediaGrid Adapter', function () { 'bidder': 'grid', 'params': { 'uid': '1', - 'useNewFormat': true + 'bidFloor': 1.25 }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -294,8 +71,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '2', - 'useNewFormat': true + 'uid': '2' }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -306,8 +82,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '11', - 'useNewFormat': true + 'uid': '11' }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90]], @@ -324,8 +99,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '3', - 'useNewFormat': true + 'uid': '3' }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90]], @@ -344,7 +118,7 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -361,6 +135,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -371,7 +146,7 @@ describe('TheMediaGrid Adapter', function () { }); it('make possible to process request without mediaTypes', function () { - const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -388,6 +163,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -397,6 +173,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -407,7 +184,7 @@ describe('TheMediaGrid Adapter', function () { }); it('should attach valid params to the video tag', function () { - const [request] = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); + const request = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -424,6 +201,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -433,6 +211,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -442,6 +221,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, + 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -452,7 +232,7 @@ describe('TheMediaGrid Adapter', function () { }); it('should support mixed mediaTypes', function () { - const [request] = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -469,6 +249,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -478,6 +259,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -487,6 +269,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, + 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -496,6 +279,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[3].bidId, 'tagid': bidRequests[3].params.uid, 'ext': {'divid': bidRequests[3].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 728, 'h': 90, @@ -511,7 +295,7 @@ describe('TheMediaGrid Adapter', function () { 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); + const request = spec.buildRequests(bidRequests, gdprBidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -524,7 +308,7 @@ describe('TheMediaGrid Adapter', function () { it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); + const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('regs'); @@ -536,22 +320,65 @@ describe('TheMediaGrid Adapter', function () { const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ userId: { - id5id: { uid: 'id5id_1' }, + id5id: { uid: 'id5id_1', ext: { linkType: 2 } }, tdid: 'tdid_1', digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - lipb: {lipbid: 'lipb_1'} + lipb: {lipbid: 'lipb_1'}, + idl_env: 'idl_env_1', + criteoId: 'criteoId_1' } }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); expect(payload.user).to.have.property('ext'); - expect(payload.user.ext).to.have.property('unifiedid', 'tdid_1'); - expect(payload.user.ext).to.have.property('id5id', 'id5id_1'); - expect(payload.user.ext).to.have.property('digitrustid', 'DTID'); - expect(payload.user.ext).to.have.property('liveintentid', 'lipb_1'); + expect(payload.user.ext.digitrust).to.deep.equal({ + id: 'DTID', + keyv: 4, + privacy: { + optout: false + }, + producer: 'ABC', + version: 2 + }); + expect(payload.user.ext.eids).to.deep.equal([ + { + source: 'adserver.org', + uids: [{ + id: 'tdid_1', + ext: { + rtiPartner: 'TDID' + } + }] + }, + { + source: 'id5-sync.com', + uids: [{ + id: 'id5id_1' + }], + ext: { linkType: 2 } + }, + { + source: 'liveintent.com', + uids: [{ + id: 'lipb_1' + }] + }, + { + source: 'identityLink', + uids: [{ + id: 'idl_env_1' + }] + }, + { + source: 'criteo.com', + uids: [{ + id: 'criteoId_1' + }] + } + ]); }); it('if schain is present payload must have source.ext.schain param', function () { @@ -570,7 +397,7 @@ describe('TheMediaGrid Adapter', function () { schain: schain }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithSChain, bidderRequest); + const request = spec.buildRequests(bidRequestsWithSChain, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('source'); @@ -590,7 +417,7 @@ describe('TheMediaGrid Adapter', function () { } }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -604,14 +431,68 @@ describe('TheMediaGrid Adapter', function () { expect(payload).to.have.property('site'); expect(payload.site.content).to.deep.equal(jsContent); }); + + it('shold 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('shold 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(); + }); + describe('floorModule', function () { + const floorTestData = { + 'currency': 'USD', + 'floor': 1.50 + }; + const bidRequest = Object.assign({ + getFloor: _ => { + return floorTestData; + } + }, bidRequests[1]); + it('should return the value from getFloor if present', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the getFloor.floor value if it is greater than bidfloor', function () { + const bidfloor = 0.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the bidfloor value if it is greater than getFloor.floor', function () { + const bidfloor = 1.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(bidfloor); + }); + }); }); describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '659423fff799cb', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '1'}, + {'bid': [{'impid': '4dff80cc4ee346', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '5703af74d0472a', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'impid': '2344da98f78b42', 'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, @@ -632,7 +513,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -663,7 +544,7 @@ describe('TheMediaGrid Adapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71a5b', + 'bidId': '659423fff799cb', 'bidderRequestId': '2c2bb1972df9a', 'auctionId': '1fa09aee5c8c99', }, @@ -690,10 +571,10 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c8c99', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { - 'requestId': '300bfeb0d71a5b', + 'requestId': '659423fff799cb', 'cpm': 1.15, 'creativeId': 1, 'dealId': 11, @@ -776,10 +657,10 @@ describe('TheMediaGrid Adapter', function () { } ]; const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} + {'bid': [{'impid': '659423fff799cb', 'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'impid': '2bc598e42b6a', 'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -797,6 +678,23 @@ describe('TheMediaGrid Adapter', function () { 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } + }, + { + 'requestId': '2bc598e42b6a', + 'cpm': 1.00, + 'creativeId': 12, + 'dealId': undefined, + 'width': undefined, + 'height': undefined, + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': false, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } } ]; @@ -840,18 +738,18 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c84d34', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); expect(result.length).to.equal(0); }); it('complicated case', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, + {'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'}, + {'bid': [{'impid': '26d6f897b516', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'impid': '326bde7fbf69', 'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '2234f233b22a', 'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, ]; const bidRequests = [ { @@ -910,7 +808,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '32a1f276cb87cb8', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '2164be6358b9', @@ -973,82 +871,6 @@ describe('TheMediaGrid Adapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); - - it('dublicate uids and sizes in one slot', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '5126e301f4be', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57b2ebe70e16', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '225fcd44b18c', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - } - ]; - const [request] = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '5126e301f4be', - 'cpm': 1.15, - 'creativeId': 1, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'grid', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': false, - 'ttl': 360, - }, - { - 'requestId': '57b2ebe70e16', - 'cpm': 0.5, - 'creativeId': 1, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 2
', - 'bidderCode': 'grid', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': false, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); }); describe('user sync', function () { From 73b4fc741478b20540c8317543891a98ffae583e Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 21 Oct 2020 13:48:20 -0400 Subject: [PATCH 0299/1476] Floors Module update to include floorMin (#5805) * Update to floors module to allow floorMin definition using setConfig({floors:...}); 1) If floorMin exists, set floorValue to new property floorRuleValue. 2) If floorMin is greater than floorValue, set floorValue to floorMin. Update to Rubicon Analytics Adapter to pass floorMin under auction.floors.floorMin if exists. Also includes update to pass floorRuleValue for each bid if floorMin exists Update to floorsModule roundup functionality to fix to one decimal place prior to roundup. This will fix issues in which JS evalutates a whole number to include a very small decimal value that forces a roundup to the next whole number. * Remove extra spaces * Package Lock revert * Updates to commit * Remove comment * Remove excess spaces * Update to priceFloor and rubiconAnalytics adapters --- modules/priceFloors.js | 15 +- modules/rubiconAnalyticsAdapter.js | 3 + test/spec/modules/priceFloors_spec.js | 156 ++++++++++++++++++ .../modules/rubiconAnalyticsAdapter_spec.js | 111 ++++++++++++- 4 files changed, 277 insertions(+), 8 deletions(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 1b865e05c0a..fd8a46b172f 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -55,7 +55,7 @@ export let _floorDataForAuction = {}; * @summary Simple function to round up to a certain decimal degree */ function roundUp(number, precision) { - return Math.ceil(parseFloat(number) * Math.pow(10, precision)) / Math.pow(10, precision); + return Math.ceil((parseFloat(number) * Math.pow(10, precision)).toFixed(1)) / Math.pow(10, precision); } let referrerHostname; @@ -98,7 +98,7 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) let fieldValues = enumeratePossibleFieldValues(utils.deepAccess(floorData, 'schema.fields') || [], bidObject, responseObject); if (!fieldValues.length) return { matchingFloor: floorData.default }; - // look to see iof a request for this context was made already + // look to see if a request for this context was made already let matchingInput = fieldValues.map(field => field[0]).join('-'); // if we already have gotten the matching rule from this matching input then use it! No need to look again let previousMatch = utils.deepAccess(floorData, `matchingInputs.${matchingInput}`); @@ -109,10 +109,12 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) let matchingRule = find(allPossibleMatches, hashValue => floorData.values.hasOwnProperty(hashValue)); let matchingData = { - matchingFloor: floorData.values[matchingRule] || floorData.default, + floorMin: floorData.floorMin || 0, + floorRuleValue: floorData.values[matchingRule] || floorData.default, matchingData: allPossibleMatches[0], // the first possible match is an "exact" so contains all data relevant for anlaytics adapters matchingRule }; + matchingData.matchingFloor = Math.max(matchingData.floorMin, matchingData.floorRuleValue); // save for later lookup if needed utils.deepSetValue(floorData, `matchingInputs.${matchingInput}`, {...matchingData}); return matchingData; @@ -287,11 +289,12 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { bid.floorData = { skipped: floorData.skipped, skipRate: floorData.skipRate, + floorMin: floorData.floorMin, modelVersion: utils.deepAccess(floorData, 'data.modelVersion'), location: utils.deepAccess(floorData, 'data.location', 'noData'), floorProvider: floorData.floorProvider, fetchStatus: _floorsConfig.fetchStatus - } + }; }); }); } @@ -336,6 +339,8 @@ export function createFloorsDataForAuction(adUnits, auctionId) { const isSkipped = Math.random() * 100 < parseFloat(auctionSkipRate); resolvedFloorsData.skipped = isSkipped; } + // copy FloorMin to floorData.data + if (resolvedFloorsData.hasOwnProperty('floorMin')) resolvedFloorsData.data.floorMin = resolvedFloorsData.floorMin; // add floorData to bids updateAdUnitsForAuction(adUnits, resolvedFloorsData, auctionId); return resolvedFloorsData; @@ -568,6 +573,7 @@ function addFieldOverrides(overrides) { */ export function handleSetFloorsConfig(config) { _floorsConfig = utils.pick(config, [ + 'floorMin', 'enabled', enabled => enabled !== false, // defaults to true 'auctionDelay', auctionDelay => auctionDelay || 0, 'floorProvider', floorProvider => utils.deepAccess(config, 'data.floorProvider', floorProvider), @@ -623,6 +629,7 @@ function addFloorDataToBid(floorData, floorInfo, bid, adjustedCpm) { bid.floorData = { floorValue: floorInfo.matchingFloor, floorRule: floorInfo.matchingRule, + floorRuleValue: floorInfo.floorRuleValue, floorCurrency: floorData.data.currency, cpmAfterAdjustments: adjustedCpm, enforcements: {...floorData.enforcement}, diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index f6d30e06e9a..4011232ae3b 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -129,6 +129,7 @@ function sendMessage(auctionId, bidWonId) { 'dimensions', 'mediaType', 'floorValue', + 'floorRuleValue', 'floorRule' ]) : undefined ]); @@ -233,6 +234,7 @@ function sendMessage(auctionId, bidWonId) { 'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'), 'skipRate', 'fetchStatus', + 'floorMin', 'floorProvider as provider' ]); } @@ -344,6 +346,7 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { }, 'seatBidId', 'floorValue', () => utils.deepAccess(bid, 'floorData.floorValue'), + 'floorRuleValue', () => utils.deepAccess(bid, 'floorData.floorRuleValue'), 'floorRule', () => utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined ]); } diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index ae45244f03d..8c673d29701 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -34,6 +34,34 @@ describe('the price floors module', function () { '*': 2.5 } }; + const basicFloorDataHigh = { + floorMin: 7.0, + modelVersion: 'basic model', + currency: 'USD', + schema: { + delimiter: '|', + fields: ['mediaType'] + }, + values: { + 'banner': 1.0, + 'video': 5.0, + '*': 2.5 + } + }; + const basicFloorDataLow = { + floorMin: 2.3, + modelVersion: 'basic model', + currency: 'USD', + schema: { + delimiter: '|', + fields: ['mediaType'] + }, + values: { + 'banner': 1.0, + 'video': 5.0, + '*': 2.5 + } + }; const basicFloorConfig = { enabled: true, auctionDelay: 0, @@ -46,6 +74,32 @@ describe('the price floors module', function () { }, data: basicFloorData } + const minFloorConfigHigh = { + enabled: true, + auctionDelay: 0, + floorMin: 7, + endpoint: {}, + enforcement: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + data: basicFloorDataHigh + } + const minFloorConfigLow = { + enabled: true, + auctionDelay: 0, + floorMin: 2.3, + endpoint: {}, + enforcement: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + data: basicFloorDataLow + } const basicBidRequest = { bidder: 'rubicon', adUnitCode: 'test_div_1', @@ -165,22 +219,50 @@ describe('the price floors module', function () { it('selects the right floor for different mediaTypes', function () { // banner with * size (not in rule file so does not do anything) expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.0, matchingFloor: 1.0, matchingData: 'banner', matchingRule: 'banner' }); // video with * size (not in rule file so does not do anything) expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'video', size: '*'})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 5.0, matchingFloor: 5.0, matchingData: 'video', matchingRule: 'video' }); // native (not in the rule list) with * size (not in rule file so does not do anything) expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'native', size: '*'})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 2.5, matchingFloor: 2.5, matchingData: 'native', matchingRule: '*' }); + // banner with floorMin higher than matching rule + handleSetFloorsConfig({ + ...minFloorConfigHigh + }); + expect(getFirstMatchingFloor({...basicFloorDataHigh}, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + floorMin: 7, + floorRuleValue: 1.0, + matchingFloor: 7, + matchingData: 'banner', + matchingRule: 'banner' + }); + // banner with floorMin higher than matching rule + handleSetFloorsConfig({ + ...minFloorConfigLow + }); + expect(getFirstMatchingFloor({...basicFloorDataLow}, basicBidRequest, {mediaType: 'video', size: '*'})).to.deep.equal({ + floorMin: 2.3, + floorRuleValue: 5, + matchingFloor: 5, + matchingData: 'video', + matchingRule: 'video' + }); }); it('does not alter cached matched input if conversion occurs', function () { let inputData = {...basicFloorData}; @@ -188,6 +270,8 @@ describe('the price floors module', function () { let result = getFirstMatchingFloor(inputData, basicBidRequest, {mediaType: 'banner', size: '*'}); // result should always be the same expect(result).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.0, matchingFloor: 1.0, matchingData: 'banner', matchingRule: 'banner' @@ -213,24 +297,32 @@ describe('the price floors module', function () { } // banner with 300x250 size expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: [300, 250]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.1, matchingFloor: 1.1, matchingData: '300x250', matchingRule: '300x250' }); // video with 300x250 size expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.1, matchingFloor: 1.1, matchingData: '300x250', matchingRule: '300x250' }); // native (not in the rule list) with 300x600 size expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'native', size: [600, 300]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 4.4, matchingFloor: 4.4, matchingData: '600x300', matchingRule: '600x300' }); // n/a mediaType with a size not in file should go to catch all expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: undefined, size: [1, 1]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 5.5, matchingFloor: 5.5, matchingData: '1x1', matchingRule: '*' @@ -254,12 +346,16 @@ describe('the price floors module', function () { }; // banner with 300x250 size expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: [300, 250]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.1, matchingFloor: 1.1, matchingData: 'test_div_1^banner^300x250', matchingRule: 'test_div_1^banner^300x250' }); // video with 300x250 size -> No matching rule so should use default expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 0.5, matchingFloor: 0.5, matchingData: 'test_div_1^video^300x250', matchingRule: undefined @@ -267,6 +363,8 @@ describe('the price floors module', function () { // remove default and should still return the same floor as above since matches are cached delete inputFloorData.default; expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 0.5, matchingFloor: 0.5, matchingData: 'test_div_1^video^300x250', matchingRule: undefined @@ -274,6 +372,8 @@ describe('the price floors module', function () { // update adUnitCode to test_div_2 with weird other params let newBidRequest = { ...basicBidRequest, adUnitCode: 'test_div_2' } expect(getFirstMatchingFloor(inputFloorData, newBidRequest, {mediaType: 'badmediatype', size: [900, 900]})).to.deep.equal({ + floorMin: 0, + floorRuleValue: 3.3, matchingFloor: 3.3, matchingData: 'test_div_2^badmediatype^900x900', matchingRule: 'test_div_2^*^*' @@ -327,6 +427,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(false, { skipped: true, + floorMin: undefined, modelVersion: undefined, location: 'noData', skipRate: 0, @@ -358,11 +459,46 @@ describe('the price floors module', function () { } }; runStandardAuction([adUnitWithFloors1, adUnitWithFloors2]); + validateBidRequests(true, { + skipped: false, + floorMin: undefined, + modelVersion: 'adUnit Model Version', + location: 'adUnit', + skipRate: 0, + fetchStatus: undefined, + floorProvider: undefined + }); + }); + it('should use adUnit level data and minFloor should be set', function () { + handleSetFloorsConfig({ + ...minFloorConfigHigh, + data: undefined + }); + // attach floor data onto an adUnit and run an auction + let adUnitWithFloors1 = { + ...getAdUnitMock('adUnit-Div-1'), + floors: { + ...basicFloorData, + modelVersion: 'adUnit Model Version', // change the model name + } + }; + let adUnitWithFloors2 = { + ...getAdUnitMock('adUnit-Div-2'), + floors: { + ...basicFloorData, + values: { + 'banner': 5.0, + '*': 10.4 + } + } + }; + runStandardAuction([adUnitWithFloors1, adUnitWithFloors2]); validateBidRequests(true, { skipped: false, modelVersion: 'adUnit Model Version', location: 'adUnit', skipRate: 0, + floorMin: 7, fetchStatus: undefined, floorProvider: undefined }); @@ -372,6 +508,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -392,6 +529,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -405,6 +543,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -418,6 +557,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -440,6 +580,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 50, @@ -453,6 +594,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 10, @@ -466,6 +608,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -529,6 +672,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'model-1', location: 'setConfig', skipRate: 0, @@ -541,6 +685,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'model-2', location: 'setConfig', skipRate: 0, @@ -553,6 +698,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'model-3', location: 'setConfig', skipRate: 0, @@ -581,6 +727,7 @@ describe('the price floors module', function () { runStandardAuction(); validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -659,6 +806,7 @@ describe('the price floors module', function () { // the exposedAdUnits should be from the fetch not setConfig level data validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -696,6 +844,7 @@ describe('the price floors module', function () { // and fetchStatus is success since fetch worked validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'fetch model name', location: 'fetch', skipRate: 0, @@ -732,6 +881,7 @@ describe('the price floors module', function () { // and fetchStatus is success since fetch worked validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'fetch model name', location: 'fetch', skipRate: 0, @@ -771,6 +921,7 @@ describe('the price floors module', function () { // and fetchStatus is success since fetch worked validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'fetch model name', location: 'fetch', skipRate: 95, @@ -792,6 +943,7 @@ describe('the price floors module', function () { // and fetch failed is true validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -815,6 +967,7 @@ describe('the price floors module', function () { // and fetchStatus is 'success' but location is setConfig since it had bad data validateBidRequests(true, { skipped: false, + floorMin: undefined, modelVersion: 'basic model', location: 'setConfig', skipRate: 0, @@ -1303,6 +1456,7 @@ describe('the price floors module', function () { runBidResponse(); expect(returnedBidResponse).to.haveOwnProperty('floorData'); expect(returnedBidResponse.floorData).to.deep.equal({ + floorRuleValue: 0.3, floorValue: 0.3, floorCurrency: 'USD', floorRule: 'banner', @@ -1340,6 +1494,7 @@ describe('the price floors module', function () { expect(returnedBidResponse).to.haveOwnProperty('floorData'); expect(returnedBidResponse.floorData).to.deep.equal({ floorValue: 0.5, + floorRuleValue: 0.5, floorCurrency: 'USD', floorRule: 'banner|300x250', cpmAfterAdjustments: 0.5, @@ -1366,6 +1521,7 @@ describe('the price floors module', function () { }); expect(returnedBidResponse).to.haveOwnProperty('floorData'); expect(returnedBidResponse.floorData).to.deep.equal({ + floorRuleValue: 5.5, floorValue: 5.5, floorCurrency: 'USD', floorRule: 'video|*', diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 2bbab506b34..7c552570da6 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -111,10 +111,60 @@ const BID2 = Object.assign({}, BID, { } }); +const BID3 = Object.assign({}, BID, { + adUnitCode: '/19968336/siderail-tag1', + bidId: '5fg6hyy4r879f0', + adId: 'fake_ad_id', + requestId: '5fg6hyy4r879f0', + width: 300, + height: 250, + mediaType: 'banner', + cpm: 2.01, + source: 'server', + seatBidId: 'aaaa-bbbb-cccc-dddd', + rubiconTargeting: { + 'rpfl_elemid': '/19968336/siderail-tag1', + 'rpfl_14062': '15_tier0200' + }, + adserverTargeting: { + 'hb_bidder': 'rubicon', + 'hb_adid': '5fg6hyy4r879f0', + 'hb_pb': '2.00', + 'hb_size': '300x250', + 'hb_source': 'server' + } +}); + +const floorMinRequest = { + 'bidder': 'rubicon', + 'params': { + 'accountId': '14062', + 'siteId': '70608', + 'zoneId': '335918', + 'userId': '12346', + 'keywords': ['a', 'b', 'c'], + 'inventory': {'rating': '4-star', 'prodtype': 'tech'}, + 'visitor': {'ucat': 'new', 'lastsearch': 'iphone'}, + 'position': 'atf' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': '/19968336/siderail-tag1', + 'transactionId': 'c435626g-9e3f-401a-bee1-d56aec29a1d4', + 'sizes': [[300, 250]], + 'bidId': '5fg6hyy4r879f0', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' +}; + const MOCK = { SET_TARGETING: { [BID.adUnitCode]: BID.adserverTargeting, - [BID2.adUnitCode]: BID2.adserverTargeting + [BID2.adUnitCode]: BID2.adserverTargeting, + [BID3.adUnitCode]: BID3.adserverTargeting }, AUCTION_INIT: { 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', @@ -241,7 +291,8 @@ const MOCK = { }, BID_RESPONSE: [ BID, - BID2 + BID2, + BID3 ], AUCTION_END: { 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' @@ -252,6 +303,9 @@ const MOCK = { }), Object.assign({}, BID2, { 'status': 'rendered' + }), + Object.assign({}, BID3, { + 'status': 'rendered' }) ], BIDDER_DONE: { @@ -260,6 +314,9 @@ const MOCK = { BID, Object.assign({}, BID2, { 'serverResponseTimeMs': 42, + }), + Object.assign({}, BID3, { + 'serverResponseTimeMs': 55, }) ] }, @@ -842,14 +899,40 @@ describe('rubicon analytics adapter', function () { } }; + let floorMinResponse = { + ...BID3, + floorData: { + floorValue: 1.5, + floorRuleValue: 1, + floorRule: '12345/entertainment|banner', + floorCurrency: 'USD', + cpmAfterAdjustments: 2.00, + enforcements: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + matchedFields: { + gptSlot: '12345/entertainment', + mediaType: 'banner' + } + } + }; + + let bidRequest = utils.deepClone(MOCK.BID_REQUESTED); + bidRequest.bids.push(floorMinRequest) + // spoof the auction with just our duplicates events.emit(AUCTION_INIT, auctionInit); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_REQUESTED, bidRequest); events.emit(BID_RESPONSE, flooredResponse); events.emit(BID_RESPONSE, notFlooredResponse); + events.emit(BID_RESPONSE, floorMinResponse); events.emit(AUCTION_END, MOCK.AUCTION_END); events.emit(SET_TARGETING, MOCK.SET_TARGETING); events.emit(BID_WON, MOCK.BID_WON[1]); + events.emit(BID_WON, MOCK.BID_WON[2]); clock.tick(SEND_TIMEOUT + 1000); expect(server.requests.length).to.equal(1); @@ -860,7 +943,7 @@ describe('rubicon analytics adapter', function () { } it('should capture price floor information correctly', function () { - let message = performFloorAuction('rubicon') + let message = performFloorAuction('rubicon'); // verify our floor stuff is passed // top level floor info @@ -892,6 +975,16 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.equal(1); expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); + + // second adUnit's adSlot + expect(message.auctions[0].adUnits[2].gam.adSlot).to.equal('12345/entertainment'); + // top level adUnit status is success + expect(message.auctions[0].adUnits[2].status).to.equal('success'); + // second adUnits bid is success + expect(message.auctions[0].adUnits[2].bids[0].status).to.equal('success'); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorValue).to.equal(1.5); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorRuleValue).to.equal(1); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.bidPriceUSD).to.equal(2.01); }); it('should still send floor info if provider is not rubicon', function () { @@ -927,6 +1020,16 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success'); expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.equal(1); expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52); + + // second adUnit's adSlot + expect(message.auctions[0].adUnits[2].gam.adSlot).to.equal('12345/entertainment'); + // top level adUnit status is success + expect(message.auctions[0].adUnits[2].status).to.equal('success'); + // second adUnits bid is success + expect(message.auctions[0].adUnits[2].bids[0].status).to.equal('success'); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorValue).to.equal(1.5); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.floorRuleValue).to.equal(1); + expect(message.auctions[0].adUnits[2].bids[0].bidResponse.bidPriceUSD).to.equal(2.01); }); describe('with session handling', function () { From 372fbbded6fbb2fa14c9f7a03d2d8388d8bfc87f Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 21 Oct 2020 11:45:04 -0700 Subject: [PATCH 0300/1476] Prebid 4.13.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6273d680e19..740e905053b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.13.0-pre", + "version": "4.13.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 73d880f962c7f1550d55d5decd9ae3b38d74918f Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 21 Oct 2020 12:19:18 -0700 Subject: [PATCH 0301/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 740e905053b..cf82e7a9e95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.13.0", + "version": "4.14.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d3c0dedca0390835dded96fb968740fe063d0437 Mon Sep 17 00:00:00 2001 From: John Rosendahl Date: Wed, 21 Oct 2020 16:24:32 -0600 Subject: [PATCH 0302/1476] configurable TTL for impressions (#5880) --- modules/sovrnBidAdapter.js | 2 +- test/spec/modules/sovrnBidAdapter_spec.js | 35 ++++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 935b0ceb489..62f5e85779e 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -143,7 +143,7 @@ export const spec = { netRevenue: true, mediaType: BANNER, ad: decodeURIComponent(`${sovrnBid.adm}`), - ttl: 60 + ttl: sovrnBid.ttl || 90 }); }); } diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 321fed40d83..983ade4dd14 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -331,16 +331,16 @@ describe('sovrnBidAdapter', function() { 'currency': 'USD', 'netRevenue': true, 'mediaType': 'banner', - 'ad': decodeURIComponent(`>`), - 'ttl': 60000 + 'ad': decodeURIComponent(``), + 'ttl': 90 }]; let result = spec.interpretResponse(response); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0]).to.deep.equal(expectedResponse[0]); }); it('should get correct bid response when dealId is passed', function () { - response.body.dealid = 'baking'; + response.body.seatbid[0].bid[0].dealid = 'baking'; let expectedResponse = [{ 'requestId': '263c448586f5a1', @@ -352,12 +352,33 @@ describe('sovrnBidAdapter', function() { 'currency': 'USD', 'netRevenue': true, 'mediaType': 'banner', - 'ad': decodeURIComponent(`>`), - 'ttl': 60000 + 'ad': decodeURIComponent(``), + 'ttl': 90 }]; let result = spec.interpretResponse(response); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('should get correct bid response when ttl is set', function () { + response.body.seatbid[0].bid[0].ttl = 480; + + let expectedResponse = [{ + 'requestId': '263c448586f5a1', + 'cpm': 0.45882675, + 'width': 728, + 'height': 90, + 'creativeId': 'creativelycreatedcreativecreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': decodeURIComponent(``), + 'ttl': 480 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); }); it('handles empty bid response', function () { From 05606c66e1f8eb824c5ccea2e945164c3e0b8b33 Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Thu, 22 Oct 2020 05:07:51 -0400 Subject: [PATCH 0303/1476] PulsePoint Adapter: Fix on multi-format support (#5857) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * APPS-3774 --- modules/pulsepointBidAdapter.js | 4 +-- .../spec/modules/pulsepointBidAdapter_spec.js | 25 ++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 12937dbec9d..005eadaa390 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -165,12 +165,12 @@ function impression(slot) { function banner(slot) { const sizes = parseSizes(slot); const size = adSize(slot, sizes); - return (slot.nativeParams || slot.params.video) ? null : { + return (slot.mediaTypes && slot.mediaTypes.banner) ? { w: size[0], h: size[1], battr: slot.params.battr, format: sizes - }; + } : null; } /** diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index a6f5ff2a0dc..cf81a26eebb 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -19,6 +19,11 @@ describe('PulsePoint Adapter Tests', function () { } }, { placementCode: '/DfpAccount2/slot2', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, bidId: 'bid23456', params: { cp: 'p10000', @@ -72,6 +77,11 @@ describe('PulsePoint Adapter Tests', function () { }]; const additionalParamsConfig = [{ placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, bidId: 'bid12345', params: { cp: 'p10000', @@ -89,6 +99,11 @@ describe('PulsePoint Adapter Tests', function () { const ortbParamsSlotConfig = [{ placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, bidId: 'bid12345', params: { cp: 'p10000', @@ -146,6 +161,11 @@ describe('PulsePoint Adapter Tests', function () { const schainParamsSlotConfig = [{ placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, bidId: 'bid12345', params: { cp: 'p10000', @@ -681,7 +701,10 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.imp[1].banner).to.not.be.null; expect(ortbRequest.imp[1].banner.w).to.equal(728); expect(ortbRequest.imp[1].banner.h).to.equal(90); - expect(ortbRequest.imp[1].banner.format).to.be.null; + expect(ortbRequest.imp[1].banner.format).to.not.be.null; + expect(ortbRequest.imp[1].banner.format).to.have.lengthOf(1); + expect(ortbRequest.imp[1].banner.format[0].w).to.equal(728); + expect(ortbRequest.imp[1].banner.format[0].h).to.equal(90); // adsize on response const ortbResponse = { seatbid: [{ From 0576ee211d64fb6011e0f1de55a3cae86c07db24 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 22 Oct 2020 11:51:38 +0200 Subject: [PATCH 0304/1476] ID5 user id module: migrate publishers to use local storage instead of 1p cookies (#5874) * change storage name * id5 user id module will now prefer localstorage over cookies with a specific name. - for now, the requirement is a warning, but in a future release it will be a strict requirement and the module will not work if it's not configured properly by the publisher - remove code to support legacy endpoint / storage since all publishers using ID5 have upgraded past v3.25.0 - once a publisher is using localstorage, remove any legacy cookies that are not longer needed * add id5 markdown file * update example docs to use html5 and new storage name * add todo * code review updates * update version * doc tweaks * doc tweaks * address PR feedback - fix bug in storage expiration dates - remove unnecessary check --- modules/id5IdSystem.js | 147 +++++++++++++---- modules/id5IdSystem.md | 54 +++++++ modules/userId/userId.md | 10 +- test/spec/modules/id5IdSystem_spec.js | 220 ++++++++++++-------------- 4 files changed, 271 insertions(+), 160 deletions(-) create mode 100644 modules/id5IdSystem.md diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index f8ff50f52a3..9a1dcafc138 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -13,8 +13,13 @@ import { getStorageManager } from '../src/storageManager.js'; const MODULE_NAME = 'id5Id'; const GVLID = 131; -const BASE_NB_COOKIE_NAME = 'id5id.1st'; -const NB_COOKIE_EXP_DAYS = (30 * 24 * 60 * 60 * 1000); // 30 days +const NB_EXP_DAYS = 30; +export const ID5_STORAGE_NAME = 'id5id'; +const LOCAL_STORAGE = 'html5'; + +// 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' ]; const storage = getStorageManager(GVLID, MODULE_NAME); @@ -42,10 +47,7 @@ export const id5IdSubmodule = { let uid; let linkType = 0; - if (value && typeof value.ID5ID === 'string') { - // don't lose our legacy value from cache - uid = value.ID5ID; - } else if (value && typeof value.universal_uid === 'string') { + if (value && typeof value.universal_uid === 'string') { uid = value.universal_uid; linkType = value.link_type || linkType; } else { @@ -71,22 +73,20 @@ export const id5IdSubmodule = { * @returns {IdResponse|undefined} */ getId(config, consentData, cacheIdObj) { - const configParams = (config && config.params) || {}; - if (!hasRequiredParams(configParams)) { + if (!hasRequiredConfig(config)) { return undefined; } + const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; const gdprConsentString = hasGdpr ? consentData.consentString : ''; - const url = `https://id5-sync.com/g/v2/${configParams.partner}.json?gdpr_consent=${gdprConsentString}&gdpr=${hasGdpr}`; + const url = `https://id5-sync.com/g/v2/${config.params.partner}.json?gdpr_consent=${gdprConsentString}&gdpr=${hasGdpr}`; const referer = getRefererInfo(); - const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : ''; - const pubId = (cacheIdObj && cacheIdObj.ID5ID) ? cacheIdObj.ID5ID : ''; // TODO: remove when 1puid isn't needed + const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : getLegacyCookieSignature(); const data = { - 'partner': configParams.partner, - '1puid': pubId, // TODO: remove when 1puid isn't needed - 'nbPage': incrementNb(configParams), + 'partner': config.params.partner, + 'nbPage': incrementNb(config.params.partner), 'o': 'pbjs', - 'pd': configParams.pd || '', + 'pd': config.params.pd || '', 'rf': referer.referer, 's': signature, 'top': referer.reachedTop ? 1 : 0, @@ -101,7 +101,13 @@ export const id5IdSubmodule = { if (response) { try { responseObj = JSON.parse(response); - resetNb(configParams); + resetNb(config.params.partner); + + // 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) { utils.logError(error); } @@ -109,7 +115,7 @@ export const id5IdSubmodule = { callback(responseObj); }, error: error => { - utils.logError(`id5Id: ID fetch encountered an error`, error); + utils.logError(`User ID - ID5 submodule getId fetch encountered an error`, error); callback(); } }; @@ -129,39 +135,112 @@ export const id5IdSubmodule = { * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ extendId(config, cacheIdObj) { - const configParams = (config && config.params) || {}; - incrementNb(configParams); + const partnerId = (config && config.params && config.params.partner) || 0; + incrementNb(partnerId); return cacheIdObj; } }; -function hasRequiredParams(configParams) { - if (!configParams || typeof configParams.partner !== 'number') { +function hasRequiredConfig(config) { + if (!config || !config.params || !config.params.partner || typeof config.params.partner !== 'number') { utils.logError(`User ID - ID5 submodule requires partner to be defined as a number`); return false; } + + if (!config.storage || !config.storage.type || !config.storage.name) { + utils.logError(`User ID - ID5 submodule requires storage to be set`); + return false; + } + + // TODO: in a future release, return false if storage type or name are not set as required + if (config.storage.type !== LOCAL_STORAGE) { + utils.logWarn(`User ID - ID5 submodule recommends storage type to be '${LOCAL_STORAGE}'. In a future release this will become a strict requirement`); + } + // TODO: in a future release, return false if storage type or name are not set as required + if (config.storage.name !== ID5_STORAGE_NAME) { + utils.logWarn(`User ID - ID5 submodule recommends storage name to be '${ID5_STORAGE_NAME}'. In a future release this will become a strict requirement`); + } + return true; } -function nbCookieName(configParams) { - return hasRequiredParams(configParams) ? `${BASE_NB_COOKIE_NAME}_${configParams.partner}_nb` : undefined; + +export function expDaysStr(expDays) { + return (new Date(Date.now() + (1000 * 60 * 60 * 24 * expDays))).toUTCString(); } -function nbCookieExpStr(expDays) { - return (new Date(Date.now() + expDays)).toUTCString(); + +export function nbCacheName(partnerId) { + return `${ID5_STORAGE_NAME}_${partnerId}_nb`; } -function storeNbInCookie(configParams, nb) { - storage.setCookie(nbCookieName(configParams), nb, nbCookieExpStr(NB_COOKIE_EXP_DAYS), 'Lax'); +export function storeNbInCache(partnerId, nb) { + storeInLocalStorage(nbCacheName(partnerId), nb, NB_EXP_DAYS); } -function getNbFromCookie(configParams) { - const cacheNb = storage.getCookie(nbCookieName(configParams)); +export function getNbFromCache(partnerId) { + let cacheNb = getFromLocalStorage(nbCacheName(partnerId)); return (cacheNb) ? parseInt(cacheNb) : 0; } -function incrementNb(configParams) { - const nb = (getNbFromCookie(configParams) + 1); - storeNbInCookie(configParams, nb); +function incrementNb(partnerId) { + const nb = (getNbFromCache(partnerId) + 1); + storeNbInCache(partnerId, nb); return nb; } -function resetNb(configParams) { - storeNbInCookie(configParams, 0); +function resetNb(partnerId) { + storeNbInCache(partnerId, 0); +} + +function getLegacyCookieSignature() { + let legacyStoredValue; + LEGACY_COOKIE_NAMES.forEach(function(cookie) { + if (storage.getCookie(cookie)) { + legacyStoredValue = JSON.parse(storage.getCookie(cookie)) || legacyStoredValue; + } + }); + 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) { + 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 + */ +export function getFromLocalStorage(key) { + const storedValueExp = storage.getDataFromLocalStorage(`${key}_exp`); + // empty string means no expiration set + if (storedValueExp === '') { + return storage.getDataFromLocalStorage(key); + } else if (storedValueExp) { + if ((new Date(storedValueExp)).getTime() - Date.now() > 0) { + return storage.getDataFromLocalStorage(key); + } + } + // if we got here, then we have an expired item or we didn't set an + // expiration initially somehow, so we need to remove the item from the + // local storage + storage.removeDataFromLocalStorage(key); + return null; +} +/** + * Ensure that we always set an expiration in local storage since + * by default it's not required + * @param {string} key + * @param {any} value + * @param {integer} expDays + */ +export function storeInLocalStorage(key, value, expDays) { + storage.setDataInLocalStorage(`${key}_exp`, expDaysStr(expDays)); + storage.setDataInLocalStorage(`${key}`, value); } submodule('userId', id5IdSubmodule); diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md new file mode 100644 index 00000000000..b2369dba07e --- /dev/null +++ b/modules/id5IdSystem.md @@ -0,0 +1,54 @@ +# ID5 Universal ID + +The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://console.id5.io/docs/public/prebid). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. + +## ID5 Universal ID Registration + +The ID5 Universal ID is free to use, but requires a simple registration with ID5. Please visit [id5.io/universal-id](https://id5.io/universal-id) to sign up and request your ID5 Partner Number to get started. + +The ID5 privacy policy is at [https://www.id5.io/platform-privacy-policy](https://www.id5.io/platform-privacy-policy). + +## ID5 Universal ID Configuration + +First, make sure to add the ID5 submodule to your Prebid.js package with: + +``` +gulp build --modules=id5IdSystem,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: "id5Id", + params: { + partner: 173, // change to the Partner Number you received from ID5 + pd: "MT1iNTBjY..." // optional, see table below for a link to how to generate this + }, + storage: { + type: "html5", // "html5" is the required storage type + name: "id5id", // "id5id" is the required storage name + expires: 90, // storage lasts for 90 days + refreshInSeconds: 8*3600 // refresh ID every 8 hours to ensure it's fresh + } + }], + auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules + } +}); +``` + +| 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 Universal ID. | | +| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | +| params.pd | Optional | String | Publisher-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/x/BIAZ) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| 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.13.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). \ No newline at end of file diff --git a/modules/userId/userId.md b/modules/userId/userId.md index c46f67f9a9a..267b3a60cea 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -29,9 +29,9 @@ pbjs.setConfig({ pd: "some-pd-string" // See https://wiki.id5.io/x/BIAZ for details }, storage: { - type: "cookie", - name: "id5id.1st", - expires: 90, // Expiration of cookies in days + type: "html5", // ID5 requires html5 + name: "id5id", + expires: 90, // Expiration in days refreshInSeconds: 8*3600 // User Id cache lifetime in seconds, defaulting to 'expires' }, }, { @@ -144,8 +144,8 @@ pbjs.setConfig({ }, storage: { type: 'html5', - name: 'id5id.1st', - expires: 90, // Expiration of cookies in days + name: 'id5id', + expires: 90, // Expiration in days refreshInSeconds: 8*3600 // User Id cache lifetime in seconds, defaulting to 'expires' }, }, { diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index ac000c1e6dd..bfc41e5f5e8 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -1,9 +1,19 @@ +import { + id5IdSubmodule, + ID5_STORAGE_NAME, + getFromLocalStorage, + storeInLocalStorage, + expDaysStr, + nbCacheName, + getNbFromCache, + storeNbInCache +} from 'modules/id5IdSystem.js'; import { init, requestBidsHook, setSubmoduleRegistry, coreStorage } from 'modules/userId/index.js'; import { config } from 'src/config.js'; -import { id5IdSubmodule } from 'modules/id5IdSystem.js'; import { server } from 'test/mocks/xhr.js'; import events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; +import * as utils from 'src/utils.js'; let expect = require('chai').expect; @@ -11,20 +21,15 @@ 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_PARTNER = 173; - const ID5_ENDPOINT = `https://id5-sync.com/g/v2/${ID5_PARTNER}.json`; - const ID5_COOKIE_NAME = 'id5idcookie'; - const ID5_NB_COOKIE_NAME = `id5id.1st_${ID5_PARTNER}_nb`; - const ID5_EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; + const ID5_TEST_PARTNER_ID = 173; + const ID5_ENDPOINT = `https://id5-sync.com/g/v2/${ID5_TEST_PARTNER_ID}.json`; + const ID5_NB_STORAGE_NAME = nbCacheName(ID5_TEST_PARTNER_ID); const ID5_STORED_ID = 'storedid5id'; const ID5_STORED_SIGNATURE = '123456'; const ID5_STORED_OBJ = { 'universal_uid': ID5_STORED_ID, 'signature': ID5_STORED_SIGNATURE }; - const ID5_LEGACY_STORED_OBJ = { - 'ID5ID': ID5_STORED_ID - } const ID5_RESPONSE_ID = 'newid5id'; const ID5_RESPONSE_SIGNATURE = 'abcdef'; const ID5_JSON_RESPONSE = { @@ -33,11 +38,11 @@ describe('ID5 ID System', function() { 'link_type': 0 }; - function getId5FetchConfig(storageName = ID5_COOKIE_NAME, storageType = 'cookie') { + function getId5FetchConfig(storageName = ID5_STORAGE_NAME, storageType = 'html5') { return { name: ID5_MODULE_NAME, params: { - partner: ID5_PARTNER + partner: ID5_TEST_PARTNER_ID }, storage: { name: storageName, @@ -65,10 +70,10 @@ describe('ID5 ID System', function() { } } function getFetchCookieConfig() { - return getUserSyncConfig([getId5FetchConfig()]); + return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'cookie')]); } function getFetchLocalStorageConfig() { - return getUserSyncConfig([getId5FetchConfig(ID5_COOKIE_NAME, 'html5')]); + return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'html5')]); } function getValueConfig(value) { return getUserSyncConfig([getId5ValueConfig(value)]); @@ -82,6 +87,37 @@ describe('ID5 ID System', 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); + + // valid params, invalid storage + expect(id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ params: { partner: 123 }, storage: {} })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ params: { partner: 123 }, storage: { name: '' } })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ params: { partner: 123 }, storage: { type: '' } })).to.be.eq(undefined); + + // valid storage, invalid params + expect(id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { } })).to.be.eq(undefined); + expect(id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 'abc' } })).to.be.eq(undefined); + }); + + 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 } }); + expect(logWarnStub.calledOnce).to.be.true; + logWarnStub.restore(); + + id5IdSubmodule.getId({ storage: { name: ID5_STORAGE_NAME, type: 'cookie', }, params: { partner: 123 } }); + expect(logWarnStub.calledOnce).to.be.true; + logWarnStub.restore(); + }); + }); + describe('Xhr Requests from getId()', function() { const responseHeader = { 'Content-Type': 'application/json' }; let callbackSpy = sinon.spy(); @@ -93,44 +129,45 @@ describe('ID5 ID System', function() { }); - it('should fail if no partner is provided in the config', function() { - expect(id5IdSubmodule.getId()).to.be.eq(undefined); - expect(id5IdSubmodule.getId({ })).to.be.eq(undefined); - expect(id5IdSubmodule.getId({ params: { } })).to.be.eq(undefined); - }); - - it('should call the ID5 server with 1puid field for legacy storedObj format', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_LEGACY_STORED_OBJ).callback; + 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.eq(''); expect(requestBody.s).to.eq(''); - expect(requestBody.partner).to.eq(ID5_PARTNER); - expect(requestBody['1puid']).to.eq(ID5_STORED_ID); + expect(requestBody.v).to.eq('$prebid.version$'); 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); }); - it('should call the ID5 server with signature field for new storedObj format', function () { + it('should call the ID5 server with empty signature field when no stored object', function () { + let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, undefined).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + let requestBody = JSON.parse(request.requestBody); + expect(requestBody.s).to.eq(''); + + request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + }); + + 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); 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.s).to.eq(ID5_STORED_SIGNATURE); - expect(requestBody.partner).to.eq(ID5_PARTNER); - expect(requestBody['1puid']).to.eq(''); 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); }); it('should call the ID5 server with pd field when pd config is set', function () { @@ -144,15 +181,9 @@ describe('ID5 ID System', function() { 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.s).to.eq(ID5_STORED_SIGNATURE); expect(requestBody.pd).to.eq(pubData); - expect(requestBody['1puid']).to.eq(''); 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); }); it('should call the ID5 server with empty pd field when pd config is not set', function () { @@ -164,52 +195,39 @@ describe('ID5 ID System', function() { 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.pd).to.eq(''); 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); }); - it('should call the ID5 server with nb=1 when no stored value exists', function () { - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); + it('should call the ID5 server with nb=1 when no stored value exists and reset after', function () { + 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(request.url).to.contain(ID5_ENDPOINT); - expect(request.withCredentials).to.be.true; expect(requestBody.nbPage).to.eq(1); 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); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('0'); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); }); - it('should call the ID5 server with incremented nb when stored value exists', function () { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '1', expStr); + it('should call the ID5 server with incremented nb when stored value exists and reset after', function () { + 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(request.url).to.contain(ID5_ENDPOINT); - expect(request.withCredentials).to.be.true; expect(requestBody.nbPage).to.eq(2); 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); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('0'); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); }); }); @@ -218,25 +236,24 @@ describe('ID5 ID System', function() { beforeEach(function() { sinon.stub(events, 'getEvents').returns([]); - coreStorage.setCookie(ID5_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); - coreStorage.setCookie(`${ID5_COOKIE_NAME}_last`, '', ID5_EXPIRED_COOKIE_DATE); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); + coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); + coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); + coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); adUnits = [getAdUnitMock()]; }); afterEach(function() { events.getEvents.restore(); - coreStorage.setCookie(ID5_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); - coreStorage.setCookie(`${ID5_COOKIE_NAME}_last`, '', ID5_EXPIRED_COOKIE_DATE); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); + coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); + coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); + coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); }); - it('should add stored ID from cookie to bids', function (done) { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_COOKIE_NAME, JSON.stringify(ID5_STORED_OBJ), expStr); + it('should add stored ID from cache to bids', function (done) { + storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); setSubmoduleRegistry([id5IdSubmodule]); init(config); - config.setConfig(getFetchCookieConfig()); + config.setConfig(getFetchLocalStorageConfig()); requestBidsHook(function () { adUnits.forEach(unit => { @@ -276,43 +293,40 @@ describe('ID5 ID System', function() { }, { adUnits }); }); - it('should set nb=1 in cookie when no stored value exists', function () { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_COOKIE_NAME, JSON.stringify(ID5_STORED_OBJ), expStr); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '', ID5_EXPIRED_COOKIE_DATE); + it('should set nb=1 in cache when no stored nb value exists and cached ID', function () { + storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); setSubmoduleRegistry([id5IdSubmodule]); init(config); - config.setConfig(getFetchCookieConfig()); + config.setConfig(getFetchLocalStorageConfig()); let innerAdUnits; requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('1'); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(1); }); - it('should increment nb in cookie when stored value exists', function () { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_COOKIE_NAME, JSON.stringify(ID5_STORED_OBJ), expStr); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '1', expStr); + it('should increment nb in cache when stored nb value exists and cached ID', function () { + storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + storeNbInCache(ID5_TEST_PARTNER_ID, 1); setSubmoduleRegistry([id5IdSubmodule]); init(config); - config.setConfig(getFetchCookieConfig()); + config.setConfig(getFetchLocalStorageConfig()); let innerAdUnits; requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('2'); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); }); it('should call ID5 servers with signature and incremented nb post auction if refresh needed', function () { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_COOKIE_NAME, JSON.stringify(ID5_STORED_OBJ), expStr); - coreStorage.setCookie(`${ID5_COOKIE_NAME}_last`, (new Date(Date.now() - 50000).toUTCString()), expStr); - coreStorage.setCookie(ID5_NB_COOKIE_NAME, '1', expStr); + storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + storeInLocalStorage(`${ID5_STORAGE_NAME}_last`, expDaysStr(-1), 1); + storeNbInCache(ID5_TEST_PARTNER_ID, 1); - let id5Config = getFetchCookieConfig(); + let id5Config = getFetchLocalStorageConfig(); id5Config.userSync.userIds[0].storage.refreshInSeconds = 2; setSubmoduleRegistry([id5IdSubmodule]); @@ -322,7 +336,7 @@ describe('ID5 ID System', function() { let innerAdUnits; requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('2'); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); expect(server.requests).to.be.empty; events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); @@ -336,41 +350,8 @@ describe('ID5 ID System', function() { const responseHeader = { 'Content-Type': 'application/json' }; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(coreStorage.getCookie(ID5_COOKIE_NAME)).to.be.eq(JSON.stringify(ID5_JSON_RESPONSE)); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('0'); - }); - - it('should call ID5 servers with 1puid and nb=1 post auction if refresh needed for legacy stored object', function () { - let expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(ID5_COOKIE_NAME, JSON.stringify(ID5_LEGACY_STORED_OBJ), expStr); - coreStorage.setCookie(`${ID5_COOKIE_NAME}_last`, (new Date(Date.now() - 50000).toUTCString()), expStr); - - let id5Config = getFetchCookieConfig(); - id5Config.userSync.userIds[0].storage.refreshInSeconds = 2; - - setSubmoduleRegistry([id5IdSubmodule]); - init(config); - config.setConfig(id5Config); - - let innerAdUnits; - requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('1'); - - expect(server.requests).to.be.empty; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(request.url).to.contain(ID5_ENDPOINT); - expect(requestBody['1puid']).to.eq(ID5_STORED_ID); - expect(requestBody.nbPage).to.eq(1); - - const responseHeader = { 'Content-Type': 'application/json' }; - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - - expect(coreStorage.getCookie(ID5_COOKIE_NAME)).to.be.eq(JSON.stringify(ID5_JSON_RESPONSE)); - expect(coreStorage.getCookie(ID5_NB_COOKIE_NAME)).to.be.eq('0'); + expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).to.be.eq(JSON.stringify(ID5_JSON_RESPONSE)); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); }); }); @@ -380,9 +361,6 @@ describe('ID5 ID System', function() { it('should properly decode from a stored object', function() { expect(id5IdSubmodule.decode(ID5_STORED_OBJ)).to.deep.equal(expectedDecodedObject); }); - it('should properly decode from a legacy stored object', function() { - expect(id5IdSubmodule.decode(ID5_LEGACY_STORED_OBJ)).to.deep.equal(expectedDecodedObject); - }); it('should return undefined if passed a string', function() { expect(id5IdSubmodule.decode('somestring')).to.eq(undefined); }); From b2ecc7eae50b91818b974d417fff614c5de94321 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 22 Oct 2020 22:15:35 +0200 Subject: [PATCH 0305/1476] add us_privacy to id5 id module (#5858) --- modules/id5IdSystem.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 9a1dcafc138..5a1fc69a758 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -10,6 +10,7 @@ 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; @@ -79,7 +80,8 @@ export const id5IdSubmodule = { const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; const gdprConsentString = hasGdpr ? consentData.consentString : ''; - const url = `https://id5-sync.com/g/v2/${config.params.partner}.json?gdpr_consent=${gdprConsentString}&gdpr=${hasGdpr}`; + const usp = uspDataHandler.getConsentData() || ''; + const url = `https://id5-sync.com/g/v2/${config.params.partner}.json?gdpr_consent=${gdprConsentString}&gdpr=${hasGdpr}&us_privacy=${usp}`; const referer = getRefererInfo(); const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : getLegacyCookieSignature(); const data = { From 081a9f231cedbfd6cde529f0c8c556913e51258e Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Thu, 22 Oct 2020 18:39:35 -0400 Subject: [PATCH 0306/1476] Rubicon Bid Adapter - Interpret response adds new meta values (#5864) --- modules/rubiconBidAdapter.js | 15 ++++++++++++++- test/spec/modules/rubiconBidAdapter_spec.js | 11 ++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 069a7e7ead5..5d631d2b08e 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -685,6 +685,14 @@ export const spec = { bidObject.dealId = bid.dealid; } + if (bid.adomain) { + utils.deepSetValue(bidObject, 'meta.advertiserDomains', Array.isArray(bid.adomain) ? bid.adomain : [bid.adomain]); + } + + if (utils.deepAccess(bid, 'ext.bidder.rp.advid')) { + utils.deepSetValue(bidObject, 'meta.advertiserId', bid.ext.bidder.rp.advid); + } + let serverResponseTimeMs = utils.deepAccess(responseObj, 'ext.responsetimemillis.rubicon'); if (bidRequest && serverResponseTimeMs) { bidRequest.serverResponseTimeMs = serverResponseTimeMs; @@ -692,6 +700,7 @@ export const spec = { if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { bidObject.mediaType = VIDEO; + utils.deepSetValue(bidObject, 'meta.mediaType', VIDEO); const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting'); // If ext.prebid.targeting exists, add it as a property value named 'adserverTargeting' @@ -757,7 +766,7 @@ export const spec = { advertiserId: ad.advertiser, networkId: ad.network }, meta: { - advertiserId: ad.advertiser, networkId: ad.network + advertiserId: ad.advertiser, networkId: ad.network, mediaType: BANNER } }; @@ -765,6 +774,10 @@ export const spec = { bid.mediaType = ad.creative_type; } + if (ad.adomain) { + bid.meta.advertiserDomains = Array.isArray(ad.adomain) ? ad.adomain : [ad.adomain]; + } + if (ad.creative_type === VIDEO) { bid.width = associatedBidRequest.params.video.playerWidth; bid.height = associatedBidRequest.params.video.playerHeight; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index a38743d634a..920a2fb2502 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2127,6 +2127,7 @@ describe('the rubicon adapter', function () { 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', 'size_id': '15', 'ad_id': '6', + 'adomain': ['test.com'], 'advertiser': 7, 'network': 8, 'creative_id': 'crid-9', @@ -2148,6 +2149,7 @@ describe('the rubicon adapter', function () { 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', 'size_id': '43', 'ad_id': '7', + 'adomain': ['test.com'], 'advertiser': 7, 'network': 8, 'creative_id': 'crid-9', @@ -2182,6 +2184,8 @@ describe('the rubicon adapter', function () { expect(bids[0].rubicon.networkId).to.equal(8); expect(bids[0].creativeId).to.equal('crid-9'); expect(bids[0].currency).to.equal('USD'); + expect(bids[0].meta.mediaType).to.equal('banner'); + expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); expect(bids[0].ad).to.contain(`alert('foo')`) .and.to.contain(``) .and.to.contain(`
`); @@ -2715,13 +2719,15 @@ describe('the rubicon adapter', function () { bid: [{ id: '0', impid: 'instream_video1', + adomain: ['test.com'], price: 2, crid: '4259970', ext: { bidder: { rp: { mime: 'application/javascript', - size_id: 201 + size_id: 201, + advid: 12345 } }, prebid: { @@ -2750,6 +2756,9 @@ describe('the rubicon adapter', function () { expect(bids[0].netRevenue).to.equal(true); expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); expect(bids[0].mediaType).to.equal('video'); + expect(bids[0].meta.mediaType).to.equal('video'); + expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); + expect(bids[0].meta.advertiserId).to.equal(12345); expect(bids[0].bidderCode).to.equal('rubicon'); expect(bids[0].currency).to.equal('USD'); expect(bids[0].width).to.equal(640); From 22677c17f628170aad2dbf0979a0d3f92727eb28 Mon Sep 17 00:00:00 2001 From: Scott Laufer Date: Thu, 22 Oct 2020 22:47:02 -0400 Subject: [PATCH 0307/1476] [Synacormedia] Config override for site.domain property (#5885) * CAP-1992 - use get config for site.domain --- modules/synacormediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index e0d017a6c51..6608107c93f 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -37,7 +37,7 @@ export const spec = { const openRtbBidRequest = { id: bidderRequest.auctionId, site: { - domain: location.hostname, + domain: config.getConfig('publisherDomain') || location.hostname, page: refererInfo.referer, ref: document.referrer }, From da480f207e8d767017d71bab1bfa607279421b3f Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Fri, 23 Oct 2020 04:01:02 +0100 Subject: [PATCH 0308/1476] AOL Adapter: User ID Support (#5886) * Added support for passing VMUID to SSP endpoints * Remove 'only' command * Do not create user.ext object unless required * Add support for passing Liveramp envelope to VM SSP * WIP * Updated tests * Remove trailing comma Co-authored-by: slimkrazy --- modules/aolBidAdapter.js | 34 +++++++++++++++ test/spec/modules/aolBidAdapter_spec.js | 57 ++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 1f43231e495..93c56846639 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -30,6 +30,17 @@ const SYNC_TYPES = { } }; +const SUPPORTED_USER_ID_SOURCES = [ + 'adserver.org', + 'criteo.com', + 'id5-sync.com', + 'intentiq.com', + 'liveintent.com', + 'quantcast.com', + 'verizonmedia.com', + 'liveramp.com' +]; + const pubapiTemplate = template`${'host'}/pubapi/3.0/${'network'}/${'placement'}/${'pageid'}/${'sizeid'}/ADTECH;v=2;cmd=bid;cors=yes;alias=${'alias'};misc=${'misc'};${'dynamicParams'}`; const nexageBaseApiTemplate = template`${'host'}/bidRequest?`; const nexageGetApiTemplate = template`dcn=${'dcn'}&pos=${'pos'}&cmd=bid${'dynamicParams'}`; @@ -103,6 +114,12 @@ function resolveEndpointCode(bid) { } } +function getSupportedEids(bid) { + return bid.userIdAsEids.filter(eid => { + return SUPPORTED_USER_ID_SOURCES.includes(eid.source) + }); +} + export const spec = { code: AOL_BIDDERS_CODES.AOL, gvlid: 25, @@ -226,6 +243,13 @@ export const spec = { }, buildOneMobileGetUrl(bid, consentData) { let { dcn, pos, ext } = bid.params; + if (typeof bid.userId === 'object') { + ext = ext || {}; + let eids = getSupportedEids(bid); + eids.forEach(eid => { + ext['eid' + eid.source] = eid.uids[0].id; + }); + } let nexageApi = this.buildOneMobileBaseUrl(bid); if (dcn && pos) { let dynamicParams = this.formatOneMobileDynamicParams(ext, consentData); @@ -292,6 +316,16 @@ export const spec = { utils.deepSetValue(openRtbObject, 'regs.ext.us_privacy', consentData.uspConsent); } + if (typeof bid.userId === 'object') { + openRtbObject.user = openRtbObject.user || {}; + openRtbObject.user.ext = openRtbObject.user.ext || {}; + + let eids = getSupportedEids(bid); + if (eids.length > 0) { + openRtbObject.user.ext.eids = eids + } + } + return openRtbObject; }, isEUConsentRequired(consentData) { diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index dd10a57bbfe..2276e32ece7 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import * as utils from 'src/utils.js'; import {spec} from 'modules/aolBidAdapter.js'; -import {config} from 'src/config.js'; +import {createEidsArray} from '../../../modules/userId/eids.js'; const DEFAULT_AD_CONTENT = ''; @@ -80,6 +80,33 @@ describe('AolAdapter', function () { const NEXAGE_URL = 'https://c2shb.ssp.yahoo.com/bidRequest?'; const ONE_DISPLAY_TTL = 60; const ONE_MOBILE_TTL = 3600; + const SUPPORTED_USER_ID_SOURCES = { + 'adserver.org': '100', + 'criteo.com': '200', + 'id5-sync.com': '300', + 'intentiq.com': '400', + 'liveintent.com': '500', + 'quantcast.com': '600', + 'verizonmedia.com': '700', + 'liveramp.com': '800' + }; + + const USER_ID_DATA = { + criteoId: SUPPORTED_USER_ID_SOURCES['criteo.com'], + vmuid: SUPPORTED_USER_ID_SOURCES['verizonmedia.com'], + idl_env: SUPPORTED_USER_ID_SOURCES['liveramp.com'], + lipb: { + lipbid: SUPPORTED_USER_ID_SOURCES['liveintent.com'], + segments: ['100', '200'] + }, + tdid: SUPPORTED_USER_ID_SOURCES['adserver.org'], + id5id: { + uid: SUPPORTED_USER_ID_SOURCES['id5-sync.com'], + ext: {foo: 'bar'} + }, + intentIqId: SUPPORTED_USER_ID_SOURCES['intentiq.com'], + quantcastId: SUPPORTED_USER_ID_SOURCES['quantcast.com'] + }; function createCustomBidRequest({bids, params} = {}) { var bidderRequest = getDefaultBidRequest(); @@ -463,6 +490,18 @@ describe('AolAdapter', function () { '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); }); + for (const [source, idValue] of Object.entries(SUPPORTED_USER_ID_SOURCES)) { + it(`should set the user ID query param for ${source}`, function () { + let bidRequest = createCustomBidRequest({ + params: getNexageGetBidParams() + }); + bidRequest.bids[0].userId = {}; + bidRequest.bids[0].userIdAsEids = createEidsArray(USER_ID_DATA); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(`&eid${source}=${encodeURIComponent(idValue)}`); + }); + } + it('should return request object for One Mobile POST endpoint when POST configuration is present', function () { let bidConfig = getNexagePostBidParams(); let bidRequest = createCustomBidRequest({ @@ -581,6 +620,22 @@ describe('AolAdapter', function () { } }); }); + + it('returns the bid object with eid array populated with PB set eids', () => { + let userIdBid = Object.assign({ + userId: {} + }, bid); + userIdBid.userIdAsEids = createEidsArray(USER_ID_DATA); + expect(spec.buildOpenRtbRequestData(userIdBid)).to.deep.equal({ + id: 'bid-id', + imp: [], + user: { + ext: { + eids: userIdBid.userIdAsEids + } + } + }); + }); }); describe('getUserSyncs()', function () { From fb958509e90c61c5c8861ff822f7702a847088ed Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Fri, 23 Oct 2020 11:02:26 +0200 Subject: [PATCH 0309/1476] the code to require local storage will be released in 4.14.0 not 4.13.0 (#5889) --- modules/id5IdSystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index b2369dba07e..80ba451b235 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -51,4 +51,4 @@ pbjs.setConfig({ | 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.13.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). \ No newline at end of file +**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). \ No newline at end of file From 63604921c6cad4f85cac5611b61a694147688dd2 Mon Sep 17 00:00:00 2001 From: Ivan J Date: Tue, 27 Oct 2020 13:13:20 +0100 Subject: [PATCH 0310/1476] fix: schain complete can be 0 (#5902) --- modules/yieldlabBidAdapter.js | 2 +- test/spec/modules/yieldlabBidAdapter_spec.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index b252c0db2ee..5465a10a884 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -240,7 +240,7 @@ function createTargetingString (obj) { */ function createSchainString (schain) { const ver = schain.ver || '' - const complete = schain.complete || '' + 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(',')}` diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 90fa26fa823..cd2c46a5664 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -138,6 +138,12 @@ describe('yieldlabBidAdapter', function () { } }) + it('passes unencoded schain string to bid request when complete == 0', function () { + REQUEST.schain.complete = 0; + const request = spec.buildRequests([REQUEST]) + 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', function () { expect(refererRequest.url).to.include('pubref=https%3A%2F%2Fwww.yieldlab.de%2Ftest%3Fwith%3Dquerystring') }) From 894c1d7565c87972b1d95857963c8007c27fdfb9 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Tue, 27 Oct 2020 09:37:39 -0400 Subject: [PATCH 0311/1476] [AD-1020] JWPlayer RTD: Obtain targeting params from FPD (#5892) * reads jwTargeting from fpd * refactors param extraction * updates documentation * mentions support of config fpd * reduces auction delay examples Co-authored-by: karimJWP --- .../gpt/jwplayerRtdProvider_example.html | 17 +- modules/jwplayerRtdProvider.js | 14 +- modules/jwplayerRtdProvider.md | 35 ++--- test/spec/modules/jwplayerRtdProvider_spec.js | 146 +++++++++++++++--- 4 files changed, 166 insertions(+), 46 deletions(-) diff --git a/integrationExamples/gpt/jwplayerRtdProvider_example.html b/integrationExamples/gpt/jwplayerRtdProvider_example.html index e47f3ca45ec..75eb85a2d8c 100644 --- a/integrationExamples/gpt/jwplayerRtdProvider_example.html +++ b/integrationExamples/gpt/jwplayerRtdProvider_example.html @@ -11,11 +11,18 @@ var adUnits = [{ code: 'div-gpt-ad-1460505748561-0', - jwTargeting: { - // Note: the following Ids are placeholders and should be replaced with your Ids. - playerID: '123', - mediaID: 'abc' + fpd: { + context: { + data: { + jwTargeting: { + // Note: the following Ids are placeholders and should be replaced with your Ids. + playerID: '123', + mediaID: 'abc' + } + }, + } }, + mediaTypes: { banner: { sizes: [[300, 250], [300,600]], @@ -45,7 +52,7 @@ pbjs.que.push(function() { pbjs.setConfig({ realTimeData: { - auctionDelay: 5000, + auctionDelay: 100, dataProviders: [{ name: "jwplayer", waitForIt: true, diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 2262d39ff3a..17cb978aea3 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -144,6 +144,7 @@ function enrichBidRequest(bidReqConfig, onDone) { * @param {function} onDone */ export function enrichAdUnits(adUnits) { + const fpdFallback = config.getConfig('fpd.context.data.jwTargeting'); adUnits.forEach(adUnit => { const onVatResponse = function (vat) { if (!vat) { @@ -153,12 +154,21 @@ export function enrichAdUnits(adUnits) { addTargetingToBids(adUnit.bids, targeting); }; - loadVat(adUnit.jwTargeting, onVatResponse); + const jwTargeting = extractPublisherParams(adUnit, fpdFallback); + loadVat(jwTargeting, onVatResponse); }); } +export function extractPublisherParams(adUnit, fallback) { + let adUnitTargeting; + try { + adUnitTargeting = adUnit.fpd.context.data.jwTargeting; + } catch (e) {} + return Object.assign({}, fallback, adUnitTargeting); +} + function loadVat(params, onCompletion) { - if (!params) { + if (!params || !Object.keys(params).length) { return; } diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index 83e1a4d7772..3c83b6f521c 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -25,16 +25,22 @@ pbjs.setConfig({ } }); ``` -Lastly, include the content's media ID and/or the player's ID in the matching AdUnit: +Lastly, include the content's media ID and/or the player's ID in the matching AdUnit's `fpd.context.data`: ```javascript const adUnit = { code: '/19968336/prebid_native_example_1', ... - jwTargeting: { - waitForIt: true, - playerID: 'abcd', - mediaID: '1234' + fpd: { + context: { + data: { + jwTargeting: { + // Note: the following Ids are placeholders and should be replaced with your Ids. + playerID: 'abcd', + mediaID: '1234' + } + } + } } }; @@ -45,34 +51,27 @@ pbjs.que.push(function() { }); }); ``` + +**Note**: You may also include `jwTargeting` information in the prebid config's `fpd.context.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. + ##Prefetching -In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var: +In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var and set `waitForIt` to `true`: ```javascript const jwplayerDataProvider = { name: "jwplayer", + waitForIt: true, params: { mediaIDs: ['abc', 'def', 'ghi', 'jkl'] } }; ``` -To ensure that the prefetched targeting information is added to your bid, we strongly suggest setting -`jwTargeting.waitForIt` to `true`. If the prefetch is still in progress at the time of the bid request, the auction will -be delayed until the targeting information specific to the requested adUnits has been obtained. - -```javascript -jwTargeting: { - waitForIt: true, - ... -} -``` - You must also set a value to `auctionDelay` in the config's `realTimeData` object ```javascript realTimeData = { - auctionDelay: 1000, + auctionDelay: 100, ... }; ``` diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index 6e301e2a5a6..6e0fd8eb8d7 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -1,5 +1,5 @@ -import { fetchTargetingForMediaId, enrichBidRequest, - getVatFromCache, formatTargetingResponse, getVatFromPlayer, enrichAdUnits, +import { fetchTargetingForMediaId, getVatFromCache, extractPublisherParams, + formatTargetingResponse, getVatFromPlayer, enrichAdUnits, fetchTargetingInformation, jwplayerSubmodule } from 'modules/jwplayerRtdProvider.js'; import { server } from 'test/mocks/xhr.js'; @@ -229,9 +229,15 @@ describe('jwplayerRtdProvider', function() { const bid = {}; const adUnit = { - jwTargeting: { - mediaID: mediaIdWithSegment, - playerID: validPlayerID + fpd: { + context: { + data: { + jwTargeting: { + mediaID: mediaIdWithSegment, + playerID: validPlayerID + } + } + } }, bids: [ bid @@ -292,8 +298,14 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - jwTargeting: { - mediaID: testIdForSuccess + fpd: { + context: { + data: { + jwTargeting: { + mediaID: testIdForSuccess + } + } + } }, bids }; @@ -333,8 +345,14 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - jwTargeting: { - mediaID: testIdForSuccess + fpd: { + context: { + data: { + jwTargeting: { + mediaID: testIdForSuccess + } + } + } }, bids }; @@ -374,8 +392,14 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - jwTargeting: { - mediaID: testIdForFailure + fpd: { + context: { + data: { + jwTargeting: { + mediaID: testIdForFailure + } + } + } }, bids }; @@ -388,6 +412,50 @@ describe('jwplayerRtdProvider', function() { }); }); + describe(' Extract Publisher Params', function () { + it('should default to config', function () { + const config = { mediaID: 'test' }; + + const adUnit1 = { fpd: { context: {} } }; + const targeting1 = extractPublisherParams(adUnit1, config); + expect(targeting1).to.deep.equal(config); + + const adUnit2 = { fpd: { context: { data: { jwTargeting: {} } } } }; + const targeting2 = extractPublisherParams(adUnit2, config); + expect(targeting2).to.deep.equal(config); + + const targeting3 = extractPublisherParams(null, config); + expect(targeting3).to.deep.equal(config); + }); + + it('should prioritize adUnit properties ', function () { + const expectedMediaID = 'test_media_id'; + const expectedPlayerID = 'test_player_id'; + const config = { playerID: 'bad_id', mediaID: 'bad_id' }; + + const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID, playerID: expectedPlayerID } } } } }; + const targeting = extractPublisherParams(adUnit, config); + expect(targeting).to.have.property('mediaID', expectedMediaID); + expect(targeting).to.have.property('playerID', expectedPlayerID); + }); + + it('should use config properties as fallbacks', function () { + const expectedMediaID = 'test_media_id'; + const expectedPlayerID = 'test_player_id'; + const config = { playerID: expectedPlayerID, mediaID: 'bad_id' }; + + const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID } } } } }; + const targeting = extractPublisherParams(adUnit, config); + expect(targeting).to.have.property('mediaID', expectedMediaID); + expect(targeting).to.have.property('playerID', expectedPlayerID); + }); + + it('should return empty object when Publisher Params are absent', function () { + const targeting = extractPublisherParams(null, null); + expect(targeting).to.deep.equal({}); + }) + }); + describe('jwplayerSubmodule', function () { it('successfully instantiates', function () { expect(jwplayerSubmodule.init()).to.equal(true); @@ -404,16 +472,28 @@ describe('jwplayerRtdProvider', function() { bidReqConfig = { adUnits: [ { - jwTargeting: { - mediaID: validMediaIDs[0] + fpd: { + context: { + data: { + jwTargeting: { + mediaID: validMediaIDs[0] + } + } + } }, bids: [ {}, {} ] }, { - jwTargeting: { - mediaID: validMediaIDs[1] + fpd: { + context: { + data: { + jwTargeting: { + mediaID: validMediaIDs[1] + } + } + } }, bids: [ {}, {} @@ -473,8 +553,14 @@ describe('jwplayerRtdProvider', function() { it('sets targeting data in proper structure', function () { const bid = {}; const adUnitWithMediaId = { - jwTargeting: { - mediaID: testIdForSuccess + fpd: { + context: { + data: { + jwTargeting: { + mediaID: testIdForSuccess + } + } + } }, bids: [ bid @@ -499,12 +585,18 @@ describe('jwplayerRtdProvider', function() { const adUnitCode = 'test_ad_unit'; const bid = {}; const adUnit = { - jwTargeting: { - mediaID: testIdForFailure + fpd: { + context: { + data: { + jwTargeting: { + mediaID: testIdForFailure + } + } + } }, bids: [ bid ] }; - const expectedContentId = 'jw_' + adUnit.jwTargeting.mediaID; + const expectedContentId = 'jw_' + adUnit.fpd.context.data.jwTargeting.mediaID; const expectedTargeting = { content: { id: expectedContentId @@ -522,6 +614,7 @@ describe('jwplayerRtdProvider', function() { const adUnitCode = 'test_ad_unit'; const bid1 = {}; const bid2 = {}; + const bid3 = {}; const adUnitWithMediaId = { code: adUnitCode, mediaID: testIdForSuccess, @@ -532,10 +625,21 @@ describe('jwplayerRtdProvider', function() { bids: [ bid2 ] }; - jwplayerSubmodule.getBidRequestData({ adUnits: [adUnitWithMediaId, adUnitEmpty] }, bidRequestSpy); + const adUnitEmptyfpd = { + code: 'test_ad_unit_empty_fpd', + fpd: { + context: { + id: 'sthg' + } + }, + bids: [ bid3 ] + }; + + jwplayerSubmodule.getBidRequestData({ adUnits: [adUnitWithMediaId, adUnitEmpty, adUnitEmptyfpd] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; expect(bid1).to.not.have.property('jwTargeting'); expect(bid2).to.not.have.property('jwTargeting'); + expect(bid3).to.not.have.property('jwTargeting'); }); }); }); From 6f531d5602ecee8381ffd2d568c3e553a10cca76 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 27 Oct 2020 09:07:46 -0700 Subject: [PATCH 0312/1476] Add support for Publisher Common ID Module (#5871) - New user id value to be sent to STR Ad Server as `pubcid` of the bid request object Story: [#175125639](https://www.pivotaltracker.com/story/show/175125639) --- modules/sharethroughBidAdapter.js | 6 + .../modules/sharethroughBidAdapter_spec.js | 127 ++++++++++-------- 2 files changed, 79 insertions(+), 54 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 0d183be05df..7df161db713 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -50,6 +50,12 @@ export const sharethroughAdapterSpec = { query.ttduid = bidRequest.userId.tdid; } + if (bidRequest.userId && bidRequest.userId.pubcid) { + query.pubcid = bidRequest.userId.pubcid; + } else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { + query.pubcid = bidRequest.crumbs.pubcid; + } + if (bidRequest.schain) { query.schain = JSON.stringify(bidRequest.schain); } diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 92f9fd11eeb..d45d1e977e6 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -12,7 +12,13 @@ const bidRequests = [ params: { pkey: 'aaaa1111' }, - userId: { tdid: 'fake-tdid' } + userId: { + tdid: 'fake-tdid', + pubcid: 'fake-pubcid' + }, + crumbs: { + pubcid: 'fake-pubcid-in-crumbs-obj' + } }, { bidder: 'sharethrough', @@ -33,8 +39,8 @@ const bidRequests = [ pkey: 'cccc3333', iframe: true, iframeSize: [500, 500] - }, - }, + } + } ]; const prebidRequests = [ @@ -98,7 +104,7 @@ const prebidRequests = [ skipIframeBusting: false, sizes: [[300, 250], [300, 300], [250, 250], [600, 50]] } - }, + } ]; const bidderResponse = { @@ -121,12 +127,12 @@ const bidderResponse = { }; const setUserAgent = (uaString) => { - window.navigator['__defineGetter__']('userAgent', function () { + window.navigator['__defineGetter__']('userAgent', function() { return uaString; }); }; -describe('sharethrough internal spec', function () { +describe('sharethrough internal spec', function() { let windowSpy, windowTopSpy; beforeEach(function() { @@ -141,7 +147,7 @@ describe('sharethrough internal spec', function () { window.top.STR = undefined; }); - describe('we cannot access top level document', function () { + describe('we cannot access top level document', function() { beforeEach(function() { window.lockedInFrame = true; }); @@ -150,12 +156,12 @@ describe('sharethrough internal spec', function () { window.lockedInFrame = false; }); - it('appends sfp.js to the safeframe', function () { + it('appends sfp.js to the safeframe', function() { sharethroughInternal.handleIframe(); expect(windowSpy.calledOnce).to.be.true; }); - it('does not append anything if sfp.js is already loaded in the safeframe', function () { + it('does not append anything if sfp.js is already loaded in the safeframe', function() { window.STR = { Tag: true }; sharethroughInternal.handleIframe(); expect(windowSpy.notCalled).to.be.true; @@ -163,14 +169,14 @@ describe('sharethrough internal spec', function () { }); }); - describe('we are able to bust out of the iframe', function () { - it('appends sfp.js to window.top', function () { + describe('we are able to bust out of the iframe', function() { + it('appends sfp.js to window.top', function() { sharethroughInternal.handleIframe(); expect(windowSpy.calledOnce).to.be.true; expect(windowTopSpy.calledOnce).to.be.true; }); - it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function () { + it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function() { window.top.STR = { Tag: true }; sharethroughInternal.handleIframe(); expect(windowSpy.calledOnce).to.be.true; @@ -179,15 +185,15 @@ describe('sharethrough internal spec', function () { }); }); -describe('sharethrough adapter spec', function () { - describe('.code', function () { - it('should return a bidder code of sharethrough', function () { +describe('sharethrough adapter spec', function() { + describe('.code', function() { + it('should return a bidder code of sharethrough', function() { expect(spec.code).to.eql('sharethrough'); }); }); - describe('.isBidRequestValid', function () { - it('should return false if req has no pkey', function () { + describe('.isBidRequestValid', function() { + it('should return false if req has no pkey', function() { const invalidBidRequest = { bidder: 'sharethrough', params: { @@ -197,7 +203,7 @@ describe('sharethrough adapter spec', function () { expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); - it('should return false if req has wrong bidder code', function () { + it('should return false if req has wrong bidder code', function() { const invalidBidRequest = { bidder: 'notSharethrough', params: { @@ -207,14 +213,14 @@ describe('sharethrough adapter spec', function () { expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); - it('should return true if req is correct', function () { + it('should return true if req is correct', function() { expect(spec.isBidRequestValid(bidRequests[0])).to.eq(true); expect(spec.isBidRequestValid(bidRequests[1])).to.eq(true); - }) + }); }); - describe('.buildRequests', function () { - it('should return an array of requests', function () { + describe('.buildRequests', function() { + it('should return an array of requests', function() { const builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1'); @@ -222,7 +228,7 @@ describe('sharethrough adapter spec', function () { expect(builtBidRequests[0].method).to.eq('GET'); }); - it('should set the instant_play_capable parameter correctly based on browser userAgent string', function () { + it('should set the instant_play_capable parameter correctly based on browser userAgent string', function() { setUserAgent('Android Chrome/60'); let builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0].data.instant_play_capable).to.be.true; @@ -252,31 +258,31 @@ describe('sharethrough adapter spec', function () { const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('http:'); const bidRequest = spec.buildRequests(bidRequests, null)[0]; expect(bidRequest.data.secure).to.be.false; - stub.restore() + stub.restore(); }); it('should set the secure parameter to true when the protocol is https', function() { const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https:'); const bidRequest = spec.buildRequests(bidRequests, null)[0]; expect(bidRequest.data.secure).to.be.true; - stub.restore() + stub.restore(); }); it('should set the secure parameter to true when the protocol is neither http or https', function() { const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('about:'); const bidRequest = spec.buildRequests(bidRequests, null)[0]; expect(bidRequest.data.secure).to.be.true; - stub.restore() + stub.restore(); }); - it('should add ccpa parameter if uspConsent is present', function () { + it('should add ccpa parameter if uspConsent is present', function() { const uspConsent = '1YNN'; const bidderRequest = { uspConsent: uspConsent }; const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(bidRequest.data.us_privacy).to.eq(uspConsent); }); - it('should add consent parameters if gdprConsent is present', function () { + it('should add consent parameters if gdprConsent is present', function() { const gdprConsent = { consentString: 'consent_string123', gdprApplies: true }; const bidderRequest = { gdprConsent: gdprConsent }; const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; @@ -284,19 +290,32 @@ describe('sharethrough adapter spec', function () { expect(bidRequest.data.consent_string).to.eq('consent_string123'); }); - it('should handle gdprConsent is present but values are undefined case', function () { + it('should handle gdprConsent is present but values are undefined case', function() { const gdprConsent = { consent_string: undefined, gdprApplies: undefined }; const bidderRequest = { gdprConsent: gdprConsent }; const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(bidRequest.data).to.not.include.any.keys('consent_string') + expect(bidRequest.data).to.not.include.any.keys('consent_string'); }); - it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function () { + it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function() { const bidRequest = spec.buildRequests(bidRequests)[0]; expect(bidRequest.data.ttduid).to.eq('fake-tdid'); }); - it('should add Sharethrough specific parameters', function () { + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + + ' userId object of the bidrequest', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); + }); + + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + + ' crumbs object of the bidrequest', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + delete bidRequest.userId; + expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); + }); + + it('should add Sharethrough specific parameters', function() { const builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0]).to.deep.include({ strData: { @@ -346,8 +365,8 @@ describe('sharethrough adapter spec', function () { }); }); - describe('.interpretResponse', function () { - it('returns a correctly parsed out response', function () { + describe('.interpretResponse', function() { + it('returns a correctly parsed out response', function() { expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.include( { width: 1, @@ -357,11 +376,11 @@ describe('sharethrough adapter spec', function () { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360, + ttl: 360 }); }); - it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function () { + it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function() { expect(spec.interpretResponse(bidderResponse, prebidRequests[1])[0]).to.include( { width: 300, @@ -371,11 +390,11 @@ describe('sharethrough adapter spec', function () { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360, + ttl: 360 }); }); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function () { + it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function() { expect(spec.interpretResponse(bidderResponse, prebidRequests[2])[0]).to.include( { width: 500, @@ -385,11 +404,11 @@ describe('sharethrough adapter spec', function () { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360, + ttl: 360 }); }); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function () { + it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function() { expect(spec.interpretResponse(bidderResponse, prebidRequests[3])[0]).to.include( { width: 0, @@ -399,11 +418,11 @@ describe('sharethrough adapter spec', function () { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360, + ttl: 360 }); }); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function () { + it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function() { expect(spec.interpretResponse(bidderResponse, prebidRequests[4])[0]).to.include( { width: 300, @@ -413,26 +432,26 @@ describe('sharethrough adapter spec', function () { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360, + ttl: 360 }); }); - it('returns a blank array if there are no creatives', function () { + it('returns a blank array if there are no creatives', function() { const bidResponse = { body: { creatives: [] } }; expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; }); - it('returns a blank array if body object is empty', function () { + it('returns a blank array if body object is empty', function() { const bidResponse = { body: {} }; expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; }); - it('returns a blank array if body is null', function () { + it('returns a blank array if body is null', function() { const bidResponse = { body: null }; expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; }); - it('correctly generates ad markup when skipIframeBusting is false', function () { + it('correctly generates ad markup when skipIframeBusting is false', function() { const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[0])[0].ad; let resp = null; @@ -447,7 +466,7 @@ describe('sharethrough adapter spec', function () { expect(adMarkup).to.match(/handleIframe/); }); - it('correctly generates ad markup when skipIframeBusting is true', function () { + it('correctly generates ad markup when skipIframeBusting is true', function() { const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[1])[0].ad; let resp = null; @@ -461,11 +480,11 @@ describe('sharethrough adapter spec', function () { }); }); - describe('.getUserSyncs', function () { + describe('.getUserSyncs', function() { const cookieSyncs = ['cookieUrl1', 'cookieUrl2', 'cookieUrl3']; const serverResponses = [{ body: { cookieSyncUrls: cookieSyncs } }]; - it('returns an array of correctly formatted user syncs', function () { + it('returns an array of correctly formatted user syncs', function() { const syncArray = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, null, 'fake-privacy-signal'); expect(syncArray).to.deep.equal([ { type: 'image', url: 'cookieUrl1&us_privacy=fake-privacy-signal' }, @@ -474,22 +493,22 @@ describe('sharethrough adapter spec', function () { ); }); - it('returns an empty array if serverResponses is empty', function () { + it('returns an empty array if serverResponses is empty', function() { const syncArray = spec.getUserSyncs({ pixelEnabled: true }, []); expect(syncArray).to.be.an('array').that.is.empty; }); - it('returns an empty array if the body is null', function () { + it('returns an empty array if the body is null', function() { const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: null }]); expect(syncArray).to.be.an('array').that.is.empty; }); - it('returns an empty array if the body.cookieSyncUrls is missing', function () { + it('returns an empty array if the body.cookieSyncUrls is missing', function() { const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: { creatives: ['creative'] } }]); expect(syncArray).to.be.an('array').that.is.empty; }); - it('returns an empty array if pixels are not enabled', function () { + it('returns an empty array if pixels are not enabled', function() { const syncArray = spec.getUserSyncs({ pixelEnabled: false }, serverResponses); expect(syncArray).to.be.an('array').that.is.empty; }); From 5bda2f162f155bb88c213a208d323088c422f0a3 Mon Sep 17 00:00:00 2001 From: Yevhenii Melnyk Date: Tue, 27 Oct 2020 18:09:28 +0100 Subject: [PATCH 0313/1476] Liveintent id module doesn't fall back to the default implementations of ajax, pixel and storage. (#5859) Liveintent id module reads an email hash that is provided in the configuration. --- modules/liveIntentIdSystem.js | 56 +++++-- modules/userId/index.js | 2 +- package-lock.json | 158 ++++++++++++++++++- package.json | 2 +- test/spec/modules/liveIntentIdSystem_spec.js | 123 +++++++++------ 5 files changed, 276 insertions(+), 65 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 7981b62dc51..4f18c73ad2a 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -5,14 +5,32 @@ * @requires module:modules/userId */ import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; +import { triggerPixel } from '../src/utils.js'; +import { ajaxBuilder } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { LiveConnect } from 'live-connect-js/cjs/live-connect.js'; -import { uspDataHandler } from '../src/adapterManager.js'; +import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; const MODULE_NAME = 'liveIntentId'; export const storage = getStorageManager(null, MODULE_NAME); +const calls = { + ajaxGet: (url, onSuccess, onError, timeout) => { + ajaxBuilder(timeout)( + url, + { + success: onSuccess, + error: onError + }, + undefined, + { + method: 'GET', + withCredentials: true + } + ) + }, + pixelGet: (url, onload) => triggerPixel(url, onload) +} let eventFired = false; let liveConnect = null; @@ -64,18 +82,30 @@ function initializeLiveConnect(configParams) { if (configParams.partner) { identityResolutionConfig.source = configParams.partner } + if (configParams.ajaxTimeout) { + identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout; + } const liveConnectConfig = parseLiveIntentCollectorConfig(configParams.liCollectConfig); liveConnectConfig.wrapperName = 'prebid'; liveConnectConfig.identityResolutionConfig = identityResolutionConfig; liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || []; + if (configParams.emailHash) { + liveConnectConfig.eventSource = { hash: configParams.emailHash } + } const usPrivacyString = uspDataHandler.getConsentData(); if (usPrivacyString) { liveConnectConfig.usPrivacyString = usPrivacyString; } + const gdprConsent = gdprDataHandler.getConsentData() + if (gdprConsent) { + liveConnectConfig.gdprApplies = gdprConsent.gdprApplies; + liveConnectConfig.gdprConsent = gdprConsent.consentString; + } - // The second param is the storage object, which means that all LS & Cookie manipulation will go through PBJS utils. - liveConnect = LiveConnect(liveConnectConfig, storage); + // The second param is the storage object, LS & Cookie manipulation uses PBJS utils. + // The third param is the ajax and pixel object, the ajax and pixel use PBJS utils. + liveConnect = LiveConnect(liveConnectConfig, storage, calls); return liveConnect; } @@ -132,11 +162,9 @@ export const liveIntentIdSubmodule = { return; } tryFireEvent(); - // Don't do the internal ajax call, but use the composed url and fire it via PBJS ajax module - const url = liveConnect.resolutionCallUrl(); - const result = function (callback) { - const callbacks = { - success: response => { + const result = function(callback) { + liveConnect.resolve( + response => { let responseObj = {}; if (response) { try { @@ -147,14 +175,14 @@ export const liveIntentIdSubmodule = { } callback(responseObj); }, - error: error => { + error => { utils.logError(`${MODULE_NAME}: ID fetch encountered an error: `, error); callback(); } - }; - ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); - }; - return {callback: result}; + ) + } + + return { callback: result }; } }; diff --git a/modules/userId/index.js b/modules/userId/index.js index 83573be8682..a5e5fd4eff1 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -83,9 +83,9 @@ * @property {(string|undefined)} publisherId - the unique identifier of the publisher in question * @property {(string|undefined)} ajaxTimeout - the number of milliseconds a resolution request can take before automatically being terminated * @property {(array|undefined)} identifiersToResolve - the identifiers from either ls|cookie to be attached to the getId query - * @property {(string|undefined)} providedIdentifierName - defines the name of an identifier that can be found in local storage or in the cookie jar that can be sent along with the getId request. This parameter should be used whenever a customer is able to provide the most stable identifier possible * @property {(LiveIntentCollectConfig|undefined)} liCollectConfig - the config for LiveIntent's collect requests * @property {(string|undefined)} pd - publisher provided data for reconciling ID5 IDs + * @property {(string|undefined)} emailHash - if provided, the hashed email address of a user */ /** diff --git a/package-lock.json b/package-lock.json index 81ff16fdc5d..bf95487ed9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.11.0-pre", + "version": "4.14.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7800,7 +7800,9 @@ "is-regex": "^1.0.5", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0" + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" }, "dependencies": { "is-regex": { @@ -12029,6 +12031,12 @@ "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", "dev": true }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -15719,9 +15727,9 @@ "dev": true }, "live-connect-js": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-1.1.10.tgz", - "integrity": "sha512-G/LJKN3b21DZILCQRyataC/znLvJRyogtu7mAkKlkhP9B9UJ8bcOL7ihW/clD2PsT4hVUkeabHhUGsPCmhsjFw==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-1.1.23.tgz", + "integrity": "sha512-alOXlYyDdMXt8zzCIs3+iCrdi6r/69c7YRN3sMETa3b2cCOxep3i9j2O0iepk2hxT5JxiR1MvqlqdWAL9d2Hcg==", "requires": { "@kiosked/ulid": "^3.0.0", "abab": "^2.0.3", @@ -20707,6 +20715,146 @@ "es-abstract": "^1.17.0-next.1" } }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", diff --git a/package.json b/package.json index cf82e7a9e95..d7d71f28647 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,6 @@ "fun-hooks": "^0.9.9", "jsencrypt": "^3.0.0-rc.1", "just-clone": "^1.0.2", - "live-connect-js": "1.1.10" + "live-connect-js": "^1.1.23" } } diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 80f776168c4..aae60cbcd19 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -1,46 +1,50 @@ -import {liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage} from 'modules/liveIntentIdSystem.js'; +import { liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage } from 'modules/liveIntentIdSystem.js'; import * as utils from 'src/utils.js'; -import {uspDataHandler} from '../../../src/adapterManager.js'; -import {server} from 'test/mocks/xhr.js'; +import { gdprDataHandler, uspDataHandler } from '../../../src/adapterManager.js'; +import { server } from 'test/mocks/xhr.js'; const PUBLISHER_ID = '89899'; const defaultConfigParams = { params: {publisherId: PUBLISHER_ID} }; const responseHeader = {'Content-Type': 'application/json'} -describe('LiveIntentId', function () { - let pixel = {}; +describe('LiveIntentId', function() { let logErrorStub; - let consentDataStub; + let uspConsentDataStub; + let gdprConsentDataStub; let getCookieStub; let getDataFromLocalStorageStub; let imgStub; - beforeEach(function () { - imgStub = sinon.stub(window, 'Image').returns(pixel); + beforeEach(function() { + imgStub = sinon.stub(utils, 'triggerPixel'); getCookieStub = sinon.stub(storage, 'getCookie'); getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); logErrorStub = sinon.stub(utils, 'logError'); - consentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); + uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); + gdprConsentDataStub = sinon.stub(gdprDataHandler, 'getConsentData'); }); - afterEach(function () { - pixel = {}; + afterEach(function() { imgStub.restore(); getCookieStub.restore(); getDataFromLocalStorageStub.restore(); logErrorStub.restore(); - consentDataStub.restore(); + uspConsentDataStub.restore(); + gdprConsentDataStub.restore(); resetLiveIntentIdSubmodule(); }); - it('should initialize LiveConnect with a us privacy string when getId, and include it in all requests', function () { - consentDataStub.returns('1YNY'); + it('should initialize LiveConnect with a privacy string when getId, and include it in the resolution request', function() { + uspConsentDataStub.returns('1YNY'); + gdprConsentDataStub.returns({ + gdprApplies: true, + consentString: 'consentDataString' + }) let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; - expect(pixel.src).to.match(/.*us_privacy=1YNY/); submoduleCallback(callBackSpy); - let request = server.requests[0]; - expect(request.url).to.match(/.*us_privacy=1YNY/); + let request = server.requests[1]; + expect(request.url).to.match(/.*us_privacy=1YNY.*&gdpr=1&gdpr_consent=consentDataString.*/); request.respond( 200, responseHeader, @@ -49,9 +53,22 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should fire an event when getId', function () { + it('should fire an event when getId', function() { + uspConsentDataStub.returns('1YNY'); + gdprConsentDataStub.returns({ + gdprApplies: true, + consentString: 'consentDataString' + }) liveIntentIdSubmodule.getId(defaultConfigParams); - expect(pixel.src).to.match(/https:\/\/rp.liadm.com\/p\?wpn=prebid.*/) + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?wpn=prebid.*us_privacy=1YNY.*&gdpr=1&gdpr_consent=consentDataString.*/); + }); + + it('should fire an event when getId and a hash is provided', function() { + liveIntentIdSubmodule.getId({ params: { + ...defaultConfigParams, + emailHash: '58131bc547fb87af94cebdaf3102321f' + }}); + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*e=58131bc547fb87af94cebdaf3102321f.+/) }); it('should initialize LiveConnect with the config params when decode and emit an event', function () { @@ -64,40 +81,52 @@ describe('LiveIntentId', function () { collectorUrl: 'https://collector.liveintent.com' } } - } }); - expect(pixel.src).to.match(/https:\/\/collector.liveintent.com\/p\?aid=a-0001&wpn=prebid.*/) + }}); + expect(server.requests[0].url).to.match(/https:\/\/collector.liveintent.com\/j\?aid=a-0001&wpn=prebid.*/); }); - it('should initialize LiveConnect and emit an event with a us privacy string when decode', function () { - consentDataStub.returns('1YNY'); + it('should initialize LiveConnect and emit an event with a privacy string when decode', function() { + uspConsentDataStub.returns('1YNY'); + gdprConsentDataStub.returns({ + gdprApplies: false, + consentString: 'consentDataString' + }) liveIntentIdSubmodule.decode({}, defaultConfigParams); - expect(pixel.src).to.match(/.*us_privacy=1YNY/); + expect(server.requests[0].url).to.match(/.*us_privacy=1YNY.*&gdpr=0&gdpr_consent=consentDataString.*/); }); - it('should not return a decoded identifier when the unifiedId is not present in the value', function () { - const result = liveIntentIdSubmodule.decode({additionalData: 'data'}); + it('should fire an event when decode and a hash is provided', function() { + liveIntentIdSubmodule.decode({}, { params: { + ...defaultConfigParams.params, + emailHash: '58131bc547fb87af94cebdaf3102321f' + }}); + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*e=58131bc547fb87af94cebdaf3102321f.+/); + }); + + it('should not return a decoded identifier when the unifiedId is not present in the value', function() { + const result = liveIntentIdSubmodule.decode({ additionalData: 'data' }); expect(result).to.be.undefined; }); - it('should fire an event when decode', function () { + it('should fire an event when decode', function() { liveIntentIdSubmodule.decode({}, defaultConfigParams); - expect(pixel.src).to.be.not.null + expect(server.requests[0].url).to.be.not.null }); - it('should initialize LiveConnect and send data only once', function () { + it('should initialize LiveConnect and send data only once', function() { liveIntentIdSubmodule.getId(defaultConfigParams); liveIntentIdSubmodule.decode({}, defaultConfigParams); liveIntentIdSubmodule.getId(defaultConfigParams); liveIntentIdSubmodule.decode({}, defaultConfigParams); - expect(imgStub.calledOnce).to.be.true; + expect(server.requests.length).to.be.eq(1); }); - it('should call the Custom URL of the LiveIntent Identity Exchange endpoint', function () { + it('should call the Custom URL of the LiveIntent Identity Exchange endpoint', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899'); request.respond( 200, @@ -107,7 +136,7 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function () { + it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { @@ -118,7 +147,7 @@ describe('LiveIntentId', function () { } } }).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899'); request.respond( 200, @@ -128,12 +157,12 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the LiveIntent Identity Exchange endpoint, with no additional query params', function () { + it('should call the LiveIntent Identity Exchange endpoint, with no additional query params', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899'); request.respond( 200, @@ -143,12 +172,12 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should log an error and continue to callback if ajax request errors', function () { + it('should log an error and continue to callback if ajax request errors', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899'); request.respond( 503, @@ -159,13 +188,13 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should include the LiveConnect identifier when calling the LiveIntent Identity Exchange endpoint', function () { + it('should include the LiveConnect identifier when calling the LiveIntent Identity Exchange endpoint', function() { const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000' getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}`); request.respond( 200, @@ -175,7 +204,7 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should include the LiveConnect identifier and additional Identifiers to resolve', function () { + it('should include the LiveConnect identifier and additional Identifiers to resolve', function() { const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000' getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie); getDataFromLocalStorageStub.withArgs('_thirdPC').returns('third-pc'); @@ -188,7 +217,7 @@ describe('LiveIntentId', function () { let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&_thirdPC=third-pc`); request.respond( 200, @@ -198,7 +227,7 @@ describe('LiveIntentId', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should include an additional identifier value to resolve even if it is an object', function () { + it('should include an additional identifier value to resolve even if it is an object', function() { getCookieStub.returns(null); getDataFromLocalStorageStub.withArgs('_thirdPC').returns({'key': 'value'}); const configParams = { params: { @@ -210,7 +239,7 @@ describe('LiveIntentId', function () { let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); - let request = server.requests[0]; + let request = server.requests[1]; expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?_thirdPC=%7B%22key%22%3A%22value%22%7D'); request.respond( 200, @@ -219,4 +248,10 @@ describe('LiveIntentId', function () { ); expect(callBackSpy.calledOnce).to.be.true; }); + + it('should send an error when the cookie jar throws an unexpected error', function() { + getCookieStub.throws('CookieError', 'A message'); + liveIntentIdSubmodule.getId(defaultConfigParams); + expect(imgStub.getCall(0).args[0]).to.match(/.*ae=.+/); + }); }); From 7da5dadce612890bbf2c101a61b02f6a546e2178 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 28 Oct 2020 02:15:55 -0700 Subject: [PATCH 0314/1476] aol bid adapter: support IE (#5894) * support IE in aol spec * array includes not supported IE11 --- modules/aolBidAdapter.js | 2 +- test/spec/modules/aolBidAdapter_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 93c56846639..c899da32340 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -116,7 +116,7 @@ function resolveEndpointCode(bid) { function getSupportedEids(bid) { return bid.userIdAsEids.filter(eid => { - return SUPPORTED_USER_ID_SOURCES.includes(eid.source) + return SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1 }); } diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 2276e32ece7..11e1a317b70 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -490,7 +490,7 @@ describe('AolAdapter', function () { '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); }); - for (const [source, idValue] of Object.entries(SUPPORTED_USER_ID_SOURCES)) { + Object.keys(SUPPORTED_USER_ID_SOURCES).forEach(source => { it(`should set the user ID query param for ${source}`, function () { let bidRequest = createCustomBidRequest({ params: getNexageGetBidParams() @@ -498,9 +498,9 @@ describe('AolAdapter', function () { bidRequest.bids[0].userId = {}; bidRequest.bids[0].userIdAsEids = createEidsArray(USER_ID_DATA); let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain(`&eid${source}=${encodeURIComponent(idValue)}`); + expect(request.url).to.contain(`&eid${source}=${encodeURIComponent(SUPPORTED_USER_ID_SOURCES[source])}`); }); - } + }); it('should return request object for One Mobile POST endpoint when POST configuration is present', function () { let bidConfig = getNexagePostBidParams(); From 9d37b25e9b36852761edc5f650803396112525cb Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 28 Oct 2020 15:25:44 +0530 Subject: [PATCH 0315/1476] add check for config to make sure its defined (#5873) --- modules/consentManagement.js | 2 +- modules/consentManagementUsp.js | 2 +- test/spec/modules/consentManagementUsp_spec.js | 7 +++++++ test/spec/modules/consentManagement_spec.js | 6 ++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index f44fde0554d..1060fdb5cc5 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -451,7 +451,7 @@ export function resetConsentData() { export function setConsentConfig(config) { // if `config.gdpr` or `config.usp` exist, assume new config format. // else for backward compatability, just use `config` - config = config.gdpr || config.usp ? config.gdpr : config; + config = config && (config.gdpr || config.usp ? config.gdpr : config); if (!config || typeof config !== 'object') { utils.logWarn('consentManagement config not defined, exiting consent manager'); return; diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index e4d5c12eb46..3edacb41549 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -269,7 +269,7 @@ export function resetConsentData() { * @param {object} config required; consentManagementUSP module config settings; usp (string), timeout (int), allowAuctionWithoutConsent (boolean) */ export function setConsentConfig(config) { - config = config.usp; + config = config && config.usp; if (!config || typeof config !== 'object') { utils.logWarn('consentManagement.usp config not defined, exiting usp consent manager'); return; diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index ee4140afa10..7d3cd48a8e4 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -64,6 +64,13 @@ describe('consentManagement', function () { sinon.assert.calledOnce(utils.logWarn); sinon.assert.notCalled(utils.logInfo); }); + + it('should exit consentManagementUsp module if config is "undefined"', function() { + setConsentConfig(undefined); + expect(consentAPI).to.be.undefined; + sinon.assert.calledOnce(utils.logWarn); + sinon.assert.notCalled(utils.logInfo); + }); }); describe('valid setConsentConfig value', function () { diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index deaacbc5a28..cf5c578502f 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -39,6 +39,12 @@ describe('consentManagement', function () { 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); + }); }); describe('valid setConsentConfig value', function () { From 9dbc9985c3457631b269168d0ccfda60402e3797 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 28 Oct 2020 18:25:55 +0100 Subject: [PATCH 0316/1476] Prebid 4.14.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7d71f28647..98bd1bbbce5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.14.0-pre", + "version": "4.14.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d966829b23235e5782d01489fe3162e0cc8c9ff8 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 28 Oct 2020 18:41:41 +0100 Subject: [PATCH 0317/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98bd1bbbce5..c6c78a45c5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.14.0", + "version": "4.15.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 78a2d289ade4d446baa4102d1b7484c77977a6f8 Mon Sep 17 00:00:00 2001 From: Mike Sperone Date: Thu, 29 Oct 2020 10:35:03 -0500 Subject: [PATCH 0318/1476] Media type renderers (#5760) * allow publisher to define a renderer specific to the mediaType * validate outstream bid with a renderer defined on the video mediaType * get the mediaTypes from the bidReqest * tests for publisher-defined, media-specific renderers * use single quote * undo inadvertent package-lock.json changes Co-authored-by: Michael Sperone --- src/auction.js | 23 ++++++++++++++++++++--- src/video.js | 2 +- test/spec/auctionmanager_spec.js | 30 ++++++++++++++++++++++++++++++ test/spec/video_spec.js | 23 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/auction.js b/src/auction.js index 5858c3edf78..e0b8feb5c62 100644 --- a/src/auction.js +++ b/src/auction.js @@ -512,9 +512,26 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { const bidReq = bidderRequest.bids && find(bidderRequest.bids, bid => bid.adUnitCode == adUnitCode); const adUnitRenderer = bidReq && bidReq.renderer; - if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly && isBoolean(adUnitRenderer.backupOnly) && bid.renderer)) { - bidObject.renderer = Renderer.install({ url: adUnitRenderer.url }); - bidObject.renderer.setRender(adUnitRenderer.render); + // a publisher can also define a renderer for a mediaType + const bidObjectMediaType = bidObject.mediaType; + const bidMediaType = bidReq && + bidReq.mediaTypes && + bidReq.mediaTypes[bidObjectMediaType]; + + var mediaTypeRenderer = bidMediaType && bidMediaType.renderer; + + var renderer = null; + + // the renderer for the mediaType takes precendence + if (mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render) { + renderer = mediaTypeRenderer; + } else if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly && isBoolean(adUnitRenderer.backupOnly) && bid.renderer)) { + renderer = adUnitRenderer; + } + + if (renderer) { + bidObject.renderer = Renderer.install({ url: renderer.url }); + bidObject.renderer.setRender(renderer.render); } // Use the config value 'mediaTypeGranularity' if it has been defined for mediaType, else use 'customPriceBucket' diff --git a/src/video.js b/src/video.js index befeb2ded39..20df7a92442 100644 --- a/src/video.js +++ b/src/video.js @@ -59,7 +59,7 @@ export const checkVideoBidSetup = hook('sync', function(bid, bidRequest, videoMe // outstream bids require a renderer on the bid or pub-defined on adunit if (context === OUTSTREAM) { - return !!(bid.renderer || bidRequest.renderer); + return !!(bid.renderer || bidRequest.renderer || videoMediaType.renderer); } return true; diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index e35b1406fbf..1517b5d69d8 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -763,6 +763,36 @@ describe('auctionmanager.js', function () { assert.equal(addedBid.renderer.url, 'renderer.js'); }); + it('installs publisher-defined renderers for a media type', function () { + const renderer = { + url: 'videoRenderer.js', + render: (bid) => bid + }; + let myBid = mockBid(); + let bidRequest = mockBidRequest(myBid); + + bidRequest.bids[0] = { + ...bidRequest.bids[0], + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'outstream', + renderer + } + } + }; + makeRequestsStub.returns([bidRequest]); + + myBid.mediaType = 'video'; + spec.interpretResponse.returns(myBid); + auction.callBids(); + + const addedBid = auction.getBidsReceived().pop(); + assert.equal(addedBid.renderer.url, renderer.url); + }); + it('bid for a regular unit and a video unit', function() { let renderer = { url: 'renderer.js', diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 72a585049c3..3ce8ba081da 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -71,6 +71,29 @@ describe('video.js', function () { expect(valid).to.equal(true); }); + it('validates valid outstream bids with a publisher defined renderer', function () { + const bid = { + requestId: '123abc', + }; + const bidRequests = [{ + bids: [{ + bidId: '123abc', + bidder: 'appnexus', + mediaTypes: { + video: { + context: 'outstream', + renderer: { + url: 'render.url', + render: () => true, + } + } + } + }] + }]; + const valid = isValidVideoBid(bid, bidRequests); + expect(valid).to.equal(true); + }); + it('catches invalid outstream bids', function () { const bid = { requestId: '123abc' From 9a75d26d44114a3a4e8c0e2974bf881b7788e02f Mon Sep 17 00:00:00 2001 From: lowendavid <66423906+lowendavid@users.noreply.github.com> Date: Thu, 29 Oct 2020 18:08:39 +0100 Subject: [PATCH 0319/1476] Added GVL_ID & addtl_consent for smartadserverBidAdapter (#5870) * SIM-875 Adding GVL_ID * SIM-875 Added addtl_consent * SIM-875 removing trailing whitespaces --- modules/smartadserverBidAdapter.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 8462e749b91..ed9003e3b4d 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -13,8 +13,10 @@ import { createEidsArray } from './userId/eids.js'; const BIDDER_CODE = 'smartadserver'; +const GVL_ID = 45; export const spec = { code: BIDDER_CODE, + gvlid: GVL_ID, aliases: ['smart'], // short code supportedMediaTypes: [BANNER, VIDEO], /** @@ -99,6 +101,7 @@ export const spec = { } if (bidderRequest && bidderRequest.gdprConsent) { + payload.addtl_consent = bidderRequest.gdprConsent.addtlConsent; payload.gdpr_consent = bidderRequest.gdprConsent.consentString; payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side } From 51f240519da480d09c7258bb6599abcfee62f93a Mon Sep 17 00:00:00 2001 From: Krushmedia <71434282+Krushmedia@users.noreply.github.com> Date: Fri, 30 Oct 2020 14:33:02 +0200 Subject: [PATCH 0320/1476] New krushmedia Prebid.js adapter (#5833) * inital * fix * fix * fix * fix * fix * fix * add maintener to md * Added native support Co-authored-by: Aiholkin --- modules/krushmediaBidAdapter.js | 104 ++++++ modules/krushmediaBidAdapter.md | 80 +++++ .../spec/modules/krushmediaBidAdapter_spec.js | 304 ++++++++++++++++++ 3 files changed, 488 insertions(+) create mode 100644 modules/krushmediaBidAdapter.js create mode 100644 modules/krushmediaBidAdapter.md create mode 100644 test/spec/modules/krushmediaBidAdapter_spec.js diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js new file mode 100644 index 00000000000..f70500cc101 --- /dev/null +++ b/modules/krushmediaBidAdapter.js @@ -0,0 +1,104 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'krushmedia'; +const AD_URL = 'https://ads4.krushmedia.com/?c=rtb&m=hb'; + +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); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.key))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + + const placements = []; + const request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + '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 = { + key: bid.params.key, + bidId: bid.bidId, + traffic: bid.params.traffic || BANNER, + schain: bid.schain || {}, + }; + + if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + placement.sizes = bid.mediaTypes[BANNER].sizes; + } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + placement.wPlayer = bid.mediaTypes[VIDEO].playerSize[0]; + placement.hPlayer = bid.mediaTypes[VIDEO].playerSize[1]; + } else if (bid.mediaTypes && bid.mediaTypes[NATIVE]) { + placement.native = bid.mediaTypes[NATIVE]; + } + 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)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md new file mode 100644 index 00000000000..7bf7c4fe491 --- /dev/null +++ b/modules/krushmediaBidAdapter.md @@ -0,0 +1,80 @@ +# Overview + +``` +Module Name: krushmedia Bidder Adapter +Module Type: krushmedia Bidder Adapter +Maintainer: adapter@krushmedia.com +``` + +# Description + +Module that connects to krushmedia demand sources + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 0, + traffic: 'banner' + } + } + ] + }, + // Will return test vast xml. All video params are stored under placement in publishers UI + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 0, + traffic: 'video' + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 0, + traffic: 'native' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js new file mode 100644 index 00000000000..2673627bc6d --- /dev/null +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -0,0 +1,304 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/krushmediaBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; + +describe('KrushmediabBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'krushmedia', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + key: 783, + traffic: BANNER + } + }; + + 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.key; + 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://ads4.krushmedia.com/?c=rtb&m=hb'); + }); + 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'); + 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.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement.key).to.equal(783); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + 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'); + }); + 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' + }] + }; + 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'); + 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'); + }); + 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', + }] + }; + 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'); + 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'); + }); + 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; + }); + }); +}); From 348a4f5bd00cf8292b613afe9c9f4d9a3bad23c7 Mon Sep 17 00:00:00 2001 From: etargetse <40423120+etargetse@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:36:17 +0100 Subject: [PATCH 0321/1476] eTarget: adapter update (#5881) * adapter update Send response reason * Update etargetBidAdapter.js Adding optional response parameter * Update etargetBidAdapter_spec.js --- modules/etargetBidAdapter.js | 1 + test/spec/modules/etargetBidAdapter_spec.js | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 5e07561044a..42c991a17a4 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -96,6 +96,7 @@ export const spec = { currency: data.win_cur, netRevenue: true, ttl: 360, + reason: data.reason ? data.reason : 'none', ad: data.banner, vastXml: data.vast_content, vastUrl: data.vast_link, diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index 4f5e0c224ec..2dbf6cd68c5 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -154,6 +154,7 @@ describe('etarget adapter', function () { assert.equal(result.height, 250); assert.equal(result.currency, 'EUR'); assert.equal(result.netRevenue, true); + assert.isNotNull(result.reason); assert.equal(result.ttl, 360); assert.equal(result.ad, ''); assert.equal(result.transactionId, '5f33781f-9552-4ca1'); From e3a9c6056a5e2b575f8dd0af57f5dcebfe601f35 Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Mon, 2 Nov 2020 04:33:47 -0500 Subject: [PATCH 0322/1476] DMX Fix video bug (#5910) * adding DMX test @97%, two files added one updated * Update districtm_spec.js * Update districtmDMX.js * adding all districtm needed file * remove legacy file * remove typo || 0 in the test method * force default to return a valid width and height * update unit test code for failing test * changed class for an object * remove package-lock.json * change file name for dmx adapter * renamed files * restaure package-lock.json * update to last package-lock state * update gdpr user consent * fix sizes issue * Documentation updates Adding the readme.md info * update file name and update unit testing import file location * current machine state * lint correction * remove variable assigment duplicate * adding CCPA support for DMX * adding test for ccpa and gdpr * districtm dmx adding deal id field * idsync support ccpa & gdpr * fix error on vast response that failed Co-authored-by: Steve Alliance Co-authored-by: Luis Co-authored-by: Steve Alliance Co-authored-by: Steve Alliance Co-authored-by: steve-a-districtm --- modules/districtmDMXBidAdapter.js | 72 ++++++++----------- .../modules/districtmDmxBidAdapter_spec.js | 7 +- 2 files changed, 32 insertions(+), 47 deletions(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index bcb2bb97210..4d8154e429e 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -39,9 +39,9 @@ export const spec = { nBid.requestId = nBid.impid; nBid.width = nBid.w || width; nBid.height = nBid.h || height; - nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : null; + nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner'; if (nBid.mediaType) { - nBid.vastXml = cleanVast(nBid.adm); + nBid.vastXml = cleanVast(nBid.adm, nBid.nurl); } if (nBid.dealid) { nBid.dealId = nBid.dealid; @@ -154,19 +154,16 @@ export const spec = { if (dmx.mediaTypes && dmx.mediaTypes.video) { obj.video = { topframe: 1, - skip: dmx.mediaTypes.video.skippable || 0, + skip: dmx.mediaTypes.video.skip || 0, linearity: dmx.mediaTypes.video.linearity || 1, minduration: dmx.mediaTypes.video.minduration || 5, maxduration: dmx.mediaTypes.video.maxduration || 60, - playbackmethod: getPlaybackmethod(dmx.mediaTypes.video.playback_method), + playbackmethod: dmx.mediaTypes.video.playbackmethod || [2], api: getApi(dmx.mediaTypes.video), mimes: dmx.mediaTypes.video.mimes || ['video/mp4'], protocols: getProtocols(dmx.mediaTypes.video), - w: dmx.mediaTypes.video.playerSize[0][0], h: dmx.mediaTypes.video.playerSize[0][1], - format: dmx.mediaTypes.video.playerSize.map(s => { - return {w: s[0], h: s[1]}; - }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number') + w: dmx.mediaTypes.video.playerSize[0][0] }; } else { obj.banner = { @@ -380,20 +377,10 @@ export function bindUserId(eids, value, source, atype) { } } -export function getApi({protocols}) { +export function getApi({api}) { let defaultValue = [2]; - let listProtocols = [ - {key: 'VPAID_1_0', value: 1}, - {key: 'VPAID_2_0', value: 2}, - {key: 'MRAID_1', value: 3}, - {key: 'ORMMA', value: 4}, - {key: 'MRAID_2', value: 5}, - {key: 'MRAID_3', value: 6}, - ]; - if (protocols) { - return listProtocols.filter(p => { - return protocols.indexOf(p.key) !== -1; - }).map(p => p.value) + if (api && Array.isArray(api) && api.length > 0) { + return api } else { return defaultValue; } @@ -409,35 +396,32 @@ export function getPlaybackmethod(playback) { export function getProtocols({protocols}) { let defaultValue = [2, 3, 5, 6, 7, 8]; - let listProtocols = [ - {key: 'VAST_1_0', value: 1}, - {key: 'VAST_2_0', value: 2}, - {key: 'VAST_3_0', value: 3}, - {key: 'VAST_1_0_WRAPPER', value: 4}, - {key: 'VAST_2_0_WRAPPER', value: 5}, - {key: 'VAST_3_0_WRAPPER', value: 6}, - {key: 'VAST_4_0', value: 7}, - {key: 'VAST_4_0_WRAPPER', value: 8} - ]; - if (protocols) { - return listProtocols.filter(p => { - return protocols.indexOf(p.key) !== -1 - }).map(p => p.value); + if (protocols && Array.isArray(protocols) && protocols.length > 0) { + return protocols; } else { return defaultValue; } } -export function cleanVast(str) { - const toberemove = /]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/ - const [img, url] = str.match(toberemove) - str = str.replace(toberemove, '') - if (img) { - if (url) { - const insrt = `` - str = str.replace('', `${insrt}`) +export function cleanVast(str, nurl) { + try { + const toberemove = /]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/ + const [img, url] = str.match(toberemove) + str = str.replace(toberemove, '') + if (img) { + if (url) { + const insrt = `` + str = str.replace('', `${insrt}`) + } + } + return str; + } catch (e) { + if(!nurl) { + return str } + const insrt = `` + str = str.replace('', `${insrt}`) + return str } - return str; } registerBidder(spec); diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js index 90e6957fc2c..9dd565f14b1 100644 --- a/test/spec/modules/districtmDmxBidAdapter_spec.js +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -145,7 +145,7 @@ const bidRequestVideo = [{ } }, 'mediaTypes': { video: {context: 'instream', // or 'outstream' - playerSize: [[640, 480]]} }, + playerSize: [[640, 480]]} }, 'adUnitCode': 'div-gpt-ad-12345678-1', 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', 'sizes': [ @@ -616,7 +616,7 @@ describe('DistrictM Adaptor', function () { }) describe('Test getApi function', function() { const data = { - protocols: ['VPAID_1_0'] + api: [1] } it('Will return 1 for vpaid version 1', function() { expect(getApi(data)[0]).to.be.equal(1) @@ -646,7 +646,7 @@ describe('DistrictM Adaptor', function () { describe('Test getProtocols function', function() { it('getProtocols will return 3', function() { - expect(getProtocols({protocols: ['VAST_3_0']})[0]).to.be.equal(3) + expect(getProtocols({protocols: [3]})[0]).to.be.equal(3) }) it('getProtocols will return 6', function() { expect(_.isEqual(getProtocols({}), [2, 3, 5, 6, 7, 8])).to.be.equal(true) @@ -811,3 +811,4 @@ describe('DistrictM Adaptor', function () { }); }); }); + From eb55e676a25ad9c6908d864229be5175380ead73 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Mon, 2 Nov 2020 16:01:49 +0530 Subject: [PATCH 0323/1476] fix failing lint errors on circle ci (#5918) --- modules/districtmDMXBidAdapter.js | 30 +++---- .../modules/districtmDmxBidAdapter_spec.js | 79 ++++++++++--------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index 4d8154e429e..a7bcead5f0b 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'districtmDMX'; @@ -28,12 +28,12 @@ export const spec = { response = response.body || {}; if (response.seatbid) { if (utils.isArray(response.seatbid)) { - const {seatbid} = response; + const { seatbid } = response; let winners = seatbid.reduce((bid, ads) => { - let ad = ads.bid.reduce(function(oBid, nBid) { + let ad = ads.bid.reduce(function (oBid, nBid) { if (oBid.price < nBid.price) { const bid = matchRequest(nBid.impid, bidRequest); - const {width, height} = defaultSize(bid); + const { width, height } = defaultSize(bid); nBid.cpm = parseFloat(nBid.price).toFixed(2); nBid.bidId = nBid.impid; nBid.requestId = nBid.impid; @@ -61,7 +61,7 @@ export const spec = { oBid.cpm = oBid.price; return oBid; } - }, {price: 0}); + }, { price: 0 }); if (ad.adm) { bid.push(ad) } @@ -98,7 +98,7 @@ export const spec = { let params = config.getConfig('dmx'); dmxRequest.user = params.user || {}; let site = params.site || {}; - dmxRequest.site = {...dmxRequest.site, ...site} + dmxRequest.site = { ...dmxRequest.site, ...site } } catch (e) { } @@ -144,7 +144,7 @@ export const spec = { dmxRequest.source = {}; dmxRequest.source.ext = {}; dmxRequest.source.ext.schain = schain || {} - } catch (e) {} + } catch (e) { } let tosendtags = bidRequest.map(dmx => { var obj = {}; obj.id = dmx.bidId; @@ -171,7 +171,7 @@ export const spec = { w: cleanSizes(dmx.sizes, 'w'), h: cleanSizes(dmx.sizes, 'h'), format: cleanSizes(dmx.sizes).map(s => { - return {w: s[0], h: s[1]}; + return { w: s[0], h: s[1] }; }).filter(obj => typeof obj.w === 'number' && typeof obj.h === 'number') }; } @@ -214,7 +214,7 @@ export const spec = { } } -export function getFloor (bid) { +export function getFloor(bid) { let floor = null; if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ @@ -294,7 +294,7 @@ export function shuffle(sizes, list) { } results.push(current); results = list.filter(l => results.map(r => `${r[0]}x${r[1]}`).indexOf(`${l.size[0]}x${l.size[1]}`) !== -1); - results = results.sort(function(a, b) { + results = results.sort(function (a, b) { return b.s - a.s; }) return results.map(r => r.size); @@ -340,7 +340,7 @@ export function upto5(allimps, dmxRequest, bidderRequest, DMXURI) { * */ export function matchRequest(id, bidRequest) { - const {bids} = bidRequest.bidderRequest; + const { bids } = bidRequest.bidderRequest; const [returnValue] = bids.filter(bid => bid.bidId === id); return returnValue; } @@ -356,7 +356,7 @@ export function checkDeepArray(Arr) { } } export function defaultSize(thebidObj) { - const {sizes} = thebidObj; + const { sizes } = thebidObj; const returnObject = {}; returnObject.width = checkDeepArray(sizes)[0]; returnObject.height = checkDeepArray(sizes)[1]; @@ -377,7 +377,7 @@ export function bindUserId(eids, value, source, atype) { } } -export function getApi({api}) { +export function getApi({ api }) { let defaultValue = [2]; if (api && Array.isArray(api) && api.length > 0) { return api @@ -394,7 +394,7 @@ export function getPlaybackmethod(playback) { return [2] } -export function getProtocols({protocols}) { +export function getProtocols({ protocols }) { let defaultValue = [2, 3, 5, 6, 7, 8]; if (protocols && Array.isArray(protocols) && protocols.length > 0) { return protocols; @@ -416,7 +416,7 @@ export function cleanVast(str, nurl) { } return str; } catch (e) { - if(!nurl) { + if (!nurl) { return str } const insrt = `` diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js index 9dd565f14b1..5d1f299dad5 100644 --- a/test/spec/modules/districtmDmxBidAdapter_spec.js +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as _ from 'lodash'; -import {spec, matchRequest, checkDeepArray, defaultSize, upto5, cleanSizes, shuffle, getApi, bindUserId, getPlaybackmethod, getProtocols, cleanVast} from '../../../modules/districtmDMXBidAdapter.js'; +import { spec, matchRequest, checkDeepArray, defaultSize, upto5, cleanSizes, shuffle, getApi, bindUserId, getPlaybackmethod, getProtocols, cleanVast } from '../../../modules/districtmDMXBidAdapter.js'; const sample_vast = ` @@ -144,8 +144,12 @@ const bidRequestVideo = [{ 'video/mp4'], } }, - 'mediaTypes': { video: {context: 'instream', // or 'outstream' - playerSize: [[640, 480]]} }, + 'mediaTypes': { + video: { + context: 'instream', // or 'outstream' + playerSize: [[640, 480]] + } + }, 'adUnitCode': 'div-gpt-ad-12345678-1', 'transactionId': 'f6d13fa6-ebc1-41ac-9afa-d8171d22d2c2', 'sizes': [ @@ -602,53 +606,53 @@ const emptyResponseSeatBid = { body: { seatbid: [] } }; describe('DistrictM Adaptor', function () { const districtm = spec; - describe('verification of upto5', function() { - it('upto5 function should always break 12 imps into 3 request same for 15', function() { + describe('verification of upto5', function () { + it('upto5 function should always break 12 imps into 3 request same for 15', function () { expect(upto5([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], bidRequest, bidderRequest, 'https://google').length).to.be.equal(3) expect(upto5([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], bidRequest, bidderRequest, 'https://google').length).to.be.equal(3) }) }) - describe('test vast tag', function() { - it('img tag should not be present', function() { + describe('test vast tag', function () { + it('img tag should not be present', function () { expect(cleanVast(sample_vast).indexOf('img') !== -1).to.be.equal(false) }) }) - describe('Test getApi function', function() { + describe('Test getApi function', function () { const data = { api: [1] } - it('Will return 1 for vpaid version 1', function() { + it('Will return 1 for vpaid version 1', function () { expect(getApi(data)[0]).to.be.equal(1) }) - it('Will return 2 for vpaid default', function() { + it('Will return 2 for vpaid default', function () { expect(getApi({})[0]).to.be.equal(2) }) }) - describe('Test cleanSizes function', function() { - it('sequence will be respected', function() { + describe('Test cleanSizes function', function () { + it('sequence will be respected', function () { expect(cleanSizes(bidderRequest.bids[0].sizes).toString()).to.be.equal('300,250,300,600') }) - it('sequence will be respected', function() { + it('sequence will be respected', function () { expect(cleanSizes([[728, 90], [970, 90], [300, 600], [320, 50]]).toString()).to.be.equal('728,90,320,50,300,600,970,90') }) }) - describe('Test getPlaybackmethod function', function() { - it('getPlaybackmethod will return 2', function() { + describe('Test getPlaybackmethod function', function () { + it('getPlaybackmethod will return 2', function () { expect(getPlaybackmethod([])[0]).to.be.equal(2) }) - it('getPlaybackmethod will return 6', function() { + it('getPlaybackmethod will return 6', function () { expect(getPlaybackmethod(['viewport_sound_off'])[0]).to.be.equal(6) }) }) - describe('Test getProtocols function', function() { - it('getProtocols will return 3', function() { - expect(getProtocols({protocols: [3]})[0]).to.be.equal(3) + describe('Test getProtocols function', function () { + it('getProtocols will return 3', function () { + expect(getProtocols({ protocols: [3] })[0]).to.be.equal(3) }) - it('getProtocols will return 6', function() { + it('getProtocols will return 6', function () { expect(_.isEqual(getProtocols({}), [2, 3, 5, 6, 7, 8])).to.be.equal(true) }) }) @@ -687,7 +691,7 @@ describe('DistrictM Adaptor', function () { memberid: 10003, }; it(`function should return true`, function () { - expect(districtm.isBidRequestValid({params})).to.be.equal(true); + expect(districtm.isBidRequestValid({ params })).to.be.equal(true); }); it(`function should return false`, function () { expect(districtm.isBidRequestValid({ params: { memberid: 12345 } })).to.be.equal(false); @@ -716,19 +720,19 @@ describe('DistrictM Adaptor', function () { it(`the function should return an array`, function () { expect(buildRequestResults).to.be.an('object'); }); - it(`contain gdpr consent & ccpa`, function() { + it(`contain gdpr consent & ccpa`, function () { const bidr = JSON.parse(buildRequestResults.data) expect(bidr.regs.ext.gdpr).to.be.equal(1); expect(bidr.regs.ext.us_privacy).to.be.equal('1NY'); expect(bidr.user.ext.consent).to.be.an('string'); }); - it(`test contain COPPA`, function() { + it(`test contain COPPA`, function () { const bidr = JSON.parse(buildRequestResults.data) bidr.regs = bidr.regs || {}; bidr.regs.coppa = 1; expect(bidr.regs.coppa).to.be.equal(1) }) - it(`test should not contain COPPA`, function() { + it(`test should not contain COPPA`, function () { const bidr = JSON.parse(buildRequestResultsNoCoppa.data) expect(bidr.regs.coppa).to.be.equal(0) }) @@ -737,17 +741,17 @@ describe('DistrictM Adaptor', function () { }); }); - describe('bidRequest Video testing', function() { + describe('bidRequest Video testing', function () { const request = districtm.buildRequests(bidRequestVideo, bidRequestVideo); const data = JSON.parse(request.data) expect(data instanceof Object).to.be.equal(true) }) describe(`interpretResponse test usage`, function () { - const responseResults = districtm.interpretResponse(responses, {bidderRequest}); - const emptyResponseResults = districtm.interpretResponse(emptyResponse, {bidderRequest}); - const emptyResponseResultsNegation = districtm.interpretResponse(responsesNegative, {bidderRequest}); - const emptyResponseResultsEmptySeat = districtm.interpretResponse(emptyResponseSeatBid, {bidderRequest}); + const responseResults = districtm.interpretResponse(responses, { bidderRequest }); + const emptyResponseResults = districtm.interpretResponse(emptyResponse, { bidderRequest }); + const emptyResponseResultsNegation = districtm.interpretResponse(responsesNegative, { bidderRequest }); + const emptyResponseResultsEmptySeat = districtm.interpretResponse(emptyResponseSeatBid, { bidderRequest }); it(`the function should return an array`, function () { expect(responseResults).to.be.an('array'); }); @@ -767,10 +771,10 @@ describe('DistrictM Adaptor', function () { }); describe(`check validation for id sync gdpr ccpa`, () => { - let allin = spec.getUserSyncs({iframeEnabled: true}, {}, bidderRequest.gdprConsent, bidderRequest.uspConsent)[0] - let noCCPA = spec.getUserSyncs({iframeEnabled: true}, {}, bidderRequest.gdprConsent, null)[0] - let noGDPR = spec.getUserSyncs({iframeEnabled: true}, {}, null, bidderRequest.uspConsent)[0] - let nothing = spec.getUserSyncs({iframeEnabled: true}, {}, null, null)[0] + let allin = spec.getUserSyncs({ iframeEnabled: true }, {}, bidderRequest.gdprConsent, bidderRequest.uspConsent)[0] + let noCCPA = spec.getUserSyncs({ iframeEnabled: true }, {}, bidderRequest.gdprConsent, null)[0] + let noGDPR = spec.getUserSyncs({ iframeEnabled: true }, {}, null, bidderRequest.uspConsent)[0] + let nothing = spec.getUserSyncs({ iframeEnabled: true }, {}, null, null)[0] /* @@ -793,10 +797,10 @@ describe('DistrictM Adaptor', function () { }) describe(`Helper function testing`, function () { - const bid = matchRequest('29a28a1bbc8a8d', {bidderRequest}); - const {width, height} = defaultSize(bid); + const bid = matchRequest('29a28a1bbc8a8d', { bidderRequest }); + const { width, height } = defaultSize(bid); it(`test matchRequest`, function () { - expect(matchRequest('29a28a1bbc8a8d', {bidderRequest})).to.be.an('object'); + expect(matchRequest('29a28a1bbc8a8d', { bidderRequest })).to.be.an('object'); }); it(`test checkDeepArray`, function () { expect(_.isEqual(checkDeepArray([728, 90]), [728, 90])).to.be.equal(true); @@ -811,4 +815,3 @@ describe('DistrictM Adaptor', function () { }); }); }); - From 1a76a461055da8c6f9cf4cc29ffb0006687e394c Mon Sep 17 00:00:00 2001 From: ix-certification Date: Mon, 2 Nov 2020 06:54:00 -0500 Subject: [PATCH 0324/1476] IX missing sizes testing and diagnosis (#5856) * Added support for Liveramp userId submodule * Fixing URL length for large requests * adding telemetry to missing sizes feature * adding markdown file with detectMissingSizes * example value update Co-authored-by: IX-Prebid-Support --- modules/ixBidAdapter.js | 186 ++++++++++++++++++++--- modules/ixBidAdapter.md | 20 +++ test/spec/modules/ixBidAdapter_spec.js | 199 +++++++++++++++++++++++-- 3 files changed, 374 insertions(+), 31 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 77d4220e59a..cb37e4735c5 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -235,9 +235,9 @@ function addUserEids(userEids, seenIdPartners, id, source, ixlPartnerName, rtiPa * * @param {array} validBidRequests A list of valid bid request config objects. * @param {object} bidderRequest An object containing other info like gdprConsent. - * @param {array} impressions List of impression objects describing the bids. + * @param {object} impressions An object containing a list of impression objects describing the bids for each transactionId * @param {array} version Endpoint version denoting banner or video. - * @return {object} Info describing the request to the server. + * @return {array} List of objects describing the request to the server. * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { @@ -280,11 +280,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Since bidderRequestId are the same for different bid request, just use the first one. r.id = validBidRequests[0].bidderRequestId; - r.imp = impressions; - r.site = {}; r.ext = {}; r.ext.source = 'prebid'; + r.ext.ixdiag = {}; // if an schain is provided, send it along if (validBidRequests[0].schain) { @@ -358,30 +357,140 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { if (typeof otherIxConfig.timeout === 'number') { payload.t = otherIxConfig.timeout; } + + if (typeof otherIxConfig.detectMissingSizes === 'boolean') { + r.ext.ixdiag.dms = otherIxConfig.detectMissingSizes; + } else { + r.ext.ixdiag.dms = true; + } } // Use the siteId in the first bid request as the main siteId. payload.s = validBidRequests[0].params.siteId; payload.v = version; - payload.r = JSON.stringify(r); payload.ac = 'j'; payload.sd = 1; if (version === VIDEO_ENDPOINT_VERSION) { payload.nf = 1; } - return { + const requests = []; + + const request = { method: 'GET', url: baseUrl, data: payload }; + + const BASE_REQ_SIZE = new Blob([`${request.url}${utils.parseQueryStringParameters({...request.data, r: JSON.stringify(r)})}`]).size; + let currReqSize = BASE_REQ_SIZE; + + const MAX_REQ_SIZE = 8000; + const MAX_REQ_LIMIT = 4; + let sn = 0; + let msi = 0; + let msd = 0; + r.ext.ixdiag.msd = 0; + r.ext.ixdiag.msi = 0; + r.imp = []; + let i = 0; + const transactionIds = Object.keys(impressions); + let currMissingImps = []; + + while (i < transactionIds.length && requests.length < MAX_REQ_LIMIT) { + if (impressions[transactionIds[i]].hasOwnProperty('missingCount')) { + msd = impressions[transactionIds[i]].missingCount; + } + + trimImpressions(impressions[transactionIds[i]], MAX_REQ_SIZE - BASE_REQ_SIZE); + + if (impressions[transactionIds[i]].hasOwnProperty('missingImps')) { + msi = impressions[transactionIds[i]].missingImps.length; + } + + let currImpsSize = new Blob([encodeURIComponent(JSON.stringify(impressions[transactionIds[i]]))]).size; + currReqSize += currImpsSize; + if (currReqSize < MAX_REQ_SIZE) { + // pushing ix configured sizes first + r.imp.push(...impressions[transactionIds[i]].ixImps); + // update msd msi + r.ext.ixdiag.msd += msd; + r.ext.ixdiag.msi += msi; + + if (impressions[transactionIds[i]].hasOwnProperty('missingImps')) { + currMissingImps.push(...impressions[transactionIds[i]].missingImps); + } + + i++; + } else { + // pushing missing sizes after configured ones + const clonedPayload = utils.deepClone(payload); + + r.imp.push(...currMissingImps); + r.ext.ixdiag.sn = sn; + clonedPayload.sn = sn; + sn++; + clonedPayload.r = JSON.stringify(r); + + requests.push({ + method: 'GET', + url: baseUrl, + data: clonedPayload + }); + currMissingImps = []; + currReqSize = BASE_REQ_SIZE; + r.imp = []; + msd = 0; + msi = 0; + r.ext.ixdiag.msd = 0; + r.ext.ixdiag.msi = 0; + } + } + + if (currReqSize > BASE_REQ_SIZE && currReqSize < MAX_REQ_SIZE && requests.length < MAX_REQ_LIMIT) { + const clonedPayload = utils.deepClone(payload); + r.imp.push(...currMissingImps); + + if (requests.length > 0) { + r.ext.ixdiag.sn = sn; + clonedPayload.sn = sn; + } + clonedPayload.r = JSON.stringify(r); + + requests.push({ + method: 'GET', + url: baseUrl, + data: clonedPayload + }); + } + + return requests; } +/** + * + * @param {Object} impressions containing ixImps and possibly missingImps + * + */ +function trimImpressions(impressions, maxSize) { + let currSize = new Blob([encodeURIComponent(JSON.stringify(impressions))]).size; + if (currSize < maxSize) { + return; + } + while (currSize > maxSize) { + if (impressions.hasOwnProperty('missingImps') && impressions.missingImps.length > 0) { + impressions.missingImps.pop(); + } else if (impressions.hasOwnProperty('ixImps') && impressions.ixImps.length > 0) { + impressions.ixImps.pop(); + } + currSize = new Blob([encodeURIComponent(JSON.stringify(impressions))]).size; + } +} /** * * @param {array} bannerSizeList list of banner sizes * @param {array} bannerSize the size to be removed - * @return {boolean} true if succesfully removed, false if not found + * @return {boolean} true if successfully removed, false if not found */ function removeFromSizes(bannerSizeList, bannerSize) { @@ -496,19 +605,32 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { let reqs = []; - let bannerImps = []; - let videoImps = []; + let bannerImps = {}; + let videoImps = {}; let validBidRequest = null; // To capture the missing sizes i.e not configured for ix let missingBannerSizes = {}; + const DEFAULT_IX_CONFIG = { + detectMissingSizes: true, + }; + + const ixConfig = {...DEFAULT_IX_CONFIG, ...config.getConfig('ix')}; + for (let i = 0; i < validBidRequests.length; i++) { validBidRequest = validBidRequests[i]; if (validBidRequest.mediaType === VIDEO || utils.deepAccess(validBidRequest, 'mediaTypes.video')) { if (validBidRequest.mediaType === VIDEO || includesSize(validBidRequest.mediaTypes.video.playerSize, validBidRequest.params.size)) { - videoImps.push(bidToVideoImp(validBidRequest)); + if (!videoImps.hasOwnProperty(validBidRequest.transactionId)) { + videoImps[validBidRequest.transactionId] = {}; + } + if (!videoImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { + videoImps[validBidRequest.transactionId].ixImps = []; + } + + videoImps[validBidRequest.transactionId].ixImps.push(bidToVideoImp(validBidRequest)); } else { utils.logError('Bid size is not included in video playerSize') } @@ -516,27 +638,47 @@ export const spec = { if (validBidRequest.mediaType === BANNER || utils.deepAccess(validBidRequest, 'mediaTypes.banner') || (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { let imp = bidToBannerImp(validBidRequest); - bannerImps.push(imp); - updateMissingSizes(validBidRequest, missingBannerSizes, imp); + + if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { + bannerImps[validBidRequest.transactionId] = {}; + } + if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { + bannerImps[validBidRequest.transactionId].ixImps = [] + } + bannerImps[validBidRequest.transactionId].ixImps.push(imp); + if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { + updateMissingSizes(validBidRequest, missingBannerSizes, imp); + } } } - // Finding the missing banner sizes ,and making impressions for them - for (var transactionID in missingBannerSizes) { - if (missingBannerSizes.hasOwnProperty(transactionID)) { - let missingSizes = missingBannerSizes[transactionID].missingSizes; + + // Finding the missing banner sizes, and making impressions for them + for (var transactionId in missingBannerSizes) { + if (missingBannerSizes.hasOwnProperty(transactionId)) { + let missingSizes = missingBannerSizes[transactionId].missingSizes; + + if (!bannerImps.hasOwnProperty(transactionId)) { + bannerImps[transactionId] = {}; + } + if (!bannerImps[transactionId].hasOwnProperty('missingImps')) { + bannerImps[transactionId].missingImps = []; + bannerImps[transactionId].missingCount = 0; + } + + let origImp = missingBannerSizes[transactionId].impression; for (let i = 0; i < missingSizes.length; i++) { - let origImp = missingBannerSizes[transactionID].impression; let newImp = createMissingBannerImp(origImp, missingSizes[i]); - bannerImps.push(newImp); + bannerImps[transactionId].missingImps.push(newImp); + bannerImps[transactionId].missingCount++; } } } - if (bannerImps.length > 0) { - reqs.push(buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); + if (Object.keys(bannerImps).length > 0) { + reqs.push(...buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); } - if (videoImps.length > 0) { - reqs.push(buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); + if (Object.keys(videoImps).length > 0) { + reqs.push(...buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); } return reqs; diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index b5cb0d9d2c1..c2e308870db 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -288,6 +288,26 @@ pbjs.setConfig({ } }); ``` +#### The **detectMissingSizes** feature +With a recent update, the IX bid adapter bids on all banner sizes available in an ad unit, if IX is configured for at least one banner size in that ad unit. This default behavior if not required, can be turned off by using the `detectMissingSizes` flag. +``` +pbjs.setConfig({ + ix: { + detectMissingSizes: false + } + }); +``` +OR +``` +pbjs.setBidderConfig({ + bidders: ["ix"], + config: { + ix: { + detectMissingSizes: false + } + } + }); +``` ### 2. Include `ixBidAdapter` in your build process diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 63b04077f4e..ad4f90b0397 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/ixBidAdapter.js'; -describe('IndexexchangeAdapter', function () { +describe.only('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/cygnus'; const VIDEO_ENDPOINT_VERSION = 8.1; const BANNER_ENDPOINT_VERSION = 7.2; @@ -18,7 +18,6 @@ describe('IndexexchangeAdapter', function () { 'sid': '00001', 'hp': 1 }, - { 'asi': 'indirectseller-2.com', 'sid': '00002', @@ -26,7 +25,74 @@ describe('IndexexchangeAdapter', function () { } ] }; - + var div_many_sizes = [ + [300, 250], + [600, 410], + [336, 280], + [400, 300], + [320, 50], + [360, 360], + [250, 250], + [320, 250], + [400, 250], + [387, 359], + [300, 50], + [372, 250], + [320, 320], + [412, 412], + [327, 272], + [312, 260], + [384, 320], + [335, 250], + [366, 305], + [374, 250], + [375, 375], + [272, 391], + [364, 303], + [414, 414], + [366, 375], + [272, 360], + [364, 373], + [366, 359], + [320, 100], + [360, 250], + [468, 60], + [480, 300], + [600, 400], + [600, 300], + [33, 28], + [40, 30], + [32, 5], + [36, 36], + [25, 25], + [320, 25], + [400, 25], + [387, 35], + [300, 5], + [372, 20], + [320, 32], + [412, 41], + [327, 27], + [312, 26], + [384, 32], + [335, 25], + [366, 30], + [374, 25], + [375, 37], + [272, 31], + [364, 303], + [414, 41], + [366, 35], + [272, 60], + [364, 73], + [366, 59], + [320, 10], + [360, 25], + [468, 6], + [480, 30], + [600, 40], + [600, 30] + ]; const DEFAULT_BANNER_VALID_BID = [ { bidder: 'ix', @@ -587,7 +653,6 @@ describe('IndexexchangeAdapter', function () { it('IX adapter reads LiveRamp IDL envelope from Prebid and adds it to Video', function () { const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA); - const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -972,6 +1037,97 @@ describe('IndexexchangeAdapter', function () { expect(videoImp.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); }); + it('single request under 8k size limit for large ad unit', function () { + const options = {}; + const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid1.mediaTypes.banner.sizes = div_many_sizes; + const requests = spec.buildRequests([bid1], options); + + const reqSize = new Blob([`${requests[0].url}?${utils.parseQueryStringParameters(requests[0].data)}`]).size; + expect(requests).to.be.an('array'); + expect(requests).to.have.lengthOf(1); + expect(reqSize).to.be.lessThan(8000); + }); + + it('2 requests due to 2 ad units, one larger than url size', function () { + const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid1.mediaTypes.banner.sizes = div_many_sizes; + bid1.params.siteId = '124'; + bid1.adUnitCode = 'div-gpt-1' + bid1.transactionId = '152e36d1-1241-4242-t35e-y1dv34d12315'; + bid1.bidId = '2f6g5s5e'; + + const requests = spec.buildRequests([bid1, DEFAULT_BANNER_VALID_BID[0]], DEFAULT_OPTION); + expect(requests).to.be.an('array'); + expect(requests).to.have.lengthOf(2); + expect(requests[0].data.sn).to.be.equal(0); + expect(requests[1].data.sn).to.be.equal(1); + }); + + it('6 ad units should generate only 4 requests', function () { + const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid1.mediaTypes.banner.sizes = div_many_sizes; + bid1.params.siteId = '121'; + bid1.adUnitCode = 'div-gpt-1' + bid1.transactionId = 'tr1'; + bid1.bidId = '2f6g5s5e'; + + const bid2 = utils.deepClone(bid1); + bid2.transactionId = 'tr2'; + + const bid3 = utils.deepClone(bid1); + bid3.transactionId = 'tr3'; + + const bid4 = utils.deepClone(bid1); + bid4.transactionId = 'tr4'; + + const bid5 = utils.deepClone(bid1); + bid5.transactionId = 'tr5'; + + const bid6 = utils.deepClone(bid1); + bid6.transactionId = 'tr6'; + + const requests = spec.buildRequests([bid1, bid2, bid3, bid4, bid5, bid6], DEFAULT_OPTION); + + expect(requests).to.be.an('array'); + expect(requests).to.have.lengthOf(4); + + // check if seq number increases + for (var i = 0; i < requests.length; i++) { + const reqSize = new Blob([`${requests[i].url}?${utils.parseQueryStringParameters(requests[i].data)}`]).size; + expect(reqSize).to.be.lessThan(8000); + let payload = JSON.parse(requests[i].data.r); + if (requests.length > 1) { + expect(requests[i].data.sn).to.equal(i); + } + expect(payload.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); + } + }); + + it('multiple ad units in one request', function () { + const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid1.mediaTypes.banner.sizes = [[300, 250], [300, 600], [100, 200]]; + bid1.params.siteId = '121'; + bid1.adUnitCode = 'div-gpt-1' + bid1.transactionId = 'tr1'; + bid1.bidId = '2f6g5s5e'; + + const bid2 = utils.deepClone(bid1); + bid2.transactionId = 'tr2'; + bid2.mediaTypes.banner.sizes = [[220, 221], [222, 223], [300, 250]]; + const bid3 = utils.deepClone(bid1); + bid3.transactionId = 'tr3'; + bid3.mediaTypes.banner.sizes = [[330, 331], [332, 333], [300, 250]]; + + const requests = spec.buildRequests([bid1, bid2, bid3], DEFAULT_OPTION); + expect(requests).to.be.an('array'); + expect(requests).to.have.lengthOf(1); + + const impressions = JSON.parse(requests[0].data.r).imp; + expect(impressions).to.be.an('array'); + expect(impressions).to.have.lengthOf(9); + }); + it('request should contain the extra banner ad sizes that IX is not configured for using the first site id in the ad unit', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.sizes.push([336, 280], [970, 90]); @@ -1017,23 +1173,26 @@ describe('IndexexchangeAdapter', function () { const impressions = JSON.parse(request.data.r).imp; expect(impressions).to.be.an('array'); expect(impressions).to.have.lengthOf(4); - - expect(impressions[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()) - expect(impressions[1].ext.siteID).to.equal(bid.params.siteId) - expect(impressions[2].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()) - expect(impressions[3].ext.siteID).to.equal(bid.params.siteId) + expect(impressions[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(impressions[1].ext.siteID).to.equal(bid.params.siteId); + expect(impressions[2].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(impressions[3].ext.siteID).to.equal(bid.params.siteId); expect(impressions[0].banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); expect(impressions[0].banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); + expect(impressions[1].banner.w).to.equal(bid.params.size[0]); expect(impressions[1].banner.h).to.equal(bid.params.size[1]); + expect(impressions[2].banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][0]); expect(impressions[2].banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][1]); + expect(impressions[3].banner.w).to.equal(bid.mediaTypes.banner.sizes[1][0]); expect(impressions[3].banner.h).to.equal(bid.mediaTypes.banner.sizes[1][1]); expect(impressions[0].ext.sid).to.equal(`${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`); expect(impressions[1].ext.sid).to.equal(`${bid.params.size[0].toString()}x${bid.params.size[1].toString()}`); + expect(impressions[2].ext.sid).to.equal(`${DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][0].toString()}x${DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][1].toString()}`); expect(impressions[3].ext.sid).to.equal(`${bid.mediaTypes.banner.sizes[1][0].toString()}x${bid.mediaTypes.banner.sizes[1][1].toString()}`); }); @@ -1045,6 +1204,28 @@ describe('IndexexchangeAdapter', function () { expect(impressions).to.be.an('array'); expect(impressions).to.have.lengthOf(1); }); + + describe('detect missing sizes', function () { + beforeEach(function () { + config.setConfig({ + ix: { + detectMissingSizes: false + } + }); + }) + + it('request should not contain missing sizes if detectMissingSizes = false', function () { + const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid1.mediaTypes.banner.sizes = div_many_sizes; + + const requests = spec.buildRequests([bid1, DEFAULT_BANNER_VALID_BID[0]], DEFAULT_OPTION); + + const impressions = JSON.parse(requests[0].data.r).imp; + + expect(impressions).to.be.an('array'); + expect(impressions).to.have.lengthOf(2); + }); + }); }); describe('buildRequestVideo', function () { From ae1b4d74ad9e6e2cbfe6035de329d2b6d89ceadd Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Mon, 2 Nov 2020 20:51:43 +0700 Subject: [PATCH 0325/1476] Add apacdex bid adapter & Merge valueimpression, quantumdex to apacdex (#5888) --- ...mdexBidAdapter.js => apacdexBidAdapter.js} | 12 ++- ...mdexBidAdapter.md => apacdexBidAdapter.md} | 16 ++-- ...pter_spec.js => apacdexBidAdapter_spec.js} | 74 +++++++++---------- 3 files changed, 53 insertions(+), 49 deletions(-) rename modules/{quantumdexBidAdapter.js => apacdexBidAdapter.js} (94%) rename modules/{quantumdexBidAdapter.md => apacdexBidAdapter.md} (56%) rename test/spec/modules/{quantumdexBidAdapter_spec.js => apacdexBidAdapter_spec.js} (92%) diff --git a/modules/quantumdexBidAdapter.js b/modules/apacdexBidAdapter.js similarity index 94% rename from modules/quantumdexBidAdapter.js rename to modules/apacdexBidAdapter.js index 738b6165f79..2582e4788c1 100644 --- a/modules/quantumdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -1,7 +1,11 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -const BIDDER_CODE = 'quantumdex'; +const BIDDER_CODE = 'apacdex'; const CONFIG = { + 'apacdex': { + 'ENDPOINT': 'https://useast.quantumdex.io/auction/apacdex', + 'USERSYNC': 'https://sync.quantumdex.io/usersync/apacdex' + }, 'quantumdex': { 'ENDPOINT': 'https://useast.quantumdex.io/auction/quantumdex', 'USERSYNC': 'https://sync.quantumdex.io/usersync/quantumdex' @@ -12,14 +16,14 @@ const CONFIG = { } }; -var bidderConfig = CONFIG['quantumdex']; +var bidderConfig = CONFIG[BIDDER_CODE]; var bySlotTargetKey = {}; var bySlotSizesCount = {} export const spec = { code: BIDDER_CODE, supportedMediaTypes: ['banner', 'video'], - aliases: ['valueimpression'], + aliases: ['quantumdex', 'valueimpression'], isBidRequestValid: function (bid) { if (!bid.params) { return false; @@ -30,7 +34,7 @@ export const spec = { if (!utils.deepAccess(bid, 'mediaTypes.banner') && !utils.deepAccess(bid, 'mediaTypes.video')) { return false; } - if (utils.deepAccess(bid, 'mediaTypes.banner')) { // Quantumdex does not support multi type bids, favor banner over video + if (utils.deepAccess(bid, 'mediaTypes.banner')) { // Not support multi type bids, favor banner over video if (!utils.deepAccess(bid, 'mediaTypes.banner.sizes')) { // sizes at the banner is required. return false; diff --git a/modules/quantumdexBidAdapter.md b/modules/apacdexBidAdapter.md similarity index 56% rename from modules/quantumdexBidAdapter.md rename to modules/apacdexBidAdapter.md index 8c35ea8cb05..b88190cda94 100644 --- a/modules/quantumdexBidAdapter.md +++ b/modules/apacdexBidAdapter.md @@ -1,15 +1,15 @@ # Overview ``` -Module Name: Quantum Digital Exchange Bidder Adapter +Module Name: APAC Digital Exchange Bidder Adapter Module Type: Bidder Adapter -Maintainer: ken@quantumdex.io +Maintainer: ken@apacdex.com ``` # Description -Connects to Quantum Digital Exchange for bids. -Quantumdex bid adapter supports Banner and Video (Instream and Outstream) ads. +Connects to APAC Digital Exchange for bids. +Apacdex bid adapter supports Banner and Video (Instream and Outstream) ads. # Test Parameters ``` @@ -23,9 +23,9 @@ var adUnits = [ }, bids: [ { - bidder: 'quantumdex', + bidder: 'apacdex', params: { - siteId: 'quantumdex-site-id', // siteId provided by Quantumdex + siteId: 'apacdex1234', // siteId provided by Apacdex } } ] @@ -46,9 +46,9 @@ var videoAdUnit = { }, bids: [ { - bidder: 'quantumdex', + bidder: 'apacdex', params: { - siteId: 'quantumdex-site-id', // siteId provided by Quantumdex + siteId: 'apacdex1234', // siteId provided by Apacdex } } ] diff --git a/test/spec/modules/quantumdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js similarity index 92% rename from test/spec/modules/quantumdexBidAdapter_spec.js rename to test/spec/modules/apacdexBidAdapter_spec.js index d1817493b36..da9a050a8de 100644 --- a/test/spec/modules/quantumdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -1,14 +1,14 @@ import { expect } from 'chai' -import { spec } from 'modules/quantumdexBidAdapter.js' +import { spec } from 'modules/apacdexBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; -describe('QuantumdexBidAdapter', function () { +describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) describe('.code', function () { - it('should return a bidder code of quantumdex', function () { - expect(spec.code).to.equal('quantumdex') + it('should return a bidder code of apacdex', function () { + expect(spec.code).to.equal('apacdex') }) }) @@ -21,7 +21,7 @@ describe('QuantumdexBidAdapter', function () { describe('.isBidRequestValid', function () { it('should return false if there are no params', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', 'mediaTypes': { banner: { @@ -37,7 +37,7 @@ describe('QuantumdexBidAdapter', function () { it('should return false if there is no siteId param', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { site_id: '1a2b3c4d5e6f1a2b3c4d', @@ -56,7 +56,7 @@ describe('QuantumdexBidAdapter', function () { it('should return false if there is no mediaTypes', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d' @@ -72,7 +72,7 @@ describe('QuantumdexBidAdapter', function () { it('should return true if the bid is valid', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d' @@ -92,7 +92,7 @@ describe('QuantumdexBidAdapter', function () { describe('banner', () => { it('should return false if there are no banner sizes', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d' @@ -111,7 +111,7 @@ describe('QuantumdexBidAdapter', function () { it('should return true if there is banner sizes', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d' @@ -132,7 +132,7 @@ describe('QuantumdexBidAdapter', function () { describe('video', () => { it('should return false if there is no playerSize defined in the video mediaType', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d', @@ -152,7 +152,7 @@ describe('QuantumdexBidAdapter', function () { it('should return true if there is playerSize defined on the video mediaType', () => { const bid = { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'adUnitCode': 'adunit-code', params: { siteId: '1a2b3c4d5e6f1a2b3c4d', @@ -196,7 +196,7 @@ describe('QuantumdexBidAdapter', function () { }, ] }, - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', }, @@ -206,7 +206,7 @@ describe('QuantumdexBidAdapter', function () { 'bidId': '30b31c1838de1f', }, { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'params': { 'ad_unit': '/7780971/sparks_prebid_LB', 'sizes': [[300, 250], [300, 600]], @@ -235,14 +235,14 @@ describe('QuantumdexBidAdapter', function () { it('should return a properly formatted request', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/apacdex') expect(bidRequests.method).to.equal('POST') expect(bidRequests.bidderRequests).to.eql(bidRequest); }) it('should return a properly formatted request with GDPR applies set to true', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/apacdex') expect(bidRequests.method).to.equal('POST') expect(bidRequests.data.gdpr.gdprApplies).to.equal(true) expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') @@ -251,7 +251,7 @@ describe('QuantumdexBidAdapter', function () { it('should return a properly formatted request with GDPR applies set to false', function () { bidderRequests.gdprConsent.gdprApplies = false; const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/apacdex') expect(bidRequests.method).to.equal('POST') expect(bidRequests.data.gdpr.gdprApplies).to.equal(false) expect(bidRequests.data.gdpr.consentString).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==') @@ -271,7 +271,7 @@ describe('QuantumdexBidAdapter', function () { } }; const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/apacdex') expect(bidRequests.method).to.equal('POST') expect(bidRequests.data.gdpr.gdprApplies).to.equal(false) expect(bidRequests.data.gdpr).to.not.include.keys('consentString') @@ -291,7 +291,7 @@ describe('QuantumdexBidAdapter', function () { } }; const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/quantumdex') + expect(bidRequests.url).to.equal('https://useast.quantumdex.io/auction/apacdex') expect(bidRequests.method).to.equal('POST') expect(bidRequests.data.gdpr.gdprApplies).to.equal(true) expect(bidRequests.data.gdpr).to.not.include.keys('consentString') @@ -309,7 +309,7 @@ describe('QuantumdexBidAdapter', function () { describe('.interpretResponse', function () { const bidRequests = { 'method': 'POST', - 'url': 'https://useast.quantumdex.io/auction/quantumdex', + 'url': 'https://useast.quantumdex.io/auction/apacdex', 'withCredentials': true, 'data': { 'device': { @@ -328,7 +328,7 @@ describe('QuantumdexBidAdapter', function () { }, 'bidderRequests': [ { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'params': { 'siteId': '343' }, @@ -363,7 +363,7 @@ describe('QuantumdexBidAdapter', function () { } }, { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'params': { 'siteId': '343' }, @@ -398,7 +398,7 @@ describe('QuantumdexBidAdapter', function () { } }, { - 'bidder': 'quantumdex', + 'bidder': 'apacdex', 'params': { 'siteId': '343' }, @@ -464,12 +464,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1.07, 'width': 160, 'height': 600, - 'ad': `
Quantumdex AD
`, + 'ad': `
Apacdex AD
`, 'ttl': 500, 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'banner' }, { @@ -477,12 +477,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1, 'width': 300, 'height': 250, - 'ad': `
Quantumdex AD
`, + 'ad': `
Apacdex AD
`, 'ttl': 500, 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'banner' }, { @@ -490,12 +490,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1.25, 'width': 300, 'height': 250, - 'vastXml': 'quantumdex', + 'vastXml': 'apacdex', 'ttl': 500, 'creativeId': '30292e432662bd5f86d90774b944b038', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'video' } ], @@ -512,12 +512,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1.07, 'width': 160, 'height': 600, - 'ad': `
Quantumdex AD
`, + 'ad': `
Apacdex AD
`, 'ttl': 500, 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'banner' }, { @@ -525,12 +525,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1, 'width': 300, 'height': 250, - 'ad': `
Quantumdex AD
`, + 'ad': `
Apacdex AD
`, 'ttl': 500, 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'banner' }, { @@ -538,12 +538,12 @@ describe('QuantumdexBidAdapter', function () { 'cpm': 1.25, 'width': 300, 'height': 250, - 'vastXml': 'quantumdex', + 'vastXml': 'apacdex', 'ttl': 500, 'creativeId': '30292e432662bd5f86d90774b944b038', 'netRevenue': true, 'currency': 'USD', - 'dealId': 'quantumdex', + 'dealId': 'apacdex', 'mediaType': 'video' } ]; @@ -561,10 +561,10 @@ describe('QuantumdexBidAdapter', function () { expect(resp.currency).to.equal(prebidResponse[i].currency); expect(resp.dealId).to.equal(prebidResponse[i].dealId); if (resp.mediaType === 'video') { - expect(resp.vastXml.indexOf('quantumdex')).to.be.greaterThan(0); + expect(resp.vastXml.indexOf('apacdex')).to.be.greaterThan(0); } if (resp.mediaType === 'banner') { - expect(resp.ad.indexOf('Quantumdex AD')).to.be.greaterThan(0); + expect(resp.ad.indexOf('Apacdex AD')).to.be.greaterThan(0); } }); }); From 640a3cd0d7fd6d601694455d34f99be7af306393 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 2 Nov 2020 21:09:24 +0200 Subject: [PATCH 0326/1476] Adkernel: basic meta forwarding (#5836) --- modules/adkernelBidAdapter.js | 26 +++++++++++++++++--- test/spec/modules/adkernelBidAdapter_spec.js | 20 +++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 972dd696bf6..d489a1a84e0 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -116,13 +116,10 @@ export const spec = { requestId: rtbBid.impid, cpm: rtbBid.price, creativeId: rtbBid.crid, - currency: 'USD', + currency: response.cur || 'USD', ttl: 360, netRevenue: true }; - if (rtbBid.dealid !== undefined) { - prBid.dealId = rtbBid.dealid; - } if ('banner' in imp) { prBid.mediaType = BANNER; prBid.width = rtbBid.w; @@ -137,6 +134,27 @@ export const spec = { prBid.mediaType = NATIVE; prBid.native = buildNativeAd(JSON.parse(rtbBid.adm)); } + if (utils.isStr(rtbBid.dealid)) { + prBid.dealId = rtbBid.dealid; + } + if (utils.isArray(rtbBid.adomain)) { + utils.deepSetValue(prBid, 'meta.advertiserDomains', rtbBid.adomain); + } + if (utils.isArray(rtbBid.cat)) { + utils.deepSetValue(prBid, 'meta.secondaryCatIds', rtbBid.cat); + } + if (utils.isPlainObject(rtbBid.ext)) { + if (utils.isNumber(rtbBid.ext.advertiser_id)) { + utils.deepSetValue(prBid, 'meta.advertiserId', rtbBid.ext.advertiser_id); + } + if (utils.isStr(rtbBid.ext.advertiser_name)) { + utils.deepSetValue(prBid, 'meta.advertiserName', rtbBid.ext.advertiser_name); + } + if (utils.isStr(rtbBid.ext.agency_name)) { + utils.deepSetValue(prBid, 'meta.agencyName', rtbBid.ext.agency_name); + } + } + return prBid; }); }, diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 87504aa46af..1483816d94d 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -175,7 +175,6 @@ describe('Adkernel adapter', function () { dealid: 'deal' }] }], - cur: 'USD', ext: { adk_usersync: [{type: 1, url: 'https://adk.sync.com/sync'}] } @@ -192,7 +191,6 @@ describe('Adkernel adapter', function () { cid: '16855' }] }], - cur: 'USD' }, usersyncOnlyResponse = { id: 'nobid1', ext: { @@ -222,12 +220,18 @@ describe('Adkernel adapter', function () { } }), adomain: ['displayurl.com'], + cat: ['IAB1-4', 'IAB8-16', 'IAB25-5'], cid: '1', - crid: '4' + crid: '4', + ext: { + 'advertiser_id': 777, + 'advertiser_name': 'advertiser', + 'agency_name': 'agency' + } }] }], bidid: 'pTuOlf5KHUo', - cur: 'USD' + cur: 'EUR' }; var sandbox; @@ -587,7 +591,13 @@ describe('Adkernel adapter', function () { let resp = spec.interpretResponse({body: nativeResponse}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_01'); expect(resp).to.have.property('cpm', 2.25); - expect(resp).to.have.property('currency', 'USD'); + expect(resp).to.have.property('currency', 'EUR'); + expect(resp).to.have.property('meta'); + expect(resp.meta.advertiserId).to.be.eql(777); + expect(resp.meta.advertiserName).to.be.eql('advertiser'); + expect(resp.meta.agencyName).to.be.eql('agency'); + expect(resp.meta.advertiserDomains).to.be.eql(['displayurl.com']); + expect(resp.meta.secondaryCatIds).to.be.eql(['IAB1-4', 'IAB8-16', 'IAB25-5']); expect(resp).to.have.property('mediaType', NATIVE); expect(resp).to.have.property('native'); expect(resp.native).to.have.property('clickUrl', 'http://rtb.com/click?i=pTuOlf5KHUo_0'); From 78d51176d60686b308696bf81f94dbc23952b868 Mon Sep 17 00:00:00 2001 From: John Salis Date: Mon, 2 Nov 2020 14:09:58 -0500 Subject: [PATCH 0327/1476] Add skip params to Beachfront adapter (#5847) * feat: add skip params and standard params to video bid request * refactor: add props to exclude list * refactor: bump adapter version Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 23 +++++++++++------- .../spec/modules/beachfrontBidAdapter_spec.js | 24 +++++++++++++++---- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 12e78c684ad..f0e28956e5d 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.11'; +const ADAPTER_VERSION = '1.12'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; @@ -14,7 +14,7 @@ export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id='; export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display'; export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; -export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement']; +export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement', 'skip', 'skipmin', 'skipafter']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; let appId = ''; @@ -258,12 +258,19 @@ function getTopWindowReferrer() { } function getVideoTargetingParams(bid) { - return Object.keys(Object(bid.params.video)) - .filter(param => includes(VIDEO_TARGETING, param)) - .reduce((obj, param) => { - obj[ param ] = bid.params.video[ param ]; - return obj; - }, {}); + const result = {}; + const excludeProps = ['playerSize', 'context', 'w', 'h']; + Object.keys(Object(bid.mediaTypes.video)) + .filter(key => !includes(excludeProps, key)) + .forEach(key => { + result[ key ] = bid.mediaTypes.video[ key ]; + }); + Object.keys(Object(bid.params.video)) + .filter(key => includes(VIDEO_TARGETING, key)) + .forEach(key => { + result[ key ] = bid.params.video[ key ]; + }); + return result; } function createVideoRequestData(bid, bidderRequest) { diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 5711e111e30..587596eaa5c 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -223,17 +223,33 @@ describe('BeachfrontAdapter', function () { expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); - it('must override video targeting params', function () { + it('must set video params from the standard object', function () { const bidRequest = bidRequests[0]; const mimes = ['video/webm']; const playbackmethod = 2; const maxduration = 30; const placement = 4; - bidRequest.mediaTypes = { video: {} }; - bidRequest.params.video = { mimes, playbackmethod, maxduration, placement }; + const skip = 1; + bidRequest.mediaTypes = { + video: { mimes, playbackmethod, maxduration, placement, skip } + }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, placement, skip }); + }); + + it('must override video params from the bidder object', function () { + const bidRequest = bidRequests[0]; + const mimes = ['video/webm']; + const playbackmethod = 2; + const maxduration = 30; + const placement = 4; + const skip = 1; + bidRequest.mediaTypes = { video: { placement: 3, skip: 0 } }; + bidRequest.params.video = { mimes, playbackmethod, maxduration, placement, skip }; const requests = spec.buildRequests([ bidRequest ]); const data = requests[0].data; - expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, placement }); + expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, placement, skip }); }); it('must add US privacy data to the request', function () { From 39df31f88071f5827671accdbc7dc9be1605b631 Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Mon, 2 Nov 2020 14:10:24 -0500 Subject: [PATCH 0328/1476] AMX RTB: improve URL handling in request (#5905) --- modules/amxBidAdapter.js | 24 +++++++++++---- test/spec/modules/amxBidAdapter_spec.js | 39 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 2e9529b633c..a1fa202c154 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -6,16 +6,28 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'amx'; const storage = getStorageManager(737, BIDDER_CODE); -const SIMPLE_TLD_TEST = /\.co\.\w{2,4}$/; +const SIMPLE_TLD_TEST = /\.com?\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; -const VERSION = 'pba1.2'; +const VERSION = 'pba1.2.1'; const xmlDTDRxp = /^\s*<\?xml[^\?]+\?>/; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; const AMUID_KEY = '__amuidpb'; -const getLocation = (request) => - parseUrl(deepAccess(request, 'refererInfo.canonicalUrl', location.href)) +function getLocation (request) { + const refInfo = request.refererInfo; + if (refInfo == null) { + return parseUrl(location.href); + } + + if (refInfo.isAmp && refInfo.referer != null) { + return parseUrl(refInfo.referer) + } + + const topUrl = refInfo.numIframes > 0 && refInfo.stack[0] != null + ? refInfo.stack[0] : location.href; + return parseUrl(topUrl); +}; const largestSize = (sizes, mediaTypes) => { const allSizes = sizes @@ -44,7 +56,7 @@ const nullOrType = (value, type) => function getID(loc) { const host = loc.hostname.split('.'); const short = host.slice( - host.length - (SIMPLE_TLD_TEST.test(loc.host) ? 3 : 2) + host.length - (SIMPLE_TLD_TEST.test(loc.hostname) ? 3 : 2) ).join('.'); return btoa(short).replace(/=+$/, ''); } @@ -239,7 +251,7 @@ export const spec = { gs: deepAccess(bidderRequest, 'gdprConsent.gdprApplies', ''), gc: deepAccess(bidderRequest, 'gdprConsent.consentString', ''), u: deepAccess(bidderRequest, 'refererInfo.canonicalUrl', loc.href), - do: loc.host, + do: loc.hostname, re: deepAccess(bidderRequest, 'refererInfo.referer'), am: getUIDSafe(), usp: bidderRequest.uspConsent || '1---', diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 91315da8801..766045b0f3e 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -189,6 +189,45 @@ describe('AmxBidAdapter', () => { expect(data.tm).to.equal(true); }); + it('if prebid is in an iframe, will use the frame url as domain, if the topmost is not avialable', () => { + const { data } = spec.buildRequests([sampleBidRequestBase], { + ...sampleBidderRequest, + refererInfo: { + numIframes: 1, + referer: 'http://search-traffic-source.com', + stack: [] + } + }); + expect(data.do).to.equal('localhost') + expect(data.re).to.equal('http://search-traffic-source.com'); + }); + + it('if we are in AMP, make sure we use the canonical URL or the referrer (which is sourceUrl)', () => { + const { data } = spec.buildRequests([sampleBidRequestBase], { + ...sampleBidderRequest, + refererInfo: { + isAmp: true, + referer: 'http://real-publisher-site.com/content', + stack: [] + } + }); + expect(data.do).to.equal('real-publisher-site.com') + expect(data.re).to.equal('http://real-publisher-site.com/content'); + }) + + it('if prebid is in an iframe, will use the topmost url as domain', () => { + const { data } = spec.buildRequests([sampleBidRequestBase], { + ...sampleBidderRequest, + refererInfo: { + numIframes: 1, + referer: 'http://search-traffic-source.com', + stack: ['http://top-site.com', 'http://iframe.com'] + } + }); + expect(data.do).to.equal('top-site.com'); + expect(data.re).to.equal('http://search-traffic-source.com'); + }); + it('handles referer data and GDPR, USP Consent, COPPA', () => { const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); delete data.m; // don't deal with "m" in this test From d19f4ca5dcc511fb5169f4f049a7b9b63d2706c7 Mon Sep 17 00:00:00 2001 From: gpolaert Date: Mon, 2 Nov 2020 20:39:38 +0100 Subject: [PATCH 0329/1476] feat: add the elapsed time to events for debugging (#5868) * feat: add the elapsed time to events for debugging * naming --- src/events.js | 3 ++- src/utils.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/events.js b/src/events.js index e7a11635476..8749ddf206b 100644 --- a/src/events.js +++ b/src/events.js @@ -44,7 +44,8 @@ module.exports = (function () { eventsFired.push({ eventType: eventString, args: eventPayload, - id: key + id: key, + elapsedTime: utils.getPerformanceNow(), }); /** Push each specific callback to the `callbacks` array. diff --git a/src/utils.js b/src/utils.js index 8af7a25668d..acdf0f101ad 100644 --- a/src/utils.js +++ b/src/utils.js @@ -727,6 +727,14 @@ export function timestamp() { return new Date().getTime(); } +/** + * The returned value represents the time elapsed since the time origin. @see https://developer.mozilla.org/en-US/docs/Web/API/Performance/now + * @returns {number} + */ +export function getPerformanceNow() { + return (window.performance && window.performance.now && window.performance.now()) || 0; +} + /** * When the deviceAccess flag config option is false, no cookies should be read or set * @returns {boolean} From 024cac8e02fe4dbc899edfab3c3cc5506a82e18d Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Tue, 3 Nov 2020 15:53:04 +0530 Subject: [PATCH 0330/1476] remove 'only' to run all tests (#5926) --- test/spec/modules/ixBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index ad4f90b0397..7ac4bd94f9d 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/ixBidAdapter.js'; -describe.only('IndexexchangeAdapter', function () { +describe('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/cygnus'; const VIDEO_ENDPOINT_VERSION = 8.1; const BANNER_ENDPOINT_VERSION = 7.2; From 07ec2bc4117672959ee81082d4e8956ea3a37f6f Mon Sep 17 00:00:00 2001 From: Nicholas Colletti <34142758+ncolletti@users.noreply.github.com> Date: Tue, 3 Nov 2020 10:24:10 -0500 Subject: [PATCH 0331/1476] Add Auction Options Config (#5787) * feature/auction-timing * rename to auctionOptions * move filtering outside of loop and organized logic. * remove auctionOptions test page --- src/auction.js | 14 +++- src/config.js | 34 +++++++++ test/spec/auctionmanager_spec.js | 115 +++++++++++++++++++++++++++++++ test/spec/config_spec.js | 33 +++++++++ 4 files changed, 194 insertions(+), 2 deletions(-) diff --git a/src/auction.js b/src/auction.js index e0b8feb5c62..6285bfdd905 100644 --- a/src/auction.js +++ b/src/auction.js @@ -66,6 +66,7 @@ import { config } from './config.js'; import { userSync } from './userSync.js'; import { hook } from './hook.js'; import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; import { OUTSTREAM } from './video.js'; import { VIDEO } from './mediaTypes.js'; @@ -397,10 +398,19 @@ export function auctionCallbacks(auctionDone, auctionInstance) { function adapterDone() { let bidderRequest = this; + let bidderRequests = auctionInstance.getBidRequests(); + const auctionOptionsConfig = config.getConfig('auctionOptions'); bidderRequestsDone.add(bidderRequest); - allAdapterCalledDone = auctionInstance.getBidRequests() - .every(bidderRequest => bidderRequestsDone.has(bidderRequest)); + + if (auctionOptionsConfig && !utils.isEmpty(auctionOptionsConfig)) { + const secondaryBidders = auctionOptionsConfig.secondaryBidders; + if (secondaryBidders && !bidderRequests.every(bidder => includes(secondaryBidders, bidder.bidderCode))) { + bidderRequests = bidderRequests.filter(request => !includes(secondaryBidders, request.bidderCode)); + } + } + + allAdapterCalledDone = bidderRequests.every(bidderRequest => bidderRequestsDone.has(bidderRequest)); bidderRequest.bids.forEach(bid => { if (!bidResponseMap[bid.bidId]) { diff --git a/src/config.js b/src/config.js index 3284be52296..daaf739bbbd 100644 --- a/src/config.js +++ b/src/config.js @@ -199,6 +199,16 @@ export function newConfig() { set disableAjaxTimeout(val) { this._disableAjaxTimeout = val; }, + + _auctionOptions: {}, + get auctionOptions() { + return this._auctionOptions; + }, + set auctionOptions(val) { + if (validateauctionOptions(val)) { + this._auctionOptions = val; + } + }, }; if (config) { @@ -237,6 +247,30 @@ export function newConfig() { } return true; } + + function validateauctionOptions(val) { + if (!utils.isPlainObject(val)) { + utils.logWarn('Auction Options must be an object') + return false + } + + for (let k of Object.keys(val)) { + if (k !== 'secondaryBidders') { + utils.logWarn(`Auction Options given an incorrect param: ${k}`) + return false + } + if (k === 'secondaryBidders') { + if (!utils.isArray(val[k])) { + utils.logWarn(`Auction Options ${k} must be of type Array`); + return false + } else if (!val[k].every(utils.isStr)) { + utils.logWarn(`Auction Options ${k} must be only string`); + return false + } + } + } + return true; + } } /** diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 1517b5d69d8..d880ff0eaee 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -1282,4 +1282,119 @@ describe('auctionmanager.js', function () { assert.equal(doneSpy.callCount, 1); }) }); + + describe('auctionOptions', function() { + let bidRequests; + let doneSpy; + let clock; + let auction = { + getBidRequests: () => bidRequests, + getAuctionId: () => '1', + addBidReceived: () => true, + getTimeout: () => 1000 + } + let requiredBidder = BIDDER_CODE; + let requiredBidder1 = BIDDER_CODE1; + let secondaryBidder = 'doNotWaitForMe'; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + doneSpy = sinon.spy(); + config.setConfig({ + 'auctionOptions': { + secondaryBidders: [ secondaryBidder ] + } + }) + }); + + afterEach(() => { + doneSpy.resetHistory(); + config.resetConfig(); + clock.restore(); + }); + + it('should not wait to call auction done for secondary bidders', function () { + let bids1 = [mockBid({ bidderCode: requiredBidder })]; + let bids2 = [mockBid({ bidderCode: requiredBidder1 })]; + let bids3 = [mockBid({ bidderCode: secondaryBidder })]; + bidRequests = [ + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), + ]; + let cbs = auctionCallbacks(doneSpy, auction); + // required bidder responds immeaditely to auction + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]); + cbs.adapterDone.call(bidRequests[0]); + assert.equal(doneSpy.callCount, 0); + + // auction waits for second required bidder to respond + clock.tick(100); + cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]); + cbs.adapterDone.call(bidRequests[1]); + + // auction done is reported and does not wait for secondaryBidder request + assert.equal(doneSpy.callCount, 1); + + cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]); + cbs.adapterDone.call(bidRequests[2]); + }); + + it('should wait for all bidders if they are all secondary', function () { + config.setConfig({ + 'auctionOptions': { + secondaryBidders: [requiredBidder, requiredBidder1, secondaryBidder] + } + }) + let bids1 = [mockBid({ bidderCode: requiredBidder })]; + let bids2 = [mockBid({ bidderCode: requiredBidder1 })]; + let bids3 = [mockBid({ bidderCode: secondaryBidder })]; + bidRequests = [ + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), + ]; + let cbs = auctionCallbacks(doneSpy, auction); + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]); + cbs.adapterDone.call(bidRequests[0]); + clock.tick(100); + assert.equal(doneSpy.callCount, 0) + + cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]); + cbs.adapterDone.call(bidRequests[1]); + clock.tick(100); + assert.equal(doneSpy.callCount, 0); + + cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]); + cbs.adapterDone.call(bidRequests[2]); + assert.equal(doneSpy.callCount, 1); + }); + + it('should allow secondaryBidders to respond in auction before is is done', function () { + let bids1 = [mockBid({ bidderCode: requiredBidder })]; + let bids2 = [mockBid({ bidderCode: requiredBidder1 })]; + let bids3 = [mockBid({ bidderCode: secondaryBidder })]; + bidRequests = [ + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), + ]; + let cbs = auctionCallbacks(doneSpy, auction); + // secondaryBidder is first to respond + cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]); + cbs.adapterDone.call(bidRequests[2]); + clock.tick(100); + assert.equal(doneSpy.callCount, 0); + + cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]); + cbs.adapterDone.call(bidRequests[1]); + clock.tick(100); + assert.equal(doneSpy.callCount, 0); + + // first required bidder takes longest to respond, auction isn't marked as done until this occurs + cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]); + cbs.adapterDone.call(bidRequests[0]); + assert.equal(doneSpy.callCount, 1); + }); + }); }); diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index be5b4bbb78b..81ce966efb2 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -211,4 +211,37 @@ describe('config API', function () { setConfig({ bidderSequence: 'random' }); expect(logWarnSpy.called).to.equal(false); }); + + it('sets auctionOptions', function () { + const auctionOptionsConfig = { + 'secondaryBidders': ['rubicon', 'appnexus'] + } + setConfig({ auctionOptions: auctionOptionsConfig }); + expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); + }); + + it('should log warning for the wrong value passed to auctionOptions', function () { + setConfig({ auctionOptions: '' }); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options must be an object'; + assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); + }); + + it('should log warning for invalid auctionOptions bidder values', function () { + setConfig({ auctionOptions: { + 'secondaryBidders': 'appnexus, rubicon', + }}); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options secondaryBidders must be of type Array'; + assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); + }); + + it('should log warning for invalid properties to auctionOptions', function () { + setConfig({ auctionOptions: { + 'testing': true + }}); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options given an incorrect param: testing'; + assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); + }); }); From 84649e8c3dc836d65a9efccfb0b518c952eb3279 Mon Sep 17 00:00:00 2001 From: sdao-tl <49252703+sdao-tl@users.noreply.github.com> Date: Tue, 3 Nov 2020 07:29:16 -0800 Subject: [PATCH 0332/1476] TL: Add GVLID, update validation method, add unit tests (#5904) * 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 * TripleLift: Sending schain (#1) * Sending schain * null -> undefined * Hardcode sync endpoint protocol * Switch to EB2 sync endpoint * Add support for image based user syncing * Rename endpoint variable * Add assertion * Add CCPA query param * Simplify check for usPrivacy argument * put advertiser name in the bid.meta field if it exists * update unit tests with meta.advertiserName field * Triplelift: FPD key value pair support (#5) * Triplelift: Add support for global fpd * don't filter fpd * adds coppa support back in * add gvlid, update validation method, add unit tests * remove advertiserDomains logic * typo * update _buildResponseObject to use new instream validation Co-authored-by: Will Chapin Co-authored-by: colbertk <50499465+colbertk@users.noreply.github.com> Co-authored-by: David Andersen Co-authored-by: Brandon Ling Co-authored-by: colbertk Co-authored-by: Kevin Zhou Co-authored-by: kzhouTL <43545828+kzhouTL@users.noreply.github.com> Co-authored-by: Sy Dao --- modules/tripleliftBidAdapter.js | 9 +- .../spec/modules/tripleliftBidAdapter_spec.js | 167 +++++++++++++++++- 2 files changed, 165 insertions(+), 11 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 77a313a0f55..69c52711236 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -3,20 +3,17 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +const GVLID = 28; const BIDDER_CODE = 'triplelift'; const STR_ENDPOINT = 'https://tlx.3lift.com/header/auction?'; let gdprApplies = true; let consentString = null; export const tripleliftAdapterSpec = { - + gvlid: GVLID, code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { - if (bid.mediaTypes.video) { - let video = _getORTBVideo(bid); - if (!video.w || !video.h) return false; - } return typeof bid.params.inventoryCode !== 'undefined'; }, @@ -288,7 +285,7 @@ function _buildResponseObject(bidderRequest, bid) { meta: {} }; - if (breq.mediaTypes.video) { + if (_isInstreamBidRequest(breq)) { bidResponse.vastXml = bid.ad; bidResponse.mediaType = 'video'; }; diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 96cab3d837c..f01949755c7 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -83,15 +83,21 @@ describe('triplelift adapter', function () { expect(tripleliftAdapterSpec.isBidRequestValid(instreamBid)).to.equal(true); }); + it('should return true when required params found - instream - 2', function () { + delete instreamBid.mediaTypes.playerSize; + delete instreamBid.params.video.w; + delete instreamBid.params.video.h; + // the only required param is inventoryCode + expect(tripleliftAdapterSpec.isBidRequestValid(instreamBid)).to.equal(true); + }); + it('should return false when required params are not passed', function () { delete bid.params.inventoryCode; expect(tripleliftAdapterSpec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when required params are not passed - instream', function () { - delete instreamBid.mediaTypes.playerSize; - delete instreamBid.params.video.w; - delete instreamBid.params.video.h; + delete instreamBid.params.inventoryCode; expect(tripleliftAdapterSpec.isBidRequestValid(instreamBid)).to.equal(false); }); }); @@ -165,6 +171,7 @@ describe('triplelift adapter', function () { userId: {}, schain, }, + // banner and outstream video { bidder: 'triplelift', params: { @@ -197,6 +204,140 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + }, + // banner and incomplete video + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + + }, + 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, + }, + // incomplete banner and incomplete video + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + + }, + banner: { + + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // banner and instream video + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + }, + 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, + }, + // banner and outream video and native + { + 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] + }, + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + }, + native: { + + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, } ]; @@ -261,10 +402,26 @@ describe('triplelift adapter', function () { 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'); - + // banner and outstream video expect(payload.imp[2]).to.not.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}]); + // banner and incomplete video + expect(payload.imp[3]).to.not.have.property('video'); + expect(payload.imp[3]).to.have.property('banner'); + expect(payload.imp[3].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + // incomplete mediatypes.banner and incomplete video + expect(payload.imp[4]).to.not.have.property('video'); + expect(payload.imp[4]).to.have.property('banner'); + expect(payload.imp[4].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + // banner and instream video + 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'); + // banner and outream video and native + expect(payload.imp[6]).to.not.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}]); }); it('should add tdid to the payload if included', function () { @@ -587,7 +744,7 @@ describe('triplelift adapter', function () { it('should include the advertiser name in the meta field if available', function () { let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result[0].meta.advertiserName).to.equal('fake advertiser name') + expect(result[0].meta.advertiserName).to.equal('fake advertiser name'); expect(result[1].meta).to.not.have.key('advertiserName'); }); }); From 38a1c8edc8e6adefb81fd6a096aa511404f50305 Mon Sep 17 00:00:00 2001 From: harpere Date: Tue, 3 Nov 2020 14:44:31 -0500 Subject: [PATCH 0333/1476] rubicon - support all userIds (#5923) * rubicon - support all userIds * rubicon - support all userIds update * rubicon update to userId logic Co-authored-by: Eric Harper --- modules/rubiconBidAdapter.js | 107 +++++++++----------- test/spec/modules/rubiconBidAdapter_spec.js | 18 ++-- 2 files changed, 52 insertions(+), 73 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 5d631d2b08e..b44ae108b38 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -238,15 +238,6 @@ export const spec = { const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); if (eids && eids.length) { utils.deepSetValue(data, 'user.ext.eids', eids); - - // liveintent requires additional props to be set - const liveIntentEid = find(data.user.ext.eids, eid => eid.source === 'liveintent.com'); - if (liveIntentEid) { - utils.deepSetValue(data, 'user.ext.tpid', { source: liveIntentEid.source, uid: liveIntentEid.uids[0].id }); - if (liveIntentEid.ext && liveIntentEid.ext.segments) { - utils.deepSetValue(data, 'rp.target.LIseg', liveIntentEid.ext.segments); - } - } } // set user.id value from config value @@ -374,6 +365,7 @@ export const spec = { getOrderedParams: function(params) { const containsTgV = /^tg_v/ const containsTgI = /^tg_i/ + const containsUId = /^eid_|^tpid_/ const orderedParams = [ 'account_id', @@ -386,18 +378,15 @@ export const spec = { 'gdpr_consent', 'us_privacy', 'rp_schain', - 'tpid_tdid', - 'tpid_liveintent.com', - 'tg_v.LIseg', - 'ppuid', - 'eid_pubcid.org', - 'eid_sharedid.org', - 'eid_criteo.com', - 'rf', - 'p_geo.latitude', - 'p_geo.longitude', - 'kw' - ].concat(Object.keys(params).filter(item => containsTgV.test(item))) + ].concat(Object.keys(params).filter(item => containsUId.test(item))) + .concat([ + 'x_liverampidl', + 'ppuid', + 'rf', + 'p_geo.latitude', + 'p_geo.longitude', + 'kw' + ]).concat(Object.keys(params).filter(item => containsTgV.test(item))) .concat(Object.keys(params).filter(item => containsTgI.test(item))) .concat([ 'tk_flint', @@ -503,51 +492,47 @@ export const spec = { // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; - if (bidRequest.userIdAsEids && bidRequest.userIdAsEids.length) { - const unifiedId = find(bidRequest.userIdAsEids, eid => eid.source === 'adserver.org'); - if (unifiedId) { - data['tpid_tdid'] = unifiedId.uids[0].id; - } - const liveintentId = find(bidRequest.userIdAsEids, eid => eid.source === 'liveintent.com'); - if (liveintentId) { - data['tpid_liveintent.com'] = liveintentId.uids[0].id; - if (liveintentId.ext && Array.isArray(liveintentId.ext.segments) && liveintentId.ext.segments.length) { - data['tg_v.LIseg'] = liveintentId.ext.segments.join(','); - } - } - const liverampId = find(bidRequest.userIdAsEids, eid => eid.source === 'liveramp.com'); - if (liverampId) { - data['x_liverampidl'] = liverampId.uids[0].id; - } - const sharedId = find(bidRequest.userIdAsEids, eid => eid.source === 'sharedid.org'); - if (sharedId) { - data['eid_sharedid.org'] = `${sharedId.uids[0].id}^${sharedId.uids[0].atype}^${sharedId.uids[0].ext.third}`; - } - const pubcid = find(bidRequest.userIdAsEids, eid => eid.source === 'pubcid.org'); - if (pubcid) { - data['eid_pubcid.org'] = `${pubcid.uids[0].id}^${pubcid.uids[0].atype}`; - } - const criteoId = find(bidRequest.userIdAsEids, eid => eid.source === 'criteo.com'); - if (criteoId) { - data['eid_criteo.com'] = `${criteoId.uids[0].id}^${criteoId.uids[0].atype}`; - } - } - - // set ppuid value from config value + // pass publisher provided userId if configured const configUserId = config.getConfig('user.id'); if (configUserId) { data['ppuid'] = configUserId; - } else { - // if config.getConfig('user.id') doesn't return anything, then look for the first eid.uids[*].ext.stype === 'ppuid' - for (let i = 0; bidRequest.userIdAsEids && i < bidRequest.userIdAsEids.length; i++) { - if (bidRequest.userIdAsEids[i].uids) { - const pubProvidedId = find(bidRequest.userIdAsEids[i].uids, uid => uid.ext && uid.ext.stype === 'ppuid'); - if (pubProvidedId && pubProvidedId.id) { - data['ppuid'] = pubProvidedId.id; - break; + } + // loop through userIds and add to request + if (bidRequest.userIdAsEids) { + bidRequest.userIdAsEids.forEach(eid => { + try { + // special cases + if (eid.source === 'adserver.org') { + data['tpid_tdid'] = eid.uids[0].id; + data['eid_adserver.org'] = eid.uids[0].id; + } else if (eid.source === 'liveintent.com') { + data['tpid_liveintent.com'] = eid.uids[0].id; + data['eid_liveintent.com'] = eid.uids[0].id; + if (eid.ext && Array.isArray(eid.ext.segments) && eid.ext.segments.length) { + data['tg_v.LIseg'] = eid.ext.segments.join(','); + } + } else if (eid.source === 'liveramp.com') { + data['x_liverampidl'] = eid.uids[0].id; + } else if (eid.source === 'sharedid.org') { + data['eid_sharedid.org'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.uids[0].ext && eid.uids[0].ext.third) || ''}`; + } else if (eid.source === 'id5-sync.com') { + data['eid_id5-sync.com'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.ext && eid.ext.linkType) || ''}`; + } else { + // add anything else with this generic format + data[`eid_${eid.source}`] = `${eid.uids[0].id}^${eid.uids[0].atype || ''}`; } + // send AE "ppuid" signal if exists, and hasn't already been sent + if (!data['ppuid']) { + // get the first eid.uids[*].ext.stype === 'ppuid', if one exists + const ppId = find(eid.uids, uid => uid.ext && uid.ext.stype === 'ppuid'); + if (ppId && ppId.id) { + data['ppuid'] = ppId.id; + } + } + } catch (e) { + utils.logWarn('Rubicon: error reading eid:', eid, e); } - } + }); } if (bidderRequest.gdprConsent) { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 920a2fb2502..6659c281c33 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -246,7 +246,7 @@ describe('the rubicon adapter', function () { id: '4444444' }] }], - criteoId: '1111' + criteoId: '1111', }; bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; @@ -1097,6 +1097,7 @@ describe('the rubicon adapter', function () { let data = parseQuery(request.data); expect(data['tpid_tdid']).to.equal('abcd-efgh-ijkl-mnop-1234'); + expect(data['eid_adserver.org']).to.equal('abcd-efgh-ijkl-mnop-1234'); }); describe('LiveIntent support', function () { @@ -1104,7 +1105,8 @@ describe('the rubicon adapter', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { lipb: { - lipbid: '0000-1111-2222-3333' + lipbid: '0000-1111-2222-3333', + segments: ['segA', 'segB'] } }; clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); @@ -1112,6 +1114,8 @@ describe('the rubicon adapter', function () { let data = parseQuery(request.data); expect(data['tpid_liveintent.com']).to.equal('0000-1111-2222-3333'); + expect(data['eid_liveintent.com']).to.equal('0000-1111-2222-3333'); + expect(data['tg_v.LIseg']).to.equal('segA,segB'); }); it('should send tg_v.LIseg when userIdAsEids contains liveintentId with ext.segments as array', function () { @@ -1429,20 +1433,10 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[0].ext).to.have.property('segments').that.is.an('array'); expect(post.user.ext.eids[0].ext.segments[0]).to.equal('segA'); expect(post.user.ext.eids[0].ext.segments[1]).to.equal('segB'); - // Non-EID properties set using liveintent EID values - expect(post.user.ext).to.have.property('tpid').that.is.an('object'); - expect(post.user.ext.tpid.source).to.equal('liveintent.com'); - expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); - expect(post).to.have.property('rp').that.is.an('object'); - expect(post.rp).to.have.property('target').that.is.an('object'); - expect(post.rp.target).to.have.property('LIseg').that.is.an('array'); - expect(post.rp.target.LIseg[0]).to.equal('segA'); - expect(post.rp.target.LIseg[1]).to.equal('segB'); // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); expect(post.user.ext.eids[1].uids[0].atype).to.equal(1); - // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); From 97766fbda01d5d3ada1836dbb09366b4619119d6 Mon Sep 17 00:00:00 2001 From: OneTagDevOps <38786435+OneTagDevOps@users.noreply.github.com> Date: Tue, 3 Nov 2020 23:02:24 +0100 Subject: [PATCH 0334/1476] Adds tcf v2 support (#5883) Co-authored-by: francesco --- modules/onetagBidAdapter.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index fd66c8ce69f..1a2df023b81 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -7,11 +7,12 @@ import find from 'core-js-pure/features/array/find.js'; import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -const storage = getStorageManager(); - const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; const BIDDER_CODE = 'onetag'; +const GVLID = 241; + +const storage = getStorageManager(GVLID); /** * Determines whether or not the given bid request is valid. @@ -231,7 +232,7 @@ function getPageInfo() { timing: getTiming(), version: { prebid: '$prebid.version$', - adapter: '1.0.0' + adapter: '1.1.0' } }; } @@ -379,6 +380,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, From 1da9ca6e07f02068d80ab71525bd5e5f592312fb Mon Sep 17 00:00:00 2001 From: liranbaruch Date: Wed, 4 Nov 2020 03:32:57 +0200 Subject: [PATCH 0335/1476] get dynamic ttl from the server response (#5896) * Change ironsource to be lower case all over code * Add test mode to the IronSource bidder * get dynamic ttl from the server response --- modules/ironsourceBidAdapter.js | 2 +- test/spec/modules/ironsourceBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js index 922e7b94c5b..e23c0cb857d 100644 --- a/modules/ironsourceBidAdapter.js +++ b/modules/ironsourceBidAdapter.js @@ -48,7 +48,7 @@ export const spec = { creativeId: body.requestId, currency: body.currency, netRevenue: body.netRevenue, - ttl: TTL, + ttl: body.ttl || TTL, vastXml: body.vastXml, mediaType: VIDEO }; diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js index 0c59dfef14b..93c3a6fb7b9 100644 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -278,7 +278,7 @@ describe('ironsourceAdapter', function () { mediaType: VIDEO } ]; - const result = spec.interpretResponse({ body: [response] }); + const result = spec.interpretResponse({ body: response }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); }) From fa2394f984630886fe8edbfbd6989f8251e59766 Mon Sep 17 00:00:00 2001 From: Kylian Deau Date: Wed, 4 Nov 2020 10:38:20 +0100 Subject: [PATCH 0336/1476] Teads adapter: add Global Vendor Id (GDPR enforcement) (#5929) --- modules/teadsBidAdapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 6d55aabbfb5..aad7f6762c4 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -1,6 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const utils = require('../src/utils.js'); const BIDDER_CODE = 'teads'; +const GVL_ID = 132; const ENDPOINT_URL = 'https://a.teads.tv/hb/bid-request'; const gdprStatus = { GDPR_APPLIES_PUBLISHER: 12, @@ -11,6 +12,7 @@ const gdprStatus = { export const spec = { code: BIDDER_CODE, + gvlid: GVL_ID, supportedMediaTypes: ['video', 'banner'], /** * Determines whether or not the given bid request is valid. From 7bbb05312ca86e8c9db1fdd495222902e8b1bc83 Mon Sep 17 00:00:00 2001 From: Stephan Brosinski Date: Wed, 4 Nov 2020 14:02:47 +0100 Subject: [PATCH 0337/1476] Smaato: Add userIds to BidRequest (#5927) --- modules/smaatoBidAdapter.js | 5 +++ test/spec/modules/smaatoBidAdapter_spec.js | 46 +++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index 49b4ed6aa34..93915689cee 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -117,6 +117,11 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { utils.deepSetValue(request, 'device.ifa', ifa); } + const eids = utils.deepAccess(validBidRequests[0], 'userIdAsEids'); + if (eids && eids.length) { + utils.deepSetValue(request, 'user.ext.eids', eids); + } + utils.logInfo('[SMAATO] OpenRTB Request:', request); return JSON.stringify(request); } diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 13716b51436..6af0a855800 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,6 +1,7 @@ import { spec } from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; +import {createEidsArray} from 'modules/userId/eids.js'; const imageAd = { image: { @@ -321,6 +322,37 @@ const inAppBidRequest = { bidderWinsCount: 0 }; +const userIdBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + userId: { + criteoId: '123456', + tdid: '89145' + }, + userIdAsEids: createEidsArray({ + criteoId: '123456', + tdid: '89145' + }) +}; + describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { it('has valid params', () => { @@ -422,7 +454,11 @@ describe('smaatoBidAdapterTest', () => { expect(req_fpd.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); expect(req_fpd.site.keywords).to.eql('power tools,drills'); expect(req_fpd.site.publisher.id).to.equal('publisherId'); - }) + }); + + it('has no user ids', () => { + expect(this.req.user.ext.eids).to.not.exist; + }); }); describe('buildRequests for video imps', () => { @@ -513,6 +549,14 @@ describe('smaatoBidAdapterTest', () => { }); }); + describe('user ids in requests', () => { + it('user ids are added to user.ext.eids', () => { + let req = JSON.parse(spec.buildRequests([userIdBidRequest], defaultBidderRequest).data); + expect(req.user.ext.eids).to.exist; + expect(req.user.ext.eids).to.have.length(2); + }); + }); + describe('interpretResponse', () => { it('single image reponse', () => { const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); From 7729bb7e6d167d862056dcabbe299d6b6052543f Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 4 Nov 2020 20:09:21 +0100 Subject: [PATCH 0338/1476] Mediasquare: add native and video support (#5823) * Mediasquare: Add support for uspConsent + schain userIds support. Plus enhance userSync * fix iframeEnabled and pixelEnabled + suggested shortand statement * mediasquare bidder: add metrics to onBidWon Event * mediasquare bidder: fix getUserSyncs * MediaSquare: add native and video support --- modules/mediasquareBidAdapter.js | 14 ++++- .../modules/mediasquareBidAdapter_spec.js | 56 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 288526d3cc5..87f768d3b77 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -1,7 +1,7 @@ import {ajax} from '../src/ajax.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'mediasquare'; const BIDDER_URL_PROD = 'https://pbs-front.mediasquare.fr/' @@ -13,7 +13,7 @@ const BIDDER_ENDPOINT_WINNING = 'winning'; export const spec = { code: BIDDER_CODE, aliases: ['msq'], // short code - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], /** * Determines whether or not the given bid request is valid. * @@ -99,6 +99,14 @@ export const spec = { 'code': value['code'] } }; + if ('native' in value) { + bidResponse['native'] = value['native']; + bidResponse['mediaType'] = 'native'; + } else if ('video' in value) { + if ('url' in value['video']) { bidResponse['vastUrl'] = value['video']['url'] } + if ('xml' in value['video']) { bidResponse['vastXml'] = value['video']['xml'] } + bidResponse['mediaType'] = 'video'; + } if (value.hasOwnProperty('deal_id')) { bidResponse['dealId'] = value['deal_id']; } bidResponses.push(bidResponse); }); @@ -136,7 +144,7 @@ export const spec = { // fires a pixel to confirm a winning bid let params = []; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'auctionId', 'requestId'] + let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond'] if (bid.hasOwnProperty('mediasquare')) { if (bid['mediasquare'].hasOwnProperty('bidder')) { params.push('bidder=' + bid['mediasquare']['bidder']); } if (bid['mediasquare'].hasOwnProperty('code')) { params.push('code=' + bid['mediasquare']['code']); } diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 5d930f2b6ac..3c18cfe0be7 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -24,6 +24,43 @@ describe('MediaSquare bid adapter tests', function () { code: 'publishername_atf_desktop_rg_pave' }, }]; + var VIDEO_PARAMS = [{ + adUnitCode: 'banner-div', + bidId: 'aaaa1234', + auctionId: 'bbbb1234', + transactionId: 'cccc1234', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + } + }, + bidder: 'mediasquare', + params: { + owner: 'test', + code: 'publishername_atf_desktop_rg_pave' + }, + }]; + var NATIVE_PARAMS = [{ + adUnitCode: 'banner-div', + bidId: 'aaaa1234', + auctionId: 'bbbb1234', + transactionId: 'cccc1234', + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + } + }, + bidder: 'mediasquare', + params: { + owner: 'test', + code: 'publishername_atf_desktop_rg_pave' + }, + }]; var BID_RESPONSE = {'body': { 'responses': [{ @@ -128,4 +165,23 @@ describe('MediaSquare bid adapter tests', function () { 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 native in bid response', function () { + const request = spec.buildRequests(NATIVE_PARAMS, DEFAULT_OPTIONS); + BID_RESPONSE.body.responses[0].native = {'title': 'native title'}; + const response = spec.interpretResponse(BID_RESPONSE, request); + expect(response).to.have.lengthOf(1); + const bid = response[0]; + expect(bid).to.have.property('native'); + delete BID_RESPONSE.body.responses[0].native; + }); + it('Verifies video in bid response', function () { + const request = spec.buildRequests(VIDEO_PARAMS, DEFAULT_OPTIONS); + BID_RESPONSE.body.responses[0].video = {'xml': 'my vast XML', 'url': 'my vast url'}; + const response = spec.interpretResponse(BID_RESPONSE, request); + expect(response).to.have.lengthOf(1); + const bid = response[0]; + expect(bid).to.have.property('vastXml'); + expect(bid).to.have.property('vastUrl'); + delete BID_RESPONSE.body.responses[0].video; + }); }); From 31e00f20b73ed5e8f35aa9178972158d3b8cf65d Mon Sep 17 00:00:00 2001 From: Aparna Rao Date: Wed, 4 Nov 2020 14:47:35 -0500 Subject: [PATCH 0339/1476] 33Across: Added Video Support (#5884) * check gdpr in buildRequest * User sync based on whether gdpr applies or not * check if consent data exists during user sync * split user sync into further branches: 1) when gdpr does not apply 2) when consent data is unavailable * contribute viewability to ttxRequest * update tests * remove window mock from tests * use local variables * introduce ServerRequestBuilder * add withOptions() method to ServerRequestBuilder * add semicolons * sync up package-lock.json with upstream/master * stub window.top in tests * introduce getTopWindowSize() for test purpose * reformat code * add withSite() method to TtxRequestBuilder add withSite() method to TtxRequestBuilder * add isIframe() and _isViewabilityMeasurable() * handle NON_MEASURABLE viewability in nested iframes * consider page visibility, stub utils functions getWindowTop() and getWindowSelf() * contribute viewability as 0 for inactive tab * add prebidjs version to ttx request * send caller as an array * send viewability as non measurable when unable to locate target HTMLElement, add warning message * fix JSDoc in utils.js * introduce mapAdSlotPathToElementId() * introduce getAdSlotHTMLElement(), add logging * introduce mapAdSlotPathToElementId() * update logging in ad unit path to element id mapping * rephrase logging, fix tests * update adapter documentation * remove excessive logging * improve logging * revert change * fix return of _mapAdUnitPathToElementId() * improve logging of _mapAdUnitPathToElementId() * do not use Array.find() * return id once element is found * return id once element is found * let -> const * Removing killswitch behavior for GDPR * Updated comments to reflect current gdpr logic * URI encode consent string * Updated example site ID to help Prebid team e2e test our adapter * send page url in ortb * Removed redundant pageUrl default * Restored package-log.json that mirrors prebid's repo * Sending USP string during buildRequest * Adding USP consent data to user sync * add unit test for syncing without bidrequest * Changed to uspConsent to make the connatation consistent * Resetting adapter state in adapter after user sync rather than exposing it. * removed console log * Adding schain info * remove setting empty format ext * better tests invalid values * removing validation of schain * Fixed lint errors * First cut for bidfloors support * fixed where getFloors is read * fixed merge conflicts * support the guid in the api endpoint * Reformat + validation updates * refactor banner to conform to mediaType format * Building video ORTB * code review changes for better refactor * Building video ORTB * Interpret video response * Updated documentation * Updated supported mediatypes * Added bidfloors * Adding support bidder specific overrides * only validate startdelay when instream * fixed incorrect params for instream * Removed usage of an actual GUID for safety. * Added mimes and protocols as required * placement is +ve int * fix for sizes + valid sample GUID Co-authored-by: Gleb Glushtsov Co-authored-by: Gleb Glushtsov Co-authored-by: Gleb Glushtsov Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: Aparna Hegde Co-authored-by: terryc33x <64039851+terryc33x@users.noreply.github.com> Co-authored-by: Terry Chen --- modules/33acrossBidAdapter.js | 518 ++++++++---- modules/33acrossBidAdapter.md | 146 +++- test/spec/modules/33acrossBidAdapter_spec.js | 811 ++++++++++++++++--- 3 files changed, 1184 insertions(+), 291 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 798b6450946..65df8baad2e 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,12 +1,37 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = '33across'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; const SYNC_ENDPOINT = 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb'; -const MEDIA_TYPE = 'banner'; + const CURRENCY = 'USD'; +const GUID_PATTERN = /^[a-zA-Z0-9_-]{22}$/; + +const PRODUCT = { + SIAB: 'siab', + INVIEW: 'inview', + INSTREAM: 'instream' +}; + +const VIDEO_ORTB_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity' +]; const adapterState = { uniqueSiteIds: [] @@ -14,57 +39,122 @@ const adapterState = { const NON_MEASURABLE = 'nm'; -// All this assumes that only one bid is ever returned by ttx -function _createBidResponse(response) { - return { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ad: response.seatbid[0].bid[0].adm, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - netRevenue: true +// **************************** VALIDATION *************************** // +function isBidRequestValid(bid) { + return ( + _validateBasic(bid) && + _validateBanner(bid) && + _validateVideo(bid) + ); +} + +function _validateBasic(bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + + if (!_validateGUID(bid)) { + return false; } + + return true; } -function _isViewabilityMeasurable(element) { - return !_isIframe() && element !== null; +function _validateGUID(bid) { + const siteID = utils.deepAccess(bid, 'params.siteId', '') || ''; + if (siteID.trim().match(GUID_PATTERN) === null) { + return false; + } + + return true; } -function _getViewability(element, topWin, { w, h } = {}) { - return topWin.document.visibilityState === 'visible' - ? _getPercentInView(element, topWin, { w, h }) - : 0; +function _validateBanner(bid) { + const banner = utils.deepAccess(bid, 'mediaTypes.banner'); + // If there's no banner no need to validate against banner rules + if (banner === undefined) { + return true; + } + + if (!Array.isArray(banner.sizes)) { + return false; + } + + return true; } -function _mapAdUnitPathToElementId(adUnitCode) { - if (utils.isGptPubadsDefined()) { - // eslint-disable-next-line no-undef - const adSlots = googletag.pubads().getSlots(); - const isMatchingAdSlot = utils.isSlotMatchingAdUnitCode(adUnitCode); +function _validateVideo(bid) { + const videoAdUnit = utils.deepAccess(bid, 'mediaTypes.video'); + const videoBidderParams = utils.deepAccess(bid, 'params.video', {}); - for (let i = 0; i < adSlots.length; i++) { - if (isMatchingAdSlot(adSlots[i])) { - const id = adSlots[i].getSlotElementId(); + // If there's no video no need to validate against video rules + if (videoAdUnit === undefined) { + return true; + } - utils.logInfo(`[33Across Adapter] Map ad unit path to HTML element id: '${adUnitCode}' -> ${id}`); + if (!Array.isArray(videoAdUnit.playerSize)) { + return false; + } - return id; - } - } + if (!videoAdUnit.context) { + return false; } - utils.logWarn(`[33Across Adapter] Unable to locate element for ad unit code: '${adUnitCode}'`); + const videoParams = { + ...videoAdUnit, + ...videoBidderParams + }; - return null; + if (!Array.isArray(videoParams.mimes) || videoParams.mimes.length === 0) { + return false; + } + + if (!Array.isArray(videoParams.protocols) || videoParams.protocols.length === 0) { + return false; + } + + // If placement if defined, it must be a number + if ( + typeof videoParams.placement !== 'undefined' && + typeof videoParams.placement !== 'number' + ) { + return false; + } + + // If startdelay is defined it must be a number + if ( + videoAdUnit.context === 'instream' && + typeof videoParams.startdelay !== 'undefined' && + typeof videoParams.startdelay !== 'number' + ) { + return false; + } + + return true; } -function _getAdSlotHTMLElement(adUnitCode) { - return document.getElementById(adUnitCode) || - document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +// **************************** BUILD REQUESTS *************************** // +// NOTE: With regards to gdrp consent data, the server will independently +// infer the gdpr applicability therefore, setting the default value to false +function buildRequests(bidRequests, bidderRequest) { + const gdprConsent = Object.assign({ + consentString: undefined, + gdprApplies: false + }, bidderRequest && bidderRequest.gdprConsent); + + const uspConsent = bidderRequest && bidderRequest.uspConsent; + const pageUrl = (bidderRequest && bidderRequest.refererInfo) ? (bidderRequest.refererInfo.referer) : (undefined); + + adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques); + + return bidRequests.map(bidRequest => _createServerRequest( + { + bidRequest, + gdprConsent, + uspConsent, + pageUrl + }) + ); } // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request @@ -72,46 +162,28 @@ function _getAdSlotHTMLElement(adUnitCode) { function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl}) { const ttxRequest = {}; const params = bidRequest.params; - const element = _getAdSlotHTMLElement(bidRequest.adUnitCode); - const sizes = _transformSizes(bidRequest.sizes); - let format; - - // We support size based bidfloors so obtain one if there's a rule associated - if (typeof bidRequest.getFloor === 'function') { - let getFloor = bidRequest.getFloor.bind(bidRequest); - - format = sizes.map((size) => { - const formatExt = _getBidFloors(getFloor, size); + /* + * Infer data for the request payload + */ + ttxRequest.imp = [{}]; - return Object.assign({}, size, formatExt); - }); - } else { - format = sizes; + if (utils.deepAccess(bidRequest, 'mediaTypes.banner')) { + ttxRequest.imp[0].banner = { + ..._buildBannerORTB(bidRequest) + } } - const minSize = _getMinSize(sizes); - - const viewabilityAmount = _isViewabilityMeasurable(element) - ? _getViewability(element, utils.getWindowTop(), minSize) - : NON_MEASURABLE; - - const contributeViewability = ViewabilityContributor(viewabilityAmount); + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + ttxRequest.imp[0].video = _buildVideoORTB(bidRequest); + } - /* - * Infer data for the request payload - */ - ttxRequest.imp = []; - ttxRequest.imp[0] = { - banner: { - format - }, - ext: { - ttx: { - prod: params.productId - } + ttxRequest.imp[0].ext = { + ttx: { + prod: _getProduct(bidRequest) } }; + ttxRequest.site = { id: params.siteId }; if (pageUrl) { @@ -173,53 +245,187 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl return { 'method': 'POST', 'url': url, - 'data': JSON.stringify(contributeViewability(ttxRequest)), + 'data': JSON.stringify(ttxRequest), 'options': options } } -// Sync object will always be of type iframe for TTX -function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent }) { - const ttxSettings = config.getConfig('ttxSettings'); - const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; +// BUILD REQUESTS: SIZE INFERENCE +function _transformSizes(sizes) { + if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) { + return [ _getSize(sizes) ]; + } - const { consentString, gdprApplies } = gdprConsent; + return sizes.map(_getSize); +} - const sync = { - type: 'iframe', - url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}` - }; +function _getSize(size) { + return { + w: parseInt(size[0], 10), + h: parseInt(size[1], 10) + } +} - if (typeof gdprApplies === 'boolean') { - sync.url += `&gdpr=${Number(gdprApplies)}`; +// BUILD REQUESTS: PRODUCT INFERENCE +function _getProduct(bidRequest) { + const { params, mediaTypes } = bidRequest; + + const { banner, video } = mediaTypes; + + if ((video && !banner) && video.context === 'instream') { + return PRODUCT.INSTREAM; } - return sync; + return (params.productId === PRODUCT.INVIEW) ? (params.productId) : PRODUCT.SIAB; } -function _getBidFloors(getFloor, size) { - const bidFloors = getFloor({ +// BUILD REQUESTS: BANNER +function _buildBannerORTB(bidRequest) { + const bannerAdUnit = utils.deepAccess(bidRequest, 'mediaTypes.banner', {}); + const element = _getAdSlotHTMLElement(bidRequest.adUnitCode); + + const sizes = _transformSizes(bannerAdUnit.sizes); + + let format; + + // We support size based bidfloors so obtain one if there's a rule associated + if (typeof bidRequest.getFloor === 'function') { + format = sizes.map((size) => { + const bidfloors = _getBidFloors(bidRequest, size, BANNER); + + let formatExt; + if (bidfloors) { + formatExt = { + ext: { + ttx: { + bidfloors: [ bidfloors ] + } + } + } + } + + return Object.assign({}, size, formatExt); + }); + } else { + format = sizes; + } + + const minSize = _getMinSize(sizes); + + const viewabilityAmount = _isViewabilityMeasurable(element) + ? _getViewability(element, utils.getWindowTop(), minSize) + : NON_MEASURABLE; + + const ext = contributeViewability(viewabilityAmount); + + return { + format, + ext + } +} + +// BUILD REQUESTS: VIDEO +// eslint-disable-next-line no-unused-vars +function _buildVideoORTB(bidRequest) { + const videoAdUnit = utils.deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {}); + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams // Bidder Specific overrides + }; + + const video = {} + + const {w, h} = _getSize(videoParams.playerSize[0]); + video.w = w; + video.h = h; + + // Obtain all ORTB params related video from Ad Unit + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; + } + }); + + const product = _getProduct(bidRequest); + + // Placement Inference Rules: + // - If no placement is defined then default to 2 (In Banner) + // - If product is instream (for instream context) then override placement to 1 + video.placement = video.placement || 2; + + if (product === PRODUCT.INSTREAM) { + video.startdelay = video.startdelay || 0; + video.placement = 1; + }; + + // bidfloors + if (typeof bidRequest.getFloor === 'function') { + const bidfloors = _getBidFloors(bidRequest, {w: video.w, h: video.h}, VIDEO); + + if (bidfloors) { + Object.assign(video, { + ext: { + ttx: { + bidfloors: [ bidfloors ] + } + } + }); + } + } + return video; +} + +// BUILD REQUESTS: BIDFLOORS +function _getBidFloors(bidRequest, size, mediaType) { + const bidFloors = bidRequest.getFloor({ currency: CURRENCY, - mediaType: MEDIA_TYPE, + mediaType, size: [ size.w, size.h ] }); if (!isNaN(bidFloors.floor) && (bidFloors.currency === CURRENCY)) { - return { - ext: { - ttx: { - bidfloors: [ bidFloors.floor ] - } + return bidFloors.floor; + } +} + +// BUILD REQUESTS: VIEWABILITY +function _isViewabilityMeasurable(element) { + return !_isIframe() && element !== null; +} + +function _getViewability(element, topWin, { w, h } = {}) { + return topWin.document.visibilityState === 'visible' + ? _getPercentInView(element, topWin, { w, h }) + : 0; +} + +function _mapAdUnitPathToElementId(adUnitCode) { + if (utils.isGptPubadsDefined()) { + // eslint-disable-next-line no-undef + const adSlots = googletag.pubads().getSlots(); + const isMatchingAdSlot = utils.isSlotMatchingAdUnitCode(adUnitCode); + + for (let i = 0; i < adSlots.length; i++) { + if (isMatchingAdSlot(adSlots[i])) { + const id = adSlots[i].getSlotElementId(); + + utils.logInfo(`[33Across Adapter] Map ad unit path to HTML element id: '${adUnitCode}' -> ${id}`); + + return id; } } } + + utils.logWarn(`[33Across Adapter] Unable to locate element for ad unit code: '${adUnitCode}'`); + + return null; } -function _getSize(size) { - return { - w: parseInt(size[0], 10), - h: parseInt(size[1], 10) - } +function _getAdSlotHTMLElement(adUnitCode) { + return document.getElementById(adUnitCode) || + document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); } function _getMinSize(sizes) { @@ -239,14 +445,6 @@ function _getBoundingBox(element, { w, h } = {}) { return { width, height, left, top, right, bottom }; } -function _transformSizes(sizes) { - if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) { - return [ _getSize(sizes) ]; - } - - return sizes.map(_getSize); -} - function _getIntersectionOfRects(rects) { const bbox = { left: rects[0].left, @@ -307,20 +505,16 @@ function _getPercentInView(element, topWin, { w, h } = {}) { /** * Viewability contribution to request.. */ -function ViewabilityContributor(viewabilityAmount) { - function contributeViewability(ttxRequest) { - const req = Object.assign({}, ttxRequest); - const imp = req.imp = req.imp.map(impItem => Object.assign({}, impItem)); - const banner = imp[0].banner = Object.assign({}, imp[0].banner); - const ext = banner.ext = Object.assign({}, banner.ext); - const ttx = ext.ttx = Object.assign({}, ext.ttx); - - ttx.viewability = { amount: isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount) }; +function contributeViewability(viewabilityAmount) { + const amount = isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); - return req; - } - - return contributeViewability; + return { + ttx: { + viewability: { + amount + } + } + }; } function _isIframe() { @@ -331,42 +525,9 @@ function _isIframe() { } } -function isBidRequestValid(bid) { - if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { - return false; - } - - if (typeof bid.params.siteId === 'undefined' || typeof bid.params.productId === 'undefined') { - return false; - } - - return true; -} - -// NOTE: With regards to gdrp consent data, -// - the server independently infers gdpr applicability therefore, setting the default value to false -function buildRequests(bidRequests, bidderRequest) { - const gdprConsent = Object.assign({ - consentString: undefined, - gdprApplies: false - }, bidderRequest && bidderRequest.gdprConsent); - - const uspConsent = bidderRequest && bidderRequest.uspConsent; - const pageUrl = (bidderRequest && bidderRequest.refererInfo) ? (bidderRequest.refererInfo.referer) : (undefined); - - adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques); - - return bidRequests.map(bidRequest => _createServerRequest( - { - bidRequest, - gdprConsent, - uspConsent, - pageUrl - }) - ); -} - -// NOTE: At this point, the response from 33exchange will only ever contain one bid i.e. the highest bid +// **************************** INTERPRET RESPONSE ******************************** // +// NOTE: At this point, the response from 33exchange will only ever contain one bid +// i.e. the highest bid function interpretResponse(serverResponse, bidRequest) { const bidResponses = []; @@ -378,6 +539,36 @@ function interpretResponse(serverResponse, bidRequest) { return bidResponses; } +// All this assumes that only one bid is ever returned by ttx +function _createBidResponse(response) { + const bid = { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ad: response.seatbid[0].bid[0].adm, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + mediaType: utils.deepAccess(response.seatbid[0].bid[0], 'ext.ttx.mediaType', BANNER), + currency: response.cur, + netRevenue: true + } + + if (bid.mediaType === VIDEO) { + const vastType = utils.deepAccess(response.seatbid[0].bid[0], 'ext.ttx.vastType', 'xml'); + + if (vastType === 'xml') { + bid.vastXml = bid.ad; + } else { + bid.vastUrl = bid.ad; + } + } + + return bid; +} + +// **************************** USER SYNC *************************** // // Register one sync per unique guid so long as iframe is enable // Else no syncs // For logic on how we handle gdpr data see _createSyncs and module's unit tests @@ -395,11 +586,30 @@ function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) { return syncUrls; } +// Sync object will always be of type iframe for TTX +function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent }) { + const ttxSettings = config.getConfig('ttxSettings'); + const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; + + const { consentString, gdprApplies } = gdprConsent; + + const sync = { + type: 'iframe', + url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}` + }; + + if (typeof gdprApplies === 'boolean') { + sync.url += `&gdpr=${Number(gdprApplies)}`; + } + + return sync; +} + export const spec = { NON_MEASURABLE, code: BIDDER_CODE, - + supportedMediaTypes: [ BANNER, VIDEO ], isBidRequestValid, buildRequests, interpretResponse, diff --git a/modules/33acrossBidAdapter.md b/modules/33acrossBidAdapter.md index c313f3b6e0b..c01c04251e5 100644 --- a/modules/33acrossBidAdapter.md +++ b/modules/33acrossBidAdapter.md @@ -10,23 +10,145 @@ Maintainer: headerbidding@33across.com Connects to 33Across's exchange for bids. -33Across bid adapter supports only Banner at present and follows MRA +33Across bid adapter supports Banner and Video at present and follows MRA # Sample Ad Unit: For Publishers +## Sample Banner only Ad Unit ``` var adUnits = [ { - code: '33across-hb-ad-123456-1', // ad slot HTML element ID - sizes: [ - [300, 250], - [728, 90] - ], - bids: [{ - bidder: '33across', - params: { - siteId: 'cxBE0qjUir6iopaKkGJozW', - productId: 'siab' + code: '33across-hb-ad-123456-1', // ad slot HTML element ID + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [728, 90] + ] + } + } + bids: [{ + bidder: '33across', + params: { + siteId: 'sample33xGUID123456789', + productId: 'siab' + } + }] +} +``` + +## Sample Video only Ad Unit: Outstream +``` +var adUnits = [ +{ + code: '33across-hb-ad-123456-1', // ad slot HTML element ID + mediaTypes: { + video: { + playerSize: [300, 250], + context: 'outstream', + placement: 2 + ... // Aditional ORTB video params + } + }, + renderer: { + url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: function (bid) { + adResponse = { + ad: { + video: { + content: bid.vastXml, + player_height: bid.playerHeight, + player_width: bid.playerWidth + } + } } - }] + // push to render queue because ANOutstreamVideo may not be loaded yet. + bid.renderer.push(() => { + ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, // target div id to render video. + adResponse: adResponse + }); + }); + } + }, + bids: [{ + bidder: '33across', + params: { + siteId: 'sample33xGUID123456789', + productId: 'siab' + } + }] +} +``` + +## Sample Multi-Format Ad Unit: Outstream +``` +var adUnits = [ +{ + code: '33across-hb-ad-123456-1', // ad slot HTML element ID + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [728, 90] + ] + }, + video: { + playerSize: [300, 250], + context: 'outstream', + placement: 2 + ... // Aditional ORTB video params + } + }, + renderer: { + url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + render: function (bid) { + adResponse = { + ad: { + video: { + content: bid.vastXml, + player_height: bid.playerHeight, + player_width: bid.playerWidth + } + } + } + // push to render queue because ANOutstreamVideo may not be loaded yet. + bid.renderer.push(() => { + ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, // target div id to render video. + adResponse: adResponse + }); + }); + } + }, + bids: [{ + bidder: '33across', + params: { + siteId: 'sample33xGUID123456789', + productId: 'siab' + } + }] +} +``` + +## Sample Video only Ad Unit: Instream +``` +var adUnits = [ +{ + code: '33across-hb-ad-123456-1', // ad slot HTML element ID + mediaTypes: { + video: { + playerSize: [300, 250], + context: 'intstream', + placement: 1 + ... // Aditional ORTB video params + } + } + bids: [{ + bidder: '33across', + params: { + siteId: 'sample33xGUID123456789', + productId: 'instream' + } + }] } ``` diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index d30659791ea..edc7b7a2767 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -7,8 +7,8 @@ import { spec } from 'modules/33acrossBidAdapter.js'; describe('33acrossBidAdapter:', function () { const BIDDER_CODE = '33across'; - const SITE_ID = 'pub1234'; - const PRODUCT_ID = 'product1'; + const SITE_ID = 'sample33xGUID123456789'; + const PRODUCT_ID = 'siab'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; let element, win; @@ -17,39 +17,13 @@ describe('33acrossBidAdapter:', function () { function TtxRequestBuilder() { const ttxRequest = { - imp: [{ - banner: { - format: [ - { - w: 300, - h: 250 - }, - { - w: 728, - h: 90 - } - ], - ext: { - ttx: { - viewability: { - amount: 100 - } - } - } - }, - ext: { - ttx: { - prod: PRODUCT_ID - } - } - }], + imp: [{}], site: { id: SITE_ID }, id: 'b1', user: { ext: { - consent: undefined } }, regs: { @@ -69,13 +43,52 @@ describe('33acrossBidAdapter:', function () { } }; - this.withSizes = sizes => { + this.withBanner = () => { + Object.assign(ttxRequest.imp[0], { + banner: { + format: [ + { + w: 300, + h: 250 + }, + { + w: 728, + h: 90 + } + ], + ext: { + ttx: { + viewability: { + amount: 100 + } + } + } + } + }); + + return this; + }; + + this.withBannerSizes = this.withSizes = sizes => { Object.assign(ttxRequest.imp[0].banner, { format: sizes }); return this; }; - this.withViewability = viewability => { - Object.assign(ttxRequest.imp[0].banner, { + this.withVideo = (params = {}) => { + Object.assign(ttxRequest.imp[0], { + video: { + w: 300, + h: 250, + placement: 2, + ...params + } + }); + + return this; + }; + + this.withViewability = (viewability, format = 'banner') => { + Object.assign(ttxRequest.imp[0][format], { ext: { ttx: { viewability } } @@ -83,6 +96,18 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withProduct = (prod = PRODUCT_ID) => { + Object.assign(ttxRequest.imp[0], { + ext: { + ttx: { + prod + } + } + }); + + return this; + }; + this.withGdprConsent = (consent, gdpr) => { Object.assign(ttxRequest, { user: { @@ -140,18 +165,31 @@ describe('33acrossBidAdapter:', function () { return this; }; - this.withFormatFloors = floors => { - const format = ttxRequest.imp[0].banner.format.map((fm, i) => { - return Object.assign(fm, { - ext: { - ttx: { - bidfloors: [ floors[i] ] + this.withFloors = this.withFormatFloors = (mediaType, floors) => { + switch (mediaType) { + case 'banner': + const format = ttxRequest.imp[0].banner.format.map((fm, i) => { + return Object.assign(fm, { + ext: { + ttx: { + bidfloors: [ floors[i] ] + } + } + }) + }); + + ttxRequest.imp[0].banner.format = format; + break; + case 'video': + Object.assign(ttxRequest.imp[0].video, { + ext: { + ttx: { + bidfloors: floors + } } - } - }) - }); - - ttxRequest.imp[0].banner.format = format; + }); + break; + } return this; }; @@ -188,6 +226,53 @@ describe('33acrossBidAdapter:', function () { this.build = () => serverRequest; } + function BidRequestsBuilder() { + const bidRequests = [ + { + bidId: 'b1', + bidder: '33across', + bidderRequestId: 'b1a', + params: { + siteId: SITE_ID, + productId: PRODUCT_ID + }, + adUnitCode: 'div-id', + auctionId: 'r1', + mediaTypes: {}, + transactionId: 't1' + } + ]; + + this.withBanner = () => { + bidRequests[0].mediaTypes.banner = { + sizes: [ + [300, 250], + [728, 90] + ] + }; + + return this; + }; + + this.withProduct = (prod) => { + bidRequests[0].params.productId = prod; + + return this; + }; + + this.withVideo = (params) => { + bidRequests[0].mediaTypes.video = { + playerSize: [[300, 250]], + context: 'outstream', + ...params + }; + + return this; + } + + this.build = () => bidRequests; + } + beforeEach(function() { element = { x: 0, @@ -217,24 +302,11 @@ describe('33acrossBidAdapter:', function () { innerHeight: 600 }; - bidRequests = [ - { - bidId: 'b1', - bidder: '33across', - bidderRequestId: 'b1a', - params: { - siteId: SITE_ID, - productId: PRODUCT_ID - }, - adUnitCode: 'div-id', - auctionId: 'r1', - sizes: [ - [300, 250], - [728, 90] - ], - transactionId: 't1' - } - ]; + bidRequests = ( + new BidRequestsBuilder() + .withBanner() + .build() + ); sandbox = sinon.sandbox.create(); sandbox.stub(Date, 'now').returns(1); @@ -248,78 +320,246 @@ describe('33acrossBidAdapter:', function () { }); describe('isBidRequestValid:', function() { - it('returns true when valid bid request is sent', function() { - const validBid = { - bidder: BIDDER_CODE, - params: { - siteId: SITE_ID, - productId: PRODUCT_ID - } - }; + context('basic validation', function() { + it('returns true for valid guid values', function() { + // NOTE: We ignore whitespace at the start and end since + // in our experience these are common typos + const validGUIDs = [ + `${SITE_ID}`, + `${SITE_ID} `, + ` ${SITE_ID}`, + ` ${SITE_ID} ` + ]; - expect(spec.isBidRequestValid(validBid)).to.be.true; - }); + validGUIDs.forEach((siteId) => { + const bid = { + bidder: '33across', + params: { + siteId + } + }; - it('returns true when valid test bid request is sent', function() { - const validBid = { - bidder: BIDDER_CODE, - params: { - siteId: SITE_ID, - productId: PRODUCT_ID, - test: 1 - } - }; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + }); - expect(spec.isBidRequestValid(validBid)).to.be.true; - }); + it('returns false for invalid guid values', function() { + const invalidGUIDs = [ + undefined, + 'siab' + ]; - it('returns false when bidder not set to "33across"', function() { - const invalidBid = { - bidder: 'foo', - params: { - siteId: SITE_ID, - productId: PRODUCT_ID - } - }; + invalidGUIDs.forEach((siteId) => { + const bid = { + bidder: '33across', + params: { + siteId + } + }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); }); - it('returns false when params not set', function() { - const invalidBid = { - bidder: 'foo' - }; + context('banner validation', function() { + it('returns true when banner mediaType does not exist', function() { + const bid = { + bidder: '33across', + params: { + siteId: 'cxBE0qjUir6iopaKkGJozW' + } + }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); + expect(spec.isBidRequestValid(bid)).to.be.true; + }); - it('returns false when site ID is not set in params', function() { - const invalidBid = { - bidder: 'foo', - params: { - productId: PRODUCT_ID - } - }; + it('returns true when banner sizes are defined', function() { + const bid = { + bidder: '33across', + mediaTypes: { + banner: { + sizes: [[250, 300]] + } + }, + params: { + siteId: 'cxBE0qjUir6iopaKkGJozW' + } + }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); + expect(spec.isBidRequestValid(bid)).to.be.true; + }); - it('returns false when product ID not set in params', function() { - const invalidBid = { - bidder: 'foo', - params: { - siteId: SITE_ID - } - }; + it('returns false when banner sizes are invalid', function() { + const invalidSizes = [ + undefined, + '16:9', + 300, + 'foo' + ]; + + invalidSizes.forEach((sizes) => { + const bid = { + bidder: '33across', + mediaTypes: { + banner: { + sizes + } + }, + params: { + siteId: 'cxBE0qjUir6iopaKkGJozW' + } + }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); }); + + context('video validation', function() { + beforeEach(function() { + // Basic Valid BidRequest + this.bid = { + bidder: '33across', + mediaTypes: { + video: { + playerSize: [[300, 50]], + context: 'outstream', + mimes: ['foo', 'bar'], + protocols: [1, 2] + } + }, + params: { + siteId: `${SITE_ID}` + } + }; + }); + + it('returns true when video mediaType does not exist', function() { + const bid = { + bidder: '33across', + params: { + siteId: `${SITE_ID}` + } + }; + + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns true when valid video mediaType is defined', function() { + expect(spec.isBidRequestValid(this.bid)).to.be.true; + }); + + it('returns false when video context is not defined', function() { + delete this.bid.mediaTypes.video.context; + + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + + it('returns false when video playserSize is invalid', function() { + const invalidSizes = [ + undefined, + '16:9', + 300, + 'foo' + ]; + + invalidSizes.forEach((playerSize) => { + this.bid.mediaTypes.video.playerSize = playerSize; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + }); + + it('returns false when video mimes is invalid', function() { + const invalidMimes = [ + undefined, + 'foo', + 1, + [] + ] + + invalidMimes.forEach((mimes) => { + this.bid.mediaTypes.video.mimes = mimes; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }) + }); + + it('returns false when video protocols is invalid', function() { + const invalidMimes = [ + undefined, + 'foo', + 1, + [] + ] + + invalidMimes.forEach((protocols) => { + this.bid.mediaTypes.video.protocols = protocols; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }) + }); + + it('returns false when video placement is invalid', function() { + const invalidPlacement = [ + [], + '1', + {}, + 'foo' + ]; + + invalidPlacement.forEach((placement) => { + this.bid.mediaTypes.video.placement = placement; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + }); + + it('returns false when video startdelay is invalid for instream context', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'instream', protocols: [1, 2], mimes: ['foo', 'bar']}) + .build() + ); + + const invalidStartdelay = [ + [], + '1', + {}, + 'foo' + ]; + + invalidStartdelay.forEach((startdelay) => { + bidRequests[0].mediaTypes.video.startdelay = startdelay; + expect(spec.isBidRequestValid(bidRequests[0])).to.be.false; + }); + }); + + it('returns true when video startdelay is invalid for outstream context', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'outstream', protocols: [1, 2], mimes: ['foo', 'bar']}) + .build() + ); + + const invalidStartdelay = [ + [], + '1', + {}, + 'foo' + ]; + + invalidStartdelay.forEach((startdelay) => { + bidRequests[0].mediaTypes.video.startdelay = startdelay; + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + }); + }); + }) }); describe('buildRequests:', function() { context('when element is fully in view', function() { it('returns 100', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withViewability({amount: 100}) .build(); const serverRequest = new ServerRequestBuilder() @@ -335,6 +575,8 @@ describe('33acrossBidAdapter:', function () { context('when element is out of view', function() { it('returns 0', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withViewability({amount: 0}) .build(); const serverRequest = new ServerRequestBuilder() @@ -350,6 +592,8 @@ describe('33acrossBidAdapter:', function () { context('when element is partially in view', function() { it('returns percentage', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withViewability({amount: 75}) .build(); const serverRequest = new ServerRequestBuilder() @@ -365,6 +609,8 @@ describe('33acrossBidAdapter:', function () { context('when width or height of the element is zero', function() { it('try to use alternative values', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withSizes([{ w: 800, h: 2400 }]) .withViewability({amount: 25}) .build(); @@ -373,7 +619,7 @@ describe('33acrossBidAdapter:', function () { .build(); Object.assign(element, { width: 0, height: 0 }); - bidRequests[0].sizes = [[800, 2400]]; + bidRequests[0].mediaTypes.banner.sizes = [[800, 2400]]; expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); }); @@ -382,6 +628,8 @@ describe('33acrossBidAdapter:', function () { context('when nested iframes', function() { it('returns \'nm\'', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withViewability({amount: spec.NON_MEASURABLE}) .build(); const serverRequest = new ServerRequestBuilder() @@ -402,6 +650,8 @@ describe('33acrossBidAdapter:', function () { context('when tab is inactive', function() { it('returns 0', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withViewability({amount: 0}) .build(); const serverRequest = new ServerRequestBuilder() @@ -432,6 +682,8 @@ describe('33acrossBidAdapter:', function () { it('returns corresponding server requests with gdpr consent data', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withGdprConsent('foobarMyPreference', 1) .build(); const serverRequest = new ServerRequestBuilder() @@ -450,6 +702,8 @@ describe('33acrossBidAdapter:', function () { }); const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withGdprConsent('foobarMyPreference', 1) .build(); const serverRequest = new ServerRequestBuilder() @@ -471,6 +725,8 @@ describe('33acrossBidAdapter:', function () { it('returns corresponding server requests with default gdpr consent data', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -488,6 +744,8 @@ describe('33acrossBidAdapter:', function () { }); const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -510,6 +768,8 @@ describe('33acrossBidAdapter:', function () { it('returns corresponding server requests with us_privacy consent data', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withUspConsent('foo') .build(); const serverRequest = new ServerRequestBuilder() @@ -528,6 +788,8 @@ describe('33acrossBidAdapter:', function () { }); const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withUspConsent('foo') .build(); const serverRequest = new ServerRequestBuilder() @@ -549,6 +811,8 @@ describe('33acrossBidAdapter:', function () { it('returns corresponding server requests with default us_privacy data', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -566,6 +830,8 @@ describe('33acrossBidAdapter:', function () { }); const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -586,6 +852,8 @@ describe('33acrossBidAdapter:', function () { }; const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withPageUrl('http://foo.com/bar') .build(); const serverRequest = new ServerRequestBuilder() @@ -605,6 +873,8 @@ describe('33acrossBidAdapter:', function () { }; const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -656,6 +926,8 @@ describe('33acrossBidAdapter:', function () { bidRequests[0].schain = schain; const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withSchain(schain) .build(); const serverRequest = new ServerRequestBuilder() @@ -672,6 +944,8 @@ describe('33acrossBidAdapter:', function () { context('when there no schain object is passed', function() { it('does not set source field', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() @@ -684,9 +958,11 @@ describe('33acrossBidAdapter:', function () { }); }); - context('when price floor module is not enabled in bidRequest', function() { + context('when price floor module is not enabled for banner in bidRequest', function() { it('does not set any bidfloors in ttxRequest', function() { const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -697,11 +973,13 @@ describe('33acrossBidAdapter:', function () { }); }); - context('when price floor module is enabled in bidRequest', function() { + context('when price floor module is enabled for banner in bidRequest', function() { it('does not set any bidfloors in ttxRequest if there is no floor', function() { bidRequests[0].getFloor = () => ({}); const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -723,9 +1001,188 @@ describe('33acrossBidAdapter:', function () { }; const ttxRequest = new TtxRequestBuilder() - .withFormatFloors([ 1.0, 0.10 ]) + .withBanner() + .withProduct() + .withFormatFloors('banner', [ 1.0, 0.10 ]) + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + }); + + context('when mediaType has video only and context is instream', function() { + it('builds instream request with default params', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'instream'}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withVideo() + .withProduct('instream') + .build(); + + ttxRequest.imp[0].video.placement = 1; + ttxRequest.imp[0].video.startdelay = 0; + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + + it('builds instream request with params passed', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'instream', startdelay: -2}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withVideo({startdelay: -2, placement: 1}) + .withProduct('instream') + .build(); + + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + }); + }); + + context('when mediaType has video only and context is outstream', function() { + it('builds siab request with video only with default params', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'outstream'}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withVideo() + .withProduct('siab') + .build(); + + ttxRequest.imp[0].video.placement = 2; + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + + it('builds siab request with video params passed', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'outstream', placement: 3, playbackmethod: [2]}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withVideo({placement: 3, playbackmethod: [2]}) + .withProduct('siab') + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + }); + + context('when mediaType has banner only', function() { + it('builds default siab request', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withBanner() + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct('siab') + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + + it('builds default inview request when product is set as such', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withBanner() + .withProduct('inview') + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct('inview') + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + }); + + context('when mediaType has banner and video', function() { + it('builds siab request with banner and outstream video', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withBanner() + .withVideo({context: 'outstream'}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withVideo() + .withProduct('siab') + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(builtServerRequests).to.deep.equal([serverRequest]); + }); + + it('builds siab request with banner and outstream video even when context is instream', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withBanner() + .withVideo({context: 'instream'}) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withVideo() + .withProduct('siab') .build(); + ttxRequest.imp[0].video.placement = 2; + const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); @@ -734,6 +1191,55 @@ describe('33acrossBidAdapter:', function () { expect(builtServerRequests).to.deep.equal([serverRequest]); }); }); + + context('when price floor module is enabled for video in bidRequest', function() { + it('does not set any bidfloors in video if there is no floor', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'outstream'}) + .build() + ); + + bidRequests[0].getFloor = () => ({}); + + const ttxRequest = new TtxRequestBuilder() + .withVideo() + .withProduct() + .build(); + + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + }); + + it('sets bidfloors in video if there is a floor', function() { + const bidRequests = ( + new BidRequestsBuilder() + .withVideo({context: 'outstream'}) + .build() + ); + + bidRequests[0].getFloor = ({size, currency, mediaType}) => { + const floor = (mediaType === 'video') ? 1.0 : 0.10 + return ( + { + floor, + currency: 'USD' + } + ); + }; + + const ttxRequest = new TtxRequestBuilder() + .withVideo() + .withProduct() + .withFloors('video', [ 1.0 ]) + .build(); + + const builtServerRequests = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + }); + }); }); describe('interpretResponse', function() { @@ -741,6 +1247,8 @@ describe('33acrossBidAdapter:', function () { beforeEach(function() { ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() .withSite({ id: SITE_ID, page: 'https://test-url.com' @@ -757,7 +1265,7 @@ describe('33acrossBidAdapter:', function () { }); context('when exactly one bid is returned', function() { - it('interprets and returns the single bid response', function() { + it('interprets and returns the single banner bid response', function() { const serverResponse = { cur: 'USD', ext: {}, @@ -784,12 +1292,56 @@ describe('33acrossBidAdapter:', function () { ad: '

I am an ad

', ttl: 60, creativeId: 1, + mediaType: 'banner', currency: 'USD', netRevenue: true }; expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([bidResponse]); }); + + it('interprets and returns the single video bid response', function() { + const videoBid = ''; + const serverResponse = { + cur: 'USD', + ext: {}, + id: 'b1', + seatbid: [ + { + bid: [{ + id: '1', + adm: videoBid, + ext: { + ttx: { + mediaType: 'video', + vastType: 'xml' + } + }, + crid: 1, + h: 250, + w: 300, + price: 0.0938 + }] + } + ] + }; + const bidResponse = { + requestId: 'b1', + bidderCode: BIDDER_CODE, + cpm: 0.0938, + width: 300, + height: 250, + ad: videoBid, + ttl: 60, + creativeId: 1, + mediaType: 'video', + currency: 'USD', + netRevenue: true, + vastXml: videoBid + }; + + expect(spec.interpretResponse({ body: serverResponse }, serverRequest)).to.deep.equal([bidResponse]); + }); }); context('when no bids are returned', function() { @@ -852,6 +1404,7 @@ describe('33acrossBidAdapter:', function () { ad: '

I am an ad

', ttl: 60, creativeId: 1, + mediaType: 'banner', currency: 'USD', netRevenue: true }; @@ -886,9 +1439,13 @@ describe('33acrossBidAdapter:', function () { }, adUnitCode: 'div-id', auctionId: 'r1', - sizes: [ - [300, 250] - ], + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, transactionId: 't1' }, { @@ -901,9 +1458,13 @@ describe('33acrossBidAdapter:', function () { }, adUnitCode: 'div-id', auctionId: 'r1', - sizes: [ - [300, 250] - ], + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, transactionId: 't2' } ]; From 014b65f29cfc812e8e3e26a6c5c66939f494cd86 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 4 Nov 2020 14:51:57 -0800 Subject: [PATCH 0340/1476] Prebid 4.15.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6c78a45c5c..ce8c660a869 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.15.0-pre", + "version": "4.15.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d3eec3a0c6cb973493e44cffed530a9e137f9a02 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 4 Nov 2020 15:18:15 -0800 Subject: [PATCH 0341/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce8c660a869..8dc3535cc21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.15.0", + "version": "4.16.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 28473bcf5981ea101270d986bb239b5f924334bc Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Thu, 5 Nov 2020 14:20:29 +0100 Subject: [PATCH 0342/1476] Improve Digital adapter: eids support (#5935) * Improve Digital adapter: eids support * Fix quotes --- modules/improvedigitalBidAdapter.js | 11 +++++++++++ .../modules/improvedigitalBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 3c000258ede..0e2d8f6f7dd 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -3,6 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; +import { createEidsArray } from './userId/eids.js'; const BIDDER_CODE = 'improvedigital'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -56,6 +57,13 @@ export const spec = { requestParameters.schain = bidRequests[0].schain; + if (bidRequests[0].userId) { + const eids = createEidsArray(bidRequests[0].userId); + if (eids.length) { + utils.deepSetValue(requestParameters, 'user.ext.eids', eids); + } + } + let requestObj = idClient.createRequest( normalizedBids, // requestObject requestParameters @@ -552,6 +560,9 @@ export function ImproveDigitalAdServerJSClient(endPoint) { if (requestParameters.schain) { impressionBidRequestObject.schain = requestParameters.schain; } + if (requestParameters.user) { + impressionBidRequestObject.user = requestParameters.user; + } if (extraRequestParameters) { for (let prop in extraRequestParameters) { impressionBidRequestObject[prop] = extraRequestParameters[prop]; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 5a20944a6ed..89ec5aed8c3 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -154,6 +154,8 @@ describe('Improve Digital Adapter Tests', function () { expect(params.bid_request.version).to.equal(`${spec.version}-${idClient.CONSTANTS.CLIENT_VERSION}`); expect(params.bid_request.gdpr).to.not.exist; expect(params.bid_request.us_privacy).to.not.exist; + expect(params.bid_request.schain).to.not.exist; + expect(params.bid_request.user).to.not.exist; expect(params.bid_request.imp).to.deep.equal([ { id: '33e9500b21129f', @@ -345,6 +347,22 @@ describe('Improve Digital Adapter Tests', function () { expect(params.bid_request.schain).to.equal(schain); }); + it('should add eids', function () { + const userId = { id5id: { uid: '1111' } }; + const expectedUserObject = { ext: { eids: [{ + source: 'id5-sync.com', + uids: [{ + atype: 1, + id: '1111' + }] + }]}}; + const bidRequest = Object.assign({}, simpleBidRequest); + bidRequest.userId = userId; + const request = spec.buildRequests([bidRequest], bidderRequestReferrer)[0]; + const params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.user).to.deep.equal(expectedUserObject); + }); + it('should return 2 requests', function () { const requests = spec.buildRequests([ simpleBidRequest, From bb60431114c9bfcd870aee9d41d5942bca865cea Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Thu, 5 Nov 2020 18:16:32 +0200 Subject: [PATCH 0343/1476] Adkernel: andbeyond alias (#5922) --- modules/adkernelBidAdapter.js | 2 +- modules/andbeyondBidAdapter.md | 32 -------------------- test/spec/modules/adkernelBidAdapter_spec.js | 3 +- 3 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 modules/andbeyondBidAdapter.md diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index d489a1a84e0..0e9093b0f63 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/modules/andbeyondBidAdapter.md b/modules/andbeyondBidAdapter.md deleted file mode 100644 index 7d58bac0abc..00000000000 --- a/modules/andbeyondBidAdapter.md +++ /dev/null @@ -1,32 +0,0 @@ -# Overview - -``` -Module Name: andbeyond Bidder Adapter -Module Type: Bidder Adapter -Maintainer: shreyanschopra@rtbdemand.com -``` - -# Description - -Connects to andbeyond whitelabel platform. -Banner formats are supported. - - -# Test Parameters -``` - var adUnits = [ - { - code: 'banner-ad-div', - sizes: [[300, 250]], // banner size - bids: [ - { - bidder: 'andbeyond', - params: { - zoneId: '30164', //required parameter - host: 'cpm.metaadserving.com' //required parameter - } - } - ] - } - ]; -``` diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 1483816d94d..4015a56e82b 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,8 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(6); - expect(spec.aliases).to.include.members(['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon']); + expect(spec.aliases).to.have.lengthOf(7); }); }); From f9fdc9cab59bdb5b21b875b13eb832e2a035edcb Mon Sep 17 00:00:00 2001 From: lunamedia <73552749+lunamedia@users.noreply.github.com> Date: Sat, 7 Nov 2020 01:19:36 +0200 Subject: [PATCH 0344/1476] LunamediaHB bid adapter (#5906) --- modules/lunamediahbBidAdapter.js | 107 ++++++ modules/lunamediahbBidAdapter.md | 72 +++++ .../modules/lunamediahbBidAdapter_spec.js | 304 ++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 modules/lunamediahbBidAdapter.js create mode 100644 modules/lunamediahbBidAdapter.md create mode 100644 test/spec/modules/lunamediahbBidAdapter_spec.js diff --git a/modules/lunamediahbBidAdapter.js b/modules/lunamediahbBidAdapter.js new file mode 100644 index 00000000000..00f7a9a056c --- /dev/null +++ b/modules/lunamediahbBidAdapter.js @@ -0,0 +1,107 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'lunamediahb'; +const AD_URL = 'https://balancer.lmgssp.com/?c=o&m=multi'; + +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); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + + const placements = []; + const request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + '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 || {}, + }; + const mediaType = bid.mediaTypes + + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { + placement.sizes = mediaType[BANNER].sizes; + placement.traffic = BANNER; + } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { + placement.wPlayer = mediaType[VIDEO].playerSize[0]; + placement.hPlayer = mediaType[VIDEO].playerSize[1]; + placement.traffic = VIDEO; + } else if (mediaType && mediaType[NATIVE]) { + placement.native = mediaType[NATIVE]; + placement.traffic = NATIVE; + } + 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)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/lunamediahbBidAdapter.md b/modules/lunamediahbBidAdapter.md new file mode 100644 index 00000000000..184dd846a9d --- /dev/null +++ b/modules/lunamediahbBidAdapter.md @@ -0,0 +1,72 @@ +# Overview + +``` +Module Name: lunamedia Bidder Adapter +Module Type: lunamedia Bidder Adapter +Maintainer: support@lunamedia.io +``` + +# Description + +Module that connects to lunamedia demand sources + +# Test Parameters +``` + var adUnits = [ + { + code:'1', + mediaTypes:{ + banner: { + sizes: [[300, 250]], + } + }, + bids:[ + { + bidder: 'lunamediahb', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids:[ + { + bidder: 'lunamediahb', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + native: { + title: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids:[ + { + bidder: 'lunamediahb', + params: { + placementId: 0 + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/lunamediahbBidAdapter_spec.js b/test/spec/modules/lunamediahbBidAdapter_spec.js new file mode 100644 index 00000000000..e9f88935ed5 --- /dev/null +++ b/test/spec/modules/lunamediahbBidAdapter_spec.js @@ -0,0 +1,304 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/lunamediahbBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; + +describe('LunamediaHBBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'lunamediahb', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 783, + traffic: BANNER + } + }; + + 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://balancer.lmgssp.com/?c=o&m=multi'); + }); + 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'); + 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.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement.placementId).to.equal(783); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + 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'); + }); + 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' + }] + }; + 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'); + 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'); + }); + 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', + }] + }; + 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'); + 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'); + }); + 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; + }); + }); +}); From 7efc3330e8e5013dab727a8024c4ce9d6ba1b6ee Mon Sep 17 00:00:00 2001 From: Lyubomir Shishkov <61063794+lyubomirshishkov@users.noreply.github.com> Date: Mon, 9 Nov 2020 10:05:51 +0200 Subject: [PATCH 0345/1476] Add User ID Targeting to googletag.cmd as a fallback when GPT API is not ready (#5925) * Add User IDs to googletag.cmd The purpose of this change is to allow the userIdTargeting module to function even when googletag has not been defined yet. * Fixing indentation errors Fixing indentation errors thrown by * Fix 'googletag' is not defined errors * Added unit test for userIdTargeting fallback --- modules/userIdTargeting.js | 14 ++++++++------ test/spec/modules/shareUserIds_spec.js | 10 ++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/userIdTargeting.js b/modules/userIdTargeting.js index 3ed8b2a14b5..e15c9ddaca2 100644 --- a/modules/userIdTargeting.js +++ b/modules/userIdTargeting.js @@ -20,14 +20,16 @@ export function userIdTargeting(userIds, config) { if (!SHARE_WITH_GAM) { logInfo(MODULE_NAME + ': Not enabled for ' + GAM); - } - - if (window.googletag && isFn(window.googletag.pubads) && hasOwn(window.googletag.pubads(), 'setTargeting') && isFn(window.googletag.pubads().setTargeting)) { + } else if (window.googletag && isFn(window.googletag.pubads) && hasOwn(window.googletag.pubads(), 'setTargeting') && isFn(window.googletag.pubads().setTargeting)) { GAM_API = window.googletag.pubads().setTargeting; } else { - SHARE_WITH_GAM = false; - logInfo(MODULE_NAME + ': Could not find googletag.pubads().setTargeting API. Not adding User Ids in targeting.') - return; + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + GAM_API = function (key, value) { + window.googletag.cmd.push(function () { + window.googletag.pubads().setTargeting(key, value); + }); + }; } Object.keys(userIds).forEach(function(key) { diff --git a/test/spec/modules/shareUserIds_spec.js b/test/spec/modules/shareUserIds_spec.js index 451892919cb..67e39533fc7 100644 --- a/test/spec/modules/shareUserIds_spec.js +++ b/test/spec/modules/shareUserIds_spec.js @@ -50,6 +50,16 @@ describe('#userIdTargeting', function() { pubads.setTargeting('test', ['TEST']); config.GAM_KEYS.tdid = ''; userIdTargeting(userIds, config); + expect(pubads.getTargeting('tdid')).to.be.an('array').that.is.empty; expect(pubads.getTargeting('test')).to.deep.equal(['TEST']); }); + + it('User Id Targeting is added to googletag queue when GPT is not ready', function() { + let pubads = window.googletag.pubads; + delete window.googletag.pubads; + userIdTargeting(userIds, config); + window.googletag.pubads = pubads; + window.googletag.cmd.map(command => command()); + expect(window.googletag.pubads().getTargeting('TD_ID')).to.deep.equal(['my-tdid']); + }); }); From 83279da21e8d4a8d29b1be7fdbbde354287b6722 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Mon, 9 Nov 2020 10:46:55 -0800 Subject: [PATCH 0346/1476] No bid version 1.2.9 (#5794) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. Co-authored-by: Reda Guermas --- modules/nobidBidAdapter.js | 21 ++++++- test/spec/modules/nobidBidAdapter_spec.js | 69 +++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 00cb14dc01d..051202cab97 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.8'; +window.nobidVersion = '1.2.9'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -114,6 +114,23 @@ function nobidBuildRequests(bids, bidderRequest) { utils.logWarn('Could not parse screen dimensions, error details:', e); } } + var getEIDs = function(eids) { + if (utils.isArray(eids) && eids.length > 0) { + let src = []; + eids.forEach((eid) => { + let ids = []; + if (eid.uids) { + eid.uids.forEach(value => { + ids.push({'id': value.id + ''}); + }); + } + if (eid.source && ids.length > 0) { + src.push({source: eid.source, uids: ids}); + } + }); + return src; + } + } var state = {}; state['sid'] = siteId; state['l'] = topLocation(bidderRequest); @@ -131,6 +148,8 @@ function nobidBuildRequests(bids, bidderRequest) { if (sch) state['schain'] = sch; const cop = coppa(); if (cop) state['coppa'] = cop; + const eids = getEIDs(utils.deepAccess(bids, '0.userIdAsEids')); + if (eids && eids.length > 0) state['eids'] = eids; return state; } function newAdunit(adunitObject, adunits) { diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 346356e7d5b..e67d3b41f1d 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -228,6 +228,75 @@ describe('Nobid Adapter', function () { }); }); + describe('buildRequestsEIDs', function () { + const SITE_ID = 2; + const REFERER = 'https://www.examplereferer.com'; + let bidRequests = [ + { + 'bidder': 'nobid', + 'params': { + 'siteId': SITE_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'userIdAsEids': [ + { + 'source': 'criteo.com', + 'uids': [ + { + 'id': 'CRITEO_ID', + 'atype': 1 + } + ] + }, + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5_ID', + 'atype': 1 + } + ], + 'ext': { + 'linkType': 0 + } + }, + { + 'source': 'adserver.org', + 'uids': [ + { + 'id': 'TD_ID', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + } + ] + } + ]; + + let bidderRequest = { + refererInfo: {referer: REFERER} + } + + it('should criteo eid', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.sid).to.exist.and.to.equal(2); + expect(payload.eids[0].source).to.exist.and.to.equal('criteo.com'); + expect(payload.eids[0].uids[0].id).to.exist.and.to.equal('CRITEO_ID'); + expect(payload.eids[1].source).to.exist.and.to.equal('id5-sync.com'); + expect(payload.eids[1].uids[0].id).to.exist.and.to.equal('ID5_ID'); + expect(payload.eids[2].source).to.exist.and.to.equal('adserver.org'); + expect(payload.eids[2].uids[0].id).to.exist.and.to.equal('TD_ID'); + }); + }); + describe('buildRequests', function () { const SITE_ID = 2; const REFERER = 'https://www.examplereferer.com'; From 49f0be3bb55b37d5f43881abc4060edc54efc703 Mon Sep 17 00:00:00 2001 From: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Date: Mon, 9 Nov 2020 17:20:08 -0500 Subject: [PATCH 0347/1476] EMX Adding Schain forwarding (#5946) * adding ccpa support for emx_digital adapter * emx_digital ccpa compliance: lint fix * emx 3.0 compliance update * fix outstream renderer issue, update test spec * refactor formatVideoResponse function to use core-js/find * Add support for schain forwarding Co-authored-by: Nick Colletti Co-authored-by: Nick Colletti Co-authored-by: Kiyoshi Hara Co-authored-by: Dan Bogdan Co-authored-by: Jherez Taylor Co-authored-by: EMXDigital --- modules/emx_digitalBidAdapter.js | 12 +++++++++++ .../modules/emx_digitalBidAdapter_spec.js | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index fa58481548a..72da18d5691 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -156,6 +156,17 @@ export const emxAdapter = { }; } + return emxData; + }, + getSupplyChain: (bidRequests, emxData) => { + if (bidRequests.schain) { + emxData.source = { + ext: { + schain: bidRequests.schain + } + }; + } + return emxData; } }; @@ -237,6 +248,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 } diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 138786b9c74..39e56638ece 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -367,6 +367,27 @@ describe('emx_digital Adapter', function () { expect(request.us_privacy).to.exist; expect(request.us_privacy).to.exist.and.to.equal(consentString); }); + + it('should add schain object to request', function() { + const schainBidderRequest = utils.deepClone(bidderRequest); + schainBidderRequest.schain = { + 'complete': 1, + 'ver': '1.0', + 'nodes': [ + { + 'asi': 'testing.com', + 'sid': 'abc', + 'hp': 1 + } + ] + }; + let request = spec.buildRequests(schainBidderRequest.bids, schainBidderRequest); + request = JSON.parse(request.data); + expect(request.source.ext.schain).to.exist; + expect(request.source.ext.schain).to.have.property('complete', 1); + expect(request.source.ext.schain).to.have.property('ver', '1.0'); + expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.schain.nodes[0].asi); + }); }); describe('interpretResponse', function () { From 0a6b3229f6c70b2f8c622a2689f73f58f6de4430 Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Mon, 9 Nov 2020 17:21:24 -0500 Subject: [PATCH 0348/1476] pubGENIUS bid adapter: fix bug that requestBids timeout is not respected (#5940) * fix requestBids timeout * fix pubgenius bid adapter test --- modules/pubgeniusBidAdapter.js | 2 +- test/spec/modules/pubgeniusBidAdapter_spec.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 05f18f99a9a..55f50e4b6a9 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -37,7 +37,7 @@ export const spec = { const data = { id: bidderRequest.auctionId, imp: bidRequests.map(buildImp), - tmax: config.getConfig('bidderTimeout'), + tmax: bidderRequest.timeout, ext: { pbadapter: { version: BIDDER_VERSION, diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 52f2e3aeefe..4b5bf7efac0 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -122,6 +122,7 @@ describe('pubGENIUS adapter', () => { bidderCode: 'pubgenius', bidderRequestId: 'fakebidderrequestid', refererInfo: {}, + timeout: 1200, }; expectedRequest = { @@ -149,7 +150,7 @@ describe('pubGENIUS adapter', () => { }; config.setConfig({ - bidderTimeout: 1200, + bidderTimeout: 1000, pageUrl: undefined, coppa: undefined, }); From ffc62100a173946b01d4c73a9e7d0424901f8637 Mon Sep 17 00:00:00 2001 From: rimaburder-index <55195208+rimaburder-index@users.noreply.github.com> Date: Mon, 9 Nov 2020 17:21:50 -0500 Subject: [PATCH 0349/1476] Updated the text in line 292 (#5937) Updated the text in line 292 --- modules/ixBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index c2e308870db..5b9903c91d2 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -289,7 +289,7 @@ pbjs.setConfig({ }); ``` #### The **detectMissingSizes** feature -With a recent update, the IX bid adapter bids on all banner sizes available in an ad unit, if IX is configured for at least one banner size in that ad unit. This default behavior if not required, can be turned off by using the `detectMissingSizes` flag. +By default, the IX bidding adapter bids on all banner sizes available in the ad unit when configured to at least one banner size. If you want the IX bidding adapter to only bid on the banner size it’s configured to, switch off this feature using `detectMissingSizes`. ``` pbjs.setConfig({ ix: { From 7070b1f78b3c0a49bab4a3bfb5fadc2886e92612 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Tue, 10 Nov 2020 05:22:43 +0700 Subject: [PATCH 0350/1476] Update for Qwarry bid adapter (#5936) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 3 ++- test/spec/modules/qwarryBidAdapter_spec.js | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 36c1562324a..0c6dee9be69 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -19,7 +19,8 @@ export const spec = { validBidRequests.forEach(bidRequest => { bids.push({ bidId: bidRequest.bidId, - zoneToken: bidRequest.params.zoneToken + zoneToken: bidRequest.params.zoneToken, + pos: bidRequest.params.pos }) }) diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index a5bb438f384..02fe9c4538b 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -6,7 +6,8 @@ const REQUEST = { 'bidId': '456', 'bidder': 'qwarry', 'params': { - zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f' + zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', + pos: 7 } } @@ -70,7 +71,7 @@ describe('qwarryBidAdapter', function () { it('sends bid request to ENDPOINT via POST', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') - expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f' }) + expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') expect(bidderRequest.url).to.equal(ENDPOINT) From 9676ce0e16c668fd324cbba62ea52864c728d18f Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 10 Nov 2020 11:35:08 +0100 Subject: [PATCH 0351/1476] Adagio Bid Adapter: support UserId's (#5938) --- modules/adagioBidAdapter.js | 13 +++++- test/spec/modules/adagioBidAdapter_spec.js | 48 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 65a35284d1b..b20f832fd42 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -7,10 +7,11 @@ import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; import sha256 from 'crypto-js/sha256.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; +import { createEidsArray } from './userId/eids.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.4.0'; +export const VERSION = '2.5.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; export const SUPPORTED_MEDIA_TYPES = ['banner']; @@ -572,6 +573,12 @@ function _getSchain(bidRequest) { } } +function _getEids(bidRequest) { + if (utils.deepAccess(bidRequest, 'userId')) { + return createEidsArray(bidRequest.userId) + } +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -657,6 +664,7 @@ export const spec = { const uspConsent = _getUspConsent(bidderRequest) || {}; const coppa = _getCoppa(); const schain = _getSchain(validBidRequests[0]); + const eids = _getEids(validBidRequests[0]) || []; const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); return bidRequest; @@ -691,6 +699,9 @@ export const spec = { ccpa: uspConsent }, schain: schain, + user: { + eids: eids + }, prebidVersion: '$prebid.version$', adapterVersion: VERSION, featuresVersion: FEATURES_VERSION diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index a18cd797d68..86fb2e7cbd3 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -287,6 +287,7 @@ describe('Adagio bid adapter', () => { 'pageviewId', 'adUnits', 'regs', + 'user', 'schain', 'prebidVersion', 'adapterVersion', @@ -573,6 +574,53 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.regs.ccpa).to.be.empty; }); }); + + describe('with userID modules', function() { + const userId = { + sharedid: {id: '01EAJWWNEPN3CYMM5N8M5VXY22', third: '01EAJWWNEPN3CYMM5N8M5VXY22'}, + unsuported: '666' + } + + it('should send "user.eids" in the request for Prebid.js supported modules only', function() { + const bid01 = new BidRequestBuilder({ + userId + }).withParams().build(); + + const bidderRequest = new BidderRequestBuilder().build(); + + const requests = spec.buildRequests([bid01], bidderRequest); + + const expected = [{ + source: 'sharedid.org', + uids: [ + { + atype: 1, + ext: { + third: '01EAJWWNEPN3CYMM5N8M5VXY22' + }, + id: '01EAJWWNEPN3CYMM5N8M5VXY22' + } + ] + }] + + expect(requests[0].data.user.eids).to.have.lengthOf(1) + expect(requests[0].data.user.eids).to.deep.equal(expected) + }) + + it('should send an empty "user.eids" array in the request if userId module is unsupported', function() { + const bid01 = new BidRequestBuilder({ + userId: { + unsuported: '666' + } + }).withParams().build(); + + const bidderRequest = new BidderRequestBuilder().build(); + + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.user.eids).to.be.empty + }) + }) }); describe('interpretResponse()', function() { From 690709bc13b9b712630bde1fd63ca980846e955a Mon Sep 17 00:00:00 2001 From: Filip Stamenkovic Date: Tue, 10 Nov 2020 11:41:28 +0100 Subject: [PATCH 0352/1476] userId module: fix auctionDelay submodules with callbacks (#5891) * clearTimeout only after all submodules are done * check that setTimeout function was not cleared --- modules/userId/index.js | 9 +++++++-- test/spec/modules/userId_spec.js | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index a5e5fd4eff1..9ef4da0f96f 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -319,7 +319,13 @@ function hasGDPRConsent(consentData) { * @param {function} cb - callback for after processing is done. */ function processSubmoduleCallbacks(submodules, cb) { - const done = cb ? utils.delayExecution(cb, submodules.length) : function () { }; + let done = () => {}; + if (cb) { + done = utils.delayExecution(() => { + clearTimeout(timeoutID); + cb(); + }, submodules.length); + } submodules.forEach(function (submodule) { submodule.callback(function callbackCompleted(idObj) { // if valid, id data should be saved to cookie/html storage @@ -338,7 +344,6 @@ function processSubmoduleCallbacks(submodules, cb) { // clear callback, this prop is used to test if all submodule callbacks are complete below submodule.callback = undefined; }); - clearTimeout(timeoutID); } /** diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index c5ab2e249fc..d5ed96a5bc1 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -611,6 +611,7 @@ describe('User ID', function () { beforeEach(function () { sandbox = sinon.createSandbox(); sandbox.stub(global, 'setTimeout').returns(2); + sandbox.stub(global, 'clearTimeout'); sandbox.stub(events, 'on'); sandbox.stub(coreStorage, 'getCookie'); @@ -662,6 +663,7 @@ describe('User ID', function () { requestBidsHook(auctionSpy, {adUnits}); // check auction was delayed + global.clearTimeout.calledOnce.should.equal(false); global.setTimeout.calledOnce.should.equal(true); global.setTimeout.calledWith(sinon.match.func, 33); auctionSpy.calledOnce.should.equal(false); @@ -696,6 +698,7 @@ describe('User ID', function () { // check auction was delayed // global.setTimeout.calledOnce.should.equal(true); + global.clearTimeout.calledOnce.should.equal(false); global.setTimeout.calledWith(sinon.match.func, 33); auctionSpy.calledOnce.should.equal(false); From fee75c7322d3e6064bd2c9a3220751503d539c97 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Tue, 10 Nov 2020 17:00:01 +0530 Subject: [PATCH 0353/1476] fix circle ci failing lint error (#5952) --- modules/lunamediahbBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/lunamediahbBidAdapter.js b/modules/lunamediahbBidAdapter.js index 00f7a9a056c..1376d0c1714 100644 --- a/modules/lunamediahbBidAdapter.js +++ b/modules/lunamediahbBidAdapter.js @@ -1,4 +1,4 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; @@ -70,7 +70,7 @@ export const spec = { schain: bid.schain || {}, }; const mediaType = bid.mediaTypes - + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { placement.sizes = mediaType[BANNER].sizes; placement.traffic = BANNER; From 1bbe837f764cce8da6559dc259f849c63fb9704b Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 10 Nov 2020 11:23:38 -0500 Subject: [PATCH 0354/1476] PR-Review process: fleshing out RTD review (#5948) * PR-Review process: fleshing out RTD review * align bidrequest attribute --- PR_REVIEW.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index a4bcc43b11a..f991a0254f5 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -89,9 +89,14 @@ Documentation they're supposed to be following is https://docs.prebid.org/dev-do Follow steps above for general review process. In addition: - The RTD Provider must include a `providerRtdProvider.md` file. This file must have example parameters and document a sense of what to expect: what should change in the bidrequest, or what targeting data should be added? - Try running the new sub-module and confirm the provided test parameters. -- Make sure the sub-module is making HTTP requests as early as possible, but not more often than needed. +- Confirm that the module + - is not loading external code. If it is, escalate to the #prebid-js Slack channel. + - is reading `config` from the function signature rather than calling `getConfig`. + - is sending data to the bid request only as either First Party Data or in bidRequest.rtd.RTDPROVIDERCODE. + - is making HTTPS requests as early as possible, but not more often than needed. + - doesn't force bid adapters to load additional code. - Consider whether the kind of data the module is obtaining could have privacy implications. If so, make sure they're utilizing the `consent` data passed to them. -- make sure there's a docs pull request +- Make sure there's a docs pull request ## Ticket Coordinator From 0de9caf66128a42b68f7da514707a238305c5a4a Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 10 Nov 2020 18:29:26 +0100 Subject: [PATCH 0355/1476] delete pubcommon test cookie for domainOverride after writing it in all cases (#5943) * delete pubcommon test cookie after writing it in all cases, not just when it is found again * fix lunamediahbBidAdapter lint issue * call domainOverride only when needed in the module, not ahead of time when the module is registered. --- modules/pubCommonIdSystem.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index 51b4335fe60..919e735d34e 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -37,7 +37,7 @@ function storeData(config, value) { if (config.storage.type === COOKIE) { if (storage.cookiesAreEnabled()) { - storage.setCookie(key, value, expiresStr, 'LAX', COOKIE_DOMAIN); + storage.setCookie(key, value, expiresStr, 'LAX', pubCommonIdSubmodule.domainOverride()); } } else if (config.storage.type === LOCAL_STORAGE) { if (storage.hasLocalStorage()) { @@ -282,16 +282,19 @@ export const pubCommonIdSubmodule = { domainOverride: function () { const domainElements = document.domain.split('.'); const cookieName = `_gd${Date.now()}`; - for (let i = 0, topDomain; i < domainElements.length; i++) { + for (let i = 0, topDomain, testCookie; i < domainElements.length; i++) { const nextDomain = domainElements.slice(i).join('.'); // write test cookie storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); // read test cookie to verify domain was valid - if (storage.getCookie(cookieName) === '1') { - // delete test cookie - storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); + testCookie = storage.getCookie(cookieName); + + // delete test cookie + storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); + + if (testCookie === '1') { // cookie was written successfully using test domain so the topDomain is updated topDomain = nextDomain; } else { @@ -302,6 +305,4 @@ export const pubCommonIdSubmodule = { } }; -const COOKIE_DOMAIN = pubCommonIdSubmodule.domainOverride(); - submodule('userId', pubCommonIdSubmodule); From e065e87da9c958468fb692bfdba348dbc0ff67af Mon Sep 17 00:00:00 2001 From: Salomon Rada Date: Wed, 11 Nov 2020 00:03:07 +0200 Subject: [PATCH 0356/1476] Gamoshi - Add new alias (#5895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add logic to prefer prebid modules over external modules in build process (#4124) * add check in getModules helper function * update logic based on feedback * update node version of project * Improve Digital adapter: adding bid floor, referrer, more native fields (#4103) * Bid floor, https, native ad update * Update the ad server protocol module * Adding referrer * YIELDONE adapter - change urls to adapt https (#4139) * update: change urls to adapt https * fix test code * Added SupplyChain Object support and an onTimeout Callback (#4137) * - 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. * Revert "Added SupplyChain Object support and an onTimeout Callback (#4137)" This reverts commit e61b246b45bd2c2390350eaeca693f208b1a3a24. This commit doesn't use the schain module added in #4084 * 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 * use adUnits from orbidder_example.html * replace obsolete functions * forgot to commit the test * check if bidderRequest object is available * try to fix weird safari/ie issue * ebayK: add more params * update orbidderBidAdapter.md * use spec. instead of this. for consistency reasons * add bidfloor parameter to params object * fix gdpr object handling * default to consentRequired: false when not explicitly given * wip - use onSetTargeting callback * add tests for onSetTargeting callback * fix params and respective tests * remove not used bid.params.keyValues * add bidRequestCount to orbidder.otto.de/bid Post request * add bidRequestCount to test object defaultBidRequest * PulsePoint: remove usage of deprecated utils method / prep for 3.0 (#4257) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * Removing usage of deprecated utils method * minor refactor * Use isArray method (#4288) * Add Parrable ID submodule (#4266) * add parrable id submodule * fix integration test config * fix var name * always refresh sotredId for parrable * add submodulesThatAlwaysRefresh concept * remove comment * add parrable url as one string * add parrable prod endpoint * use .indexOf instead of .includes * add params to test config * comment failing test * uncomment failing assertion * add parrable ID to prebid server adapter * add parrableIdSystem to .submodules.json * extract parrableId unit tests from userId spec * remove breakline between imports * remove unused param * remove userId generic feature from parrableId module * remove trailing space * fix failing test due to none merged conflict * Prebid 2.36.0 Release * Increment pre version * Support schain module and send bidfloor param in Sharethrough adapter (#4271) * Add support for supply chain object module Story: [#168742394](https://www.pivotaltracker.com/story/show/168742394) Co-authored-by: Josh Becker * Add bidfloor parameter to bid request sent to STX Story: [#168742573](https://www.pivotaltracker.com/story/show/168742573) * Platform One Analytics Adapter (#4233) * Added Y1 Analytics Adapter * rename y1AnalyticsAdapter in yieldoneAnalyticsAdapter * Yieldone Bid Adapter: fixes from lint check * Yieldone Analytics Adapter: fix endpoint protocol * Added spec file for yieldone Analytics Adapter * Fix parrable id integration example (#4317) * fix parrableId integration example * add parentheses * Improve Digital adapter: support for video (#4318) * Bid floor, https, native ad update * Update the ad server protocol module * Adding referrer * Improve Digital support for video * Improve Digital adapter: video * adapter version -> 6.0.0 * Gamoshi: Update aliases list. Add support for userSync. (#4319) * Add support for multi-format ad units. Add favoredMediaType property to params. * Add tests for gdpr consent. * Add adId to outbids * Modify media type resolving * Refactor multi-format ad units handler. * Modify the way of sending GDPR data. Update aliases. * Add new consent fields. Add unit test. * Add new consent fields. Add unit test. * Add support for id5 and unified id cookie sync. * Add support for id5 and unified id cookie sync. * Add restricted check for gdpr consent. * fix for userSync endpoint getting called with bidder alias names, instead of actual bidder names (#4265) * modify ixBidAdapater to always use the secure endpoint (#4323) * PubMatic to support Parrable User Id sub-module (#4324) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * PubMatic to support parrable id * VISX: currency validation & fix double escape of referer (#4299) * PubMatic to support coppa (#4336) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * added coppa compliance * vuble: outstream has fullscreen option (#4320) * Mod: vuble oustream has fullscreen option * Add: vuble test for outstream scenario * EMXDigital: hotfix to resolve URIError from decodeURIComponent (#4333) * hotfix to resolve URIError from decodeURIComponent * added unit for decoding adm * Specify second parameter for parseInt for pubmaticBidAdapter (#4347) * Remove usage of getTopWindowUrl in Prebid Adapter (#4341) * Conversant Bid Adapter update for 3.0 (#4284) * Add cpmDistribution function for Google Analytics adapter (#4240) * Add cpmDistribution function for Google Analytics adapter * Add test for the cpmDistribution function * Remove half written comment * fixing SRA p_pos (#4337) * In Sonobi Adapter, only read sizes from bid.mediaTypes (#4311) * Fix mediaTypes (#4332) * 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 * Fix mediaTypes * no message * Update outconBidAdapter_spec.js * Adding VAS response * no message * no message * no message * Fix * Changed ttl * no message * supportedMediaTypes * no message * no message * Prebid 2.37.0 release * increment pre version * Add vast xml support and other minor changes to Beachfront adapter (#4350) * Add support for vast xml in the bid response * add secure protocol to outstream player url * add device connection type * add player setting for poster color * add new value for creative Id * Update smartrtbBidAdapter (#4362) * 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 * Remove old params checks, fix documentation playerSize field name * Revert "Update smartrtbBidAdapter (#4362)" (#4368) This reverts commit be6704bcec65a28d80b6d09a8d1c51ef9a8ba824. * Add userSync in onetagBidAdapter (#4358) * Minor bug fixing in onetagBidAdapter.js Fixed a minor bug. Updated TTL in response to align the correct specifications. * Update onetagBidAdapter Added additional page info and user sync function. * Update onetagBidAdapter_spec.js Added the test for getUserSyncs function. * Fix about userSync * getUserSyncs: test update with gdpr params * Sovrn adapter updates: schain, digitrust, pixel syncing, and 3.0 upgrades (#4335) * schain and digitrust * pixel beacons * unit tests and fixes from testing * Prebid 3.0 updates * review fix * Add bid adapter for ablida (#4256) * Add ablida adapter * rename category parameter, add documentation * AdKernel: added waardex_ak alias (#4290) * added alias Added a new alias * fixing unit test * Revert "Sovrn adapter updates: schain, digitrust, pixel syncing, and 3.0 upgrades (#4335)" (#4376) This reverts commit 6114a3dba93815dcfb535707d7b4d84f1adb2bc7. * Vrtcal Markets Inc. Bid Adapter Addition (#4259) * Added 3 key Vrtcal Adapter files: adapter,markdown,unit tests * Removed unused getUserSyncs;Added mediaTypes.banner.sizes support;Raised test coverage to 85% * lint formatting errors corrected * Update schain path in ORTB path for spotxBidAdapter (#4377) - Move schain object from request.ext.source.ext.schain to request.source.ext.schain * Update Grid Bid Adapter (#4379) * Added Grid Bid Adapter * remove priceType from TheMediaGrid Bid Adapter * Add video support in Grid Bid Adapter * Added test parameter for video slot * update Grid Bid Adapter to set size in response bid * Update Grid Bid Adapter to support identical uids in parameters * Fix typo in test file for Grid Bid Adapter * Update The Grid Media Bidder Adapter to send refererInfo.referer as 'u' parameter in ad request * Hotfix for referrer in Grid Bid Adapter * Grid Bid Adapter: added wrapperType and wrappweVersion to the ad request * TripleLift: Sending schain (#4375) * 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 * TripleLift: Sending schain (#1) * Sending schain * null -> undefined * DistrictmDMX: adding support for schain and remove content type to default to prebid selection (#4366) * adding DMX test @97%, two files added one updated * Update districtm_spec.js * Update districtmDMX.js * adding all districtm needed file * remove legacy file * remove typo || 0 in the test method * force default to return a valid width and height * update unit test code for failing test * changed class for an object * remove package-lock.json * change file name for dmx adapter * renamed files * restaure package-lock.json * update to last package-lock state * update gdpr user consent * fix sizes issue * Documentation updates Adding the readme.md info * update file name and update unit testing import file location * current machine state * lint correction * remove variable assigment duplicate * Support for ID5 + receive meta data (#4352) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter * Collect info on ad units receiving any valid bid * Support for ID5 Pass metadata from adapter * Typo in test + eids on wrong level * Rubicon Adapter: Always make requests using HTTPS (#4380) * 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 * Always make bids requests using https * rp_secure and imp.secure should always be 1 * 7xbid adapter (#4328) * 7xbid adapter * fix error when cli build * - update 33across adapter cookie sync end point (#4345) - update unit test for 33across adapter * Adform adapter: add renderer for outstream bids (#4363) * Prebid 2.38.0 Release * Increment pre version * Adagio: update with external js (#4217) * Add external loader in AdagioBidAdapter * Change adagioAnalyticsAdapter to "endpoint" type * Change _setPredictions for a generic method * Improve AdagioBidAdapter test coverage * Add features detection in Adagio adapter * Fix adagioBidAdapter tests * Add featuresVersion field to bidRequest * Refacto adagio.queue * Expose versions in ADAGIO namespace * Generate a ADAGIO.pageviewId if missing * Move ad-server events tracking to adagioBidAdapter * Store adUnitCodes in ADAGIO namespace * Update documentation Better description of test parameters. * Add internal array to prevent empty pbjs.adUnits * Be sure to access to window.top - does not work in safe-frame env * Add PrintNumber feature * Be sure to compute features on window.top * Bump versions * Add Post-Bid support - ad-server events are listen in current window (instead of window.top) - a new "outerAdUnitElementId" property is set to ADAGIO.pbjsAdUnits array in case of Post-Bid scenario. This property is the 1st parent element id attribute of the iframe in window.top. * Set pagetype param as optional * Add AdThink ad-server support * Improve internal `pbjsAdUnits.sizes` detection Use the adUnit `mediaTypes.banner.sizes` property if exists to build the `ADAGIO.pbjsAdUnits.sizes`. The use of the `sizes` root property is deprecated. * adagioAnalyticsAdapter: add and improve tests * adagioBidAdapter: add and improve tests # Conflicts: # modules/adagioBidAdapter.js # test/spec/modules/adagioBidAdapter_spec.js * adagioBidAdapter: Bump version 1.5 * Adagio: fix import path * PostBid: insure window.top is accessible for specifics functions * Consistency: use Prebid.js utils and fix deprecated * PostBid: do not build a request if in safeframe * Bump version 2.0.0 * Try to fix tests without UA stubing * Try to fix adagioAnalytics failling tests on CI * Consistency: use Prebid loadExternalScript() * Add "adagio" to Prebid.js adloader vendor whitelist * Remove proprietary ad-server listeners * Add RSA validation to adagio external script * add viewdeosDX whitelabel (#4231) * add viewdeosDX hitelabel * Fixed tests and support for sizes * Fix strings * Fix strings * remove only * Fix tests * fix codereview * Fix test + Code review * code review + tests * One video display ad (#4344) * 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 * testing display ad * adding banner * validating banner object * display=1 changes * checking whether diplsy == 1 * html page change * reverting video.html * adding more test cases * spaces * md file change * updated working oneVideoBidAdapter.md file * Update oneVideoBidAdapter.md * Update oneVideoBidAdapter.md * updated the file with both video params and banner * Update video.html * fix double-urlecoded referrer (#4386) * fix double-urlecoded referer (#4388) * PulsePoint Adapter - update for ttl logic (#4400) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * Using the TTL from the bid.ext * Minor refactor * IdentityLink - add logic for sending consent string (#4346) * Fix adagio analytics adapter circleci (#4409) * 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 * update to skip broken circleci tests * skip all * Feature/7xbid remove unneeded params (#4402) * 7xbid adapter * fix error when cli build * remove unneeded params * Empty commit * Empty commit * Remove none ssl (#4406) * adding DMX test @97%, two files added one updated * Update districtm_spec.js * Update districtmDMX.js * adding all districtm needed file * remove legacy file * remove typo || 0 in the test method * force default to return a valid width and height * update unit test code for failing test * changed class for an object * remove package-lock.json * change file name for dmx adapter * renamed files * restaure package-lock.json * update to last package-lock state * update gdpr user consent * fix sizes issue * Documentation updates Adding the readme.md info * update file name and update unit testing import file location * current machine state * lint correction * remove variable assigment duplicate * remove none ssl element from all request] * fixed reference to global object (#4412) * ucfunnel adapter support supply chain (#4383) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel adapter support supply chain * LiveIntent support in RP Adapter and PBS Adapter update to pass segments (#4303) * 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 * added semi-colon * update eid source to use domain * update video oRTB with liveintent segments * update pbs adapter with liveintent segments support * update rp adapter liveintent support for fastlane * reverted package lock, fix for unintentional update * added unit tests for fastlane.json and ortb, fix to join segments with commas * fix obj property path data.tpid * update remove unnecessary function call * re-ordering query string params * Rubicon Adapter: Add multiple sizes to sizeMap (#4407) * Add Utils to remove item in LocalStorage (#4355) * Making originalCpm and originalCurrency fields in bid object always available (#4396) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * moving originalCurrency declaration from currency to bidderFactory * added a comment * trying to re-run the CI job * added unit test case * trying to re-run the CI job * Placement and inventory (#4353) * 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 * inventory_id and placement * removed unnecessary file * lint error * Update oneVideoBidAdapter.js * lint error fix * Fixes for Platform One Analytics Adapter (#4359) * Added Y1 Analytics Adapter * rename y1AnalyticsAdapter in yieldoneAnalyticsAdapter * Yieldone Bid Adapter: fixes from lint check * Yieldone Analytics Adapter: fix endpoint protocol * Added spec file for yieldone Analytics Adapter * Add adUnitName to analytics data for Yieldone Analytics Adapter * Fix yieldone Analytics Adapter to log only id from adUnitPath * Fix bug with timeout event in Yieldone Analytics Adapter * Added protocol to url (#4395) * initial commit * updated contact and tag details * changes ti support the renderers * changes to pass dimId * fixed names of internal mapping * added comment * added gdpr param to request and other fixes * modified api url * fix * fixed the secure api call * rolled back video event callback till we support it * updated doc with video details * added bid won and timeout pixel * added testcase for bid events * modified testcase * fixed the url logged * tag param values passed ot renderer * added a conditioal check * changes to support new param to adserver for purpose of tracking * passed param to renderer * missing variable defined * added protocol to url * fixed test for protocol * changed urls to secure only * Update emoteev endpoints (#4329) * JustPremium: Update to Prebid 3.0 (#4410) * Update underdogmedia adapter for pbjs 3.0 (#4390) * Update underdogmedia adapter for pbjs 3.0 * Ensure request to endpoint is secure * Update prebid version * Lint fix * Update Consumable adapter for Prebid.js 3.0 (#4401) * Consumable: Clean up tests. * Consumable: Update use of deprecated function. * Consumable: Read sizes from mediaTypes.banner.sizes. * Consumable: Fix lint violation. * CriteoId User Module (#4287) * Add CriteoId module * Update the return type of getId in Criteo Id module Changes: - Use of url parsing function from url lib - Update the return type of getId() - Update the jsdoc to reflect the real return types * Fix failing tests for Criteo user module * Add CriteoIdSystem submodule to .submodule.json. * 2019/10/18 Create Mobsmart bidder adapter (#4339) * Adpod deal support (#4389) * Adpod deal support * Replacing filterBids with minTier * fix potential issue * remove querystringify package (#4422) * Browsi real time data module (#4114) * 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 * Prebid 2.39.0 Release * increment pre version * OpenX Adapter: Prebid 3.0 Compatibility Update (#4413) * Removed usage of deprecated functions * Removed beacons * Banner sizes now reads from bidRequest.mediaTypes.banner.sizes instead of bidRequest.sizes * Updated tests to reflect changes. * GumGum: use mediaTypes.banner.sizes (#4416) * adds digitrust module, mods gdpr from bool to int * update unit test * only send gdprconsent if present * uses mediaTypes before trying bidRequest sizes * removes use of deprecated method * RTBhouse Bid Adapter update for 3.0 (#4428) * add viewable rendering format (#4201) * Feature/adapter (#4219) * feat(bidrequest): code for making bidrequest * feat(bidresponse): format and return the response * feat(tests): added tests for adapter * feat(docs): added docs for the adapter * refactor(url): changed adserver url * test(user sync): added unit tests for the user syncs * refactor(endpoint): changed endpoint for prebid * refactor(endpoint): changed endpoint for prebid * doc(tagid): mandatory param definition added * fix(imp id): fix for correct impression id * fix(width/height): fix for correct width and height sequence * PulsePoint Bid Adapter: Support for schain (#4433) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * ET-5938 SupplyChain Object Support * Formatting * Code review * Code review * Fix to currency parsing on response * Add supply chain support for Teads adapter (#4420) * Rubicon: support SupplyChain (schain) (#4315) * 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 * Starting schain * More tests for banner schain support * Video tests * Encoding tweaks, required fields and comments * Removed .only() from tests * Change requests per Bret * Add 1ad4good bidder (#4081) * adding bidder code and A bidder for non-profit free ads. more info about this bidder project can be found on project site http://1ad4good.org * removed unused code test coverage is improved to >80% tested for instream video support * removed some legacy code, unused params * hardcoding https to endpoint * Improve Digital adapter fix: don't send sizes for instream video (#4427) * Bid floor, https, native ad update * Update the ad server protocol module * Adding referrer * Improve Digital support for video * Improve Digital adapter: video * adapter version -> 6.0.0 * Improve Digital adapter: don't send sizes for video * Fix a typo in code comment (#4450) * Inventory id and schain support for display (#4426) * supporting schain * Update coinzillaBidAdapter.js (#4438) Update sizes const. * Support schain in ZEDO adapter (#4441) * changes to pass schain * PubMatic supporting updated Criteo User Id module (#4431) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * PubMatic supporting updated Criteo User Id module * added a comment to re-start CI * Remove duplicate param to fix unit tests (#4459) * Brightcom Bid Adapter update for 3.0 (#4343) * add support for min_height field in pbs native requests (#4434) * Supporting Alias via Video Requests (#4460) * New adapter Proxistore (#4365) * add test adapter and documentation * integration test with hello_world * reset package-lock.json * delete useless conditionnal * make integrate test work * revert hello-world * revert hello_world * fix descriptor * change adUnits for integration test * remove proxistore widget * uncomment file * change sizes * remove useless script tag * Implementation of setBidderConfig and bidder-specific data (#4334) * initial implementation of setBidderConfig * fix ie11 test errors * Support new setBidderConfig format. Include props from both config and bidderConfig in _getConfig * Use core-js Set to avoid issues with IE * Fix tests in IE * put registerSyncs back on bidderFactory * run bidder event methods with bidder config enabled * Prebid 2.40.0 Release * Increment pre version * Conversant Bid Adapter checks pubcid directly (#4430) * Cookie Sync functionality (#4457) * changing PID param value for testing * cookie sync integration * merge from upstream * Staq Adapter: update with meta envelope (#4372) * initial dev * fix staq adapter name * fix hello world staq call * get hello world working again * add user agent collection * fix some unite tests * Add STAQ Analytics Adapter doc * clean up hello world * fix tests to play nice with browserstack * fix around issues with browserstack and deep equals of objects * dump variable env testing since we can't mod user agent stuff in browserstack * Update STAQ adapter to stop using deprecated utils for referrer * remove package-lock.json changes via master rebase * improve call frequency for ref util * change ajax content type * adjust ajax request to not expect whitelisting * remove superflous commented-out code * update event package to use meta information in envelope rather than per event basis * fix formatting * more formatting fixes * more formatting! * Rhythmone Adapter - schain support (#4414) Circle CI failing tests are not related to this PR. * Media.net Adapter: Support Prebid 3.0 (#4378) * Media.net Adapter: Support Prebid 3.0 * Media.net Adapter: add tests to increase code coverage * Vi Adapter: Passes additional param in the bid request (#4134) * Add focus check (cherry picked from commit 9d6d6dfb83580d6a5ffed8faa5762db48f8fd44d) * Pass focus as numeric value (cherry picked from commit 9fae56a637f87b0d39cc1d24eeb1f9ff9df88f64) * Add unit test (cherry picked from commit 946710f2e9960b3839613d4bdf730e57ba38a964) * Sovrn adapter updates: schain, digitrust, pixel syncing, and 3.0 upgrades (#4385) * schain and digitrust * pixel beacons * unit tests and fixes from testing * Prebid 3.0 updates * review fix * use backwards compatible flatMap impl * update pixel tests * unit test fix * update one more url to ssl * fixed test * review updates * TheMediaGrid Bid Adapter update (#4447) * Added Grid Bid Adapter * remove priceType from TheMediaGrid Bid Adapter * Add video support in Grid Bid Adapter * Added test parameter for video slot * update Grid Bid Adapter to set size in response bid * Update Grid Bid Adapter to support identical uids in parameters * Fix typo in test file for Grid Bid Adapter * Update The Grid Media Bidder Adapter to send refererInfo.referer as 'u' parameter in ad request * Hotfix for referrer in Grid Bid Adapter * Grid Bid Adapter: added wrapperType and wrappweVersion to the ad request * TheMediaGrid Bid Adapter: added sync url * TheMediaGrid Bid Adapter: added GDPR params to sync url * TheMediaGrid Bid Adapter: added tests for getUserSyncs function * Conversant Bid Adapter adds support for extended ids (#4462) * Adkernel 3.0 compatibility (#4477) * Rubicon Adapter pchain support (#4480) * rubicon pchain support * removed describe.only * Implemented changes required to provide support for video in the IX bidding adapter for Instream and Outstream contexts. (#4424) * Default size filter & KVP support (#4452) * adding DMX test @97%, two files added one updated * Update districtm_spec.js * Update districtmDMX.js * adding all districtm needed file * remove legacy file * remove typo || 0 in the test method * force default to return a valid width and height * update unit test code for failing test * changed class for an object * remove package-lock.json * change file name for dmx adapter * renamed files * restaure package-lock.json * update to last package-lock state * update gdpr user consent * fix sizes issue * Documentation updates Adding the readme.md info * update file name and update unit testing import file location * current machine state * lint correction * remove variable assigment duplicate * adding logic upto5 * adding support for removing and shuffle sizes * adding array split test * re-assign none standard size to the request * resolve duplicate format inside format array * update .md and adaptor file for KVP support * remove array helper includes * inforce two digit after decimal * RUn error check nothing on my side but error form another adapter * add id5id to prebid server bid adapter (#4468) * Added _pbjsGlobals for tracking renames. Resolves #4254 (#4419) * Feature/smart video (#4367) * Adding outstream video support. * Fixing unit test. * Adding video instream support. * Handling video startDelay parameter. * Improving unit tests. * Fixing indent. * Handling the request when videoMediaType context is not supported. * Changing maintainer mail address. * Remove video outstream specific code. * Unit test updated. * do not select element that gets removed after dfp render (#4423) * add smms adapter (#4439) * add smms adapter * re-run ci, why adigo adapter failed?? * review comments fix, remove deprecated functions, fix unit test * Prebid 2.41.0 release * Increment pre version * adds schain param (#4442) * Create newborntownWeb adapter (#4455) * Create newborntownWeb adapter * only https protocol * Provide criteoId to server by user.ext.eids (#4478) * ucfunnel adapter fix error message in debug mode (#4338) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel fix error message in debug mode * explicitly check undefined to allow falsey values in getConfig (#4486) * Conversant Bid Adapter handles vast xml (#4492) * [feature] Add a config list of submodules that require refreshing the stored ID after each bid request (#4325) * add a feature to always refresh stored id on each bid request for submodules that require that * update test comments * Prebid 2.42.0 Release * Increment pre version * Make adhese adapter prebid 3.0 compatible (#4507) * Added 'adhese' attribute to bid that contains meta data - Jira AD-2642 * added DALE to adhese determination * extra config option: no format, but use size array as format string * Read sizes from mediaTypes.banner.sizes + Apply Eslint suggestions * Use map and join, add originData to response * properly use originData obj * Remove duplicated ids * Update tests * BugFix: Site id missing (#4467) * 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 * adding site id * adding placement and siteis * site id param test case * removing deprecated functions * correcting test cases * indentation * test cases fix * Add new bid adaptor alias Co-authored-by: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Co-authored-by: koji-eguchi <50477903+DAC-KOJI-EGUCHI@users.noreply.github.com> Co-authored-by: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Co-authored-by: Mike Chowla Co-authored-by: robdubois <53589945+robdubois@users.noreply.github.com> Co-authored-by: sumit116 Co-authored-by: nwlosinski Co-authored-by: Bret Gorsline Co-authored-by: bretg 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: Robert Ray Martinez III 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: 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: Léonard Labat 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: TheMediaGrid <44166371+TheMediaGrid@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: Olivier Co-authored-by: Gena 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 --- modules/gamoshiBidAdapter.js | 2 +- modules/gamoshiBidAdapter.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 2e09cf55d0a..48a142a66a6 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -42,7 +42,7 @@ export const helper = { export const spec = { code: 'gamoshi', - aliases: ['gambid', 'cleanmedia', '9MediaOnline'], + aliases: ['gambid', 'cleanmedia', '9MediaOnline', 'MobfoxX'], supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { diff --git a/modules/gamoshiBidAdapter.md b/modules/gamoshiBidAdapter.md index 6e930375059..49b727cecae 100644 --- a/modules/gamoshiBidAdapter.md +++ b/modules/gamoshiBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Gamoshi Bid Adapter Module Type: Bidder Adapter -Maintainer: salomon@gamoshi.com +Maintainer: dev@gamoshi.com ``` # Description From 7d9772009919eea9f72b54f18e7aa7fceac8ab5d Mon Sep 17 00:00:00 2001 From: Maxime Lequain Date: Wed, 11 Nov 2020 14:37:24 +0100 Subject: [PATCH 0357/1476] adot: add publisher id retrieval from bidder config (#5928) Co-authored-by: Maxime Lequain --- modules/adotBidAdapter.js | 7 ++++++- modules/adotBidAdapter.md | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 3d0af864a31..54bd9156b48 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -3,6 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {isStr, isArray, isNumber, isPlainObject, isBoolean, logError, replaceAuctionPrice} from '../src/utils.js'; import find from 'core-js-pure/features/array/find.js'; +import { config } from '../src/config.js'; const ADAPTER_VERSION = 'v1.0.0'; const BID_METHOD = 'POST'; @@ -334,13 +335,17 @@ function generateSiteFromAdUnitContext(adUnitContext) { if (!adUnitContext || !adUnitContext.refererInfo) return null; const domain = extractSiteDomainFromURL(adUnitContext.refererInfo.referer); + const publisherId = config.getConfig('adot.publisherId'); if (!domain) return null; return { page: adUnitContext.refererInfo.referer, domain: domain, - name: domain + name: domain, + publisher: { + id: publisherId + } }; } diff --git a/modules/adotBidAdapter.md b/modules/adotBidAdapter.md index 7fc1d84ee60..e1388311e23 100644 --- a/modules/adotBidAdapter.md +++ b/modules/adotBidAdapter.md @@ -214,3 +214,20 @@ const adUnit = { }] } ``` + +### PublisherId + +You can set a publisherId using `pbjs.setBidderConfig` for the bidder `adot` + +#### Example + +```javascript +pbjs.setBidderConfig({ + bidders: ['adot'], + config: { + adot: { + publisherId: '__MY_PUBLISHER_ID__' + } + } +}); +``` \ No newline at end of file From 2b0bd53604563b51165bf96af08b31f00ad8c2da Mon Sep 17 00:00:00 2001 From: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Date: Thu, 12 Nov 2020 22:06:09 +0530 Subject: [PATCH 0358/1476] Lemma bid adapter: Add user sync support (#5934) * 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. * Update lemmaBidAdapter.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter_spec.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.md Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.js Added user sync support into bid adapter. * updated include modules file extension. updated include modules js file extension. * Update lemmaBidAdapter_spec.js Added unit test for user sync feature. * Update lemmaBidAdapter.js Fixed format error. * Update lemmaBidAdapter_spec.js Fixed format error and typo error. --- modules/lemmaBidAdapter.js | 34 ++++++++++++++++++++--- test/spec/modules/lemmaBidAdapter_spec.js | 34 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/modules/lemmaBidAdapter.js b/modules/lemmaBidAdapter.js index 1ad660e5916..5941802f97d 100644 --- a/modules/lemmaBidAdapter.js +++ b/modules/lemmaBidAdapter.js @@ -5,10 +5,13 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; var BIDDER_CODE = 'lemma'; var LOG_WARN_PREFIX = 'LEMMA: '; var ENDPOINT = 'https://ads.lemmatechnologies.com/lemma/servad'; +var USER_SYNC = 'https://sync.lemmatechnologies.com/js/usersync.html?'; var DEFAULT_CURRENCY = 'USD'; var AUCTION_TYPE = 2; var DEFAULT_TMAX = 300; var DEFAULT_NET_REVENUE = false; +var pubId = 0; +var adunitId = 0; export var spec = { @@ -57,6 +60,29 @@ export var spec = { interpretResponse: (response, request) => { return parseRTBResponse(request, response.body); }, + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + let syncurl = USER_SYNC + 'pid=' + pubId; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: syncurl + }]; + } else { + utils.logWarn(LOG_WARN_PREFIX + 'Please enable iframe based user sync.'); + } + }, }; function _initConf(refererInfo) { @@ -167,8 +193,8 @@ function _getImpressionArray(request) { function endPointURL(request) { var params = request && request[0].params ? request[0].params : null; if (params) { - var pubId = params.pubId ? params.pubId : 0; - var adunitId = params.adunitId ? params.adunitId : 0; + pubId = params.pubId ? params.pubId : 0; + adunitId = params.adunitId ? params.adunitId : 0; return ENDPOINT + '?pid=' + pubId + '&aid=' + adunitId; } return null; @@ -183,7 +209,7 @@ function _getDomain(url) { function _getSiteObject(request, conf) { var params = request && request.params ? request.params : null; if (params) { - var pubId = params.pubId ? params.pubId : '0'; + pubId = params.pubId ? params.pubId : '0'; var siteId = params.siteId ? params.siteId : '0'; var appParams = params.app; if (!appParams) { @@ -204,7 +230,7 @@ function _getSiteObject(request, conf) { function _getAppObject(request) { var params = request && request.params ? request.params : null; if (params) { - var pubId = params.pubId ? params.pubId : 0; + pubId = params.pubId ? params.pubId : 0; var appParams = params.app; if (appParams) { return { diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js index a236ac17d71..a00c25d126c 100644 --- a/test/spec/modules/lemmaBidAdapter_spec.js +++ b/test/spec/modules/lemmaBidAdapter_spec.js @@ -331,5 +331,39 @@ describe('lemmaBidAdapter', function() { }); }); }); + describe('getUserSyncs', function() { + const syncurl_iframe = 'https://sync.lemmatechnologies.com/js/usersync.html?pid=1001'; + let sandbox; + beforeEach(function() { + sandbox = sinon.sandbox.create(); + }); + afterEach(function() { + sandbox.restore(); + }); + + it('execute as per config', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined)).to.deep.equal([{ + type: 'iframe', url: syncurl_iframe + }]); + }); + + it('CCPA/USP', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}&us_privacy=1NYN` + }]); + }); + + it('GDPR', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}&gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=` + }]); + }); + }); }); }); From 954acdb21f10fc14198f6c135364f81eee04d48c Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Thu, 12 Nov 2020 10:33:13 -0800 Subject: [PATCH 0359/1476] ID Library (#5863) * ID Library * build fix * build fix * build fix * build fix * Fixing review comments * Fixing review comments (debounce, full body scan option * Fixing review comments (var declaration, strict check) * Fixing space Co-authored-by: skocheri --- modules/idLibrary.js | 243 ++++++++++++++++++++++++++++ modules/idLibrary.md | 24 +++ test/spec/modules/idLibrary_spec.js | 61 +++++++ 3 files changed, 328 insertions(+) create mode 100644 modules/idLibrary.js create mode 100644 modules/idLibrary.md create mode 100644 test/spec/modules/idLibrary_spec.js diff --git a/modules/idLibrary.js b/modules/idLibrary.js new file mode 100644 index 00000000000..ba3cc0b5efb --- /dev/null +++ b/modules/idLibrary.js @@ -0,0 +1,243 @@ +import {getGlobal} from '../src/prebidGlobal.js'; +import {ajax} from '../src/ajax.js'; +import {config} from '../src/config.js'; +import * as utils from '../src/utils.js'; +import MD5 from 'crypto-js/md5.js'; + +let email; +let conf; +const LOG_PRE_FIX = 'ID-Library: '; +const CONF_DEFAULT_OBSERVER_DEBOUNCE_MS = 250; +const CONF_DEFAULT_FULL_BODY_SCAN = true; +const OBSERVER_CONFIG = { + subtree: true, + attributes: true, + attributeOldValue: false, + childList: true, + attirbuteFilter: ['value'], + characterData: true, + characterDataOldValue: false +}; +const logInfo = createLogInfo(LOG_PRE_FIX); +const logError = createLogError(LOG_PRE_FIX); + +function createLogInfo(prefix) { + return function (...strings) { + utils.logInfo(prefix + ' ', ...strings); + } +} + +function createLogError(prefix) { + return function (...strings) { + utils.logError(prefix + ' ', ...strings); + } +} + +function getEmail(value) { + const matched = value.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi); + if (!matched) { + return null; + } + logInfo('Email found' + matched[0]); + return matched[0]; +} + +function bodyAction(mutations, observer) { + logInfo('BODY observer on debounce called'); + // If the email is found in the input element, disconnect the observer + if (email) { + observer.disconnect(); + logInfo('Email is found, body observer disconnected'); + return; + } + + const body = document.body.innerHTML; + email = getEmail(body); + if (email !== null) { + logInfo(`Email obtained from the body ${email}`); + observer.disconnect(); + logInfo('Post data on email found in body'); + postData(); + } +} + +function targetAction(mutations, observer) { + logInfo('Target observer called'); + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + email = node.textContent; + + if (email) { + logInfo('Email obtained from the target ' + email); + observer.disconnect(); + logInfo('Post data on email found in target'); + postData(); + return; + } + } + } +} + +function addInputElementsElementListner(conf) { + logInfo('Adding input element listeners'); + const inputs = document.querySelectorAll('input[type=text], input[type=email]'); + + for (var i = 0; i < inputs.length; i++) { + logInfo(`Original Value in Input = ${inputs[i].value}`); + inputs[i].addEventListener('change', event => processInputChange(event)); + inputs[i].addEventListener('blur', event => processInputChange(event)); + } +} + +function removeInputElementsElementListner() { + logInfo('Removing input element listeners'); + const inputs = document.querySelectorAll('input[type=text], input[type=email]'); + + for (var i = 0; i < inputs.length; i++) { + inputs[i].removeEventListener('change', event => processInputChange(event)); + inputs[i].removeEventListener('blur', event => processInputChange(event)); + } +} + +function processInputChange(event) { + const value = event.target.value; + logInfo(`Modified Value of input ${event.target.value}`); + email = getEmail(value); + if (email !== null) { + logInfo('Email found in input ' + email); + postData(); + removeInputElementsElementListner(); + } +} + +function debounce(func, wait, immediate) { + var timeout; + return function () { + const context = this; + const args = arguments; + const later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + if (callNow) { + func.apply(context, args); + } else { + logInfo('Debounce wait time ' + wait); + timeout = setTimeout(later, wait); + } + }; +}; + +function handleTargetElement() { + const targetObserver = new MutationObserver(debounce(targetAction, conf.debounce, false)); + + const targetElement = document.getElementById(conf.target); + if (targetElement) { + email = targetElement.innerText; + + if (!email) { + logInfo('Finding the email with observer'); + targetObserver.observe(targetElement, OBSERVER_CONFIG); + } else { + logInfo('Target found with target ' + email); + logInfo('Post data on email found in target with target'); + postData(); + } + } +} + +function handleBodyElements() { + if (doesInputElementsHaveEmail()) { + logInfo('Email found in input elements ' + email); + logInfo('Post data on email found in target without'); + postData(); + return; + } + email = getEmail(document.body.innerHTML); + if (email !== null) { + logInfo('Email found in body ' + email); + logInfo('Post data on email found in the body without observer'); + postData(); + return; + } + addInputElementsElementListner(); + if (conf.fullscan === true) { + const bodyObserver = new MutationObserver(debounce(bodyAction, conf.debounce, false)); + bodyObserver.observe(document.body, OBSERVER_CONFIG); + } +} + +function doesInputElementsHaveEmail() { + const inputs = document.getElementsByTagName('input'); + + for (let index = 0; index < inputs.length; ++index) { + const curInput = inputs[index]; + email = getEmail(curInput.value); + if (email !== null) { + return true; + } + } + return false; +} + +function syncCallback() { + return { + success: function () { + logInfo('Data synced successfully.'); + }, + error: function () { + logInfo('Data sync failed.'); + } + } +} + +function postData() { + (getGlobal()).refreshUserIds(); + const userIds = (getGlobal()).getUserIds(); + if (Object.keys(userIds).length === 0) { + logInfo('No user ids'); + return; + } + logInfo('Users' + JSON.stringify(userIds)); + const syncPayload = {}; + syncPayload.hid = MD5(email).toString(); + syncPayload.uids = JSON.stringify(userIds); + const payloadString = JSON.stringify(syncPayload); + logInfo(payloadString); + ajax(conf.url, syncCallback(), payloadString, {method: 'POST', withCredentials: true}); +} + +function associateIds() { + if (window.MutationObserver || window.WebKitMutationObserver) { + if (conf.target) { + handleTargetElement(); + } else { + handleBodyElements(); + } + } +} + +export function setConfig(config) { + if (!config) { + logError('Required confirguration not provided'); + return; + } + if (!config.url) { + logError('The required url is not configured'); + return; + } + if (typeof config.debounce !== 'number') { + config.debounce = CONF_DEFAULT_OBSERVER_DEBOUNCE_MS; + logInfo('Set default observer debounce to ' + CONF_DEFAULT_OBSERVER_DEBOUNCE_MS); + } + if (typeof config.fullscan !== 'boolean') { + config.fullscan = CONF_DEFAULT_FULL_BODY_SCAN; + logInfo('Set default fullscan ' + CONF_DEFAULT_FULL_BODY_SCAN); + } + conf = config; + associateIds(); +} + +config.getConfig('idLibrary', config => setConfig(config.idLibrary)); diff --git a/modules/idLibrary.md b/modules/idLibrary.md new file mode 100644 index 00000000000..69b63dc466b --- /dev/null +++ b/modules/idLibrary.md @@ -0,0 +1,24 @@ +## ID Library Configuration Example + + +|Param |Required |Description | +|----------------|-------------------------------|-----------------------------| +|url |Yes | The url endpoint is used to post the hashed email and user ids. | +|target |No |It should contain the element id from which the email can be read. | +|debounce |No | Time in milliseconds before the email and ids are fetched | +|fullscan |No | Option to enable/disable full body scan to get email. By default the full body scan is enabled. | + +### Example +``` + pbjs.setConfig({ + idLibrary:{ + url: , + debounce: 250, + target: 'username', + fullscan: false + }, + }); +``` + + +``` diff --git a/test/spec/modules/idLibrary_spec.js b/test/spec/modules/idLibrary_spec.js new file mode 100644 index 00000000000..da61850f29b --- /dev/null +++ b/test/spec/modules/idLibrary_spec.js @@ -0,0 +1,61 @@ +import * as utils from 'src/utils.js'; +import * as idlibrary from 'modules/idLibrary.js'; + +var expect = require('chai').expect; + +describe('currency', function () { + let fakeCurrencyFileServer; + let sandbox; + let clock; + + let fn = sinon.spy(); + + beforeEach(function () { + fakeCurrencyFileServer = sinon.fakeServer.create(); + sinon.stub(utils, 'logInfo'); + sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + utils.logInfo.restore(); + utils.logError.restore(); + fakeCurrencyFileServer.restore(); + idlibrary.setConfig({}); + }); + + describe('setConfig', function () { + beforeEach(function() { + sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(1046952000000); // 2003-03-06T12:00:00Z + }); + + afterEach(function () { + sandbox.restore(); + clock.restore(); + }); + + it('results when no config available', function () { + idlibrary.setConfig({}); + sinon.assert.called(utils.logError); + }); + it('results with config available', function () { + idlibrary.setConfig({ 'url': 'URL' }); + sinon.assert.called(utils.logInfo); + }); + it('results with config default debounce ', function () { + let config = { 'url': 'URL' } + idlibrary.setConfig(config); + expect(config.debounce).to.be.equal(250); + }); + it('results with config default fullscan ', function () { + let config = { 'url': 'URL' } + idlibrary.setConfig(config); + expect(config.fullscan).to.be.equal(true); + }); + it('results with config fullscan ', function () { + let config = { 'url': 'URL', 'fullscan': false } + idlibrary.setConfig(config); + expect(config.fullscan).to.be.equal(false); + }); + }); +}); From 17beca2fb045e29fe9dfcb722825eeffccf26852 Mon Sep 17 00:00:00 2001 From: mwehr-zeta <70167335+mwehr-zeta@users.noreply.github.com> Date: Thu, 12 Nov 2020 14:09:48 -0500 Subject: [PATCH 0360/1476] Zeta-Prebid (#5813) * Submit Zeta Adapter to Prebid * comments addressed * demo changes * additional polishing * additional polishing * Update hello_world.html * remove extraneous changes to hello_world.html * no, really this time * additional polishing * add unit test * stop tracking package-lock.json * stop tracking package-lock.json * stop tracking, please * Submit Zeta Adapter * refactor bidder_code value * fix markdown bidder code and include definerId in request --- modules/zetaBidAdapter.js | 161 +++++++++++++++++++++++ modules/zetaBidAdapter.md | 40 ++++++ test/spec/modules/zetaBidAdapter_spec.js | 79 +++++++++++ 3 files changed, 280 insertions(+) create mode 100644 modules/zetaBidAdapter.js create mode 100644 modules/zetaBidAdapter.md create mode 100644 test/spec/modules/zetaBidAdapter_spec.js diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js new file mode 100644 index 00000000000..f60e8946799 --- /dev/null +++ b/modules/zetaBidAdapter.js @@ -0,0 +1,161 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +const BIDDER_CODE = 'zeta_global'; +const ENDPOINT_URL = 'https://prebid.rfihub.com/prebid'; +const USER_SYNC_URL = 'https://p.rfihub.com/cm?pub=42770&in=1'; +const DEFAULT_CUR = 'USD'; +const TTL = 200; +const NET_REV = true; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [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. + */ + isBidRequestValid: function(bid) { + // check for all required bid fields + let isValid = !!( + bid && + bid.bidId && + bid.params && + bid.params.ip && + bid.params.user && + bid.params.user.buyeruid && + bid.params.definerId + ); + if (!isValid) { + utils.logWarn('Invalid bid request'); + } + return isValid; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {Bids[]} validBidRequests - an array of bidRequest objects + * @param {BidderRequest} bidderRequest - master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const secure = 1; // treat all requests as secure + const request = validBidRequests[0]; + const params = request.params; + let impData = { + id: request.bidId, + secure: secure, + banner: buildBanner(request) + }; + let isMobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; + let payload = { + id: bidderRequest.auctionId, + cur: [DEFAULT_CUR], + imp: [impData], + site: { + mobile: isMobile, + page: bidderRequest.refererInfo.referer + }, + device: { + ua: navigator.userAgent, + ip: params.ip + }, + user: { + buyeruid: params.user.buyeruid, + uid: params.user.uid + }, + ext: { + definerId: params.definerId + } + }; + if (params.test) { + payload.test = params.test; + } + if (request.gdprConsent) { + payload.regs = { + ext: { + gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0 + } + }; + } + if (request.gdprConsent && request.gdprConsent.gdprApplies) { + payload.user = { + ext: { + consent: request.gdprConsent.consentString + } + }; + } + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(payload), + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest The payload from the server's response. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + let bidResponse = []; + if (Object.keys(serverResponse.body).length !== 0) { + let zetaResponse = serverResponse.body; + let zetaBid = zetaResponse.seatbid[0].bid[0]; + let bid = { + requestId: zetaBid.impid, + cpm: zetaBid.price, + currency: zetaResponse.cur, + width: zetaBid.w, + height: zetaBid.h, + ad: zetaBid.adm, + ttl: TTL, + creativeId: zetaBid.crid, + netRevenue: NET_REV + }; + bidResponse.push(bid); + } + return bidResponse; + }, + + /** + * 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. + * @param gdprConsent The GDPR consent parameters + * @param uspConsent The USP consent parameters + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = []; + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: USER_SYNC_URL + }); + } + return syncs; + } +} + +function buildBanner(request) { + let sizes = request.sizes; + if (request.mediaTypes && + request.mediaTypes.banner && + request.mediaTypes.banner.sizes) { + sizes = request.mediaTypes.banner.sizes; + } + return { + w: sizes[0][0], + h: sizes[0][1] + }; +} + +registerBidder(spec); diff --git a/modules/zetaBidAdapter.md b/modules/zetaBidAdapter.md new file mode 100644 index 00000000000..ce19b831d4d --- /dev/null +++ b/modules/zetaBidAdapter.md @@ -0,0 +1,40 @@ +# Overview + +``` +Module Name: Zeta Bidder Adapter +Module Type: Bidder Adapter +Maintainer: DL-ZetaDSP-Supply-Engineering@zetaglobal.com +``` + +# Description + +Module that connects to Zeta's demand sources + +# Test Parameters +``` + var adUnits = [ + { + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: 'zeta_global', + bidId: 12345, + params: { + placement: 12345, + user: { + uid: 12345, + buyeruid: 12345 + }, + ip: '111.222.33.44', + definerId: 1, + test: 1 + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/zetaBidAdapter_spec.js b/test/spec/modules/zetaBidAdapter_spec.js new file mode 100644 index 00000000000..0d11614c926 --- /dev/null +++ b/test/spec/modules/zetaBidAdapter_spec.js @@ -0,0 +1,79 @@ +import { spec } from '../../../modules/zetaBidAdapter.js' + +describe('Zeta Bid Adapter', function() { + const bannerRequest = [{ + bidId: 12345, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + refererInfo: { + referer: 'zetaglobal.com' + }, + params: { + placement: 12345, + user: { + uid: 12345, + buyeruid: 12345 + }, + ip: '111.222.33.44', + definerId: 1, + test: 1 + } + }]; + + it('Test the bid validation function', function() { + const validBid = spec.isBidRequestValid(bannerRequest[0]); + const invalidBid = spec.isBidRequestValid(null); + + expect(validBid).to.be.true; + expect(invalidBid).to.be.false; + }); + + it('Test the request processing function', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + expect(request).to.not.be.empty; + + const payload = request.data; + expect(payload).to.not.be.empty; + }); + + const responseBody = { + id: '12345', + seatbid: [ + { + bid: [ + { + id: 'auctionId', + impid: 'impId', + price: 0.0, + adm: 'adMarkup', + crid: 'creativeId', + h: 250, + w: 300 + } + ] + } + ], + cur: 'USD' + }; + + it('Test the response parsing function', function () { + const receivedBid = responseBody.seatbid[0].bid[0]; + const response = {}; + response.body = responseBody; + + const bidResponse = spec.interpretResponse(response, null); + expect(bidResponse).to.not.be.empty; + + const bid = bidResponse[0]; + expect(bid).to.not.be.empty; + expect(bid.ad).to.equal(receivedBid.adm); + expect(bid.cpm).to.equal(receivedBid.price); + expect(bid.height).to.equal(receivedBid.h); + expect(bid.width).to.equal(receivedBid.w); + expect(bid.requestId).to.equal(receivedBid.impid); + }); +}); From d5c2c8fb70df5111aeff22eb9902829f273e7085 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 12 Nov 2020 12:36:28 -0800 Subject: [PATCH 0361/1476] Prebid 4.16.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8dc3535cc21..c61e97d0adf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.16.0-pre", + "version": "4.16.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ef7fb8673535d3f2718248ca75702472f5f9899f Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 12 Nov 2020 13:08:10 -0800 Subject: [PATCH 0362/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c61e97d0adf..4b62de09e1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.16.0", + "version": "4.17.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 09468bf036ad44a28de867f1baf8a81e90b03b80 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 13 Nov 2020 10:49:46 -0500 Subject: [PATCH 0363/1476] [AD-1043] JW Player RTD - add targeting info to bid.rtd (#5950) * adds targeting to rtd * updates tmg module * updates unit tests * shortens realTimeData * updates grid adapter for rtd Co-authored-by: karimJWP --- modules/gridBidAdapter.js | 3 +- modules/jwplayerRtdProvider.js | 11 +- modules/jwplayerRtdProvider.md | 14 ++- test/spec/modules/gridBidAdapter_spec.js | 10 +- test/spec/modules/jwplayerRtdProvider_spec.js | 115 +++++++++++++++--- 5 files changed, 124 insertions(+), 29 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 5436a18c6cb..db1402ea9ad 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -72,11 +72,12 @@ export const spec = { if (!userId) { userId = bid.userId; } - const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, jwTargeting} = bid; + const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, rtd} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); } + const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { if (!jwpseg && jwTargeting.segments) { jwpseg = jwTargeting.segments; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 17cb978aea3..197c3c192c8 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -247,9 +247,14 @@ function addTargetingToBids(bids, targeting) { return; } - bids.forEach(bid => { - bid.jwTargeting = targeting; - }); + bids.forEach(bid => addTargetingToBid(bid, targeting)); +} + +export function addTargetingToBid(bid, targeting) { + const rtd = bid.rtd || {}; + const jwRtd = {}; + jwRtd[SUBMODULE_NAME] = Object.assign({}, rtd[SUBMODULE_NAME], { targeting }); + bid.rtd = Object.assign({}, rtd, jwRtd); } function getPlayer(playerID) { diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index 3c83b6f521c..ae09277979a 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -86,11 +86,15 @@ Each bid for which targeting information was found will conform to the following adUnitCode: 'xyz', bidId: 'abc', ..., - jwTargeting: { - segments: ['123', '456'], - content: { - id: 'jw_abc123' - } + rtd: { + jwplayer: { + targeting: { + segments: ['123', '456'], + content: { + id: 'jw_abc123' + } + } + } } } ``` diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 9cd7d100318..640bfda1fee 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -411,9 +411,13 @@ describe('TheMediaGrid Adapter', function () { const jsSegments = ['test_seg_1', 'test_seg_2']; const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ - jwTargeting: { - segments: jsSegments, - content: jsContent + rtd: { + jwplayer: { + targeting: { + segments: jsSegments, + content: jsContent + } + } } }, bid); }); diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index 6e0fd8eb8d7..48b432b6bb4 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -1,5 +1,5 @@ import { fetchTargetingForMediaId, getVatFromCache, extractPublisherParams, - formatTargetingResponse, getVatFromPlayer, enrichAdUnits, + formatTargetingResponse, getVatFromPlayer, enrichAdUnits, addTargetingToBid, fetchTargetingInformation, jwplayerSubmodule } from 'modules/jwplayerRtdProvider.js'; import { server } from 'test/mocks/xhr.js'; @@ -252,7 +252,7 @@ describe('jwplayerRtdProvider', function() { }; jwplayerSubmodule.getBidRequestData({ adUnits: [adUnit] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; - expect(bid).to.have.deep.property('jwTargeting', expectedTargeting); + expect(bid.rtd.jwplayer).to.have.deep.property('targeting', expectedTargeting); fakeServer.respond(); expect(bidRequestSpy.calledOnce).to.be.true; }); @@ -313,8 +313,8 @@ describe('jwplayerRtdProvider', function() { enrichAdUnits([adUnit]); const bid1 = bids[0]; const bid2 = bids[1]; - expect(bid1).to.not.have.property('jwTargeting'); - expect(bid2).to.not.have.property('jwTargeting'); + expect(bid1).to.not.have.property('rtd'); + expect(bid2).to.not.have.property('rtd'); const request = fakeServer.requests[0]; request.respond( @@ -330,8 +330,8 @@ describe('jwplayerRtdProvider', function() { }) ); - expect(bid1).to.have.deep.property('jwTargeting', expectedTargetingForSuccess); - expect(bid2).to.have.deep.property('jwTargeting', expectedTargetingForSuccess); + expect(bid1.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForSuccess); + expect(bid2.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForSuccess); }); it('immediately adds cached targeting', function () { @@ -373,8 +373,8 @@ describe('jwplayerRtdProvider', function() { enrichAdUnits([adUnit]); const bid1 = bids[0]; const bid2 = bids[1]; - expect(bid1).to.have.deep.property('jwTargeting', expectedTargetingForSuccess); - expect(bid2).to.have.deep.property('jwTargeting', expectedTargetingForSuccess); + expect(bid1.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForSuccess); + expect(bid2.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForSuccess); }); it('adds content block when segments are absent and no request is pending', function () { @@ -407,8 +407,8 @@ describe('jwplayerRtdProvider', function() { enrichAdUnits([adUnit]); const bid1 = bids[0]; const bid2 = bids[1]; - expect(bid1).to.have.deep.property('jwTargeting', expectedTargetingForFailure); - expect(bid2).to.have.deep.property('jwTargeting', expectedTargetingForFailure); + expect(bid1.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForFailure); + expect(bid2.rtd.jwplayer).to.have.deep.property('targeting', expectedTargetingForFailure); }); }); @@ -456,6 +456,87 @@ describe('jwplayerRtdProvider', function() { }) }); + describe('Add Targeting to Bid', function () { + const targeting = {foo: 'bar'}; + + it('creates realTimeData when absent from Bid', function () { + const targeting = {foo: 'bar'}; + const bid = {}; + addTargetingToBid(bid, targeting); + expect(bid).to.have.property('rtd'); + expect(bid).to.have.nested.property('rtd.jwplayer.targeting', targeting); + }); + + it('adds to existing realTimeData', function () { + const otherRtd = { + targeting: { + seg: 'rtd seg' + } + }; + + const bid = { + rtd: { + otherRtd + } + }; + + addTargetingToBid(bid, targeting); + expect(bid).to.have.property('rtd'); + const rtd = bid.rtd; + expect(rtd).to.have.property('jwplayer'); + expect(rtd).to.have.nested.property('jwplayer.targeting', targeting); + + expect(rtd).to.have.deep.property('otherRtd', otherRtd); + }); + + it('adds to existing realTimeData.jwplayer', function () { + const otherInfo = { seg: 'rtd seg' }; + const bid = { + rtd: { + jwplayer: { + otherInfo + } + } + }; + addTargetingToBid(bid, targeting); + + expect(bid).to.have.property('rtd'); + const rtd = bid.rtd; + expect(rtd).to.have.property('jwplayer'); + expect(rtd).to.have.nested.property('jwplayer.otherInfo', otherInfo); + expect(rtd).to.have.nested.property('jwplayer.targeting', targeting); + }); + + it('overrides existing jwplayer.targeting', function () { + const otherInfo = { seg: 'rtd seg' }; + const bid = { + rtd: { + jwplayer: { + targeting: { + otherInfo + } + } + } + }; + addTargetingToBid(bid, targeting); + + expect(bid).to.have.property('rtd'); + const rtd = bid.rtd; + expect(rtd).to.have.property('jwplayer'); + expect(rtd).to.have.nested.property('jwplayer.targeting', targeting); + }); + + it('creates jwplayer when absent from realTimeData', function () { + const bid = { rtd: {} }; + addTargetingToBid(bid, targeting); + + expect(bid).to.have.property('rtd'); + const rtd = bid.rtd; + expect(rtd).to.have.property('jwplayer'); + expect(rtd).to.have.nested.property('jwplayer.targeting', targeting); + }); + }); + describe('jwplayerSubmodule', function () { it('successfully instantiates', function () { expect(jwplayerSubmodule.init()).to.equal(true); @@ -578,7 +659,7 @@ describe('jwplayerRtdProvider', function() { }; jwplayerSubmodule.getBidRequestData({ adUnits: [adUnitWithMediaId, adUnitEmpty] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; - expect(bid).to.have.deep.property('jwTargeting', expectedTargeting); + expect(bid.rtd.jwplayer).to.have.deep.property('targeting', expectedTargeting); }); it('excludes segments when absent', function () { @@ -605,9 +686,9 @@ describe('jwplayerRtdProvider', function() { jwplayerSubmodule.getBidRequestData({ adUnits: [ adUnit ] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; - expect(bid.jwTargeting).to.not.have.property('segments'); - expect(bid.jwTargeting).to.not.have.property('segments'); - expect(bid).to.have.deep.property('jwTargeting', expectedTargeting); + expect(bid.rtd.jwplayer.targeting).to.not.have.property('segments'); + expect(bid.rtd.jwplayer.targeting).to.not.have.property('segments'); + expect(bid.rtd.jwplayer).to.have.deep.property('targeting', expectedTargeting); }); it('does not modify bid when jwTargeting block is absent', function () { @@ -637,9 +718,9 @@ describe('jwplayerRtdProvider', function() { jwplayerSubmodule.getBidRequestData({ adUnits: [adUnitWithMediaId, adUnitEmpty, adUnitEmptyfpd] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; - expect(bid1).to.not.have.property('jwTargeting'); - expect(bid2).to.not.have.property('jwTargeting'); - expect(bid3).to.not.have.property('jwTargeting'); + expect(bid1).to.not.have.property('rtd'); + expect(bid2).to.not.have.property('rtd'); + expect(bid3).to.not.have.property('rtd'); }); }); }); From 5f36be90bb816e84a68cff58ebc6999967dc3739 Mon Sep 17 00:00:00 2001 From: jdwieland8282 Date: Fri, 13 Nov 2020 12:24:57 -0700 Subject: [PATCH 0364/1476] Update pubCommonIdSystem.js adding Prebid GVLID to getStorageManager function --- modules/pubCommonIdSystem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index 919e735d34e..acdfaefd5d0 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -20,8 +20,9 @@ const SHAREDID_URL = 'https://id.sharedid.org/id'; const SHAREDID_SUFFIX = '_sharedid'; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const SHAREDID_DEFAULT_STATE = false; +const GLVID = 887; -const storage = getStorageManager(null, 'pubCommonId'); +const storage = getStorageManager(GLVID, 'pubCommonId'); /** * Store sharedid in either cookie or local storage From 92a9a09b5509f266c7f69761b7c652f9586cc61f Mon Sep 17 00:00:00 2001 From: jdwieland8282 Date: Sun, 15 Nov 2020 06:01:35 -0700 Subject: [PATCH 0365/1476] Update pubCommonIdSystem.js (#5974) * Update pubCommonIdSystem.js adding GVLID to pubCommonIdSubmodule definition * Update pubCommonIdSystem.js adding GVLID to pubCommonIdSubmodule definition * Update pubCommonIdSystem.js removing duplicate reference to GVLID in pubCommonIdSubmodule defintion * Update pubCommonIdSystem.js fixing typo on ln 23 GLVID --> GVLID * Update pubCommonIdSystem.js fixing typos, * Update pubCommonIdSystem.js removing trailing white space * Update pubCommonIdSystem.js removing trailing white space --- modules/pubCommonIdSystem.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index acdfaefd5d0..cb0c07cefa8 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -20,9 +20,9 @@ const SHAREDID_URL = 'https://id.sharedid.org/id'; const SHAREDID_SUFFIX = '_sharedid'; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const SHAREDID_DEFAULT_STATE = false; -const GLVID = 887; +const GVLID = 887; -const storage = getStorageManager(GLVID, 'pubCommonId'); +const storage = getStorageManager(GVLID, 'pubCommonId'); /** * Store sharedid in either cookie or local storage @@ -160,7 +160,11 @@ export const pubCommonIdSubmodule = { * @type {string} */ name: MODULE_NAME, - + /** + * Vendor id of prebid + * @type {Number} + */ + gvlid: GVLID, /** * Return a callback function that calls the pixelUrl with id as a query parameter * @param pixelUrl From 840c04d07e3bb7b2b569d4479c10751f7d1e1773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alberto=20Polo=20Garc=C3=ADa?= Date: Mon, 16 Nov 2020 09:22:01 +0100 Subject: [PATCH 0366/1476] [CriteoId] Add local storage check and migrate cookie check to use StorageManager API (#5953) * [CriteoId] Check if local storage is writable. * [CriteoId] Check cookies are writable using StorageManager Co-authored-by: Jesus Alberto Polo Garcia --- modules/criteoIdSystem.js | 17 ++++++----------- test/spec/modules/criteoIdSystem_spec.js | 3 +-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 83bc773cb30..ac26d34d529 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -17,19 +17,11 @@ export const storage = getStorageManager(gvlid, bidderCode); const bididStorageKey = 'cto_bidid'; const bundleStorageKey = 'cto_bundle'; -const cookieWriteableKey = 'cto_test_cookie'; const cookiesMaxAge = 13 * 30 * 24 * 60 * 60 * 1000; const pastDateString = new Date(0).toString(); const expirationString = new Date(utils.timestamp() + cookiesMaxAge).toString(); -function areCookiesWriteable() { - storage.setCookie(cookieWriteableKey, '1'); - const canWrite = storage.getCookie(cookieWriteableKey) === '1'; - storage.setCookie(cookieWriteableKey, '', pastDateString); - return canWrite; -} - function extractProtocolHost (url, returnOnlyHost = false) { const parsedUrl = utils.parseUrl(url, {noDecodeWholeURL: true}) return returnOnlyHost @@ -60,20 +52,22 @@ function getCriteoDataFromAllStorages() { } } -function buildCriteoUsersyncUrl(topUrl, domain, bundle, areCookiesWriteable, isPublishertagPresent, gdprString) { +function buildCriteoUsersyncUrl(topUrl, domain, bundle, areCookiesWriteable, isLocalStorageWritable, isPublishertagPresent, gdprString) { const url = 'https://gum.criteo.com/sid/json?origin=prebid' + `${topUrl ? '&topUrl=' + encodeURIComponent(topUrl) : ''}` + `${domain ? '&domain=' + encodeURIComponent(domain) : ''}` + `${bundle ? '&bundle=' + encodeURIComponent(bundle) : ''}` + `${gdprString ? '&gdprString=' + encodeURIComponent(gdprString) : ''}` + `${areCookiesWriteable ? '&cw=1' : ''}` + - `${isPublishertagPresent ? '&pbt=1' : ''}` + `${isPublishertagPresent ? '&pbt=1' : ''}` + + `${isLocalStorageWritable ? '&lsw=1' : ''}`; return url; } function callCriteoUserSync(parsedCriteoData, gdprString) { - const cw = areCookiesWriteable(); + const cw = storage.cookiesAreEnabled(); + const lsw = storage.localStorageIsEnabled(); const topUrl = extractProtocolHost(getRefererInfo().referer); const domain = extractProtocolHost(document.location.href, true); const isPublishertagPresent = typeof criteo_pubtag !== 'undefined'; // eslint-disable-line camelcase @@ -83,6 +77,7 @@ function callCriteoUserSync(parsedCriteoData, gdprString) { domain, parsedCriteoData.bundle, cw, + lsw, isPublishertagPresent, gdprString ); diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index aa5807da0da..65e5aaf741d 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -71,7 +71,6 @@ describe('CriteoId module', function () { }); it('should call user sync url with the right params', function () { - getCookieStub.withArgs('cto_test_cookie').returns('1'); getCookieStub.withArgs('cto_bundle').returns('bundle'); window.criteo_pubtag = {} @@ -80,7 +79,7 @@ describe('CriteoId module', function () { ajaxBuilderStub.callsFake(mockResponse(undefined, ajaxStub)) criteoIdSubmodule.getId(); - const expectedUrl = `https://gum.criteo.com/sid/json?origin=prebid&topUrl=https%3A%2F%2Ftestdev.com%2F&domain=testdev.com&bundle=bundle&cw=1&pbt=1`; + const expectedUrl = `https://gum.criteo.com/sid/json?origin=prebid&topUrl=https%3A%2F%2Ftestdev.com%2F&domain=testdev.com&bundle=bundle&cw=1&pbt=1&lsw=1`; expect(ajaxStub.calledWith(expectedUrl)).to.be.true; From fb32955e72a99f1aea0b9c26b50f54da40dd5751 Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Mon, 16 Nov 2020 04:01:54 -0500 Subject: [PATCH 0367/1476] Audigent RTD configurable per-bidder segment mappings (#5903) * configurable per bidder segment mappings, optional segment caching, documentation update * rubicon->appnexus in docs * apply fpd convention, remove publisher defined mappers, add unit tests, fixes * segment mapping arr->dict * add fpd data to bids * update audigent tests * openrtb fpd data descriptions, update docs, more tests * update docs, update integration example * integration update * documentation table update * add examples of adding and overriding segment mappers * fix documentation * add contact verbiage * requestParams object type * brand rtd provider * update appnexus segment function * support numerical segments for appnexus * no default appnexus segments * update url * update gpt example * conditionally set bid segments for generic * fixes for integration example * update integration example * add appnexus handler override --- .../gpt/audigentSegments_example.html | 301 ------------------ .../gpt/haloRtdProvider_example.html | 149 +++++++++ modules/.submodules.json | 2 +- modules/audigentRtdProvider.js | 145 --------- modules/audigentRtdProvider.md | 76 ----- modules/haloRtdProvider.js | 198 ++++++++++++ modules/haloRtdProvider.md | 132 ++++++++ test/spec/modules/haloRtdProvider_spec.js | 220 +++++++++++++ 8 files changed, 700 insertions(+), 523 deletions(-) delete mode 100644 integrationExamples/gpt/audigentSegments_example.html create mode 100644 integrationExamples/gpt/haloRtdProvider_example.html delete mode 100644 modules/audigentRtdProvider.js delete mode 100644 modules/audigentRtdProvider.md create mode 100644 modules/haloRtdProvider.js create mode 100644 modules/haloRtdProvider.md create mode 100644 test/spec/modules/haloRtdProvider_spec.js diff --git a/integrationExamples/gpt/audigentSegments_example.html b/integrationExamples/gpt/audigentSegments_example.html deleted file mode 100644 index 1536ece9ab7..00000000000 --- a/integrationExamples/gpt/audigentSegments_example.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - - -

Audigent Segments Prebid

- -
- -
- -TDID: -
-
- -Audigent Segments: -
-
- - diff --git a/integrationExamples/gpt/haloRtdProvider_example.html b/integrationExamples/gpt/haloRtdProvider_example.html new file mode 100644 index 00000000000..7f9a34e55ee --- /dev/null +++ b/integrationExamples/gpt/haloRtdProvider_example.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + +

Audigent Segments Prebid

+ +
+ +
+ +Halo Id: +
+
+ +Audigent Segments (Appnexus): +
+
+ + diff --git a/modules/.submodules.json b/modules/.submodules.json index 9b523a0c73a..53caf7a0671 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -26,7 +26,7 @@ ], "rtdModule": [ "browsiRtdProvider", - "audigentRtdProvider", + "haloRtdProvider", "jwplayerRtdProvider", "reconciliationRtdProvider" ] diff --git a/modules/audigentRtdProvider.js b/modules/audigentRtdProvider.js deleted file mode 100644 index 09b76cac0df..00000000000 --- a/modules/audigentRtdProvider.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * This module adds audigent provider to the real time data module - * The {@link module:modules/realTimeData} module is required - * The module will fetch segments from audigent server - * @module modules/audigentRtdProvider - * @requires module:modules/realTimeData - */ -import {getGlobal} from '../src/prebidGlobal.js'; -import * as utils from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); - -/** @type {string} */ -const MODULE_NAME = 'realTimeData'; -const SUBMODULE_NAME = 'audigent'; -const HALOID_LOCAL_NAME = 'auHaloId'; -const SEG_LOCAL_NAME = '__adgntseg'; - -/** - * decorate adUnits with segment data - * @param {adUnit[]} adUnits - * @param {Object} data - */ -function addSegmentData(adUnits, data) { - adUnits.forEach(adUnit => { - if (adUnit.hasOwnProperty('bids')) { - adUnit.bids.forEach(bid => { - bid.audigent_segments = data; - }) - } - }) - - return adUnits; -} - -/** - * segment retrieval from audigent's backends - * @param {Object} reqBidsConfigObj - * @param {function} onDone - * @param {Object} config - * @param {Object} userConsent - */ -function getSegments(reqBidsConfigObj, onDone, config, userConsent) { - const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; - - let jsonData = storage.getDataFromLocalStorage(SEG_LOCAL_NAME); - if (jsonData) { - let data = JSON.parse(jsonData); - if (data.audigent_segments) { - addSegmentData(adUnits, data.audigent_segments); - onDone(); - return; - } - } - - const userIds = (getGlobal()).getUserIds(); - if (typeof userIds == 'undefined' || userIds == null) { - onDone(); - return; - } - - let haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); - if (haloId) { - userIds.haloId = haloId; - getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); - } else { - var script = document.createElement('script') - script.type = 'text/javascript'; - - script.onload = function() { - userIds.haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); - getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); - } - - script.src = 'https://id.halo.ad.gt/api/v1/haloid'; - document.getElementsByTagName('head')[0].appendChild(script); - } -} - -/** - * async segment retrieval from audigent's backends - * @param {adUnit[]} adUnits - * @param {function} onDone - * @param {Object} config - * @param {Object} userConsent - * @param {Object} userIds - */ -function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds) { - let reqParams = {} - if (typeof config == 'object' && config != null && Object.keys(config).length > 0) { - reqParams = config.params - } - - const url = `https://seg.halo.ad.gt/api/v1/rtb_segments`; - ajax(url, { - success: function (response, req) { - if (req.status === 200) { - try { - const data = JSON.parse(response); - if (data && data.audigent_segments) { - addSegmentData(adUnits, data.audigent_segments); - onDone(); - storage.setDataInLocalStorage(SEG_LOCAL_NAME, JSON.stringify(data)); - } else { - onDone(); - } - } catch (err) { - utils.logError('unable to parse audigent segment data'); - onDone(); - } - } else if (req.status === 204) { - // unrecognized partner config - onDone(); - } - }, - error: function () { - onDone(); - utils.logError('unable to get audigent segment data'); - } - }, - JSON.stringify({'userIds': userIds, 'config': reqParams}), - {contentType: 'application/json'} - ); -} - -/** - * module init - * @param {Object} config - * @return {boolean} - */ -export function init(config) { - return true; -} - -/** @type {RtdSubmodule} */ -export const audigentSubmodule = { - name: SUBMODULE_NAME, - getBidRequestData: getSegments, - init: init -}; - -submodule(MODULE_NAME, audigentSubmodule); diff --git a/modules/audigentRtdProvider.md b/modules/audigentRtdProvider.md deleted file mode 100644 index 03e647f651d..00000000000 --- a/modules/audigentRtdProvider.md +++ /dev/null @@ -1,76 +0,0 @@ -## Audigent Real-time Data Submodule - -Audigent is a next-generation data management platform and a first-of-a-kind -"data agency" containing some of the most exclusive content-consuming audiences -across desktop, mobile and social platforms. - -This real-time data module provides quality user segmentation that can be -attached to bid request objects destined for different SSPs in order to optimize -targeting. Audigent maintains a large database of first-party Tradedesk Unified -ID, Audigent Halo ID and other id provider mappings to various third-party -segment types that are utilizable across different SSPs. With this module, -these segments can be retrieved and supplied to the SSP in real-time during -the bid request cycle. - -### Usage - -Compile the audigent RTD module into your Prebid build: - -`gulp build --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,rubiconBidAdapter` - -Configure Prebid to add the Audigent RTD Segment Handler: -``` -pbjs.setConfig( - ... - realTimeData: { - auctionDelay: 1000, - dataProviders: [ - { - name: "audigent", - waitForIt: true - } - ] - } - ... -} -``` - -Audigent segments will then be attached to each bid request objects in -`bid.realTimeData.audigent_segments` - -The format of the segments is a per-SSP mapping: - -``` -{ - 'appnexus': ['anseg1', 'anseg2'], - 'google': ['gseg1', 'gseg2'] -} -``` - -If a given SSP's API backend supports segment fields, they can then be -attached prior to the bid request being sent: - -``` -pbjs.requestBids({bidsBackHandler: addAudigentSegments}); - -function addAudigentSegments() { - for (i = 0; i < adUnits.length; i++) { - let adUnit = adUnits[i]; - for (j = 0; j < adUnit.bids.length; j++) { - adUnit.bids[j].userId.lipb.segments = adUnit.bids[j].audigent_segments['rubicon']; - } - } -} -``` - -### Testing - -To view an example of available segments returned by Audigent's backends: - -`gulp serve --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,rubiconBidAdapter` - -and then point your browser at: - -`http://localhost:9999/integrationExamples/gpt/audigentSegments_example.html` - - diff --git a/modules/haloRtdProvider.js b/modules/haloRtdProvider.js new file mode 100644 index 00000000000..fff9e43ea2a --- /dev/null +++ b/modules/haloRtdProvider.js @@ -0,0 +1,198 @@ +/** + * This module adds audigent provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch segments from audigent server + * @module modules/audigentRtdProvider + * @requires module:modules/realTimeData + */ +import {getGlobal} from '../src/prebidGlobal.js'; +import * as utils from '../src/utils.js'; +import {submodule} from '../src/hook.js'; +import {ajax} from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); + +/** @type {string} */ +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'halo'; + +export const HALOID_LOCAL_NAME = 'auHaloId'; +export const SEG_LOCAL_NAME = '__adgntseg'; + +const set = (obj, path, val) => { + const keys = path.split('.'); + const lastKey = keys.pop(); + const lastObj = keys.reduce((obj, key) => obj[key] = obj[key] || {}, obj); + lastObj[lastKey] = lastObj[lastKey] || val; +}; + +/** bid adapter format segment augmentation functions */ +const segmentMappers = { + appnexus: function(bid, segments) { + set(bid, 'params.user.segments', []); + let appnexusSegments = []; + segments.forEach(segment => { + if (typeof segment.value != 'undefined' && segment.value != null) { + let appnexusSegment = {'id': segment.id, 'value': segment.value}; + appnexusSegments.push(appnexusSegment); + } + }) + bid.params.user.segments = bid.params.user.segments.concat(appnexusSegments); + }, + generic: function(bid, segments) { + bid.segments = bid.segments || []; + if (Array.isArray(bid.segments)) { + bid.segments = bid.segments.concat(segments); + } + } +} + +/** + * decorate adUnits with segment data + * @param {adUnit[]} adUnits + * @param {Object} data + */ +export function addSegmentData(adUnits, segmentData, config) { + adUnits.forEach(adUnit => { + if (adUnit.hasOwnProperty('bids')) { + adUnit.bids.forEach(bid => { + try { + set(bid, 'fpd.user.data', []); + if (Array.isArray(bid.fpd.user.data)) { + bid.fpd.user.data.forEach(fpdData => { + let segments = segmentData[fpdData.id] || segmentData[fpdData.name] || []; + fpdData.segment = (fpdData.segment || []).concat(segments); + }); + } + } catch (err) { + utils.logError(err.message); + } + + try { + if (config.params.mapSegments && config.params.mapSegments[bid.bidder] && segmentData[bid.bidder]) { + if (typeof config.params.mapSegments[bid.bidder] == 'function') { + config.params.mapSegments[bid.bidder](bid, segmentData[bid.bidder]); + } else if (segmentMappers[bid.bidder]) { + segmentMappers[bid.bidder](bid, segmentData[bid.bidder]); + } + } + } catch (err) { + utils.logError(err.message); + } + }); + } + }); + + return adUnits; +} + +/** + * segment retrieval from audigent's backends + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} config + * @param {Object} userConsent + */ +export function getSegments(reqBidsConfigObj, onDone, config, userConsent) { + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + + if (config.params.segmentCache) { + let jsonData = storage.getDataFromLocalStorage(SEG_LOCAL_NAME); + + if (jsonData) { + let data = JSON.parse(jsonData); + + if (data.audigent_segments) { + addSegmentData(adUnits, data.audigent_segments, config); + onDone(); + return; + } + } + } + + const userIds = (getGlobal()).getUserIds(); + + let haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); + if (haloId) { + userIds.haloId = haloId; + getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); + } else { + var script = document.createElement('script') + script.type = 'text/javascript'; + + script.onload = function() { + userIds.haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME); + getSegmentsAsync(adUnits, onDone, config, userConsent, userIds); + } + + script.src = 'https://id.halo.ad.gt/api/v1/haloid'; + document.getElementsByTagName('head')[0].appendChild(script); + } +} + +/** + * async segment retrieval from audigent's backends + * @param {adUnit[]} adUnits + * @param {function} onDone + * @param {Object} config + * @param {Object} userConsent + * @param {Object} userIds + */ +export function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds) { + let reqParams = {}; + if (typeof config == 'object' && config != null) { + set(config, 'params.requestParams', {}); + reqParams = config.params.requestParams; + } + + const url = `https://seg.halo.ad.gt/api/v1/rtb_segments`; + ajax(url, { + success: function (response, req) { + if (req.status === 200) { + try { + const data = JSON.parse(response); + if (data && data.audigent_segments) { + addSegmentData(adUnits, data.audigent_segments, config); + onDone(); + storage.setDataInLocalStorage(SEG_LOCAL_NAME, JSON.stringify(data)); + } else { + onDone(); + } + } catch (err) { + utils.logError('unable to parse audigent segment data'); + onDone(); + } + } else if (req.status === 204) { + // unrecognized partner config + onDone(); + } + }, + error: function () { + onDone(); + utils.logError('unable to get audigent segment data'); + } + }, + JSON.stringify({'userIds': userIds, 'config': reqParams}), + {contentType: 'application/json'} + ); +} + +/** + * module init + * @param {Object} provider + * @param {Objkect} userConsent + * @return {boolean} + */ +function init(provider, userConsent) { + return true; +} + +/** @type {RtdSubmodule} */ +export const haloSubmodule = { + name: SUBMODULE_NAME, + getBidRequestData: getSegments, + init: init +}; + +submodule(MODULE_NAME, haloSubmodule); diff --git a/modules/haloRtdProvider.md b/modules/haloRtdProvider.md new file mode 100644 index 00000000000..2897a5917fa --- /dev/null +++ b/modules/haloRtdProvider.md @@ -0,0 +1,132 @@ +## Audigent Halo Real-time Data Submodule + +Audigent is a next-generation data management platform and a first-of-a-kind +"data agency" containing some of the most exclusive content-consuming audiences +across desktop, mobile and social platforms. + +This real-time data module provides quality user segmentation that can be +attached to bid request objects destined for different SSPs in order to optimize +targeting. Audigent maintains a large database of first-party Tradedesk Unified +ID, Audigent Halo ID and other id provider mappings to various third-party +segment types that are utilizable across different SSPs. With this module, +these segments can be retrieved and supplied to the SSP in real-time during +the bid request cycle. + +### Publisher Usage + +Compile the Halo RTD module into your Prebid build: + +`gulp build --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,appnexusBidAdapter` + +Add the Halo RTD provider to your Prebid config. For any adapters +that you would like to retrieve segments for, add a mapping in the 'mapSegments' +parameter. In this example we will configure publisher 1234 to retrieve +appnexus segments from Audigent. See the "Parameter Descriptions" below for +more detailed information of the configuration parameters. Currently, +OpenRTB compatible fpd data will be added for any bid adapter in the +"mapSegments" objects. Automated bid augmentation exists for some bidders. +Please work with your Audigent Prebid support team (prebid@audigent.com) on +which version of Prebid.js supports which bidders automatically. + +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: auctionDelay, + dataProviders: [ + { + name: "halo", + waitForIt: true, + params: { + mapSegments: { + appnexus: true, + }, + segmentCache: false, + requestParams: { + publisherId: 1234 + } + } + } + ] + } + ... +} +``` + +### Parameter Descriptions for the Halo `dataProviders` Configuration Section + +| Name |Type | Description | Notes | +| :------------ | :------------ | :------------ |:------------ | +| name | String | Real time data module name | Always 'halo' | +| waitForIt | Boolean | Required to ensure that the auction is delayed until prefetch is complete | Optional. Defaults to false | +| params | Object | | | +| params.mapSegments | Boolean | Dictionary of bidders you would like to supply Audigent segments for. Maps to boolean values, but also allows functions for custom mapping logic. The function signature is (bid, segments) => {}. | Required | +| params.segmentCache | Boolean | This parameter tells the Halo RTD module to attempt reading segments from a local storage cache instead of always requesting them from the Audigent server. | Optional. Defaults to false. | +| params.requestParams | Object | Publisher partner specific configuration options, such as optional publisher id and other segment query related metadata to be submitted to Audigent's backend with each request. Contact prebid@audigent.com for more information. | Optional | + +### Overriding & Adding Segment Mappers +As indicated above, it is possible to provide your own bid augmentation +functions. This is useful if you know a bid adapter's API supports segment +fields which aren't specifically being added to request objects in the Prebid +bid adapter. You can also override segment mappers by passing a function +instead of a boolean to the Halo RTD segment module. This might be useful +if you'd like to use custom logic to determine which segments are sent +to a specific backend. + +Please see the following example, which provides a function to modify bids for +a bid adapter called adBuzz and overrides the appnexus segment mapper. + +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: auctionDelay, + dataProviders: [ + { + name: "halo", + waitForIt: true, + params: { + mapSegments: { + // adding an adBuzz segment mapper + adBuzz: function(bid, segments) { + bid.params.adBuzzCustomSegments = []; + for (var i = 0; i < segments.length; i++) { + bid.params.adBuzzCustomSegments.push(segments[i].id); + } + }, + // overriding the appnexus segment mapper to exclude certain segments + appnexus: function(bid, segments) { + for (var i = 0; i < segments.length; i++) { + if (segments[i].id != 'exclude_segment') { + bid.params.user.segments.push(segments[i].id); + } + } + } + }, + segmentCache: false, + requestParams: { + publisherId: 1234 + } + } + } + ] + } + ... +} +``` + +More examples can be viewed in the haloRtdAdapter_spec.js tests. + +### Testing + +To view an example of available segments returned by Audigent's backends: + +`gulp serve --modules=userId,unifiedIdSystem,rtdModule,haloRtdProvider,appnexusBidAdapter` + +and then point your browser at: + +`http://localhost:9999/integrationExamples/gpt/haloRtdProvider_example.html` + + + + diff --git a/test/spec/modules/haloRtdProvider_spec.js b/test/spec/modules/haloRtdProvider_spec.js new file mode 100644 index 00000000000..17c2e19e1a6 --- /dev/null +++ b/test/spec/modules/haloRtdProvider_spec.js @@ -0,0 +1,220 @@ +import { HALOID_LOCAL_NAME, SEG_LOCAL_NAME, addSegmentData, getSegments, haloSubmodule, storage } from 'modules/haloRtdProvider.js'; +import { server } from 'test/mocks/xhr.js'; + +const responseHeader = {'Content-Type': 'application/json'}; + +describe('haloRtdProvider', function() { + describe('haloSubmodule', function() { + it('successfully instantiates', function () { + expect(haloSubmodule.init()).to.equal(true); + }); + }); + + describe('Add Segment Data', function() { + it('adds segment data', function() { + const config = { + params: { + mapSegments: { + 'appnexus': true, + 'generic': true + } + } + }; + + let adUnits = [ + { + bids: [ + // bid with existing segment data in bid obj and fpd + { + bidder: 'appnexus', + fpd: { + user: { + data: [ + { + id: 'appnexus', + segment: [ + { + id: 'apnseg0' + } + ] + } + ] + } + }, + params: { + user: { + segments: [{'id': 'apnseg0', 'value': 0}] + } + } + } + ] + }, + + // bids with fpd data definitions but without existing segment data + { + bids: [ + { + bidder: 'appnexus', + fpd: { + user: { + data: [ + { + id: 'appnexus' + } + ] + } + } + }, + { + bidder: 'generic', + fpd: { + user: { + data: [ + { + id: 'generic' + } + ] + } + } + } + ] + } + ]; + + const data = { + appnexus: [{id: 'apnseg1', value: 0}, {id: 'apnseg2', value: 2}, {id: 'apnseg3'}], + generic: [{id: 'seg1'}, {id: 'seg2'}, {id: 'seg3'}] + }; + + addSegmentData(adUnits, data, config); + + expect(adUnits[0].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'apnseg0'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'apnseg1'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', 'apnseg2'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[3]).to.have.deep.property('id', 'apnseg3'); + expect(adUnits[0].bids[0].params.user).to.have.deep.property('segments', [{'id': 'apnseg0', 'value': 0}, {'id': 'apnseg1', 'value': 0}, {'id': 'apnseg2', 'value': 2}]); + + expect(adUnits[1].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'apnseg1'); + expect(adUnits[1].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'apnseg2'); + expect(adUnits[1].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', 'apnseg3'); + expect(adUnits[1].bids[0].params.user).to.have.deep.property('segments', [{'id': 'apnseg1', 'value': 0}, {'id': 'apnseg2', 'value': 2}]); + + expect(adUnits[1].bids[1].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'seg1'); + expect(adUnits[1].bids[1].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'seg2'); + expect(adUnits[1].bids[1].fpd.user.data[0].segment[2]).to.have.deep.property('id', 'seg3'); + expect(adUnits[1].bids[1].segments[0]).to.have.deep.property('id', 'seg1'); + expect(adUnits[1].bids[1].segments[1]).to.have.deep.property('id', 'seg2'); + expect(adUnits[1].bids[1].segments[2]).to.have.deep.property('id', 'seg3'); + }); + + it('allows mapper extensions and overrides', function() { + const config = { + params: { + mapSegments: { + generic: (bid, segments) => { + bid.overrideSegments = segments; + }, + newBidder: (bid, segments) => { + bid.newBidderSegments = segments; + } + } + } + }; + + let adUnits = [ + { + bids: [ {bidder: 'newBidder'}, {bidder: 'generic'} ] + } + ]; + + const data = { + newBidder: [{id: 'nbseg1', name: 'New Bidder Segment 1'}, {id: 'nbseg2', name: 'New Bidder Segment 2'}, {id: 'nbseg3', name: 'New Bidder Segment 3'}], + generic: [{id: 'seg1'}, {id: 'seg2'}, {id: 'seg3'}] + }; + + addSegmentData(adUnits, data, config); + + expect(adUnits[0].bids[0].newBidderSegments[0]).to.have.deep.property('id', 'nbseg1'); + expect(adUnits[0].bids[0].newBidderSegments[0]).to.have.deep.property('name', 'New Bidder Segment 1'); + expect(adUnits[0].bids[0].newBidderSegments[1]).to.have.deep.property('id', 'nbseg2'); + expect(adUnits[0].bids[0].newBidderSegments[1]).to.have.deep.property('name', 'New Bidder Segment 2'); + expect(adUnits[0].bids[0].newBidderSegments[2]).to.have.deep.property('id', 'nbseg3'); + expect(adUnits[0].bids[0].newBidderSegments[2]).to.have.deep.property('name', 'New Bidder Segment 3'); + + expect(adUnits[0].bids[1].overrideSegments[0]).to.have.deep.property('id', 'seg1'); + expect(adUnits[0].bids[1].overrideSegments[1]).to.have.deep.property('id', 'seg2'); + expect(adUnits[0].bids[1].overrideSegments[2]).to.have.deep.property('id', 'seg3'); + }); + }); + + describe('Get Segments', function() { + it('gets segment data from local storage cache', function() { + const config = { + params: { + segmentCache: true, + mapSegments: { + 'generic': true + } + } + }; + + let reqBidsConfigObj = { + adUnits: [ + { + bids: [{bidder: 'generic'}] + } + ] + }; + + const data = { + audigent_segments: { + generic: [{id: 'seg1'}] + } + }; + + storage.setDataInLocalStorage(SEG_LOCAL_NAME, JSON.stringify(data)); + + getSegments(reqBidsConfigObj, () => {}, config, {}); + + expect(reqBidsConfigObj.adUnits[0].bids[0].segments[0]).to.have.deep.property('id', 'seg1'); + }); + + it('gets segment data via async request', function() { + const config = { + params: { + segmentCache: false, + mapSegments: { + 'generic': true + }, + requestParams: { + 'publisherId': 1234 + } + } + }; + + let reqBidsConfigObj = { + adUnits: [ + { + bids: [{bidder: 'generic'}] + } + ] + }; + const data = { + audigent_segments: { + generic: [{id: 'seg1'}] + } + }; + + storage.setDataInLocalStorage(HALOID_LOCAL_NAME, 'haloid'); + getSegments(reqBidsConfigObj, () => {}, config, {}); + + let request = server.requests[0]; + let postData = JSON.parse(request.requestBody); + expect(postData.config).to.have.deep.property('publisherId', 1234); + + request.respond(200, responseHeader, JSON.stringify(data)); + + expect(reqBidsConfigObj.adUnits[0].bids[0].segments[0]).to.have.deep.property('id', 'seg1'); + }); + }); +}); From ac686e5c0e65cc9a8babf90cb8fabc9694064a94 Mon Sep 17 00:00:00 2001 From: r-schweitzer <50628828+r-schweitzer@users.noreply.github.com> Date: Mon, 16 Nov 2020 18:43:14 +0100 Subject: [PATCH 0368/1476] added feature/getNoBidsForAdUnitCode function (#5932) * added feature/getNoBidsForAdUnitCode function * added unit test --- src/prebid.js | 12 ++++++++++++ test/spec/api_spec.js | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/prebid.js b/src/prebid.js index 8bfb6024d7a..0f72ca878e5 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -258,6 +258,18 @@ $$PREBID_GLOBAL$$.getNoBids = function () { return getBids('getNoBids'); }; +/** + * This function returns the bids requests involved in an auction but not bid on or the specified adUnitCode + * @param {string} adUnitCode adUnitCode + * @alias module:pbjs.getNoBidsForAdUnitCode + * @return {Object} bidResponse object + */ + +$$PREBID_GLOBAL$$.getNoBidsForAdUnitCode = function (adUnitCode) { + const bids = auctionManager.getNoBids().filter(bid => bid.adUnitCode === adUnitCode); + return { bids }; +}; + /** * This function returns the bid responses at the given moment. * @alias module:pbjs.getBidResponses diff --git a/test/spec/api_spec.js b/test/spec/api_spec.js index 6f21eba7aaf..6d67565056f 100755 --- a/test/spec/api_spec.js +++ b/test/spec/api_spec.js @@ -43,10 +43,14 @@ describe('Publisher API', function () { assert.isFunction($$PREBID_GLOBAL$$.getBidResponses); }); - it('should have function $$PREBID_GLOBAL$$.getBidResponses', function () { + it('should have function $$PREBID_GLOBAL$$.getNoBids', function () { assert.isFunction($$PREBID_GLOBAL$$.getNoBids); }); + it('should have function $$PREBID_GLOBAL$$.getNoBidsForAdUnitCode', function () { + assert.isFunction($$PREBID_GLOBAL$$.getNoBidsForAdUnitCode); + }); + it('should have function $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode', function () { assert.isFunction($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode); }); From a4a194c0086ed3ea3a13315798fc7f05abef1442 Mon Sep 17 00:00:00 2001 From: Mehmet Can Kurt Date: Mon, 16 Nov 2020 13:51:29 -0800 Subject: [PATCH 0369/1476] update quantcastBidAdapter to make user sync flag non-constant (#5949) --- modules/quantcastBidAdapter.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 894bb991a71..347ebc2ff55 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -101,6 +101,8 @@ function checkTCFv2(tcData) { return !!(vendorConsent && purposeConsent); } +let hasUserSynced = false; + /** * The documentation for Prebid.js Adapter 1.0 can be found at link below, * http://prebid.org/dev-docs/bidder-adapter-1.html @@ -109,7 +111,6 @@ export const spec = { code: BIDDER_CODE, GVLID: 11, supportedMediaTypes: ['banner', 'video'], - hasUserSynced: false, /** * Verify the `AdUnits.bids` response with `true` for valid request and `false` @@ -271,7 +272,7 @@ export const spec = { }, getUserSyncs(syncOptions, serverResponses) { const syncs = [] - if (!this.hasUserSynced && syncOptions.pixelEnabled) { + if (!hasUserSynced && syncOptions.pixelEnabled) { const responseWithUrl = find(serverResponses, serverResponse => utils.deepAccess(serverResponse.body, 'userSync.url') ); @@ -283,12 +284,12 @@ export const spec = { url: url }); } - this.hasUserSynced = true; + hasUserSynced = true; } return syncs; }, resetUserSync() { - this.hasUserSynced = false; + hasUserSynced = false; } }; From a126dcf06fc320f180e97a8ffef87c5b1d0744fb Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Tue, 17 Nov 2020 04:52:26 +0700 Subject: [PATCH 0370/1476] Update for Qwarry bid adapter: onBidWon hotfix (#5955) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 4 +- test/spec/modules/qwarryBidAdapter_spec.js | 67 +++++++++++++--------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 0c6dee9be69..7c2ec0f085b 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -66,7 +66,9 @@ export const spec = { onBidWon: function (bid) { if (bid.winUrl) { - ajax(bid.winUrl, null); + const cpm = bid.cpm; + const winUrl = bid.winUrl.replace(/\$\{AUCTION_PRICE\}/, cpm); + ajax(winUrl, null); return true; } return false; diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 02fe9c4538b..91e3cf4bfdf 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -11,33 +11,37 @@ const REQUEST = { } } -const BIDDER_BANNER_RESPONSE = {'prebidResponse': [{ - 'ad': '
test
', - 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46d', - 'cpm': 900.5, - 'currency': 'USD', - 'width': 640, - 'height': 480, - 'ttl': 300, - 'creativeId': 1, - 'netRevenue': true, - 'winUrl': 'http://test.com', - 'format': 'banner' -}]} - -const BIDDER_VIDEO_RESPONSE = {'prebidResponse': [{ - 'ad': 'vast', - 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46z', - 'cpm': 800.4, - 'currency': 'USD', - 'width': 1024, - 'height': 768, - 'ttl': 200, - 'creativeId': 2, - 'netRevenue': true, - 'winUrl': 'http://test.com', - 'format': 'video' -}]} +const BIDDER_BANNER_RESPONSE = { + 'prebidResponse': [{ + 'ad': '
test
', + 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46d', + 'cpm': 900.5, + 'currency': 'USD', + 'width': 640, + 'height': 480, + 'ttl': 300, + 'creativeId': 1, + 'netRevenue': true, + 'winUrl': 'http://test.com', + 'format': 'banner' + }] +} + +const BIDDER_VIDEO_RESPONSE = { + 'prebidResponse': [{ + 'ad': 'vast', + 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46z', + 'cpm': 800.4, + 'currency': 'USD', + 'width': 1024, + 'height': 768, + 'ttl': 200, + 'creativeId': 2, + 'netRevenue': true, + 'winUrl': 'http://test.com', + 'format': 'video' + }] +} const BIDDER_NO_BID_RESPONSE = '' @@ -120,4 +124,13 @@ describe('qwarryBidAdapter', function () { expect(result).to.deep.equal([]) }) }) + + describe('onBidWon', function () { + it('handles banner win: should get true', function () { + const win = BIDDER_BANNER_RESPONSE.prebidResponse[0] + const bidWonResult = spec.onBidWon(win) + + expect(bidWonResult).to.equal(true) + }) + }) }) From 1664f4e2648ea90a93b25c76510858c0de30fa69 Mon Sep 17 00:00:00 2001 From: jsut Date: Mon, 16 Nov 2020 16:53:02 -0500 Subject: [PATCH 0371/1476] FIX typo's in CONTRIBUTING (#5956) --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 962e057fbc5..606d26cd25a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ Contributions are always welcome. To contribute, [fork](https://help.github.com/ commit your changes, and [open a pull request](https://help.github.com/articles/using-pull-requests/) against the master branch. -Pull requests must have 80% code coverage before beign considered for merge. +Pull requests must have 80% code coverage before being considered for merge. Additional details about the process can be found [here](./PR_REVIEW.md). There are more details available if you'd like to contribute a [bid adapter](https://docs.prebid.org/dev-docs/bidder-adaptor.html) or [analytics adapter](https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html). @@ -59,7 +59,7 @@ When you are adding code to Prebid.js, or modifying code that isn't covered by a Prebid.js already has many tests. Read them to see how Prebid.js is tested, and for inspiration: - Look in `test/spec` and its subdirectories -- Tests for bidder adaptors are located in `test/spec/modules` +- Tests for bidder adapters are located in `test/spec/modules` A test module might have the following general structure: From 5aa0fe65061578446c59d6659e799c5c3df5b3bc Mon Sep 17 00:00:00 2001 From: jsut Date: Mon, 16 Nov 2020 16:54:03 -0500 Subject: [PATCH 0372/1476] Add a note to the readme about adapter aliases (#5968) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44882570d89..40df62ccee4 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ This runs some code quality checks, starts a web server at `http://localhost:999 ### Build Optimization -The standard build output contains all the available modules from within the `modules` folder. +The standard build output contains all the available modules from within the `modules` folder. Note, however that there are bid adapters which support multiple bidders through aliases, so if you don't see a file in modules for a bid adapter, you may need to grep the repository to find the name of the module you need to include. You might want to exclude some/most of them from the final bundle. To make sure the build only includes the modules you want, you can specify the modules to be included with the `--modules` CLI argument. From c04bd6d70c1107ebfd69f7c838e94485408e454f Mon Sep 17 00:00:00 2001 From: Mehmet Can Kurt Date: Mon, 16 Nov 2020 17:07:08 -0800 Subject: [PATCH 0373/1476] update quantcastBidAdapter to pass quantcast fpa in the bid request (#5947) * update quantcastBidAdapter to pass quantcast fpa in the bid request * remove empty line from lunamediahBidAdapter * pass quantcast vendor id when obtaining storage manager --- modules/quantcastBidAdapter.js | 14 ++++++++-- test/spec/modules/quantcastBidAdapter_spec.js | 27 ++++++++++++++----- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 347ebc2ff55..e9541edb534 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -1,6 +1,7 @@ import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import find from 'core-js-pure/features/array/find.js'; @@ -18,6 +19,9 @@ export const QUANTCAST_TEST_PUBLISHER = 'test-publisher'; export const QUANTCAST_TTL = 4; export const QUANTCAST_PROTOCOL = 'https'; export const QUANTCAST_PORT = '8443'; +export const QUANTCAST_FPA = '__qca'; + +export const storage = getStorageManager(QUANTCAST_VENDOR_ID, BIDDER_CODE); function makeVideoImp(bid) { const video = {}; @@ -101,6 +105,11 @@ function checkTCFv2(tcData) { return !!(vendorConsent && purposeConsent); } +function getQuantcastFPA() { + let fpa = storage.getCookie(QUANTCAST_FPA) + return fpa || '' +} + let hasUserSynced = false; /** @@ -109,7 +118,7 @@ let hasUserSynced = false; */ export const spec = { code: BIDDER_CODE, - GVLID: 11, + GVLID: QUANTCAST_VENDOR_ID, supportedMediaTypes: ['banner', 'video'], /** @@ -189,7 +198,8 @@ export const spec = { uspSignal: uspConsent ? 1 : 0, uspConsent, coppa: config.getConfig('coppa') === true ? 1 : 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: getQuantcastFPA() }; const data = JSON.stringify(requestData); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index caa554c8cd8..5b4e7963e60 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -7,7 +7,8 @@ import { QUANTCAST_TEST_PUBLISHER, QUANTCAST_PROTOCOL, QUANTCAST_PORT, - spec as qcSpec + spec as qcSpec, + storage } from '../../../modules/quantcastBidAdapter.js'; import { newBidder } from '../../../src/adapters/bidderFactory.js'; import { parseUrl } from 'src/utils.js'; @@ -42,6 +43,8 @@ describe('Quantcast adapter', function () { canonicalUrl: 'http://example.com/hello.html' } }; + + storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); }); function setupVideoBidRequest(videoParams) { @@ -140,7 +143,8 @@ describe('Quantcast adapter', function () { gdprSignal: 0, uspSignal: 0, coppa: 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: '' }; it('sends banner bid requests contains all the required parameters', function () { @@ -208,7 +212,8 @@ describe('Quantcast adapter', function () { gdprSignal: 0, uspSignal: 0, coppa: 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: '' }; expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); @@ -244,7 +249,8 @@ describe('Quantcast adapter', function () { gdprSignal: 0, uspSignal: 0, coppa: 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: '' }; expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); @@ -276,7 +282,8 @@ describe('Quantcast adapter', function () { gdprSignal: 0, uspSignal: 0, coppa: 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: '' }; expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); @@ -340,7 +347,8 @@ describe('Quantcast adapter', function () { gdprSignal: 0, uspSignal: 0, coppa: 0, - prebidJsVersion: '$prebid.version$' + prebidJsVersion: '$prebid.version$', + fpa: '' }; expect(requests[0].data).to.equal(JSON.stringify(expectedBidRequest)); @@ -584,6 +592,13 @@ describe('Quantcast adapter', function () { expect(parsed.uspConsent).to.equal('consentString'); }); + it('propagates Quantcast first-party cookie (fpa)', function() { + storage.setCookie('__qca', 'P0-TestFPA'); + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const parsed = JSON.parse(requests[0].data); + expect(parsed.fpa).to.equal('P0-TestFPA'); + }); + describe('propagates coppa', function() { let sandbox; beforeEach(() => { From fe7acf8a62e9f253c66a23358e4f5bc7906a1536 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 17 Nov 2020 06:33:57 +0100 Subject: [PATCH 0374/1476] improve console ogging of user id module by listing all user id modules that have been enabled (#5975) --- modules/userId/index.js | 2 +- test/spec/modules/userId_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 9ef4da0f96f..0923a92f516 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -667,7 +667,7 @@ function updateSubmodules() { if (!addedUserIdHook && submodules.length) { // priority value 40 will load after consentManagement with a priority of 50 getGlobal().requestBids.before(requestBidsHook, 40); - utils.logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules`); + utils.logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules: `, submodules.map(a => a.submodule.name)); addedUserIdHook = true; } } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index d5ed96a5bc1..887e1f45640 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -443,7 +443,7 @@ describe('User ID', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); }); @@ -506,7 +506,7 @@ describe('User ID', function () { init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); it('config with 13 configurations should result in 13 submodules add', function () { @@ -553,7 +553,7 @@ describe('User ID', function () { }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 13 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 13 submodules'); }); it('config syncDelay updates module correctly', function () { From f6e34657e2b1347677b87afbdadd73a6195b1cb3 Mon Sep 17 00:00:00 2001 From: John Salis Date: Tue, 17 Nov 2020 01:00:18 -0500 Subject: [PATCH 0375/1476] Add IdentityLink support to Beachfront adapter (#5977) * add IdentityLink support to beachfront adapter * bump adapter version Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 52 ++++++++++++++----- .../spec/modules/beachfrontBidAdapter_spec.js | 28 ++++++++++ 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index f0e28956e5d..5f0a4b03a04 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.12'; +const ADAPTER_VERSION = '1.13'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; @@ -17,6 +17,11 @@ export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/l export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement', 'skip', 'skipmin', 'skipafter']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; +export const SUPPORTED_USER_IDS = [ + { key: 'tdid', source: 'adserver.org', rtiPartner: 'TDID', queryParam: 'tdid' }, + { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' } +]; + let appId = ''; export const spec = { @@ -257,6 +262,29 @@ function getTopWindowReferrer() { } } +function getEids(bid) { + return SUPPORTED_USER_IDS + .map(getUserId(bid)) + .filter(x => x); +} + +function getUserId(bid) { + return ({ key, source, rtiPartner }) => { + let id = bid.userId && bid.userId[key]; + return id ? formatEid(id, source, rtiPartner) : null; + }; +} + +function formatEid(id, source, rtiPartner) { + return { + source, + uids: [{ + id, + ext: { rtiPartner } + }] + }; +} + function getVideoTargetingParams(bid) { const result = {}; const excludeProps = ['playerSize', 'context', 'w', 'h']; @@ -281,6 +309,7 @@ function createVideoRequestData(bid, bidderRequest) { let bidfloor = getVideoBidParam(bid, 'bidfloor'); let tagid = getVideoBidParam(bid, 'tagid'); let topLocation = getTopWindowLocation(bidderRequest); + let eids = getEids(bid); let payload = { isPrebid: true, appId: appId, @@ -329,16 +358,8 @@ function createVideoRequestData(bid, bidderRequest) { payload.user.ext.consent = consentString; } - if (bid.userId && bid.userId.tdid) { - payload.user.ext.eids = [{ - source: 'adserver.org', - uids: [{ - id: bid.userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }]; + if (eids.length > 0) { + payload.user.ext.eids = eids; } let connection = navigator.connection || navigator.webkitConnection; @@ -385,9 +406,12 @@ function createBannerRequestData(bids, bidderRequest) { payload.gdprConsent = consentString; } - if (bids[0] && bids[0].userId && bids[0].userId.tdid) { - payload.tdid = bids[0].userId.tdid; - } + SUPPORTED_USER_IDS.forEach(({ key, queryParam }) => { + let id = bids[0] && bids[0].userId && bids[0].userId[key]; + if (id) { + payload[queryParam] = id; + } + }); return payload; } diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 587596eaa5c..aa952d088a7 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -295,6 +295,24 @@ describe('BeachfrontAdapter', function () { }] }); }); + + it('must add the IdentityLink ID to the request', () => { + const idl_env = '4321'; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.userId = { idl_env }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.user.ext.eids[0]).to.deep.equal({ + source: 'liveramp.com', + uids: [{ + id: idl_env, + ext: { + rtiPartner: 'idl' + } + }] + }); + }); }); describe('for banner bids', function () { @@ -435,6 +453,16 @@ describe('BeachfrontAdapter', function () { const data = requests[0].data; expect(data.tdid).to.equal(tdid); }); + + it('must add the IdentityLink ID to the request', () => { + const idl_env = '4321'; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.userId = { idl_env }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.idl).to.equal(idl_env); + }); }); describe('for multi-format bids', function () { From f47287eef645c9c20b62aeaedf96a7893c602036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Tue, 17 Nov 2020 08:18:40 +0200 Subject: [PATCH 0376/1476] Vidazoo Adapter: Feature/spec-gvlid (#5980) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): expose spec gvlid Co-authored-by: roman --- modules/vidazooBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 4b3b1767cec..7fc6e3a5395 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -const GLVID = 744; +const GVLID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; const BIDDER_CODE = 'vidazoo'; const BIDDER_VERSION = '1.0.0'; @@ -24,7 +24,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'pubcid': 1, 'tdid': 1, }; -const storage = getStorageManager(GLVID); +const storage = getStorageManager(GVLID); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; @@ -266,6 +266,7 @@ export function tryParseJSON(value) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, version: BIDDER_VERSION, supportedMediaTypes: [BANNER], isBidRequestValid, From 842f21c86fc3d7dba5bf5707140621c91243f3f6 Mon Sep 17 00:00:00 2001 From: jdwieland8282 Date: Tue, 17 Nov 2020 03:26:16 -0700 Subject: [PATCH 0377/1476] Update sharedIdSystem.js with GVLID (#5988) adding GVLID variable and spec definition, will come back and add it to getStorageManager once if I find that function referenced in the sharedid module. --- modules/sharedIdSystem.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index cdd840c4f54..49cac46f1df 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -21,6 +21,7 @@ const TIME_MAX = Math.pow(2, 48) - 1; const TIME_LEN = 10; const RANDOM_LEN = 16; const id = factory(); +const GVLID = 887; /** * Constructs cookie value * @param value @@ -283,6 +284,11 @@ export const sharedIdSubmodule = { */ name: MODULE_NAME, + /** + * Vendor id of Prebid + * @type {Number} + */ + gvlid: GVLID, /** * decode the stored id value for passing to bid requests * @function From 18afadd0ed90cf16f4989f300ae9468623f56176 Mon Sep 17 00:00:00 2001 From: Elijah Valenciano Date: Tue, 17 Nov 2020 06:51:07 -0500 Subject: [PATCH 0378/1476] FreeWheel SSP - Added GDPR to userSync (#5969) --- modules/freewheel-sspBidAdapter.js | 13 ++++++++-- .../modules/freewheel-sspBidAdapter_spec.js | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index dce678362cb..53f490a0a3c 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -407,11 +407,20 @@ export const spec = { return bidResponses; }, - getUserSyncs: function(syncOptions) { + getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy) { + var gdprParams = ''; + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `?gdpr_consent=${gdprConsent.consentString}`; + } + } + if (syncOptions && syncOptions.pixelEnabled) { return [{ type: 'image', - url: USER_SYNC_URL + url: USER_SYNC_URL + gdprParams }]; } else { return []; diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js index 3047b635d13..c44d7908ba8 100644 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -152,6 +152,19 @@ describe('freewheelSSP BidAdapter Test', () => { expect(payload.playerSize).to.equal('300x600'); expect(payload._fw_gdpr_consent).to.exist.and.to.be.a('string'); expect(payload._fw_gdpr_consent).to.equal(gdprConsentString); + + let gdprConsent = { + 'gdprApplies': true, + 'consentString': gdprConsentString + } + let syncOptions = { + 'pixelEnabled': true + } + const userSyncs = spec.getUserSyncs(syncOptions, null, gdprConsent, null); + expect(userSyncs).to.deep.equal([{ + type: 'image', + url: 'https://ads.stickyadstv.com/auto-user-sync?gdpr=1&gdpr_consent=1FW-SSP-gdprConsent-' + }]); }); }) @@ -226,6 +239,19 @@ describe('freewheelSSP BidAdapter Test', () => { expect(payload.playerSize).to.equal('300x600'); expect(payload._fw_gdpr_consent).to.exist.and.to.be.a('string'); expect(payload._fw_gdpr_consent).to.equal(gdprConsentString); + + let gdprConsent = { + 'gdprApplies': true, + 'consentString': gdprConsentString + } + let syncOptions = { + 'pixelEnabled': true + } + const userSyncs = spec.getUserSyncs(syncOptions, null, gdprConsent, null); + expect(userSyncs).to.deep.equal([{ + type: 'image', + url: 'https://ads.stickyadstv.com/auto-user-sync?gdpr=1&gdpr_consent=1FW-SSP-gdprConsent-' + }]); }); }) From b73ab25d9a5d1d33a6a377bf2413b07fa1fca5bf Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Tue, 17 Nov 2020 21:03:31 +0900 Subject: [PATCH 0379/1476] Fix request size validate (#5951) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * fixed request flow use cookie instead of local storage validate video size Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- modules/relaidoBidAdapter.js | 38 +++++---------------- test/spec/modules/relaidoBidAdapter_spec.js | 14 ++++---- 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index bc2854de40b..c77afbe6ec5 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,17 +6,13 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.1'; +const ADAPTER_VERSION = '1.0.2'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; const storage = getStorageManager(); function isBidRequestValid(bid) { - if (!utils.isSafariBrowser() && !hasUuid()) { - utils.logWarn('uuid is not found.'); - return false; - } if (!utils.deepAccess(bid, 'params.placementId')) { utils.logWarn('placementId param is reqeuired.'); return false; @@ -64,7 +60,7 @@ function buildRequests(validBidRequests, bidderRequest) { }; if (hasVideoMediaType(bidRequest)) { - const playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + const playerSize = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize')); payload.width = playerSize[0][0]; payload.height = playerSize[0][1]; } else if (hasBannerMediaType(bidRequest)) { @@ -101,10 +97,6 @@ function interpretResponse(serverResponse, bidRequest) { return []; } - if (body.uuid) { - storage.setDataInLocalStorage(UUID_KEY, body.uuid); - } - const playerUrl = bidRequest.player || body.playerUrl; const mediaType = bidRequest.mediaType || VIDEO; @@ -141,7 +133,6 @@ function getUserSyncs(syncOptions, serverResponses) { if (serverResponses.length > 0) { syncUrl = utils.deepAccess(serverResponses, '0.body.syncUrl') || syncUrl; } - receiveMessage(); return [{ type: 'iframe', url: syncUrl @@ -219,17 +210,6 @@ function outstreamRender(bid) { }); } -function receiveMessage() { - window.addEventListener('message', setUuid); -} - -function setUuid(e) { - if (utils.isPlainObject(e.data) && e.data.relaido_uuid) { - storage.setDataInLocalStorage(UUID_KEY, e.data.relaido_uuid); - window.removeEventListener('message', setUuid); - } -} - function isBannerValid(bid) { if (!isMobile()) { return false; @@ -242,8 +222,8 @@ function isBannerValid(bid) { } function isVideoValid(bid) { - const playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); - if (playerSize && utils.isArray(playerSize) && playerSize.length > 0) { + const playerSize = getValidSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize')); + if (playerSize.length > 0) { const context = utils.deepAccess(bid, 'mediaTypes.video.context'); if (context && context === 'outstream') { return true; @@ -252,12 +232,12 @@ function isVideoValid(bid) { return false; } -function hasUuid() { - return !!storage.getDataFromLocalStorage(UUID_KEY); -} - function getUuid() { - return storage.getDataFromLocalStorage(UUID_KEY) || ''; + const id = storage.getCookie(UUID_KEY) + if (id) return id; + const newId = utils.generateUUID(); + storage.setCookie(UUID_KEY, newId); + return newId; } export function isMobile() { diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 42818232cda..65dcd9b7db7 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -1,16 +1,21 @@ import { expect } from 'chai'; import { spec } from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; +import { getStorageManager } from '../../../src/storageManager.js'; const UUID_KEY = 'relaido_uuid'; const DEFAULT_USER_AGENT = window.navigator.userAgent; const MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1'; +const relaido_uuid = 'hogehoge'; const setUADefault = () => { window.navigator.__defineGetter__('userAgent', function () { return DEFAULT_USER_AGENT }) }; const setUAMobile = () => { window.navigator.__defineGetter__('userAgent', function () { return MOBILE_USER_AGENT }) }; +const storage = getStorageManager(); +storage.setCookie(UUID_KEY, relaido_uuid); + describe('RelaidoAdapter', function () { - const relaido_uuid = 'hogehoge'; + window.document.cookie = `${UUID_KEY}=${relaido_uuid}` let bidRequest; let bidderRequest; let serverResponse; @@ -65,7 +70,6 @@ describe('RelaidoAdapter', function () { height: bidRequest.mediaTypes.video.playerSize[0][1], mediaType: 'video', }; - localStorage.setItem(UUID_KEY, relaido_uuid); }); describe('spec.isBidRequestValid', function () { @@ -140,12 +144,6 @@ describe('RelaidoAdapter', function () { setUADefault(); }); - it('should return false when the uuid are missing', function () { - localStorage.removeItem(UUID_KEY); - const result = !!(utils.isSafariBrowser()); - expect(spec.isBidRequestValid(bidRequest)).to.equal(result); - }); - it('should return false when the placementId params are missing', function () { bidRequest.params.placementId = undefined; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); From 96989ca4b8d36917e87caec1208dbf0a92e87537 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 17 Nov 2020 06:29:42 -0800 Subject: [PATCH 0380/1476] Prebid Server Bid Adapter: Expose errors and server response times always (#5866) * Expose pbs reported errors Expose serverLatencyMillis always * clean up logic for other weird edge case * review comment for make function * not sure how parenthesis got there --- modules/prebidServerBidAdapter/index.js | 17 ++++++++++ modules/rubiconAnalyticsAdapter.js | 32 ++++++++++++++++--- .../modules/rubiconAnalyticsAdapter_spec.js | 1 + 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index b153d0bf8db..7c7962781d2 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -369,6 +369,18 @@ function addWurl(auctionId, adId, wurl) { } } +function getPbsResponseData(bidderRequests, response, pbsName, pbjsName) { + const bidderValues = utils.deepAccess(response, `ext.${pbsName}`); + if (bidderValues) { + Object.keys(bidderValues).forEach(bidder => { + let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); + if (biddersReq) { + biddersReq[pbjsName] = bidderValues[bidder]; + } + }); + } +} + /** * @param {string} auctionId * @param {string} adId generated value set to bidObject.adId by bidderFactory Bid() @@ -676,6 +688,9 @@ const OPEN_RTB_PROTOCOL = { interpretResponse(response, bidderRequests) { const bids = []; + [['errors', 'serverErrors'], ['responsetimemillis', 'serverResponseTimeMs']] + .forEach(info => getPbsResponseData(bidderRequests, response, info[0], info[1])) + if (response.seatbid) { // a seatbid object contains a `bid` array and a `seat` string response.seatbid.forEach(seatbid => { @@ -698,6 +713,8 @@ const OPEN_RTB_PROTOCOL = { bidObject.cpm = cpm; + // 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 let serverResponseTimeMs = utils.deepAccess(response, ['ext', 'responsetimemillis', seatbid.seat].join('.')); if (bidRequest && serverResponseTimeMs) { bidRequest.serverResponseTimeMs = serverResponseTimeMs; diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 4011232ae3b..6f00e9536d9 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -13,6 +13,14 @@ const COOKIE_NAME = 'rpaSession'; const LAST_SEEN_EXPIRE_TIME = 1800000; // 30 mins const END_EXPIRE_TIME = 21600000; // 6 hours +const pbsErrorMap = { + 1: 'timeout-error', + 2: 'input-error', + 3: 'connect-error', + 4: 'request-error', + 999: 'generic-error' +} + let prebidGlobal = getGlobal(); const { EVENTS: { @@ -631,10 +639,22 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { bid.bidResponse = parseBidResponse(args, bid.bidResponse); break; case BIDDER_DONE: + const serverError = utils.deepAccess(args, 'serverErrors.0'); + const serverResponseTimeMs = args.serverResponseTimeMs; args.bids.forEach(bid => { let cachedBid = cache.auctions[bid.auctionId].bids[bid.bidId || bid.requestId]; if (typeof bid.serverResponseTimeMs !== 'undefined') { cachedBid.serverLatencyMillis = bid.serverResponseTimeMs; + } else if (serverResponseTimeMs && bid.source === 's2s') { + cachedBid.serverLatencyMillis = serverResponseTimeMs; + } + // if PBS said we had an error, and this bid has not been processed by BID_RESPONSE YET + if (serverError && (!cachedBid.status || ['no-bid', 'error'].indexOf(cachedBid.status) !== -1)) { + cachedBid.status = 'error'; + cachedBid.error = { + code: pbsErrorMap[serverError.code] || pbsErrorMap[999], + description: serverError.message + } } if (!cachedBid.status) { cachedBid.status = 'no-bid'; @@ -675,10 +695,14 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { args.forEach(badBid => { let auctionCache = cache.auctions[badBid.auctionId]; let bid = auctionCache.bids[badBid.bidId || badBid.requestId]; - bid.status = 'error'; - bid.error = { - code: 'timeout-error' - }; + // might be set already by bidder-done, so do not overwrite + if (bid.status !== 'error') { + bid.status = 'error'; + bid.error = { + code: 'timeout-error', + message: 'marked by prebid.js as timeout' // will help us diff if timeout was set by PBS or PBJS + }; + } }); break; } diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 7c552570da6..d65083ce480 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -310,6 +310,7 @@ const MOCK = { ], BIDDER_DONE: { 'bidderCode': 'rubicon', + 'serverResponseTimeMs': 42, 'bids': [ BID, Object.assign({}, BID2, { From 71e9cc7b48b736ab6671acf6a5ad7c17ac4460e5 Mon Sep 17 00:00:00 2001 From: Ignat Khaylov Date: Wed, 18 Nov 2020 09:29:05 +0300 Subject: [PATCH 0381/1476] Between: schain support was added (#5982) Co-authored-by: Ignat Khaylov --- modules/betweenBidAdapter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index fb3fcdb8d89..0ed05717391 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -59,6 +59,10 @@ export const spec = { } } + if (i.schain) { + params.schain = encodeToBase64WebSafe(JSON.stringify(i.schain)); + } + if (refInfo && refInfo.referer) params.ref = refInfo.referer; if (gdprConsent) { @@ -166,6 +170,10 @@ function getTz() { return new Date().getTimezoneOffset(); } +function encodeToBase64WebSafe(string) { + return btoa(string).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + /* function get_pubdata(adds) { if (adds !== undefined && adds.pubdata !== undefined) { From efbc9d308b6e6451d234c7548e6eb1821044e413 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 17 Nov 2020 22:38:58 -0800 Subject: [PATCH 0382/1476] fix source lowercase bug (#5989) --- modules/rubiconAnalyticsAdapter.js | 4 ++-- test/spec/modules/rubiconAnalyticsAdapter_spec.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 6f00e9536d9..85b6596ba12 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -125,7 +125,7 @@ function sendMessage(auctionId, bidWonId) { if (source) { return source; } - return serverConfig && Array.isArray(serverConfig.bidders) && serverConfig.bidders.indexOf(bid.bidder) !== -1 + return serverConfig && Array.isArray(serverConfig.bidders) && serverConfig.bidders.some(s2sBidder => s2sBidder.toLowerCase() === bid.bidder) !== -1 ? 'server' : 'client' }, 'clientLatencyMillis', @@ -533,7 +533,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { 'bidder', bidder => bidder.toLowerCase(), 'bidId', 'status', () => 'no-bid', // default a bid to no-bid until response is recieved or bid is timed out - 'finalSource as source', + 'source', () => formatSource(bid.src), 'params', (params, bid) => { switch (bid.bidder) { // specify bidder params we want here diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index d65083ce480..9e343d07dd5 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -255,7 +255,8 @@ const MOCK = { 'sizes': [[640, 480]], 'bidId': '2ecff0db240757', 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 'client' }, { 'bidder': 'rubicon', @@ -279,7 +280,8 @@ const MOCK = { 'sizes': [[1000, 300], [970, 250], [728, 90]], 'bidId': '3bd4ebb1c900e2', 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 's2s' } ], 'auctionStart': 1519149536560, From da87e57527ea3c26c6678f1a223794555706b4fd Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Wed, 18 Nov 2020 08:44:49 +0200 Subject: [PATCH 0383/1476] Add consent to sync url (#5981) * Add Adman bid adapter * Add supportedMediaTypes property * Update ADman Media bidder adapter * Remove console.log * Fix typo * revert package-json.lock * Delete package-lock.json * back to original package-lock.json * catch pbjs error * catch pbjs error * catch pbjs error * log * remove eu url * remove eu url * remove eu url * remove eu url * remove eu url * Update admanBidAdapter.js add consnet to sync url * Update admanBidAdapter.js fix import * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js check consent object data availability Co-authored-by: minoru katogi Co-authored-by: minoru katogi Co-authored-by: ADman Media Co-authored-by: SmartyAdman --- modules/admanBidAdapter.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index 5dc3412ee66..2e4091e7a24 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -95,10 +95,21 @@ export const spec = { return response; }, - getUserSyncs: (syncOptions, serverResponses) => { + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncUrl = URL_SYNC + 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}`; + } return [{ type: 'image', - url: URL_SYNC + url: syncUrl }]; } From c308898cddc80d7669c3d2d318e13ef5cb366780 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 18 Nov 2020 07:58:56 +0100 Subject: [PATCH 0384/1476] add provider as an option in id5 config params to identity prebid identity wrappers (#5983) --- modules/id5IdSystem.js | 1 + modules/id5IdSystem.md | 3 ++- test/spec/modules/id5IdSystem_spec.js | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 5a1fc69a758..a1596e96fcd 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -89,6 +89,7 @@ export const id5IdSubmodule = { 'nbPage': incrementNb(config.params.partner), 'o': 'pbjs', 'pd': config.params.pd || '', + 'provider': config.params.provider || '', 'rf': referer.referer, 's': signature, 'top': referer.reachedTop ? 1 : 0, diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 80ba451b235..e5e3969c19c 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -45,10 +45,11 @@ pbjs.setConfig({ | params | Required | Object | Details for the ID5 Universal ID. | | | params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | | params.pd | Optional | String | Publisher-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/x/BIAZ) 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` | | 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). \ No newline at end of file +**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 bfc41e5f5e8..adffca6dbe5 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -141,6 +141,7 @@ describe('ID5 ID System', function() { expect(requestBody.o).to.eq('pbjs'); expect(requestBody.pd).to.eq(''); expect(requestBody.s).to.eq(''); + expect(requestBody.provider).to.eq(''); expect(requestBody.v).to.eq('$prebid.version$'); request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); From 8340be830911e904b457950861945d6a34bbeed7 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 18 Nov 2020 15:14:06 -0500 Subject: [PATCH 0385/1476] Prebid 4.17.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b62de09e1f..d957c0901c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.17.0-pre", + "version": "4.17.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a84b4e994c7c64c4adabba4fa2215df8e64f23b8 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 18 Nov 2020 15:42:30 -0500 Subject: [PATCH 0386/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d957c0901c3..48963f36c76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.17.0", + "version": "4.18.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 57fb14ccc2d0f3cf9113d820f75a9f59645e66db Mon Sep 17 00:00:00 2001 From: Dmitriy Labuzov Date: Thu, 19 Nov 2020 11:16:55 +0300 Subject: [PATCH 0387/1476] Instream video support for Yieldmo adapter (#5973) Co-authored-by: Dmitriy Labuzov --- modules/yieldmoBidAdapter.js | 399 +++++++++++++++--- modules/yieldmoBidAdapter.md | 75 +++- test/spec/modules/yieldmoBidAdapter_spec.js | 438 ++++++++++---------- 3 files changed, 614 insertions(+), 298 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 08dc3189eda..05af0bf0d66 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -1,103 +1,133 @@ import * as utils from '../src/utils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import includes from 'core-js-pure/features/array/includes'; const BIDDER_CODE = 'yieldmo'; const CURRENCY = 'USD'; const TIME_TO_LIVE = 300; const NET_REVENUE = true; -const SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; +const BANNER_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; +const VIDEO_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebidvideo'; +const OPENRTB_VIDEO_BIDPARAMS = ['placement', 'startdelay', 'skipafter', + 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos']; +const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const localWindow = utils.getWindowTop(); export const spec = { code: BIDDER_CODE, - supportedMediaTypes: ['banner'], + supportedMediaTypes: [BANNER, VIDEO], + /** * Determines whether or not the given bid request is valid. * @param {object} bid, bid to validate * @return boolean, true if valid, otherwise false */ isBidRequestValid: function (bid) { - return !!(bid && bid.adUnitCode && bid.bidId); + return !!(bid && bid.adUnitCode && bid.bidId && (hasBannerMediaType(bid) || hasVideoMediaType(bid)) && + validateVideoParams(bid)); }, + /** * 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. + * @param {BidderRequest} bidderRequest bidder request object. * @return ServerRequest Info describing the request to the server. */ buildRequests: function (bidRequests, bidderRequest) { - let serverRequest = { - pbav: '$prebid.version$', - p: [], - page_url: bidderRequest.refererInfo.referer, - bust: new Date().getTime().toString(), - pr: bidderRequest.refererInfo.referer, - scrd: localWindow.devicePixelRatio || 0, - dnt: getDNT(), - description: getPageDescription(), - title: localWindow.document.title || '', - w: localWindow.innerWidth, - h: localWindow.innerHeight, - userConsent: JSON.stringify({ - // case of undefined, stringify will remove param - gdprApplies: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', - cmp: utils.deepAccess(bidderRequest, 'gdprConsent.consentString') || '' - }), - us_privacy: utils.deepAccess(bidderRequest, 'uspConsent') || '' - }; - - const mtp = window.navigator.maxTouchPoints; - if (mtp) { - serverRequest.mtp = mtp; - } + const bannerBidRequests = bidRequests.filter(request => hasBannerMediaType(request)); + const videoBidRequests = bidRequests.filter(request => hasVideoMediaType(request)); + + let serverRequests = []; + if (bannerBidRequests.length > 0) { + let serverRequest = { + pbav: '$prebid.version$', + p: [], + page_url: bidderRequest.refererInfo.referer, + bust: new Date().getTime().toString(), + pr: bidderRequest.refererInfo.referer, + scrd: localWindow.devicePixelRatio || 0, + dnt: getDNT(), + description: getPageDescription(), + title: localWindow.document.title || '', + w: localWindow.innerWidth, + h: localWindow.innerHeight, + userConsent: JSON.stringify({ + // case of undefined, stringify will remove param + gdprApplies: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', + cmp: utils.deepAccess(bidderRequest, 'gdprConsent.consentString') || '' + }), + us_privacy: utils.deepAccess(bidderRequest, 'uspConsent') || '' + }; - bidRequests.forEach(request => { - serverRequest.p.push(addPlacement(request)); - const pubcid = getId(request, 'pubcid'); - if (pubcid) { - serverRequest.pubcid = pubcid; - } else if (request.crumbs) { - if (request.crumbs.pubcid) { + const mtp = window.navigator.maxTouchPoints; + if (mtp) { + serverRequest.mtp = mtp; + } + + bannerBidRequests.forEach(request => { + serverRequest.p.push(addPlacement(request)); + const pubcid = getId(request, 'pubcid'); + if (pubcid) { + serverRequest.pubcid = pubcid; + } else if (request.crumbs && request.crumbs.pubcid) { serverRequest.pubcid = request.crumbs.pubcid; } - } - const tdid = getId(request, 'tdid'); - if (tdid) { - serverRequest.tdid = tdid; - } - const criteoId = getId(request, 'criteoId'); - if (criteoId) { - serverRequest.cri_prebid = criteoId; - } - if (request.schain) { - serverRequest.schain = - JSON.stringify(request.schain); - } - }); - serverRequest.p = '[' + serverRequest.p.toString() + ']'; - return { - method: 'GET', - url: SERVER_ENDPOINT, - data: serverRequest - }; + const tdid = getId(request, 'tdid'); + if (tdid) { + serverRequest.tdid = tdid; + } + const criteoId = getId(request, 'criteoId'); + if (criteoId) { + serverRequest.cri_prebid = criteoId; + } + if (request.schain) { + serverRequest.schain = JSON.stringify(request.schain); + } + }); + serverRequest.p = '[' + serverRequest.p.toString() + ']'; + serverRequests.push({ + method: 'GET', + url: BANNER_SERVER_ENDPOINT, + data: serverRequest + }); + } + + if (videoBidRequests.length > 0) { + const serverRequest = openRtbRequest(videoBidRequests, bidderRequest); + serverRequests.push({ + method: 'POST', + url: VIDEO_SERVER_ENDPOINT, + data: serverRequest + }); + } + return serverRequests; }, + /** * Makes Yieldmo Ad Server response compatible to Prebid specs - * @param serverResponse successful response from Ad Server + * @param {ServerResponse} serverResponse successful response from Ad Server + * @param {ServerRequest} bidRequest * @return {Bid[]} an array of bids */ - interpretResponse: function (serverResponse) { + interpretResponse: function (serverResponse, bidRequest) { let bids = []; - let data = serverResponse.body; + const data = serverResponse.body; if (data.length > 0) { data.forEach(response => { - if (response.cpm && response.cpm > 0) { - bids.push(createNewBid(response)); + if (response.cpm > 0) { + bids.push(createNewBannerBid(response)); } }); } + if (data.seatbid) { + const seatbids = data.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []); + seatbids.forEach(bid => bids.push(createNewVideoBid(bid, bidRequest))); + } return bids; }, + getUserSyncs: function () { return []; } @@ -108,6 +138,20 @@ registerBidder(spec); * Helper Functions ***************************************/ +/** + * @param {BidRequest} bidRequest bid request + */ +function hasBannerMediaType(bidRequest) { + return !!utils.deepAccess(bidRequest, 'mediaTypes.banner'); +} + +/** + * @param {BidRequest} bidRequest bid request + */ +function hasVideoMediaType(bidRequest) { + return !!utils.deepAccess(bidRequest, 'mediaTypes.video'); +} + /** * Adds placement information to array * @param request bid request @@ -130,10 +174,10 @@ function addPlacement(request) { } /** - * creates a new bid with response information + * creates a new banner bid with response information * @param response server response */ -function createNewBid(response) { +function createNewBannerBid(response) { return { requestId: response['callback_id'], cpm: response.cpm, @@ -147,6 +191,27 @@ function createNewBid(response) { }; } +/** + * creates a new video bid with response information + * @param response openRTB server response + * @param bidRequest server request + */ +function createNewVideoBid(response, bidRequest) { + const imp = (utils.deepAccess(bidRequest, 'data.imp') || []).find(imp => imp.id === response.impid); + return { + requestId: imp.id, + cpm: response.price, + width: imp.video.w, + height: imp.video.h, + creativeId: response.crid || response.adid, + currency: CURRENCY, + netRevenue: NET_REVENUE, + mediaType: VIDEO, + ttl: TIME_TO_LIVE, + vastXml: response.adm + }; +} + /** * Detects whether dnt is true * @returns true if user enabled dnt @@ -179,3 +244,215 @@ function getPageDescription() { function getId(request, idType) { return (typeof utils.deepAccess(request, 'userId') === 'object') ? request.userId[idType] : undefined; } + +/** + * @param {BidRequest[]} bidRequests bid request object + * @param {BidderRequest} bidderRequest bidder request object + * @return Object OpenRTB request object + */ +function openRtbRequest(bidRequests, bidderRequest) { + let openRtbRequest = { + id: bidRequests[0].bidderRequestId, + at: 1, + imp: bidRequests.map(bidRequest => openRtbImpression(bidRequest)), + site: openRtbSite(bidRequests[0], bidderRequest), + device: openRtbDevice(), + badv: bidRequests[0].params.badv || [], + bcat: bidRequests[0].params.bcat || [], + ext: { + prebid: '$prebid.version$', + } + }; + + populateOpenRtbGdpr(openRtbRequest, bidderRequest); + + return openRtbRequest; +} + +/** + * @param {BidRequest} bidRequest bidder request object. + * @return Object OpenRTB's 'imp' (impression) object + */ +function openRtbImpression(bidRequest) { + const videoReq = utils.deepAccess(bidRequest, 'mediaTypes.video'); + const size = extractPlayerSize(bidRequest); + const imp = { + id: bidRequest.bidId, + tagid: bidRequest.adUnitCode, + bidfloor: bidRequest.params.bidfloor || 0, + ext: { + placement_id: bidRequest.params.placementId + }, + video: { + w: size[0], + h: size[1], + mimes: videoReq.mimes, + linearity: 1 + } + }; + + const videoParams = utils.deepAccess(bidRequest, 'params.video'); + Object.keys(videoParams) + .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) + .forEach(param => imp.video[param] = videoParams[param]); + + if (videoParams.skippable) { + imp.video.skip = 1; + } + + return imp; +} + +/** + * @param {BidRequest} bidRequest bidder request object. + * @return [number, number] || null Player's width and height, or undefined otherwise. + */ +function extractPlayerSize(bidRequest) { + const sizeArr = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + if (utils.isArrayOfNums(sizeArr, 2)) { + return sizeArr; + } else if (utils.isArray(sizeArr) && utils.isArrayOfNums(sizeArr[0], 2)) { + return sizeArr[0]; + } + return null; +} + +/** + * @param {BidRequest} bidRequest bid request object + * @param {BidderRequest} bidderRequest bidder request object + * @return Object OpenRTB's 'site' object + */ +function openRtbSite(bidRequest, bidderRequest) { + let result = {}; + + const loc = utils.parseUrl(utils.deepAccess(bidderRequest, 'refererInfo.referer')); + if (!utils.isEmpty(loc)) { + result.page = `${loc.protocol}://${loc.hostname}${loc.pathname}`; + } + + if (self === top && document.referrer) { + result.ref = document.referrer; + } + + const keywords = document.getElementsByTagName('meta')['keywords']; + if (keywords && keywords.content) { + result.keywords = keywords.content; + } + + const siteParams = utils.deepAccess(bidRequest, 'params.site'); + if (siteParams) { + Object.keys(siteParams) + .filter(param => includes(OPENRTB_VIDEO_SITEPARAMS, param)) + .forEach(param => result[param] = siteParams[param]); + } + return result; +} + +/** + * @return Object OpenRTB's 'device' object + */ +function openRtbDevice() { + return { + ua: navigator.userAgent, + language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), + }; +} + +/** + * Updates openRtbRequest with GDPR info from bidderRequest, if present. + * @param {Object} openRtbRequest OpenRTB's request to update. + * @param {BidderRequest} bidderRequest bidder request object. + */ +function populateOpenRtbGdpr(openRtbRequest, bidderRequest) { + const gdpr = bidderRequest.gdprConsent; + if (gdpr && 'gdprApplies' in gdpr) { + utils.deepSetValue(openRtbRequest, 'regs.ext.gdpr', gdpr.gdprApplies ? 1 : 0); + utils.deepSetValue(openRtbRequest, 'user.ext.consent', gdpr.consentString); + } + const uspConsent = utils.deepAccess(bidderRequest, 'uspConsent'); + if (uspConsent) { + utils.deepSetValue(openRtbRequest, 'regs.ext.us_privacy', uspConsent); + } +} + +/** + * Determines whether or not the given video bid request is valid. If it's not a video bid, returns true. + * @param {object} bid, bid to validate + * @return boolean, true if valid, otherwise false + */ +function validateVideoParams(bid) { + if (!hasVideoMediaType(bid)) { + return true; + } + + const paramRequired = (paramStr, value, conditionStr) => { + let error = `"${paramStr}" is required`; + if (conditionStr) { + 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) => { + const value = utils.deepAccess(bid, fieldPath); + if (!validateCb(value)) { + errorCb(fieldPath, value, errorCbParam); + } + return value; + } + + try { + validate('params.placementId', val => !utils.isEmpty(val), paramRequired); + + validate('mediaTypes.video.playerSize', val => utils.isArrayOfNums(val, 2) || + (utils.isArray(val) && val.every(v => utils.isArrayOfNums(v, 2))), + paramInvalid, 'array of 2 integers, ex: [640,480] or [[640,480]]'); + + validate('mediaTypes.video.mimes', val => isDefined(val), paramRequired); + validate('mediaTypes.video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, + 'array of strings, ex: ["video/mp4"]'); + + validate('params.video', val => !utils.isEmpty(val), paramRequired); + + const placement = validate('params.video.placement', val => isDefined(val), paramRequired); + validate('params.video.placement', val => val >= 1 && val <= 5, paramInvalid); + if (placement === 1) { + validate('params.video.startdelay', val => isDefined(val), + (field, v) => paramRequired(field, v, 'placement == 1')); + validate('params.video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); + } + + validate('params.video.protocols', val => isDefined(val), paramRequired); + validate('params.video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + paramInvalid, 'array of numbers, ex: [2,3]'); + + validate('params.video.api', val => isDefined(val), paramRequired); + validate('params.video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + paramInvalid, 'array of numbers, ex: [2,3]'); + + validate('params.video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, + 'array of integers, ex: [2,6]'); + + validate('params.video.maxduration', val => isDefined(val), paramRequired); + validate('params.video.maxduration', val => utils.isInteger(val), paramInvalid); + validate('params.video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('params.video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); + validate('params.video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('params.video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('params.badv', val => !isDefined(val) || utils.isArray(val), paramInvalid, + 'array of strings, ex: ["ford.com","pepsi.com"]'); + validate('params.bcat', val => !isDefined(val) || utils.isArray(val), paramInvalid, + 'array of strings, ex: ["IAB1-5","IAB1-6"]'); + return true; + } catch (e) { + utils.logError(e.message); + return false; + } +} diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 0f86d2507d1..1b8b7b1b741 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -11,26 +11,61 @@ Note: Our ads will only render in mobile Connects to Yieldmo Ad Server for bids. -Yieldmo bid adapter supports Banner. +Yieldmo bid adapter supports Banner and Video. # Test Parameters + +## Banner + +Sample banner ad unit config: +```javascript +var adUnits = [{ // Banner adUnit + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [{ + bidder: 'yieldmo', + params: { + placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only) + bidFloor: .28 // optional param + } + }] +}]; +``` + +## Video + +Sample instream video ad unit config: +```javascript +var adUnits = [{ // Video adUnit + code: 'div-video-ad-1234567890', + mediaTypes: { + video: { + playerSize: [640, 480], // required + context: 'instream', + mimes: ['video/mp4'] // required, array of strings + } + }, + bids: [{ + bidder: 'yieldmo', + params: { + placementId: '1524592390382976659', // required + video: { + placement: 1, // required, integer + maxduration: 30, // required, integer + minduration: 15, // optional, integer + pos: 1, // optional, integer + startdelay: 10, // required if placement == 1 + protocols: [2, 3], // required, array of integers + api: [2, 3], // required, array of integers + playbackmethod: [2,6], // required, array of integers + skippable: true, // optional, boolean + skipafter: 10 // optional, integer + } + } + }] +}]; ``` -var adUnits = [ - // Banner adUnit - { - code: 'div-gpt-ad-1460505748561-0', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]], - } - } - bids: [{ - bidder: 'yieldmo', - params: { - placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only) - bidFloor: .28 // optional param - } - }] - } -]; -``` \ No newline at end of file diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index caeb26266fe..deabef69093 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -1,19 +1,16 @@ import { expect } from 'chai'; import { spec } from 'modules/yieldmoBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; describe('YieldmoAdapter', function () { - const adapter = newBidder(spec); - const ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; + const BANNER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; + const VIDEO_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebidvideo'; - let tdid = '8d146286-91d4-4958-aff4-7e489dd1abd6'; - let criteoId = 'aff4'; - - let bid = { + const mockBannerBid = (rootParams = {}, params = {}) => ({ bidder: 'yieldmo', params: { bidFloor: 0.1, + ...params, }, adUnitCode: 'adunit-code', mediaTypes: { @@ -31,15 +28,44 @@ describe('YieldmoAdapter', function () { pubcid: 'c604130c-0144-4b63-9bf2-c2bd8c8d86da', }, userId: { - tdid, + tdid: '8d146286-91d4-4958-aff4-7e489dd1abd6' + }, + ...rootParams + }); + + const mockVideoBid = (rootParams = {}, params = {}, videoParams = {}) => ({ + bidder: 'yieldmo', + adUnitCode: 'adunit-code-video', + bidId: '321video123', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + mimes: ['video/mp4'] + }, }, - }; - let bidArray = [bid]; - let bidderRequest = { + params: { + placementId: '123', + ...params, + video: { + placement: 1, + maxduration: 30, + startdelay: 10, + protocols: [2, 3], + api: [2, 3], + skipppable: true, + playbackmethod: [1, 2], + ...videoParams + } + }, + ...rootParams + }); + + const mockBidderRequest = (params = {}, bids = [mockBannerBid()]) => ({ bidderCode: 'yieldmo', auctionId: 'e3a336ad-2761-4a1c-b421-ecc7c5294a34', bidderRequestId: '14c4ede8c693f', - bids: bidArray, + bids, auctionStart: 1520001292880, timeout: 3000, start: 1520001292884, @@ -49,236 +75,215 @@ describe('YieldmoAdapter', function () { reachedTop: true, referer: 'yieldmo.com', }, - }; + ...params + }); describe('isBidRequestValid', function () { - it('should return true when necessary information is found', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; + describe('Banner:', function () { + it('should return true when necessary information is found', function () { + expect(spec.isBidRequestValid(mockBannerBid())).to.be.true; + }); + + it('should return false when necessary information is not found', function () { + // empty bid + expect(spec.isBidRequestValid({})).to.be.false; + + // empty bidId + expect(spec.isBidRequestValid(mockBannerBid({bidId: ''}))).to.be.false; + + // empty adUnitCode + expect(spec.isBidRequestValid(mockBannerBid({adUnitCode: ''}))).to.be.false; + + let invalidBid = mockBannerBid(); + delete invalidBid.mediaTypes.banner; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); }); - it('should return false when necessary information is not found', function () { - // empty bid - expect(spec.isBidRequestValid({})).to.be.false; + describe('Instream video:', function () { + const getVideoBidWithoutParam = (key, paramToRemove) => { + let bid = mockVideoBid(); + delete utils.deepAccess(bid, key)[paramToRemove]; + return bid; + } + + it('should return true when necessary information is found', function () { + expect(spec.isBidRequestValid(mockVideoBid())).to.be.true; + }); + + it('should return false when necessary information is not found', function () { + // empty bidId + expect(spec.isBidRequestValid(mockVideoBid({bidId: ''}))).to.be.false; - // empty bidId - bid.bidId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; + // empty adUnitCode + expect(spec.isBidRequestValid(mockVideoBid({adUnitCode: ''}))).to.be.false; + }); + + it('should return false when required mediaTypes.video.* param is not found', function () { + const getBidAndExclude = paramToRemove => getVideoBidWithoutParam('mediaTypes.video', paramToRemove); + + expect(spec.isBidRequestValid(getBidAndExclude('playerSize'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('mimes'))).to.be.false; + }); + + it('should return false when required bid.params.* is not found', function () { + const getBidAndExclude = paramToRemove => getVideoBidWithoutParam('params', paramToRemove); + + expect(spec.isBidRequestValid(getBidAndExclude('placementId'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('video'))).to.be.false; + }); - // empty adUnitCode - bid.bidId = '30b31c1838de1e'; - bid.adUnitCode = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; + it('should return false when required bid.params.video.* is not found', function () { + const getBidAndExclude = paramToRemove => getVideoBidWithoutParam('params.video', paramToRemove); - bid.adUnitCode = 'adunit-code'; + expect(spec.isBidRequestValid(getBidAndExclude('placement'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('maxduration'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('startdelay'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('protocols'))).to.be.false; + expect(spec.isBidRequestValid(getBidAndExclude('api'))).to.be.false; + }); }); }); describe('buildRequests', function () { - it('should attempt to send bid requests to the endpoint via GET', function () { - const request = spec.buildRequests(bidArray, bidderRequest); - expect(request.method).to.equal('GET'); - expect(request.url).to.be.equal(ENDPOINT); - }); + const build = (bidRequests, bidderReq = mockBidderRequest()) => spec.buildRequests(bidRequests, bidderReq); + const buildAndGetPlacementInfo = (bidRequests, index = 0, bidderReq = mockBidderRequest()) => + utils.deepAccess(build(bidRequests, bidderReq), `${index}.data.p`); + const buildAndGetData = (bidRequests, index = 0, bidderReq = mockBidderRequest()) => + utils.deepAccess(build(bidRequests, bidderReq), `${index}.data`) || {}; - it('should not blow up if crumbs is undefined', function () { - let bidArray = [{ ...bid, crumbs: undefined }]; - expect(function () { - spec.buildRequests(bidArray, bidderRequest); - }).not.to.throw(); - }); + describe('Banner:', function () { + it('should attempt to send banner bid requests to the endpoint via GET', function () { + const requests = build([mockBannerBid()]); + expect(requests.length).to.equal(1); + expect(requests[0].method).to.equal('GET'); + expect(requests[0].url).to.be.equal(BANNER_ENDPOINT); + }); - it('should place bid information into the p parameter of data', function () { - let placementInfo = spec.buildRequests(bidArray, bidderRequest).data.p; - expect(placementInfo).to.equal( - '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1}]' - ); - bidArray.push({ - bidder: 'yieldmo', - params: { - bidFloor: 0.2, - }, - adUnitCode: 'adunit-code-1', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bidId: '123456789', - bidderRequestId: '987654321', - auctionId: '0246810', - crumbs: { - pubcid: 'c604130c-0144-4b63-9bf2-c2bd8c8d86da', - }, + it('should not blow up if crumbs is undefined', function () { + expect(function () { + build([mockBannerBid({crumbs: undefined})]); + }).not.to.throw(); }); - // multiple placements - placementInfo = spec.buildRequests(bidArray, bidderRequest).data.p; - expect(placementInfo).to.equal( - '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1},{"placement_id":"adunit-code-1","callback_id":"123456789","sizes":[[300,250],[300,600]],"bidFloor":0.2}]' - ); - }); + it('should place bid information into the p parameter of data', function () { + let bidArray = [mockBannerBid()]; + expect(buildAndGetPlacementInfo(bidArray)).to.equal( + '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1}]' + ); - it('should add placement id if given', function () { - bidArray[0].params.placementId = 'ym_1293871298'; - let placementInfo = spec.buildRequests(bidArray, bidderRequest).data.p; - expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); - expect(placementInfo).not.to.include('"ym_placement_id":"ym_0987654321"'); + // multiple placements + bidArray.push(mockBannerBid( + {adUnitCode: 'adunit-2', bidId: '123a', bidderRequestId: '321', auctionId: '222'}, {bidFloor: 0.2})); + expect(buildAndGetPlacementInfo(bidArray)).to.equal( + '[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1},' + + '{"placement_id":"adunit-2","callback_id":"123a","sizes":[[300,250],[300,600]],"bidFloor":0.2}]' + ); + }); - bidArray[1].params.placementId = 'ym_0987654321'; - placementInfo = spec.buildRequests(bidArray, bidderRequest).data.p; - expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); - expect(placementInfo).to.include('"ym_placement_id":"ym_0987654321"'); - }); + it('should add placement id if given', function () { + let bidArray = [mockBannerBid({}, {placementId: 'ym_1293871298'})]; + let placementInfo = buildAndGetPlacementInfo(bidArray); + expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); + expect(placementInfo).not.to.include('"ym_placement_id":"ym_0987654321"'); - it('should add additional information to data parameter of request', function () { - const data = spec.buildRequests(bidArray, bidderRequest).data; - expect(data.hasOwnProperty('page_url')).to.be.true; - expect(data.hasOwnProperty('bust')).to.be.true; - expect(data.hasOwnProperty('pr')).to.be.true; - expect(data.hasOwnProperty('scrd')).to.be.true; - expect(data.dnt).to.be.false; - expect(data.hasOwnProperty('description')).to.be.true; - expect(data.hasOwnProperty('title')).to.be.true; - expect(data.hasOwnProperty('h')).to.be.true; - expect(data.hasOwnProperty('w')).to.be.true; - expect(data.hasOwnProperty('pubcid')).to.be.true; - expect(data.userConsent).to.equal('{"gdprApplies":"","cmp":""}'); - expect(data.us_privacy).to.equal(''); - }); + bidArray.push(mockBannerBid({}, {placementId: 'ym_0987654321'})); + placementInfo = buildAndGetPlacementInfo(bidArray); + expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); + expect(placementInfo).to.include('"ym_placement_id":"ym_0987654321"'); + }); - it('should add pubcid as parameter of request', function () { - const pubcidBid = { - bidder: 'yieldmo', - params: {}, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - userId: { - pubcid: 'c604130c-0144-4b63-9bf2-c2bd8c8d86da2', - }, - }; - const data = spec.buildRequests([pubcidBid], bidderRequest).data; - expect(data.pubcid).to.deep.equal( - 'c604130c-0144-4b63-9bf2-c2bd8c8d86da2' - ); - }); + it('should add additional information to data parameter of request', function () { + const data = buildAndGetData([mockBannerBid()]); + expect(data.hasOwnProperty('page_url')).to.be.true; + expect(data.hasOwnProperty('bust')).to.be.true; + expect(data.hasOwnProperty('pr')).to.be.true; + expect(data.hasOwnProperty('scrd')).to.be.true; + expect(data.dnt).to.be.false; + expect(data.hasOwnProperty('description')).to.be.true; + expect(data.hasOwnProperty('title')).to.be.true; + expect(data.hasOwnProperty('h')).to.be.true; + expect(data.hasOwnProperty('w')).to.be.true; + expect(data.hasOwnProperty('pubcid')).to.be.true; + expect(data.userConsent).to.equal('{"gdprApplies":"","cmp":""}'); + expect(data.us_privacy).to.equal(''); + }); - it('should add unified id as parameter of request', function () { - const unifiedIdBid = { - bidder: 'yieldmo', - params: {}, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - userId: { - tdid, - }, - }; - const data = spec.buildRequests([unifiedIdBid], bidderRequest).data; - expect(data.tdid).to.deep.equal(tdid); - }); + it('should add pubcid as parameter of request', function () { + const pubcid = 'c604130c-0144-4b63-9bf2-c2bd8c8d86da2'; + const pubcidBid = mockBannerBid({crumbs: undefined, userId: {pubcid}}); + expect(buildAndGetData([pubcidBid]).pubcid).to.deep.equal(pubcid); + }); - it('should add CRITEO RTUS id as parameter of request', function () { - const criteoIdBid = { - bidder: 'yieldmo', - params: {}, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - userId: { - criteoId, - }, - }; - const data = spec.buildRequests([criteoIdBid], bidderRequest).data; - expect(data.cri_prebid).to.deep.equal(criteoId); - }); + it('should add unified id as parameter of request', function () { + const unifiedIdBid = mockBannerBid({crumbs: undefined}); + expect(buildAndGetData([unifiedIdBid]).tdid).to.deep.equal(mockBannerBid().userId.tdid); + }); + + it('should add CRITEO RTUS id as parameter of request', function () { + const criteoId = 'aff4'; + const criteoIdBid = mockBannerBid({crumbs: undefined, userId: { criteoId }}); + expect(buildAndGetData([criteoIdBid]).cri_prebid).to.deep.equal(criteoId); + }); - it('should add gdpr information to request if available', () => { - bidderRequest.gdprConsent = { - consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: { blerp: 1 }, - gdprApplies: true, - }; - const data = spec.buildRequests(bidArray, bidderRequest).data; - expect(data.userConsent).equal( - JSON.stringify({ + it('should add gdpr information to request if available', () => { + const gdprConsent = { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + vendorData: {blerp: 1}, gdprApplies: true, - cmp: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - }) - ); - }); + }; + const data = buildAndGetData([mockBannerBid()], 0, mockBidderRequest({gdprConsent})); + expect(data.userConsent).equal( + JSON.stringify({ + gdprApplies: true, + cmp: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + }) + ); + }); + + it('should add ccpa information to request if available', () => { + const uspConsent = '1YNY'; + const data = buildAndGetData([mockBannerBid()], 0, mockBidderRequest({uspConsent})); + expect(data.us_privacy).equal(uspConsent); + }); - it('should add ccpa information to request if available', () => { - const privacy = '1YNY'; - bidderRequest.uspConsent = privacy; - const data = spec.buildRequests(bidArray, bidderRequest).data; - expect(data.us_privacy).equal(privacy); + it('should add schain if it is in the bidRequest', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [{asi: 'indirectseller.com', sid: '00001', hp: 1}], + }; + const data = buildAndGetData([mockBannerBid({schain})]); + expect(data.schain).equal(JSON.stringify(schain)); + }); }); - it('should add schain if it is in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], - }; - bidArray[0].schain = schain; - const request = spec.buildRequests([bidArray[0]], bidderRequest); - expect(request.data.schain).equal(JSON.stringify(schain)); + describe('Instream video:', function () { + it('should attempt to send banner bid requests to the endpoint via POST', function () { + const requests = build([mockVideoBid()]); + expect(requests.length).to.equal(1); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); + }); }); }); describe('interpretResponse', function () { - let serverResponse; - - beforeEach(function () { - serverResponse = { - body: [ - { - callback_id: '21989fdbef550a', - cpm: 3.45455, - width: 300, - height: 250, - ad: - '
', - creative_id: '9874652394875', - }, - ], - header: 'header?', - }; + const mockServerResponse = () => ({ + body: [{ + callback_id: '21989fdbef550a', + cpm: 3.45455, + width: 300, + height: 250, + ad: '' + + '
', + creative_id: '9874652394875', + }], + header: 'header?', }); it('should correctly reorder the server response', function () { - const newResponse = spec.interpretResponse(serverResponse); + const newResponse = spec.interpretResponse(mockServerResponse()); expect(newResponse.length).to.be.equal(1); expect(newResponse[0]).to.deep.equal({ requestId: '21989fdbef550a', @@ -289,19 +294,18 @@ describe('YieldmoAdapter', function () { currency: 'USD', netRevenue: true, ttl: 300, - ad: - '
', + ad: '' + + '
', }); }); it('should not add responses if the cpm is 0 or null', function () { - serverResponse.body[0].cpm = 0; - let response = spec.interpretResponse(serverResponse); - expect(response).to.deep.equal([]); + let response = mockServerResponse(); + response.body[0].cpm = 0; + expect(spec.interpretResponse(response)).to.deep.equal([]); - serverResponse.body[0].cpm = null; - response = spec.interpretResponse(serverResponse); - expect(response).to.deep.equal([]); + response.body[0].cpm = null; + expect(spec.interpretResponse(response)).to.deep.equal([]); }); }); From d5a08ce227b94f79d98c25a0adf0c10e4436fc17 Mon Sep 17 00:00:00 2001 From: pnh-pubx <73683023+pnh-pubx@users.noreply.github.com> Date: Thu, 19 Nov 2020 14:03:00 +0530 Subject: [PATCH 0388/1476] PubxAi analytics adapter (#5915) * Added PubxAi analytics adapter * Updated Pubxai Analytics Adapter documentation * Updated test cases * Fixed issues with test cases * Updated deviceType and platform in the specs to fix browserstack errors * Updated documentation with description of each property * Updated hostname in the documentation Co-authored-by: Phaneendra Hegde --- modules/pubxaiAnalyticsAdapter.js | 168 ++++ modules/pubxaiAnalyticsAdapter.md | 27 + .../modules/pubxaiAnalyticsAdapter_spec.js | 734 ++++++++++++++++++ 3 files changed, 929 insertions(+) create mode 100644 modules/pubxaiAnalyticsAdapter.js create mode 100644 modules/pubxaiAnalyticsAdapter.md create mode 100644 test/spec/modules/pubxaiAnalyticsAdapter_spec.js diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js new file mode 100644 index 00000000000..7e2f5061621 --- /dev/null +++ b/modules/pubxaiAnalyticsAdapter.js @@ -0,0 +1,168 @@ +import { ajax } from '../src/ajax.js'; +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import CONSTANTS from '../src/constants.json'; +import * as utils from '../src/utils.js'; + +const emptyUrl = ''; +const analyticsType = 'endpoint'; +const pubxaiAnalyticsVersion = 'v1.0.0'; +const defaultHost = 'api.pbxai.com'; +const auctionPath = '/analytics/auction'; +const winningBidPath = '/analytics/bidwon'; + +let initOptions; +let auctionTimestamp; +let events = { + bids: [] +}; + +var pubxaiAnalyticsAdapter = Object.assign(adapter( + { + emptyUrl, + analyticsType + }), { + track({ eventType, args }) { + if (typeof args !== 'undefined') { + if (eventType === CONSTANTS.EVENTS.BID_TIMEOUT) { + args.forEach(item => { mapBidResponse(item, 'timeout'); }); + } else if (eventType === CONSTANTS.EVENTS.AUCTION_INIT) { + events.auctionInit = args; + auctionTimestamp = args.timestamp; + } else if (eventType === CONSTANTS.EVENTS.BID_REQUESTED) { + mapBidRequests(args).forEach(item => { events.bids.push(item) }); + } else if (eventType === CONSTANTS.EVENTS.BID_RESPONSE) { + mapBidResponse(args, 'response'); + } else if (eventType === CONSTANTS.EVENTS.BID_WON) { + send({ + winningBid: mapBidResponse(args, 'bidwon') + }, 'bidwon'); + } + } + + if (eventType === CONSTANTS.EVENTS.AUCTION_END) { + send(events, 'auctionEnd'); + } + } +}); + +function mapBidRequests(params) { + let arr = []; + if (typeof params.bids !== 'undefined' && params.bids.length) { + params.bids.forEach(function (bid) { + arr.push({ + bidderCode: bid.bidder, + bidId: bid.bidId, + adUnitCode: bid.adUnitCode, + requestId: bid.bidderRequestId, + auctionId: bid.auctionId, + transactionId: bid.transactionId, + sizes: utils.parseSizesInput(bid.mediaTypes.banner.sizes).toString(), + renderStatus: 1, + requestTimestamp: params.auctionStart + }); + }); + } + return arr; +} + +function mapBidResponse(bidResponse, status) { + if (status !== 'bidwon') { + let bid = events.bids.filter(o => o.bidId === bidResponse.bidId || o.bidId === bidResponse.requestId)[0]; + Object.assign(bid, { + bidderCode: bidResponse.bidder, + bidId: status === 'timeout' ? bidResponse.bidId : bidResponse.requestId, + adUnitCode: bidResponse.adUnitCode, + auctionId: bidResponse.auctionId, + creativeId: bidResponse.creativeId, + transactionId: bidResponse.transactionId, + currency: bidResponse.currency, + cpm: bidResponse.cpm, + netRevenue: bidResponse.netRevenue, + mediaType: bidResponse.mediaType, + statusMessage: bidResponse.statusMessage, + floorData: bidResponse.floorData, + status: bidResponse.status, + renderStatus: status === 'timeout' ? 3 : 2, + timeToRespond: bidResponse.timeToRespond, + requestTimestamp: bidResponse.requestTimestamp, + responseTimestamp: bidResponse.responseTimestamp, + platform: navigator.platform, + deviceType: getDeviceType() + }); + } else { + return { + bidderCode: bidResponse.bidder, + bidId: bidResponse.requestId, + adUnitCode: bidResponse.adUnitCode, + auctionId: bidResponse.auctionId, + creativeId: bidResponse.creativeId, + transactionId: bidResponse.transactionId, + currency: bidResponse.currency, + cpm: bidResponse.cpm, + netRevenue: bidResponse.netRevenue, + floorData: bidResponse.floorData, + renderedSize: bidResponse.size, + mediaType: bidResponse.mediaType, + statusMessage: bidResponse.statusMessage, + status: bidResponse.status, + renderStatus: 4, + timeToRespond: bidResponse.timeToRespond, + requestTimestamp: bidResponse.requestTimestamp, + responseTimestamp: bidResponse.responseTimestamp, + platform: navigator.platform, + deviceType: getDeviceType() + } + } +} + +export function getDeviceType() { + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { + return 'tablet'; + } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { + return 'mobile'; + } + return 'desktop'; +} + +// 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)) { + let location = utils.getWindowLocation(); + if (typeof data !== 'undefined' && typeof data.auctionInit !== 'undefined') { + Object.assign(data.auctionInit, { host: location.host, path: location.pathname, search: location.search }); + } + data.initOptions = initOptions; + + let pubxaiAnalyticsRequestUrl = utils.buildUrl({ + protocol: 'https', + hostname: (initOptions && initOptions.hostName) || defaultHost, + pathname: status == 'bidwon' ? winningBidPath : auctionPath, + search: { + auctionTimestamp: auctionTimestamp, + pubxaiAnalyticsVersion: pubxaiAnalyticsVersion, + prebidVersion: $$PREBID_GLOBAL$$.version + } + }); + + ajax(pubxaiAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/plain' }); + } +} + +pubxaiAnalyticsAdapter.originEnableAnalytics = pubxaiAnalyticsAdapter.enableAnalytics; +pubxaiAnalyticsAdapter.enableAnalytics = function (config) { + initOptions = config.options; + pubxaiAnalyticsAdapter.originEnableAnalytics(config); +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: pubxaiAnalyticsAdapter, + code: 'pubxai' +}); + +export default pubxaiAnalyticsAdapter; diff --git a/modules/pubxaiAnalyticsAdapter.md b/modules/pubxaiAnalyticsAdapter.md new file mode 100644 index 00000000000..112329fc171 --- /dev/null +++ b/modules/pubxaiAnalyticsAdapter.md @@ -0,0 +1,27 @@ +# Overview +Module Name: PubX.io Analytics Adapter +Module Type: Analytics Adapter +Maintainer: phaneendra@pubx.ai + +# Description + +Analytics adapter for prebid provided by Pubx.ai. Contact alex@pubx.ai for information. + +# Test Parameters + +``` +{ + provider: 'pubxai', + options : { + pubxId: 'xxx', + hostName: 'example.com', + samplingRate: 1 + } +} +``` +Property | Data Type | Is required? | Description |Example +:-----:|:-----:|:-----:|:-----:|:-----: +pubxId|string|Yes | A unique identifier provided by PubX.ai to indetify publishers. |`"a9d48e2f-24ec-4ec1-b3e2-04e32c3aeb03"` +hostName|string|No|hostName is provided by Pubx.ai. |`"https://example.com"` +samplingRate |number |No|How often the sampling must be taken. |`2` - (sample one in two cases) \ `3` - (sample one in three cases) + | | | | \ No newline at end of file diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..91c81dcae8d --- /dev/null +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -0,0 +1,734 @@ +import pubxaiAnalyticsAdapter from 'modules/pubxaiAnalyticsAdapter.js'; +import { getDeviceType } from 'modules/pubxaiAnalyticsAdapter.js'; +import { + expect +} from 'chai'; +import adapterManager from 'src/adapterManager.js'; +import * as utils from 'src/utils.js'; +import { + server +} from 'test/mocks/xhr.js'; + +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('pubxai analytics adapter', function() { + beforeEach(function() { + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function() { + events.getEvents.restore(); + }); + + describe('track', function() { + let initOptions = { + samplingRate: '1', + pubxId: '6c415fc0-8b0e-4cf5-be73-01526a4db625' + }; + + let prebidEvent = { + 'auctionInit': { + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'timestamp': 1603865707180, + 'auctionStatus': 'inProgress', + 'adUnits': [{ + 'code': '/19968336/header-bid-tag-1', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + } + }], + 'sizes': [ + [ + 300, + 250 + ] + ], + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294' + }], + 'adUnitCodes': [ + '/19968336/header-bid-tag-1' + ], + 'bidderRequests': [{ + 'bidderCode': 'appnexus', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'bidderRequestId': '184cbc05bb90ba', + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '248f9a4489835e', + 'bidderRequestId': '184cbc05bb90ba', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }], + 'auctionStart': 1603865707180, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://local-pnh.net:8080/stream/', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://local-pnh.net:8080/stream/' + ], + 'canonicalUrl': null + }, + 'start': 1603865707182 + }], + 'noBids': [], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 1000, + 'config': { + 'samplingRate': '1', + 'pubxId': '6c415fc0-8b0e-4cf5-be73-01526a4db625' + } + }, + 'bidRequested': { + 'bidderCode': 'appnexus', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'bidderRequestId': '184cbc05bb90ba', + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '248f9a4489835e', + 'bidderRequestId': '184cbc05bb90ba', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }], + 'auctionStart': 1603865707180, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://local-pnh.net:8080/stream/', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://local-pnh.net:8080/stream/' + ], + 'canonicalUrl': null + }, + 'start': 1603865707182 + }, + 'bidTimeout': [], + 'bidResponse': { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '32780c4bc382cb', + 'requestId': '248f9a4489835e', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'creativeId': 96846035, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'meta': { + 'advertiserId': 2529885 + }, + 'ad': '', + 'originalCpm': 0.5, + 'originalCurrency': 'USD', + 'floorData': { + 'floorValue': 0.4, + 'floorRule': '/19968336/header-bid-tag-1|banner', + 'floorCurrency': 'USD', + 'cpmAfterAdjustments': 0.5, + 'enforcements': { + 'enforceJS': true, + 'enforcePBS': false, + 'floorDeals': true, + 'bidAdjustment': true + }, + 'matchedFields': { + 'gptSlot': '/19968336/header-bid-tag-1', + 'mediaType': 'banner' + } + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'responseTimestamp': 1603865707449, + 'requestTimestamp': 1603865707182, + 'bidder': 'appnexus', + 'timeToRespond': 267, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.50', + 'pbAg': '0.50', + 'pbDg': '0.50', + 'pbCg': '0.50', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '32780c4bc382cb', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + }, + 'auctionEnd': { + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'timestamp': 1603865707180, + 'auctionEnd': 1603865707180, + 'auctionStatus': 'completed', + 'adUnits': [{ + 'code': '/19968336/header-bid-tag-1', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + } + }], + 'sizes': [ + [ + 300, + 250 + ] + ], + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294' + }], + 'adUnitCodes': [ + '/19968336/header-bid-tag-1' + ], + 'bidderRequests': [{ + 'bidderCode': 'appnexus', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'bidderRequestId': '184cbc05bb90ba', + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '248f9a4489835e', + 'bidderRequestId': '184cbc05bb90ba', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }], + 'auctionStart': 1603865707180, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://local-pnh.net:8080/stream/', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://local-pnh.net:8080/stream/' + ], + 'canonicalUrl': null + }, + 'start': 1603865707182 + }], + 'noBids': [], + 'bidsReceived': [{ + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '32780c4bc382cb', + 'requestId': '248f9a4489835e', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'creativeId': 96846035, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'meta': { + 'advertiserId': 2529885 + }, + 'ad': '', + 'originalCpm': 0.5, + 'originalCurrency': 'USD', + 'floorData': { + 'floorValue': 0.4, + 'floorRule': '/19968336/header-bid-tag-1|banner', + 'floorCurrency': 'USD', + 'cpmAfterAdjustments': 0.5, + 'enforcements': { + 'enforceJS': true, + 'enforcePBS': false, + 'floorDeals': true, + 'bidAdjustment': true + }, + 'matchedFields': { + 'gptSlot': '/19968336/header-bid-tag-1', + 'mediaType': 'banner' + } + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'responseTimestamp': 1603865707449, + 'requestTimestamp': 1603865707182, + 'bidder': 'appnexus', + 'timeToRespond': 267, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.50', + 'pbAg': '0.50', + 'pbDg': '0.50', + 'pbCg': '0.50', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '32780c4bc382cb', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + 'status': 'rendered', + 'params': [{ + 'placementId': 13144370 + }] + }], + 'winningBids': [], + 'timeout': 1000 + }, + 'bidWon': { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '32780c4bc382cb', + 'requestId': '248f9a4489835e', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'creativeId': 96846035, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'appnexus': { + 'buyerMemberId': 9325 + }, + 'meta': { + 'advertiserId': 2529885 + }, + 'ad': '', + 'originalCpm': 0.5, + 'originalCurrency': 'USD', + 'floorData': { + 'floorValue': 0.4, + 'floorRule': '/19968336/header-bid-tag-1|banner', + 'floorCurrency': 'USD', + 'cpmAfterAdjustments': 0.5, + 'enforcements': { + 'enforceJS': true, + 'enforcePBS': false, + 'floorDeals': true, + 'bidAdjustment': true + }, + 'matchedFields': { + 'gptSlot': '/19968336/header-bid-tag-1', + 'mediaType': 'banner' + } + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'responseTimestamp': 1603865707449, + 'requestTimestamp': 1603865707182, + 'bidder': 'appnexus', + 'timeToRespond': 267, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.50', + 'pbAg': '0.50', + 'pbDg': '0.50', + 'pbCg': '0.50', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '32780c4bc382cb', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + 'status': 'rendered', + 'params': [{ + 'placementId': 13144370 + }] + } + }; + let location = utils.getWindowLocation(); + + let expectedAfterBid = { + 'bids': [{ + 'bidderCode': 'appnexus', + 'bidId': '248f9a4489835e', + 'adUnitCode': '/19968336/header-bid-tag-1', + 'requestId': '184cbc05bb90ba', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'sizes': '300x250', + 'renderStatus': 2, + 'requestTimestamp': 1603865707182, + 'creativeId': 96846035, + 'currency': 'USD', + 'cpm': 0.5, + 'netRevenue': true, + 'mediaType': 'banner', + 'statusMessage': 'Bid available', + 'floorData': { + 'floorValue': 0.4, + 'floorRule': '/19968336/header-bid-tag-1|banner', + 'floorCurrency': 'USD', + 'cpmAfterAdjustments': 0.5, + 'enforcements': { + 'enforceJS': true, + 'enforcePBS': false, + 'floorDeals': true, + 'bidAdjustment': true + }, + 'matchedFields': { + 'gptSlot': '/19968336/header-bid-tag-1', + 'mediaType': 'banner' + } + }, + 'timeToRespond': 267, + 'responseTimestamp': 1603865707449, + 'platform': navigator.platform, + 'deviceType': getDeviceType() + }], + 'auctionInit': { + 'host': location.host, + 'path': location.pathname, + 'search': location.search, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'timestamp': 1603865707180, + 'auctionStatus': 'inProgress', + 'adUnits': [{ + 'code': '/19968336/header-bid-tag-1', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + } + }], + 'sizes': [ + [ + 300, + 250 + ] + ], + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294' + }], + 'adUnitCodes': [ + '/19968336/header-bid-tag-1' + ], + 'bidderRequests': [{ + 'bidderCode': 'appnexus', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'bidderRequestId': '184cbc05bb90ba', + 'bids': [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': 13144370 + }, + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'floorData': { + 'skipped': false, + 'skipRate': 0, + 'modelVersion': 'new model 1.0', + 'location': 'fetch', + 'floorProvider': 'PubXFloor', + 'fetchStatus': 'success' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': '/19968336/header-bid-tag-1', + 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '248f9a4489835e', + 'bidderRequestId': '184cbc05bb90ba', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }], + 'auctionStart': 1603865707180, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://local-pnh.net:8080/stream/', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://local-pnh.net:8080/stream/' + ], + 'canonicalUrl': null + }, + 'start': 1603865707182 + }], + 'noBids': [], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 1000, + 'config': { + 'samplingRate': '1', + 'pubxId': '6c415fc0-8b0e-4cf5-be73-01526a4db625' + } + }, + 'initOptions': initOptions + }; + + let expectedAfterBidWon = { + 'winningBid': { + 'bidderCode': 'appnexus', + 'bidId': '248f9a4489835e', + 'adUnitCode': '/19968336/header-bid-tag-1', + 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', + 'renderedSize': '300x250', + 'renderStatus': 4, + 'requestTimestamp': 1603865707182, + 'creativeId': 96846035, + 'currency': 'USD', + 'cpm': 0.5, + 'netRevenue': true, + 'mediaType': 'banner', + 'status': 'rendered', + 'statusMessage': 'Bid available', + 'floorData': { + 'floorValue': 0.4, + 'floorRule': '/19968336/header-bid-tag-1|banner', + 'floorCurrency': 'USD', + 'cpmAfterAdjustments': 0.5, + 'enforcements': { + 'enforceJS': true, + 'enforcePBS': false, + 'floorDeals': true, + 'bidAdjustment': true + }, + 'matchedFields': { + 'gptSlot': '/19968336/header-bid-tag-1', + 'mediaType': 'banner' + } + }, + 'timeToRespond': 267, + 'responseTimestamp': 1603865707449, + 'platform': navigator.platform, + 'deviceType': getDeviceType() + }, + 'initOptions': initOptions + } + + adapterManager.registerAnalyticsAdapter({ + code: 'pubxai', + adapter: pubxaiAnalyticsAdapter + }); + + beforeEach(function() { + adapterManager.enableAnalytics({ + provider: 'pubxai', + options: initOptions + }); + }); + + afterEach(function() { + pubxaiAnalyticsAdapter.disableAnalytics(); + }); + + it('builds and sends auction data', function() { + // Step 1: Send auction init event + events.emit(constants.EVENTS.AUCTION_INIT, prebidEvent['auctionInit']); + + // Step 2: Send bid requested event + events.emit(constants.EVENTS.BID_REQUESTED, prebidEvent['bidRequested']); + + // Step 3: Send bid response event + events.emit(constants.EVENTS.BID_RESPONSE, prebidEvent['bidResponse']); + + // Step 4: Send bid time out event + events.emit(constants.EVENTS.BID_TIMEOUT, prebidEvent['bidTimeout']); + + // Step 5: Send auction end event + events.emit(constants.EVENTS.AUCTION_END, prebidEvent['auctionEnd']); + + expect(server.requests.length).to.equal(1); + + let realAfterBid = JSON.parse(server.requests[0].requestBody); + + expect(realAfterBid).to.deep.equal(expectedAfterBid); + + // Step 6: Send auction bid won event + events.emit(constants.EVENTS.BID_WON, prebidEvent['bidWon']); + + expect(server.requests.length).to.equal(2); + + let winEventData = JSON.parse(server.requests[1].requestBody); + + expect(winEventData).to.deep.equal(expectedAfterBidWon); + }); + }); +}); From 6fea8444b35b1c903c662603947f884059f92bc0 Mon Sep 17 00:00:00 2001 From: BizzClick <73241175+BizzClick@users.noreply.github.com> Date: Thu, 19 Nov 2020 10:37:09 +0200 Subject: [PATCH 0389/1476] init bizzclick prebid.js adapter (#5914) * init bizzclick prebid.js adapter * increase test coverage * init bizzclick prebid.js adapter * increase test coverage * fix linting --- modules/bizzclickBidAdapter.js | 307 +++++++++++++++ modules/bizzclickBidAdapter.md | 83 +++- test/spec/modules/bizzclickBidAdapter_spec.js | 369 ++++++++++++++++++ 3 files changed, 756 insertions(+), 3 deletions(-) create mode 100644 modules/bizzclickBidAdapter.js create mode 100644 test/spec/modules/bizzclickBidAdapter_spec.js diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js new file mode 100644 index 00000000000..80d2f6b5395 --- /dev/null +++ b/modules/bizzclickBidAdapter.js @@ -0,0 +1,307 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; + +const BIDDER_CODE = 'bizzclick'; +const ACCOUNTID_MACROS = '[account_id]'; +const URL_ENDPOINT = `https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=${ACCOUNTID_MACROS}`; +const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + body: { + id: 4, + name: 'data', + type: 2 + }, + cta: { + id: 1, + type: 12, + name: 'data' + } +}; +const NATIVE_VERSION = '1.2'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + /** + * 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: (bid) => { + return Boolean(bid.params.accountId) && Boolean(bid.params.placementId) + }, + + /** + * 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: (validBidRequests, bidderRequest) => { + if (validBidRequests && validBidRequests.length === 0) return [] + let accuontId = validBidRequests[0].params.accountId; + const endpointURL = URL_ENDPOINT.replace(ACCOUNTID_MACROS, accuontId); + + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + + let bids = []; + for (let bidRequest of validBidRequests) { + let impObject = prepareImpObject(bidRequest); + let data = { + id: bidRequest.bidId, + test: config.getConfig('debug') ? 1 : 0, + cur: ['USD'], + device: { + w: winTop.screen.width, + h: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '', + }, + site: { + page: location.pathname, + host: location.host + }, + source: { + tid: bidRequest.transactionId + }, + tmax: bidRequest.timeout, + imp: [impObject], + }; + bids.push(data) + } + return { + method: 'POST', + url: endpointURL, + data: bids + }; + }, + + /** + * 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: (serverResponse) => { + if (!serverResponse || !serverResponse.body) return [] + let bizzclickResponse = serverResponse.body; + + let bids = []; + for (let response of bizzclickResponse) { + let mediaType = response.seatbid[0].bid[0].ext && response.seatbid[0].bid[0].ext.mediaType ? response.seatbid[0].bid[0].ext.mediaType : BANNER; + + let bid = { + requestId: response.id, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ttl: response.ttl || 1200, + currency: response.cur || 'USD', + netRevenue: true, + creativeId: response.seatbid[0].bid[0].crid, + dealId: response.seatbid[0].bid[0].dealid, + mediaType: mediaType + }; + + switch (mediaType) { + case VIDEO: + bid.vastXml = response.seatbid[0].bid[0].adm + bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl + break + case NATIVE: + bid.native = parseNative(response.seatbid[0].bid[0].adm) + break + default: + bid.ad = response.seatbid[0].bid[0].adm + } + + bids.push(bid); + } + + return bids; + }, +}; + +/** + * Determine type of request + * + * @param bidRequest + * @param type + * @returns {boolean} + */ +const checkRequestType = (bidRequest, type) => { + return (typeof utils.deepAccess(bidRequest, `mediaTypes.${type}`) !== 'undefined'); +} + +const parseNative = admObject => { + const { assets, link, imptrackers, jstracker } = admObject.native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined, + impressionTrackers: imptrackers || undefined, + javascriptTrackers: jstracker ? [ jstracker ] : undefined + }; + assets.forEach(asset => { + const kind = NATIVE_ASSET_IDS[asset.id]; + const content = kind && asset[NATIVE_PARAMS[kind].name]; + if (content) { + result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; + } + }); + + return result; +} + +const prepareImpObject = (bidRequest) => { + let impObject = { + id: bidRequest.transactionId, + secure: 1, + ext: { + placementId: bidRequest.params.placementId + } + }; + if (checkRequestType(bidRequest, BANNER)) { + impObject.banner = addBannerParameters(bidRequest); + } + if (checkRequestType(bidRequest, VIDEO)) { + impObject.video = addVideoParameters(bidRequest); + } + if (checkRequestType(bidRequest, NATIVE)) { + impObject.native = { + ver: NATIVE_VERSION, + request: addNativeParameters(bidRequest) + }; + } + return impObject +}; + +const addNativeParameters = bidRequest => { + let impObject = { + id: bidRequest.transactionId, + ver: NATIVE_VERSION, + }; + + const assets = utils._map(bidRequest.mediaTypes.native, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + wmin = sizes[0]; + hmin = sizes[1]; + } + + 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; + if (hmin) asset[props.name]['hmin'] = hmin; + + return asset; + } + }).filter(Boolean); + + impObject.assets = assets; + return impObject +} + +const addBannerParameters = (bidRequest) => { + let bannerObject = {}; + const size = parseSizes(bidRequest, 'banner'); + bannerObject.w = size[0]; + bannerObject.h = size[1]; + return bannerObject; +}; + +const parseSizes = (bid, mediaType) => { + let mediaTypes = bid.mediaTypes; + if (mediaType === 'video') { + let size = []; + if (mediaTypes.video && mediaTypes.video.w && mediaTypes.video.h) { + size = [ + mediaTypes.video.w, + mediaTypes.video.h + ]; + } else if (Array.isArray(utils.deepAccess(bid, 'mediaTypes.video.playerSize')) && bid.mediaTypes.video.playerSize.length === 1) { + size = bid.mediaTypes.video.playerSize[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) { + size = bid.sizes[0]; + } + return size; + } + let sizes = []; + if (Array.isArray(mediaTypes.banner.sizes)) { + sizes = mediaTypes.banner.sizes[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { + sizes = bid.sizes + } else { + utils.logWarn('no sizes are setup or found'); + } + + return sizes +} + +const addVideoParameters = (bidRequest) => { + let videoObj = {}; + let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'placement', 'skip', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'] + + for (let param of supportParamsList) { + if (bidRequest.mediaTypes.video[param] !== undefined) { + videoObj[param] = bidRequest.mediaTypes.video[param]; + } + } + + const size = parseSizes(bidRequest, 'video'); + videoObj.w = size[0]; + videoObj.h = size[1]; + return videoObj; +} + +const flatten = arr => { + return [].concat(...arr); +} + +registerBidder(spec); diff --git a/modules/bizzclickBidAdapter.md b/modules/bizzclickBidAdapter.md index 7dfa458b34c..6fc1bebf546 100644 --- a/modules/bizzclickBidAdapter.md +++ b/modules/bizzclickBidAdapter.md @@ -14,14 +14,91 @@ Module that connects to BizzClick SSP demand sources ``` var adUnits = [{ code: 'placementId', - sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, bids: [{ bidder: 'bizzclick', params: { - placementId: 0, - type: 'banner' + placementId: 'hash', + accountId: 'accountId' } }] + }, + { + code: 'native_example', + // sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + } + } + + }, + bids: [ { + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + } + }] + }, + { + code: 'video1', + sizes: [640,480], + mediaTypes: { video: { + minduration:0, + maxduration:999, + boxingallowed:1, + skip:0, + mimes:[ + 'application/javascript', + 'video/mp4' + ], + w:1920, + h:1080, + protocols:[ + 2 + ], + linearity:1, + api:[ + 1, + 2 + ] + } }, + bids: [ + { + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' } + } + ] + } ]; ``` \ No newline at end of file diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js new file mode 100644 index 00000000000..39ad4ae39c9 --- /dev/null +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -0,0 +1,369 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bizzclickBidAdapter.js'; + +const NATIVE_BID_REQUEST = { + code: 'native_example', + mediaTypes: { + native: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + } + } + }, + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + }, + timeout: 1000 + +}; + +const BANNER_BID_REQUEST = { + code: 'banner_example', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + }, + timeout: 1000, + +} + +const bidRequest = { + refererInfo: { + referer: 'test.com' + } +} + +const VIDEO_BID_REQUEST = { + code: 'video1', + sizes: [640, 480], + mediaTypes: { video: { + minduration: 0, + maxduration: 999, + boxingallowed: 1, + skip: 0, + mimes: [ + 'application/javascript', + 'video/mp4' + ], + w: 1920, + h: 1080, + protocols: [ + 2 + ], + linearity: 1, + api: [ + 1, + 2 + ] + } + }, + + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + }, + timeout: 1000 + +} + +const BANNER_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: 'admcode', + crid: 'crid', + ext: { + mediaType: 'banner' + } + }], + }], +}; + +const VIDEO_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: 'admcode', + crid: 'crid', + ext: { + mediaType: 'video', + vastUrl: 'http://example.vast', + } + }], + }], +}; + +let imgData = { + url: `https://example.com/image`, + w: 1200, + h: 627 +}; + +const NATIVE_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: { native: + { + assets: [ + {id: 0, title: 'dummyText'}, + {id: 3, image: imgData}, + { + id: 5, + data: {value: 'organization.name'} + } + ], + link: {url: 'example.com'}, + imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], + jstracker: 'tracker1.com' + } + }, + crid: 'crid', + ext: { + mediaType: 'native' + } + }], + }], +}; + +describe('BizzclickAdapter', function() { + describe('isBidRequestValid', function() { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, NATIVE_BID_REQUEST); + delete bid.params; + bid.params = { + 'IncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('build Native Request', function () { + const request = spec.buildRequests([NATIVE_BID_REQUEST], bidRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + }); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); + }); + + it('Returns empty data if no valid requests are passed', function () { + let serverRequest = spec.buildRequests([]); + expect(serverRequest).to.be.an('array').that.is.empty; + }); + }); + + describe('build Banner Request', function () { + const request = spec.buildRequests([BANNER_BID_REQUEST]); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + }); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); + }); + }); + + describe('build Video Request', function () { + const request = spec.buildRequests([VIDEO_BID_REQUEST]); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + }); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); + }); + }); + + describe('interpretResponse', function () { + it('Empty response must return empty array', function() { + const emptyResponse = null; + let response = spec.interpretResponse(emptyResponse); + + expect(response).to.be.an('array').that.is.empty; + }) + + it('Should interpret banner response', function () { + const bannerResponse = { + body: [BANNER_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: BANNER_BID_RESPONSE.id, + cpm: BANNER_BID_RESPONSE.seatbid[0].bid[0].price, + width: BANNER_BID_RESPONSE.seatbid[0].bid[0].w, + height: BANNER_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: BANNER_BID_RESPONSE.ttl || 1200, + currency: BANNER_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: BANNER_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: BANNER_BID_RESPONSE.seatbid[0].bid[0].dealid, + mediaType: 'banner', + ad: BANNER_BID_RESPONSE.seatbid[0].bid[0].adm + } + + let bannerResponses = spec.interpretResponse(bannerResponse); + + 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'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.ad).to.equal(expectedBidResponse.ad); + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); + + it('Should interpret video response', function () { + const videoResponse = { + body: [VIDEO_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: VIDEO_BID_RESPONSE.id, + cpm: VIDEO_BID_RESPONSE.seatbid[0].bid[0].price, + width: VIDEO_BID_RESPONSE.seatbid[0].bid[0].w, + height: VIDEO_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: VIDEO_BID_RESPONSE.ttl || 1200, + currency: VIDEO_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].dealid, + mediaType: 'video', + vastXml: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm, + vastUrl: VIDEO_BID_RESPONSE.seatbid[0].bid[0].ext.vastUrl + } + + let videoResponses = spec.interpretResponse(videoResponse); + + expect(videoResponses).to.be.an('array').that.is.not.empty; + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml) + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); + + it('Should interpret native response', function () { + const nativeResponse = { + body: [NATIVE_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: NATIVE_BID_RESPONSE.id, + cpm: NATIVE_BID_RESPONSE.seatbid[0].bid[0].price, + width: NATIVE_BID_RESPONSE.seatbid[0].bid[0].w, + height: NATIVE_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: NATIVE_BID_RESPONSE.ttl || 1200, + currency: NATIVE_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].dealid, + mediaType: 'native', + native: {clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url} + } + + let nativeResponses = spec.interpretResponse(nativeResponse); + + expect(nativeResponses).to.be.an('array').that.is.not.empty; + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl) + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); + }); +}) From 172df7f455fe2bf0605026ab0e21af1c5edf9be3 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal <7393273+jaiminpanchal27@users.noreply.github.com> Date: Thu, 19 Nov 2020 12:24:38 -0500 Subject: [PATCH 0390/1476] Appnexus: Update maintainer (#5987) * Update maintainer * change maintainer Co-authored-by: Jaimin Panchal Co-authored-by: fawke --- modules/appnexusBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.md b/modules/appnexusBidAdapter.md index 6ec40e83b41..d1f61836297 100644 --- a/modules/appnexusBidAdapter.md +++ b/modules/appnexusBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Appnexus Bid Adapter Module Type: Bidder Adapter -Maintainer: info@prebid.org +Maintainer: prebid-js@xandr.com ``` # Description From 578213ccb52f5db858026b436f5c249e6f9e418e Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Thu, 19 Nov 2020 19:34:23 -0500 Subject: [PATCH 0391/1476] Fix import warning for webpack 5 (#5933) Webpack 5 emits a warning when importing named exports from json files: ``` "WARNING in ../../node_modules/prebid.js/src/secureCreatives.js 15:16-30 Should not import the named export 'EVENTS'.'BID_WON' (imported as 'EVENTS') from default-exporting module (only default export is available soon)" ``` Co-authored-by: Garth Poitras <411908+gpoitch@users.noreply.github.com> Co-authored-by: gpoitch --- src/secureCreatives.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 34de2be275c..cb192dd773e 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -5,14 +5,14 @@ import events from './events.js'; import { fireNativeTrackers, getAssetMessage } from './native.js'; -import { EVENTS } from './constants.json'; +import constants from './constants.json'; import { logWarn, replaceAuctionPrice } from './utils.js'; import { auctionManager } from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import includes from 'core-js-pure/features/array/includes.js'; -const BID_WON = EVENTS.BID_WON; +const BID_WON = constants.EVENTS.BID_WON; export function listenMessagesFromCreative() { window.addEventListener('message', receiveMessage, false); From c98a6334011bb3738885b2240cc8687170ec9bf7 Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Sat, 21 Nov 2020 02:04:35 +0530 Subject: [PATCH 0392/1476] PulsePoint Adapter: Fixing issue with multi-format requests (#5995) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * APPS-3793: Fixing multi-format request issue * Added test --- modules/pulsepointBidAdapter.js | 10 ++-- .../spec/modules/pulsepointBidAdapter_spec.js | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 005eadaa390..f74d79a3dc5 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -121,10 +121,7 @@ function bidResponseAvailable(request, response) { netRevenue: DEFAULT_NET_REVENUE, currency: bidResponse.cur || DEFAULT_CURRENCY }; - if (idToImpMap[id]['native']) { - bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); - bid.mediaType = 'native'; - } else if (idToImpMap[id].video) { + if (idToImpMap[id].video) { // for outstream, a renderer is specified if (idToSlotConfig[id] && utils.deepAccess(idToSlotConfig[id], 'mediaTypes.video.context') === 'outstream') { bid.renderer = outstreamRenderer(utils.deepAccess(idToSlotConfig[id], 'renderer.options'), utils.deepAccess(idToBidMap[id], 'ext.outstream')); @@ -133,10 +130,13 @@ function bidResponseAvailable(request, response) { bid.mediaType = 'video'; bid.width = idToBidMap[id].w; bid.height = idToBidMap[id].h; - } else { + } else if (idToImpMap[id].banner) { bid.ad = idToBidMap[id].adm; bid.width = idToBidMap[id].w || idToImpMap[id].banner.w; bid.height = idToBidMap[id].h || idToImpMap[id].banner.h; + } else if (idToImpMap[id]['native']) { + bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); + bid.mediaType = 'native'; } bids.push(bid); } diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index cf81a26eebb..c3830d5cb46 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -724,4 +724,58 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.width).to.equal(728); expect(bid.height).to.equal(90); }); + it('Verify multi-format response', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].mediaTypes['native'] = { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + }; + bidRequests[1].params.video = { + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request).to.be.not.null; + expect(request.data).to.be.not.null; + const ortbRequest = request.data; + expect(ortbRequest.imp).to.have.lengthOf(2); + // adsize on response + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad', + crid: 'Creative#123', + w: 728, + h: 90 + }, { + impid: ortbRequest.imp[1].id, + price: 2.5, + adm: '', + crid: 'Creative#234', + w: 728, + h: 90 + }] + }] + }; + // request has both types - banner and native, response is parsed as banner. + // for impression#2, response is parsed as video + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(2); + const bid = bids[0]; + expect(bid.width).to.equal(728); + expect(bid.height).to.equal(90); + const secondBid = bids[1]; + expect(secondBid.vastXml).to.equal(''); + }); }); From 1e98aef06bbe470838ee67779756747e962bdcec Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Sat, 21 Nov 2020 05:37:14 +0900 Subject: [PATCH 0393/1476] Fix/test code set cookie (#5996) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * use storage Class Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- test/spec/modules/relaidoBidAdapter_spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 65dcd9b7db7..ebc62752f16 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -15,7 +15,6 @@ const storage = getStorageManager(); storage.setCookie(UUID_KEY, relaido_uuid); describe('RelaidoAdapter', function () { - window.document.cookie = `${UUID_KEY}=${relaido_uuid}` let bidRequest; let bidderRequest; let serverResponse; From 04c941fcff2d4fb0581e363c81df4753f90be0af Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Sat, 21 Nov 2020 03:55:13 +0530 Subject: [PATCH 0394/1476] Honour bidFloor key in params (#5999) Co-authored-by: monis.q --- modules/medianetBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index a30f6fc2627..a2dc8bdfd03 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -204,7 +204,7 @@ function slotParams(bidRequest) { params.tagid = bidRequest.params.crid.toString(); } - let bidFloor = parseFloat(bidRequest.params.bidfloor); + let bidFloor = parseFloat(bidRequest.params.bidfloor || bidRequest.params.bidFloor); if (bidFloor) { params.bidfloor = bidFloor; } From 8135e5d0045dfd3a045164f29624837a7c77289b Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Sat, 21 Nov 2020 07:47:37 +0200 Subject: [PATCH 0395/1476] adform adapter - allow to pass custom eids param (#6008) --- modules/adformBidAdapter.js | 8 ++------ test/spec/modules/adformBidAdapter_spec.js | 8 ++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/adformBidAdapter.js b/modules/adformBidAdapter.js index 48000e082b2..05e45a428d3 100644 --- a/modules/adformBidAdapter.js +++ b/modules/adformBidAdapter.js @@ -23,7 +23,7 @@ export const spec = { const eids = getEncodedEIDs(utils.deepAccess(validBidRequests, '0.userIdAsEids')); var request = []; - var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'fd', 1 ], [ 'url', null ], [ 'tid', null ] ]; + var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'fd', 1 ], [ 'url', null ], [ 'tid', null ], [ 'eids', eids ] ]; var bids = JSON.parse(JSON.stringify(validBidRequests)); var bidder = (bids[0] && bids[0].bidder) || BIDDER_CODE; for (i = 0, l = bids.length; i < l; i++) { @@ -65,10 +65,6 @@ export const spec = { request.push('us_privacy=' + bidderRequest.uspConsent); } - if (eids) { - request.push('eids=' + eids); - } - for (i = 1, l = globalParams.length; i < l; i++) { _key = globalParams[i][0]; _value = globalParams[i][1]; @@ -100,7 +96,7 @@ export const spec = { function getEncodedEIDs(eids) { if (utils.isArray(eids) && eids.length > 0) { const parsed = parseEIDs(eids); - return encodeURIComponent(btoa(JSON.stringify(parsed))); + return btoa(JSON.stringify(parsed)); } } diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js index 360979659de..23db7a8dc97 100644 --- a/test/spec/modules/adformBidAdapter_spec.js +++ b/test/spec/modules/adformBidAdapter_spec.js @@ -149,6 +149,14 @@ describe('Adform adapter', function () { }); }); + it('should allow to pass custom extended ids', function () { + bids[0].params.eids = 'some_id_value'; + let request = spec.buildRequests(bids); + let eids = parseUrl(request.url).query.eids; + + assert.equal(eids, 'some_id_value'); + }); + describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); From e5e899d2a8fca90f7b03e8e72e1d910a640cc6a0 Mon Sep 17 00:00:00 2001 From: John Salis Date: Sat, 21 Nov 2020 00:58:14 -0500 Subject: [PATCH 0396/1476] Add video response type param to Beachfront adapter (#6011) * add video response type param to beachfront bidder * run tests Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 19 +++++--- .../spec/modules/beachfrontBidAdapter_spec.js | 43 ++++++++++++------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 5f0a4b03a04..4b30f47e2cf 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.13'; +const ADAPTER_VERSION = '1.14'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; @@ -61,18 +61,17 @@ export const spec = { response = response.body; if (isVideoBid(bidRequest)) { - if (!response || !response.url || !response.bidPrice) { + if (!response || !response.bidPrice) { utils.logWarn(`No valid video bids from ${spec.code} bidder`); return []; } let sizes = getVideoSizes(bidRequest); let firstSize = getFirstSize(sizes); let context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); - return { + let responseType = getVideoBidParam(bidRequest, 'responseType') || 'both'; + let bidResponse = { requestId: bidRequest.bidId, bidderCode: spec.code, - vastUrl: response.url, - vastXml: response.vast, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, @@ -83,6 +82,16 @@ export const spec = { netRevenue: true, ttl: 300 }; + + if (responseType === 'nurl' || responseType === 'both') { + bidResponse.vastUrl = response.url; + } + + if (responseType === 'adm' || responseType === 'both') { + bidResponse.vastXml = response.vast; + } + + return bidResponse; } else { if (!response || !response.length) { utils.logWarn(`No valid banner bids from ${spec.code} bidder`); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index aa952d088a7..661780ffac0 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -530,16 +530,6 @@ describe('BeachfrontAdapter', function () { expect(bidResponse.length).to.equal(0); }); - it('should return no bids if the response "url" is missing', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { video: {} }; - const serverResponse = { - bidPrice: 5.00 - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - expect(bidResponse.length).to.equal(0); - }); - it('should return no bids if the response "bidPrice" is missing', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; @@ -562,6 +552,7 @@ describe('BeachfrontAdapter', function () { const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + vast: '', crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); @@ -571,7 +562,7 @@ describe('BeachfrontAdapter', function () { cpm: serverResponse.bidPrice, creativeId: serverResponse.crid, vastUrl: serverResponse.url, - vastXml: undefined, + vastXml: serverResponse.vast, width: width, height: height, renderer: null, @@ -602,7 +593,7 @@ describe('BeachfrontAdapter', function () { }); }); - it('should return vast xml if found on the bid response', () => { + it('should return only vast url if the response type is "nurl"', () => { const width = 640; const height = 480; const bidRequest = bidRequests[0]; @@ -611,6 +602,7 @@ describe('BeachfrontAdapter', function () { playerSize: [ width, height ] } }; + bidRequest.params.video = { responseType: 'nurl' }; const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', @@ -618,10 +610,29 @@ describe('BeachfrontAdapter', function () { crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - expect(bidResponse).to.deep.contain({ - vastUrl: serverResponse.url, - vastXml: serverResponse.vast - }); + expect(bidResponse.vastUrl).to.equal(serverResponse.url); + expect(bidResponse.vastXml).to.equal(undefined); + }); + + it('should return only vast xml if the response type is "adm"', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + bidRequest.params.video = { responseType: 'adm' }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + vast: '', + crid: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.vastUrl).to.equal(undefined); + expect(bidResponse.vastXml).to.equal(serverResponse.vast); }); it('should return a renderer for outstream video bids', function () { From 022bcea3e9c72a9a2817c82484adad51fda8dcc0 Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Sat, 21 Nov 2020 01:26:35 -0500 Subject: [PATCH 0397/1476] fix appnexus segment field format (#6013) --- modules/haloRtdProvider.js | 5 ++--- test/spec/modules/haloRtdProvider_spec.js | 24 +++++++++++------------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/modules/haloRtdProvider.js b/modules/haloRtdProvider.js index fff9e43ea2a..1ce44ca6004 100644 --- a/modules/haloRtdProvider.js +++ b/modules/haloRtdProvider.js @@ -33,9 +33,8 @@ const segmentMappers = { set(bid, 'params.user.segments', []); let appnexusSegments = []; segments.forEach(segment => { - if (typeof segment.value != 'undefined' && segment.value != null) { - let appnexusSegment = {'id': segment.id, 'value': segment.value}; - appnexusSegments.push(appnexusSegment); + if (typeof segment.id != 'undefined' && segment.id != null) { + appnexusSegments.push(parseInt(segment.id)); } }) bid.params.user.segments = bid.params.user.segments.concat(appnexusSegments); diff --git a/test/spec/modules/haloRtdProvider_spec.js b/test/spec/modules/haloRtdProvider_spec.js index 17c2e19e1a6..69ea4bc997c 100644 --- a/test/spec/modules/haloRtdProvider_spec.js +++ b/test/spec/modules/haloRtdProvider_spec.js @@ -34,7 +34,7 @@ describe('haloRtdProvider', function() { id: 'appnexus', segment: [ { - id: 'apnseg0' + id: '0' } ] } @@ -43,7 +43,7 @@ describe('haloRtdProvider', function() { }, params: { user: { - segments: [{'id': 'apnseg0', 'value': 0}] + segments: [0] } } } @@ -82,22 +82,22 @@ describe('haloRtdProvider', function() { ]; const data = { - appnexus: [{id: 'apnseg1', value: 0}, {id: 'apnseg2', value: 2}, {id: 'apnseg3'}], + appnexus: [{id: '1'}, {id: '2'}, {id: '3'}], generic: [{id: 'seg1'}, {id: 'seg2'}, {id: 'seg3'}] }; addSegmentData(adUnits, data, config); - expect(adUnits[0].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'apnseg0'); - expect(adUnits[0].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'apnseg1'); - expect(adUnits[0].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', 'apnseg2'); - expect(adUnits[0].bids[0].fpd.user.data[0].segment[3]).to.have.deep.property('id', 'apnseg3'); - expect(adUnits[0].bids[0].params.user).to.have.deep.property('segments', [{'id': 'apnseg0', 'value': 0}, {'id': 'apnseg1', 'value': 0}, {'id': 'apnseg2', 'value': 2}]); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', '0'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', '1'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', '2'); + expect(adUnits[0].bids[0].fpd.user.data[0].segment[3]).to.have.deep.property('id', '3'); + expect(adUnits[0].bids[0].params.user).to.have.deep.property('segments', [0, 1, 2, 3]); - expect(adUnits[1].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'apnseg1'); - expect(adUnits[1].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'apnseg2'); - expect(adUnits[1].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', 'apnseg3'); - expect(adUnits[1].bids[0].params.user).to.have.deep.property('segments', [{'id': 'apnseg1', 'value': 0}, {'id': 'apnseg2', 'value': 2}]); + expect(adUnits[1].bids[0].fpd.user.data[0].segment[0]).to.have.deep.property('id', '1'); + expect(adUnits[1].bids[0].fpd.user.data[0].segment[1]).to.have.deep.property('id', '2'); + expect(adUnits[1].bids[0].fpd.user.data[0].segment[2]).to.have.deep.property('id', '3'); + expect(adUnits[1].bids[0].params.user).to.have.deep.property('segments', [1, 2, 3]); expect(adUnits[1].bids[1].fpd.user.data[0].segment[0]).to.have.deep.property('id', 'seg1'); expect(adUnits[1].bids[1].fpd.user.data[0].segment[1]).to.have.deep.property('id', 'seg2'); From cebae4a07748bcf0570e1781c31bb9f865bffbab Mon Sep 17 00:00:00 2001 From: haxmediagithub <74675750+haxmediagithub@users.noreply.github.com> Date: Mon, 23 Nov 2020 18:06:30 +0200 Subject: [PATCH 0398/1476] New haxmedia bidder adapter (#6001) --- modules/haxmediaBidAdapter.js | 107 +++++++ modules/haxmediaBidAdapter.md | 72 +++++ test/spec/modules/haxmediaBidAdapter_spec.js | 304 +++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 modules/haxmediaBidAdapter.js create mode 100644 modules/haxmediaBidAdapter.md create mode 100644 test/spec/modules/haxmediaBidAdapter_spec.js diff --git a/modules/haxmediaBidAdapter.js b/modules/haxmediaBidAdapter.js new file mode 100644 index 00000000000..c4ce2eb3663 --- /dev/null +++ b/modules/haxmediaBidAdapter.js @@ -0,0 +1,107 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'haxmedia'; +const AD_URL = 'https://balancer.haxmedia.io/?c=o&m=multi'; + +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); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + + const placements = []; + const request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + '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 || {}, + }; + const mediaType = bid.mediaTypes + + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { + placement.sizes = mediaType[BANNER].sizes; + placement.traffic = BANNER; + } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { + placement.wPlayer = mediaType[VIDEO].playerSize[0]; + placement.hPlayer = mediaType[VIDEO].playerSize[1]; + placement.traffic = VIDEO; + } else if (mediaType && mediaType[NATIVE]) { + placement.native = mediaType[NATIVE]; + placement.traffic = NATIVE; + } + 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)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/haxmediaBidAdapter.md b/modules/haxmediaBidAdapter.md new file mode 100644 index 00000000000..f661a9e4e71 --- /dev/null +++ b/modules/haxmediaBidAdapter.md @@ -0,0 +1,72 @@ +# Overview + +``` +Module Name: haxmedia Bidder Adapter +Module Type: haxmedia Bidder Adapter +Maintainer: haxmixqk@haxmediapartners.io +``` + +# Description + +Module that connects to haxmedia demand sources + +# Test Parameters +``` + var adUnits = [ + { + code:'1', + mediaTypes:{ + banner: { + sizes: [[300, 250]], + } + }, + bids:[ + { + bidder: 'haxmedia', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids:[ + { + bidder: 'haxmedia', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + native: { + title: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids:[ + { + bidder: 'haxmedia', + params: { + placementId: 0 + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/haxmediaBidAdapter_spec.js b/test/spec/modules/haxmediaBidAdapter_spec.js new file mode 100644 index 00000000000..2e39d771bdf --- /dev/null +++ b/test/spec/modules/haxmediaBidAdapter_spec.js @@ -0,0 +1,304 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/haxmediaBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; + +describe('haxmediaBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'haxmedia', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 783, + traffic: BANNER + } + }; + + 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://balancer.haxmedia.io/?c=o&m=multi'); + }); + 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'); + 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.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement.placementId).to.equal(783); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + 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'); + }); + 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' + }] + }; + 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'); + 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'); + }); + 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', + }] + }; + 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'); + 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'); + }); + 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; + }); + }); +}); From 8930be4fc59e086bd6e0c5caf83e0dee673b4fa1 Mon Sep 17 00:00:00 2001 From: Brandon Ling <51931757+blingster7@users.noreply.github.com> Date: Tue, 24 Nov 2020 02:02:34 -0500 Subject: [PATCH 0399/1476] [Triplelift] Add advertiserDomains support (#5993) * 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 * TripleLift: Sending schain (#1) * Sending schain * null -> undefined * Hardcode sync endpoint protocol * Switch to EB2 sync endpoint * Add support for image based user syncing * Rename endpoint variable * Add assertion * Add CCPA query param * Simplify check for usPrivacy argument * put advertiser name in the bid.meta field if it exists * update unit tests with meta.advertiserName field * Triplelift: FPD key value pair support (#5) * Triplelift: Add support for global fpd * don't filter fpd * adds coppa support back in * add gvlid, update validation method, add unit tests * remove advertiserDomains logic * typo * update _buildResponseObject to use new instream validation * add advertiserDomains support Co-authored-by: Will Chapin Co-authored-by: colbertk <50499465+colbertk@users.noreply.github.com> Co-authored-by: David Andersen Co-authored-by: colbertk Co-authored-by: Kevin Zhou Co-authored-by: kzhouTL <43545828+kzhouTL@users.noreply.github.com> Co-authored-by: Sy Dao Co-authored-by: sdao-tl <49252703+sdao-tl@users.noreply.github.com> --- modules/tripleliftBidAdapter.js | 4 ++++ test/spec/modules/tripleliftBidAdapter_spec.js | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 69c52711236..d54d76efb41 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -293,6 +293,10 @@ function _buildResponseObject(bidderRequest, bid) { if (bid.advertiser_name) { bidResponse.meta.advertiserName = bid.advertiser_name; } + + if (bid.adomain && bid.adomain.length) { + bidResponse.meta.advertiserDomains = bid.adomain; + } }; return bidResponse; } diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index f01949755c7..b417876f276 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -638,7 +638,8 @@ describe('triplelift adapter', function () { ad: 'ad-markup', iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', tl_source: 'tlx', - advertiser_name: 'fake advertiser name' + advertiser_name: 'fake advertiser name', + adomain: ['basspro.com', 'internetalerts.org'] }, { imp_id: 1, @@ -747,6 +748,13 @@ describe('triplelift adapter', function () { expect(result[0].meta.advertiserName).to.equal('fake advertiser name'); expect(result[1].meta).to.not.have.key('advertiserName'); }); + + it('should include the advertiser domain array in the meta field if available', function () { + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result[0].meta.advertiserDomains[0]).to.equal('basspro.com'); + expect(result[0].meta.advertiserDomains[1]).to.equal('internetalerts.org'); + expect(result[1].meta).to.not.have.key('advertiserDomains'); + }); }); describe('getUserSyncs', function() { From 015c48b67956348888f1768fbaae8a9027222468 Mon Sep 17 00:00:00 2001 From: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Date: Tue, 24 Nov 2020 23:08:13 +0200 Subject: [PATCH 0400/1476] Browsi RTD provider docs (#5920) * 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 * RTD docs --- modules/browsiRtdProvider.md | 55 +++++++++++++++++++++++++++++++ modules/rtdModule/provider.md | 28 +++++++++------- modules/rtdModule/realTimeData.md | 32 ------------------ 3 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 modules/browsiRtdProvider.md delete mode 100644 modules/rtdModule/realTimeData.md diff --git a/modules/browsiRtdProvider.md b/modules/browsiRtdProvider.md new file mode 100644 index 00000000000..0dd8c1d7609 --- /dev/null +++ b/modules/browsiRtdProvider.md @@ -0,0 +1,55 @@ +# Overview + +The Browsi RTD module provides viewability predictions for ad slots on the page. +To use this module, you’ll need to work with [Browsi](https://gobrowsi.com/) to get an account and receive instructions on how to set up your pages and ad server. + +# Configurations + +Compile the Browsi RTD Provider into your Prebid build: + +`gulp build --modules=browsiRtdProvider` + + +Configuration example for using RTD module with `browsi` provider +```javascript + pbjs.setConfig({ + "realTimeData": { + "auctionDelay": 1000, + dataProviders:[{ + "name": "browsi", + "waitForIt": "true" + "params": { + "url": "testUrl.com", + "siteKey": "testKey", + "pubKey": "testPub", + "keyName":"bv" + } + }] + } + }); +``` + +#Params + +Contact Browsi to get required params + +| param name | type |Scope | Description | +| :------------ | :------------ | :------- | :------- | +| url | string | required | Browsi server URL | +| siteKey | string | required | Site key | +| pubKey | string | required | Publisher key | +| keyName | string | optional | Ad unit targeting key | + + +#Output +`getTargetingData` function will return expected viewability prediction in the following structure: +```json +{ + "adUnitCode":{ + "browsiViewability":"0.6" + }, + "adUnitCode2":{ + "browsiViewability":"0.9" + } +} +``` diff --git a/modules/rtdModule/provider.md b/modules/rtdModule/provider.md index fb42e7188d3..116db160238 100644 --- a/modules/rtdModule/provider.md +++ b/modules/rtdModule/provider.md @@ -1,27 +1,33 @@ New provider must include the following: -1. sub module object: -``` -export const subModuleName = { - name: String, - getData: Function -}; -``` +1. sub module object with the following keys: -2. Function that returns the real time data according to the following structure: -``` +| param name | type | Scope | Description | Params | +| :------------ | :------------ | :------ | :------ | :------ | +| name | string | required | must match the name provided by the publisher in the on-page config | n/a | +| init | function | required | defines the function that does any auction-level initialization required | config, userConsent | +| getTargetingData | function | optional | defines a function that provides ad server targeting data to RTD-core | adUnitArray, config, userConsent | +| getBidRequestData | function | optional | defines a function that provides ad server targeting data to RTD-core | reqBidsConfigObj, callback, config, userConsent | +| onAuctionInitEvent | function | optional | listens to the AUCTION_INIT event and calls a sub-module function that lets it inspect and/or update the auction | auctionDetails, config, userConsent | +| onAuctionEndEvent | function |optional | listens to the AUCTION_END event and calls a sub-module function that lets it know when auction is done | auctionDetails, config, userConsent | +| onBidResponseEvent | function |optional | listens to the BID_RESPONSE event and calls a sub-module function that lets it know when a bid response has been collected | bidResponse, config, userConsent | + +2. `getTargetingData` function (if defined) should return ad unit targeting data according to the following structure: +```json { "adUnitCode":{ "key":"value", "key2":"value" }, "adUnitCode2":{ - "dataKey":"dataValue", + "dataKey":"dataValue" } } ``` 3. Hook to Real Time Data module: -``` +```javascript submodule('realTimeData', subModuleName); ``` + +4. See detailed documentation [here](https://docs.prebid.org/dev-docs/add-rtd-submodule.html) diff --git a/modules/rtdModule/realTimeData.md b/modules/rtdModule/realTimeData.md deleted file mode 100644 index b2859098b1f..00000000000 --- a/modules/rtdModule/realTimeData.md +++ /dev/null @@ -1,32 +0,0 @@ -## Real Time Data Configuration Example - -Example showing config using `browsi` sub module -``` - pbjs.setConfig({ - "realTimeData": { - "auctionDelay": 1000, - dataProviders[{ - "name": "browsi", - "params": { - "url": "testUrl.com", - "siteKey": "testKey", - "pubKey": "testPub", - "keyName":"bv" - } - }] - } - }); -``` - -Example showing real time data object received form `browsi` real time data provider -``` -{ - "adUnitCode":{ - "key":"value", - "key2":"value" - }, - "adUnitCode2":{ - "dataKey":"dataValue", - } -} -``` From 1f24ee48c906fd446178659a830cb1fe9af49532 Mon Sep 17 00:00:00 2001 From: GeoEdge-r-and-d <72186958+GeoEdge-r-and-d@users.noreply.github.com> Date: Tue, 24 Nov 2020 23:30:30 +0200 Subject: [PATCH 0401/1476] Add Geoedge RTD provider submodule (#5869) * Add Geoedge RTD provider submodule * Add Geoedge RTD provider submodule accroding to RTD phase 3 * Add tests * Add docs * Add integration example * Add as child module of RTD for easier builds * Update RTD submodule interface See https://docs.prebid.org/dev-docs/add-rtd-submodule.html * Update tests * Update integration example remove unnecessary param * Update docs * Update RTD submodule provider * Remove getConfig * Get params from init * Use beforeInit * Update docs Extend and document the wap param * Update tests * Remove unused config module * Update integreation example Relevant opening and inline comments * Update Geoedge RTD submodule provider * Hardcode HTTPS scheme * Rename to "donePreload" for clarity * Use regex to replace macros instead of loop * Update tests Preload request scheme is now always HTTPS * Remove integration example HTML page As for @Fawke request at https://github.com/prebid/Prebid.js/pull/5869#issuecomment-732237482 Co-authored-by: daniel manan Co-authored-by: bretg --- modules/.submodules.json | 3 +- modules/geoedgeRtdProvider.js | 213 +++++++++++++++++++ modules/geoedgeRtdProvider.md | 67 ++++++ test/spec/modules/geoedgeRtdProvider_spec.js | 111 ++++++++++ 4 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 modules/geoedgeRtdProvider.js create mode 100644 modules/geoedgeRtdProvider.md create mode 100644 test/spec/modules/geoedgeRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 53caf7a0671..36ead7df888 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -28,6 +28,7 @@ "browsiRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", - "reconciliationRtdProvider" + "reconciliationRtdProvider", + "geoedgeRtdProvider" ] } diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js new file mode 100644 index 00000000000..001ef67b66a --- /dev/null +++ b/modules/geoedgeRtdProvider.js @@ -0,0 +1,213 @@ +/** + * This module adds geoedge provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch creative wrapper from geoedge server + * The module will place geoedge RUM client on bid responses markup + * @module modules/geoedgeProvider + * @requires module:modules/realTimeData + */ + +/** + * @typedef {Object} ModuleParams + * @property {string} key + * @property {?Object} bidders + * @property {?boolean} wap + * @property {?string} keyName + */ + +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js'; + +/** @type {string} */ +const SUBMODULE_NAME = 'geoedge'; +/** @type {string} */ +export const WRAPPER_URL = 'https://wrappers.geoedge.be/wrapper.html'; +/** @type {string} */ +/* eslint-disable no-template-curly-in-string */ +export const HTML_PLACEHOLDER = '${creative}'; +/** @type {string} */ +const PV_ID = generateUUID(); +/** @type {string} */ +const HOST_NAME = 'https://rumcdn.geoedge.be'; +/** @type {string} */ +const FILE_NAME = 'grumi.js'; +/** @type {function} */ +export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME}`; +/** @type {string} */ +export let wrapper +/** @type {boolean} */; +let wrapperReady; +/** @type {boolean} */; +let preloaded; + +/** + * fetches the creative wrapper + * @param {function} sucess - success callback + */ +export function fetchWrapper(success) { + if (wrapperReady) { + return success(wrapper); + } + ajax(WRAPPER_URL, success); +} + +/** + * sets the wrapper and calls preload client + * @param {string} responseText + */ +export function setWrapper(responseText) { + wrapperReady = true; + wrapper = responseText; +} + +/** + * preloads the client + * @param {string} key + */ +export function preloadClient(key) { + let link = document.createElement('link'); + link.rel = 'preload'; + link.as = 'script'; + link.href = getClientUrl(key); + link.onload = () => { preloaded = true }; + insertElement(link); +} + +/** + * creates identity function for string replace without special replacement patterns + * @param {string} str + * @return {function} + */ +function replacer(str) { + return function () { + return str; + } +} + +export function wrapHtml(wrapper, html) { + return wrapper.replace(HTML_PLACEHOLDER, replacer(html)); +} + +/** + * generate macros dictionary from bid response + * @param {Object} bid + * @param {string} key + * @return {Object} + */ +function getMacros(bid, key) { + return { + '${key}': key, + '%%ADUNIT%%': bid.adUnitCode, + '%%WIDTH%%': bid.width, + '%%HEIGHT%%': bid.height, + '%%PATTERN:hb_adid%%': bid.adId, + '%%PATTERN:hb_bidder%%': bid.bidderCode, + '%_isHb!': true, + '%_hbcid!': bid.creativeId || '', + '%%PATTERN:hb_pb%%': bid.pbHg, + '%%SITE%%': location.hostname, + '%_pimp%': PV_ID + }; +} + +/** + * replace macro placeholders in a string with values from a dictionary + * @param {string} wrapper + * @param {Object} macros + * @return {string} + */ +function replaceMacros(wrapper, macros) { + var re = new RegExp('\\' + Object.keys(macros).join('|'), 'gi'); + + return wrapper.replace(re, function(matched) { + return macros[matched]; + }); +} + +/** + * build final creative html with creative wrapper + * @param {Object} bid + * @param {string} wrapper + * @param {string} html + * @return {string} + */ +function buildHtml(bid, wrapper, html, key) { + let macros = getMacros(bid, key); + wrapper = replaceMacros(wrapper, macros); + return wrapHtml(wrapper, html); +} + +/** + * muatates the bid ad property + * @param {Object} bid + * @param {string} ad + */ +function mutateBid(bid, ad) { + bid.ad = ad; +} + +/** + * wraps a bid object with the creative wrapper + * @param {Object} bid + * @param {string} key + */ +export function wrapBidResponse(bid, key) { + let wrapped = buildHtml(bid, wrapper, bid.ad, key); + mutateBid(bid, wrapped); +} + +/** + * checks if bidder's bids should be monitored + * @param {string} bidder + * @return {boolean} + */ +function isSupportedBidder(bidder, paramsBidders) { + return isEmpty(paramsBidders) || paramsBidders[bidder] === true; +} + +/** + * checks if bid should be monitored + * @param {Object} bid + * @return {boolean} + */ +function shouldWrap(bid, params) { + let supportedBidder = isSupportedBidder(bid.bidderCode, params.bidders); + let donePreload = params.wap ? preloaded : true; + return wrapperReady && supportedBidder && donePreload; +} + +function conditionallyWrap(bidResponse, config, userConsent) { + let params = config.params; + if (shouldWrap(bidResponse, params)) { + wrapBidResponse(bidResponse, params.key); + } +} + +function init(config, userConsent) { + let params = config.params; + if (!params || !params.key) { + logError('missing key for geoedge RTD module provider'); + return false; + } + preloadClient(params.key); + return true; +} + +/** @type {RtdSubmodule} */ +export const geoedgeSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: SUBMODULE_NAME, + init, + onBidResponseEvent: conditionallyWrap +}; + +export function beforeInit() { + fetchWrapper(setWrapper); + submodule('realTimeData', geoedgeSubmodule); +} + +beforeInit(); diff --git a/modules/geoedgeRtdProvider.md b/modules/geoedgeRtdProvider.md new file mode 100644 index 00000000000..e4aa046a97d --- /dev/null +++ b/modules/geoedgeRtdProvider.md @@ -0,0 +1,67 @@ +## Overview + +Module Name: Geoedge Rtd provider +Module Type: Rtd Provider +Maintainer: guy.books@geoedge.com + +The Geoedge Realtime module let pusblishers to block bad ads such as automatic redirects, malware, offensive creatives and landing pages. +To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key. + +## Integration + +1) Build the geoedge RTD module into the Prebid.js package with: + +``` +gulp build --modules=geoedgeRtdProvider,... +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the geoedge module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` object: + +```javascript +pbjs.setConfig({ + realTimeData: { + dataProviders: [{ + name: 'geoedge', + params: { + key: '123123', + bidders: { + 'bidderA': true, // monitor bids form this bidder + 'bidderB': false // do not monitor bids form this bidder. + }, + wap: true + } + }] + } +}); +``` + +Parameters details: + +{: .table .table-bordered .table-striped } +|Name |Type |Description |Notes | +| :------------ | :------------ | :------------ |:------------ | +|name | String | Real time data module name |Required, always 'geoedge' | +|params | Object | | | +|params.key | String | Customer key |Required, contact Geoedge to get your key | +|params.bidders | Object | Bidders to monitor |Optional, list of bidder to include / exclude from monitoring. Omitting this will monitor bids from all bidders. | +|params.wap |Boolean |Wrap after preload |Optional, defaults to `false`. Set to `true` if you want to monitor only after the module has preloaded the monitoring client. | + +## Example + +To view an integration example: + +1) in your cli run: + +``` +gulp serve --modules=appnexusBidAdapter,geoedgeRtdProvider +``` + +2) in your browser, navigate to: + +``` +http://localhost:9999/integrationExamples/gpt/geoedgeRtdProvider_example.html +``` diff --git a/test/spec/modules/geoedgeRtdProvider_spec.js b/test/spec/modules/geoedgeRtdProvider_spec.js new file mode 100644 index 00000000000..cf4e0b53fde --- /dev/null +++ b/test/spec/modules/geoedgeRtdProvider_spec.js @@ -0,0 +1,111 @@ +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'; + +let key = '123123123'; +function makeConfig() { + return { + name: 'geoedge', + params: { + wap: false, + key: key, + bidders: { + bidderA: true, + bidderB: false + } + } + }; +} + +function mockBid(bidderCode) { + return { + 'ad': '', + 'cpm': '1.00', + 'width': 300, + 'height': 250, + 'bidderCode': bidderCode, + 'requestId': utils.getUniqueIdentifierStr(), + 'creativeId': 'id', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360 + }; +} + +let mockWrapper = `${htmlPlaceholder}`; + +describe('Geoedge RTD module', function () { + describe('beforeInit', function () { + let submoduleStub; + + before(function () { + submoduleStub = sinon.stub(hook, 'submodule'); + }); + after(function () { + submoduleStub.restore(); + }); + it('should fetch the wrapper', function () { + beforeInit(); + let request = server.requests[0]; + let isWrapperRequest = request && request.url && request.url && request.url === WRAPPER_URL; + expect(isWrapperRequest).to.equal(true); + }); + it('should register RTD submodule provider', function () { + expect(submoduleStub.calledWith('realTimeData', geoedgeSubmodule)).to.equal(true); + }); + }); + describe('setWrapper', function () { + it('should set the wrapper', function () { + setWrapper(mockWrapper); + expect(wrapper).to.equal(mockWrapper); + }); + }); + describe('submodule', function () { + describe('name', function () { + it('should be geoedge', function () { + expect(geoedgeSubmodule.name).to.equal('geoedge'); + }); + }); + describe('init', function () { + let insertElementStub; + + before(function () { + insertElementStub = sinon.stub(utils, 'insertElement'); + }); + after(function () { + utils.insertElement.restore(); + }); + it('should return false when missing params or key', function () { + let missingParams = geoedgeSubmodule.init({}); + let missingKey = geoedgeSubmodule.init({ params: {} }); + expect(missingParams || missingKey).to.equal(false); + }); + it('should return true when params are ok', function () { + expect(geoedgeSubmodule.init(makeConfig())).to.equal(true); + }); + it('should preload the client', 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); + }); + }); + describe('onBidResponseEvent', function () { + let bidFromA = mockBid('bidderA'); + it('should wrap bid html when bidder is configured', function () { + geoedgeSubmodule.onBidResponseEvent(bidFromA, makeConfig()); + expect(bidFromA.ad.indexOf('')).to.equal(0); + }); + it('should not wrap bid html when bidder is not configured', function () { + let bidFromB = mockBid('bidderB'); + geoedgeSubmodule.onBidResponseEvent(bidFromB, makeConfig()); + expect(bidFromB.ad.indexOf('')).to.equal(-1); + }); + it('should only muatate the bid ad porperty', function () { + let copy = Object.assign({}, bidFromA); + delete copy.ad; + let equalsOriginal = Object.keys(copy).every(key => copy[key] === bidFromA[key]); + expect(equalsOriginal).to.equal(true); + }); + }); + }); +}); From 678ffdfd69a666570dab4738c8b665b686a16458 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Wed, 25 Nov 2020 05:41:17 +0100 Subject: [PATCH 0402/1476] sspBC adapter: update to v4.6 (notifications, user sync) (#5941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update adapter to v4.6 - add notification endpoint - send bidWon and onTimeout notifications - send CMP version to user sync endpoint Co-authored-by: Wojciech Biały --- modules/sspBCAdapter.js | 51 +++++++++++++++++-- test/spec/modules/sspBCAdapter_spec.js | 70 +++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/modules/sspBCAdapter.js b/modules/sspBCAdapter.js index ef89fb08449..4069c722e9d 100644 --- a/modules/sspBCAdapter.js +++ b/modules/sspBCAdapter.js @@ -1,14 +1,17 @@ import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'sspBC'; const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; +const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.5'; +const BIDDER_VERSION = '4.6'; const W = window; const { navigator } = W; +var consentApiVersion; const cookieSupport = () => { const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); @@ -53,6 +56,7 @@ const applyClientHints = ortbRequest => { function applyGdpr(bidderRequest, ortbRequest) { if (bidderRequest && bidderRequest.gdprConsent) { + consentApiVersion = bidderRequest.gdprConsent.apiVersion; ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': bidderRequest.gdprConsent.gdprApplies ? 1 : 0 }); ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': bidderRequest.gdprConsent.consentString }); } @@ -68,6 +72,14 @@ function setOnAny(collection, key) { } } +function sendNotification(payload) { + ajax(NOTIFY_URL, null, JSON.stringify(payload), { + withCredentials: false, + method: 'POST', + crossOrigin: true + }); +} + /** * @param {object} slot Ad Unit Params by Prebid * @returns {object} Banner by OpenRTB 2.5 §3.2.6 @@ -278,6 +290,7 @@ const spec = { mediaType: 'banner', meta: { advertiserDomains: serverBid.adomain, + networkName: seat, }, netRevenue: true, ad: renderCreative(site, response.id, serverBid, seat, request.bidderRequest), @@ -303,12 +316,44 @@ const spec = { if (syncOptions.iframeEnabled) { return [{ type: 'iframe', - url: SYNC_URL, + url: SYNC_URL + '?tcf=' + consentApiVersion, }]; } utils.logWarn('sspBC adapter requires iframe based user sync.'); }, - onTimeout() { + + onTimeout(timeoutData) { + var adSlots = []; + const bid = timeoutData && timeoutData[0]; + if (bid) { + timeoutData.forEach(bid => { adSlots.push(bid.params[0] && bid.params[0].id) }) + const payload = { + event: 'timeout', + requestId: bid.auctionId, + siteId: bid.params ? [bid.params[0].siteId] : [], + slotId: adSlots, + timeout: bid.timeout, + } + sendNotification(payload); + return payload; + } + }, + + onBidWon(bid) { + if (bid && bid.auctionId) { + const payload = { + event: 'bidWon', + requestId: bid.auctionId, + siteId: bid.params ? [bid.params[0].siteId] : [], + slotId: bid.params ? [bid.params[0].id] : [], + cpm: bid.cpm, + creativeId: bid.creativeId, + adomain: (bid.meta && bid.meta.advertiserDomains) ? bid.meta.advertiserDomains[0] : '', + networkName: (bid.meta && bid.meta.networkName) ? bid.meta.networkName : '', + } + sendNotification(payload); + return payload; + } }, }; diff --git a/test/spec/modules/sspBCAdapter_spec.js b/test/spec/modules/sspBCAdapter_spec.js index 2cb0e8defa4..29718deb031 100644 --- a/test/spec/modules/sspBCAdapter_spec.js +++ b/test/spec/modules/sspBCAdapter_spec.js @@ -1,6 +1,8 @@ import { assert, expect } from 'chai'; import { spec } from 'modules/sspBCAdapter.js'; import * as utils from 'src/utils.js'; +import * as sinon from 'sinon'; +import * as ajax from 'src/ajax.js'; const BIDDER_CODE = 'sspBC'; const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; @@ -60,10 +62,33 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: auctionId + '2', transactionId, } ]; + const bids_timeouted = [{ + adUnitCode: 'test_wideboard', + bidder: BIDDER_CODE, + params: [{ + id: '003', + siteId: '8816', + }], + auctionId, + bidId: auctionId + '1', + timeout: 100, + }, + { + adUnitCode: 'test_rectangle', + bidder: BIDDER_CODE, + params: [{ + id: '005', + siteId: '8816', + }], + auctionId, + bidId: auctionId + '2', + timeout: 100, + } + ]; const bids_test = [{ adUnitCode: 'test_wideboard', bidder: BIDDER_CODE, @@ -209,6 +234,7 @@ describe('SSPBC adapter', function () { return { bids, bids_test, + bids_timeouted, bidRequest, bidRequestSingle, bidRequestTest, @@ -323,7 +349,7 @@ describe('SSPBC adapter', function () { it('should provide correct url, if frame sync is allowed', function () { expect(syncResultAll).to.have.length(1); - expect(syncResultAll[0].url).to.be.equal(SYNC_URL); + expect(syncResultAll[0].url).to.have.string(SYNC_URL); }); it('should send no syncs, if frame sync is not allowed', function () { @@ -331,4 +357,44 @@ describe('SSPBC adapter', function () { expect(syncResultNone).to.be.undefined; }); }); + + describe('onBidWon', function () { + it('should generate no notification if bid is undefined', function () { + let notificationPayload = spec.onBidWon(); + expect(notificationPayload).to.be.undefined; + }); + + it('should generate notification with event name and request/site/slot data, if correct bid is provided', function () { + const { bids } = prepareTestData(); + let bid = bids[0]; + bid.params = [bid.params]; + + let notificationPayload = spec.onBidWon(bid); + expect(notificationPayload).to.have.property('event').that.equals('bidWon'); + expect(notificationPayload).to.have.property('requestId').that.equals(bid.auctionId); + expect(notificationPayload).to.have.property('siteId').that.deep.equals([bid.params[0].siteId]); + expect(notificationPayload).to.have.property('slotId').that.deep.equals([bid.params[0].id]); + }); + }); + + describe('onTimeout', function () { + it('should generate no notification if timeout data is undefined / has no bids', function () { + let notificationPayloadUndefined = spec.onTimeout(); + let notificationPayloadNoBids = spec.onTimeout([]); + + expect(notificationPayloadUndefined).to.be.undefined; + expect(notificationPayloadNoBids).to.be.undefined; + }); + + it('should generate single notification for any number of timeouted bids', function () { + const { bids_timeouted } = prepareTestData(); + + let notificationPayload = spec.onTimeout(bids_timeouted); + + expect(notificationPayload).to.have.property('event').that.equals('timeout'); + expect(notificationPayload).to.have.property('requestId').that.equals(bids_timeouted[0].auctionId); + expect(notificationPayload).to.have.property('siteId').that.deep.equals([bids_timeouted[0].params[0].siteId]); + expect(notificationPayload).to.have.property('slotId').that.deep.equals([bids_timeouted[0].params[0].id, bids_timeouted[1].params[0].id]); + }); + }); }); From 99fe1a77fa2b693517ecb139c7a40d0291c88e0b Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 25 Nov 2020 16:17:40 +0530 Subject: [PATCH 0403/1476] Appnexus - Send CriteoId in eids array. (#6025) * send criteo id in eids array * remove a line space --- modules/appnexusBidAdapter.js | 6 +++++- test/spec/modules/appnexusBidAdapter_spec.js | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index ff0e3230007..f714472bbb1 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -229,16 +229,20 @@ export const spec = { } const criteoId = utils.deepAccess(bidRequests[0], `userId.criteoId`); + let eids = []; if (criteoId) { let tpuids = []; tpuids.push({ 'provider': 'criteo', 'user_id': criteoId }); + eids.push({ + source: 'criteo.com', + id: criteoId + }); payload.tpuids = tpuids; } - let eids = []; const tdid = utils.deepAccess(bidRequests[0], `userId.tdid`); if (tdid) { eids.push({ diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 4102896ba94..21d3da358be 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -824,6 +824,11 @@ describe('AppNexusAdapter', function () { rti_partner: 'TDID' }); + expect(payload.eids).to.deep.include({ + source: 'criteo.com', + id: 'sample-criteo-userid', + }); + expect(payload.tpuids).to.deep.include({ provider: 'criteo', user_id: 'sample-criteo-userid', From e202cf5ad7ff39b0fa221908c8abdb31d2115847 Mon Sep 17 00:00:00 2001 From: Tucker <72400387+MenelikTucker-districtm@users.noreply.github.com> Date: Wed, 25 Nov 2020 08:21:39 -0500 Subject: [PATCH 0404/1476] allow users to be sent to dmx even when gdpr is configured in prebid (#6027) --- modules/districtmDMXBidAdapter.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index a7bcead5f0b..845cff460ee 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -128,9 +128,12 @@ export const spec = { dmxRequest.regs = {}; dmxRequest.regs.ext = {}; dmxRequest.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0; - dmxRequest.user = {}; - dmxRequest.user.ext = {}; - dmxRequest.user.ext.consent = bidderRequest.gdprConsent.consentString; + + if (bidderRequest.gdprConsent.gdprApplies === true) { + dmxRequest.user = {}; + dmxRequest.user.ext = {}; + dmxRequest.user.ext.consent = bidderRequest.gdprConsent.consentString; + } } dmxRequest.regs = dmxRequest.regs || {}; dmxRequest.regs.coppa = config.getConfig('coppa') === true ? 1 : 0; From 1137a64c508827d952a384975462b38d269cb46a Mon Sep 17 00:00:00 2001 From: Galphimbl Date: Wed, 25 Nov 2020 15:40:04 +0200 Subject: [PATCH 0405/1476] Admixer adapter update - add user syncs (#6024) * Migrating to Prebid 1.0 * Migrating to Prebid 1.0 * Fix spec * add gdpr and usp * remove changes in gdpr_hello_world.html * Update gdpr_hello_world.html add spaces * add user syncs * remove comments * tests Co-authored-by: atkachov --- modules/admixerBidAdapter.js | 20 ++++-- test/spec/modules/admixerBidAdapter_spec.js | 76 +++++++++++++++------ 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index b2f24cfa910..3bb392538ff 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'admixer'; const ALIASES = ['go2net']; -const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.0.aspx'; +const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.1.aspx'; export const spec = { code: BIDDER_CODE, aliases: ALIASES, @@ -51,10 +51,9 @@ export const spec = { */ interpretResponse: function (serverResponse, bidRequest) { const bidResponses = []; - // loop through serverResponses { try { - serverResponse = serverResponse.body; - serverResponse.forEach((bidResponse) => { + const {body: {ads = []} = {}} = serverResponse; + ads.forEach((bidResponse) => { const bidResp = { requestId: bidResponse.bidId, cpm: bidResponse.cpm, @@ -73,6 +72,19 @@ export const spec = { utils.logError(e); } return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { + const pixels = []; + serverResponses.forEach(({body: {cm = {}} = {}}) => { + const {pixels: img = [], iframes: frm = []} = cm; + if (syncOptions.pixelEnabled) { + img.forEach((url) => pixels.push({type: 'image', url})); + } + if (syncOptions.iframeEnabled) { + frm.forEach((url) => pixels.push({type: 'iframe', url})); + } + }); + return pixels; } }; registerBidder(spec); diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 6d2e3059dc8..6298eac4448 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -3,7 +3,7 @@ import {spec} from 'modules/admixerBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; const BIDDER_CODE = 'admixer'; -const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.0.aspx'; +const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.1.aspx'; const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; describe('AdmixerAdapter', function () { @@ -78,33 +78,35 @@ describe('AdmixerAdapter', function () { describe('interpretResponse', function () { let response = { - body: [{ - 'currency': 'USD', - 'cpm': 6.210000, - 'ad': '
ad
', - 'width': 300, - 'height': 600, - 'creativeId': 'ccca3e5e-0c54-4761-9667-771322fbdffc', - 'ttl': 360, - 'netRevenue': false, - 'bidId': '5e4e763b6bc60b' - }] + body: { + ads: [{ + 'currency': 'USD', + 'cpm': 6.210000, + 'ad': '
ad
', + 'width': 300, + 'height': 600, + 'creativeId': 'ccca3e5e-0c54-4761-9667-771322fbdffc', + 'ttl': 360, + 'netRevenue': false, + 'bidId': '5e4e763b6bc60b' + }] + } }; it('should get correct bid response', function () { - const body = response.body; + const ads = response.body.ads; let expectedResponse = [ { - 'requestId': body[0].bidId, - 'cpm': body[0].cpm, - 'creativeId': body[0].creativeId, - 'width': body[0].width, - 'height': body[0].height, - 'ad': body[0].ad, + 'requestId': ads[0].bidId, + 'cpm': ads[0].cpm, + 'creativeId': ads[0].creativeId, + 'width': ads[0].width, + 'height': ads[0].height, + 'ad': ads[0].ad, 'vastUrl': undefined, - 'currency': body[0].currency, - 'netRevenue': body[0].netRevenue, - 'ttl': body[0].ttl, + 'currency': ads[0].currency, + 'netRevenue': ads[0].netRevenue, + 'ttl': ads[0].ttl, } ]; @@ -119,4 +121,34 @@ describe('AdmixerAdapter', function () { expect(result.length).to.equal(0); }); }); + + describe('getUserSyncs', function () { + let imgUrl = 'https://example.com/img1'; + let frmUrl = 'https://example.com/frm2'; + let responses = [{ + body: { + cm: { + pixels: [ + imgUrl + ], + iframes: [ + frmUrl + ], + } + } + }]; + + it('Returns valid values', function () { + let userSyncAll = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: true}, responses); + let userSyncImg = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: false}, responses); + let userSyncFrm = spec.getUserSyncs({pixelEnabled: false, iframeEnabled: true}, responses); + expect(userSyncAll).to.be.an('array').with.lengthOf(2); + expect(userSyncImg).to.be.an('array').with.lengthOf(1); + expect(userSyncImg[0].url).to.be.equal(imgUrl); + expect(userSyncImg[0].type).to.be.equal('image'); + expect(userSyncFrm).to.be.an('array').with.lengthOf(1); + expect(userSyncFrm[0].url).to.be.equal(frmUrl); + expect(userSyncFrm[0].type).to.be.equal('iframe'); + }); + }); }); From 939c455a469703f0c98d021fd70d3b851ae69ba7 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 25 Nov 2020 16:41:39 +0100 Subject: [PATCH 0406/1476] Add support for `backupOnly` option in mediaType video renderer (#5972) * Add support for `backupOnly` option in mediaType video renderer * Improve readability --- src/Renderer.js | 18 +++++++++++++++++- src/auction.js | 6 +++--- test/spec/auctionmanager_spec.js | 32 ++++++++++++++++++++++++++++++++ test/spec/renderer_spec.js | 30 ++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/Renderer.js b/src/Renderer.js index f073d97d052..7cedf278537 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -115,5 +115,21 @@ function isRendererPreferredFromAdUnit(adUnitCode) { const adUnit = find(adUnits, adUnit => { return adUnit.code === adUnitCode; }); - return !!(adUnit && adUnit.renderer && adUnit.renderer.url && adUnit.renderer.render && !(utils.isBoolean(adUnit.renderer.backupOnly) && adUnit.renderer.backupOnly)); + + if (!adUnit) { + return false + } + + // renderer defined at adUnit level + const adUnitRenderer = utils.deepAccess(adUnit, 'renderer'); + const hasValidAdUnitRenderer = !!(adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render); + + // renderer defined at adUnit.mediaTypes level + const mediaTypeRenderer = utils.deepAccess(adUnit, 'mediaTypes.video.renderer'); + const hasValidMediaTypeRenderer = !!(mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render) + + return !!( + (hasValidAdUnitRenderer && !(adUnitRenderer.backupOnly === true)) || + (hasValidMediaTypeRenderer && !(mediaTypeRenderer.backupOnly === true)) + ); } diff --git a/src/auction.js b/src/auction.js index 6285bfdd905..c94e3adc9a7 100644 --- a/src/auction.js +++ b/src/auction.js @@ -57,7 +57,7 @@ * @property {function(): void} callBids - sends requests to all adapters for bids */ -import {flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl, isBoolean} from './utils.js'; +import {flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl} from './utils.js'; import { getPriceBucketString } from './cpmBucketManager.js'; import { getNativeTargeting } from './native.js'; import { getCacheUrl, store } from './videoCache.js'; @@ -533,9 +533,9 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { var renderer = null; // the renderer for the mediaType takes precendence - if (mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render) { + if (mediaTypeRenderer && mediaTypeRenderer.url && !(mediaTypeRenderer.backupOnly === true && mediaTypeRenderer.render)) { renderer = mediaTypeRenderer; - } else if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly && isBoolean(adUnitRenderer.backupOnly) && bid.renderer)) { + } else if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly === true && bid.renderer)) { renderer = adUnitRenderer; } diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index d880ff0eaee..a50eba5e585 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -793,6 +793,38 @@ describe('auctionmanager.js', function () { assert.equal(addedBid.renderer.url, renderer.url); }); + it('installs bidder-defined renderer when onlyBackup is true in mediaTypes.video options ', function () { + const renderer = { + url: 'videoRenderer.js', + backupOnly: true, + render: (bid) => bid + }; + let myBid = mockBid(); + let bidRequest = mockBidRequest(myBid); + + bidRequest.bids[0] = { + ...bidRequest.bids[0], + mediaTypes: { + video: { + context: 'outstream', + renderer + } + } + }; + makeRequestsStub.returns([bidRequest]); + + myBid.mediaType = 'video'; + myBid.renderer = { + url: 'renderer.js', + render: sinon.spy() + }; + spec.interpretResponse.returns(myBid); + auction.callBids(); + + const addedBid = auction.getBidsReceived().pop(); + assert.strictEqual(addedBid.renderer.url, myBid.renderer.url); + }); + it('bid for a regular unit and a video unit', function() { let renderer = { url: 'renderer.js', diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js index 9bf551f35e8..dcca94396cd 100644 --- a/test/spec/renderer_spec.js +++ b/test/spec/renderer_spec.js @@ -155,6 +155,36 @@ describe('Renderer', function () { expect(loadExternalScript.called).to.be.true; }); + it('should load external script instead of publisher-defined one when backupOnly option is true in mediaTypes.video options', function() { + $$PREBID_GLOBAL$$.adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'], + playerSize: [[400, 300]], + renderer: { + url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', + backupOnly: true, + render: sinon.spy() + }, + } + } + }] + + let testRenderer = Renderer.install({ + url: 'https://httpbin.org/post', + config: { test: 'config1' }, + id: 1, + adUnitCode: 'video1' + + }); + testRenderer.setRender(() => {}) + + testRenderer.render() + expect(loadExternalScript.called).to.be.true; + }); + it('should call loadExternalScript() for script not defined on adUnit, only when .render() is called', function() { $$PREBID_GLOBAL$$.adUnits = [{ code: 'video1', From 18086e44fd33e921ef6ffe7ac8552d55c650fc35 Mon Sep 17 00:00:00 2001 From: mobfxoHB <74364234+mobfxoHB@users.noreply.github.com> Date: Mon, 30 Nov 2020 19:24:12 +0200 Subject: [PATCH 0407/1476] New mobfox prebid adapter (#5978) New mobfox prebid adapter --- modules/mobfoxpbBidAdapter.js | 99 ++++++ modules/mobfoxpbBidAdapter.md | 72 +++++ test/spec/modules/mobfoxpbBidAdapter_spec.js | 304 +++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 modules/mobfoxpbBidAdapter.js create mode 100644 modules/mobfoxpbBidAdapter.md create mode 100644 test/spec/modules/mobfoxpbBidAdapter_spec.js diff --git a/modules/mobfoxpbBidAdapter.js b/modules/mobfoxpbBidAdapter.js new file mode 100644 index 00000000000..c7e96b95179 --- /dev/null +++ b/modules/mobfoxpbBidAdapter.js @@ -0,0 +1,99 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'mobfoxpb'; +const AD_URL = 'https://bes.mobfox.com/?c=o&m=multi'; + +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); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + const winTop = utils.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] : '', + 'secure': 1, + '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 || {}, + }; + const mediaType = bid.mediaTypes + + if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { + placement.sizes = mediaType[BANNER].sizes; + placement.traffic = BANNER; + } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { + placement.wPlayer = mediaType[VIDEO].playerSize[0]; + placement.hPlayer = mediaType[VIDEO].playerSize[1]; + placement.traffic = VIDEO; + } else if (mediaType && mediaType[NATIVE]) { + placement.native = mediaType[NATIVE]; + placement.traffic = NATIVE; + } + 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)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/mobfoxpbBidAdapter.md b/modules/mobfoxpbBidAdapter.md new file mode 100644 index 00000000000..6eb549919d7 --- /dev/null +++ b/modules/mobfoxpbBidAdapter.md @@ -0,0 +1,72 @@ +# Overview + +``` +Module Name: mobfox Bidder Adapter +Module Type: mobfox Bidder Adapter +Maintainer: platform@mobfox.com +``` + +# Description + +Module that connects to mobfox demand sources + +# Test Parameters +``` + var adUnits = [ + { + code:'1', + mediaTypes:{ + banner: { + sizes: [[300, 250]], + } + }, + bids:[ + { + bidder: 'mobfoxpb', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids:[ + { + bidder: 'mobfoxpb', + params: { + placementId: 0 + } + } + ] + }, + { + code:'1', + mediaTypes:{ + native: { + title: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids:[ + { + bidder: 'mobfoxpb', + params: { + placementId: 0 + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/mobfoxpbBidAdapter_spec.js b/test/spec/modules/mobfoxpbBidAdapter_spec.js new file mode 100644 index 00000000000..a02d580ab88 --- /dev/null +++ b/test/spec/modules/mobfoxpbBidAdapter_spec.js @@ -0,0 +1,304 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/mobfoxpbBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; + +describe('MobfoxHBBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'mobfoxpb', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 783, + traffic: BANNER + } + }; + + 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://bes.mobfox.com/?c=o&m=multi'); + }); + 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'); + 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.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement.placementId).to.equal(783); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + 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'); + }); + 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' + }] + }; + 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'); + 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'); + }); + 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', + }] + }; + 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'); + 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'); + }); + 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; + }); + }); +}); From 4b3faf46277eabc28c078f3bd94d2ad950de5261 Mon Sep 17 00:00:00 2001 From: Avimobi <34269094+Avimobi@users.noreply.github.com> Date: Tue, 1 Dec 2020 00:52:22 +0530 Subject: [PATCH 0408/1476] Update adkernelBidAdapter.js (#5957) * Update adkernelBidAdapter.js description: Prebid adbite Bidder Adaptor Host: cpm.adbite.com * Update adkernelBidAdapter_spec.js --- modules/adkernelBidAdapter.js | 2 +- test/spec/modules/adkernelBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 0e9093b0f63..29990ef1c44 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 4015a56e82b..70789c4b933 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,7 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(7); + expect(spec.aliases).to.have.lengthOf(8); }); }); From a7beb57e691fea813e036ffb507c4a4b89b43923 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Mon, 30 Nov 2020 15:34:43 -0500 Subject: [PATCH 0409/1476] RP Analytics Adapter update for UTM KVs (#5998) * RP Analytics Adapter update to scan URL query params to match 'utm_' params, convert to KV object format and pass data along as fpkvs. Unique query aram KVs will be added to the fpkvs object; whereas, query params will overwrite both matching KVs defined in localstorage or setConfig * Update to attempt to fix CircleCi errors * Update to use utils parseQS * Minor change to attempt to pass Safari tests * Switching spec logic to mock parseQS function as opposed to the window.location * Another attempt to determine Safari failure * Reverting last change and modifying fpkvs * Added sort to test to correctly evaluate on Safari browser and reverted back to windowLocation mock * Update to switch logic to utilize reduce() --- modules/rubiconAnalyticsAdapter.js | 22 +++++ .../modules/rubiconAnalyticsAdapter_spec.js | 87 +++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 85b6596ba12..ff8cb7895b9 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -359,7 +359,29 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) { ]); } +/* + Filters and converts URL Params into an object and returns only KVs that match the 'utm_KEY' format +*/ +function getUtmParams() { + let search; + + try { + search = utils.parseQS(utils.getWindowLocation().search); + } catch (e) { + search = {}; + } + + return Object.keys(search).reduce((accum, param) => { + if (param.match(/utm_/)) { + accum[param.replace(/utm_/, '')] = search[param]; + } + return accum; + }, {}); +} + function getFpkvs() { + rubiConf.fpkvs = Object.assign((rubiConf.fpkvs || {}), getUtmParams()); + const isValid = rubiConf.fpkvs && typeof rubiConf.fpkvs === 'object' && Object.keys(rubiConf.fpkvs).every(key => typeof rubiConf.fpkvs[key] === 'string'); return isValid ? rubiConf.fpkvs : {}; } diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 9e343d07dd5..4891b8d3282 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1083,6 +1083,34 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(expectedMessage); }); + it('should use the query utm param rubicon kv value and pass updated kv and pvid when defined', function () { + sandbox.stub(utils, 'getWindowLocation').returns({'search': '?utm_source=other', 'pbjs_debug': 'true'}); + + config.setConfig({rubicon: { + fpkvs: { + source: 'fb', + link: 'email' + } + }}); + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8); + expectedMessage.fpkvs = [ + {key: 'source', value: 'other'}, + {key: 'link', value: 'email'} + ] + + message.fpkvs.sort((left, right) => left.key < right.key); + expectedMessage.fpkvs.sort((left, right) => left.key < right.key); + + expect(message).to.deep.equal(expectedMessage); + }); + it('should pick up existing localStorage and use its values', function () { // set some localStorage let inputlocalStorage = { @@ -1135,6 +1163,65 @@ describe('rubicon analytics adapter', function () { }); }); + it('should overwrite matching localstorge value and use its remaining values', function () { + sandbox.stub(utils, 'getWindowLocation').returns({'search': '?utm_source=fb&utm_click=dog'}); + + // set some localStorage + let inputlocalStorage = { + id: '987654', + start: 1519766113781, // 15 mins before "now" + expires: 1519787713781, // six hours later + lastSeen: 1519766113781, + fpkvs: { source: 'tw', link: 'email' } + }; + getDataFromLocalStorageStub.withArgs('rpaSession').returns(btoa(JSON.stringify(inputlocalStorage))); + + config.setConfig({rubicon: { + fpkvs: { + link: 'email' // should merge this with what is in the localStorage! + } + }}); + performStandardAuction(); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.session = { + id: '987654', + start: 1519766113781, + expires: 1519787713781, + pvid: expectedPvid + } + expectedMessage.fpkvs = [ + {key: 'source', value: 'fb'}, + {key: 'link', value: 'email'}, + {key: 'click', value: 'dog'} + ] + + message.fpkvs.sort((left, right) => left.key < right.key); + expectedMessage.fpkvs.sort((left, right) => left.key < right.key); + + expect(message).to.deep.equal(expectedMessage); + + let calledWith; + try { + calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1])); + } catch (e) { + calledWith = {}; + } + + expect(calledWith).to.deep.equal({ + id: '987654', // should have stayed same + start: 1519766113781, // should have stayed same + expires: 1519787713781, // should have stayed same + lastSeen: 1519767013781, // lastSeen updated to our "now" + fpkvs: { source: 'fb', link: 'email', click: 'dog' }, // link merged in + pvid: expectedPvid // new pvid stored + }); + }); + it('should throw out session if lastSeen > 30 mins ago and create new one', function () { // set some localStorage let inputlocalStorage = { From 57ef24aef6ea7bce3f339843f12c606e079d160c Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 30 Nov 2020 21:38:29 +0100 Subject: [PATCH 0410/1476] remove pubcommon optout from user id module checks (#5994) --- modules/userId/index.js | 7 ++++--- test/spec/modules/userId_spec.js | 15 +++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 0923a92f516..f063fbc973b 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -134,6 +134,7 @@ const CONSENT_DATA_COOKIE_STORAGE_CONFIG = { name: '_pbjs_userid_consent_data', expires: 30 // 30 days expiration, which should match how often consent is refreshed by CMPs }; +export const PBJS_USER_ID_OPTOUT_NAME = '_pbjs_id_optout'; export const coreStorage = getCoreStorageManager('userid'); /** @type {string[]} */ @@ -701,15 +702,15 @@ export function init(config) { ].filter(i => i !== null); // exit immediately if opt out cookie or local storage keys exists. - if (validStorageTypes.indexOf(COOKIE) !== -1 && (coreStorage.getCookie('_pbjs_id_optout') || coreStorage.getCookie('_pubcid_optout'))) { + if (validStorageTypes.indexOf(COOKIE) !== -1 && coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) { utils.logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); return; } - // _pubcid_optout is checked for compatibility with pubCommonId - if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (coreStorage.getDataFromLocalStorage('_pbjs_id_optout') || coreStorage.getDataFromLocalStorage('_pubcid_optout'))) { + if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) { utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); return; } + // listen for config userSyncs to be set config.getConfig(conf => { // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 887e1f45640..981ebb5f50e 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -7,7 +7,8 @@ import { setStoredConsentData, setStoredValue, setSubmoduleRegistry, - syncDelay + syncDelay, + PBJS_USER_ID_OPTOUT_NAME } from 'modules/userId/index.js'; import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; @@ -91,9 +92,7 @@ describe('User ID', function () { } before(function () { - coreStorage.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); - localStorage.removeItem('_pbjs_id_optout'); - localStorage.removeItem('_pubcid_optout'); + localStorage.removeItem(PBJS_USER_ID_OPTOUT_NAME); }); beforeEach(function () { @@ -413,7 +412,7 @@ describe('User ID', function () { describe('Opt out', function () { before(function () { - coreStorage.setCookie('_pbjs_id_optout', '1', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie(PBJS_USER_ID_OPTOUT_NAME, '1', (new Date(Date.now() + 5000).toUTCString())); }); beforeEach(function () { @@ -422,16 +421,12 @@ describe('User ID', function () { afterEach(function () { // removed cookie - coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie(PBJS_USER_ID_OPTOUT_NAME, '', EXPIRED_COOKIE_DATE); $$PREBID_GLOBAL$$.requestBids.removeAll(); utils.logInfo.restore(); config.resetConfig(); }); - after(function () { - coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); - }); - it('fails initialization if opt out cookie exists', function () { setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); From 4638dffcb8996954dd0cd947c19c8eb56e614763 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 30 Nov 2020 22:19:11 +0100 Subject: [PATCH 0411/1476] ID5 ID module - pass gdpr and usp parameters in body instead of querystring (#6032) * move gdpr and usp parameters to the body rather than the querystring of calls to ID5 * remove unnecessary variables --- modules/id5IdSystem.js | 7 ++++--- test/spec/modules/id5IdSystem_spec.js | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index a1596e96fcd..7033a71d015 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -78,13 +78,13 @@ export const id5IdSubmodule = { 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 gdprConsentString = hasGdpr ? consentData.consentString : ''; - const usp = uspDataHandler.getConsentData() || ''; - const url = `https://id5-sync.com/g/v2/${config.params.partner}.json?gdpr_consent=${gdprConsentString}&gdpr=${hasGdpr}&us_privacy=${usp}`; const referer = getRefererInfo(); const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : getLegacyCookieSignature(); const data = { + 'gdpr': hasGdpr, + 'gdpr_consent': hasGdpr ? consentData.consentString : '', 'partner': config.params.partner, 'nbPage': incrementNb(config.params.partner), 'o': 'pbjs', @@ -94,6 +94,7 @@ export const id5IdSubmodule = { 's': signature, 'top': referer.reachedTop ? 1 : 0, 'u': referer.stack[0] || window.location.href, + 'us_privacy': uspDataHandler.getConsentData() || '', 'v': '$prebid.version$' }; diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index adffca6dbe5..845cf7fa010 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -143,6 +143,9 @@ describe('ID5 ID System', function() { expect(requestBody.s).to.eq(''); expect(requestBody.provider).to.eq(''); expect(requestBody.v).to.eq('$prebid.version$'); + expect(requestBody.gdpr).to.exist; + expect(requestBody.gdpr_consent).to.exist + expect(requestBody.us_privacy).to.exist; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; From 6f147c6ab3e072b9b052ef390be757e1f9fb0383 Mon Sep 17 00:00:00 2001 From: Prebid Manager <49466873+Prebid-Manager@users.noreply.github.com> Date: Tue, 1 Dec 2020 03:51:20 +0000 Subject: [PATCH 0412/1476] Prebidmanager analytics adapter: fix console error when utm is null and collect page info (#6002) * Fix PrebidManager analytics console error when utm data is null * collect pageInfo in PrebidManager analytics adapter * minor edit + add test for pageInfo in PrebidManager analytics adapter Co-authored-by: apuzanova --- modules/prebidmanagerAnalyticsAdapter.js | 13 ++++++++++- .../prebidmanagerAnalyticsAdapter_spec.js | 23 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index b98ca864cd5..994ce4989f5 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -83,7 +83,7 @@ function collectUtmTagData() { if (newUtm === false) { utmTags.forEach(function (utmKey) { let itemValue = localStorage.getItem(`pm_${utmKey}`); - if (itemValue.length !== 0) { + if (itemValue && itemValue.length !== 0) { pmUtmTags[utmKey] = itemValue; } }); @@ -99,6 +99,16 @@ function collectUtmTagData() { return pmUtmTags; } +function collectPageInfo() { + const pageInfo = { + domain: window.location.hostname, + } + if (document.referrer) { + pageInfo.referrerDomain = utils.parseUrl(document.referrer).hostname; + } + return pageInfo; +} + function flush() { if (!pmAnalyticsEnabled) { return; @@ -111,6 +121,7 @@ function flush() { bundleId: initOptions.bundleId, events: _eventQueue, utmTags: collectUtmTagData(), + pageInfo: collectPageInfo(), }; ajax( diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index ce97789fe3e..ef7cb2bbe3b 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -1,6 +1,8 @@ import prebidmanagerAnalytics from 'modules/prebidmanagerAnalyticsAdapter.js'; import {expect} from 'chai'; import {server} from 'test/mocks/xhr.js'; +import * as utils from 'src/utils.js'; + let events = require('src/events'); let constants = require('src/constants.json'); @@ -98,7 +100,7 @@ describe('Prebid Manager Analytics Adapter', function () { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(prebidmanagerAnalytics.track, 7); + sinon.assert.callCount(prebidmanagerAnalytics.track, 6); }); }); @@ -135,4 +137,23 @@ describe('Prebid Manager Analytics Adapter', function () { expect(pmEvents.utmTags.utm_content).to.equal(''); }); }); + + describe('build page info', function () { + afterEach(function () { + prebidmanagerAnalytics.disableAnalytics() + }); + it('should build page info', function () { + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); + + expect(pmEvents.pageInfo.domain).to.equal(window.location.hostname); + expect(pmEvents.pageInfo.referrerDomain).to.equal(utils.parseUrl(document.referrer).hostname); + }); + }); }); From d5f228bd312d657e1f4881ebb51114696846bbf4 Mon Sep 17 00:00:00 2001 From: Nicholas Llerandi Date: Mon, 30 Nov 2020 22:56:03 -0500 Subject: [PATCH 0413/1476] Rp adapter unit tests - userid mod support (#5985) * ID5 support * ID5 support; tests passed * no-console:error * UserId catchall support * minor revision --- test/spec/modules/rubiconBidAdapter_spec.js | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 6659c281c33..b1ef02a6369 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1221,6 +1221,43 @@ describe('the rubicon adapter', function () { }); }); + describe('ID5 support', function () { + it('should send ID5 id when userIdAsEids contains ID5', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + id5id: { + uid: '11111', + ext: { + linkType: '22222' + } + } + }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_id5-sync.com']).to.equal('11111^1^22222'); + }); + }); + + describe('UserID catchall support', function () { + it('should send user id with generic format', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + // Hardcoding userIdAsEids since createEidsArray returns empty array if source not found in eids.js + clonedBid.userIdAsEids = [{ + source: 'catchall', + uids: [{ + id: '11111', + atype: 2 + }] + }] + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_catchall']).to.equal('11111^2'); + }); + }); + describe('Config user.id support', function () { it('should send ppuid when config defines user.id', function () { config.setConfig({user: {id: '123'}}); From 0820790bbc1509b9c964f15d1487b5cff990cef2 Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Tue, 1 Dec 2020 11:33:33 +0100 Subject: [PATCH 0414/1476] Read floor data in analytic + support for Criteo Id (#6003) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter * Collect info on ad units receiving any valid bid * Support for ID5 Pass metadata from adapter * Typo in test + eids on wrong level * Fix for Prebid 3.0 * Fix get referer * http -> https in tests * Native support * Read sizes from mediatype.banner * Revert accidental commit * Support native data collection + minor refactorings * Set analytics endpoint * Support for app parameters * Fix issue where adunits with bids were not counted on reload * Send debug info from adapter to external debugger * SChain support * Send GDPR data in analytics request * video support Video support * Report back floor via analytic * Send auction id and adunit/bidder connection id * Criteo id support * Updated example * livewrapped Analytics Adapter info file --- modules/livewrappedAnalyticsAdapter.js | 108 +++++++++++--- modules/livewrappedAnalyticsAdapter.md | 22 +++ modules/livewrappedBidAdapter.js | 7 +- modules/livewrappedBidAdapter.md | 2 +- .../livewrappedAnalyticsAdapter_spec.js | 132 ++++++++++++++++-- .../modules/livewrappedBidAdapter_spec.js | 21 ++- 6 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 modules/livewrappedAnalyticsAdapter.md diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 9f571cb5ae0..a872a709ec9 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -36,6 +36,15 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE args.bids.forEach(function(bidRequest) { cache.auctions[args.auctionId].gdprApplies = args.gdprConsent ? args.gdprConsent.gdprApplies : undefined; cache.auctions[args.auctionId].gdprConsent = args.gdprConsent ? args.gdprConsent.consentString : undefined; + let lwFloor; + + if (bidRequest.lwflr) { + lwFloor = bidRequest.lwflr.flr; + + let buyerFloor = bidRequest.lwflr.bflrs ? bidRequest.lwflr.bflrs[bidRequest.bidder] : undefined; + + lwFloor = buyerFloor || lwFloor; + } cache.auctions[args.auctionId].bids[bidRequest.bidId] = { bidder: bidRequest.bidder, @@ -45,7 +54,11 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE timeout: false, sendStatus: 0, readyToSend: 0, - start: args.start + start: args.start, + lwFloor: lwFloor, + floorData: bidRequest.floorData, + auc: bidRequest.auc, + buc: bidRequest.buc } utils.logInfo(bidRequest); @@ -123,10 +136,11 @@ livewrappedAnalyticsAdapter.sendEvents = function() { var events = { publisherId: initOptions.publisherId, gdpr: sentRequests.gdpr, + auctionIds: sentRequests.auctionIds, requests: sentRequests.sentRequests, - responses: getResponses(), - wins: getWins(), - timeouts: getTimeouts(), + responses: getResponses(sentRequests.gdpr, sentRequests.auctionIds), + wins: getWins(sentRequests.gdpr, sentRequests.auctionIds), + timeouts: getTimeouts(sentRequests.auctionIds), bidAdUnits: getbidAdUnits(), rcv: getAdblockerRecovered() }; @@ -150,20 +164,12 @@ function getAdblockerRecovered() { function getSentRequests() { var sentRequests = []; var gdpr = []; + var auctionIds = []; Object.keys(cache.auctions).forEach(auctionId => { let auction = cache.auctions[auctionId]; - var gdprPos = 0; - for (gdprPos = 0; gdprPos < gdpr.length; gdprPos++) { - if (gdpr[gdprPos].gdprApplies == auction.gdprApplies && - gdpr[gdprPos].gdprConsent == auction.gdprConsent) { - break; - } - } - - if (gdprPos == gdpr.length) { - gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; - } + let gdprPos = getGdprPos(gdpr, auction); + let auctionIdPos = getAuctionIdPos(auctionIds, auctionId); Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let bid = auction.bids[bidId]; @@ -174,21 +180,27 @@ function getSentRequests() { timeStamp: auction.timeStamp, adUnit: bid.adUnit, bidder: bid.bidder, - gdpr: gdprPos + gdpr: gdprPos, + floor: bid.lwFloor, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc }); } }); }); - return {gdpr: gdpr, sentRequests: sentRequests}; + return {gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests}; } -function getResponses() { +function getResponses(gdpr, auctionIds) { var responses = []; Object.keys(cache.auctions).forEach(auctionId => { Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; + let gdprPos = getGdprPos(gdpr, auction); + let auctionIdPos = getAuctionIdPos(auctionIds, auctionId) let bid = auction.bids[bidId]; if (bid.readyToSend && !(bid.sendStatus & RESPONSESENT) && !bid.timeout) { bid.sendStatus |= RESPONSESENT; @@ -202,7 +214,13 @@ function getResponses() { cpm: bid.cpm, ttr: bid.ttr, IsBid: bid.isBid, - mediaType: bid.mediaType + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.floorData ? bid.floorData.floorValue : bid.lwFloor, + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc }); } }); @@ -211,13 +229,16 @@ function getResponses() { return responses; } -function getWins() { +function getWins(gdpr, auctionIds) { var wins = []; Object.keys(cache.auctions).forEach(auctionId => { Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; + let gdprPos = getGdprPos(gdpr, auction); + let auctionIdPos = getAuctionIdPos(auctionIds, auctionId); let bid = auction.bids[bidId]; + if (!(bid.sendStatus & WINSENT) && bid.won) { bid.sendStatus |= WINSENT; @@ -228,7 +249,13 @@ function getWins() { width: bid.width, height: bid.height, cpm: bid.cpm, - mediaType: bid.mediaType + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.floorData ? bid.floorData.floorValue : bid.lwFloor, + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc }); } }); @@ -237,10 +264,42 @@ function getWins() { return wins; } -function getTimeouts() { +function getGdprPos(gdpr, auction) { + var gdprPos = 0; + for (gdprPos = 0; gdprPos < gdpr.length; gdprPos++) { + if (gdpr[gdprPos].gdprApplies == auction.gdprApplies && + gdpr[gdprPos].gdprConsent == auction.gdprConsent) { + break; + } + } + + if (gdprPos == gdpr.length) { + gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; + } + + return gdprPos; +} + +function getAuctionIdPos(auctionIds, auctionId) { + var auctionIdPos = 0; + for (auctionIdPos = 0; auctionIdPos < auctionIds.length; auctionIdPos++) { + if (auctionIds[auctionIdPos] == auctionId) { + break; + } + } + + if (auctionIdPos == auctionIds.length) { + auctionIds[auctionIdPos] = auctionId; + } + + return auctionIdPos; +} + +function getTimeouts(auctionIds) { var timeouts = []; Object.keys(cache.auctions).forEach(auctionId => { + let auctionIdPos = getAuctionIdPos(auctionIds, auctionId); Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; let bid = auction.bids[bidId]; @@ -250,7 +309,10 @@ function getTimeouts() { timeouts.push({ bidder: bid.bidder, adUnit: bid.adUnit, - timeStamp: auction.timeStamp + timeStamp: auction.timeStamp, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc }); } }); diff --git a/modules/livewrappedAnalyticsAdapter.md b/modules/livewrappedAnalyticsAdapter.md new file mode 100644 index 00000000000..de4f352aa19 --- /dev/null +++ b/modules/livewrappedAnalyticsAdapter.md @@ -0,0 +1,22 @@ +# Overview +Module Name: Livewrapped Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: info@livewrapped.com + +# Description + +Analytics adapter for Livewrapped AB. In order to use the adapter, please contact Livewrapped AB. + +# Test Parameters + +``` +{ + provider: 'livewrapped', + options : { + publisherId: "64c01620-fa98-4936-9794-6001d8ebfdb0" + } +} + +``` diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 0a5464fd21f..512fc775d95 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -221,6 +221,10 @@ function bidToAdRequest(bid) { options: bid.params.options }; + if (bid.auc !== undefined) { + adRequest.auc = bid.auc; + } + adRequest.native = utils.deepAccess(bid, 'mediaTypes.native'); adRequest.video = utils.deepAccess(bid, 'mediaTypes.video'); @@ -276,8 +280,9 @@ function handleEids(bidRequests) { let eids = []; const bidRequest = bidRequests[0]; if (bidRequest && bidRequest.userId) { - AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcommon', 1); // Also add this to eids + AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcid.org', 1); // Also add this to eids AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 1); + AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteoId`), 'criteo.com', 1); } if (eids.length > 0) { return {user: {ext: {eids}}}; diff --git a/modules/livewrappedBidAdapter.md b/modules/livewrappedBidAdapter.md index c5d867af8fe..10fc2a4778a 100644 --- a/modules/livewrappedBidAdapter.md +++ b/modules/livewrappedBidAdapter.md @@ -20,7 +20,7 @@ var adUnits = [ bids: [{ bidder: 'livewrapped', params: { - adUnitId: '6A32352E-BC17-4B94-B2A7-5BF1724417D7' + adUnitId: 'D801852A-681F-11E8-86A7-0A44794250D4' } }] } diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js index c723f589fa0..ba9430e0b95 100644 --- a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -121,6 +121,7 @@ const MOCK = { const ANALYTICS_MESSAGE = { publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', gdpr: [{}], + auctionIds: ['25c6d7f5-699a-4bfc-87c9-996f915341fa'], bidAdUnits: [ { adUnit: 'panorama_d_1', @@ -136,19 +137,22 @@ const ANALYTICS_MESSAGE = { adUnit: 'panorama_d_1', bidder: 'livewrapped', timeStamp: 1519149562216, - gdpr: 0 + gdpr: 0, + auctionId: 0 }, { adUnit: 'box_d_1', bidder: 'livewrapped', timeStamp: 1519149562216, - gdpr: 0 + gdpr: 0, + auctionId: 0 }, { adUnit: 'box_d_2', bidder: 'livewrapped', timeStamp: 1519149562216, - gdpr: 0 + gdpr: 0, + auctionId: 0 } ], responses: [ @@ -161,7 +165,9 @@ const ANALYTICS_MESSAGE = { cpm: 1.1, ttr: 200, IsBid: true, - mediaType: 1 + mediaType: 1, + gdpr: 0, + auctionId: 0 }, { timeStamp: 1519149562216, @@ -172,14 +178,18 @@ const ANALYTICS_MESSAGE = { cpm: 2.2, ttr: 300, IsBid: true, - mediaType: 1 + mediaType: 1, + gdpr: 0, + auctionId: 0 }, { timeStamp: 1519149562216, adUnit: 'box_d_2', bidder: 'livewrapped', ttr: 200, - IsBid: false + IsBid: false, + gdpr: 0, + auctionId: 0 } ], timeouts: [], @@ -191,7 +201,9 @@ const ANALYTICS_MESSAGE = { width: 980, height: 240, cpm: 1.1, - mediaType: 1 + mediaType: 1, + gdpr: 0, + auctionId: 0 }, { timeStamp: 1519149562216, @@ -200,7 +212,9 @@ const ANALYTICS_MESSAGE = { width: 300, height: 250, cpm: 2.2, - mediaType: 1 + mediaType: 1, + gdpr: 0, + auctionId: 0 } ] }; @@ -351,7 +365,9 @@ describe('Livewrapped analytics adapter', function () { } }, ); - events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); + + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_WON, MOCK.BID_WON[0]); events.emit(AUCTION_END, MOCK.AUCTION_END); clock.tick(BID_WON_TIMEOUT + 1000); @@ -366,6 +382,104 @@ describe('Livewrapped analytics adapter', function () { expect(message.requests.length).to.equal(2); expect(message.requests[0].gdpr).to.equal(0); expect(message.requests[1].gdpr).to.equal(0); + + expect(message.responses.length).to.equal(1); + expect(message.responses[0].gdpr).to.equal(0); + + expect(message.wins.length).to.equal(1); + expect(message.wins[0].gdpr).to.equal(0); + }); + + it('should forward floor data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'livewrapped', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'livewrapped', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ecff0db240757', + 'floorData': { + 'floorValue': 1.1, + 'floorCurrency': 'SEK' + } + } + ], + 'start': 1519149562216 + }); + + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + + expect(message.responses.length).to.equal(1); + expect(message.responses[0].floor).to.equal(1.1); + expect(message.responses[0].floorCur).to.equal('SEK'); + + expect(message.wins.length).to.equal(1); + expect(message.wins[0].floor).to.equal(1.1); + expect(message.wins[0].floorCur).to.equal('SEK'); + }); + + it('should forward Livewrapped floor data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'livewrapped', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'livewrapped', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ecff0db240757', + 'lwflr': { + 'flr': 1.1 + } + }, + { + 'bidder': 'livewrapped', + 'adUnitCode': 'box_d_1', + 'bidId': '3ecff0db240757', + 'lwflr': { + 'flr': 1.1, + 'bflrs': {'livewrapped': 2.2} + } + } + ], + 'start': 1519149562216 + }); + + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + + expect(message.responses.length).to.equal(2); + expect(message.responses[0].floor).to.equal(1.1); + expect(message.responses[1].floor).to.equal(2.2); + + expect(message.wins.length).to.equal(2); + expect(message.wins[0].floor).to.equal(1.1); + expect(message.wins[1].floor).to.equal(2.2); }); }); diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 2d5ba3f48df..7983e8fbb0b 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -811,7 +811,7 @@ describe('Livewrapped adapter tests', function () { let data = JSON.parse(result.data); expect(data.rtbData.user.ext.eids).to.deep.equal([{ - 'source': 'pubcommon', + 'source': 'pubcid.org', 'uids': [{ 'id': 'publisher-common-id', 'atype': 1 @@ -819,6 +819,25 @@ describe('Livewrapped adapter tests', function () { }]); }); + it('should make use of criteoId if available', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + testbidRequest.bids[0].userId = {}; + testbidRequest.bids[0].userId.criteoId = 'criteo-id'; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(data.rtbData.user.ext.eids).to.deep.equal([{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'criteo-id', + 'atype': 1 + }] + }]); + }); + it('should send schain object if available', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); From 24cae189046b52d753ce5720a1701bd3ed7125f1 Mon Sep 17 00:00:00 2001 From: Galphimbl Date: Tue, 1 Dec 2020 16:43:31 +0200 Subject: [PATCH 0415/1476] Admixer adapter update - add deal id (#6042) * Migrating to Prebid 1.0 * Migrating to Prebid 1.0 * Fix spec * add gdpr and usp * remove changes in gdpr_hello_world.html * Update gdpr_hello_world.html add spaces * add user syncs * remove comments * tests * add-deal-id Co-authored-by: atkachov --- modules/admixerBidAdapter.js | 1 + test/spec/modules/admixerBidAdapter_spec.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index 3bb392538ff..405dd81cc8c 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -65,6 +65,7 @@ export const spec = { netRevenue: bidResponse.netRevenue, currency: bidResponse.currency, vastUrl: bidResponse.vastUrl, + dealId: bidResponse.dealId, }; bidResponses.push(bidResp); }); diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 6298eac4448..dfadf1f95d5 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -88,7 +88,8 @@ describe('AdmixerAdapter', function () { 'creativeId': 'ccca3e5e-0c54-4761-9667-771322fbdffc', 'ttl': 360, 'netRevenue': false, - 'bidId': '5e4e763b6bc60b' + 'bidId': '5e4e763b6bc60b', + 'dealId': 'asd123', }] } }; @@ -107,6 +108,7 @@ describe('AdmixerAdapter', function () { 'currency': ads[0].currency, 'netRevenue': ads[0].netRevenue, 'ttl': ads[0].ttl, + 'dealId': ads[0].dealId, } ]; From 2609a1b804394fadd05bb99cd3c314e326f5796a Mon Sep 17 00:00:00 2001 From: Vladimir Fedoseev Date: Tue, 1 Dec 2020 16:03:51 +0100 Subject: [PATCH 0416/1476] FID-251: Update Reconciliation RTD Provider to 2.1 (#6037) --- .../reconciliationRtdProvider_example.html | 5 +- modules/reconciliationRtdProvider.js | 27 +------- .../modules/reconciliationRtdProvider_spec.js | 66 ++++++++----------- 3 files changed, 34 insertions(+), 64 deletions(-) diff --git a/integrationExamples/gpt/reconciliationRtdProvider_example.html b/integrationExamples/gpt/reconciliationRtdProvider_example.html index af414e0b055..4f9b663c22d 100644 --- a/integrationExamples/gpt/reconciliationRtdProvider_example.html +++ b/integrationExamples/gpt/reconciliationRtdProvider_example.html @@ -82,8 +82,9 @@ `); adSlotIframe.contentDocument.close(); setTimeout(() => { - expect(trackGetStub.calledOnce).to.be.true; - expect(trackGetStub.getCalls()[0].args[0]).to.eql('https://confirm.fiduciadlt.com/imp'); - expect(trackGetStub.getCalls()[0].args[1].adUnitId).to.eql('/reconciliationAdunit'); - expect(trackGetStub.getCalls()[0].args[1].adDeliveryId).to.eql('12345'); - expect(trackGetStub.getCalls()[0].args[1].sourceMemberId).to.eql('test_member_id'); ; - expect(trackGetStub.getCalls()[0].args[1].sourceImpressionId).to.eql('123'); ; - expect(trackGetStub.getCalls()[0].args[1].publisherMemberId).to.eql('test_prebid_publisher'); + expect(trackPostStub.calledOnce).to.be.true; + expect(trackPostStub.getCalls()[0].args[0]).to.eql('https://confirm.fiduciadlt.com/pimp'); + expect(trackPostStub.getCalls()[0].args[1].adUnitId).to.eql('/reconciliationAdunit'); + expect(trackPostStub.getCalls()[0].args[1].adDeliveryId).to.eql('12345'); + expect(trackPostStub.getCalls()[0].args[1].tagOwnerMemberId).to.eql('test_member_id'); ; + expect(trackPostStub.getCalls()[0].args[1].dataSources.length).to.eql(1); + expect(trackPostStub.getCalls()[0].args[1].dataRecipients.length).to.eql(2); + expect(trackPostStub.getCalls()[0].args[1].publisherMemberId).to.eql('test_prebid_publisher'); done(); }, 100); }); From 6963bb33e01d6809fb6a641a9094fdf19bbe38d1 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Tue, 1 Dec 2020 18:22:59 +0300 Subject: [PATCH 0417/1476] grid Bid Adapter: Fix empty bidfloor (#6031) * 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 --- modules/gridBidAdapter.js | 15 +++++++++------ test/spec/modules/gridBidAdapter_spec.js | 8 +------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index db1402ea9ad..3f9d4fba31d 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -72,11 +72,12 @@ export const spec = { if (!userId) { userId = bid.userId; } - const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, rtd} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); } + const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { if (!jwpseg && jwTargeting.segments) { @@ -91,10 +92,13 @@ export const spec = { tagid: uid.toString(), ext: { divid: adUnitCode - }, - bidfloor: _getFloor(mediaTypes || {}, bidFloor, bid) + } }; + if (bidFloor) { + impObj.bidfloor = bidFloor; + } + if (!mediaTypes || mediaTypes[BANNER]) { const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); if (banner) { @@ -323,13 +327,12 @@ export const spec = { /** * Gets bidfloor * @param {Object} mediaTypes - * @param {Number} bidfloor * @param {Object} bid * @returns {Number} floor */ -function _getFloor (mediaTypes, bidfloor, bid) { +function _getFloor (mediaTypes, bid) { const curMediaType = mediaTypes.video ? 'video' : 'banner'; - let floor = bidfloor || 0; + let floor = bid.params.bidFloor || 0; if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 640bfda1fee..e884df40c5e 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -173,7 +173,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -211,7 +210,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -221,7 +219,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, - 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -259,7 +256,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -269,7 +265,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, - 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -279,7 +274,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[3].bidId, 'tagid': bidRequests[3].params.uid, 'ext': {'divid': bidRequests[3].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 728, 'h': 90, @@ -460,7 +454,7 @@ describe('TheMediaGrid Adapter', function () { 'floor': 1.50 }; const bidRequest = Object.assign({ - getFloor: _ => { + getFloor: (_) => { return floorTestData; } }, bidRequests[1]); From 804d76c03ff9b6d55acaeb85a0072e437b67ccfb Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Tue, 1 Dec 2020 11:38:49 -0500 Subject: [PATCH 0418/1476] Add fabrick to eids file (#6022) * Add entry of fabrickId in Eids #6021 * Add entry of fabrickId in Eids #6021 * Add entry of fabrickId in Eids #6021 * Add entry of fabrickId in Eids #6021 Co-authored-by: Anderson, Ben --- modules/userId/eids.js | 6 ++++++ modules/userId/eids.md | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 8118607fbde..2c627416341 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -162,6 +162,12 @@ const USER_IDS_CONFIG = { 'vmuid': { source: 'verizonmedia.com', atype: 1 + }, + + // Neustar Fabrick + 'fabrickId': { + source: 'neustar.biz', + atype: 1 } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 3e51eff3165..0cf9b6d2d22 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -21,6 +21,14 @@ userIdAsEids = [ }] }, + { + source: 'neustar.biz', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { source: 'id5-sync.com', uids: [{ From 1739fafdb35be2717236bbdd028302e97c976ec2 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Tue, 1 Dec 2020 16:37:43 -0500 Subject: [PATCH 0419/1476] Added pubProvidedIdSystem to .submodules.json (#6056) Seemed to be missing, so added it. --- modules/.submodules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/.submodules.json b/modules/.submodules.json index 36ead7df888..4e02391129a 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -18,7 +18,8 @@ "quantcastIdSystem", "idxIdSystem", "fabrickIdSystem", - "verizonMediaIdSystem" + "verizonMediaIdSystem", + "pubProvidedIdSystem" ], "adpod": [ "freeWheelAdserverVideo", From 30fa056a74652f2a6c0bf6a279b36e7c35a46f88 Mon Sep 17 00:00:00 2001 From: Krushmedia <71434282+Krushmedia@users.noreply.github.com> Date: Wed, 2 Dec 2020 01:22:01 +0200 Subject: [PATCH 0420/1476] Add getUserSync implementation into adapter (#6052) * Add getUserSync implementation into adapter --- modules/krushmediaBidAdapter.js | 19 ++++++++++++++ .../spec/modules/krushmediaBidAdapter_spec.js | 25 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index f70500cc101..de1cce503e3 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -4,6 +4,7 @@ import * as utils from '../src/utils.js'; const BIDDER_CODE = 'krushmedia'; const AD_URL = 'https://ads4.krushmedia.com/?c=rtb&m=hb'; +const SYNC_URL = 'https://cs.krushmedia.com/html?src=pbjs' function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -99,6 +100,24 @@ export const spec = { } return response; }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncUrl = SYNC_URL + 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}`; + } + return [{ + type: 'iframe', + url: syncUrl + }]; + } }; registerBidder(spec); diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index 2673627bc6d..3af9ed64c43 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -301,4 +301,29 @@ describe('KrushmediabBidAdapter', function () { 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('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&gdpr=1&gdpr_consent=ALL') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1NNN' + }); + 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('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&ccpa_consent=1NNN') + }); + }); }); From a52e943192d039fd13184fbe2118db9d5a4fe06a Mon Sep 17 00:00:00 2001 From: Slind14 Date: Wed, 2 Dec 2020 13:38:09 +0100 Subject: [PATCH 0421/1476] .babelrc.js - changed IE target from 10 to 11 (#6035) IE 10 support was dropped a long time ago --- .babelrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.babelrc.js b/.babelrc.js index bece57ec4a5..2fa95d0716e 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -20,7 +20,7 @@ module.exports = { "safari >=8", "edge >= 14", "ff >= 57", - "ie >= 10", + "ie >= 11", "ios >= 8" ] } From ca452445ff8c43f05e1b2d686a99f5e558a9f07b Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Wed, 2 Dec 2020 06:22:09 -0800 Subject: [PATCH 0422/1476] 6060 Fix for: Changing globalVarName causes gulp serve tests to fail (#6069) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * fixed the breaking test case when globalVarName is changed --- test/spec/modules/richaudienceBidAdapter_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 1c710c46ea2..bdf50f2d7f5 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -348,7 +348,7 @@ describe('Richaudience adapter tests', function () { }); describe('UID test', function () { - pbjs.setConfig({ + config.setConfig({ consentManagement: { cmpApi: 'iab', timeout: 5000, @@ -739,7 +739,7 @@ describe('Richaudience adapter tests', function () { }, [], {consentString: '', gdprApplies: true}); expect(syncs).to.have.lengthOf(0); - pbjs.setConfig({ + config.setConfig({ consentManagement: { cmpApi: 'iab', timeout: 5000, From 03ed22133e4ebe8db68ff09bfe59e6af418e421c Mon Sep 17 00:00:00 2001 From: steve-a-districtm <56413795+steve-a-districtm@users.noreply.github.com> Date: Wed, 2 Dec 2020 09:57:13 -0500 Subject: [PATCH 0423/1476] update ttl value (#6041) * allow users to be sent to dmx even when gdpr is configured in prebid * Change default ttl value Change default ttl value for banner and video Co-authored-by: Menelik Tucker Co-authored-by: Tucker <72400387+MenelikTucker-districtm@users.noreply.github.com> --- modules/districtmDMXBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index 845cff460ee..60f6b9b64b1 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -39,9 +39,11 @@ export const spec = { nBid.requestId = nBid.impid; nBid.width = nBid.w || width; nBid.height = nBid.h || height; + nBid.ttl = 360; nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner'; if (nBid.mediaType) { nBid.vastXml = cleanVast(nBid.adm, nBid.nurl); + nBid.ttl = 3600; } if (nBid.dealid) { nBid.dealId = nBid.dealid; @@ -51,7 +53,6 @@ export const spec = { nBid.netRevenue = true; nBid.creativeId = nBid.crid; nBid.currency = 'USD'; - nBid.ttl = 60; nBid.meta = nBid.meta || {}; if (nBid.adomain && nBid.adomain.length > 0) { nBid.meta.advertiserDomains = nBid.adomain; From 6d29360058627a2d520aa33a9b1590f744d6815c Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Wed, 2 Dec 2020 17:53:34 +0200 Subject: [PATCH 0424/1476] oneVideo Adapter - Custom Key Value Pair targeting support (SAPR-15478) (#6053) * Custom key value pair support * validate values are string or number terary * validate values are string or number if statement * custom key value pair unit tests * restored LiveIntentIdSystem failing unit tests * keep version 3.0.4 * fix double quotes eslint * updated md file --- modules/oneVideoBidAdapter.js | 8 +++ modules/oneVideoBidAdapter.md | 4 ++ test/spec/modules/oneVideoBidAdapter_spec.js | 58 ++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index c5bc054ac04..c287bc2f3b7 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -302,6 +302,14 @@ function getRequestData(bid, consentData, bidRequest) { bidData.site.ref = 'https://verizonmedia.com'; bidData.tmax = 1000; } + if (bid.params.video.custom && Object.prototype.toString.call(bid.params.video.custom) === '[object Object]') { + bidData.imp[0].ext.custom = {}; + for (const key in bid.params.video.custom) { + if (typeof bid.params.video.custom[key] === 'string' || typeof bid.params.video.custom[key] === 'number') { + bidData.imp[0].ext.custom[key] = bid.params.video.custom[key]; + } + } + } return bidData; } diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index 72f251aac04..92958af9e83 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -40,6 +40,10 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to inventoryid: 123, minduration: 10, maxduration: 30, + custom: { + key1: "value1", + key2: 123345 + } }, site: { id: 1, diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index ae29bcd48ec..331ac8976e6 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -315,6 +315,64 @@ describe('OneVideoBidAdapter', function () { const schain = data.source.ext.schain; expect(schain.nodes[0].hp).to.equal(bidRequest.params.video.hp); }) + it('should not accept key values pairs if custom is Undefined ', function () { + bidRequest.params.video.custom = null; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.undefined; + }); + it('should not accept key values pairs if custom is Array ', function () { + bidRequest.params.video.custom = []; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.undefined; + }); + it('should not accept key values pairs if custom is Number ', function () { + bidRequest.params.video.custom = 123456; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.undefined; + }); + it('should not accept key values pairs if custom is String ', function () { + bidRequest.params.video.custom = 'keyValuePairs'; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.undefined; + }); + it('should not accept key values pairs if custom is Boolean ', function () { + bidRequest.params.video.custom = true; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.undefined; + }); + it('should accept key values pairs if custom is Object ', function () { + bidRequest.params.video.custom = {}; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.imp[0].ext.custom).to.be.a('object'); + }); + it('should accept key values pairs if custom is Object ', function () { + bidRequest.params.video.custom = { + key1: 'value1', + key2: 'value2', + key3: 4444444, + key4: false, + key5: {nested: 'object'}, + key6: ['string', 2, true, null], + key7: null, + key8: undefined + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const custom = requests[0].data.imp[0].ext.custom; + expect(custom['key1']).to.be.a('string'); + expect(custom['key2']).to.be.a('string'); + expect(custom['key3']).to.be.a('number'); + expect(custom['key4']).to.not.exist; + expect(custom['key5']).to.not.exist; + expect(custom['key6']).to.not.exist; + expect(custom['key7']).to.not.exist; + expect(custom['key8']).to.not.exist; + }); }); describe('spec.interpretResponse', function () { From 53b0ed0fbe9fca4cb8bd45d583db6a0de767ae6f Mon Sep 17 00:00:00 2001 From: Neelanjan Sen <14229985+Fawke@users.noreply.github.com> Date: Wed, 2 Dec 2020 23:27:04 +0530 Subject: [PATCH 0425/1476] appnexusBidAdapter - remove tpuids (#6074) --- modules/appnexusBidAdapter.js | 6 ------ test/spec/modules/appnexusBidAdapter_spec.js | 7 +------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index f714472bbb1..203835db611 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -231,16 +231,10 @@ export const spec = { const criteoId = utils.deepAccess(bidRequests[0], `userId.criteoId`); let eids = []; if (criteoId) { - let tpuids = []; - tpuids.push({ - 'provider': 'criteo', - 'user_id': criteoId - }); eids.push({ source: 'criteo.com', id: criteoId }); - payload.tpuids = tpuids; } const tdid = utils.deepAccess(bidRequests[0], `userId.tdid`); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 21d3da358be..426259639e8 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -808,7 +808,7 @@ describe('AppNexusAdapter', function () { expect(request.options).to.deep.equal({withCredentials: false}); }); - it('should populate eids and tpuids when ttd id and criteo is available', function () { + it('should populate eids when ttd id and criteo is available', function () { const bidRequest = Object.assign({}, bidRequests[0], { userId: { tdid: 'sample-userid', @@ -828,11 +828,6 @@ describe('AppNexusAdapter', function () { source: 'criteo.com', id: 'sample-criteo-userid', }); - - expect(payload.tpuids).to.deep.include({ - provider: 'criteo', - user_id: 'sample-criteo-userid', - }); }); it('should populate iab_support object at the root level if omid support is detected', function () { From bb501f062dbcadfa1e6554b9b33d5471e9b79e71 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 2 Dec 2020 13:36:36 -0500 Subject: [PATCH 0426/1476] Rubicon Bid Adapter remove rp_floor param if floor not set (#6062) * RP bid adapter update to not set rp_floor when floor param does not exist. Left logic to set rp_floor to value if above 0.01. If floor param exists and equals 0.01 or below, 0.01 will be passed * Updated floor logic to be if a value is set greater than or equal to 0.01 then pass it otherwise dont set rp_floor --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b44ae108b38..e439f7fd2a4 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -460,7 +460,7 @@ export const spec = { 'zone_id': params.zoneId, 'size_id': parsedSizes[0], 'alt_size_ids': parsedSizes.slice(1).join(',') || undefined, - 'rp_floor': (params.floor = parseFloat(params.floor)) > 0.01 ? params.floor : 0.01, + 'rp_floor': (params.floor = parseFloat(params.floor)) >= 0.01 ? params.floor : undefined, 'rp_secure': '1', 'tk_flint': `${rubiConf.int_type || DEFAULT_INTEGRATION}_v$prebid.version$`, 'x_source.tid': bidRequest.transactionId, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index b1ef02a6369..6944034a787 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -396,7 +396,10 @@ describe('the rubicon adapter', function () { describe('to fastlane', function () { it('should make a well-formed request object', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let duplicate = Object.assign(bidderRequest); + duplicate.bids[0].params.floor = 0.01; + + let [request] = spec.buildRequests(duplicate.bids, duplicate); let data = parseQuery(request.data); expect(request.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -551,7 +554,7 @@ describe('the rubicon adapter', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const referenceOrdering = ['account_id', 'site_id', 'zone_id', 'size_id', 'alt_size_ids', 'p_pos', 'rf', 'p_geo.latitude', 'p_geo.longitude', 'kw', 'tg_v.ucat', 'tg_v.lastsearch', 'tg_v.likes', 'tg_i.rating', 'tg_i.prodtype', 'tk_flint', 'x_source.tid', 'x_source.pchain', 'p_screen_res', 'rp_floor', 'rp_secure', 'tk_user_key', 'tg_fl.eid', 'slots', 'rand']; + const referenceOrdering = ['account_id', 'site_id', 'zone_id', 'size_id', 'alt_size_ids', 'p_pos', 'rf', 'p_geo.latitude', 'p_geo.longitude', 'kw', 'tg_v.ucat', 'tg_v.lastsearch', 'tg_v.likes', 'tg_i.rating', 'tg_i.prodtype', 'tk_flint', 'x_source.tid', 'x_source.pchain', 'p_screen_res', 'rp_secure', 'tk_user_key', 'tg_fl.eid', 'slots', 'rand']; request.data.split('&').forEach((item, i) => { expect(item.split('=')[0]).to.equal(referenceOrdering[i]); @@ -566,7 +569,6 @@ describe('the rubicon adapter', function () { 'size_id': '15', 'alt_size_ids': '43', 'p_pos': 'atf', - 'rp_floor': '0.01', 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, @@ -883,7 +885,6 @@ describe('the rubicon adapter', function () { 'size_id': '15', 'alt_size_ids': '43', 'p_pos': 'atf', - 'rp_floor': '0.01', 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, @@ -2047,7 +2048,6 @@ describe('the rubicon adapter', function () { 'size_id': 15, 'alt_size_ids': '43', 'p_pos': 'atf', - 'rp_floor': 0.01, 'rp_secure': /[01]/, 'tk_flint': INTEGRATION, 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', From 571c6a1b0357ec234ba8548534319e9a3f9b1eca Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 2 Dec 2020 15:40:01 -0500 Subject: [PATCH 0427/1476] Prebid 4.18.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 48963f36c76..799c730e3cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.18.0-pre", + "version": "4.18.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b8eb346f72980ce126240efaee87488bb53ffa8f Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 2 Dec 2020 15:53:46 -0500 Subject: [PATCH 0428/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 799c730e3cd..ed4514cee42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.18.0", + "version": "4.19.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From cfe9786a282b3bb6acc9495ca59524b5ba3909ed Mon Sep 17 00:00:00 2001 From: David Carver <54326287+ndxcarver@users.noreply.github.com> Date: Wed, 2 Dec 2020 15:45:16 -0600 Subject: [PATCH 0429/1476] LKQD: update adapter to include new parameters (#6033) * LKQD: update adapter to include new parameters * LKQD: update to use standardized coppa * LKQD: convert true to 1 for api --- modules/lkqdBidAdapter.js | 10 ++++++++ test/spec/modules/lkqdBidAdapter_spec.js | 29 ++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js index 51d5c48e1fc..0f5782649ad 100644 --- a/modules/lkqdBidAdapter.js +++ b/modules/lkqdBidAdapter.js @@ -1,6 +1,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'lkqd'; const BID_TTL_DEFAULT = 300; @@ -148,6 +149,9 @@ function buildRequests(validBidRequests, bidderRequest) { if (bidRequest.params.hasOwnProperty('dnt') && bidRequest.params.dnt != null) { sspData.dnt = bidRequest.params.dnt; } + if (config.getConfig('coppa') === true) { + sspData.coppa = 1; + } if (bidRequest.params.hasOwnProperty('pageurl') && bidRequest.params.pageurl != null) { sspData.pageurl = bidRequest.params.pageurl; } else if (bidderRequest && bidderRequest.refererInfo) { @@ -177,6 +181,12 @@ function buildRequests(validBidRequests, bidderRequest) { sspData.bidWidth = playerWidth; sspData.bidHeight = playerHeight; + for (let k = 1; k <= 40; k++) { + if (bidRequest.params.hasOwnProperty(`c${k}`) && bidRequest.params[`c${k}`]) { + sspData[`c${k}`] = bidRequest.params[`c${k}`]; + } + } + bidRequests.push({ method: 'GET', url: sspUrl, diff --git a/test/spec/modules/lkqdBidAdapter_spec.js b/test/spec/modules/lkqdBidAdapter_spec.js index f10d936a28b..1cd33d9ec59 100644 --- a/test/spec/modules/lkqdBidAdapter_spec.js +++ b/test/spec/modules/lkqdBidAdapter_spec.js @@ -1,6 +1,7 @@ import { spec } from 'modules/lkqdBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -const { expect } = require('chai'); +import { config } from 'src/config.js'; +import { expect } from 'chai'; describe('LKQD Bid Adapter Test', () => { const adapter = newBidder(spec); @@ -47,7 +48,9 @@ describe('LKQD Bid Adapter Test', () => { 'bidder': 'lkqd', 'params': { 'siteId': '662921', - 'placementId': '263' + 'placementId': '263', + 'c1': 'newWindow', + 'c20': 'lkqdCustom' }, 'adUnitCode': 'lkqd', 'sizes': [[300, 250], [640, 480]], @@ -75,6 +78,10 @@ describe('LKQD Bid Adapter Test', () => { ]; it('should populate available parameters', () => { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + const requests = spec.buildRequests(bidRequests); expect(requests.length).to.equal(2); const r1 = requests[0].data; @@ -82,14 +89,26 @@ describe('LKQD Bid Adapter Test', () => { expect(r1).to.have.string('&sid=662921&'); expect(r1).to.have.string('&width=300&'); expect(r1).to.have.string('&height=250&'); + expect(r1).to.have.string('&coppa=1&'); + expect(r1).to.have.string('&c1=newWindow&'); + expect(r1).to.have.string('&c20=lkqdCustom'); const r2 = requests[1].data; expect(r2).to.have.string('pid=263&'); expect(r2).to.have.string('&sid=662921&'); expect(r2).to.have.string('&width=640&'); expect(r2).to.have.string('&height=480&'); + expect(r2).to.have.string('&coppa=1&'); + expect(r2).to.have.string('&c1=newWindow&'); + expect(r2).to.have.string('&c20=lkqdCustom'); + + config.getConfig.restore(); }); it('should not populate unspecified parameters', () => { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(false); + const requests = spec.buildRequests(bidRequests); expect(requests.length).to.equal(2); const r1 = requests[0].data; @@ -99,6 +118,8 @@ describe('LKQD Bid Adapter Test', () => { expect(r1).to.not.have.string('&contentlength='); expect(r1).to.not.have.string('&contenturl='); expect(r1).to.not.have.string('&schain='); + expect(r1).to.not.have.string('&c10='); + expect(r1).to.not.have.string('coppa'); const r2 = requests[1].data; expect(r2).to.not.have.string('&dnt='); expect(r2).to.not.have.string('&contentid='); @@ -106,6 +127,10 @@ describe('LKQD Bid Adapter Test', () => { expect(r2).to.not.have.string('&contentlength='); expect(r2).to.not.have.string('&contenturl='); expect(r2).to.not.have.string('&schain='); + expect(r2).to.not.have.string('&c39='); + expect(r2).to.not.have.string('coppa'); + + config.getConfig.restore(); }); it('should handle single size request', () => { From 19da021b34207fabc36686ee0ea6a2b2fc7d724e Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Thu, 3 Dec 2020 10:07:06 -0500 Subject: [PATCH 0430/1476] pubGENIUS bid adapter: support video (#6040) * pubGENIUS bid adapter: support video * update md doc to show more video params --- modules/pubgeniusBidAdapter.js | 93 ++++++++++++-- modules/pubgeniusBidAdapter.md | 35 +++++- test/spec/modules/pubgeniusBidAdapter_spec.js | 116 +++++++++++++++++- 3 files changed, 229 insertions(+), 15 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 55f50e4b6a9..88e85a4fd7a 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; -import { BANNER } from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { deepAccess, deepSetValue, @@ -12,15 +12,16 @@ import { isStr, logError, parseQueryStringParameters, + pick, } from '../src/utils.js'; -const BIDDER_VERSION = '1.0.0'; +const BIDDER_VERSION = '1.1.0'; const BASE_URL = 'https://ortb.adpearl.io'; export const spec = { code: 'pubgenius', - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [ BANNER, VIDEO ], isBidRequestValid(bid) { const adUnitId = bid.params.adUnitId; @@ -29,8 +30,13 @@ export const spec = { return false; } - const sizes = deepAccess(bid, 'mediaTypes.banner.sizes'); - return !!(sizes && sizes.length) && sizes.every(size => isArrayOfNums(size, 2)); + const { mediaTypes } = bid; + + if (mediaTypes.banner) { + return isValidBanner(mediaTypes.banner); + } + + return isValidVideo(mediaTypes.video, bid.params.video); }, buildRequests: function (bidRequests, bidderRequest) { @@ -141,16 +147,44 @@ export const spec = { }, }; +function buildVideoParams(videoMediaType, videoParams) { + videoMediaType = videoMediaType || {}; + const params = pick(videoMediaType, ['api', 'mimes', 'protocols', 'playbackmethod']); + + switch (videoMediaType.context) { + case 'instream': + params.placement = 1; + break; + case 'outstream': + params.placement = 2; + break; + default: + break; + } + + if (videoMediaType.playerSize) { + params.w = videoMediaType.playerSize[0][0]; + params.h = videoMediaType.playerSize[0][1]; + } + + return Object.assign(params, videoParams); +} + function buildImp(bid) { const imp = { id: bid.bidId, - banner: { - format: deepAccess(bid, 'mediaTypes.banner.sizes').map(size => ({ w: size[0], h: size[1] })), - topframe: numericBoolean(!inIframe()), - }, tagid: String(bid.params.adUnitId), }; + if (bid.mediaTypes.banner) { + imp.banner = { + format: bid.mediaTypes.banner.sizes.map(size => ({ w: size[0], h: size[1] })), + topframe: numericBoolean(!inIframe()), + }; + } else { + imp.video = buildVideoParams(bid.mediaTypes.video, bid.params.video); + } + const bidFloor = bid.params.bidFloor; if (isNumber(bidFloor)) { imp.bidfloor = bidFloor; @@ -197,7 +231,6 @@ function interpretBid(bid) { cpm: bid.price, width: bid.w, height: bid.h, - ad: bid.adm, ttl: bid.exp, creativeId: bid.crid, netRevenue: true, @@ -209,6 +242,28 @@ function interpretBid(bid) { }; } + const pbadapter = deepAccess(bid, 'ext.pbadapter') || {}; + switch (pbadapter.mediaType) { + case 'video': + if (bid.nurl) { + bidResponse.vastUrl = bid.nurl; + } + + if (bid.adm) { + bidResponse.vastXml = bid.adm; + } + + if (pbadapter.cacheKey) { + bidResponse.videoCacheKey = pbadapter.cacheKey; + } + + bidResponse.mediaType = VIDEO; + break; + default: // banner by default + bidResponse.ad = bid.adm; + break; + } + return bidResponse; } @@ -221,4 +276,22 @@ function getBaseUrl() { return (pubg && pubg.endpoint) || BASE_URL; } +function isValidSize(size) { + return isArrayOfNums(size, 2) && size[0] > 0 && size[1] > 0; +} + +function isValidBanner(banner) { + const sizes = banner.sizes; + return !!(sizes && sizes.length) && sizes.every(isValidSize); +} + +function isValidVideo(videoMediaType, videoParams) { + const params = buildVideoParams(videoMediaType, videoParams); + + return !!(params.placement && + isValidSize([params.w, params.h]) && + params.mimes && params.mimes.length && + isArrayOfNums(params.protocols) && params.protocols.length); +} + registerBidder(spec); diff --git a/modules/pubgeniusBidAdapter.md b/modules/pubgeniusBidAdapter.md index 66851af9c3f..ff23a433331 100644 --- a/modules/pubgeniusBidAdapter.md +++ b/modules/pubgeniusBidAdapter.md @@ -50,7 +50,40 @@ var adUnits = [ } } ] - } + }, + { + code: 'test-video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 360], + mimes: ['video/mp4'], + protocols: [3], + } + }, + bids: [ + { + bidder: 'pubgenius', + params: { + adUnitId: '1001', + bidFloor: 1, + test: true, + + // other video parameters as in OpenRTB v2.5 spec + video: { + skip: 1 + + // the following overrides mediaTypes.video of the ad unit + placement: 1, + w: 640, + h: 360, + mimes: ['video/mp4'], + protocols: [3], + } + } + } + ] + }, ]; ``` diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 4b5bf7efac0..c0b05510707 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -1,8 +1,9 @@ import { expect } from 'chai'; import { spec } from 'modules/pubgeniusBidAdapter.js'; -import { deepClone, parseQueryStringParameters } from 'src/utils.js'; import { config } from 'src/config.js'; +import { VIDEO } from 'src/mediaTypes.js'; +import { deepClone, parseQueryStringParameters } from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; const { @@ -23,8 +24,8 @@ describe('pubGENIUS adapter', () => { }); describe('supportedMediaTypes', () => { - it('should contain only banner', () => { - expect(supportedMediaTypes).to.deep.equal(['banner']); + it('should contain banner and video', () => { + expect(supportedMediaTypes).to.deep.equal(['banner', 'video']); }); }); @@ -77,6 +78,51 @@ describe('pubGENIUS adapter', () => { expect(isBidRequestValid(bid)).to.be.false; }); + + it('should return false without banner or video', () => { + bid.mediaTypes = {}; + + expect(isBidRequestValid(bid)).to.be.false; + }); + + it('should return true with valid video media type', () => { + bid.mediaTypes = { + video: { + context: 'instream', + playerSize: [[100, 100]], + mimes: ['video/mp4'], + protocols: [1], + }, + }; + + expect(isBidRequestValid(bid)).to.be.true; + }); + + it('should return true with valid video params', () => { + bid.params.video = { + placement: 1, + w: 200, + h: 200, + mimes: ['video/mp4'], + protocols: [1], + }; + + expect(isBidRequestValid(bid)).to.be.true; + }); + + it('should return false without video protocols', () => { + bid.mediaTypes = { + video: { + context: 'instream', + playerSize: [[100, 100]], + }, + }; + bid.params.video = { + mimes: ['video/mp4'], + }; + + expect(isBidRequestValid(bid)).to.be.false; + }); }); describe('buildRequests', () => { @@ -143,7 +189,7 @@ describe('pubGENIUS adapter', () => { tmax: 1200, ext: { pbadapter: { - version: '1.0.0', + version: '1.1.0', }, }, }, @@ -314,6 +360,44 @@ describe('pubGENIUS adapter', () => { expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); }); + + it('should build video imp', () => { + bidRequest.mediaTypes = { + video: { + context: 'instream', + playerSize: [[200, 100]], + mimes: ['video/mp4'], + protocols: [2, 3], + api: [1, 2], + playbackmethod: [3, 4], + }, + }; + bidRequest.params.video = { + minduration: 5, + maxduration: 100, + skip: 1, + skipafter: 1, + startdelay: -1, + }; + + delete expectedRequest.data.imp[0].banner; + expectedRequest.data.imp[0].video = { + mimes: ['video/mp4'], + minduration: 5, + maxduration: 100, + protocols: [2, 3], + w: 200, + h: 100, + startdelay: -1, + placement: 1, + skip: 1, + skipafter: 1, + playbackmethod: [3, 4], + api: [1, 2], + }; + + expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); + }); }); describe('interpretResponse', () => { @@ -370,6 +454,30 @@ describe('pubGENIUS adapter', () => { it('should interpret no bids', () => { expect(interpretResponse({ body: {} })).to.deep.equal([]); }); + + it('should interpret video response', () => { + serverResponse.body.seatbid[0].bid[0] = { + ...serverResponse.body.seatbid[0].bid[0], + nurl: 'http://vasturl/cache?id=x', + ext: { + pbadapter: { + mediaType: 'video', + cacheKey: 'x', + }, + }, + }; + + delete expectedBidResponse.ad; + expectedBidResponse = { + ...expectedBidResponse, + vastUrl: 'http://vasturl/cache?id=x', + vastXml: 'fake_creative', + videoCacheKey: 'x', + mediaType: VIDEO, + }; + + expect(interpretResponse(serverResponse)).to.deep.equal([expectedBidResponse]); + }); }); describe('getUserSyncs', () => { From ddebc4a2f8efb0aaa9c2c26c260bc25c380599b0 Mon Sep 17 00:00:00 2001 From: Jenine Drew <2839303+jeninedrew@users.noreply.github.com> Date: Thu, 3 Dec 2020 21:12:29 -0500 Subject: [PATCH 0431/1476] Add Optional Params to Concert Adapter (#6064) --- modules/concertBidAdapter.js | 9 ++++++--- modules/concertBidAdapter.md | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index d153ddf9ee2..3eb75799705 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -42,7 +42,7 @@ export const spec = { debug: utils.debugTurnedOn(), uid: getUid(bidderRequest), optedOut: hasOptedOutOfPersonalization(), - adapterVersion: '1.1.0', + adapterVersion: '1.1.1', uspConsent: bidderRequest.uspConsent, gdprConsent: bidderRequest.gdprConsent } @@ -53,9 +53,12 @@ export const spec = { name: bidRequest.adUnitCode, bidId: bidRequest.bidId, transactionId: bidRequest.transactionId, - sizes: bidRequest.sizes, + sizes: bidRequest.params.sizes || bidRequest.sizes, partnerId: bidRequest.params.partnerId, - slotType: bidRequest.params.slotType + slotType: bidRequest.params.slotType, + adSlot: bidRequest.params.slot || bidRequest.adUnitCode, + placementId: bidRequest.params.placementId || '', + site: bidRequest.params.site || bidderRequest.refererInfo.referer } return slot; diff --git a/modules/concertBidAdapter.md b/modules/concertBidAdapter.md index faf774946d1..d8736082e5c 100644 --- a/modules/concertBidAdapter.md +++ b/modules/concertBidAdapter.md @@ -24,10 +24,14 @@ Module that connects to Concert demand sources { bidder: "concert", params: { - partnerId: 'test_partner' + partnerId: 'test_partner', + site: 'site_name', + placementId: 1234567, + slot: 'slot_name', + sizes: [[1030, 590]] } } ] } ]; -``` \ No newline at end of file +``` From 2b730a841b59335ed089269913f4a4d44145db63 Mon Sep 17 00:00:00 2001 From: Olivier Date: Fri, 4 Dec 2020 10:34:25 +0100 Subject: [PATCH 0432/1476] adagioBidAdapter: add Video support (#6038) * adagioBidAdapter: add outstream video support * Lint: semi rule consistency * IE11 support: remove Array.includes() * Generate bidResponse.vastUrl based on vastXml dataUri encoding * Update .md file --- modules/adagioBidAdapter.js | 163 ++++++++++++++++++--- modules/adagioBidAdapter.md | 58 +++++++- test/spec/modules/adagioBidAdapter_spec.js | 142 ++++++++++++++++-- 3 files changed, 327 insertions(+), 36 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index b20f832fd42..cab233d5387 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -2,23 +2,27 @@ import find from 'core-js-pure/features/array/find.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { loadExternalScript } from '../src/adloader.js' +import { loadExternalScript } from '../src/adloader.js'; import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; import sha256 from 'crypto-js/sha256.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { createEidsArray } from './userId/eids.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.5.0'; +export const VERSION = '2.6.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; -export const SUPPORTED_MEDIA_TYPES = ['banner']; +export const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; export const GVLID = 617; export const storage = getStorageManager(GVLID, 'adagio'); +export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 @@ -27,6 +31,35 @@ t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj pV6EP3MTLosuUEpLaQIDAQAB -----END PUBLIC KEY-----`; +// This provide a whitelist and a basic validation +// of OpenRTB 2.5 options used by the Adagio SSP. +// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf +export const ORTB_VIDEO_PARAMS = { + 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), + 'minduration': (value) => utils.isInteger(value), + 'maxduration': (value) => utils.isInteger(value), + 'protocols': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].indexOf(v) !== -1), + 'w': (value) => utils.isInteger(value), + 'h': (value) => utils.isInteger(value), + 'startdelay': (value) => utils.isInteger(value), + 'placement': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5].indexOf(v) !== -1), + 'linearity': (value) => [1, 2].indexOf(value) !== -1, + 'skip': (value) => [0, 1].indexOf(value) !== -1, + 'skipmin': (value) => utils.isInteger(value), + 'skipafter': (value) => utils.isInteger(value), + 'sequence': (value) => utils.isInteger(value), + 'battr': (value) => Array.isArray(value) && value.every(v => Array.from({length: 17}, (_, i) => i + 1).indexOf(v) !== -1), + 'maxextended': (value) => utils.isInteger(value), + 'minbitrate': (value) => utils.isInteger(value), + 'maxbitrate': (value) => utils.isInteger(value), + 'boxingallowed': (value) => [0, 1].indexOf(value) !== -1, + 'playbackmethod': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1), + 'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1, + 'delivery': (value) => [1, 2, 3].indexOf(value) !== -1, + 'pos': (value) => [0, 1, 2, 3, 4, 5, 6, 7].indexOf(value) !== -1, + 'api': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1) +}; + let currentWindow; export function adagioScriptFromLocalStorageCb(ls) { @@ -64,7 +97,7 @@ export function adagioScriptFromLocalStorageCb(ls) { export function getAdagioScript() { storage.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY, (ls) => { - internal.adagioScriptFromLocalStorageCb(ls) + internal.adagioScriptFromLocalStorageCb(ls); }); storage.localStorageIsEnabled(isValid => { @@ -335,7 +368,7 @@ function getOrAddAdagioAdUnit(adUnitCode) { w.ADAGIO = w.ADAGIO || {}; if (w.ADAGIO.adUnits[adUnitCode]) { - return w.ADAGIO.adUnits[adUnitCode] + return w.ADAGIO.adUnits[adUnitCode]; } return w.ADAGIO.adUnits[adUnitCode] = {}; @@ -435,7 +468,7 @@ function getElementFromTopWindow(element, currentWindow) { }; function autoDetectAdUnitElementId(adUnitCode) { - const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode) + const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode); let adUnitElementId = null; if (autoDetectedAdUnit && autoDetectedAdUnit.divId) { @@ -450,16 +483,16 @@ function autoDetectEnvironment() { let environment; switch (device) { case 2: - environment = 'desktop' + environment = 'desktop'; break; case 4: - environment = 'mobile' + environment = 'mobile'; break; case 5: - environment = 'tablet' + environment = 'tablet'; break; }; - return environment + return environment; }; function getFeatures(bidRequest, bidderRequest) { @@ -507,6 +540,21 @@ function getFeatures(bidRequest, bidderRequest) { return features; }; +function isRendererPreferredFromPublisher(bidRequest) { + // renderer defined at adUnit level + const adUnitRenderer = utils.deepAccess(bidRequest, 'renderer'); + const hasValidAdUnitRenderer = !!(adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render); + + // renderer defined at adUnit.mediaTypes level + const mediaTypeRenderer = utils.deepAccess(bidRequest, 'mediaTypes.video.renderer'); + const hasValidMediaTypeRenderer = !!(mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render); + + return !!( + (hasValidAdUnitRenderer && !(adUnitRenderer.backupOnly === true)) || + (hasValidMediaTypeRenderer && !(mediaTypeRenderer.backupOnly === true)) + ); +} + export const internal = { enqueue, getOrAddAdagioAdUnit, @@ -521,7 +569,8 @@ export const internal = { getRefererInfo, adagioScriptFromLocalStorageCb, getCurrentWindow, - canAccessTopWindow + canAccessTopWindow, + isRendererPreferredFromPublisher }; function _getGdprConsent(bidderRequest) { @@ -539,7 +588,7 @@ function _getGdprConsent(bidderRequest) { const consent = {}; if (apiVersion !== undefined) { - consent.apiVersion = apiVersion + consent.apiVersion = apiVersion; } if (consentString !== undefined) { @@ -575,10 +624,62 @@ function _getSchain(bidRequest) { function _getEids(bidRequest) { if (utils.deepAccess(bidRequest, 'userId')) { - return createEidsArray(bidRequest.userId) + return createEidsArray(bidRequest.userId); } } +function _buildVideoBidRequest(bidRequest) { + const videoAdUnitParams = utils.deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {}); + const computedParams = {}; + + // Special case for playerSize. + // Eeach props will be overrided if they are defined in config. + if (Array.isArray(videoAdUnitParams.playerSize)) { + const tempSize = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize; + computedParams.w = tempSize[0]; + computedParams.h = tempSize[1]; + } + + const videoParams = { + ...computedParams, + ...videoAdUnitParams, + ...videoBidderParams + }; + + if (videoParams.context && videoParams.context === OUTSTREAM) { + bidRequest.mediaTypes.video.playerName = (internal.isRendererPreferredFromPublisher(bidRequest)) ? 'other' : 'adagio'; + + if (bidRequest.mediaTypes.video.playerName === 'other') { + utils.logWarn(`${LOG_PREFIX} renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.`); + } + } + + // Only whitelisted OpenRTB options need to be validated. + // Other options will still remain in the `mediaTypes.video` object + // sent in the ad-request, but will be ignored by the SSP. + Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => { + if (videoParams.hasOwnProperty(paramName)) { + if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) { + bidRequest.mediaTypes.video[paramName] = videoParams[paramName]; + } else { + delete bidRequest.mediaTypes.video[paramName]; + utils.logWarn(`${LOG_PREFIX} The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`); + } + } + }); +} + +function _renderer(bid) { + bid.renderer.push(() => { + if (typeof window.ADAGIO.outstreamPlayer === 'function') { + window.ADAGIO.outstreamPlayer(bid); + } else { + utils.logError(`${LOG_PREFIX} Adagio outstream player is not defined`); + } + }); +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -600,7 +701,7 @@ export const spec = { ...params, adUnitElementId, environment - } + }; const debugData = () => ({ action: 'pb-dbg', @@ -631,7 +732,7 @@ export const spec = { // Store adUnits config. // If an adUnitCode has already been stored, it will be replaced. w.ADAGIO = w.ADAGIO || {}; - w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode) + w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode); w.ADAGIO.pbjsAdUnits.push({ code: adUnitCode, mediaTypes: mediaTypes || {}, @@ -667,6 +768,11 @@ export const spec = { const eids = _getEids(validBidRequests[0]) || []; const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); + + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + _buildVideoBidRequest(bidRequest); + } + return bidRequest; }); @@ -674,7 +780,7 @@ export const spec = { const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { adUnit.params.organizationId = adUnit.params.organizationId.toString(); - groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || [] + groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []; groupedAdUnits[adUnit.params.organizationId].push(adUnit); return groupedAdUnits; @@ -709,7 +815,7 @@ export const spec = { options: { contentType: 'text/plain' } - } + }; }); return requests; @@ -730,7 +836,30 @@ export const spec = { if (response.bids) { response.bids.forEach(bidObj => { const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId)); + if (bidReq) { + if (bidObj.mediaType === VIDEO) { + const mediaTypeContext = utils.deepAccess(bidReq, 'mediaTypes.video.context'); + // Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`. + if (!bidObj.vastUrl && bidObj.vastXml) { + bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + btoa(bidObj.vastXml.replace(/\\"/g, '"')); + } + + if (mediaTypeContext === OUTSTREAM) { + bidObj.renderer = Renderer.install({ + id: bidObj.requestId, + adUnitCode: bidObj.adUnitCode, + url: bidObj.urlRenderer || RENDERER_URL, + config: { + ...utils.deepAccess(bidReq, 'mediaTypes.video'), + ...utils.deepAccess(bidObj, 'outstream', {}) + } + }); + + bidObj.renderer.setRender(_renderer); + } + } + bidObj.site = bidReq.params.site; bidObj.placement = bidReq.params.placement; bidObj.pagetype = bidReq.params.pagetype; diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index b34cc3fe37a..c55a24f1115 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -22,10 +22,10 @@ Connects to Adagio demand source to fetch bids. bids: [{ bidder: 'adagio', // Required params: { - organizationId: '0', // Required - Organization ID provided by Adagio. - site: 'news-of-the-day', // Required - Site Name provided by Adagio. - placement: 'ban_atf', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. - adUnitElementId: 'dfp_banniere_atf', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + organizationId: '1002', // Required - Organization ID provided by Adagio. + site: 'adagio-io', // Required - Site Name provided by Adagio. + placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. // The following params are limited to 30 characters, // and can only contain the following characters: @@ -37,7 +37,54 @@ Connects to Adagio demand source to fetch bids. 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. - postBid: false // Optional. Use it in case of Post-bid integration only. + postBid: false, // Optional. Use it in case of Post-bid integration only. + // Optional debug mode, used to get a bid response with expected cpm. + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 + } + } + }] + }, + { + code: 'article_outstream', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + mimes: ['video/mp4'], + skip: 1 + // Other OpenRTB 2.5 video options… + } + }, + bids: [{ + bidder: 'adagio', // Required + params: { + organizationId: '1002', // Required - Organization ID provided by Adagio. + site: 'adagio-io', // Required - Site Name provided by Adagio. + placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + + // The following params are limited to 30 characters, + // and can only contain the following characters: + // - alphanumeric (A-Z+a-z+0-9, case-insensitive) + // - dashes `-` + // - 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. + postBid: false, // Optional. Use it in case of Post-bid integration only. + video: { + skip: 0 + // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. + }, + // Optional debug mode, used to get a bid response with expected cpm. + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 + } } }] } @@ -88,5 +135,4 @@ Connects to Adagio demand source to fetch bids. ] } } - ``` diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 86fb2e7cbd3..2cf97a1129b 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,6 +1,16 @@ import find from 'core-js-pure/features/array/find.js'; import { expect } from 'chai'; -import { _features, internal as adagio, adagioScriptFromLocalStorageCb, getAdagioScript, storage, spec, ENDPOINT, VERSION } from '../../../modules/adagioBidAdapter.js'; +import { + _features, + internal as adagio, + adagioScriptFromLocalStorageCb, + getAdagioScript, + storage, + spec, + ENDPOINT, + VERSION, + RENDERER_URL +} from '../../../modules/adagioBidAdapter.js'; import { loadExternalScript } from '../../../src/adloader.js'; import * as utils from '../../../src/utils.js'; import { config } from 'src/config.js'; @@ -107,7 +117,7 @@ describe('Adagio bid adapter', () => { adagioMock = sinon.mock(adagio); utilsMock = sinon.mock(utils); - sandbox = sinon.sandbox.create(); + sandbox = sinon.createSandbox(); }); afterEach(() => { @@ -254,7 +264,7 @@ describe('Adagio bid adapter', () => { // replace by the values defined in beforeEach window.top.ADAGIO = { ...window.ADAGIO - } + }; spec.isBidRequestValid(bid01); spec.isBidRequestValid(bid02); @@ -384,6 +394,81 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.adUnits[0].features.url).to.not.exist; }); + describe('With video mediatype', function() { + context('Outstream video', function() { + it('should logWarn if user does not set renderer.backupOnly: true', function() { + sandbox.spy(utils, 'logWarn'); + const bid01 = new BidRequestBuilder({ + adUnitCode: 'adunit-code-01', + mediaTypes: { + banner: { sizes: [[300, 250]] }, + video: { + context: 'outstream', + playerSize: [[300, 250]], + renderer: { + url: 'https://url.tld', + render: () => true + } + } + }, + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + const request = spec.buildRequests([bid01], bidderRequest)[0]; + + expect(request.data.adUnits[0].mediaTypes.video.playerName).to.equal('other'); + sinon.assert.calledWith(utils.logWarn, 'Adagio: renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.'); + }); + }); + + it('Update mediaTypes.video with OpenRTB options. Validate and sanitize whitelisted OpenRTB', function() { + sandbox.spy(utils, 'logWarn'); + const bid01 = new BidRequestBuilder({ + adUnitCode: 'adunit-code-01', + mediaTypes: { + banner: { sizes: [[300, 250]] }, + video: { + context: 'outstream', + playerSize: [[300, 250]], + mimes: ['video/mp4'], + api: 5, // will be removed because invalid + playbackmethod: [7], // will be removed because invalid + } + }, + }).withParams({ + // options in video, will overide + video: { + skip: 1, + skipafter: 4, + minduration: 10, + maxduration: 30, + placement: [3], + protocols: [8] + } + }).build(); + + const bidderRequest = new BidderRequestBuilder().build(); + const expected = { + context: 'outstream', + playerSize: [[300, 250]], + playerName: 'adagio', + mimes: ['video/mp4'], + skip: 1, + skipafter: 4, + minduration: 10, + maxduration: 30, + placement: [3], + protocols: [8], + w: 300, + h: 250 + }; + + const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests).to.have.lengthOf(1); + expect(requests[0].data.adUnits[0].mediaTypes.video).to.deep.equal(expected); + sinon.assert.calledTwice(utils.logWarn); + }); + }); + describe('with sChain', function() { const schain = { ver: '1.0', @@ -550,7 +635,7 @@ describe('Adagio bid adapter', () => { describe('with USPrivacy', function() { const bid01 = new BidRequestBuilder().withParams().build(); - const consent = 'Y11N' + const consent = 'Y11N'; it('should send the USPrivacy "ccpa.uspConsent" in the request', function () { const bidderRequest = new BidderRequestBuilder({ @@ -579,7 +664,7 @@ describe('Adagio bid adapter', () => { const userId = { sharedid: {id: '01EAJWWNEPN3CYMM5N8M5VXY22', third: '01EAJWWNEPN3CYMM5N8M5VXY22'}, unsuported: '666' - } + }; it('should send "user.eids" in the request for Prebid.js supported modules only', function() { const bid01 = new BidRequestBuilder({ @@ -601,11 +686,11 @@ describe('Adagio bid adapter', () => { id: '01EAJWWNEPN3CYMM5N8M5VXY22' } ] - }] + }]; - expect(requests[0].data.user.eids).to.have.lengthOf(1) - expect(requests[0].data.user.eids).to.deep.equal(expected) - }) + expect(requests[0].data.user.eids).to.have.lengthOf(1); + expect(requests[0].data.user.eids).to.deep.equal(expected); + }); it('should send an empty "user.eids" array in the request if userId module is unsupported', function() { const bid01 = new BidRequestBuilder({ @@ -618,9 +703,9 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.user.eids).to.be.empty - }) - }) + expect(requests[0].data.user.eids).to.be.empty; + }); + }); }); describe('interpretResponse()', function() { @@ -732,6 +817,37 @@ describe('Adagio bid adapter', () => { utilsMock.verify(); }); + + describe('Response with video outstream', () => { + const bidRequestWithOutstream = utils.deepClone(bidRequest); + bidRequestWithOutstream.data.adUnits[0].mediaTypes.video = { + context: 'outstream', + playerSize: [[300, 250]], + mimes: ['video/mp4'], + skip: true + }; + + const serverResponseWithOutstream = utils.deepClone(serverResponse); + serverResponseWithOutstream.body.bids[0].vastXml = ''; + serverResponseWithOutstream.body.bids[0].mediaType = 'video'; + serverResponseWithOutstream.body.bids[0].outstream = { + bvwUrl: 'https://foo.baz', + impUrl: 'https://foo.bar' + }; + + it('should set a renderer in video outstream context', function() { + const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0]; + expect(bidResponse).to.have.any.keys('outstream', 'renderer', 'mediaType'); + expect(bidResponse.renderer).to.be.a('object'); + expect(bidResponse.renderer.url).to.equal(RENDERER_URL); + expect(bidResponse.renderer.config.bvwUrl).to.be.ok; + expect(bidResponse.renderer.config.impUrl).to.be.ok; + expect(bidResponse.renderer.loaded).to.not.be.ok; + expect(bidResponse.width).to.equal(300); + expect(bidResponse.height).to.equal(250); + expect(bidResponse.vastUrl).to.match(/^data:text\/xml;/) + }); + }); }); describe('getUserSyncs()', function() { @@ -1195,7 +1311,7 @@ describe('Adagio bid adapter', () => { expect(loadExternalScript.called).to.be.false; expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; - }) + }); }); it('should verify valid hash with valid script', function () { From 5552473087d8e80d953ada7710206c9e1387252e Mon Sep 17 00:00:00 2001 From: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Date: Fri, 4 Dec 2020 03:10:45 -0800 Subject: [PATCH 0433/1476] Not using utils.isEmpty on non objects (#6036) * Added telaria bid adapter * more documentation * Added more test cases. And improved some code in the adapter * Removed the check for optional params, they are handled in the server. Also updated certain param names used in the test spec. * added some spaces to fix CircleCI tests * added some spaces to fix CircleCI tests * fixed code indentation in /spec/AnalyticsAdapter_spec.js which causing the CircleCI tests to fail. * Reverted the changes * merged with prebid master. * creative Id is required when we build a response but our server doesn't always have the crid, so using a sentinel value when we don't have the crid. * - removed an un used method - Removed the package-lock file. * merging to master * updated telaria bid adapter to use player size provided by the bid.mediaTypes.video.playerSize instead of bid.sizes. https://github.com/prebid/Prebid.js/issues/3331 * - removed the requirement for having player size - updated the test spec to reflect the above change - removed changes to the package-lock.json file. * added a param to the ad call url to let us know that the request is coming via hb. * to lower casing the bidder code. * Merge branch 'master' of https://github.com/prebid/Prebid.js # Conflicts: # modules/telariaBidAdapter.js Added GDPR support * Sending the gdpr & gdpr consent string only if they're defined * - Updated the test ad unit to use 'telaria' as the bidder code. - Added an example URL. * using the bidder code constant * - 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 * - some formatting changes * using val !== '' instead of utils.isEmpty(val) * Checking for undefined in the getEncodedValIfNotEmpty method Co-authored-by: Vinay Prasad Co-authored-by: Vinay Prasad --- modules/telariaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js index acc20f6b183..71651b5af94 100644 --- a/modules/telariaBidAdapter.js +++ b/modules/telariaBidAdapter.js @@ -124,7 +124,7 @@ function getDefaultSrcPageUrl() { } function getEncodedValIfNotEmpty(val) { - return !utils.isEmpty(val) ? encodeURIComponent(val) : ''; + return (val !== '' && val !== undefined) ? encodeURIComponent(val) : ''; } /** From 4b0b82f629f952716cb065fd11b9cfb6e8497c1d Mon Sep 17 00:00:00 2001 From: iskmerof Date: Fri, 4 Dec 2020 06:47:55 -0500 Subject: [PATCH 0434/1476] Update adkernelBidAdapter.js for client alias (#6055) * Update adkernelBidAdapter.js for client alias * Update test unit to match new alias bidder count --- modules/adkernelBidAdapter.js | 2 +- test/spec/modules/adkernelBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 29990ef1c44..c34902eda46 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 70789c4b933..4d3dca7f344 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,7 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(8); + expect(spec.aliases).to.have.lengthOf(9); }); }); From 8107da60820c5b5305b391c025d076cff35bc232 Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Fri, 4 Dec 2020 14:08:49 +0100 Subject: [PATCH 0435/1476] Add GVLID RichaudienceAdapter (#6071) * Add GVLID RichaudienceAdapter * Update package-lock.json Co-authored-by: sgimenez --- modules/richaudienceBidAdapter.js | 1 + .../modules/richaudienceBidAdapter_spec.js | 27 +++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 3b899e2179d..a6b4202fc91 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -9,6 +9,7 @@ let REFERER = ''; export const spec = { code: BIDDER_CODE, + gvlid: 108, aliases: ['ra'], supportedMediaTypes: [BANNER, VIDEO], diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index bdf50f2d7f5..90723fb863f 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -353,20 +353,25 @@ describe('Richaudience adapter tests', function () { cmpApi: 'iab', timeout: 5000, allowAuctionWithoutConsent: true + }, + userSync: { + userIds: [{ + name: 'id5Id', + params: { + partner: 173, // change to the Partner Number you received from ID5 + pd: 'MT1iNTBjY...' // optional, see table below for a link to how to generate this + }, + storage: { + type: 'html5', // "html5" is the required storage type + name: 'id5id', // "id5id" is the required storage name + expires: 90, // storage lasts for 90 days + refreshInSeconds: 8 * 3600 // refresh ID every 8 hours to ensure it's fresh + } + }], + auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules } }); it('Verify build id5', function () { - DEFAULT_PARAMS_WO_OPTIONAL[0].userId = {}; - DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: 'id5-user-id' }; - - var request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, DEFAULT_PARAMS_GDPR); - var requestContent = JSON.parse(request[0].data); - - expect(requestContent.user).to.deep.equal([{ - 'userId': 'id5-user-id', - 'source': 'id5-sync.com' - }]); - var request; DEFAULT_PARAMS_WO_OPTIONAL[0].userId = {}; DEFAULT_PARAMS_WO_OPTIONAL[0].userId.id5id = { uid: 1 }; From 8ea3f3b2727439464858bcfcbbc077d863e45c58 Mon Sep 17 00:00:00 2001 From: susyt Date: Fri, 4 Dec 2020 08:45:09 -0800 Subject: [PATCH 0436/1476] GumGum: sets mediaType of bidRequest depending on product id (#6066) * adds support for zone and pubId params * adds support for iriscat field * sets mediatype depending on product id * Update doc for mediaType needed for video products --- modules/gumgumBidAdapter.js | 5 ++-- modules/gumgumBidAdapter.md | 27 ++++++++++++++++- test/spec/modules/gumgumBidAdapter_spec.js | 34 +++++++++++++++------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 3206b7e1727..2cb5ce61064 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -403,6 +403,7 @@ function interpretResponse (serverResponse, bidRequest) { } = Object.assign(defaultResponse, serverResponseBody) let data = bidRequest.data || {} let product = data.pi + let mediaType = (product === 6 || product === 7) ? VIDEO : BANNER let isTestUnit = (product === 3 && data.si === 9) let sizes = utils.parseSizesInput(bidRequest.sizes) let [width, height] = sizes[0].split('x') @@ -424,9 +425,9 @@ function interpretResponse (serverResponse, bidRequest) { bidResponses.push({ // dealId: DEAL_ID, // referrer: REFERER, - ...(product === 7 && { vastXml: markup, mediaType: VIDEO }), ad: wrapper ? getWrapperCode(wrapper, Object.assign({}, serverResponseBody, { bidRequest })) : markup, - ...(product === 6 && {ad: markup}), + ...(mediaType === VIDEO && {ad: markup, vastXml: markup}), + mediaType, cpm: isTestUnit ? 0.1 : cpm, creativeId, currency: cur || 'USD', diff --git a/modules/gumgumBidAdapter.md b/modules/gumgumBidAdapter.md index f47666e9628..7b4f0c98ea7 100644 --- a/modules/gumgumBidAdapter.md +++ b/modules/gumgumBidAdapter.md @@ -8,7 +8,10 @@ Maintainer: engineering@gumgum.com # Description -GumGum adapter for Prebid.js 1.0 +GumGum adapter for Prebid.js +Please note that both video and in-video products require a mediaType of video. +All other products (in-screen, slot, native) should have a mediaType of banner. + # Test Parameters ``` @@ -16,6 +19,11 @@ var adUnits = [ { code: 'test-div', sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, bids: [ { bidder: 'gumgum', @@ -28,6 +36,11 @@ var adUnits = [ },{ code: 'test-div', sizes: [[300, 50]], + mediaTypes: { + banner: { + sizes: [[1, 1]], + } + }, bids: [ { bidder: 'gumgum', @@ -40,6 +53,18 @@ var adUnits = [ },{ code: 'test-div', sizes: [[300, 50]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + minduration: 1, + maxduration: 2, + linearity: 2, + startdelay: 1, + placement: 1, + protocols: [1, 2] + } + } bids: [ { bidder: 'gumgum', diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 701ce9a7e81..52a3a21db4e 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/gumgumBidAdapter.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; const ENDPOINT = 'https://g2.gumgum.com/hbid/imp'; const JCSI = { t: 0, rq: 8, pbv: '$prebid.version$' } @@ -462,16 +463,15 @@ describe('gumgumAdapter', function () { pi: 3 } let expectedResponse = { - 'ad': '

I am an ad

', - 'cpm': 0, - 'creativeId': 29593, - 'currency': 'USD', - 'height': '250', - 'netRevenue': true, - 'requestId': 12345, - 'width': '300', - // dealId: DEAL_ID, - // referrer: REFERER, + ad: '

I am an ad

', + cpm: 0, + creativeId: 29593, + currency: 'USD', + height: '250', + netRevenue: true, + requestId: 12345, + width: '300', + mediaType: BANNER, ttl: 60 }; @@ -552,6 +552,20 @@ describe('gumgumAdapter', function () { const decodedResponse = JSON.parse(atob(bidResponse)); expect(decodedResponse.jcsi).to.eql(JCSI); }); + + it('sets the correct mediaType depending on product', function () { + const bannerBidResponse = spec.interpretResponse({ body: serverResponse }, bidRequest)[0]; + const invideoBidResponse = spec.interpretResponse({ body: serverResponse }, { ...bidRequest, data: { pi: 6 } })[0]; + const videoBidResponse = spec.interpretResponse({ body: serverResponse }, { ...bidRequest, data: { pi: 7 } })[0]; + expect(bannerBidResponse.mediaType).to.equal(BANNER); + expect(invideoBidResponse.mediaType).to.equal(VIDEO); + expect(videoBidResponse.mediaType).to.equal(VIDEO); + }); + + it('sets a vastXml property if mediaType is video', function () { + const videoBidResponse = spec.interpretResponse({ body: serverResponse }, { ...bidRequest, data: { pi: 7 } })[0]; + expect(videoBidResponse.vastXml).to.exist; + }); }) describe('getUserSyncs', function () { const syncOptions = { From 93536f0e5aa56ec8b3284feafeee698eaa974a74 Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Fri, 4 Dec 2020 13:29:42 -0500 Subject: [PATCH 0437/1476] pubgenius: remove video cache key to be future-proof (#6081) --- modules/pubgeniusBidAdapter.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 88e85a4fd7a..5c750e66c25 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -253,10 +253,6 @@ function interpretBid(bid) { bidResponse.vastXml = bid.adm; } - if (pbadapter.cacheKey) { - bidResponse.videoCacheKey = pbadapter.cacheKey; - } - bidResponse.mediaType = VIDEO; break; default: // banner by default From b9a4cc6eb780e79e4f5775d3519af8ad38f23d0a Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Mon, 7 Dec 2020 10:36:18 -0500 Subject: [PATCH 0438/1476] fix pubgenius unit test (#6090) --- test/spec/modules/pubgeniusBidAdapter_spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index c0b05510707..382199dcffc 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -472,7 +472,6 @@ describe('pubGENIUS adapter', () => { ...expectedBidResponse, vastUrl: 'http://vasturl/cache?id=x', vastXml: 'fake_creative', - videoCacheKey: 'x', mediaType: VIDEO, }; From 5f6dab3008d4cca306b6d27a1d27937723fc2b89 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 7 Dec 2020 07:39:25 -0800 Subject: [PATCH 0439/1476] 6012: Fix for passing US Privacy string in buildVideoUrl and buildAdpodVideoUrl (#6075) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * pass USP consent string in dfpVideoUrl and dfpAdpodVideoUrl * added test cases --- modules/dfpAdServerVideo.js | 7 ++++ test/spec/modules/dfpAdServerVideo_spec.js | 43 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 554c44aa708..13677c90bae 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -8,6 +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 { uspDataHandler } from '../src/adapterManager.js'; import events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; @@ -100,6 +101,9 @@ export function buildDfpVideoUrl(options) { const descriptionUrl = getDescriptionUrl(bid, options, 'params'); if (descriptionUrl) { queryParams.description_url = descriptionUrl; } + const uspConsent = uspDataHandler.getConsentData(); + if (uspConsent) { queryParams.us_privacy = uspConsent; } + return buildUrl({ protocol: 'https', host: 'securepubads.g.doubleclick.net', @@ -183,6 +187,9 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { { cust_params: encodedCustomParams } ); + const uspConsent = uspDataHandler.getConsentData(); + if (uspConsent) { queryParams.us_privacy = uspConsent; } + const masterTag = buildUrl({ protocol: 'https', host: 'securepubads.g.doubleclick.net', diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index c0ecb9cad5e..ed9c968cfa2 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -7,6 +7,7 @@ import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { targeting } from 'src/targeting.js'; import { auctionManager } from 'src/auctionManager.js'; +import { uspDataHandler } from 'src/adapterManager.js'; import * as adpod from 'modules/adpod.js'; import { server } from 'test/mocks/xhr.js'; @@ -115,6 +116,44 @@ describe('The DFP video support module', function () { expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); }); + it('should include the us_privacy key when USP Consent is available', function () { + let uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); + uspDataHandlerStub.returns('1YYY'); + + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); + expect(queryObject.us_privacy).to.equal('1YYY'); + uspDataHandlerStub.restore(); + }); + + it('should not include the us_privacy key when USP Consent is not available', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); + expect(queryObject.us_privacy).to.equal(undefined); + }); + describe('special targeting unit test', function () { const allTargetingData = { 'hb_format': 'video', @@ -350,6 +389,8 @@ describe('The DFP video support module', function () { it('should return masterTag url', function() { amStub.returns(getBidsReceived()); + let uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); + uspDataHandlerStub.returns('1YYY'); let url; parse(buildAdpodVideoUrl({ code: 'adUnitCode-1', @@ -380,10 +421,12 @@ describe('The DFP video support module', function () { expect(queryParams).to.have.property('unviewed_position_start', '1'); expect(queryParams).to.have.property('url'); expect(queryParams).to.have.property('cust_params'); + expect(queryParams).to.have.property('us_privacy', '1YYY'); const custParams = utils.parseQS(decodeURIComponent(queryParams.cust_params)); expect(custParams).to.have.property('hb_cache_id', '123'); expect(custParams).to.have.property('hb_pb_cat_dur', '15.00_395_15s,15.00_406_30s,10.00_395_15s'); + uspDataHandlerStub.restore(); } }); From dbd0d84e30beb1547f35663e0c30d6efc45b60b0 Mon Sep 17 00:00:00 2001 From: Reinout Stevens Date: Mon, 7 Dec 2020 16:40:06 +0100 Subject: [PATCH 0440/1476] fix prebid server playerwidth and height (#6073) Co-authored-by: Reinout Stevens --- modules/prebidServerBidAdapter/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7c7962781d2..d90572d1093 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -750,8 +750,8 @@ const OPEN_RTB_PROTOCOL = { if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { bidObject.mediaType = VIDEO; let sizes = bidRequest.sizes && bidRequest.sizes[0]; - bidObject.playerHeight = sizes[0]; - bidObject.playerWidth = sizes[1]; + bidObject.playerWidth = sizes[0]; + bidObject.playerHeight = sizes[1]; // try to get cache values from 'response.ext.prebid.cache.js' // else try 'bid.ext.prebid.targeting' as fallback From 93f393abf070ff83ce2cc9cefb376864816a1feb Mon Sep 17 00:00:00 2001 From: Paul de Rosanbo Date: Mon, 7 Dec 2020 16:40:51 +0100 Subject: [PATCH 0441/1476] Fix iframe __tcfapi arguments (#6058) * Fix __tcfapi declaration in iframe * Add tests for cmp declaration in iframe --- modules/consentManagement.js | 58 ++++++++++++++------- test/spec/modules/consentManagement_spec.js | 18 +++++++ 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 1060fdb5cc5..67af2baf959 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -199,29 +199,51 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) { let apiName = (cmpVersion === 2) ? '__tcfapi' : '__cmp'; + let callId = Math.random() + ''; + let callName = `${apiName}Call`; + /* Setup up a __cmp function to do the postMessage and stash the callback. This function behaves (from the caller's perspective identicially to the in-frame __cmp call */ - window[apiName] = function (cmd, arg, callback) { - let callId = Math.random() + ''; - let callName = `${apiName}Call`; - let msg = { - [callName]: { - command: cmd, - parameter: arg, - callId: callId - } - }; - if (cmpVersion !== 1) msg[callName].version = cmpVersion; + if (cmpVersion === 2) { + window[apiName] = function (cmd, cmpVersion, callback, arg) { + let msg = { + [callName]: { + command: cmd, + version: cmpVersion, + parameter: arg, + callId: callId + } + }; - cmpCallbacks[callId] = callback; - cmpFrame.postMessage(msg, '*'); - } + cmpCallbacks[callId] = callback; + cmpFrame.postMessage(msg, '*'); + } - /** when we get the return message, call the stashed callback */ - window.addEventListener('message', readPostMessageResponse, false); + /** when we get the return message, call the stashed callback */ + window.addEventListener('message', readPostMessageResponse, false); - // call CMP - window[apiName](commandName, undefined, moduleCallback); + // call CMP + window[apiName](commandName, cmpVersion, moduleCallback); + } else { + window[apiName] = function (cmd, arg, callback) { + let msg = { + [callName]: { + command: cmd, + parameter: arg, + callId: callId + } + }; + + cmpCallbacks[callId] = callback; + cmpFrame.postMessage(msg, '*'); + } + + /** when we get the return message, call the stashed callback */ + window.addEventListener('message', readPostMessageResponse, false); + + // call CMP + window[apiName](commandName, undefined, moduleCallback); + } function readPostMessageResponse(event) { let cmpDataPkgName = `${apiName}Return`; diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index cf5c578502f..5e9b0f07f46 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -537,6 +537,15 @@ describe('consentManagement', function () { // from CMP window postMessage listener. testIFramedPage('with/JSON response', false, 'encoded_consent_data_via_post_message', 1); testIFramedPage('with/String response', true, 'encoded_consent_data_via_post_message', 1); + + it('should contain correct V1 CMP definition', (done) => { + setConsentConfig(goodConfigWithAllowAuction); + requestBidsHook(() => { + const nbArguments = window.__cmp.toString().split('\n')[0].split(', ').length; + expect(nbArguments).to.equal(3); + done(); + }, {}); + }); }); describe('v2 CMP workflow for iframe pages:', function () { @@ -562,6 +571,15 @@ describe('consentManagement', function () { testIFramedPage('with/JSON response', false, 'abc12345234', 2); testIFramedPage('with/String response', true, 'abc12345234', 2); + + it('should contain correct v2 CMP definition', (done) => { + setConsentConfig(goodConfigWithAllowAuction); + requestBidsHook(() => { + const nbArguments = window.__tcfapi.toString().split('\n')[0].split(', ').length; + expect(nbArguments).to.equal(4); + done(); + }, {}); + }); }); }); From 12d55b951decf7f2786f3cae18d2fa34df76d4c2 Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Tue, 8 Dec 2020 18:50:33 +0000 Subject: [PATCH 0442/1476] Feature/vmuid connectid rebrand (#6045) * Key name change from vmuid to vmcid * Change the userId key name to vmconnectid * Support new key in response payload + remain backward compatible. Co-authored-by: slimkrazy --- modules/userId/eids.js | 4 +-- modules/verizonMediaIdSystem.js | 11 +++--- modules/verizonMediaSystemId.md | 4 +-- test/spec/modules/aolBidAdapter_spec.js | 2 +- .../spec/modules/verizonMediaIdSystem_spec.js | 34 +++++++++++++++---- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 2c627416341..d714d09962d 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -158,8 +158,8 @@ const USER_IDS_CONFIG = { atype: 1 }, - // Verizon Media - 'vmuid': { + // Verizon Media ConnectID + 'connectid': { source: 'verizonmedia.com', atype: 1 }, diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js index 617561765cc..f39909f6666 100644 --- a/modules/verizonMediaIdSystem.js +++ b/modules/verizonMediaIdSystem.js @@ -12,7 +12,7 @@ import * as utils from '../src/utils.js'; const MODULE_NAME = 'verizonMediaId'; const VENDOR_ID = 25; const PLACEHOLDER = '__PIXEL_ID__'; -const VMUID_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; +const VMCID_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; function isEUConsentRequired(consentData) { return !!(consentData && consentData.gdpr && consentData.gdpr.gdprApplies); @@ -33,13 +33,14 @@ export const verizonMediaIdSubmodule = { /** * decode the stored id value for passing to bid requests * @function - * @returns {{vmuid: string} | undefined} + * @returns {{connectid: string} | undefined} */ decode(value) { - return (value && typeof value.vmuid === 'string') ? {vmuid: value.vmuid} : undefined; + return (typeof value === 'object' && (value.connectid || value.vmuid)) + ? {connectid: value.connectid || value.vmuid} : undefined; }, /** - * get the VerizonMedia Id + * Gets the Verizon Media Connect ID * @function * @param {SubmoduleConfig} [config] * @param {ConsentData} [consentData] @@ -83,7 +84,7 @@ export const verizonMediaIdSubmodule = { callback(); } }; - const endpoint = VMUID_ENDPOINT.replace(PLACEHOLDER, params.pixelId); + const endpoint = VMCID_ENDPOINT.replace(PLACEHOLDER, params.pixelId); let url = `${params.endpoint || endpoint}?${utils.formatQS(data)}`; verizonMediaIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); }; diff --git a/modules/verizonMediaSystemId.md b/modules/verizonMediaSystemId.md index 8d0e0bddaa9..c0d315dc754 100644 --- a/modules/verizonMediaSystemId.md +++ b/modules/verizonMediaSystemId.md @@ -10,9 +10,9 @@ pbjs.setConfig({ userIds: [{ name: 'verizonMediaId', storage: { - name: 'vmuid', + name: 'vmcid', type: 'html5', - expires: 30 + expires: 15 }, params: { pixelId: 58776, diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 11e1a317b70..8e74e19f420 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -93,7 +93,7 @@ describe('AolAdapter', function () { const USER_ID_DATA = { criteoId: SUPPORTED_USER_ID_SOURCES['criteo.com'], - vmuid: SUPPORTED_USER_ID_SOURCES['verizonmedia.com'], + connectid: SUPPORTED_USER_ID_SOURCES['verizonmedia.com'], idl_env: SUPPORTED_USER_ID_SOURCES['liveramp.com'], lipb: { lipbid: SUPPORTED_USER_ID_SOURCES['liveintent.com'], diff --git a/test/spec/modules/verizonMediaIdSystem_spec.js b/test/spec/modules/verizonMediaIdSystem_spec.js index a30be5a2569..c5d743235d6 100644 --- a/test/spec/modules/verizonMediaIdSystem_spec.js +++ b/test/spec/modules/verizonMediaIdSystem_spec.js @@ -165,12 +165,34 @@ describe('Verizon Media ID Submodule', () => { }); describe('decode()', () => { - const VALID_API_RESPONSE = { - vmuid: '1234' - }; - it('should return a newly constructed object with the vmuid property', () => { - expect(verizonMediaIdSubmodule.decode(VALID_API_RESPONSE)).to.deep.equal(VALID_API_RESPONSE); - expect(verizonMediaIdSubmodule.decode(VALID_API_RESPONSE)).to.not.equal(VALID_API_RESPONSE); + const VALID_API_RESPONSES = [{ + key: 'vmiud', + expected: '1234', + payload: { + vmuid: '1234' + } + }, + { + key: 'connectid', + expected: '4567', + payload: { + connectid: '4567' + } + }, + { + key: 'both', + expected: '4567', + payload: { + vmuid: '1234', + connectid: '4567' + } + }]; + VALID_API_RESPONSES.forEach(responseData => { + it('should return a newly constructed object with the connectid for a payload with ${responseData.key} key(s)', () => { + expect(verizonMediaIdSubmodule.decode(responseData.payload)).to.deep.equal( + {connectid: responseData.expected} + ); + }); }); [{}, '', {foo: 'bar'}].forEach((response) => { From 4c047f91e1c3bbc0b495427630f031753c561a42 Mon Sep 17 00:00:00 2001 From: John Salis Date: Tue, 8 Dec 2020 23:48:34 -0500 Subject: [PATCH 0443/1476] Beachfront adapter: Add banner tagid param (#6086) * add tagid to banner request * bump version * update test case * run tests Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 3 ++- test/spec/modules/beachfrontBidAdapter_spec.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 4b30f47e2cf..44755a78864 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.14'; +const ADAPTER_VERSION = '1.15'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; @@ -387,6 +387,7 @@ function createBannerRequestData(bids, bidderRequest) { slot: bid.adUnitCode, id: getBannerBidParam(bid, 'appId'), bidfloor: getBannerBidParam(bid, 'bidfloor'), + tagid: getBannerBidParam(bid, 'tagid'), sizes: getBannerSizes(bid) }; }); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 661780ffac0..43c71dd6349 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -336,6 +336,7 @@ describe('BeachfrontAdapter', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; + bidRequest.params.tagid = '7cd7a7b4-ef3f-4aeb-9565-3627f255fa10'; bidRequest.mediaTypes = { banner: { sizes: [ width, height ] @@ -354,6 +355,7 @@ describe('BeachfrontAdapter', function () { slot: bidRequest.adUnitCode, id: bidRequest.params.appId, bidfloor: bidRequest.params.bidfloor, + tagid: bidRequest.params.tagid, sizes: [{ w: width, h: height }] } ]); From cbd1169204dbf7f0bf0a3ea8fea4a85241f8dc11 Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Wed, 9 Dec 2020 12:50:47 +0800 Subject: [PATCH 0444/1476] changed events endpoint (#6088) --- modules/jixieBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index d3ae090964c..7c6e0027482 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -9,7 +9,7 @@ import { Renderer } from '../src/Renderer.js'; export const storage = getStorageManager(); const BIDDER_CODE = 'jixie'; -const EVENTS_URL = 'https://jxhbtrackers.azurewebsites.net/sync/evt?'; +const EVENTS_URL = 'https://hbtra.jixie.io/sync/hb?'; const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.io/jxhboutstream.js'; const REQUESTS_URL = 'https://hb.jixie.io/v2/hbpost'; const sidTTLMins_ = 30; From 478e0455b952e7f67e3b6124a862c8a871ee618b Mon Sep 17 00:00:00 2001 From: Brandon Ling <51931757+blingster7@users.noreply.github.com> Date: Wed, 9 Dec 2020 02:29:33 -0500 Subject: [PATCH 0445/1476] [Triplelift] Fix FPD key-value pairs logic (#6065) * follow spec to parse fpd * ad unit support stub * adunit method * ad unit etc * typo * fix test * typo * change to const --- modules/tripleliftBidAdapter.js | 31 +++++++++++++++++-- .../spec/modules/tripleliftBidAdapter_spec.js | 31 ++++++++++++++----- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index d54d76efb41..4679c1faf62 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -122,6 +122,9 @@ function _buildPostBody(bidRequests) { } else if (bidRequest.mediaTypes.banner) { imp.banner = { format: _sizes(bidRequest.sizes) }; }; + if (!utils.isEmpty(bidRequest.fpd)) { + imp.fpd = _getAdUnitFpd(bidRequest.fpd); + } return imp; }); @@ -183,12 +186,34 @@ function _getFloor (bid) { } function _getGlobalFpd() { - let fpd = {}; + const fpd = {}; + const context = {} + const user = {}; + const fpdContext = Object.assign({}, config.getConfig('fpd.context')); const fpdUser = Object.assign({}, config.getConfig('fpd.user')); - _addEntries(fpd, fpdContext); - _addEntries(fpd, fpdUser); + _addEntries(context, fpdContext); + _addEntries(user, fpdUser); + + if (!utils.isEmpty(context)) { + fpd.context = context; + } + if (!utils.isEmpty(user)) { + fpd.user = user; + } + return fpd; +} + +function _getAdUnitFpd(adUnitFpd) { + const fpd = {}; + const context = {}; + + _addEntries(context, adUnitFpd.context); + + if (!utils.isEmpty(context)) { + fpd.context = context; + } return fpd; } diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index b417876f276..82578424027 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -143,6 +143,14 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + fpd: { + context: { + pbAdSlot: 'homepage-top-rect', + data: { + adUnitSpecificAttribute: 123 + } + } + } }, { bidder: 'triplelift', @@ -597,17 +605,19 @@ describe('triplelift adapter', function () { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); expect(request.data.imp[0].floor).to.equal(1.99); }); - it('should send fpd on root level ext if kvps are available', function() { + it('should send global config fpd if kvps are available', function() { const sens = null; const category = ['news', 'weather', 'hurricane']; const pmp_elig = 'true'; const fpd = { context: { - pmp_elig, - category, + pmp_elig: pmp_elig, + data: { + category: category + } }, user: { - sens, + sens: sens, } } sandbox.stub(config, 'getConfig').callsFake(key => { @@ -618,9 +628,16 @@ describe('triplelift adapter', function () { }); const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); const { data: payload } = request; - expect(payload.ext.fpd).to.not.haveOwnProperty('sens'); - expect(payload.ext.fpd).to.haveOwnProperty('category'); - expect(payload.ext.fpd).to.haveOwnProperty('pmp_elig'); + expect(payload.ext.fpd.user).to.not.exist; + expect(payload.ext.fpd.context.data).to.haveOwnProperty('category'); + expect(payload.ext.fpd.context).to.haveOwnProperty('pmp_elig'); + }); + it('should send ad unit fpd if kvps are available', function() { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + expect(request.data.imp[0].fpd.context).to.haveOwnProperty('pbAdSlot'); + expect(request.data.imp[0].fpd.context).to.haveOwnProperty('data'); + expect(request.data.imp[0].fpd.context.data).to.haveOwnProperty('adUnitSpecificAttribute'); + expect(request.data.imp[1].fpd).to.not.exist; }); }); From dd51f245abe600801703c70ca688fb55181a8d78 Mon Sep 17 00:00:00 2001 From: nyakove <43004249+nyakove@users.noreply.github.com> Date: Wed, 9 Dec 2020 15:40:23 +0200 Subject: [PATCH 0446/1476] Add new adapter - adWMGBidAdapter (#6070) * Add adWMG Bid Adapter * Fix unit tests Co-authored-by: Mikhail Dykun --- modules/adWMGBidAdapter.js | 297 ++++++++++++++++++++++ modules/adWMGBidAdapter.md | 34 +++ test/spec/modules/adWMGBidAdapter_spec.js | 292 +++++++++++++++++++++ 3 files changed, 623 insertions(+) create mode 100644 modules/adWMGBidAdapter.js create mode 100644 modules/adWMGBidAdapter.md create mode 100644 test/spec/modules/adWMGBidAdapter_spec.js diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js new file mode 100644 index 00000000000..3a0a8a22274 --- /dev/null +++ b/modules/adWMGBidAdapter.js @@ -0,0 +1,297 @@ +'use strict'; + +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'adWMG'; +const ENDPOINT = 'https://rtb.adwmg.com/prebid'; +let SYNC_ENDPOINT = 'https://rtb.adwmg.com/cphb.html?'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['wmg'], + supportedMediaTypes: [BANNER], + isBidRequestValid: (bid) => { + if (bid.bidder !== BIDDER_CODE) { + return false; + } + + if (!(bid.params.publisherId)) { + return false; + } + + return true; + }, + buildRequests: (validBidRequests, bidderRequest) => { + const timeout = bidderRequest.timeout || 0; + const debug = config.getConfig('debug') || false; + const referrer = bidderRequest.refererInfo.referer; + const locale = window.navigator.language && window.navigator.language.length > 0 ? window.navigator.language.substr(0, 2) : ''; + const domain = config.getConfig('publisherDomain') || (window.location && window.location.host ? window.location.host : ''); + const ua = window.navigator.userAgent.toLowerCase(); + const additional = spec.parseUserAgent(ua); + + return validBidRequests.map(bidRequest => { + const adUnit = { + code: bidRequest.adUnitCode, + bids: { + bidder: bidRequest.bidder, + params: bidRequest.params + }, + mediaTypes: bidRequest.mediaTypes + }; + + if (bidRequest.hasOwnProperty('sizes') && bidRequest.sizes.length > 0) { + adUnit.sizes = bidRequest.sizes; + } + + const request = { + auctionId: bidRequest.auctionId, + requestId: bidRequest.bidId, + bidRequestsCount: bidRequest.bidRequestsCount, + bidderRequestId: bidRequest.bidderRequestId, + transactionId: bidRequest.transactionId, + referrer: referrer, + timeout: timeout, + adUnit: adUnit, + locale: locale, + domain: domain, + os: additional.os, + osv: additional.osv, + devicetype: additional.devicetype + }; + + if (bidderRequest.gdprConsent) { + request.gdpr = { + applies: bidderRequest.gdprConsent.gdprApplies, + consentString: bidderRequest.gdprConsent.consentString + }; + } + + /* if (bidderRequest.uspConsent) { + request.uspConsent = bidderRequest.uspConsent; + } + */ + if (bidRequest.userId && bidRequest.userId.pubcid) { + request.userId = { + pubcid: bidRequest.userId.pubcid + }; + } + + if (debug) { + request.debug = debug; + } + + return { + method: 'POST', + url: ENDPOINT, + data: JSON.stringify(request) + } + }); + }, + interpretResponse: (serverResponse) => { + const bidResponses = []; + + if (serverResponse.body) { + const response = serverResponse.body; + const bidResponse = { + requestId: response.requestId, + cpm: response.cpm, + width: response.width, + height: response.height, + creativeId: response.creativeId, + currency: response.currency, + netRevenue: response.netRevenue, + ttl: response.ttl, + ad: response.ad, + }; + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + if (gdprConsent) { + SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0)); + } + + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'gdpr_consent', gdprConsent.consentString); + } + + /* if (uspConsent) { + SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'us_privacy', uspConsent); + } */ + let syncs = []; + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: SYNC_ENDPOINT + }); + } + return syncs; + }, + parseUserAgent: (ua) => { + function detectDevice() { + if (/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i + .test(ua.toLowerCase())) { + return 5; + } + if (/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i + .test(ua.toLowerCase())) { + return 4; + } + if (/smart[-_\s]?tv|hbbtv|appletv|googletv|hdmi|netcast|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b/i + .test(ua.toLowerCase())) { + return 3; + } + return 2; + } + + function detectOs() { + const module = { + options: [], + header: [navigator.platform, ua, navigator.appVersion, navigator.vendor, window.opera], + dataos: [{ + name: 'Windows Phone', + value: 'Windows Phone', + version: 'OS' + }, + { + name: 'Windows', + value: 'Win', + version: 'NT' + }, + { + name: 'iOS', + value: 'iPhone', + version: 'OS' + }, + { + name: 'iOS', + value: 'iPad', + version: 'OS' + }, + { + name: 'Kindle', + value: 'Silk', + version: 'Silk' + }, + { + name: 'Android', + value: 'Android', + version: 'Android' + }, + { + name: 'PlayBook', + value: 'PlayBook', + version: 'OS' + }, + { + name: 'BlackBerry', + value: 'BlackBerry', + version: '/' + }, + { + name: 'Macintosh', + value: 'Mac', + version: 'OS X' + }, + { + name: 'Linux', + value: 'Linux', + version: 'rv' + }, + { + name: 'Palm', + value: 'Palm', + version: 'PalmOS' + } + ], + init: function () { + var agent = this.header.join(' '); + var os = this.matchItem(agent, this.dataos); + return { + os + }; + }, + + getVersion: function (name, version) { + if (name === 'Windows') { + switch (parseFloat(version).toFixed(1)) { + case '5.0': + return '2000'; + case '5.1': + return 'XP'; + case '5.2': + return 'Server 2003'; + case '6.0': + return 'Vista'; + case '6.1': + return '7'; + case '6.2': + return '8'; + case '6.3': + return '8.1'; + default: + return version || 'other'; + } + } else return version || 'other'; + }, + + matchItem: function (string, data) { + var i = 0; + var j = 0; + var regex, regexv, match, matches, version; + + for (i = 0; i < data.length; i += 1) { + regex = new RegExp(data[i].value, 'i'); + match = regex.test(string); + if (match) { + regexv = new RegExp(data[i].version + '[- /:;]([\\d._]+)', 'i'); + matches = string.match(regexv); + version = ''; + if (matches) { + if (matches[1]) { + matches = matches[1]; + } + } + if (matches) { + matches = matches.split(/[._]+/); + for (j = 0; j < matches.length; j += 1) { + if (j === 0) { + version += matches[j] + '.'; + } else { + version += matches[j]; + } + } + } else { + version = 'other'; + } + return { + name: data[i].name, + version: this.getVersion(data[i].name, version) + }; + } + } + return { + name: 'unknown', + version: 'other' + }; + } + }; + + var e = module.init(); + + return { + os: e.os.name || '', + osv: e.os.version || '' + } + } + + return {devicetype: detectDevice(), os: detectOs().os, osv: detectOs().osv} + } +} +registerBidder(spec); diff --git a/modules/adWMGBidAdapter.md b/modules/adWMGBidAdapter.md new file mode 100644 index 00000000000..8c277b803db --- /dev/null +++ b/modules/adWMGBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: adWMG Adapter +Module Type: Bidder Adapter +Maintainer: wbid@adwmg.com +``` + +# Description + +Module that connects to adWMG demand sources to fetch bids. Supports 'banner' ad format. + +# Bid Parameters + +| Key | Required | Example | Description | +| --- | -------- | ------- | ----------- | +| `publisherId` | yes | `'5cebea3c9eea646c7b623d5e'` | publisher ID from WMG Dashboard | +| `IABCategories` | no | `['IAB1', 'IAB5']` | IAB ad categories for adUnit | + + +# Test Parameters + +```javascript +var adUnits = [{ + code: 'wmg-test-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'adWMG', + params: { + publisherId: '5cebea3c9eea646c7b623d5e', + IABCategories: ['IAB1', 'IAB5'] + }, + }] +}] \ No newline at end of file diff --git a/test/spec/modules/adWMGBidAdapter_spec.js b/test/spec/modules/adWMGBidAdapter_spec.js new file mode 100644 index 00000000000..5c2364d454c --- /dev/null +++ b/test/spec/modules/adWMGBidAdapter_spec.js @@ -0,0 +1,292 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adWMGBidAdapter.js'; +import { config } from 'src/config.js'; + +describe('adWMGBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid; + beforeEach(function() { + bid = { + bidder: 'adWMG', + params: { + publisherId: '5cebea3c9eea646c7b623d5e' + }, + mediaTypes: { + banner: { + size: [[300, 250]] + } + } + }; + }); + + it('should return true when valid bid request is set', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when bidder is not set to "adWMG"', function() { + bid.bidder = 'bidder'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when \'publisherId\' param are not set', function() { + delete bid.params.publisherId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('parseUserAgent', function() { + let ua_desktop, ua_mobile, ua_tv, ua_tablet; + beforeEach(function() { + ua_desktop = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'; + ua_tv = 'Mozilla/5.0 (Linux; NetCast; U) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.31 SmartTV/7.0'; + ua_mobile = 'Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G610M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/7.2 Chrome/59.0.3071.125 Mobile Safari/537.36'; + ua_tablet = 'Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53'; + }); + + it('should return correct device type: desktop', function() { + let userDeviceInfo = spec.parseUserAgent(ua_desktop); + expect(userDeviceInfo.devicetype).to.equal(2); + }); + + it('should return correct device type: TV', function() { + let userDeviceInfo = spec.parseUserAgent(ua_tv); + expect(userDeviceInfo.devicetype).to.equal(3); + }); + + it('should return correct device type: mobile', function() { + let userDeviceInfo = spec.parseUserAgent(ua_mobile); + expect(userDeviceInfo.devicetype).to.equal(4); + }); + + it('should return correct device type: tablet', function() { + let userDeviceInfo = spec.parseUserAgent(ua_tablet); + expect(userDeviceInfo.devicetype).to.equal(5); + }); + + it('should return correct OS name', function() { + let userDeviceInfo = spec.parseUserAgent(ua_desktop); + expect(userDeviceInfo.os).to.equal('Windows'); + }); + + it('should return correct OS version', function() { + let userDeviceInfo = spec.parseUserAgent(ua_desktop); + expect(userDeviceInfo.osv).to.equal('10.0'); + }); + }); + + describe('buildRequests', function () { + let bidRequests; + beforeEach(function() { + bidRequests = [ + { + bidder: 'adWMG', + adUnitCode: 'adwmg-test-ad', + auctionId: 'test-auction-id', + bidId: 'test-bid-id', + bidRequestsCount: 1, + bidderRequestId: 'bidderrequestid123', + transactionId: 'transaction-id-123', + sizes: [[300, 250]], + requestId: 'requestid123', + params: { + floorPrice: 100, + currency: 'USD' + }, + mediaTypes: { + banner: { + size: [[300, 250]] + } + }, + userId: { + pubcid: 'pubc-id-123' + } + }, { + bidder: 'adWMG', + adUnitCode: 'adwmg-test-ad-2', + auctionId: 'test-auction-id-2', + bidId: 'test-bid-id-2', + bidRequestsCount: 1, + bidderRequestId: 'bidderrequestid456', + transactionId: 'transaction-id-456', + sizes: [[320, 50]], + requestId: 'requestid456', + params: { + floorPrice: 100, + currency: 'USD' + }, + mediaTypes: { + banner: { + size: [[320, 50]] + } + }, + userId: { + pubcid: 'pubc-id-456' + } + } + ]; + }); + + let bidderRequest = { + refererInfo: { + referer: 'https://test.com' + }, + gdprConsent: { + consentString: 'CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_X_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + } + }; + + it('should not contain a sizes when sizes is not set', function() { + delete bidRequests[0].sizes; + delete bidRequests[1].sizes; + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).sizes).to.be.an('undefined'); + expect(JSON.parse(requests[1].data).sizes).to.be.an('undefined'); + }); + + it('should not contain a userId when userId is not set', function() { + delete bidRequests[0].userId; + delete bidRequests[1].userId; + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).userId).to.be.an('undefined'); + expect(JSON.parse(requests[1].data).userId).to.be.an('undefined'); + }); + + it('should have a post method', function() { + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].method).to.equal('POST'); + expect(requests[1].method).to.equal('POST'); + }); + + it('should contain a request id equals to the bid id', function() { + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).requestId).to.equal(bidRequests[0].bidId); + expect(JSON.parse(requests[1].data).requestId).to.equal(bidRequests[1].bidId); + }); + + it('should have an url that match the default endpoint', function() { + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.equal('https://rtb.adwmg.com/prebid'); + expect(requests[1].url).to.equal('https://rtb.adwmg.com/prebid'); + }); + + it('should contain GDPR consent data if GDPR set', function() { + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).gdpr.applies).to.be.true; + expect(JSON.parse(requests[0].data).gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + expect(JSON.parse(requests[1].data).gdpr.applies).to.be.true; + expect(JSON.parse(requests[1].data).gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + }) + + it('should not contain GDPR consent data if GDPR not set', function() { + delete bidderRequest.gdprConsent; + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).gdpr).to.be.an('undefined'); + expect(JSON.parse(requests[1].data).gdpr).to.be.an('undefined'); + }) + + it('should set debug mode in requests if enabled', function() { + sinon.stub(config, 'getConfig').withArgs('debug').returns(true); + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(JSON.parse(requests[0].data).debug).to.be.true; + expect(JSON.parse(requests[1].data).debug).to.be.true; + config.getConfig.restore(); + }) + }); + + describe('interpretResponse', function () { + let serverResponse; + beforeEach(function() { + serverResponse = { + body: { + 'requestId': 'request-id', + 'cpm': 100, + 'width': 300, + 'height': 250, + 'ad': '
ad
', + 'ttl': 300, + 'creativeId': 'creative-id', + 'netRevenue': true, + 'currency': 'USD' + } + }; + }); + + it('should return a valid response', () => { + var responses = spec.interpretResponse(serverResponse); + expect(responses).to.be.an('array').that.is.not.empty; + + let response = responses[0]; + expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency'); + expect(response.requestId).to.equal('request-id'); + expect(response.cpm).to.equal(100); + expect(response.width).to.equal(300); + expect(response.height).to.equal(250); + expect(response.ad).to.equal('
ad
'); + expect(response.ttl).to.equal(300); + expect(response.creativeId).to.equal('creative-id'); + expect(response.netRevenue).to.be.true; + expect(response.currency).to.equal('USD'); + }); + + it('should return an empty array when serverResponse is empty', () => { + serverResponse = {}; + var responses = spec.interpretResponse(serverResponse); + expect(responses).to.deep.equal([]); + }); + }); + + describe('getUserSyncs', function () { + it('should return nothing when sync is disabled', function () { + const syncOptions = { + 'iframeEnabled': false, + 'pixelEnabled': false + }; + + let syncs = spec.getUserSyncs(syncOptions); + expect(syncs).to.deep.equal([]); + }); + + it('should register iframe sync when only iframe is enabled', function () { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': false + }; + + let syncs = spec.getUserSyncs(syncOptions); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).includes('https://rtb.adwmg.com/cphb.html?'); + }); + + it('should register iframe sync when iframe and image are enabled', function () { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': true + }; + + let syncs = spec.getUserSyncs(syncOptions); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).includes('https://rtb.adwmg.com/cphb.html?'); + }); + + it('should send GDPR consent if enabled', function() { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': true + }; + const gdprConsent = { + consentString: 'CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_X_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + }; + const serverResponse = {}; + let syncs = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent); + expect(syncs[0].url).includes('gdpr=1'); + expect(syncs[0].url).includes(`gdpr_consent=${gdprConsent.consentString}`); + }); + }); +}); From 9143962e09d3fcb00f7b9b23cff7d74be4502332 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Wed, 9 Dec 2020 09:45:14 -0500 Subject: [PATCH 0447/1476] Add PubWise Bid Adapter (#6044) * updates for first cut at bidder * fix up height and width * adds test spec * remove hello_world from commit' * updates to support native and fix issues found in initial review * fix handling of new node in response for native vs banner * updates to handle OpenRTB base * updates to support RTB style calls * updates to get back to parity * updates to testing * updates to test media type handling * updates to handling testing * updates to testing * remove report file * updates to fix up unit/spec tests * updates to fix up unit/spec tests * updates to fix up unit/spec tests * updates to handling of gdpr * Delete hello_world.html * remove hellow-world-sample * Pubwise 481 (#7) * updates to support PubWise bid adapter, test cases and documentation * updates to fix param tes * Pubwise 481 (#8) * fixes for unit testing * remove unused variables and params * Updates to Remove Unused Vars (#9) * remove unused vars * updates to fix up used and unsused params * updates to fix up used and unsused params (#10) * updates to fix up used and unsused params * updates to remove usersync and add gvlid * Pubwise 481 (#11) * updates to remove usersync, add https, and add gvlid * Update pubwiseBidAdapter.js * updates to remove json, to remove options hit --- modules/pubwiseBidAdapter.js | 777 ++++++++++++++++++++ modules/pubwiseBidAdapter.md | 78 ++ test/spec/modules/pubwiseBidAdapter_spec.js | 575 +++++++++++++++ 3 files changed, 1430 insertions(+) create mode 100644 modules/pubwiseBidAdapter.js create mode 100644 modules/pubwiseBidAdapter.md create mode 100644 test/spec/modules/pubwiseBidAdapter_spec.js diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js new file mode 100644 index 00000000000..f450a8bede8 --- /dev/null +++ b/modules/pubwiseBidAdapter.js @@ -0,0 +1,777 @@ +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +const VERSION = '0.1.0'; +const GVLID = 842; +const NET_REVENUE = true; +const UNDEFINED = undefined; +const DEFAULT_CURRENCY = 'USD'; +const AUCTION_TYPE = 1; +const BIDDER_CODE = 'pwbid'; +const ENDPOINT_URL = 'https://bid.pubwise.io/prebid'; +const DEFAULT_WIDTH = 0; +const DEFAULT_HEIGHT = 0; +const PREBID_NATIVE_HELP_LINK = 'https://prebid.org/dev-docs/show-native-ads.html'; +// const USERSYNC_URL = '//127.0.0.1:8080/usersync' + +const CUSTOM_PARAMS = { + 'gender': '', // User gender + 'yob': '', // User year of birth + 'lat': '', // User location - Latitude + 'lon': '', // User Location - Longitude +}; + +// rtb native types are meant to be dynamic and extendable +// the extendable data asset types are nicely aligned +// in practice we set an ID that is distinct for each real type of return +const NATIVE_ASSETS = { + 'TITLE': { ID: 1, KEY: 'title', TYPE: 0 }, + 'IMAGE': { ID: 2, KEY: 'image', TYPE: 0 }, + 'ICON': { ID: 3, KEY: 'icon', TYPE: 0 }, + 'SPONSOREDBY': { ID: 4, KEY: 'sponsoredBy', TYPE: 1 }, + 'BODY': { ID: 5, KEY: 'body', TYPE: 2 }, + 'CLICKURL': { ID: 6, KEY: 'clickUrl', TYPE: 0 }, + 'VIDEO': { ID: 7, KEY: 'video', TYPE: 0 }, + 'EXT': { ID: 8, KEY: 'ext', TYPE: 0 }, + 'DATA': { ID: 9, KEY: 'data', TYPE: 0 }, + 'LOGO': { ID: 10, KEY: 'logo', TYPE: 0 }, + 'SPONSORED': { ID: 11, KEY: 'sponsored', TYPE: 1 }, + 'DESC': { ID: 12, KEY: 'data', TYPE: 2 }, + 'RATING': { ID: 13, KEY: 'rating', TYPE: 3 }, + 'LIKES': { ID: 14, KEY: 'likes', TYPE: 4 }, + 'DOWNLOADS': { ID: 15, KEY: 'downloads', TYPE: 5 }, + 'PRICE': { ID: 16, KEY: 'price', TYPE: 6 }, + 'SALEPRICE': { ID: 17, KEY: 'saleprice', TYPE: 7 }, + 'PHONE': { ID: 18, KEY: 'phone', TYPE: 8 }, + 'ADDRESS': { ID: 19, KEY: 'address', TYPE: 9 }, + 'DESC2': { ID: 20, KEY: 'desc2', TYPE: 10 }, + 'DISPLAYURL': { ID: 21, KEY: 'displayurl', TYPE: 11 }, + 'CTA': { ID: 22, KEY: 'cta', TYPE: 12 } +}; + +const NATIVE_ASSET_IMAGE_TYPE = { + 'ICON': 1, + 'LOGO': 2, + 'IMAGE': 3 +} + +// to render any native unit we have to have a few items +const NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS = [ + { + id: NATIVE_ASSETS.SPONSOREDBY.ID, + required: true, + data: { + type: 1 + } + }, + { + id: NATIVE_ASSETS.TITLE.ID, + required: true, + }, + { + id: NATIVE_ASSETS.IMAGE.ID, + required: true, + } +] + +let isInvalidNativeRequest = false +let NATIVE_ASSET_ID_TO_KEY_MAP = {}; +let NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; + +// together allows traversal of NATIVE_ASSETS_LIST in any direction +// id -> key +utils._each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); +// key -> asset +utils._each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, NATIVE], + /** + * 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) { + // siteId is required + if (bid.params && bid.params.siteId) { + // it must be a string + if (!utils.isStr(bid.params.siteId)) { + _logWarn('siteId is required for bid', bid); + return false; + } + } else { + return false; + } + + return true; + }, + /** + * 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) { + var refererInfo; + if (bidderRequest && bidderRequest.refererInfo) { + refererInfo = bidderRequest.refererInfo; + } + var conf = _initConf(refererInfo); + var payload = _createOrtbTemplate(conf); + var bidCurrency = ''; + var bid; + var blockedIabCategories = []; + + validBidRequests.forEach(originalBid => { + bid = utils.deepClone(originalBid); + bid.params.adSlot = bid.params.adSlot || ''; + _parseAdSlot(bid); + + conf = _handleCustomParams(bid.params, conf); + conf.transactionId = bid.transactionId; + bidCurrency = bid.params.currency || UNDEFINED; + bid.params.currency = bidCurrency; + + if (bid.params.hasOwnProperty('bcat') && utils.isArray(bid.params.bcat)) { + blockedIabCategories = blockedIabCategories.concat(bid.params.bcat); + } + + var impObj = _createImpressionObject(bid, conf); + if (impObj) { + payload.imp.push(impObj); + } + }); + + // no payload imps, no rason to continue + if (payload.imp.length == 0) { + return; + } + + // test bids can also be turned on here + if (window.location.href.indexOf('pubwiseTestBid=true') !== -1) { + payload.test = 1; + } + + if (bid.params.isTest) { + 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); + payload.user.geo = {}; + payload.user.geo.lat = _parseSlotParam('lat', conf.lat); + payload.user.geo.lon = _parseSlotParam('lon', conf.lon); + payload.user.yob = _parseSlotParam('yob', conf.yob); + payload.device.geo = payload.user.geo; + payload.site.page = payload.site.page.trim(); + payload.site.domain = _getDomainFromURL(payload.site.page); + + // add the content object from config in request + if (typeof config.getConfig('content') === 'object') { + payload.site.content = config.getConfig('content'); + } + + // merge the device from config.getConfig('device') + if (typeof config.getConfig('device') === 'object') { + payload.device = Object.assign(payload.device, config.getConfig('device')); + } + + // passing transactionId in source.tid + utils.deepSetValue(payload, 'source.tid', conf.transactionId); + + // schain + if (validBidRequests[0].schain) { + utils.deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + } + + // gdpr consent + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(payload, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // ccpa on the root object + if (bidderRequest && bidderRequest.uspConsent) { + utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + // if coppa is in effect then note it + if (config.getConfig('coppa') === true) { + utils.deepSetValue(payload, 'regs.coppa', 1); + } + + var options = {contentType: 'text/plain'} + + _logInfo('buildRequests payload', payload); + _logInfo('buildRequests bidderRequest', bidderRequest); + + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload, + options: options, + bidderRequest: bidderRequest, + }; + }, + /** + * 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 (response, request) { + const bidResponses = []; + var respCur = DEFAULT_CURRENCY; + _logInfo('interpretResponse request', request); + let parsedRequest = request.data; // not currently stringified + // let parsedReferrer = parsedRequest.site && parsedRequest.site.ref ? parsedRequest.site.ref : ''; + + // try { + if (response.body && response.body.seatbid && utils.isArray(response.body.seatbid)) { + // Supporting multiple bid responses for same adSize + respCur = response.body.cur || respCur; + response.body.seatbid.forEach(seatbidder => { + seatbidder.bid && + utils.isArray(seatbidder.bid) && + seatbidder.bid.forEach(bid => { + let newBid = { + requestId: bid.impid, + cpm: (parseFloat(bid.price) || 0).toFixed(2), + width: bid.w, + height: bid.h, + creativeId: bid.crid || bid.id, + currency: respCur, + netRevenue: NET_REVENUE, + ttl: 300, + ad: bid.adm, + pw_seat: seatbidder.seat || null, + pw_dspid: bid.ext && bid.ext.dspid ? bid.ext.dspid : null, + partnerImpId: bid.id || '' // partner impression Id + }; + if (parsedRequest.imp && parsedRequest.imp.length > 0) { + parsedRequest.imp.forEach(req => { + if (bid.impid === req.id) { + _checkMediaType(bid.adm, newBid); + switch (newBid.mediaType) { + case BANNER: + break; + case NATIVE: + _parseNativeResponse(bid, newBid); + break; + } + } + }); + } + + newBid.meta = {}; + if (bid.ext && bid.ext.dspid) { + newBid.meta.networkId = bid.ext.dspid; + } + if (bid.ext && bid.ext.advid) { + newBid.meta.buyerId = bid.ext.advid; + } + if (bid.adomain && bid.adomain.length > 0) { + newBid.meta.advertiserDomains = bid.adomain; + newBid.meta.clickUrl = bid.adomain[0]; + } + + bidResponses.push(newBid); + }); + }); + } + // } catch (error) { + // _logError(error); + // } + return bidResponses; + } +} + +function _checkMediaType(adm, newBid) { + // Create a regex here to check the strings + var admJSON = ''; + if (adm.indexOf('"ver":') >= 0) { + try { + admJSON = JSON.parse(adm.replace(/\\/g, '')); + if (admJSON && admJSON.assets) { + newBid.mediaType = NATIVE; + } + } catch (e) { + _logWarn('Error: Cannot parse native reponse for ad response: ' + adm); + } + } else { + newBid.mediaType = BANNER; + } +} + +function _parseNativeResponse(bid, newBid) { + newBid.native = {}; + if (bid.hasOwnProperty('adm')) { + var adm = ''; + try { + adm = JSON.parse(bid.adm.replace(/\\/g, '')); + } catch (ex) { + _logWarn('Error: Cannot parse native reponse for ad response: ' + newBid.adm); + return; + } + if (adm && adm.assets && adm.assets.length > 0) { + newBid.mediaType = NATIVE; + for (let i = 0, len = adm.assets.length; i < len; i++) { + switch (adm.assets[i].id) { + case NATIVE_ASSETS.TITLE.ID: + newBid.native.title = adm.assets[i].title && adm.assets[i].title.text; + break; + case NATIVE_ASSETS.IMAGE.ID: + newBid.native.image = { + url: adm.assets[i].img && adm.assets[i].img.url, + height: adm.assets[i].img && adm.assets[i].img.h, + width: adm.assets[i].img && adm.assets[i].img.w, + }; + break; + case NATIVE_ASSETS.ICON.ID: + newBid.native.icon = { + url: adm.assets[i].img && adm.assets[i].img.url, + height: adm.assets[i].img && adm.assets[i].img.h, + width: adm.assets[i].img && adm.assets[i].img.w, + }; + break; + case NATIVE_ASSETS.SPONSOREDBY.ID: + case NATIVE_ASSETS.BODY.ID: + case NATIVE_ASSETS.LIKES.ID: + case NATIVE_ASSETS.DOWNLOADS.ID: + case NATIVE_ASSETS.PRICE: + case NATIVE_ASSETS.SALEPRICE.ID: + case NATIVE_ASSETS.PHONE.ID: + case NATIVE_ASSETS.ADDRESS.ID: + case NATIVE_ASSETS.DESC2.ID: + case NATIVE_ASSETS.CTA.ID: + case NATIVE_ASSETS.RATING.ID: + case NATIVE_ASSETS.DISPLAYURL.ID: + newBid.native[NATIVE_ASSET_ID_TO_KEY_MAP[adm.assets[i].id]] = adm.assets[i].data && adm.assets[i].data.value; + break; + } + } + newBid.clickUrl = adm.link && adm.link.url; + newBid.clickTrackers = (adm.link && adm.link.clicktrackers) || []; + newBid.impressionTrackers = adm.imptrackers || []; + newBid.jstracker = adm.jstracker || []; + if (!newBid.width) { + newBid.width = DEFAULT_WIDTH; + } + if (!newBid.height) { + newBid.height = DEFAULT_HEIGHT; + } + } + } +} + +function _getDomainFromURL(url) { + let anchor = document.createElement('a'); + anchor.href = url; + return anchor.hostname; +} + +function _handleCustomParams(params, conf) { + var key, value, entry; + for (key in CUSTOM_PARAMS) { + if (CUSTOM_PARAMS.hasOwnProperty(key)) { + value = params[key]; + if (value) { + entry = CUSTOM_PARAMS[key]; + + if (typeof entry === 'object') { + // will be used in future when we want to + // process a custom param before using + // 'keyname': {f: function() {}} + value = entry.f(value, conf); + } + + if (utils.isStr(value)) { + conf[key] = value; + } else { + _logWarn('Ignoring param : ' + key + ' with value : ' + CUSTOM_PARAMS[key] + ', expects string-value, found ' + typeof value); + } + } + } + } + return conf; +} + +function _createOrtbTemplate(conf) { + return { + id: '' + new Date().getTime(), + at: AUCTION_TYPE, + cur: [DEFAULT_CURRENCY], + imp: [], + site: { + page: conf.pageURL, + ref: conf.refURL, + publisher: {} + }, + device: { + ua: navigator.userAgent, + js: 1, + dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, + h: screen.height, + w: screen.width, + language: navigator.language + }, + user: {}, + ext: { + version: VERSION + } + }; +} + +function _createImpressionObject(bid, conf) { + var impObj = {}; + var bannerObj; + var nativeObj = {}; + var mediaTypes = ''; + + impObj = { + id: bid.bidId, + tagid: bid.params.adUnit || undefined, + bidfloor: _parseSlotParam('bidFloor', bid.params.bidFloor), // capitalization dicated by 3.2.4 spec + secure: 1, + bidfloorcur: bid.params.currency ? _parseSlotParam('currency', bid.params.currency) : DEFAULT_CURRENCY // capitalization dicated by 3.2.4 spec + }; + + if (bid.hasOwnProperty('mediaTypes')) { + for (mediaTypes in bid.mediaTypes) { + switch (mediaTypes) { + case BANNER: + bannerObj = _createBannerRequest(bid); + if (bannerObj !== UNDEFINED) { + impObj.banner = bannerObj; + } + break; + case NATIVE: + nativeObj['request'] = JSON.stringify(_createNativeRequest(bid.nativeParams)); + if (!isInvalidNativeRequest) { + impObj.native = nativeObj; + } else { + _logWarn('Error: Error in Native adunit ' + bid.params.adUnit + '. Ignoring the adunit. Refer to ' + PREBID_NATIVE_HELP_LINK + ' for more details.'); + } + break; + } + } + } else { + _logWarn('MediaTypes are Required for all Adunit Configs', bid) + } + + _addFloorFromFloorModule(impObj, bid); + + return impObj.hasOwnProperty(BANNER) || + impObj.hasOwnProperty(NATIVE) ? impObj : UNDEFINED; +} + +function _parseSlotParam(paramName, paramValue) { + if (!utils.isStr(paramValue)) { + paramValue && _logWarn('Ignoring param key: ' + paramName + ', expects string-value, found ' + typeof paramValue); + return UNDEFINED; + } + + switch (paramName) { + case 'bidFloor': + return parseFloat(paramValue) || UNDEFINED; + case 'lat': + return parseFloat(paramValue) || UNDEFINED; + case 'lon': + return parseFloat(paramValue) || UNDEFINED; + case 'yob': + return parseInt(paramValue) || UNDEFINED; + default: + return paramValue; + } +} + +function _parseAdSlot(bid) { + _logInfo('parseAdSlot bid', bid) + bid.params.adUnit = ''; + bid.params.width = 0; + bid.params.height = 0; + bid.params.adSlot = _cleanSlotName(bid.params.adSlot); + + if (bid.hasOwnProperty('mediaTypes')) { + if (bid.mediaTypes.hasOwnProperty(BANNER) && + bid.mediaTypes.banner.hasOwnProperty('sizes')) { // if its a banner, has mediaTypes and sizes + var i = 0; + var sizeArray = []; + for (;i < bid.mediaTypes.banner.sizes.length; i++) { + if (bid.mediaTypes.banner.sizes[i].length === 2) { // sizes[i].length will not be 2 in case where size is set as fluid, we want to skip that entry + sizeArray.push(bid.mediaTypes.banner.sizes[i]); + } + } + bid.mediaTypes.banner.sizes = sizeArray; + if (bid.mediaTypes.banner.sizes.length >= 1) { + // if there is more than one size then pop one onto the banner params width + // pop the first into the params, then remove it from mediaTypes + bid.params.width = bid.mediaTypes.banner.sizes[0][0]; + bid.params.height = bid.mediaTypes.banner.sizes[0][1]; + bid.mediaTypes.banner.sizes = bid.mediaTypes.banner.sizes.splice(1, bid.mediaTypes.banner.sizes.length - 1); + } + } + } else { + _logWarn('MediaTypes are Required for all Adunit Configs', bid) + } +} + +function _cleanSlotName(slotName) { + if (utils.isStr(slotName)) { + return slotName.replace(/^\s+/g, '').replace(/\s+$/g, ''); + } + return ''; +} + +function _initConf(refererInfo) { + return { + pageURL: (refererInfo && refererInfo.referer) ? refererInfo.referer : window.location.href, + refURL: window.document.referrer + }; +} + +function _commonNativeRequestObject(nativeAsset, params) { + var key = nativeAsset.KEY; + return { + id: nativeAsset.ID, + required: params[key].required ? 1 : 0, + data: { + type: nativeAsset.TYPE, + len: params[key].len, + ext: params[key].ext + } + }; +} + +function _addFloorFromFloorModule(impObj, bid) { + let bidFloor = -1; // indicates no floor + + // get lowest floor from floorModule + if (typeof bid.getFloor === 'function' && !config.getConfig('pubwise.disableFloors')) { + [BANNER, NATIVE].forEach(mediaType => { + if (impObj.hasOwnProperty(mediaType)) { + let floorInfo = bid.getFloor({ currency: impObj.bidFloorCur, mediaType: mediaType, size: '*' }); + if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidFloorCur && !isNaN(parseInt(floorInfo.floor))) { + let mediaTypeFloor = parseFloat(floorInfo.floor); + bidFloor = (bidFloor == -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor)) + } + } + }); + } + + // get highest, if none then take the default -1 + if (impObj.bidfloor) { + bidFloor = Math.max(bidFloor, impObj.bidfloor) + } + + // assign if it has a valid floor - > 0 + impObj.bidfloor = ((!isNaN(bidFloor) && bidFloor > 0) ? bidFloor : UNDEFINED); +} + +function _createNativeRequest(params) { + var nativeRequestObject = { + assets: [] + }; + for (var key in params) { + if (params.hasOwnProperty(key)) { + var assetObj = {}; + if (!(nativeRequestObject.assets && nativeRequestObject.assets.length > 0 && nativeRequestObject.assets.hasOwnProperty(key))) { + switch (key) { + case NATIVE_ASSETS.TITLE.KEY: + if (params[key].len || params[key].length) { + assetObj = { + id: NATIVE_ASSETS.TITLE.ID, + required: params[key].required ? 1 : 0, + title: { + len: params[key].len || params[key].length, + ext: params[key].ext + } + }; + } else { + _logWarn('Error: Title Length is required for native ad: ' + JSON.stringify(params)); + } + break; + case NATIVE_ASSETS.IMAGE.KEY: + if (params[key].sizes && params[key].sizes.length > 0) { + assetObj = { + id: NATIVE_ASSETS.IMAGE.ID, + required: params[key].required ? 1 : 0, + img: { + type: NATIVE_ASSET_IMAGE_TYPE.IMAGE, + w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), + h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED), + wmin: params[key].wmin || params[key].minimumWidth || (params[key].minsizes ? params[key].minsizes[0] : UNDEFINED), + hmin: params[key].hmin || params[key].minimumHeight || (params[key].minsizes ? params[key].minsizes[1] : UNDEFINED), + mimes: params[key].mimes, + ext: params[key].ext, + } + }; + } else { + _logWarn('Error: Image sizes is required for native ad: ' + JSON.stringify(params)); + } + break; + case NATIVE_ASSETS.ICON.KEY: + if (params[key].sizes && params[key].sizes.length > 0) { + assetObj = { + id: NATIVE_ASSETS.ICON.ID, + required: params[key].required ? 1 : 0, + img: { + type: NATIVE_ASSET_IMAGE_TYPE.ICON, + w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), + h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED), + } + }; + } else { + _logWarn('Error: Icon sizes is required for native ad: ' + JSON.stringify(params)); + }; + break; + case NATIVE_ASSETS.VIDEO.KEY: + assetObj = { + id: NATIVE_ASSETS.VIDEO.ID, + required: params[key].required ? 1 : 0, + video: { + minduration: params[key].minduration, + maxduration: params[key].maxduration, + protocols: params[key].protocols, + mimes: params[key].mimes, + ext: params[key].ext + } + }; + break; + case NATIVE_ASSETS.EXT.KEY: + assetObj = { + id: NATIVE_ASSETS.EXT.ID, + required: params[key].required ? 1 : 0, + }; + break; + case NATIVE_ASSETS.LOGO.KEY: + assetObj = { + id: NATIVE_ASSETS.LOGO.ID, + required: params[key].required ? 1 : 0, + img: { + type: NATIVE_ASSET_IMAGE_TYPE.LOGO, + w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), + h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED) + } + }; + break; + case NATIVE_ASSETS.SPONSOREDBY.KEY: + case NATIVE_ASSETS.BODY.KEY: + case NATIVE_ASSETS.RATING.KEY: + case NATIVE_ASSETS.LIKES.KEY: + case NATIVE_ASSETS.DOWNLOADS.KEY: + case NATIVE_ASSETS.PRICE.KEY: + case NATIVE_ASSETS.SALEPRICE.KEY: + case NATIVE_ASSETS.PHONE.KEY: + case NATIVE_ASSETS.ADDRESS.KEY: + case NATIVE_ASSETS.DESC2.KEY: + case NATIVE_ASSETS.DISPLAYURL.KEY: + case NATIVE_ASSETS.CTA.KEY: + assetObj = _commonNativeRequestObject(NATIVE_ASSET_KEY_TO_ASSET_MAP[key], params); + break; + } + } + } + if (assetObj && assetObj.id) { + nativeRequestObject.assets[nativeRequestObject.assets.length] = assetObj; + } + }; + + // for native image adtype prebid has to have few required assests i.e. title,sponsoredBy, image + // if any of these are missing from the request then request will not be sent + var requiredAssetCount = NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.length; + var presentrequiredAssetCount = 0; + NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.forEach(ele => { + var lengthOfExistingAssets = nativeRequestObject.assets.length; + for (var i = 0; i < lengthOfExistingAssets; i++) { + if (ele.id == nativeRequestObject.assets[i].id) { + presentrequiredAssetCount++; + break; + } + } + }); + if (requiredAssetCount == presentrequiredAssetCount) { + isInvalidNativeRequest = false; + } else { + isInvalidNativeRequest = true; + } + return nativeRequestObject; +} + +function _createBannerRequest(bid) { + var sizes = bid.mediaTypes.banner.sizes; + var format = []; + var bannerObj; + if (sizes !== UNDEFINED && utils.isArray(sizes)) { + bannerObj = {}; + if (!bid.params.width && !bid.params.height) { + if (sizes.length === 0) { + // i.e. since bid.params does not have width or height, and length of sizes is 0, need to ignore this banner imp + bannerObj = UNDEFINED; + _logWarn('Error: mediaTypes.banner.size missing for adunit: ' + bid.params.adUnit + '. Ignoring the banner impression in the adunit.'); + return bannerObj; + } else { + bannerObj.w = parseInt(sizes[0][0], 10); + bannerObj.h = parseInt(sizes[0][1], 10); + sizes = sizes.splice(1, sizes.length - 1); + } + } else { + bannerObj.w = bid.params.width; + bannerObj.h = bid.params.height; + } + if (sizes.length > 0) { + format = []; + sizes.forEach(function (size) { + if (size.length > 1) { + format.push({ w: size[0], h: size[1] }); + } + }); + if (format.length > 0) { + bannerObj.format = format; + } + } + bannerObj.pos = 0; + bannerObj.topframe = utils.inIframe() ? 0 : 1; + } else { + _logWarn('Error: mediaTypes.banner.size missing for adunit: ' + bid.params.adUnit + '. Ignoring the banner impression in the adunit.'); + bannerObj = UNDEFINED; + } + return bannerObj; +} + +// various error levels are not always used +// eslint-disable-next-line no-unused-vars +function _logMessage(textValue, objectValue) { + utils.logMessage('PubWise: ' + textValue, objectValue); +} + +// eslint-disable-next-line no-unused-vars +function _logInfo(textValue, objectValue) { + utils.logInfo('PubWise: ' + textValue, objectValue); +} + +// eslint-disable-next-line no-unused-vars +function _logWarn(textValue, objectValue) { + utils.logWarn('PubWise: ' + textValue, objectValue); +} + +// eslint-disable-next-line no-unused-vars +function _logError(textValue, objectValue) { + utils.logError('PubWise: ' + textValue, objectValue); +} + +// function _decorateLog() { +// arguments[0] = 'PubWise' + arguments[0]; +// return arguments +// } + +// these are exported only for testing so maintaining the JS convention of _ to indicate the intent +export { + _checkMediaType, + _parseAdSlot +} + +registerBidder(spec); diff --git a/modules/pubwiseBidAdapter.md b/modules/pubwiseBidAdapter.md new file mode 100644 index 00000000000..8cf38a63913 --- /dev/null +++ b/modules/pubwiseBidAdapter.md @@ -0,0 +1,78 @@ +# Overview + +``` +Module Name: PubWise Bid Adapter +Module Type: Bidder Adapter +Maintainer: info@pubwise.io +``` + +# Description + +Connects to PubWise exchange for bids. + +# Sample Banner Ad Unit: For Publishers + +With isTest parameter the system will respond in whatever dimensions provided. + +## Params + + + +## Banner +``` +var adUnits = [ + { + code: "div-gpt-ad-1460505748561-0", + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'pubwise', + params: { + siteId: "xxxxxx", + isTest: true + } + }] + } +] +``` +## Native +``` +var adUnits = [ + { + code: 'div-gpt-ad-1460505748561-1', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + body: { + required: true + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + }, + icon: { + required: false + } + } + }, + bids: [{ + bidder: 'pubwise', + params: { + siteId: "xxxxxx", + isTest: true, + }, + }] + } +] +``` + diff --git a/test/spec/modules/pubwiseBidAdapter_spec.js b/test/spec/modules/pubwiseBidAdapter_spec.js new file mode 100644 index 00000000000..450b028f6c7 --- /dev/null +++ b/test/spec/modules/pubwiseBidAdapter_spec.js @@ -0,0 +1,575 @@ +// import or require modules necessary for the test, e.g.: + +import {expect} from 'chai'; +import {spec} from 'modules/pubwiseBidAdapter.js'; +import {_checkMediaType} from 'modules/pubwiseBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent +import {_parseAdSlot} from 'modules/pubwiseBidAdapter.js'; // this is exported only for testing so maintaining the JS convention of _ to indicate the intent +import * as utils from 'src/utils.js'; + +const sampleRequestBanner = { + 'id': '6c148795eb836a', + 'tagid': 'div-gpt-ad-1460505748561-0', + 'bidfloor': 1, + 'secure': 1, + 'bidfloorcur': 'USD', + 'banner': { + 'w': 300, + 'h': 250, + 'format': [ + { + 'w': 300, + 'h': 600 + } + ], + 'pos': 0, + 'topframe': 1 + } +}; + +const sampleRequest = { + 'at': 1, + 'cur': [ + 'USD' + ], + 'imp': [ + sampleRequestBanner, + { + 'id': '7329ddc1d84eb3', + 'tagid': 'div-gpt-ad-1460505748561-1', + 'secure': 1, + 'bidfloorcur': 'USD', + 'native': { + 'request': '{"assets":[{"id":1,"required":1,"title":{"len":80}},{"id":5,"required":1,"data":{"type":2}},{"id":2,"required":1,"img":{"type":{"ID":2,"KEY":"image","TYPE":0},"w":150,"h":50}},{"id":4,"required":1,"data":{"type":1}}]}' + } + } + ], + 'site': { + 'page': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'ref': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'publisher': { + 'id': 'xxxxxx' + } + }, + 'device': { + 'ua': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/86.0.4240.198 Safari/537.36', + 'js': 1, + 'dnt': 0, + 'h': 600, + 'w': 800, + 'language': 'en-US', + 'geo': { + 'lat': 33.91989876432274, + 'lon': -84.38897708175764 + } + }, + 'user': { + 'gender': 'M', + 'geo': { + 'lat': 33.91989876432274, + 'lon': -84.38897708175764 + }, + 'yob': 2000 + }, + 'test': 0, + 'ext': { + 'version': '0.0.1' + }, + 'source': { + 'tid': '2c8cd034-f068-4419-8c30-f07292c0d17b' + } +}; + +const sampleValidBannerBidRequest = { + 'bidder': 'pubwise', + 'params': { + 'siteId': 'xxxxxx', + 'bidFloor': '1.00', + 'currency': 'USD', + 'gender': 'M', + 'lat': '33.91989876432274', + 'lon': '-84.38897708175764', + 'yob': '2000', + 'bcat': ['IAB25-3', 'IAB26-1', 'IAB26-2', 'IAB26-3', 'IAB26-4'], + }, + 'gdprConsent': { + 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + 'gdprApplies': 1, + }, + 'uspConsent': 1, + 'crumbs': { + 'pubcid': '9a62f261-3c0b-4cc8-8db3-a72ae86ec6ba' + }, + 'fpd': { + 'context': { + 'adServer': { + 'name': 'gam', + 'adSlot': '/19968336/header-bid-tag-0' + }, + 'pbAdSlot': '/19968336/header-bid-tag-0' + } + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '2001a8b2-3bcf-417d-b64f-92641dae21e0', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '6c148795eb836a', + 'bidderRequestId': '18a45bff5ff705', + 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 +}; + +const sampleValidBidRequests = [ + sampleValidBannerBidRequest, + { + 'bidder': 'pubwise', + 'params': { + 'siteId': 'xxxxxx' + }, + 'crumbs': { + 'pubcid': '9a62f261-3c0b-4cc8-8db3-a72ae86ec6ba' + }, + 'nativeParams': { + 'title': { + 'required': true, + 'len': 80 + }, + 'body': { + 'required': true + }, + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ] + }, + 'sponsoredBy': { + 'required': true + }, + 'icon': { + 'required': false + } + }, + 'fpd': { + 'context': { + 'adServer': { + 'name': 'gam', + 'adSlot': '/19968336/header-bid-tag-0' + }, + 'pbAdSlot': '/19968336/header-bid-tag-0' + } + }, + 'mediaTypes': { + 'native': { + 'title': { + 'required': true, + 'len': 80 + }, + 'body': { + 'required': true + }, + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ] + }, + 'sponsoredBy': { + 'required': true + }, + 'icon': { + 'required': false + } + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-1', + 'transactionId': '2c8cd034-f068-4419-8c30-f07292c0d17b', + 'sizes': [], + 'bidId': '30ab7516a51a7c', + 'bidderRequestId': '18a45bff5ff705', + 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } +] + +const sampleBidderBannerRequest = { + 'bidder': 'pubwise', + 'params': { + 'siteId': 'xxxxxx', + 'height': 250, + 'width': 300, + 'gender': 'M', + 'yob': '2000', + 'lat': '33.91989876432274', + 'lon': '-84.38897708175764', + 'bidFloor': '1.00', + 'currency': 'USD', + 'adSlot': '', + 'adUnit': '', + 'bcat': [ + 'IAB25-3', + 'IAB26-1', + 'IAB26-2', + 'IAB26-3', + 'IAB26-4', + ], + }, + 'crumbs': { + 'pubcid': '9a62f261-3c0b-4cc8-8db3-a72ae86ec6ba' + }, + 'fpd': { + 'context': { + 'adServer': { + 'name': 'gam', + 'adSlot': '/19968336/header-bid-tag-0' + }, + 'pbAdSlot': '/19968336/header-bid-tag-0' + } + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '2001a8b2-3bcf-417d-b64f-92641dae21e0', + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ], + 'bidId': '6c148795eb836a', + 'bidderRequestId': '18a45bff5ff705', + 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'gdprConsent': { + 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + 'gdprApplies': 1, + }, + 'uspConsent': 1, +}; + +const sampleBidderRequest = { + 'bidderCode': 'pubwise', + 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + 'bidderRequestId': '18a45bff5ff705', + 'bids': [ + sampleBidderBannerRequest, + { + 'bidder': 'pubwise', + 'params': { + 'siteId': 'xxxxxx' + }, + 'crumbs': { + 'pubcid': '9a62f261-3c0b-4cc8-8db3-a72ae86ec6ba' + }, + 'nativeParams': { + 'title': { + 'required': true, + 'len': 80 + }, + 'body': { + 'required': true + }, + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ] + }, + 'sponsoredBy': { + 'required': true + }, + 'icon': { + 'required': false + } + }, + 'fpd': { + 'context': { + 'adServer': { + 'name': 'gam', + 'adSlot': '/19968336/header-bid-tag-0' + }, + 'pbAdSlot': '/19968336/header-bid-tag-0' + } + }, + 'mediaTypes': { + 'native': { + 'title': { + 'required': true, + 'len': 80 + }, + 'body': { + 'required': true + }, + 'image': { + 'required': true, + 'sizes': [ + 150, + 50 + ] + }, + 'sponsoredBy': { + 'required': true + }, + 'icon': { + 'required': false + } + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-1', + 'transactionId': '2c8cd034-f068-4419-8c30-f07292c0d17b', + 'sizes': [], + 'bidId': '30ab7516a51a7c', + 'bidderRequestId': '18a45bff5ff705', + 'auctionId': '9f20663c-4629-4b5c-bff6-ff3aa8319358', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1606269202001, + 'timeout': 1000, + 'gdprConsent': { + 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + 'gdprApplies': 1, + }, + 'uspConsent': 1, + 'refererInfo': { + 'referer': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true' + ], + 'canonicalUrl': null + }, + 'start': 1606269202004 +}; + +const sampleRTBResponse = { + 'body': { + 'id': '1606251348404', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1606579704052', + 'impid': '6c148795eb836a', + 'price': 1.23, + 'adm': '\u003cdiv style="box-sizing: border-box;width:298px;height:248px;border: 1px solid rgba(0,0,0,.25);border-radius:10px;"\u003e\n\t\u003ch3 style="margin-top:80px;text-align: center;"\u003ePubWise Test Bid\u003c/h3\u003e\n\u003c/div\u003e', + 'crid': 'test', + 'w': 300, + 'h': 250 + }, + { + 'id': '1606579704052', + 'impid': '7329ddc1d84eb3', + 'price': 1.23, + 'adm': '{"ver":"1.2","assets":[{"id":1,"title":{"text":"PubWise Test"}},{"id":2,"img":{"type":3,"url":"http://www.pubwise.io","w":300,"h":250}},{"id":3,"img":{"type":1,"url":"http://www.pubwise.io","w":150,"h":125}},{"id":5,"data":{"type":2,"value":"PubWise Test Desc"}},{"id":4,"data":{"type":1,"value":"PubWise.io"}}],"link":{"url":"http://www.pubwise.io"}}', + 'crid': 'test', + 'w': 300, + 'h': 250 + } + ] + } + ], + 'bidid': 'testtesttest' + } +}; + +const samplePBBidObjects = [ + { + 'requestId': '6c148795eb836a', + 'cpm': '1.23', + 'width': 300, + 'height': 250, + 'creativeId': 'test', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'ad': '
\n\t

PubWise Test Bid

\n
', + 'pw_seat': null, + 'pw_dspid': null, + 'partnerImpId': '1606579704052', + 'meta': {}, + 'mediaType': 'banner', + }, + { + 'requestId': '7329ddc1d84eb3', + 'cpm': '1.23', + 'width': 300, + 'height': 250, + 'creativeId': 'test', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'ad': '{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"PubWise Test\"}},{\"id\":2,\"img\":{\"type\":3,\"url\":\"http://www.pubwise.io\",\"w\":300,\"h\":250}},{\"id\":3,\"img\":{\"type\":1,\"url\":\"http://www.pubwise.io\",\"w\":150,\"h\":125}},{\"id\":5,\"data\":{\"type\":2,\"value\":\"PubWise Test Desc\"}},{\"id\":4,\"data\":{\"type\":1,\"value\":\"PubWise.io\"}}],\"link\":{\"url\":\"http://www.pubwise.io\"}}', + 'pw_seat': null, + 'pw_dspid': null, + 'partnerImpId': '1606579704052', + 'mediaType': 'native', + 'native': { + 'body': 'PubWise Test Desc', + 'icon': { + 'height': 125, + 'url': 'http://www.pubwise.io', + 'width': 150, + }, + 'image': { + 'height': 250, + 'url': 'http://www.pubwise.io', + 'width': 300, + }, + 'sponsoredBy': 'PubWise.io', + 'title': 'PubWise Test' + }, + 'meta': {}, + 'impressionTrackers': [], + 'jstracker': [], + 'clickTrackers': [], + 'clickUrl': 'http://www.pubwise.io' + } +]; + +describe('PubWiseAdapter', function () { + describe('Properly Validates Bids', function () { + it('valid bid', function () { + let validBid = { + bidder: 'pubwise', + params: { + siteId: 'xxxxxx' + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('valid bid: extra fields are ok', function () { + let validBid = { + bidder: 'pubwise', + params: { + siteId: 'xxxxxx', + gender: 'M', + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('invalid bid: no siteId', function () { + let inValidBid = { + bidder: 'pubwise', + params: { + gender: 'M', + } + }, + isValid = spec.isBidRequestValid(inValidBid); + expect(isValid).to.equal(false); + }); + + it('invalid bid: siteId should be a string', function () { + let validBid = { + bidder: 'pubwise', + params: { + siteId: 123456 + } + }, + isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }); + }); + + describe('Handling Request Construction', function () { + it('bid requests are not mutable', function() { + let sourceBidRequest = utils.deepClone(sampleValidBidRequests) + spec.buildRequests(sampleValidBidRequests, {auctinId: 'placeholder'}); + expect(sampleValidBidRequests).to.deep.equal(sourceBidRequest, 'Should be unedited as they are used elsewhere'); + }); + it('should handle complex bidRequest', function() { + let request = spec.buildRequests(sampleValidBidRequests, sampleBidderRequest); + expect(request.bidderRequest).to.equal(sampleBidderRequest); + }); + it('must conform to API for buildRequests', function() { + let request = spec.buildRequests(sampleValidBidRequests); + expect(request.bidderRequest).to.be.undefined; + }); + }); + + describe('Identifies Media Types', function () { + it('identifies native adm type', function() { + let adm = '{"ver":"1.2","assets":[{"title":{"text":"PubWise Test"}},{"img":{"type":3,"url":"http://www.pubwise.io"}},{"img":{"type":1,"url":"http://www.pubwise.io"}},{"data":{"type":2,"value":"PubWise Test Desc"}},{"data":{"type":1,"value":"PubWise.io"}}],"link":{"url":""}}'; + let newBid = {mediaType: 'unknown'}; + _checkMediaType(adm, newBid); + expect(newBid.mediaType).to.equal('native', adm + ' Is a Native adm'); + }); + + it('identifies banner adm type', function() { + let adm = '

PubWise Test Bid

'; + let newBid = {mediaType: 'unknown'}; + _checkMediaType(adm, newBid); + expect(newBid.mediaType).to.equal('banner', adm + ' Is a Banner adm'); + }); + }); + + describe('Properly Parses AdSlot Data', function () { + it('parses banner', function() { + let testBid = utils.deepClone(sampleValidBannerBidRequest) + _parseAdSlot(testBid) + expect(testBid).to.deep.equal(sampleBidderBannerRequest); + }); + }); + + describe('Properly Handles Response', function () { + it('handles response with muiltiple responses', function() { + // the request when it comes back is on the data object + let pbResponse = spec.interpretResponse(sampleRTBResponse, {'data': sampleRequest}) + expect(pbResponse).to.deep.equal(samplePBBidObjects); + }); + }); +}); From a95f1db0e11b416a4885fc59aae1970053c9a30d Mon Sep 17 00:00:00 2001 From: cpuBird <54024689+cpuBird@users.noreply.github.com> Date: Wed, 9 Dec 2020 21:24:30 +0530 Subject: [PATCH 0448/1476] Vdoai adapter update - Added video mediaType support (#5970) * added video mediatype support to vdoai adapter * added unit test --- modules/vdoaiBidAdapter.js | 16 +++++++--- modules/vdoaiBidAdapter.md | 23 +++++++++++++++ test/spec/modules/vdoaiBidAdapter_spec.js | 36 +++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index 395953fb737..8cfcd67bd00 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -1,14 +1,14 @@ import * as utils from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'vdo.ai'; const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. * @@ -40,7 +40,8 @@ export const spec = { height: height, bidId: bidRequest.bidId, referer: bidderRequest.refererInfo.referer, - id: bidRequest.auctionId + id: bidRequest.auctionId, + mediaType: bidRequest.mediaTypes.video ? 'video' : 'banner' }; bidRequest.params.bidFloor && (payload['bidFloor'] = bidRequest.params.bidFloor); return { @@ -90,8 +91,15 @@ export const spec = { ttl: config.getConfig('_bidderTimeout'), // referrer: referrer, // ad: response.adm - ad: adCreative + // ad: adCreative, + mediaType: response.mediaType }; + + if (response.mediaType == 'video') { + bidResponse.vastXml = adCreative; + } else { + bidResponse.ad = adCreative; + } bidResponses.push(bidResponse); } return bidResponses; diff --git a/modules/vdoaiBidAdapter.md b/modules/vdoaiBidAdapter.md index 81bd8e69c1d..712adc0ec76 100644 --- a/modules/vdoaiBidAdapter.md +++ b/modules/vdoaiBidAdapter.md @@ -31,4 +31,27 @@ Module that connects to VDO.AI's demand sources ] } ]; +``` + + +# Video Test Parameters +``` +var videoAdUnit = { + code: 'test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' + }, + }, + bids: [ + { + bidder: "vdo.ai", + params: { + placement: 'newsdv77' + } + } + ] +}; ``` \ No newline at end of file diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js index c9d826d8dc8..5318bb43eca 100644 --- a/test/spec/modules/vdoaiBidAdapter_spec.js +++ b/test/spec/modules/vdoaiBidAdapter_spec.js @@ -37,6 +37,7 @@ describe('vdoaiBidAdapter', function () { 'bidId': '23beaa6af6cdde', 'bidderRequestId': '19c0c1efdf37e7', 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + 'mediaTypes': 'banner' } ]; @@ -101,5 +102,40 @@ describe('vdoaiBidAdapter', function () { let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); }); + + it('handles instream video responses', function () { + let serverResponse = { + body: { + 'vdoCreative': '', + 'price': 4.2, + 'adid': '12345asdfg', + 'currency': 'EUR', + 'statusMessage': 'Bid available', + 'requestId': 'bidId123', + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'mediaType': 'video' + } + }; + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT_URL, + 'data': { + 'placementId': 'testPlacementId', + 'width': '300', + 'height': '200', + 'bidId': 'bidId123', + 'referer': 'www.example.com', + 'mediaType': 'video' + } + } + ]; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); }); }); From c0af43297cc4ba8e89ca2d238e6469565850ff42 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 9 Dec 2020 13:54:26 -0500 Subject: [PATCH 0449/1476] Price Floors update to include modelTimestamp displaying when the floors file was produced (#6061) Rubicon Anaytics Update to pass modelTimestamp if exists --- modules/priceFloors.js | 1 + modules/rubiconAnalyticsAdapter.js | 1 + test/spec/modules/priceFloors_spec.js | 23 +++++++++++++++++++ .../modules/rubiconAnalyticsAdapter_spec.js | 3 +++ 4 files changed, 28 insertions(+) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index fd8a46b172f..c0797f710de 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -291,6 +291,7 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { skipRate: floorData.skipRate, floorMin: floorData.floorMin, modelVersion: utils.deepAccess(floorData, 'data.modelVersion'), + modelTimestamp: utils.deepAccess(floorData, 'data.modelTimestamp'), location: utils.deepAccess(floorData, 'data.location', 'noData'), floorProvider: floorData.floorProvider, fetchStatus: _floorsConfig.fetchStatus diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index ff8cb7895b9..ad78c601ab6 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -237,6 +237,7 @@ function sendMessage(auctionId, bidWonId) { auction.floors = utils.pick(auctionCache.floorData, [ 'location', 'modelVersion as modelName', + 'modelTimestamp', 'skipped', 'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'), 'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'), diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 8c673d29701..1b3ce021068 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -23,6 +23,7 @@ describe('the price floors module', function () { let clock; const basicFloorData = { modelVersion: 'basic model', + modelTimestamp: 1606772895, currency: 'USD', schema: { delimiter: '|', @@ -184,6 +185,7 @@ describe('the price floors module', function () { let resultingData = getFloorsDataForAuction(inputFloorData, 'test_div_1'); expect(resultingData).to.deep.equal({ modelVersion: 'basic model', + modelTimestamp: 1606772895, currency: 'USD', schema: { delimiter: '|', @@ -201,6 +203,7 @@ describe('the price floors module', function () { resultingData = getFloorsDataForAuction(inputFloorData, 'this_is_a_div'); expect(resultingData).to.deep.equal({ modelVersion: 'basic model', + modelTimestamp: 1606772895, currency: 'USD', schema: { delimiter: '^', @@ -429,6 +432,7 @@ describe('the price floors module', function () { skipped: true, floorMin: undefined, modelVersion: undefined, + modelTimestamp: undefined, location: 'noData', skipRate: 0, fetchStatus: undefined, @@ -463,6 +467,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'adUnit Model Version', + modelTimestamp: 1606772895, location: 'adUnit', skipRate: 0, fetchStatus: undefined, @@ -496,6 +501,7 @@ describe('the price floors module', function () { validateBidRequests(true, { skipped: false, modelVersion: 'adUnit Model Version', + modelTimestamp: 1606772895, location: 'adUnit', skipRate: 0, floorMin: 7, @@ -510,6 +516,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -531,6 +538,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -545,6 +553,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -559,6 +568,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -582,6 +592,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 50, fetchStatus: undefined, @@ -596,6 +607,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 10, fetchStatus: undefined, @@ -610,6 +622,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -674,6 +687,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-1', + modelTimestamp: undefined, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -687,6 +701,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-2', + modelTimestamp: undefined, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -700,6 +715,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-3', + modelTimestamp: undefined, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -729,6 +745,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: undefined, @@ -808,6 +825,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: 'timeout', @@ -846,6 +864,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelTimestamp: 1606772895, location: 'fetch', skipRate: 0, fetchStatus: 'success', @@ -883,6 +902,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelTimestamp: 1606772895, location: 'fetch', skipRate: 0, fetchStatus: 'success', @@ -923,6 +943,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelTimestamp: 1606772895, location: 'fetch', skipRate: 95, fetchStatus: 'success', @@ -945,6 +966,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: 'error', @@ -969,6 +991,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, fetchStatus: 'success', diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 4891b8d3282..0d6cf331e52 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -852,6 +852,7 @@ describe('rubicon analytics adapter', function () { auctionInit.bidderRequests[0].bids[0].floorData = { skipped: false, modelVersion: 'someModelName', + modelTimestamp: 1606772895, location: 'setConfig', skipRate: 15, fetchStatus: 'error', @@ -953,6 +954,7 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].floors).to.deep.equal({ location: 'setConfig', modelName: 'someModelName', + modelTimestamp: 1606772895, skipped: false, enforcement: true, dealsEnforced: false, @@ -998,6 +1000,7 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].floors).to.deep.equal({ location: 'setConfig', modelName: 'someModelName', + modelTimestamp: 1606772895, skipped: false, enforcement: true, dealsEnforced: false, From a17c2346c233c301b948c5b5b69ad1d30a26c53a Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 9 Dec 2020 20:26:28 +0100 Subject: [PATCH 0450/1476] Adagio Bid Adapter: hotfix - detect support for intersectionObserver (#6095) * Detect support for intersectionObserver * Fix IE11 * Add special params for non standard integration * Stronger IntersectionObserver detection * Fix test and add new params to .md file --- modules/adagioBidAdapter.js | 19 +++++++++-- modules/adagioBidAdapter.md | 4 +++ test/spec/modules/adagioBidAdapter_spec.js | 37 ++++++++++++++++++++-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index cab233d5387..6f7feec59c9 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -495,6 +495,12 @@ function autoDetectEnvironment() { return environment; }; +function supportIObs() { + const currentWindow = internal.getCurrentWindow(); + return !!(currentWindow && currentWindow.IntersectionObserver && currentWindow.IntersectionObserverEntry && + currentWindow.IntersectionObserverEntry.prototype && 'intersectionRatio' in currentWindow.IntersectionObserverEntry.prototype); +} + function getFeatures(bidRequest, bidderRequest) { const { adUnitCode, params } = bidRequest; const { adUnitElementId } = params; @@ -569,6 +575,7 @@ export const internal = { getRefererInfo, adagioScriptFromLocalStorageCb, getCurrentWindow, + supportIObs, canAccessTopWindow, isRendererPreferredFromPublisher }; @@ -692,15 +699,21 @@ export const spec = { return false; } - const { organizationId, site, placement } = params; - const adUnitElementId = params.adUnitElementId || internal.autoDetectAdUnitElementId(adUnitCode); + const { organizationId, site } = params; + const adUnitElementId = (params.useAdUnitCodeAsAdUnitElementId === true) + ? adUnitCode + : params.adUnitElementId || internal.autoDetectAdUnitElementId(adUnitCode); + const placement = (params.useAdUnitCodeAsPlacement === true) ? adUnitCode : params.placement; const environment = params.environment || internal.autoDetectEnvironment(); + const supportIObs = internal.supportIObs(); // insure auto-detected params are kept in `bid` object. bid.params = { ...params, adUnitElementId, - environment + environment, + placement, + supportIObs }; const debugData = () => ({ diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index c55a24f1115..aa79338d79e 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -38,6 +38,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. + 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 // Optional debug mode, used to get a bid response with expected cpm. debug: { enabled: true, @@ -76,6 +78,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. + 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 video: { skip: 0 // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 2cf97a1129b..0a585caaa1a 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -144,6 +144,19 @@ describe('Adagio bid adapter', () => { sinon.assert.callCount(utils.logWarn, 1); }); + it('should use adUnit code for adUnitElementId and placement params', function() { + const bid01 = new BidRequestBuilder({ params: { + organizationId: '1000', + site: 'site-name', + useAdUnitCodeAsPlacement: true, + useAdUnitCodeAsAdUnitElementId: true + }}).build(); + + expect(spec.isBidRequestValid(bid01)).to.equal(true); + expect(bid01.params.adUnitElementId).to.equal('adunit-code'); + expect(bid01.params.placement).to.equal('adunit-code'); + }) + it('should return false when a required param is missing', function() { const bid01 = new BidRequestBuilder({ params: { organizationId: '1000', @@ -229,7 +242,8 @@ describe('Adagio bid adapter', () => { placement: 'PAVE_ATF', site: 'SITE-NAME', adUnitElementId: 'gpt-adunit-code', - environment: 'desktop' + environment: 'desktop', + supportIObs: true } }], auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', @@ -249,7 +263,8 @@ describe('Adagio bid adapter', () => { placement: 'PAVE_ATF', site: 'SITE-NAME', adUnitElementId: 'gpt-adunit-code', - environment: 'desktop' + environment: 'desktop', + supportIObs: true } }], auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', @@ -260,6 +275,7 @@ describe('Adagio bid adapter', () => { it('should store bids config once by bid in window.top if it accessible', function() { sandbox.stub(adagio, 'getCurrentWindow').returns(window.top); + sandbox.stub(adagio, 'supportIObs').returns(true); // replace by the values defined in beforeEach window.top.ADAGIO = { @@ -274,8 +290,22 @@ describe('Adagio bid adapter', () => { expect(find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-02')).to.deep.eql(expected[1]); }); + it('should detect IntersectionObserver support', function() { + sandbox.stub(adagio, 'getCurrentWindow').returns(window.top); + sandbox.stub(adagio, 'supportIObs').returns(false); + + window.top.ADAGIO = { + ...window.ADAGIO + }; + + spec.isBidRequestValid(bid01); + const validBidReq = find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01'); + expect(validBidReq.bids[0].params.supportIObs).to.equal(false); + }); + it('should store bids config once by bid in current window', function() { sandbox.stub(adagio, 'getCurrentWindow').returns(window.self); + sandbox.stub(adagio, 'supportIObs').returns(true); spec.isBidRequestValid(bid01); spec.isBidRequestValid(bid02); @@ -740,7 +770,8 @@ describe('Adagio bid adapter', () => { pagetype: 'ARTICLE', category: 'NEWS', subcategory: 'SPORT', - environment: 'desktop' + environment: 'desktop', + supportIObs: true }, adUnitCode: 'adunit-code', mediaTypes: { From 83bafc0475a68033f6e0d9d1fe9e7014cea5ead8 Mon Sep 17 00:00:00 2001 From: Gena Date: Wed, 9 Dec 2020 22:11:58 +0200 Subject: [PATCH 0451/1476] Adt new alias (#6004) * Add new alias * Add new aliases and fix endpoints * lint * Fix tests --- modules/adtelligentBidAdapter.js | 16 ++++++++++++---- test/spec/modules/adtelligentBidAdapter_spec.js | 17 +++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 51138a2cac7..b2e37767a1e 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -12,15 +12,23 @@ const HOST_GETTERS = { default: (function () { let num = 0; return function () { - return 'ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com' + return 'ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com'; } }()), appaloosa: function () { - return 'hb.appaloosa.media' + return 'ghb.hb.appaloosa.media'; + }, + onefiftytwomedia: function() { + return 'ghb.ads.152media.com'; + }, + mediafuse: function() { + return 'ghb.hbmp.mediafuse.com'; } + } const getUri = function (bidderCode) { - let getter = HOST_GETTERS[bidderCode] || HOST_GETTERS['default']; + let bidderWithoutSuffix = bidderCode.split('_')[0]; + let getter = HOST_GETTERS[bidderWithoutSuffix] || HOST_GETTERS['default']; return PROTOCOL + getter() + AUCTION_PATH } const OUTSTREAM_SRC = 'https://player.adtelligent.com/outstream-unit/2.01/outstream.min.js'; @@ -32,7 +40,7 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, gvlid: 410, - aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa'], + aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', 'mediafuse'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { return !!utils.deepAccess(bid, 'params.aid'); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 62449771416..5c1e4a38d03 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -11,8 +11,13 @@ const EXPECTED_ENDPOINTS = [ 'https://ghb.adtelligent.com/v2/auction/' ]; const aliasEP = { - appaloosa: 'https://hb.appaloosa.media/v2/auction/' + appaloosa: 'https://ghb.hb.appaloosa.media/v2/auction/', + appaloosa_publisherSuffix: 'https://ghb.hb.appaloosa.media/v2/auction/', + onefiftytwomedia: 'https://ghb.ads.152media.com/v2/auction/', + mediafuse: 'https://ghb.hbmp.mediafuse.com/v2/auction/' }; + +const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; const DISPLAY_REQUEST = { 'bidder': 'adtelligent', 'params': { @@ -244,10 +249,10 @@ describe('adtelligentBidAdapter', () => { let videoBidRequests = [VIDEO_REQUEST]; let displayBidRequests = [DISPLAY_REQUEST]; let videoAndDisplayBidRequests = [DISPLAY_REQUEST, VIDEO_REQUEST]; - const displayRequest = spec.buildRequests(displayBidRequests, {}); - const videoRequest = spec.buildRequests(videoBidRequests, {}); - const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, {}); - const rotatingRequest = spec.buildRequests(displayBidRequests, {}); + const displayRequest = spec.buildRequests(displayBidRequests, DEFAULT_ADATPER_REQ); + const videoRequest = spec.buildRequests(videoBidRequests, DEFAULT_ADATPER_REQ); + const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, DEFAULT_ADATPER_REQ); + const rotatingRequest = spec.buildRequests(displayBidRequests, DEFAULT_ADATPER_REQ); it('rotates endpoints', () => { const bidReqUrls = [displayRequest[0], videoRequest[0], videoAndDisplayRequests[0], rotatingRequest[0]].map(br => br.url); expect(bidReqUrls).to.deep.equal(EXPECTED_ENDPOINTS); @@ -276,7 +281,7 @@ describe('adtelligentBidAdapter', () => { expect(videoAndDisplayRequests.every(comparator)).to.be.true; }); it('forms correct ADPOD request', () => { - const pbBidReqData = spec.buildRequests([ADPOD_REQUEST], {})[0].data; + const pbBidReqData = spec.buildRequests([ADPOD_REQUEST], DEFAULT_ADATPER_REQ)[0].data; const impRequest = pbBidReqData.BidRequests[0] expect(impRequest.AdType).to.be.equal('video'); expect(impRequest.Adpod).to.be.a('object'); From 09601b65a6a82aca419afcc618bd798cf429aa22 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 9 Dec 2020 12:49:22 -0800 Subject: [PATCH 0452/1476] Prebid 4.9.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed4514cee42..5c82e724453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.19.0-pre", + "version": "4.19.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 3496b3f627202fedca7a4a42d97407b819d6c014 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 9 Dec 2020 13:14:51 -0800 Subject: [PATCH 0453/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c82e724453..04ec495c93d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.19.0", + "version": "4.20.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f7a91ca2e00c2530d7a4a8fbaa959aab8e455b26 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Wed, 9 Dec 2020 17:54:33 -0500 Subject: [PATCH 0454/1476] Add Release Drafter Instructions to PR_REVIEW.md (#6085) * Add Release Drafter Instructions to PR_REVIEW.md * Add Prettier Formatting Makes keywords more distinct. Co-authored-by: Scott Menzer Co-authored-by: Scott Menzer --- PR_REVIEW.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index f991a0254f5..662a1a871c8 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -18,7 +18,10 @@ For modules and core platform updates, the initial reviewer should request an ad - If the change results in needing updates to docs (such as public API change, module interface etc), add a label for "needs docs" and inform the submitter they must submit a docs PR to update the appropriate area of Prebid.org **before the PR can merge**. Help them with finding where the docs are located on prebid.org if needed. - If all above is good, add a `LGTM` comment and, if the change is in PBS-core or is an important module like the prebidServerBidAdapter, request 1 additional core member to review. - Once there are 2 `LGTM` on the PR, merge to master -- Add a line into the [draft release](https://github.com/prebid/Prebid.js/releases) notes for this submission. If no draft release is available, create one using [this template]( https://gist.github.com/mkendall07/c3af6f4691bed8a46738b3675cb5a479) +- The [draft release](https://github.com/prebid/Prebid.js/releases) notes are managed by [release drafter](https://github.com/release-drafter/release-drafter). To get the PR added to the release notes do the steps below. A github action will use that information to build the release notes. + - Adjust the PR Title to be appropriate for release notes + - Add a label for `feature`, `maintenance`, `fix`, `bugfix` or `bug` to categorize the PR + - Add a semver label of `major`, `minor` or `patch` to indicate the scope of change ### Reviewing a New or Updated Bid Adapter Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/bidder-adaptor.html From 03b8213de8bede7883323fff6f3ccf00bad08410 Mon Sep 17 00:00:00 2001 From: olafbuitelaar Date: Thu, 10 Dec 2020 10:36:45 +0100 Subject: [PATCH 0455/1476] fix typo in PREVENT_WRITING_ON_MAIN_DOCUMENT (#6102) --- src/constants.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.json b/src/constants.json index 7c0af445cdb..7e82946b65c 100644 --- a/src/constants.json +++ b/src/constants.json @@ -41,7 +41,7 @@ "AUCTION_DEBUG": "auctionDebug" }, "AD_RENDER_FAILED_REASON" : { - "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocuemnt", + "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", "NO_AD": "noAd", "EXCEPTION": "exception", "CANNOT_FIND_AD": "cannotFindAd", From 754ce678b47b8e13148e0a02a9db0d477ce3eaa6 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 10 Dec 2020 06:10:25 -0500 Subject: [PATCH 0456/1476] appnexusBidAdapter - update segment param logic (#6103) --- modules/appnexusBidAdapter.js | 14 +++++++++++++- test/spec/modules/appnexusBidAdapter_spec.js | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 203835db611..c102bee4e58 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -107,7 +107,19 @@ export const spec = { .filter(param => includes(USER_PARAMS, param)) .forEach((param) => { let uparam = utils.convertCamelToUnderscore(param); - userObj[uparam] = userObjBid.params.user[param] + if (param === 'segments' && utils.isArray(userObjBid.params.user[param])) { + let segs = []; + userObjBid.params.user[param].forEach(val => { + if (utils.isNumber(val)) { + segs.push({'id': val}); + } else if (utils.isPlainObject(val)) { + segs.push(val); + } + }); + userObj[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; + } }); } diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 426259639e8..9b12d892440 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -265,6 +265,7 @@ describe('AppNexusAdapter', function () { placementId: '10433394', user: { externalUid: '123', + segments: [123, { id: 987, value: 876 }], foobar: 'invalid' } } @@ -277,6 +278,7 @@ describe('AppNexusAdapter', function () { expect(payload.user).to.exist; expect(payload.user).to.deep.equal({ external_uid: '123', + segments: [{id: 123}, {id: 987, value: 876}] }); }); From c8353d39bc769e4fe3ee3b9a7dd3467dbd7bdf2c Mon Sep 17 00:00:00 2001 From: Hiroaki Kubota Date: Fri, 11 Dec 2020 08:56:10 +0900 Subject: [PATCH 0457/1476] Change craftBidAdapter request URL (#6096) --- modules/craftBidAdapter.js | 5 +++-- test/spec/modules/craftBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index 3838f5dee59..0124f96a107 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -7,7 +7,7 @@ import includes from 'core-js-pure/features/array/includes.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'craft'; -const URL = 'https://gacraft.jp/prebid-v3'; +const URL_BASE = 'https://gacraft.jp/prebid-v3'; const TTL = 360; const storage = getStorageManager(); @@ -143,10 +143,11 @@ function formatRequest(payload, bidderRequest) { withCredentials: false }; } + const payloadString = JSON.stringify(payload); return { method: 'POST', - url: URL, + url: `${URL_BASE}/${payload.tags[0].sitekey}`, data: payloadString, bidderRequest, options diff --git a/test/spec/modules/craftBidAdapter_spec.js b/test/spec/modules/craftBidAdapter_spec.js index ef7dd7c3232..3f4bc977016 100644 --- a/test/spec/modules/craftBidAdapter_spec.js +++ b/test/spec/modules/craftBidAdapter_spec.js @@ -81,7 +81,7 @@ describe('craftAdapter', function () { it('sends bid request to ENDPOINT via POST', function () { let request = spec.buildRequests(bidRequests, bidderRequest); expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://gacraft.jp/prebid-v3'); + expect(request.url).to.equal('https://gacraft.jp/prebid-v3/craft-prebid-example'); let data = JSON.parse(request.data); expect(data.tags).to.deep.equals([{ sitekey: 'craft-prebid-example', From 0a5f9db693d54278e8198fb00be018089d892db4 Mon Sep 17 00:00:00 2001 From: Oleg Naydenov Date: Fri, 11 Dec 2020 20:59:20 +0200 Subject: [PATCH 0458/1476] Add Kubient bid adapter, Remove alias from Fidelity bid adapter. (#6084) * Add New Kubient Bid Adapter * Add New Kubient Bid Adapter * Fidelity Bid Adapter Update. Less 'Kubient' Alias * New Kubient Bid Adapter. Errors fix. --- modules/fidelityBidAdapter.js | 1 - modules/kubientBidAdapter.js | 111 +++++++++ modules/kubientBidAdapter.md | 26 ++ test/spec/modules/kubientBidAdapter_spec.js | 259 ++++++++++++++++++++ 4 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 modules/kubientBidAdapter.js create mode 100644 modules/kubientBidAdapter.md create mode 100644 test/spec/modules/kubientBidAdapter_spec.js diff --git a/modules/fidelityBidAdapter.js b/modules/fidelityBidAdapter.js index baf5384fbfe..fac273721ff 100644 --- a/modules/fidelityBidAdapter.js +++ b/modules/fidelityBidAdapter.js @@ -6,7 +6,6 @@ const BIDDER_SERVER = 'x.fidelity-media.com'; const FIDELITY_VENDOR_ID = 408; export const spec = { code: BIDDER_CODE, - aliases: ['kubient'], gvlid: 408, isBidRequestValid: function isBidRequestValid(bid) { return !!(bid && bid.params && bid.params.zoneid); diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js new file mode 100644 index 00000000000..8f6ea53ecce --- /dev/null +++ b/modules/kubientBidAdapter.js @@ -0,0 +1,111 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'kubient'; +const END_POINT = 'https://kssp.kbntx.ch/pbjs'; +const VERSION = '1.0'; +const VENDOR_ID = 794; +export const spec = { + code: BIDDER_CODE, + gvlid: VENDOR_ID, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid && bid.params); + }, + buildRequests: function (validBidRequests, bidderRequest) { + if (!validBidRequests || !bidderRequest) { + return; + } + const result = validBidRequests.map(function (bid) { + let data = { + v: VERSION, + requestId: bid.bidderRequestId, + adSlots: [{ + bidId: bid.bidId, + zoneId: bid.params.zoneid || '', + floor: bid.params.floor || 0.0, + sizes: bid.sizes || [], + schain: bid.schain || {}, + mediaTypes: bid.mediaTypes + }], + referer: (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) ? bidderRequest.refererInfo.referer : null, + tmax: bidderRequest.timeout, + gdpr: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0, + consent: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) ? bidderRequest.gdprConsent.consentString : null, + consentGiven: kubientGetConsentGiven(bidderRequest.gdprConsent), + uspConsent: bidderRequest.uspConsent + }; + return { + method: 'POST', + url: END_POINT, + data: JSON.stringify(data) + }; + }); + return result; + }, + interpretResponse: function interpretResponse(serverResponse, request) { + if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { + return []; + } + let bidResponses = []; + serverResponse.body.seatbid.forEach(seatbid => { + let bids = seatbid.bid || []; + bids.forEach(bid => { + bidResponses.push({ + requestId: bid.bidId, + cpm: bid.price, + currency: bid.cur, + width: bid.w, + height: bid.h, + creativeId: bid.creativeId, + netRevenue: bid.netRevenue, + ttl: bid.ttl, + ad: bid.adm + }); + }); + }); + return bidResponses; + }, + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = []; + let gdprParams = ''; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + gdprParams = `?consent_str=${gdprConsent.consentString}`; + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = gdprParams + `&gdpr=${Number(gdprConsent.gdprApplies)}`; + } + gdprParams = gdprParams + `&consent_given=` + kubientGetConsentGiven(gdprConsent); + } + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: 'https://kdmp.kbntx.ch/init.html' + gdprParams + }); + } + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: 'https://kdmp.kbntx.ch/init.png' + gdprParams + }); + } + return syncs; + } +}; + +function kubientGetConsentGiven(gdprConsent) { + let consentGiven = 0; + if (typeof gdprConsent !== 'undefined') { + let apiVersion = utils.deepAccess(gdprConsent, `apiVersion`); + switch (apiVersion) { + case 1: + consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendorConsents.${VENDOR_ID}`) ? 1 : 0; + break; + case 2: + consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendor.consents.${VENDOR_ID}`) ? 1 : 0; + break; + } + } + return consentGiven; +} +registerBidder(spec); diff --git a/modules/kubientBidAdapter.md b/modules/kubientBidAdapter.md new file mode 100644 index 00000000000..9f3e1d5f52e --- /dev/null +++ b/modules/kubientBidAdapter.md @@ -0,0 +1,26 @@ +# Overview +​ +**Module Name**: Kubient Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: artem.aleksashkin@kubient.com +​ +# Description +​ +Connects to Kubient KSSP demand source to fetch bids. +​ +# Test Parameters +``` + var adUnits = [{ + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250],[728, 90]], + } + }, + bids: [{ + "bidder": "kubient", + "params": { + "zoneid": "5fbb948f1e22b", + } + }] + }]; diff --git a/test/spec/modules/kubientBidAdapter_spec.js b/test/spec/modules/kubientBidAdapter_spec.js new file mode 100644 index 00000000000..1df4370b2ba --- /dev/null +++ b/test/spec/modules/kubientBidAdapter_spec.js @@ -0,0 +1,259 @@ +import { expect, assert } from 'chai'; +import { spec } from 'modules/kubientBidAdapter.js'; + +describe('KubientAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'kubient', + bidderRequestId: '145e1d6a7837c9', + params: { + zoneid: '5678', + floor: 0.05, + }, + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + domain: 'example.com' + } + ] + } + }; + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let uspConsentData = '1YCC'; + let bidderRequest = { + bidderCode: 'kubient', + auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', + bidderRequestId: 'ffffffffffffff', + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000, + refererInfo: { + referer: 'http://www.example.com', + reachedTop: true, + }, + gdprConsent: { + consentString: consentString, + gdprApplies: true + }, + uspConsent: uspConsentData, + bids: [bid] + }; + describe('buildRequests', function () { + let serverRequests = spec.buildRequests([bid], bidderRequest); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequests).to.be.an('array'); + }); + for (let i = 0; i < serverRequests.length; i++) { + let serverRequest = serverRequests[i]; + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest.method).to.be.a('string'); + expect(serverRequest.url).to.be.a('string'); + expect(serverRequest.data).to.be.a('string'); + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://kssp.kbntx.ch/pbjs'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = JSON.parse(serverRequest.data); + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('v', 'requestId', 'adSlots', 'gdpr', 'referer', 'tmax', 'consent', 'consentGiven', 'uspConsent'); + expect(data.v).to.exist.and.to.be.a('string'); + expect(data.requestId).to.exist.and.to.be.a('string'); + expect(data.referer).to.be.a('string'); + expect(data.tmax).to.exist.and.to.be.a('number'); + expect(data.gdpr).to.exist.and.to.be.within(0, 1); + expect(data.consent).to.equal(consentString); + expect(data.uspConsent).to.exist.and.to.equal(uspConsentData); + for (let j = 0; j < data['adSlots'].length; j++) { + let adSlot = data['adSlots'][i]; + expect(adSlot).to.have.all.keys('bidId', 'zoneId', 'floor', 'sizes', 'schain', 'mediaTypes'); + expect(adSlot.bidId).to.be.a('string'); + expect(adSlot.zoneId).to.be.a('string'); + expect(adSlot.floor).to.be.a('number'); + expect(adSlot.sizes).to.be.an('array'); + expect(adSlot.schain).to.be.an('object'); + expect(adSlot.mediaTypes).to.be.an('object'); + } + }); + } + }); + + describe('isBidRequestValid', function () { + it('Should return true when required params are found', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when required params are not found', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when params are not found', function () { + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret response', function () { + const serverResponse = { + body: + { + seatbid: [ + { + bid: [ + { + bidId: '000', + price: 1.5, + adm: '
test
', + creativeId: 'creativeId', + w: 300, + h: 250, + cur: 'USD', + netRevenue: false, + ttl: 360 + } + ] + } + ] + } + }; + let bannerResponses = spec.interpretResponse(serverResponse); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'ad', 'creativeId', 'width', 'height', 'currency', 'netRevenue', 'ttl'); + expect(dataItem.requestId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].bidId); + expect(dataItem.cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(dataItem.ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(dataItem.creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].creativeId); + expect(dataItem.width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(dataItem.height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(dataItem.currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].cur); + expect(dataItem.netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(serverResponse.body.seatbid[0].bid[0].netRevenue); + expect(dataItem.ttl).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].ttl); + }); + + it('Should return no ad when not given a server response', function () { + const ads = spec.interpretResponse(null); + expect(ads).to.be.an('array').and.to.have.length(0); + }); + }); + + describe('getUserSyncs', function () { + it('should register the sync iframe without gdpr', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&consent_given=0'); + }); + it('should register the sync iframe with gdpr', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=0'); + }); + it('should register the sync iframe with gdpr vendor', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString, + apiVersion: 1, + vendorData: { + vendorConsents: { + 794: 1 + } + } + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=1'); + }); + it('should register the sync image without gdpr', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&consent_given=0'); + }); + it('should register the sync image with gdpr', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=0'); + }); + it('should register the sync image with gdpr vendor', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString, + apiVersion: 2, + vendorData: { + vendor: { + consents: { + 794: 1 + } + } + } + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=1'); + }); + }) +}); From 9f38423d98a9572163541957a50c70f1dedae037 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 11 Dec 2020 13:30:24 -0800 Subject: [PATCH 0459/1476] [Sharethrough] Add Support for badv/bcat and Identity Link User ID (#6100) * Add support for badv and bcat [#175358412] Co-authored-by: Michael Duran * Add support for Identity Link [#175688307] Co-authored-by: Michael Duran Co-authored-by: Michael Duran --- modules/sharethroughBidAdapter.js | 16 +++++- modules/sharethroughBidAdapter.md | 8 ++- .../modules/sharethroughBidAdapter_spec.js | 54 ++++++++++++++++--- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 7df161db713..89484b1c68b 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; -const VERSION = '3.2.1'; +const VERSION = '3.3.0'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -56,6 +56,10 @@ export const sharethroughAdapterSpec = { query.pubcid = bidRequest.crumbs.pubcid; } + if (bidRequest.userId && bidRequest.userId.idl_env) { + query.idluid = bidRequest.userId.idl_env; + } + if (bidRequest.schain) { query.schain = JSON.stringify(bidRequest.schain); } @@ -64,6 +68,14 @@ export const sharethroughAdapterSpec = { query.bidfloor = parseFloat(bidRequest.bidfloor); } + if (bidRequest.params.badv) { + query.badv = bidRequest.params.badv; + } + + if (bidRequest.params.bcat) { + query.bcat = bidRequest.params.bcat; + } + // Data that does not need to go to the server, // but we need as part of interpretResponse() const strData = { @@ -73,7 +85,7 @@ export const sharethroughAdapterSpec = { }; return { - method: 'GET', + method: 'POST', url: STR_ENDPOINT, data: query, strData: strData diff --git a/modules/sharethroughBidAdapter.md b/modules/sharethroughBidAdapter.md index 2290e370cae..396b8164577 100644 --- a/modules/sharethroughBidAdapter.md +++ b/modules/sharethroughBidAdapter.md @@ -29,7 +29,13 @@ Module that connects to Sharethrough's demand sources // OPTIONAL - If iframeSize is provided, we'll use this size for the iframe // otherwise we'll grab the largest size from the sizes array // This is ignored if iframe: false - iframeSize: [250, 250] + iframeSize: [250, 250], + + // OPTIONAL - Blocked Advertiser Domains + badv: ['domain1.com', 'domain2.com'], + + // OPTIONAL - Blocked Categories (IAB codes) + bcat: ['IAB1-1', 'IAB1-2'], } } ] diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index d45d1e977e6..cd9071a6098 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -14,7 +14,8 @@ const bidRequests = [ }, userId: { tdid: 'fake-tdid', - pubcid: 'fake-pubcid' + pubcid: 'fake-pubcid', + idl_env: 'fake-identity-link' }, crumbs: { pubcid: 'fake-pubcid-in-crumbs-obj' @@ -40,12 +41,32 @@ const bidRequests = [ iframe: true, iframeSize: [500, 500] } - } + }, + { + bidder: 'sharethrough', + bidId: 'bidId4', + sizes: [[700, 400]], + placementCode: 'bar', + params: { + pkey: 'dddd4444', + badv: ['domain1.com', 'domain2.com'] + } + }, + { + bidder: 'sharethrough', + bidId: 'bidId5', + sizes: [[700, 400]], + placementCode: 'bar', + params: { + pkey: 'eeee5555', + bcat: ['IAB1-1', 'IAB1-2'] + } + }, ]; const prebidRequests = [ { - method: 'GET', + method: 'POST', url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', data: { bidId: 'bidId', @@ -57,7 +78,7 @@ const prebidRequests = [ } }, { - method: 'GET', + method: 'POST', url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', data: { bidId: 'bidId', @@ -69,7 +90,7 @@ const prebidRequests = [ } }, { - method: 'GET', + method: 'POST', url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', data: { bidId: 'bidId', @@ -82,7 +103,7 @@ const prebidRequests = [ } }, { - method: 'GET', + method: 'POST', url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', data: { bidId: 'bidId', @@ -94,7 +115,7 @@ const prebidRequests = [ } }, { - method: 'GET', + method: 'POST', url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', data: { bidId: 'bidId', @@ -225,7 +246,7 @@ describe('sharethrough adapter spec', function() { expect(builtBidRequests[0].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1'); expect(builtBidRequests[1].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1'); - expect(builtBidRequests[0].method).to.eq('GET'); + expect(builtBidRequests[0].method).to.eq('POST'); }); it('should set the instant_play_capable parameter correctly based on browser userAgent string', function() { @@ -315,6 +336,11 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); }); + it('should add the idluid parameter if a bid request contains a value for Identity Link from Live Ramp', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.idluid).to.eq('fake-identity-link'); + }); + it('should add Sharethrough specific parameters', function() { const builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0]).to.deep.include({ @@ -346,6 +372,18 @@ describe('sharethrough adapter spec', function() { expect(builtBidRequest.data.schain).to.eq(JSON.stringify(bidRequest.schain)); }); + it('should add badv if provided', () => { + const builtBidRequest = spec.buildRequests([bidRequests[3]])[0]; + + expect(builtBidRequest.data.badv).to.have.members(['domain1.com', 'domain2.com']) + }); + + it('should add bcat if provided', () => { + const builtBidRequest = spec.buildRequests([bidRequests[4]])[0]; + + expect(builtBidRequest.data.bcat).to.have.members(['IAB1-1', 'IAB1-2']) + }); + it('should not add a supply chain parameter if schain is missing', function() { const bidRequest = spec.buildRequests(bidRequests)[0]; expect(bidRequest.data).to.not.include.any.keys('schain'); From 25270510561bf2b8d3f303d25b0a6c80277fd03c Mon Sep 17 00:00:00 2001 From: Egor Gordeev <48566506+egsgordeev@users.noreply.github.com> Date: Mon, 14 Dec 2020 10:51:48 +0400 Subject: [PATCH 0460/1476] Sovrn: Pass the imp.ext.deals field (#6098) * EX-2549 Pass segments parameter as imp.ext.dealids array * EX-2549 Address Jon's feedback * EX-2549 Reworked the solution * EX-2549 Blackbird compatibility * EX-2549 Address Jon's comments * EX-2549 Addressed upstream PR comments --- modules/sovrnBidAdapter.js | 14 ++++++++++-- test/spec/modules/sovrnBidAdapter_spec.js | 26 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 62f5e85779e..8f8158fd0c9 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { bidSizes = ((utils.isArray(bidSizes) && utils.isArray(bidSizes[0])) ? bidSizes : [bidSizes]) bidSizes = bidSizes.filter(size => utils.isArray(size)) const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})) - sovrnImps.push({ + const imp = { adunitcode: bid.adUnitCode, id: bid.bidId, banner: { @@ -63,7 +63,17 @@ export const spec = { }, tagid: String(utils.getBidIdParameter('tagid', bid.params)), bidfloor: utils.getBidIdParameter('bidfloor', bid.params) - }); + } + + const segmentsString = utils.getBidIdParameter('segments', bid.params) + + if (segmentsString) { + imp.ext = { + deals: segmentsString.split(',').map(deal => deal.trim()) + } + } + + sovrnImps.push(imp); }); const page = bidderRequest.refererInfo.referer diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 983ade4dd14..769be73a272 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -276,6 +276,32 @@ describe('sovrnBidAdapter', function() { expect(data.user.ext.tpid[0].uid).to.equal('A_CRITEO_ID') expect(data.user.ext.prebid_criteoid).to.equal('A_CRITEO_ID') }); + + it('should ignore empty segments', function() { + const payload = JSON.parse(request.data) + expect(payload.imp[0].ext).to.be.undefined + }) + + it('should pass the segments param value as trimmed deal ids array', function() { + const segmentsRequests = [{ + 'bidder': 'sovrn', + 'params': { + 'segments': ' test1,test2 ' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + const request = spec.buildRequests(segmentsRequests, bidderRequest) + const payload = JSON.parse(request.data) + expect(payload.imp[0].ext.deals[0]).to.equal('test1') + expect(payload.imp[0].ext.deals[1]).to.equal('test2') + }) }); describe('interpretResponse', function () { From 42be508d35e10ab60d9421873d2b60ae97b66cb0 Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Mon, 14 Dec 2020 12:07:32 +0200 Subject: [PATCH 0461/1476] oneVideo Adapter - Dynamic TTL support (SAPR-15473) (#6108) * ttl incoming value or default 300 * ttl validation 0-3600 * Fix to oneVideo tests * added ttl unit tests * updated md file * update minor version 3.0.5 * Updated unit test for minor version check * remove x from liveIntentIdSystem_spec.js * md conflict fix * fix missing comma in md file * update minor version 3.0.5 * remove x from liveIntentIdSystem_spec.js * update md file * added ttl to md file * ttl incoming value or default 300 * ttl validation 0-3600 * Fix to oneVideo tests * added ttl unit tests * updated md file * update minor version 3.0.5 * Updated unit test for minor version check * remove x from liveIntentIdSystem_spec.js * md conflict fix * update minor version 3.0.5 * remove x from liveIntentIdSystem_spec.js * update md file * added ttl to md file * cleanup --- modules/oneVideoBidAdapter.js | 5 ++-- modules/oneVideoBidAdapter.md | 2 ++ test/spec/modules/oneVideoBidAdapter_spec.js | 28 ++++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index c287bc2f3b7..8a71910e8fc 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.0.4', + VERSION: '3.0.5', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -99,7 +99,7 @@ export const spec = { width: size.width, height: size.height, currency: response.cur, - ttl: 100, + ttl: (bidRequest.params.video.ttl > 0 && bidRequest.params.video.ttl <= 3600) ? bidRequest.params.video.ttl : 300, netRevenue: true, adUnitCode: bidRequest.adUnitCode }; @@ -113,7 +113,6 @@ export const spec = { } else if (bid.adm) { bidResponse.vastXml = bid.adm; } - if (bidRequest.mediaTypes.video) { bidResponse.renderer = (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined; } diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index 92958af9e83..d413c9d64e5 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -40,6 +40,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to inventoryid: 123, minduration: 10, maxduration: 30, + ttl: 300, custom: { key1: "value1", key2: 123345 @@ -89,6 +90,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to inventoryid: 123, minduration: 10, maxduration: 30, + ttl: 250 }, site: { id: 1, diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 331ac8976e6..83adf8a4b11 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -217,7 +217,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.0.4'; + const VERSION = '3.0.5'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -407,7 +407,7 @@ describe('OneVideoBidAdapter', function () { height: 480, mediaType: 'video', currency: 'USD', - ttl: 100, + ttl: 300, netRevenue: true, adUnitCode: bidRequest.adUnitCode, renderer: (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined, @@ -434,6 +434,30 @@ describe('OneVideoBidAdapter', function () { expect(bidResponse.mediaType).to.equal('banner'); expect(bidResponse.renderer).to.be.undefined; }); + + it('should default ttl to 300', function () { + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.ttl).to.equal(300); + }); + it('should not allow ttl above 3601, default to 300', function () { + bidRequest.params.video.ttl = 3601; + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.ttl).to.equal(300); + }); + it('should not allow ttl below 1, default to 300', function () { + bidRequest.params.video.ttl = 0; + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.ttl).to.equal(300); + }); + it('should use custom ttl if under 3600', function () { + bidRequest.params.video.ttl = 1000; + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.ttl).to.equal(1000); + }); }); describe('when GDPR and uspConsent applies', function () { From a2454a5e9f18ca72e34c4eedbd144f88f5e7872e Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Mon, 14 Dec 2020 07:04:32 -0500 Subject: [PATCH 0462/1476] Add Gulp Review-Start Task (#6067) * add review-start gulp command * remove watch, unecessary for reviews * add instructions to reviewer file for new command * Updates to add back watch * updates to had reviewer hub page * Update PR_REVIEW.md --- PR_REVIEW.md | 11 ++++++ gulpfile.js | 20 +++++++++- integrationExamples/reviewerTools/index.html | 40 ++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100755 integrationExamples/reviewerTools/index.html diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 662a1a871c8..0519cbb7b6e 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -5,6 +5,17 @@ If the PR is for a standard bid adapter or a standard analytics adapter, just th For modules and core platform updates, the initial reviewer should request an additional team member to review as a sanity check. Merge should only happen when the PR has 2 `LGTM` from the core team and a documentation PR if required. +### Running Tests and Verifying Integrations + +General gulp commands include separate commands for serving the codebase on a built in webserver, creating code coverage reports and allowing serving integration examples. The `review-start` gulp command combinese those into one command. + +- Run `gulp review-start`, adding the host parameter `gulp review-start --host=0.0.0.0` will bind to all IPs on the machine + - A page will open which provides a hub for common reviewer tools. + - If you need to manually acceess the tools: + - Navigate to build/coverage/lcov-report/index.html to view coverage + - Navigate to integrationExamples/gpt/hellow_world.html for basic integration testing + - The hello_world.html and other exampls can be edited and used as needed to verify functionality + ### General PR review Process - All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. - Checkout the branch (these instructions are available on the github PR page as well). diff --git a/gulpfile.js b/gulpfile.js index 879e34ae588..b7a9e442a8c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -86,7 +86,8 @@ function viewCoverage(done) { connect.server({ port: coveragePort, root: 'build/coverage/lcov-report', - livereload: false + livereload: false, + debug: true }); opens('http://' + mylocalhost + ':' + coveragePort); done(); @@ -94,6 +95,19 @@ function viewCoverage(done) { viewCoverage.displayName = 'view-coverage'; +// View the reviewer tools page +function viewReview(done) { + var mylocalhost = (argv.host) ? argv.host : 'localhost'; + var reviewUrl = 'http://' + mylocalhost + ':' + port + '/integrationExamples/reviewerTools/index.html'; // reuse the main port from 9999 + + // console.log(`stdout: opening` + reviewUrl); + + opens(reviewUrl); + done(); +}; + +viewReview.displayName = 'view-review'; + // Watch Task with Live Reload function watch(done) { var mainWatcher = gulp.watch([ @@ -383,4 +397,8 @@ gulp.task('e2e-test', gulp.series(clean, setupE2e, gulp.parallel('build-bundle-p gulp.task(bundleToStdout); gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step +// build task for reviewers, runs test-coverage, serves, without watching +gulp.task(viewReview); +gulp.task('review-start', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, testCoverage), viewReview)); + module.exports = nodeBundle; diff --git a/integrationExamples/reviewerTools/index.html b/integrationExamples/reviewerTools/index.html new file mode 100755 index 00000000000..2732cb4fd88 --- /dev/null +++ b/integrationExamples/reviewerTools/index.html @@ -0,0 +1,40 @@ + + + + + + + Prebid Reviewer Tools + + + + +
+
+
+

Reviewer Tools

+

Below are links to the most common tool used by Prebid reviewers. For more info on PR review processes check out the General PR Review Process page on Github.

+

Common

+ +

Other Tools

+ +

Documentation & Training Material

+ +
+
+
+ + \ No newline at end of file From 94c9dcc5d64b3ec2ec10636bdc68182b9d5be462 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Mon, 14 Dec 2020 09:20:34 -0500 Subject: [PATCH 0463/1476] appnexusBidAdapter - add support for test flag (#6119) --- modules/appnexusBidAdapter.js | 6 ++++++ test/spec/modules/appnexusBidAdapter_spec.js | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index c102bee4e58..3a9f37f4ca8 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -498,6 +498,12 @@ function formatRequest(payload, bidderRequest) { } } + if (utils.getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { + options.customHeaders = { + 'X-Is-Test': 1 + } + } + if (payload.tags.length > MAX_IMPS_PER_REQUEST) { const clonedPayload = utils.deepClone(payload); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 9b12d892440..a36eab03fe1 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -784,6 +784,18 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); + it('should set the X-Is-Test customHeader if test flag is enabled', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon.stub(config, 'getConfig') + .withArgs('apn_test') + .returns(true); + + const request = spec.buildRequests([bidRequest]); + expect(request.options.customHeaders).to.deep.equal({'X-Is-Test': 1}); + + config.getConfig.restore(); + }); + it('should set withCredentials to false if purpose 1 consent is not given', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { From 2102f4a1c1548bacb775cf6c011c6eba621c81d1 Mon Sep 17 00:00:00 2001 From: fgcloutier Date: Mon, 14 Dec 2020 15:55:29 +0100 Subject: [PATCH 0464/1476] feat(sublimeBidAdapter): updating sublimeBidAdapter module (#6113) - using a simple-request for the POST bid request - renaming our internal ver pixel param to pbav --- modules/sublimeBidAdapter.js | 11 ++--- test/spec/modules/sublimeBidAdapter_spec.js | 47 ++++++++++++++++++--- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index e9f7cf19033..7a573ca4c4b 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.6.0'; +const SUBLIME_VERSION = '0.7.0'; /** * Debug log message @@ -50,7 +50,7 @@ export function sendEvent(eventName) { src: 'pa', puid: state.transactionId || state.notifyId, trId: state.transactionId || state.notifyId, - ver: SUBLIME_VERSION, + pbav: SUBLIME_VERSION, }; log('Sending pixel for event: ' + eventName, eventObject); @@ -128,10 +128,10 @@ function buildRequests(validBidRequests, bidderRequest) { return { method: 'POST', url: protocol + '://' + bidHost + '/bid', - data: payload, + data: JSON.stringify(payload), options: { - contentType: 'application/json', - withCredentials: true + contentType: 'text/plain', + withCredentials: false }, } }); @@ -210,6 +210,7 @@ export const spec = { code: BIDDER_CODE, gvlid: BIDDER_GVLID, aliases: [], + sendEvent: sendEvent, isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index 008f24730bc..a0765a0d396 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, sendEvent, log, setState, state } from 'modules/sublimeBidAdapter.js'; +import { spec } from 'modules/sublimeBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; let utils = require('src/utils'); @@ -9,6 +9,16 @@ describe('Sublime Adapter', function() { describe('sendEvent', function() { let sandbox; + const triggeredPixelProperties = [ + 't', + 'tse', + 'z', + 'e', + 'src', + 'puid', + 'trId', + 'pbav', + ]; beforeEach(function () { sandbox = sinon.sandbox.create(); @@ -16,8 +26,10 @@ describe('Sublime Adapter', function() { it('should trigger pixel', function () { sandbox.spy(utils, 'triggerPixel'); - sendEvent('test', true); + spec.sendEvent('test'); expect(utils.triggerPixel.called).to.equal(true); + const params = utils.parseUrl(utils.triggerPixel.args[0][0]).search; + expect(Object.keys(params)).to.have.members(triggeredPixelProperties); }); afterEach(function () { @@ -94,8 +106,9 @@ describe('Sublime Adapter', function() { }); it('should contains a request id equals to the bid id', function() { - expect(request[0].data.requestId).to.equal(bidRequests[0].bidId); - expect(request[1].data.requestId).to.equal(bidRequests[1].bidId); + for (let i = 0; i < request.length; i = i + 1) { + expect(JSON.parse(request[i].data).requestId).to.equal(bidRequests[i].bidId); + } }); it('should have an url that contains bid keyword', function() { @@ -149,7 +162,7 @@ describe('Sublime Adapter', function() { currency: 'USD', netRevenue: true, ttl: 600, - pbav: '0.6.0', + pbav: '0.7.0', ad: '', }, ]; @@ -191,7 +204,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.6.0', + pbav: '0.7.0', }; expect(result[0]).to.deep.equal(expectedResponse); @@ -241,7 +254,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.6.0', + pbav: '0.7.0', }; expect(result[0]).to.deep.equal(expectedResponse); @@ -279,4 +292,24 @@ describe('Sublime Adapter', function() { }); }); }); + + describe('onBidWon', function() { + let sandbox; + let bid = { foo: 'bar' }; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + it('should trigger "bidwon" pixel', function () { + sandbox.spy(utils, 'triggerPixel'); + spec.onBidWon(bid); + const params = utils.parseUrl(utils.triggerPixel.args[0][0]).search; + expect(params.e).to.equal('bidwon'); + }); + + afterEach(function () { + sandbox.restore(); + }); + }) }); From 0fec20d2914a291abdfa052a59b988f3b300b656 Mon Sep 17 00:00:00 2001 From: OneTagDevOps <38786435+OneTagDevOps@users.noreply.github.com> Date: Mon, 14 Dec 2020 16:36:39 +0100 Subject: [PATCH 0465/1476] oneTag Bid Adapter: bidRequest object adjustments (#6105) * Propagates server data directly without members picking, extracts userIds from the the validBidRequest object * Picks netRevenue value from received bid, defaulting to false Co-authored-by: francesco --- modules/onetagBidAdapter.js | 87 ++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 1a2df023b81..16b8096646f 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -6,6 +6,7 @@ import { Renderer } from '../src/Renderer.js'; import find from 'core-js-pure/features/array/find.js'; import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { createEidsArray } from './userId/eids.js'; const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; @@ -63,8 +64,8 @@ function buildRequests(validBidRequests, bidderRequest) { if (bidderRequest && bidderRequest.uspConsent) { payload.usPrivacy = bidderRequest.uspConsent; } - if (bidderRequest && bidderRequest.userId) { - payload.userId = bidderRequest.userId; + if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userId) { + payload.userId = createEidsArray(validBidRequests[0].userId); } try { if (storage.hasLocalStorage()) { @@ -88,47 +89,34 @@ function interpretResponse(serverResponse, bidderRequest) { if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) { return bids; } - body.bids.forEach(({ - requestId, - cpm, - width, - height, - creativeId, - dealId, - currency, - mediaType, - ttl, - rendererUrl, - ad, - vastUrl, - videoCacheKey - }) => { + body.bids.forEach(bid => { const responseBid = { - requestId, - cpm, - width, - height, - creativeId, - dealId: dealId == null ? dealId : '', - currency, - netRevenue: false, + requestId: bid.requestId, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + creativeId: bid.creativeId, + dealId: bid.dealId == null ? bid.dealId : '', + currency: bid.currency, + netRevenue: bid.netRevenue || false, + mediaType: bid.mediaType, meta: { - mediaType + mediaType: bid.mediaType }, - ttl: ttl || 300 + ttl: bid.ttl || 300 }; - if (mediaType === BANNER) { - responseBid.ad = ad; - } else if (mediaType === VIDEO) { - const {context, adUnitCode} = find(requestData.bids, (item) => item.bidId === requestId); + if (bid.mediaType === BANNER) { + responseBid.ad = bid.ad; + } else if (bid.mediaType === VIDEO) { + const {context, adUnitCode} = find(requestData.bids, (item) => item.bidId === bid.requestId); if (context === INSTREAM) { - responseBid.vastUrl = vastUrl; - responseBid.videoCacheKey = videoCacheKey; + responseBid.vastUrl = bid.vastUrl; + responseBid.videoCacheKey = bid.videoCacheKey; } else if (context === OUTSTREAM) { - responseBid.vastXml = ad; - responseBid.vastUrl = vastUrl; - if (rendererUrl) { - responseBid.renderer = createRenderer({requestId, rendererUrl, adUnitCode}); + responseBid.vastXml = bid.ad; + responseBid.vastUrl = bid.vastUrl; + if (bid.rendererUrl) { + responseBid.renderer = createRenderer({ ...bid, adUnitCode }); } } } @@ -146,25 +134,24 @@ function createRenderer(bid, rendererOptions = {}) { loaded: false }); try { - renderer.setRender(onetagRenderer); + renderer.setRender(({renderer, width, height, vastXml, adUnitCode}) => { + renderer.push(() => { + window.onetag.Player.init({ + ...bid, + width, + height, + vastXml, + nodeId: adUnitCode, + config: renderer.getConfig() + }); + }); + }); } catch (e) { } return renderer; } -function onetagRenderer({renderer, width, height, vastXml, adUnitCode}) { - renderer.push(() => { - window.onetag.Player.init({ - width, - height, - vastXml, - nodeId: adUnitCode, - config: renderer.getConfig() - }); - }); -} - function getFrameNesting() { let topmostFrame = window; let parent = window.parent; From 30711be00243e653a25785b2d7b46b9df8f5e120 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 14 Dec 2020 21:41:42 +0100 Subject: [PATCH 0466/1476] a/b testing framework baked in to the ID5 user id module (#6076) --- modules/id5IdSystem.js | 47 ++++++-- modules/id5IdSystem.md | 21 +++- modules/userId/eids.md | 3 +- test/spec/modules/id5IdSystem_spec.js | 157 +++++++++++++++++++++++++- 4 files changed, 209 insertions(+), 19 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 7033a71d015..17a808badaa 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -42,27 +42,58 @@ export const id5IdSubmodule = { * decode the stored id value for passing to bid requests * @function decode * @param {(Object|string)} value + * @param {SubmoduleConfig|undefined} config * @returns {(Object|undefined)} */ - decode(value) { - let uid; + decode(value, config) { + let universalUid; let linkType = 0; if (value && typeof value.universal_uid === 'string') { - uid = value.universal_uid; + universalUid = value.universal_uid; linkType = value.link_type || linkType; } else { return undefined; } - return { - 'id5id': { - 'uid': uid, - 'ext': { - 'linkType': linkType + // check for A/B testing configuration and hide ID if in Control Group + let abConfig = (config && config.params && config.params.abTesting) || { enabled: false }; + let controlGroup = false; + if ( + abConfig.enabled === true && + (!utils.isNumber(abConfig.controlGroupPct) || + abConfig.controlGroupPct < 0 || + abConfig.controlGroupPct > 1) + ) { + // A/B Testing is enabled, but configured improperly, so skip A/B testing + utils.logError('User ID - ID5 submodule: A/B Testing controlGroupPct must be a number >= 0 and <= 1! Skipping A/B Testing'); + } else if ( + abConfig.enabled === true && + Math.random() < abConfig.controlGroupPct + ) { + // A/B Testing is enabled and user is in the Control Group, so do not share the ID5 ID + utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - request is in the Control Group, so the ID5 ID is NOT exposed'); + universalUid = linkType = 0; + controlGroup = true; + } else if (abConfig.enabled === true) { + // A/B Testing is enabled but user is not in the Control Group, so ID5 ID is shared + utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - request is NOT in the Control Group, so the ID5 ID is exposed'); + } + + let responseObj = { + id5id: { + uid: universalUid, + ext: { + linkType: linkType } } }; + + if (abConfig.enabled === true) { + utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', controlGroup); + } + + return responseObj; }, /** diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index e5e3969c19c..6b2192834fa 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -22,14 +22,18 @@ The following configuration parameters are available: pbjs.setConfig({ userSync: { userIds: [{ - name: "id5Id", + name: 'id5Id', params: { partner: 173, // change to the Partner Number you received from ID5 - pd: "MT1iNTBjY..." // optional, see table below for a link to how to generate this + pd: 'MT1iNTBjY...', // optional, see table below for a link to how to generate this + abTesting: { // optional + enabled: true, // false by default + controlGroupPct: 0.1 // valid values are 0.0 - 1.0 (inclusive) + } }, storage: { - type: "html5", // "html5" is the required storage type - name: "id5id", // "id5id" is the required storage name + type: 'html5', // "html5" is the required storage type + name: 'id5id', // "id5id" is the required storage name expires: 90, // storage lasts for 90 days refreshInSeconds: 8*3600 // refresh ID every 8 hours to ensure it's fresh } @@ -46,6 +50,9 @@ pbjs.setConfig({ | params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | | params.pd | Optional | String | Publisher-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/x/BIAZ) 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"` | @@ -53,3 +60,9 @@ pbjs.setConfig({ | 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). + +### A Note on A/B Testing + +Publishers may want to test the value of the ID5 ID with their downstream partners. While there are various ways to do this, A/B testing is a standard approach. Instead of publishers manually enabling or disabling the ID5 User ID Module based on their control group settings (which leads to fewer calls to ID5, reducing our ability to recognize the user), we have baked this in to our module directly. + +To turn on A/B Testing, simply edit the configuration (see above table) to enable it and set what percentage of requests you would like to set for the control group. The control group is the set of requests where an ID5 ID will not be exposed in to bid adapters or in the various user id functions available on the `pbjs` global. An additional value of `ext.abTestingControlGroup` will be set to `true` or `false` that can be used to inform reporting systems that the request was in the control group or not. It's important to note that the control group is request based, and not user based. In other words, from one page view to another, a user may be in or out of the control group. diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 0cf9b6d2d22..fecf7e888bf 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -36,7 +36,8 @@ userIdAsEids = [ atype: 1 }, ext: { - linkType: 2 + linkType: 2, + abTestingControlGroup: false }] }, diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 845cf7fa010..afde1696766 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -26,16 +26,19 @@ describe('ID5 ID System', function() { const ID5_NB_STORAGE_NAME = nbCacheName(ID5_TEST_PARTNER_ID); const ID5_STORED_ID = 'storedid5id'; const ID5_STORED_SIGNATURE = '123456'; + const ID5_STORED_LINK_TYPE = 1; const ID5_STORED_OBJ = { 'universal_uid': ID5_STORED_ID, - 'signature': ID5_STORED_SIGNATURE + 'signature': ID5_STORED_SIGNATURE, + 'link_type': ID5_STORED_LINK_TYPE }; const ID5_RESPONSE_ID = 'newid5id'; const ID5_RESPONSE_SIGNATURE = 'abcdef'; + const ID5_RESPONSE_LINK_TYPE = 2; const ID5_JSON_RESPONSE = { 'universal_uid': ID5_RESPONSE_ID, 'signature': ID5_RESPONSE_SIGNATURE, - 'link_type': 0 + 'link_type': ID5_RESPONSE_LINK_TYPE }; function getId5FetchConfig(storageName = ID5_STORAGE_NAME, storageType = 'html5') { @@ -268,7 +271,7 @@ describe('ID5 ID System', function() { source: ID5_SOURCE, uids: [{ id: ID5_STORED_ID, atype: 1 }], ext: { - linkType: 0 + linkType: ID5_STORED_LINK_TYPE } }); }); @@ -360,13 +363,155 @@ describe('ID5 ID System', function() { }); describe('Decode stored object', function() { - const expectedDecodedObject = { id5id: { uid: ID5_STORED_ID, ext: { linkType: 0 } } }; + 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)).to.deep.equal(expectedDecodedObject); + expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).to.deep.equal(expectedDecodedObject); }); it('should return undefined if passed a string', function() { - expect(id5IdSubmodule.decode('somestring')).to.eq(undefined); + expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).to.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: 0, ext: { linkType: 0, abTestingControlGroup: true } } }; + let testConfig; + + beforeEach(function() { + testConfig = getId5FetchConfig(); + }); + + describe('Configuration Validation', function() { + let logErrorSpy; + let logInfoSpy; + + beforeEach(function() { + logErrorSpy = sinon.spy(utils, 'logError'); + logInfoSpy = sinon.spy(utils, 'logInfo'); + }); + afterEach(function() { + logErrorSpy.restore(); + logInfoSpy.restore(); + }); + + // A/B Testing ON, but invalid config + let testInvalidAbTestingConfigsWithError = [ + { enabled: true }, + { enabled: true, controlGroupPct: 2 }, + { enabled: true, controlGroupPct: -1 }, + { enabled: true, controlGroupPct: 'a' }, + { enabled: true, controlGroupPct: true } + ]; + testInvalidAbTestingConfigsWithError.forEach((testAbTestingConfig) => { + it('should error if config is invalid, and always return an ID', function () { + testConfig.params.abTesting = testAbTestingConfig; + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + sinon.assert.calledOnce(logErrorSpy); + }); + }); + + // A/B Testing OFF, with invalid config (ignore) + let testInvalidAbTestingConfigsWithoutError = [ + { enabled: false, controlGroupPct: -1 }, + { enabled: false, controlGroupPct: 2 }, + { enabled: false, controlGroupPct: 'a' }, + { enabled: false, controlGroupPct: true } + ]; + testInvalidAbTestingConfigsWithoutError.forEach((testAbTestingConfig) => { + it('should not error if config is invalid but A/B testing is off, and always return an ID', function () { + testConfig.params.abTesting = testAbTestingConfig; + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + sinon.assert.notCalled(logErrorSpy); + sinon.assert.notCalled(logInfoSpy); + }); + }); + + // A/B Testing ON, with valid config + let testValidConfigs = [ + { enabled: true, controlGroupPct: 0 }, + { enabled: true, controlGroupPct: 0.5 }, + { enabled: true, controlGroupPct: 1 } + ]; + testValidConfigs.forEach((testAbTestingConfig) => { + it('should not error if config is valid', function () { + testConfig.params.abTesting = testAbTestingConfig; + id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + sinon.assert.notCalled(logErrorSpy); + sinon.assert.calledOnce(logInfoSpy); + }); + }); + }); + + describe('A/B Testing Config is not Set', function() { + let randStub; + + beforeEach(function() { + randStub = sinon.stub(Math, 'random').callsFake(function() { + return 0; + }); + }); + afterEach(function () { + randStub.restore(); + }); + + it('should expose ID when A/B config is not set', function () { + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + }); + + it('should expose ID when A/B config is empty', function () { + testConfig.params.abTesting = { }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + }); + }); + + describe('A/B Testing Config is Set', function() { + let randStub; + + beforeEach(function() { + randStub = sinon.stub(Math, 'random').callsFake(function() { + return 0.25; + }); + }); + afterEach(function () { + randStub.restore(); + }); + + it('should expose ID when A/B testing is off', function () { + testConfig.params.abTesting = { + enabled: false, + controlGroupPct: 0.5 + }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + }); + + it('should expose ID when not in control group', function () { + testConfig.params.abTesting = { + enabled: true, + controlGroupPct: 0.1 + }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + }); + + it('should not expose ID when in control group', function () { + testConfig.params.abTesting = { + enabled: true, + controlGroupPct: 0.5 + }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); + }); }); }); }); From f6f27dd06a8fcbc06cf47dfacf61347a7d892da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Mon, 14 Dec 2020 22:47:11 +0100 Subject: [PATCH 0467/1476] Add Zemanta adapter (#6039) --- modules/zemantaBidAdapter.js | 256 ++++++++++ modules/zemantaBidAdapter.md | 107 +++++ test/spec/modules/zemantaBidAdapter_spec.js | 487 ++++++++++++++++++++ 3 files changed, 850 insertions(+) create mode 100644 modules/zemantaBidAdapter.js create mode 100644 modules/zemantaBidAdapter.md create mode 100644 test/spec/modules/zemantaBidAdapter_spec.js diff --git a/modules/zemantaBidAdapter.js b/modules/zemantaBidAdapter.js new file mode 100644 index 00000000000..aa7a24985e0 --- /dev/null +++ b/modules/zemantaBidAdapter.js @@ -0,0 +1,256 @@ +// jshint esversion: 6, es3: false, node: true +'use strict'; + +import { + registerBidder +} from '../src/adapters/bidderFactory.js'; +import { NATIVE, BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'zemanta'; +const CURRENCY = 'USD'; +const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_PARAMS = { + title: { id: 0, name: 'title' }, + icon: { id: 2, type: 1, name: 'img' }, + image: { id: 3, type: 3, name: 'img' }, + sponsoredBy: { id: 5, name: 'data', type: 1 }, + body: { id: 4, name: 'data', type: 2 }, + cta: { id: 1, type: 12, name: 'data' } +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ NATIVE, BANNER ], + isBidRequestValid: (bid) => { + return ( + !!config.getConfig('zemanta.bidderUrl') && + !!utils.deepAccess(bid, 'params.publisher.id') && + !!(bid.nativeParams || bid.sizes) + ); + }, + buildRequests: (validBidRequests, bidderRequest) => { + const page = bidderRequest.refererInfo.referer; + const ua = navigator.userAgent; + const test = setOnAny(validBidRequests, 'params.test'); + const publisher = setOnAny(validBidRequests, 'params.publisher'); + const cur = CURRENCY; + const endpointUrl = config.getConfig('zemanta.bidderUrl'); + const timeout = bidderRequest.timeout; + + const imps = validBidRequests.map((bid, id) => { + bid.netRevenue = 'net'; + const imp = { + id: id + 1 + '' + } + + if (bid.params.tagid) { + imp.tagid = bid.params.tagid + } + + if (bid.nativeParams) { + imp.native = { + request: JSON.stringify({ + assets: getNativeAssets(bid) + }) + } + } else { + imp.banner = { + format: transformSizes(bid.sizes) + } + } + + return imp; + }); + + const request = { + id: bidderRequest.auctionId, + site: { page, publisher }, + device: { ua }, + source: { fd: 1 }, + cur: [cur], + tmax: timeout, + imp: imps + }; + + if (test) { + request.is_debug = !!test; + request.test = 1; + } + + if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) { + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString) + utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1) + } + if (bidderRequest.uspConsent) { + utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent) + } + if (config.getConfig('coppa') === true) { + utils.deepSetValue(request, 'regs.coppa', config.getConfig('coppa') & 1) + } + + return { + method: 'POST', + url: endpointUrl, + data: JSON.stringify(request), + bids: validBidRequests + }; + }, + interpretResponse: (serverResponse, { bids }) => { + if (!serverResponse.body) { + return []; + } + const { seatbid, cur } = serverResponse.body; + + const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { + result[bid.impid - 1] = bid; + return result; + }, []); + + return bids.map((bid, id) => { + const bidResponse = bidResponses[id]; + if (bidResponse) { + const type = bid.nativeParams ? NATIVE : BANNER; + const bidObject = { + requestId: bid.bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + ttl: 360, + netRevenue: bid.netRevenue === 'net', + currency: cur, + mediaType: type, + nurl: bidResponse.nurl, + }; + if (type === NATIVE) { + bidObject.native = parseNative(bidResponse); + } else { + bidObject.ad = bidResponse.adm; + bidObject.width = bidResponse.w; + bidObject.height = bidResponse.h; + } + return bidObject; + } + }).filter(Boolean); + }, + getUserSyncs: (syncOptions) => { + const syncs = []; + const syncUrl = config.getConfig('zemanta.usersyncUrl'); + if (syncOptions.pixelEnabled && syncUrl) { + syncs.push({ + type: 'image', + url: syncUrl + }); + } + return syncs; + }, + onBidWon: (bid) => { + ajax(utils.replaceAuctionPrice(bid.nurl, bid.originalCpm)) + } +}; + +registerBidder(spec); + +function parseNative(bid) { + const { assets, link, eventtrackers } = JSON.parse(bid.adm); + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined + }; + assets.forEach(asset => { + const kind = NATIVE_ASSET_IDS[asset.id]; + const content = kind && asset[NATIVE_PARAMS[kind].name]; + if (content) { + result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; + } + }); + if (eventtrackers) { + result.impressionTrackers = []; + eventtrackers.forEach(tracker => { + if (tracker.event !== 1) return; + switch (tracker.method) { + case 1: // img + result.impressionTrackers.push(tracker.url); + break; + case 2: // js + result.javascriptTrackers = ``; + break; + } + }); + } + return result; +} + +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = utils.deepAccess(collection[i], key); + if (result) { + return result; + } + } +} + +function flatten(arr) { + return [].concat(...arr); +} + +function getNativeAssets(bid) { + return utils._map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + w = sizes[0]; + h = sizes[1]; + } + + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); +} + +/* Turn bid request sizes into ut-compatible format */ +function transformSizes(requestSizes) { + if (!utils.isArray(requestSizes)) { + return []; + } + + if (requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { + return [{ + w: parseInt(requestSizes[0], 10), + h: parseInt(requestSizes[1], 10) + }]; + } else if (utils.isArray(requestSizes[0])) { + return requestSizes.map(item => + ({ + w: parseInt(item[0], 10), + h: parseInt(item[1], 10) + }) + ); + } + + return []; +} diff --git a/modules/zemantaBidAdapter.md b/modules/zemantaBidAdapter.md new file mode 100644 index 00000000000..d991b67d429 --- /dev/null +++ b/modules/zemantaBidAdapter.md @@ -0,0 +1,107 @@ +# Overview + +``` +Module Name: Zemanta Adapter +Module Type: Bidder Adapter +Maintainer: prog-ops-team@outbrain.com +``` + +# Description + +Module that connects to zemanta bidder to fetch bids. +Both native and display formats are supported but not at the same time. Using OpenRTB standard. + +# Configuration + +## Bidder and usersync URLs + +The Zemanta adapter does not work without setting the correct bidder and usersync URLs. +You will receive the URLs when contacting us. + +``` +pbjs.setConfig({ + zemanta: { + bidderUrl: 'https://bidder-url.com', + usersyncUrl: 'https://usersync-url.com' + } +}); +``` + + +# Test Native Parameters +``` + var adUnits = [ + code: '/19968336/prebid_native_example_1', + mediaTypes: { + native: { + image: { + required: false, + sizes: [100, 50] + }, + title: { + required: false, + len: 140 + }, + sponsoredBy: { + required: false + }, + clickUrl: { + required: false + }, + body: { + required: false + }, + icon: { + required: false, + sizes: [50, 50] + } + } + }, + bids: [{ + bidder: 'zemanta', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id' + } + }] + ]; + + pbjs.setConfig({ + zemanta: { + bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' + } + }); +``` + +# Test Display Parameters +``` + var adUnits = [ + code: '/19968336/prebid_display_example_1', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'zemanta', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id' + }, + }] + ]; + + pbjs.setConfig({ + zemanta: { + bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' + } + }); +``` diff --git a/test/spec/modules/zemantaBidAdapter_spec.js b/test/spec/modules/zemantaBidAdapter_spec.js new file mode 100644 index 00000000000..ab685890620 --- /dev/null +++ b/test/spec/modules/zemantaBidAdapter_spec.js @@ -0,0 +1,487 @@ +import {expect} from 'chai'; +import {spec} from 'modules/zemantaBidAdapter.js'; +import {config} from 'src/config.js'; +import {server} from 'test/mocks/xhr'; + +describe('Zemanta Adapter', function () { + describe('Bid request and response', function () { + const commonBidRequest = { + bidder: 'zemanta', + params: { + publisher: { + id: 'publisher-id' + }, + }, + bidId: '2d6815a92ba1ba', + auctionId: '12043683-3254-4f74-8934-f941b085579e', + } + const nativeBidRequestParams = { + nativeParams: { + image: { + required: true, + sizes: [ + 120, + 100 + ], + sendId: true + }, + title: { + required: true, + sendId: true + }, + sponsoredBy: { + required: false + } + }, + } + + const displayBidRequestParams = { + sizes: [ + [ + 300, + 250 + ] + ] + } + + describe('isBidRequestValid', function () { + before(() => { + config.setConfig({ + zemanta: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should fail when bid is invalid', function () { + const bid = { + bidder: 'zemanta', + params: { + publisher: { + id: 'publisher-id', + } + }, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should succeed when bid contains native params', function () { + const bid = { + bidder: 'zemanta', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + it('should succeed when bid contains sizes', function () { + const bid = { + bidder: 'zemanta', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...displayBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + it('should fail if publisher id is not set', function () { + const bid = { + bidder: 'zemanta', + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should fail if bidder url is not set', function () { + const bid = { + bidder: 'zemanta', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + config.resetConfig() + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + before(() => { + config.setConfig({ + zemanta: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + const commonBidderRequest = { + refererInfo: { + referer: 'https://example.com/' + } + } + + it('should build native request', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const expectedNativeAssets = { + assets: [ + { + required: 1, + id: 3, + img: { + type: 3, + w: 120, + h: 100 + } + }, + { + required: 1, + id: 0, + title: {} + }, + { + required: 0, + id: 5, + data: { + type: 1 + } + } + ] + } + const expectedData = { + site: { + page: 'https://example.com/', + publisher: { + id: 'publisher-id' + } + }, + device: { + ua: navigator.userAgent + }, + source: { + fd: 1 + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + native: { + request: JSON.stringify(expectedNativeAssets) + } + } + ] + } + const res = spec.buildRequests([bidRequest], commonBidderRequest) + expect(res.url).to.equal('https://bidder-url.com') + expect(res.data).to.deep.equal(JSON.stringify(expectedData)) + }); + + it('should build display request', function () { + const bidRequest = { + ...commonBidRequest, + ...displayBidRequestParams, + } + const expectedData = { + site: { + page: 'https://example.com/', + publisher: { + id: 'publisher-id' + } + }, + device: { + ua: navigator.userAgent + }, + source: { + fd: 1 + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + } + } + ] + } + const res = spec.buildRequests([bidRequest], commonBidderRequest) + expect(res.url).to.equal('https://bidder-url.com') + expect(res.data).to.deep.equal(JSON.stringify(expectedData)) + }) + + it('should pass optional tagid in request', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + bidRequest.params.tagid = 'test-tag' + + const res = spec.buildRequests([bidRequest], commonBidderRequest) + const resData = JSON.parse(res.data) + expect(resData.imp[0].tagid).to.equal('test-tag') + }); + + it('should pass bidder timeout', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + timeout: 500 + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.tmax).to.equal(500) + }); + + it('should pass GDPR consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + gdprConsent: { + gdprApplies: true, + consentString: 'consentString', + } + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.user.ext.consent).to.equal('consentString') + expect(resData.regs.ext.gdpr).to.equal(1) + }); + + it('should pass us privacy consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + uspConsent: 'consentString' + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.regs.ext.us_privacy).to.equal('consentString') + }); + + it('should pass coppa consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + config.setConfig({coppa: true}) + + const res = spec.buildRequests([bidRequest], commonBidderRequest) + const resData = JSON.parse(res.data) + expect(resData.regs.coppa).to.equal(1) + + config.resetConfig() + }); + }) + + describe('interpretResponse', function () { + it('should return empty array if no valid bids', function () { + const res = spec.interpretResponse({}, []) + expect(res).to.be.an('array').that.is.empty + }); + + it('should interpret native response', function () { + const serverResponse = { + body: { + id: '0a73e68c-9967-4391-b01b-dda2d9fc54e4', + seatbid: [ + { + bid: [ + { + id: '82822cf5-259c-11eb-8a52-f29e5275aa57', + impid: '1', + price: 1.1, + nurl: 'http://example.com/win/${AUCTION_PRICE}', + adm: '{"ver":"1.2","assets":[{"id":3,"required":1,"img":{"url":"http://example.com/img/url","w":120,"h":100}},{"id":0,"required":1,"title":{"text":"Test title"}},{"id":5,"data":{"value":"Test sponsor"}}],"link":{"url":"http://example.com/click/url"},"eventtrackers":[{"event":1,"method":1,"url":"http://example.com/impression"}]}', + adomain: [ + 'example.co' + ], + cid: '3487171', + crid: '28023739', + cat: [ + 'IAB10-2' + ] + } + ], + seat: 'acc-5537' + } + ], + bidid: '82822cf5-259c-11eb-8a52-b48e7518c657', + cur: 'USD' + }, + } + const request = { + bids: [ + { + ...commonBidRequest, + ...nativeBidRequestParams, + } + ] + } + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: 1.1, + creativeId: '28023739', + ttl: 360, + netRevenue: false, + currency: 'USD', + mediaType: 'native', + nurl: 'http://example.com/win/${AUCTION_PRICE}', + native: { + clickTrackers: undefined, + clickUrl: 'http://example.com/click/url', + image: { + url: 'http://example.com/img/url', + width: 120, + height: 100 + }, + title: 'Test title', + sponsoredBy: 'Test sponsor', + impressionTrackers: [ + 'http://example.com/impression', + ] + } + } + ] + + const res = spec.interpretResponse(serverResponse, request) + expect(res).to.deep.equal(expectedRes) + }); + + it('should interpret display response', function () { + const serverResponse = { + body: { + id: '6b2eedc8-8ff5-46ef-adcf-e701b508943e', + seatbid: [ + { + bid: [ + { + id: 'd90fe7fa-28d7-11eb-8ce4-462a842a7cf9', + impid: '1', + price: 1.1, + nurl: 'http://example.com/win/${AUCTION_PRICE}', + adm: '
ad
', + adomain: [ + 'example.com' + ], + cid: '3865084', + crid: '29998660', + cat: [ + 'IAB10-2' + ], + w: 300, + h: 250 + } + ], + seat: 'acc-6536' + } + ], + bidid: 'd90fe7fa-28d7-11eb-8ce4-13d94bfa26f9', + cur: 'USD' + } + } + const request = { + bids: [ + { + ...commonBidRequest, + ...displayBidRequestParams + } + ] + } + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: 1.1, + creativeId: '29998660', + ttl: 360, + netRevenue: false, + currency: 'USD', + mediaType: 'banner', + nurl: 'http://example.com/win/${AUCTION_PRICE}', + ad: '
ad
', + width: 300, + height: 250 + } + ] + + const res = spec.interpretResponse(serverResponse, request) + expect(res).to.deep.equal(expectedRes) + }); + }) + }) + + describe('getUserSyncs', function () { + before(() => { + config.setConfig({ + zemanta: { + usersyncUrl: 'https://usersync-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should return user sync if pixel enabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) + }) + + it('should not return user sync if pixel disabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: false}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not return user sync if url is not set', function () { + config.resetConfig() + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.be.an('array').that.is.empty + }) + }) + + describe('onBidWon', function () { + it('should make an ajax call with the original cpm', function () { + const bid = { + nurl: 'http://example.com/win/${AUCTION_PRICE}', + cpm: 2.1, + originalCpm: 1.1, + } + spec.onBidWon(bid) + expect(server.requests[0].url).to.equals('http://example.com/win/1.1') + }); + }) +}) From 6b4494ccae3e928f8aa1bbd1cdbb17c49c079424 Mon Sep 17 00:00:00 2001 From: cpuBird <54024689+cpuBird@users.noreply.github.com> Date: Tue, 15 Dec 2020 14:57:25 +0530 Subject: [PATCH 0468/1476] vdoai Bid Adapter: added multisize array in bid requests (#6101) * added multisize array in vdoai bid requests * fixing a bug dimentions --- modules/vdoaiBidAdapter.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index 8cfcd67bd00..d4fda36b1fc 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -30,14 +30,12 @@ export const spec = { if (validBidRequests.length === 0) { return []; } + return validBidRequests.map(bidRequest => { - const sizes = utils.parseSizesInput(bidRequest.params.size || bidRequest.sizes)[0]; - const width = sizes.split('x')[0]; - const height = sizes.split('x')[1]; + const sizes = utils.getAdUnitSizes(bidRequest); const payload = { placementId: bidRequest.params.placementId, - width: width, - height: height, + sizes: sizes, bidId: bidRequest.bidId, referer: bidderRequest.refererInfo.referer, id: bidRequest.auctionId, @@ -64,9 +62,9 @@ export const spec = { const response = serverResponse.body; const creativeId = response.adid || 0; // const width = response.w || 0; - const width = bidRequest.data.width; + const width = response.width; // const height = response.h || 0; - const height = bidRequest.data.height; + const height = response.height; const cpm = response.price || 0; response.rWidth = width; From 53e629145010fbc97925e5dd2207324e3f2891d7 Mon Sep 17 00:00:00 2001 From: Vadim Gush Date: Wed, 16 Dec 2020 06:49:25 +0400 Subject: [PATCH 0469/1476] Sovrn Bid Adapter: Change TTL field (#6083) * Change TTL field for SovrnBidAdapter * Fix unit tests * Fix codestyle in unit tests * Fix tests * Fix tests * Removed ext field from some tests * Trying to make ext field optional * Codestyle changes --- modules/sovrnBidAdapter.js | 2 +- test/spec/modules/sovrnBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 8f8158fd0c9..176b090fbe5 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -153,7 +153,7 @@ export const spec = { netRevenue: true, mediaType: BANNER, ad: decodeURIComponent(`${sovrnBid.adm}`), - ttl: sovrnBid.ttl || 90 + ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90 }); }); } diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 769be73a272..2bb5cdbdf3c 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -387,7 +387,7 @@ describe('sovrnBidAdapter', function() { }); it('should get correct bid response when ttl is set', function () { - response.body.seatbid[0].bid[0].ttl = 480; + response.body.seatbid[0].bid[0].ext = { 'ttl': 480 }; let expectedResponse = [{ 'requestId': '263c448586f5a1', From a9f1795f6de579d84f03a542458b6c4228a7257f Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Wed, 16 Dec 2020 16:32:39 +0700 Subject: [PATCH 0470/1476] Change bidder url for Qwarry adapter (#6128) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 7c2ec0f085b..7cb83520979 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -4,7 +4,7 @@ import { ajax } from '../src/ajax.js'; import { VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'qwarry'; -export const ENDPOINT = 'https://ui-bidder.kantics.co/bid/adtag?prebid=true' +export const ENDPOINT = 'https://bidder.qwarry.co/bid/adtag?prebid=true' export const spec = { code: BIDDER_CODE, From 1410a738cf7802227e3dc353b62440e9545f9947 Mon Sep 17 00:00:00 2001 From: Stephen Johnston Date: Wed, 16 Dec 2020 05:33:36 -0500 Subject: [PATCH 0471/1476] Add Automatic Release Drafter Functionality to Prebid Repository (#5954) * Create release-drafter.yml * Create release-drafter.yml * Update release-drafter.yml * Update release-drafter.yml --- .github/release-drafter.yml | 28 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 18 +++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000000..8984252f4c3 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,28 @@ + +name-template: 'Prebid $RESOLVED_VERSION Release' +tag-template: '$RESOLVED_VERSION' +categories: + - title: '🚀 New Features' + label: 'feature' + - title: '🛠 Maintenance' + label: 'maintenance' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' +change-template: '- $TITLE (#$NUMBER)' +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## In This Release + $CHANGES diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000000..8152b61275d --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,18 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 54df547a22551cba288348768ed2d61cd2181667 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 16 Dec 2020 12:43:41 -0500 Subject: [PATCH 0472/1476] Price Floors update to include modelWeight in the bid request to give additional context for Analytics adapters that the Floors Module is schema mode 2, and the floors module picks a model prior to an auction based on the modelWeight supplied in the floors module definition (#6126) Rubicon Analytics Update to pass modelWeight if exists --- modules/priceFloors.js | 1 + modules/rubiconAnalyticsAdapter.js | 1 + test/spec/modules/priceFloors_spec.js | 25 +++++++++++++++++++ .../modules/rubiconAnalyticsAdapter_spec.js | 3 +++ 4 files changed, 30 insertions(+) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index c0797f710de..7c8834c2ae2 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -291,6 +291,7 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { skipRate: floorData.skipRate, floorMin: floorData.floorMin, modelVersion: utils.deepAccess(floorData, 'data.modelVersion'), + modelWeight: utils.deepAccess(floorData, 'data.modelWeight'), modelTimestamp: utils.deepAccess(floorData, 'data.modelTimestamp'), location: utils.deepAccess(floorData, 'data.location', 'noData'), floorProvider: floorData.floorProvider, diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index ad78c601ab6..38d83a40bc7 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -237,6 +237,7 @@ function sendMessage(auctionId, bidWonId) { auction.floors = utils.pick(auctionCache.floorData, [ 'location', 'modelVersion as modelName', + 'modelWeight', 'modelTimestamp', 'skipped', 'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'), diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 1b3ce021068..984d6da1cb9 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -23,6 +23,7 @@ describe('the price floors module', function () { let clock; const basicFloorData = { modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, currency: 'USD', schema: { @@ -38,6 +39,7 @@ describe('the price floors module', function () { const basicFloorDataHigh = { floorMin: 7.0, modelVersion: 'basic model', + modelWeight: 10, currency: 'USD', schema: { delimiter: '|', @@ -52,6 +54,7 @@ describe('the price floors module', function () { const basicFloorDataLow = { floorMin: 2.3, modelVersion: 'basic model', + modelWeight: 10, currency: 'USD', schema: { delimiter: '|', @@ -185,6 +188,7 @@ describe('the price floors module', function () { let resultingData = getFloorsDataForAuction(inputFloorData, 'test_div_1'); expect(resultingData).to.deep.equal({ modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, currency: 'USD', schema: { @@ -203,6 +207,7 @@ describe('the price floors module', function () { resultingData = getFloorsDataForAuction(inputFloorData, 'this_is_a_div'); expect(resultingData).to.deep.equal({ modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, currency: 'USD', schema: { @@ -432,6 +437,7 @@ describe('the price floors module', function () { skipped: true, floorMin: undefined, modelVersion: undefined, + modelWeight: undefined, modelTimestamp: undefined, location: 'noData', skipRate: 0, @@ -467,6 +473,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'adUnit Model Version', + modelWeight: 10, modelTimestamp: 1606772895, location: 'adUnit', skipRate: 0, @@ -501,6 +508,7 @@ describe('the price floors module', function () { validateBidRequests(true, { skipped: false, modelVersion: 'adUnit Model Version', + modelWeight: 10, modelTimestamp: 1606772895, location: 'adUnit', skipRate: 0, @@ -516,6 +524,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -538,6 +547,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -553,6 +563,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -568,6 +579,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -592,6 +604,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 50, @@ -607,6 +620,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 10, @@ -622,6 +636,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -687,6 +702,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-1', + modelWeight: 10, modelTimestamp: undefined, location: 'setConfig', skipRate: 0, @@ -701,6 +717,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-2', + modelWeight: 40, modelTimestamp: undefined, location: 'setConfig', skipRate: 0, @@ -715,6 +732,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'model-3', + modelWeight: 50, modelTimestamp: undefined, location: 'setConfig', skipRate: 0, @@ -745,6 +763,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -825,6 +844,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -864,6 +884,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelWeight: 10, modelTimestamp: 1606772895, location: 'fetch', skipRate: 0, @@ -902,6 +923,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelWeight: 10, modelTimestamp: 1606772895, location: 'fetch', skipRate: 0, @@ -943,6 +965,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'fetch model name', + modelWeight: 10, modelTimestamp: 1606772895, location: 'fetch', skipRate: 95, @@ -966,6 +989,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, @@ -991,6 +1015,7 @@ describe('the price floors module', function () { skipped: false, floorMin: undefined, modelVersion: 'basic model', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 0, diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 0d6cf331e52..1dd524e1838 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -852,6 +852,7 @@ describe('rubicon analytics adapter', function () { auctionInit.bidderRequests[0].bids[0].floorData = { skipped: false, modelVersion: 'someModelName', + modelWeight: 10, modelTimestamp: 1606772895, location: 'setConfig', skipRate: 15, @@ -954,6 +955,7 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].floors).to.deep.equal({ location: 'setConfig', modelName: 'someModelName', + modelWeight: 10, modelTimestamp: 1606772895, skipped: false, enforcement: true, @@ -1000,6 +1002,7 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].floors).to.deep.equal({ location: 'setConfig', modelName: 'someModelName', + modelWeight: 10, modelTimestamp: 1606772895, skipped: false, enforcement: true, From eebdee25e946792e21cf64eff5f97f32d4e299a0 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 16 Dec 2020 19:39:22 +0100 Subject: [PATCH 0473/1476] Prebid 4.20.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04ec495c93d..30ba2494182 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.20.0-pre", + "version": "4.20.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 282866ae79e92873d02620beac56d15b03dc7635 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 16 Dec 2020 20:12:28 +0100 Subject: [PATCH 0474/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30ba2494182..f65b996a393 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.20.0", + "version": "4.21.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 47e96ad7ae62040ede8c9b84a192e0b718d9947a Mon Sep 17 00:00:00 2001 From: susyt Date: Wed, 16 Dec 2020 12:00:48 -0800 Subject: [PATCH 0475/1476] GumGum: makes slot and invideo products avail with pubId (#6107) --- modules/gumgumBidAdapter.js | 10 ++-- modules/gumgumBidAdapter.md | 69 +++++++++++++++++++++- test/spec/modules/gumgumBidAdapter_spec.js | 58 ++++++++---------- 3 files changed, 97 insertions(+), 40 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 2cb5ce61064..c9bf77494cf 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -273,8 +273,9 @@ function buildRequests (validBidRequests, bidderRequest) { data.iriscat = params.iriscat; } - if (params.zone) { - data.t = params.zone; + if (params.zone || params.pubId) { + params.zone ? (data.t = params.zone) : (data.pubId = params.pubId); + data.pi = 2; // inscreen // override pi if the following is found if (params.slot) { @@ -285,11 +286,8 @@ function buildRequests (validBidRequests, bidderRequest) { data.ni = parseInt(params.native, 10); data.pi = 5; } else if (mediaTypes.video) { - data.pi = mediaTypes.video.linearity === 1 ? 7 : 6; // video : invideo + data.pi = mediaTypes.video.linearity === 2 ? 6 : 7; // invideo : video } - } else if (params.pubId) { - data.pubId = params.pubId - data.pi = mediaTypes.video ? 7 : 2; // video : inscreen } else { // legacy params data = { ...data, ...handleLegacyParams(params, sizes) } } diff --git a/modules/gumgumBidAdapter.md b/modules/gumgumBidAdapter.md index 7b4f0c98ea7..57d56235d1c 100644 --- a/modules/gumgumBidAdapter.md +++ b/modules/gumgumBidAdapter.md @@ -10,11 +10,76 @@ Maintainer: engineering@gumgum.com GumGum adapter for Prebid.js Please note that both video and in-video products require a mediaType of video. -All other products (in-screen, slot, native) should have a mediaType of banner. - +In-screen and slot products should have a mediaType of banner. # Test Parameters ``` +var adUnits = [ + { + code: 'slot-placement', + sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'gumgum', + params: { + zone: 'dc9d6be1', // GumGum Zone ID given to the client + slot: '15901', // GumGum Slot ID given to the client, + bidfloor: 0.03 // CPM bid floor + } + } + ] + },{ + code: 'inscreen-placement', + sizes: [[300, 50]], + mediaTypes: { + banner: { + sizes: [[1, 1]], + } + }, + bids: [ + { + bidder: 'gumgum', + params: { + zone: 'dc9d6be1', // GumGum Zone ID given to the client + bidfloor: 0.03 // CPM bid floor + } + } + ] + },{ + code: 'video-placement', + sizes: [[300, 50]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + minduration: 1, + maxduration: 2, + linearity: 1, + startdelay: 1, + placement: 1, + protocols: [1, 2] + } + }, + bids: [ + { + bidder: 'gumgum', + params: { + zone: 'ggumtest', // GumGum Zone ID given to the client + bidfloor: 0.03 // CPM bid floor + } + } + ] + } +]; +``` + +# Legacy Test Parameters +``` var adUnits = [ { code: 'test-div', diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 52a3a21db4e..a2a1b733d3c 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -35,7 +35,7 @@ describe('gumgumAdapter', function () { it('should return true when required params found', function () { const zoneBid = { ...bid, params: { 'zone': '123' } }; - const pubIdBid = { ...bid, params: { 'pubId': '123' } }; + const pubIdBid = { ...bid, params: { 'pubId': 123 } }; expect(spec.isBidRequestValid(bid)).to.equal(true); expect(spec.isBidRequestValid(zoneBid)).to.equal(true); expect(spec.isBidRequestValid(pubIdBid)).to.equal(true); @@ -143,23 +143,24 @@ describe('gumgumAdapter', function () { protocols: [1, 2] } }; + const zoneParam = { 'zone': '123a' }; + const pubIdParam = { 'pubId': 123 }; - describe('zone param', function () { - const zoneParam = { 'zone': '123a' }; + it('should set pubId param if found', function () { + const request = { ...bidRequests[0], params: pubIdParam }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pubId).to.equal(pubIdParam.pubId); + }); - it('should set t and pi param', function () { - const request = { ...bidRequests[0], params: zoneParam }; - const bidRequest = spec.buildRequests([request])[0]; - expect(bidRequest.data.t).to.equal(zoneParam.zone); - expect(bidRequest.data.pi).to.equal(2); - }); - it('should set the correct pi param if slot param is found', function () { - const request = { ...bidRequests[0], params: { ...zoneParam, 'slot': 1 } }; - const bidRequest = spec.buildRequests([request])[0]; - expect(bidRequest.data.pi).to.equal(3); - }); + it('should set t param when zone param is found', function () { + const request = { ...bidRequests[0], params: zoneParam }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.t).to.equal(zoneParam.zone); + }); + + describe('product id', function () { it('should set the correct pi param if native param is found', function () { - const request = { ...bidRequests[0], params: { ...zoneParam, 'native': 2 } }; + const request = { ...bidRequests[0], params: { ...zoneParam, native: 2 } }; const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.pi).to.equal(5); }); @@ -174,25 +175,18 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.pi).to.equal(6); }); - }); - - describe('pubId zone', function () { - const pubIdParam = { 'pubId': 'abc' }; - - it('should set t param', function () { - const request = { ...bidRequests[0], params: pubIdParam }; + it('should set the correct pi param if slot param is found', function () { + const request = { ...bidRequests[0], params: { ...zoneParam, slot: '123s' } }; const bidRequest = spec.buildRequests([request])[0]; - expect(bidRequest.data.pubId).to.equal(pubIdParam.pubId); + expect(bidRequest.data.pi).to.equal(3); }); - - it('should set the correct pi depending on what is found in mediaTypes', function () { - const request = { ...bidRequests[0], params: pubIdParam }; - const bidRequest = spec.buildRequests([request])[0]; - const vidRequest = { ...bidRequests[0], mediaTypes: vidMediaTypes, params: { 'videoPubID': 123 } }; - const vidBidRequest = spec.buildRequests([vidRequest])[0]; - - expect(bidRequest.data.pi).to.equal(2); - expect(vidBidRequest.data.pi).to.equal(7); + it('should default the pi param to 2 if only zone or pubId param is found', function () { + const zoneRequest = { ...bidRequests[0], params: zoneParam }; + const pubIdRequest = { ...bidRequests[0], params: pubIdParam }; + const zoneBidRequest = spec.buildRequests([zoneRequest])[0]; + const pubIdBidRequest = spec.buildRequests([pubIdRequest])[0]; + expect(zoneBidRequest.data.pi).to.equal(2); + expect(pubIdBidRequest.data.pi).to.equal(2); }); }); From 453e080e678570a394d8c5a9fbed8650b2a04be5 Mon Sep 17 00:00:00 2001 From: readpeaktuomo <66239046+readpeaktuomo@users.noreply.github.com> Date: Fri, 18 Dec 2020 18:37:43 +0200 Subject: [PATCH 0476/1476] Add support for tagId parameter (#6133) * Add support for tagId parameter * Update maintainer email to an alias --- modules/readpeakBidAdapter.js | 3 ++- modules/readpeakBidAdapter.md | 5 +++-- test/spec/modules/readpeakBidAdapter_spec.js | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/readpeakBidAdapter.js b/modules/readpeakBidAdapter.js index c72bbdd658f..2f4173f240b 100644 --- a/modules/readpeakBidAdapter.js +++ b/modules/readpeakBidAdapter.js @@ -98,7 +98,8 @@ function impression(slot) { id: slot.bidId, native: nativeImpression(slot), bidfloor: slot.params.bidfloor || 0, - bidfloorcur: slot.params.bidfloorcur || 'USD' + bidfloorcur: slot.params.bidfloorcur || 'USD', + tagId: slot.params.tagId || '0' }; } diff --git a/modules/readpeakBidAdapter.md b/modules/readpeakBidAdapter.md index a15767f29a7..da250e7f77a 100644 --- a/modules/readpeakBidAdapter.md +++ b/modules/readpeakBidAdapter.md @@ -4,7 +4,7 @@ Module Name: ReadPeak Bid Adapter Module Type: Bidder Adapter -Maintainer: kurre.stahlberg@readpeak.com +Maintainer: devteam@readpeak.com # Description @@ -23,7 +23,8 @@ Please reach out to your account team or hello@readpeak.com for more information params: { bidfloor: 5.00, publisherId: 'test', - siteId: 'test' + siteId: 'test', + tagId: 'test-tag-1' }, }] }]; diff --git a/test/spec/modules/readpeakBidAdapter_spec.js b/test/spec/modules/readpeakBidAdapter_spec.js index 0c6f942e724..d5a877f6221 100644 --- a/test/spec/modules/readpeakBidAdapter_spec.js +++ b/test/spec/modules/readpeakBidAdapter_spec.js @@ -28,7 +28,8 @@ describe('ReadPeakAdapter', function() { params: { bidfloor: 5.0, publisherId: '11bc5dd5-7421-4dd8-c926-40fa653bec76', - siteId: '11bc5dd5-7421-4dd8-c926-40fa653bec77' + siteId: '11bc5dd5-7421-4dd8-c926-40fa653bec77', + tagId: 'test-tag-1' }, bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', @@ -104,7 +105,8 @@ describe('ReadPeakAdapter', function() { ver: '1.1' }, bidfloor: 5, - bidfloorcur: 'USD' + bidfloorcur: 'USD', + tagId: 'test-tag-1' } ], site: { @@ -177,6 +179,7 @@ describe('ReadPeakAdapter', function() { expect(data.id).to.equal(bidRequest.bidderRequestId); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); expect(data.imp[0].bidfloorcur).to.equal('USD'); + expect(data.imp[0].tagId).to.equal('test-tag-1'); expect(data.site.publisher.id).to.equal(bidRequest.params.publisherId); expect(data.site.id).to.equal(bidRequest.params.siteId); expect(data.site.page).to.equal(bidderRequest.refererInfo.referer); From 78917f5a5128cde43e063facfaa61c81c9b14e6a Mon Sep 17 00:00:00 2001 From: Zak Andree Date: Fri, 18 Dec 2020 11:47:21 -0800 Subject: [PATCH 0477/1476] Inmar bidder adapter: Make adNetId an optional paramater (#6136) * Update Inmar bidder adapter * Set withCredentials to true * Remove inmarId from Inmar bidder adapter * Remove withCredentials because it defaults to true --- modules/inmarBidAdapter.js | 5 +---- modules/inmarBidAdapter.md | 2 -- test/spec/modules/inmarBidAdapter_spec.js | 11 ----------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/modules/inmarBidAdapter.js b/modules/inmarBidAdapter.js index b5ab72266fc..7583985b23c 100755 --- a/modules/inmarBidAdapter.js +++ b/modules/inmarBidAdapter.js @@ -17,7 +17,7 @@ export const spec = { * @returns {boolean} True if this is a valid bid, and false otherwise */ isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.partnerId && bid.params.adnetId); + return !!(bid.params && bid.params.partnerId); }, /** @@ -49,9 +49,6 @@ export const spec = { return { method: 'POST', url: 'https://prebid.owneriq.net:8443/bidder/pb/bid', - options: { - withCredentials: false - }, data: payloadString, }; }, diff --git a/modules/inmarBidAdapter.md b/modules/inmarBidAdapter.md index 1bacb30f2dd..8ed6b998602 100644 --- a/modules/inmarBidAdapter.md +++ b/modules/inmarBidAdapter.md @@ -25,7 +25,6 @@ Please reach out to your account manager for more information. bidder: 'inmar', params: { partnerId: 12345, - adnetId: 'ADb1f40rmi', position: 1 } }] @@ -37,7 +36,6 @@ Please reach out to your account manager for more information. bidder: 'inmar', params: { partnerId: 12345, - adnetId: 'ADb1f40rmo', position: 0 } }] diff --git a/test/spec/modules/inmarBidAdapter_spec.js b/test/spec/modules/inmarBidAdapter_spec.js index 86b7ab3a8af..998fe20d369 100644 --- a/test/spec/modules/inmarBidAdapter_spec.js +++ b/test/spec/modules/inmarBidAdapter_spec.js @@ -17,7 +17,6 @@ describe('Inmar adapter tests', function () { }, bidder: 'inmar', params: { - adnetId: 'ADb1f40rmi', partnerId: 12345 }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', @@ -39,7 +38,6 @@ describe('Inmar adapter tests', function () { }, bidder: 'inmar', params: { - adnetId: 'ADb1f40rmi', partnerId: 12345 }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', @@ -60,7 +58,6 @@ describe('Inmar adapter tests', function () { ], bidder: 'inmar', params: { - adnetId: 'ADb1f40rmi', partnerId: 12345, }, auctionId: '851adee7-d843-48f9-a7e9-9ff00573fcbf', @@ -118,7 +115,6 @@ describe('Inmar adapter tests', function () { expect(request).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request.data); - expect(requestContent.bidRequests[0].params).to.have.property('adnetId').and.to.equal('ADb1f40rmi'); expect(requestContent.bidRequests[0].params).to.have.property('partnerId').and.to.equal(12345); expect(requestContent.bidRequests[0]).to.have.property('auctionId').and.to.equal('0cb3144c-d084-4686-b0d6-f5dbe917c563'); expect(requestContent.bidRequests[0]).to.have.property('bidId').and.to.equal('2c7c8e9c900244'); @@ -198,19 +194,12 @@ describe('Inmar adapter tests', function () { })).to.equal(false); expect(spec.isBidRequestValid({ params: { - adnetId: 'ADb1f40rmi' } })).to.equal(false); expect(spec.isBidRequestValid({ params: { partnerId: 12345 } - })).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - adnetId: 'ADb1f40rmi', - partnerId: 12345 - } })).to.equal(true); }); From 2c1e5352efafc8fb5eae02eff4ac59bee82fdc74 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Sat, 19 Dec 2020 18:46:55 +0300 Subject: [PATCH 0478/1476] Added VOX Bidder Adapter (#6030) * Added voxBidAdapter.js to get a bid from partners.hybrid.ai * Added placements ids for testing Co-authored-by: s-shevtsov --- modules/voxBidAdapter.js | 247 ++++++++++++++++++ modules/voxBidAdapter.md | 237 ++++++++++++++++++ test/spec/modules/voxBidAdapter_spec.js | 320 ++++++++++++++++++++++++ 3 files changed, 804 insertions(+) create mode 100644 modules/voxBidAdapter.js create mode 100644 modules/voxBidAdapter.md create mode 100644 test/spec/modules/voxBidAdapter_spec.js diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js new file mode 100644 index 00000000000..450f270db31 --- /dev/null +++ b/modules/voxBidAdapter.js @@ -0,0 +1,247 @@ +import * as utils from '../src/utils.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import {BANNER, VIDEO} from '../src/mediaTypes.js' +import find from 'core-js-pure/features/array/find.js'; +import {auctionManager} from '../src/auctionManager.js'; +import {Renderer} from '../src/Renderer.js'; + +const BIDDER_CODE = 'vox'; +const SSP_ENDPOINT = 'https://ssp.hybrid.ai/auction/prebid'; +const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; +const TTL = 60; + +function buildBidRequests(validBidRequests) { + return utils._map(validBidRequests, function(validBidRequest) { + const params = validBidRequest.params; + const bidRequest = { + bidId: validBidRequest.bidId, + transactionId: validBidRequest.transactionId, + sizes: validBidRequest.sizes, + placement: params.placement, + placeId: params.placementId, + imageUrl: params.imageUrl + }; + + return bidRequest; + }) +} + +const outstreamRender = bid => { + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + sizes: [bid.width, bid.height], + targetId: bid.adUnitCode, + rendererOptions: { + showBigPlayButton: false, + showProgressBar: 'bar', + showVolume: false, + allowFullscreen: true, + skippable: false, + content: bid.vastXml + } + }); + }); +} + +const createRenderer = (bid) => { + const renderer = Renderer.install({ + targetId: bid.adUnitCode, + url: VIDEO_RENDERER_URL, + loaded: false + }); + + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + +function buildBid(bidData) { + const bid = { + requestId: bidData.bidId, + cpm: bidData.price, + width: bidData.content.width, + height: bidData.content.height, + creativeId: bidData.content.seanceId || bidData.bidId, + currency: bidData.currency, + netRevenue: true, + mediaType: BANNER, + ttl: TTL, + content: bidData.content + }; + + if (bidData.placement === 'video') { + bid.vastXml = bidData.content; + bid.mediaType = VIDEO; + + let adUnit = find(auctionManager.getAdUnits(), function (unit) { + return unit.transactionId === bidData.transactionId; + }); + + if (adUnit) { + bid.width = adUnit.mediaTypes.video.playerSize[0][0]; + bid.height = adUnit.mediaTypes.video.playerSize[0][1]; + + if (adUnit.mediaTypes.video.context === 'outstream') { + bid.renderer = createRenderer(bid); + } + } + } else if (bidData.placement === 'inImage') { + bid.mediaType = BANNER; + bid.ad = wrapInImageBanner(bid, bidData); + } else { + bid.mediaType = BANNER; + bid.ad = wrapBanner(bid, bidData); + } + + return bid; +} + +function getMediaTypeFromBid(bid) { + return bid.mediaTypes && Object.keys(bid.mediaTypes)[0]; +} + +function hasVideoMandatoryParams(mediaTypes) { + const isHasVideoContext = !!mediaTypes.video && (mediaTypes.video.context === 'instream' || mediaTypes.video.context === 'outstream'); + + const isPlayerSize = + !!utils.deepAccess(mediaTypes, 'video.playerSize') && + utils.isArray(utils.deepAccess(mediaTypes, 'video.playerSize')); + + return isHasVideoContext && isPlayerSize; +} + +function wrapInImageBanner(bid, bidData) { + return ` + + + + + + + + +
+ + + `; +} + +function wrapBanner(bid, bidData) { + return ` + + + + + + + + +
+ + + `; +} + +export const spec = { + code: BIDDER_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(bid) { + return ( + !!bid.params.placementId && + !!bid.params.placement && + ( + (getMediaTypeFromBid(bid) === BANNER && bid.params.placement === 'banner') || + (getMediaTypeFromBid(bid) === BANNER && bid.params.placement === 'inImage' && !!bid.params.imageUrl) || + (getMediaTypeFromBid(bid) === VIDEO && bid.params.placement === 'video' && hasVideoMandatoryParams(bid.mediaTypes)) + ) + ); + }, + + /** + * 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(validBidRequests, bidderRequest) { + const payload = { + url: bidderRequest.refererInfo.referer, + cmp: !!bidderRequest.gdprConsent, + bidRequests: buildBidRequests(validBidRequests) + }; + + if (payload.cmp) { + const gdprApplies = bidderRequest.gdprConsent.gdprApplies; + if (gdprApplies !== undefined) payload['ga'] = gdprApplies; + payload['cs'] = bidderRequest.gdprConsent.consentString; + } + + const payloadString = JSON.stringify(payload); + + return { + method: 'POST', + url: SSP_ENDPOINT, + data: payloadString, + options: { + contentType: 'application/json' + } + } + }, + + /** + * 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) { + let bidRequests = JSON.parse(bidRequest.data).bidRequests; + const serverBody = serverResponse.body; + + if (serverBody && serverBody.bids && utils.isArray(serverBody.bids)) { + return utils._map(serverBody.bids, function(bid) { + let rawBid = find(bidRequests, function (item) { + return item.bidId === bid.bidId; + }); + bid.placement = rawBid.placement; + bid.transactionId = rawBid.transactionId; + bid.placeId = rawBid.placeId; + return buildBid(bid); + }); + } else { + return []; + } + } + +} +registerBidder(spec); diff --git a/modules/voxBidAdapter.md b/modules/voxBidAdapter.md new file mode 100644 index 00000000000..3fc0383e6f8 --- /dev/null +++ b/modules/voxBidAdapter.md @@ -0,0 +1,237 @@ +# Overview + + +**Module Name**: VOX Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: prebid@hybrid.ai + +# Description + +You can use this adapter to get a bid from partners.hybrid.ai + + +## Sample Banner Ad Unit + +```js +var adUnits = [{ + code: 'banner_ad_unit', + mediaTypes: { + banner: { + sizes: [[160, 600]] + } + }, + bids: [{ + bidder: "vox", + params: { + placement: "banner", // required + placementId: "5fc77bc5a757531e24c89a4c" // required + } + }] +}]; +``` + +## Sample Video Ad Unit + +```js +var adUnits = [{ + code: 'video_ad_unit', + mediaTypes: { + video: { + context: 'outstream', // required + playerSize: [[640, 480]] // required + } + }, + bids: [{ + bidder: 'vox', + params: { + placement: "video", // required + placementId: "5fc77a94a757531e24c89a3d" // required + } + }] +}]; +``` + +# Sample In-Image Ad Unit + +```js +var adUnits = [{ + code: 'test-div', + mediaTypes: { + banner: { + sizes: [0, 0] + } + }, + bids: [{ + bidder: "vox", + params: { + placement: "inImage", + placementId: "5fc77b40a757531e24c89a42", + imageUrl: "https://gallery.voxexchange.io/vox-main.png" + } + }] +}]; +``` + +# Example page with In-Image + +```html + + + + + Prebid.js Banner Example + + + + + +

Prebid.js InImage Banner Test

+
+ + +
+ + +``` + +# Example page with In-Image and GPT + +```html + + + + + Prebid.js Banner Example + + + + + + +

Prebid.js Banner Ad Unit Test

+
+ + +
+ + +``` diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js new file mode 100644 index 00000000000..c6221cba9e5 --- /dev/null +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -0,0 +1,320 @@ +import { expect } from 'chai' +import { spec } from 'modules/voxBidAdapter.js' + +function getSlotConfigs(mediaTypes, params) { + return { + params: params, + sizes: [], + bidId: '2df8c0733f284e', + bidder: 'vox', + mediaTypes: mediaTypes, + transactionId: '31a58515-3634-4e90-9c96-f86196db1459' + } +} + +describe('VOX Adapter', function() { + const PLACE_ID = '5af45ad34d506ee7acad0c26'; + const bidderRequest = { + refererInfo: { referer: 'referer' } + } + const bannerMandatoryParams = { + placementId: PLACE_ID, + placement: 'banner' + } + const videoMandatoryParams = { + placementId: PLACE_ID, + placement: 'video' + } + const inImageMandatoryParams = { + placementId: PLACE_ID, + placement: 'inImage', + imageUrl: 'https://hybrid.ai/images/image.jpg' + } + const validBidRequests = [ + getSlotConfigs({ banner: {} }, bannerMandatoryParams), + getSlotConfigs({ video: {playerSize: [[640, 480]], context: 'outstream'} }, videoMandatoryParams), + getSlotConfigs({ banner: {sizes: [0, 0]} }, inImageMandatoryParams) + ] + describe('isBidRequestValid method', function() { + describe('returns true', function() { + describe('when banner slot config has all mandatory params', () => { + describe('and banner placement has the correct value', function() { + const slotConfig = getSlotConfigs( + {banner: {}}, + { + placementId: PLACE_ID, + placement: 'banner' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) + describe('and In-Image placement has the correct value', function() { + const slotConfig = getSlotConfigs( + { + banner: { + sizes: [[0, 0]] + } + }, + { + placementId: PLACE_ID, + placement: 'inImage', + imageUrl: 'imageUrl' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) + describe('when video slot has all mandatory params.', function() { + it('should return true, when video mediatype object are correct.', function() { + const slotConfig = getSlotConfigs( + { + video: { + context: 'instream', + playerSize: [[640, 480]] + } + }, + { + placementId: PLACE_ID, + placement: 'video' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) + }) + }) + }) + describe('returns false', function() { + describe('when params are not correct', function() { + function createSlotconfig(params) { + return getSlotConfigs({ banner: {} }, params) + } + it('does not have the placementId.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placement: 'banner' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placementId: PLACE_ID + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have the imageUrl.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placementId: PLACE_ID, + placement: 'inImage' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have a the correct placement.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createSlotconfig({ + placementId: PLACE_ID, + placement: 'something' + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) + describe('when video mediaType object is not correct.', function() { + function createVideoSlotconfig(mediaType) { + return getSlotConfigs(mediaType, { + placementId: PLACE_ID, + placement: 'video' + }) + } + it('is a void object', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ video: {} }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have playerSize.', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ video: { context: 'instream' } }) + ) + expect(isBidRequestValid).to.equal(false) + }) + it('does not have context', function() { + const isBidRequestValid = spec.isBidRequestValid( + createVideoSlotconfig({ + video: { + playerSize: [[640, 480]] + } + }) + ) + expect(isBidRequestValid).to.equal(false) + }) + }) + }) + }) + it('Url params should be correct ', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + expect(request.method).to.equal('POST') + expect(request.url).to.equal('https://ssp.hybrid.ai/auction/prebid') + }) + + describe('buildRequests method', function() { + it('Common data request should be correct', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(Array.isArray(data.bidRequests)).to.equal(true) + expect(data.url).to.equal('referer') + data.bidRequests.forEach(bid => { + expect(bid.bidId).to.equal('2df8c0733f284e') + expect(bid.placeId).to.equal(PLACE_ID) + expect(bid.transactionId).to.equal('31a58515-3634-4e90-9c96-f86196db1459') + }) + }) + + describe('GDPR params', function() { + describe('when there are not consent management platform', function() { + it('cmp should be false', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(false) + }) + }) + describe('when there are consent management platform', function() { + it('cmps should be true and ga should not sended, when gdprApplies is undefined', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: undefined, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(Object.keys(data).indexOf('data')).to.equal(-1) + expect(data.cs).to.equal('consentString') + }) + it('cmps should be true and all gdpr parameters should be sended, when there are gdprApplies', function() { + bidderRequest['gdprConsent'] = { + gdprApplies: true, + consentString: 'consentString' + } + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expect(data.cmp).to.equal(true) + expect(data.ga).to.equal(true) + expect(data.cs).to.equal('consentString') + }) + }) + }) + }) + + describe('interpret response method', function() { + it('should return a void array, when the server response are not correct.', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { + body: {} + } + const bids = spec.interpretResponse(serverResponse, request) + expect(typeof bids).to.equal('object') + expect(bids.length).to.equal(0) + }) + it('should return a void array, when the server response have not got bids.', function() { + const request = { data: JSON.stringify({}) } + const serverResponse = { body: { bids: [] } } + const bids = spec.interpretResponse(serverResponse, request) + expect(typeof bids).to.equal('object') + expect(bids.length).to.equal(0) + }) + describe('when the server response return a bid', function() { + describe('the bid is a banner', function() { + it('should return a banner bid', function() { + const request = spec.buildRequests([validBidRequests[0]], bidderRequest) + const serverResponse = { + body: { + bids: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: { + content: 'html', + width: 100, + height: 100 + } + } + ] + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2df8c0733f284e') + expect(bids[0].mediaType).to.equal(spec.supportedMediaTypes[0]) + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(100) + expect(bids[0].height).to.equal(100) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(typeof bids[0].ad).to.equal('string') + }) + it('should return a In-Image bid', function() { + const request = spec.buildRequests([validBidRequests[2]], bidderRequest) + const serverResponse = { + body: { + bids: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: { + content: 'html', + width: 100, + height: 100 + }, + ttl: 360 + } + ] + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2df8c0733f284e') + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].width).to.equal(100) + expect(bids[0].height).to.equal(100) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(typeof bids[0].ad).to.equal('string') + }) + }) + describe('the bid is a video', function() { + it('should return a video bid', function() { + const request = spec.buildRequests([validBidRequests[1]], bidderRequest) + const serverResponse = { + body: { + bids: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: 'html', + transactionId: '31a58515-3634-4e90-9c96-f86196db1459' + } + ] + } + } + const bids = spec.interpretResponse(serverResponse, request) + expect(bids.length).to.equal(1) + expect(bids[0].requestId).to.equal('2df8c0733f284e') + expect(bids[0].mediaType).to.equal(spec.supportedMediaTypes[1]) + expect(bids[0].cpm).to.equal(0.5) + expect(bids[0].currency).to.equal('USD') + expect(bids[0].netRevenue).to.equal(true) + expect(typeof bids[0].vastXml).to.equal('string') + }) + }) + }) + }) +}) From 5dd60a1078ebe39602ce5722e32fb3fd3a3f9d13 Mon Sep 17 00:00:00 2001 From: reemeng <29702905+reemeng@users.noreply.github.com> Date: Mon, 21 Dec 2020 10:12:30 +0200 Subject: [PATCH 0479/1476] added Engageya bid adapter (#6109) * added Engageya bid adapter * moved test function from adapter to spec * remove function import * PAGE_URL should be String --- modules/engageyaBidAdapter.js | 133 +++++++++++++++ modules/engageyaBidAdapter.md | 68 ++++++++ test/spec/modules/engageyaBidAdapter_spec.js | 161 +++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 modules/engageyaBidAdapter.js create mode 100644 modules/engageyaBidAdapter.md create mode 100644 test/spec/modules/engageyaBidAdapter_spec.js diff --git a/modules/engageyaBidAdapter.js b/modules/engageyaBidAdapter.js new file mode 100644 index 00000000000..321b3287c2b --- /dev/null +++ b/modules/engageyaBidAdapter.js @@ -0,0 +1,133 @@ +import { + BANNER, + NATIVE +} from '../src/mediaTypes.js'; + +const { + registerBidder +} = require('../src/adapters/bidderFactory.js'); +const BIDDER_CODE = 'engageya'; +const ENDPOINT_URL = 'https://recs.engageya.com/rec-api/getrecs.json'; +const ENDPOINT_METHOD = 'GET'; + +function getPageUrl() { + var pUrl = window.location.href; + if (isInIframe()) { + pUrl = document.referrer ? document.referrer : pUrl; + } + pUrl = encodeURIComponent(pUrl); + return pUrl; +} + +function isInIframe() { + try { + var isInIframe = (window.self !== window.top); + } catch (e) { + isInIframe = true; + } + return isInIframe; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], + isBidRequestValid: function(bid) { + return bid && bid.params && bid.params.hasOwnProperty('widgetId') && bid.params.hasOwnProperty('websiteId') && !isNaN(bid.params.widgetId) && !isNaN(bid.params.websiteId); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + var bidRequests = []; + if (validBidRequests && validBidRequests.length > 0) { + validBidRequests.forEach(function(bidRequest) { + if (bidRequest.params) { + var mediaType = bidRequest.hasOwnProperty('nativeParams') ? 1 : 2; + var imageWidth = -1; + var imageHeight = -1; + if (bidRequest.sizes && bidRequest.sizes.length > 0) { + imageWidth = bidRequest.sizes[0][0]; + imageHeight = bidRequest.sizes[0][1]; + } else if (bidRequest.nativeParams && bidRequest.nativeParams.image && bidRequest.nativeParams.image.sizes) { + imageWidth = bidRequest.nativeParams.image.sizes[0]; + imageHeight = bidRequest.nativeParams.image.sizes[1]; + } + + var widgetId = bidRequest.params.widgetId; + var websiteId = bidRequest.params.websiteId; + var pageUrl = (bidRequest.params.pageUrl && bidRequest.params.pageUrl != '[PAGE_URL]') ? bidRequest.params.pageUrl : ''; + if (!pageUrl) { + pageUrl = (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) ? bidderRequest.refererInfo.referer : getPageUrl(); + } + var bidId = bidRequest.bidId; + var finalUrl = ENDPOINT_URL + '?pubid=0&webid=' + websiteId + '&wid=' + widgetId + '&url=' + pageUrl + '&ireqid=' + bidId + '&pbtpid=' + mediaType + '&imw=' + imageWidth + '&imh=' + imageHeight; + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprApplies && bidderRequest.consentString) { + finalUrl += '&is_gdpr=1&gdpr_consent=' + bidderRequest.consentString; + } + bidRequests.push({ + url: finalUrl, + method: ENDPOINT_METHOD, + data: '' + }); + } + }); + } + + return bidRequests; + }, + + interpretResponse: function(serverResponse, bidRequest) { + const bidResponses = []; + if (serverResponse.body && serverResponse.body.recs && serverResponse.body.recs.length > 0) { + var response = serverResponse.body; + var isNative = response.pbtypeId == 1; + response.recs.forEach(function(rec) { + var imageSrc = rec.thumbnail_path.indexOf('http') == -1 ? 'https:' + rec.thumbnail_path : rec.thumbnail_path; + if (isNative) { + bidResponses.push({ + requestId: response.ireqId, + cpm: rec.ecpm, + width: response.imageWidth, + height: response.imageHeight, + creativeId: rec.postId, + currency: 'USD', + netRevenue: false, + ttl: 360, + native: { + title: rec.title, + body: '', + image: { + url: imageSrc, + width: response.imageWidth, + height: response.imageHeight + }, + privacyLink: '', + clickUrl: rec.clickUrl, + displayUrl: rec.url, + cta: '', + sponsoredBy: rec.displayName, + impressionTrackers: [], + }, + }); + } else { + // var htmlTag = ""; + var htmlTag = '
'; + var tag = rec.tag ? rec.tag : htmlTag; + bidResponses.push({ + requestId: response.ireqId, + cpm: rec.ecpm, + width: response.imageWidth, + height: response.imageHeight, + creativeId: rec.postId, + currency: 'USD', + netRevenue: false, + ttl: 360, + ad: tag, + }); + } + }); + } + + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/engageyaBidAdapter.md b/modules/engageyaBidAdapter.md new file mode 100644 index 00000000000..541ba548eeb --- /dev/null +++ b/modules/engageyaBidAdapter.md @@ -0,0 +1,68 @@ +# Overview + +``` +Module Name: Engageya's Bidder Adapter +Module Type: Bidder Adapter +Maintainer: reem@engageya.com +``` + +# Description + +Module that connects to Engageya's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "engageya", + params: { + widgetId: '', + websiteId: '', + pageUrl:'[PAGE_URL]' + } + } + ] + },{ + code: 'test-div', + mediaTypes: { + native: { + image: { + required: true, + sizes: [236, 202] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } + } + }, + bids: [ + { + bidder: "engageya", + params: { + widgetId: '', + websiteId: '', + pageUrl:'[PAGE_URL]' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/engageyaBidAdapter_spec.js b/test/spec/modules/engageyaBidAdapter_spec.js new file mode 100644 index 00000000000..ad411fc9350 --- /dev/null +++ b/test/spec/modules/engageyaBidAdapter_spec.js @@ -0,0 +1,161 @@ +import {expect} from 'chai'; +import {spec} from 'modules/engageyaBidAdapter.js'; +import * as utils from 'src/utils.js'; + +const ENDPOINT_URL = 'https://recs.engageya.com/rec-api/getrecs.json'; + +export const _getUrlVars = function(url) { + var hash; + var myJson = {}; + var hashes = url.slice(url.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + myJson[hash[0]] = hash[1]; + } + return myJson; +} + +describe('engageya adapter', function() { + let bidRequests; + let nativeBidRequests; + + beforeEach(function() { + bidRequests = [ + { + bidder: 'engageya', + params: { + widgetId: 85610, + websiteId: 91140, + pageUrl: '[PAGE_URL]' + } + } + ] + + nativeBidRequests = [ + { + bidder: 'engageya', + params: { + widgetId: 85610, + websiteId: 91140, + pageUrl: '[PAGE_URL]' + }, + nativeParams: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + } + } + } + ] + }) + describe('isBidRequestValid', function () { + it('valid bid case', function () { + let validBid = { + bidder: 'engageya', + params: { + widgetId: 85610, + websiteId: 91140, + pageUrl: '[PAGE_URL]' + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('invalid bid case: widgetId and websiteId is not passed', function() { + let validBid = { + bidder: 'engageya', + params: { + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }) + + it('invalid bid case: widget id must be number', function() { + let invalidBid = { + bidder: 'engageya', + params: { + widgetId: '157746a', + websiteId: 91140, + pageUrl: '[PAGE_URL]' + } + } + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }) + }) + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.include(ENDPOINT_URL); + expect(request.method).to.equal('GET'); + }); + + it('buildRequests function should not modify original bidRequests object', function () { + let originalBidRequests = utils.deepClone(bidRequests); + let request = spec.buildRequests(bidRequests); + expect(bidRequests).to.deep.equal(originalBidRequests); + }); + + it('buildRequests function should not modify original nativeBidRequests object', function () { + let originalBidRequests = utils.deepClone(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests); + expect(nativeBidRequests).to.deep.equal(originalBidRequests); + }); + + it('Request params check', function() { + let request = spec.buildRequests(bidRequests)[0]; + const data = _getUrlVars(request.url) + expect(parseInt(data.wid)).to.exist.and.to.equal(bidRequests[0].params.widgetId); + expect(parseInt(data.webid)).to.exist.and.to.equal(bidRequests[0].params.websiteId); + }) + }) + + describe('interpretResponse', function () { + let response = {recs: [ + { + 'ecpm': 0.0920, + 'postId': '', + 'ad': '', + 'thumbnail_path': 'https://engageya.live/wp-content/uploads/2019/05/images.png' + } + ], + imageWidth: 300, + imageHeight: 250, + ireqId: '1d236f7890b', + pbtypeId: 2}; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '1d236f7890b', + 'cpm': 0.0920, + 'width': 300, + 'height': 250, + 'netRevenue': false, + 'currency': 'USD', + 'creativeId': '', + 'ttl': 700, + 'ad': '' + } + ]; + let request = spec.buildRequests(bidRequests)[0]; + let result = spec.interpretResponse({body: response}, request); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].cpm).to.not.equal(null); + expect(result[0].creativeId).to.not.equal(null); + expect(result[0].ad).to.not.equal(null); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(false); + }); + }) +}) From d68f2e0c43df4429ea81fc0d8675b0ddf8fddec7 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Mon, 21 Dec 2020 12:54:18 +0300 Subject: [PATCH 0480/1476] Fix userIds format for TheMediaGrid Bid Adapter (#6142) * 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 --- modules/gridBidAdapter.js | 67 +++----------------- test/spec/modules/gridBidAdapter_spec.js | 78 +++++++----------------- 2 files changed, 30 insertions(+), 115 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 3f9d4fba31d..6e610b67e0e 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -51,6 +51,7 @@ export const spec = { let content = null; let schain = null; let userId = null; + let userIdAsEids = null; let user = null; let userExt = null; let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; @@ -72,6 +73,9 @@ export const spec = { if (!userId) { userId = bid.userId; } + if (!userIdAsEids) { + userIdAsEids = bid.userIdAsEids; + } const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { @@ -161,66 +165,9 @@ export const spec = { userExt = {consent: gdprConsent.consentString}; } - if (userId) { - if (userId.tdid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'adserver.org', // Unified ID - uids: [{ - id: userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } - if (userId.id5id && userId.id5id.uid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'id5-sync.com', - uids: [{ - id: userId.id5id.uid - }], - ext: userId.id5id.ext - }); - } - if (userId.lipb && userId.lipb.lipbid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'liveintent.com', - uids: [{ - id: userId.lipb.lipbid - }] - }); - } - if (userId.idl_env) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'identityLink', - uids: [{ - id: userId.idl_env - }] - }); - } - if (userId.criteoId) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'criteo.com', - uids: [{ - id: userId.criteoId - }] - }); - } - - if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt = userExt || {}; - userExt.digitrust = Object.assign({}, userId.digitrustid.data); - } + if (userIdAsEids && userIdAsEids.length) { + userExt = userExt || {}; + userExt.eids = [...userIdAsEids]; } if (userExt && Object.keys(userExt).length) { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e884df40c5e..084c67562e6 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -311,68 +311,36 @@ describe('TheMediaGrid Adapter', function () { }); it('if userId is present payload must have user.ext param with right keys', function () { - const bidRequestsWithUserIds = bidRequests.map((bid) => { - return Object.assign({ - userId: { - id5id: { uid: 'id5id_1', ext: { linkType: 2 } }, - tdid: 'tdid_1', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - lipb: {lipbid: 'lipb_1'}, - idl_env: 'idl_env_1', - criteoId: 'criteoId_1' - } - }, bid); - }); - const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('user'); - expect(payload.user).to.have.property('ext'); - expect(payload.user.ext.digitrust).to.deep.equal({ - id: 'DTID', - keyv: 4, - privacy: { - optout: false + const eids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] }, - producer: 'ABC', - version: 2 - }); - expect(payload.user.ext.eids).to.deep.equal([ { source: 'adserver.org', uids: [{ - id: 'tdid_1', + id: 'some-random-id-value', + atype: 1, ext: { rtiPartner: 'TDID' } }] - }, - { - source: 'id5-sync.com', - uids: [{ - id: 'id5id_1' - }], - ext: { linkType: 2 } - }, - { - source: 'liveintent.com', - uids: [{ - id: 'lipb_1' - }] - }, - { - source: 'identityLink', - uids: [{ - id: 'idl_env_1' - }] - }, - { - source: 'criteo.com', - uids: [{ - id: 'criteoId_1' - }] } - ]); + ]; + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + userIdAsEids: eids + }, bid); + }); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext.eids).to.deep.equal(eids); }); it('if schain is present payload must have source.ext.schain param', function () { @@ -403,7 +371,7 @@ describe('TheMediaGrid 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 bidRequestsWithUserIds = bidRequests.map((bid) => { + const bidRequestsWithJwTargeting = bidRequests.map((bid) => { return Object.assign({ rtd: { jwplayer: { @@ -415,7 +383,7 @@ describe('TheMediaGrid Adapter', function () { } }, bid); }); - const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithJwTargeting, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); From 3e73c11e1285aa40588c21e616602f853e4a4dab Mon Sep 17 00:00:00 2001 From: Newton <5769156+iamnewton@users.noreply.github.com> Date: Mon, 21 Dec 2020 02:00:15 -0800 Subject: [PATCH 0481/1476] ID Library feat: turn off fullscan by default (#6140) * chore: lint HTML example page * chore: add space to logging statement for clarity * chore: update package lock * feat: turn off fullscan by default * chore: format the HTML for readibility * chore: fix test * Revert "chore: update package lock" This reverts commit 4faf19489677de3c278b625535f4ba01877ccacb. --- integrationExamples/gpt/userId_example.html | 589 ++++++++++---------- modules/idLibrary.js | 4 +- modules/idLibrary.md | 38 +- test/spec/modules/idLibrary_spec.js | 6 +- 4 files changed, 318 insertions(+), 319 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 7375293fdf0..e6f9255326a 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -1,334 +1,335 @@ + - - + + + ] + } + ]; - + - var adUnits = [ - { - code: 'test-div', - mediaTypes: { - banner: { - sizes: [[300,250],[300,600],[728,90]] - } - }, - bids: [ - { - bidder: 'rubicon', - params: { - accountId: '1001', - siteId: '113932', - zoneId: '535510' - } - } - ] - } - ]; + - + pbjs.que.push(function() { + pbjs.setConfig({ + debug: true, + consentManagement: { + cmpApi: 'iab', + timeout: 1000, + defaultGdprScope: true + }, + // consentManagement: { + // cmpApi: 'static', + // consentData: { + // consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA' + // vendorData: { + // purposeConsents: { + // '1': true + // } + // } + // } + // }, + userSync: { + userIds: [{ + name: "pubProvidedId", + params: { + eids: [{ + source: "domain.com", + uids:[{ + id: "value read from cookie or local storage", + atype: 1, + ext: { + stype: "ppuid" // allowable options are sha256email, DMP, ppuid for now + } + }] + },{ + source: "3rdpartyprovided.com", + uids:[{ + id: "value read from cookie or local storage", + atype: 3, + ext: { + stype: "sha256email" + } + }] + }], + eidsFunction: getHashedEmail // any user defined function that exists in the page + } + },{ + name: "unifiedId", + params: { + partner: "prebid", + url: "http://match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" + }, + storage: { + type: "html5", + name: "unifiedid", + expires: 30 + }, + },{ + name: "intentIqId", + params: { + partner: 0, //Set your real IntentIQ partner ID here for production. + }, + storage: { + type: "cookie", + name: "intentIqId", + expires: 30 + }, + }, + { + name: "id5Id", + params: { + partner: 173 //Set your real ID5 partner ID here for production, please ask for one at http://id5.io/prebid + }, + storage: { + type: "cookie", + name: "id5id", + expires: 90, + refreshInSeconds: 8*3600 // Refresh frequency of cookies, defaulting to 'expires' + }, - + function sendAdserverRequest() { + if (pbjs.adserverRequestSent) return; + pbjs.adserverRequestSent = true; + googletag.cmd.push(function() { + pbjs.que.push(function() { + pbjs.setTargetingForGPTAsync(); + googletag.pubads().refresh(); + }); + }); + } - + setTimeout(function() { + sendAdserverRequest(); + }, FAILSAFE_TIMEOUT); + - - + - -

Rubicon Project Prebid

+ + -
- -
- + +

Rubicon Project Prebid

+
+ +
+ diff --git a/modules/idLibrary.js b/modules/idLibrary.js index ba3cc0b5efb..988f3417b30 100644 --- a/modules/idLibrary.js +++ b/modules/idLibrary.js @@ -8,7 +8,7 @@ let email; let conf; const LOG_PRE_FIX = 'ID-Library: '; const CONF_DEFAULT_OBSERVER_DEBOUNCE_MS = 250; -const CONF_DEFAULT_FULL_BODY_SCAN = true; +const CONF_DEFAULT_FULL_BODY_SCAN = false; const OBSERVER_CONFIG = { subtree: true, attributes: true, @@ -38,7 +38,7 @@ function getEmail(value) { if (!matched) { return null; } - logInfo('Email found' + matched[0]); + logInfo('Email found: ' + matched[0]); return matched[0]; } diff --git a/modules/idLibrary.md b/modules/idLibrary.md index 69b63dc466b..28d40c389f3 100644 --- a/modules/idLibrary.md +++ b/modules/idLibrary.md @@ -1,24 +1,22 @@ -## ID Library Configuration Example +# ID Library +## Configuration Options -|Param |Required |Description | -|----------------|-------------------------------|-----------------------------| -|url |Yes | The url endpoint is used to post the hashed email and user ids. | -|target |No |It should contain the element id from which the email can be read. | -|debounce |No | Time in milliseconds before the email and ids are fetched | -|fullscan |No | Option to enable/disable full body scan to get email. By default the full body scan is enabled. | +| Parameter | Required | Type | Default | Description | +| :--------- | :------- | :------ | :------ | :---------- | +| `target` | Yes | String | N/A | ID attribute of the element from which the email can be read. | +| `url` | Yes | String | N/A | URL endpoint used to post the hashed email and user IDs. | +| `debounce` | No | Number | 250 | Time in milliseconds before the email and IDs are fetched. | +| `fullscan` | No | Boolean | false | Enable/disable a full page body scan to get email. | -### Example -``` - pbjs.setConfig({ - idLibrary:{ - url: , - debounce: 250, - target: 'username', - fullscan: false - }, - }); -``` +## Example - -``` +```javascript +pbjs.setConfig({ + idLibrary: { + target: 'username', + url: 'https://example.com', + debounce: 250, + fullscan: false, + }, +}); diff --git a/test/spec/modules/idLibrary_spec.js b/test/spec/modules/idLibrary_spec.js index da61850f29b..682c2df1e44 100644 --- a/test/spec/modules/idLibrary_spec.js +++ b/test/spec/modules/idLibrary_spec.js @@ -50,12 +50,12 @@ describe('currency', function () { it('results with config default fullscan ', function () { let config = { 'url': 'URL' } idlibrary.setConfig(config); - expect(config.fullscan).to.be.equal(true); + expect(config.fullscan).to.be.equal(false); }); it('results with config fullscan ', function () { - let config = { 'url': 'URL', 'fullscan': false } + let config = { 'url': 'URL', 'fullscan': true } idlibrary.setConfig(config); - expect(config.fullscan).to.be.equal(false); + expect(config.fullscan).to.be.equal(true); }); }); }); From 2f055063ccca4d08c2cb5e8fe13abadd8346ba4c Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 21 Dec 2020 11:01:50 +0100 Subject: [PATCH 0482/1476] update release process for notes on release drafter checks (#6137) * update release process for notes on release drafter checks * update section titles --- RELEASE_SCHEDULE.md | 83 ++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md index 7b2c6244bd7..bfbd0772c3e 100644 --- a/RELEASE_SCHEDULE.md +++ b/RELEASE_SCHEDULE.md @@ -1,6 +1,14 @@ **Table of Contents** - [Release Schedule](#release-schedule) - [Release Process](#release-process) + - [1. Make sure that all PRs have been named and labeled properly per the PR Process](#1-make-sure-that-all-prs-have-been-named-and-labeled-properly-per-the-pr-process) + - [2. Make sure all browserstack tests are passing](#2-make-sure-all-browserstack-tests-are-passing) + - [3. Prepare Prebid Code](#3-prepare-prebid-code) + - [4. Verify the Release](#4-verify-the-release) + - [5. Create a GitHub release](#5-create-a-github-release) + - [6. Update coveralls _(skip for legacy)_](#6-update-coveralls-skip-for-legacy) + - [7. Distribute the code](#7-distribute-the-code) + - [8. Increment Version for Next Release](#8-increment-version-for-next-release) - [Beta Releases](#beta-releases) - [FAQs](#faqs) @@ -9,7 +17,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](http://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) @@ -19,14 +27,20 @@ Announcements regarding releases will be made to the #headerbidding-dev channel _Note: If `github.com/prebid/Prebid.js` is not configured as the git origin for your repo, all of the following git commands will have to be modified to reference the proper remote (e.g. `upstream`)_ -1. Make Sure all browserstack tests are passing. On PR merge to master CircleCI will run unit tests on browserstack. Checking the last CircleCI build [here](https://circleci.com/gh/prebid/Prebid.js) for master branch will show you detailed results. - - In case of failure do following, +### 1. Make sure that all PRs have been named and labeled properly per the [PR Process](https://github.com/prebid/Prebid.js/blob/master/PR_REVIEW.md#general-pr-review-process) + * Do this by checking the latest draft release from the [releases page](https://github.com/prebid/Prebid.js/releases) and make sure nothing appears in the first section called "In This Release". If they do, please open the PRs and add the appropriate labels. + * Do a quick check that all the titles/descriptions look ok, and if not, adjust the PR title. + +### 2. Make sure all browserstack tests are passing + + On PR merge to master, CircleCI will run unit tests on browserstack. Checking the last CircleCI build [here](https://circleci.com/gh/prebid/Prebid.js) for master branch will show you detailed results.** + + In case of failure do following, - Try to fix the failing tests. - If you are not able to fix tests in time. Skip the test, create issue and tag contributor. - #### How to run tests in browserstack - + **How to run tests in browserstack** + _Note: the following browserstack information is only relevant for debugging purposes, if you will not be debugging then it can be skipped._ Set the environment variables. You may want to add these to your `~/.bashrc` for convenience. @@ -35,40 +49,40 @@ _Note: If `github.com/prebid/Prebid.js` is not configured as the git origin for export BROWSERSTACK_USERNAME="my browserstack username" export BROWSERSTACK_ACCESS_KEY="my browserstack access key" ``` - + ``` gulp test --browserstack >> prebid_test.log - + vim prebid_test.log // Will show the test results ``` -2. Prepare Prebid Code +### 3. Prepare Prebid Code Update the package.json version to become the current release. Then commit your changes. ``` - git commit -m "Prebid 1.x.x Release" + git commit -m "Prebid 4.x.x Release" git push ``` -3. Verify Release +### 4. Verify the Release Make sure your there are no more merges to master branch. Prebid code is clean and up to date. -4. Create a GitHub release +### 5. Create a GitHub release + + Edit the most recent [release notes](https://github.com/prebid/Prebid.js/releases) draft and make sure the correct version is set and the master branch is selected in the dropdown. Click `Publish release`. GitHub will create release tag. - Edit the most recent [release notes](https://github.com/prebid/Prebid.js/releases) draft and make sure the correct tag is in the dropdown. Click `Publish`. GitHub will create release tag. - - Pull these changes locally by running command + Pull these changes locally by running command ``` git pull git fetch --tags - ``` - + ``` + and verify the tag. -5. Update coveralls _(skip for legacy)_ +### 6. Update coveralls _(skip for legacy)_ We use https://coveralls.io/ to show parts of code covered by unit tests. @@ -80,35 +94,23 @@ _Note: If `github.com/prebid/Prebid.js` is not configured as the git origin for Run `gulp coveralls` to update code coverage history. -6. Distribute the code +### 7. Distribute the code - _Note: do not go to step 7 until step 6 has been verified completed._ + _Note: do not go to step 8 until step 7 has been verified completed._ Reach out to any of the Appnexus folks to trigger the jenkins job. - // TODO + // TODO: Jenkins job is moving files to appnexus cdn, pushing prebid.js to npm, purging cache and sending notification to slack. Move all the files from Appnexus CDN to jsDelivr and create bash script to do above tasks. -7. Post Release Version - - Update the version - Manually edit Prebid's package.json to become "1.x.x-pre" (using the values for the next release). Then commit your changes. +### 8. Increment Version for Next Release + + Update the version by manually editing Prebid's `package.json` to become "4.x.x-pre" (using the values for the next release). Then commit your changes. ``` git commit -m "Increment pre version" git push ``` - -8. Create new release draft - - Go to [github releases](https://github.com/prebid/Prebid.js/releases) and add a new draft for the next version of Prebid.js with the following template: -``` -## 🚀New Features - -## 🛠Maintenance - -## 🐛Bug Fixes -``` ## Beta Releases @@ -129,11 +131,8 @@ Characteristics of a `GA` release: ## FAQs **1. Is there flexibility in the schedule?** - -If a major bug is found in the current release, a maintenance patch will be done as soon as possible. - -It is unlikely that we will put out a maintenance patch at the request of a given bid adapter or module owner. +* If a major bug is found in the current release, a maintenance patch will be done as soon as possible. +* It is unlikely that we will put out a maintenance patch at the request of a given bid adapter or module owner. **2. What Pull Requests make it into a release?** - -Every PR that's merged into master will be part of a release. Here are the [PR review guidelines](https://github.com/prebid/Prebid.js/blob/master/PR_REVIEW.md). +* Every PR that's merged into master will be part of a release. Here are the [PR review guidelines](https://github.com/prebid/Prebid.js/blob/master/PR_REVIEW.md). From 5d2ccb2fdd52c7bffa1b75b01afcd2af94036da6 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Mon, 21 Dec 2020 22:56:52 +0100 Subject: [PATCH 0483/1476] Sspbc Bid Adapter: multiple updates (#6118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add sspbc adapter * tests for sspbc adapter * sspBC adapter v4.5: set correct creativeId, add adomain to bid.meta, set test mode in adexchange, read site SN from bid response * sspBC adapter v4.5: set meta.advertiserDomains, update test to expect bid.meta * sspBC Adapter: add ajax tests (test ad with & without gdpr) * sspBC Adapter: remove ajax tests * Update adapter to v4.6 Update adapter to v4.6 - add notification endpoint - send bidWon and onTimeout notifications - send CMP version to user sync endpoint * Remove console logs for notification events * Change payload data in onTimeout event * Update tests for sspBC adapter Update tests for sspBC adapter: - add onBidWon test - add onTimeout test - alter getUserSyncs test * Update sspBC adapter to v4.7; enable oneCodeId mode; change module name to ensure combatibility with prebid.org downloader * sspBc adapter: Bug fixes in v4.7 - change notification format, fix oneCode detection data, convert slot number to int Co-authored-by: Wojciech Biały --- .../{sspBCAdapter.js => sspBCBidAdapter.js} | 140 +++++++++++------- .../{sspBCAdapter.md => sspBCBidAdapter.md} | 16 +- ...dapter_spec.js => sspBCBidAdapter_spec.js} | 109 ++++++++++++-- 3 files changed, 188 insertions(+), 77 deletions(-) rename modules/{sspBCAdapter.js => sspBCBidAdapter.js} (72%) rename modules/{sspBCAdapter.md => sspBCBidAdapter.md} (68%) rename test/spec/modules/{sspBCAdapter_spec.js => sspBCBidAdapter_spec.js} (78%) diff --git a/modules/sspBCAdapter.js b/modules/sspBCBidAdapter.js similarity index 72% rename from modules/sspBCAdapter.js rename to modules/sspBCBidAdapter.js index 4069c722e9d..1f250992fee 100644 --- a/modules/sspBCAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -8,11 +8,58 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.6'; +const BIDDER_VERSION = '4.7'; const W = window; const { navigator } = W; +const oneCodeDetection = {}; var consentApiVersion; +/** + * Get bid parameters for notification + * @param {*} bidData - bid (bidWon), or array of bids (timeout) + */ +const getNotificationPayload = bidData => { + if (bidData) { + const bids = utils.isArray(bidData) ? bidData : [bidData]; + if (bids.length > 0) { + const result = { + requestId: undefined, + siteId: [], + adUnit: [], + slotId: [], + } + bids.forEach(bid => { + let params = utils.isArray(bid.params) ? bid.params[0] : bid.params; + params = params || {}; + + // check for stored detection + if (oneCodeDetection[bid.requestId]) { + params.siteId = oneCodeDetection[bid.requestId][0]; + params.id = oneCodeDetection[bid.requestId][1]; + } + + if (params.siteId) { + result.siteId.push(params.siteId); + } + if (params.id) { + result.slotId.push(params.id); + } + if (bid.cpm) { + const meta = bid.meta || {}; + result.cpm = bid.cpm; + result.creativeId = bid.creativeId; + result.adomain = meta.advertiserDomains && meta.advertiserDomains[0]; + result.networkName = meta.networkName; + } + result.adUnit.push(bid.adUnitCode) + result.requestId = bid.auctionId || result.requestId; + result.timeout = bid.timeout || result.timeout; + }) + return result; + } + } +} + const cookieSupport = () => { const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); const useCookies = navigator.cookieEnabled || !!document.cookie.length; @@ -104,13 +151,13 @@ function mapBanner(slot) { function mapImpression(slot) { const imp = { - id: slot.params.id, + id: (slot.params && slot.params.id) ? slot.params.id : 'bidid-' + slot.bidId, banner: mapBanner(slot), /* native: mapNative(slot), */ - tagid: slot.params.id, + tagid: slot.adUnitCode, }; - const bidfloor = parseFloat(slot.params.bidfloor); + const bidfloor = (slot.params && slot.params.bidFloor) ? parseFloat(slot.params.bidFloor) : undefined; if (bidfloor) { imp.bidfloor = bidfloor; @@ -170,6 +217,7 @@ function renderCreative(site, auctionId, bid, seat, request) { ` + ad: `` } if (isVideo(bidRequest, adType)) { @@ -124,8 +124,7 @@ export const spec = { bidResponse.height = playersize[1] } bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}` - + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}` if (isOutstream(bidRequest)) { const renderer = Renderer.install({ id: bidRequest.bidId, diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index 37897b83f12..a7a3f2715dc 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -21,7 +21,6 @@ Module that connects to Yieldlab's demand sources params: { adslotId: "5220336", supplyId: "1381604", - adSize: "728x90", targeting: { key1: "value1", key2: "value2" @@ -41,8 +40,7 @@ Module that connects to Yieldlab's demand sources bidder: "yieldlab", params: { adslotId: "5220339", - supplyId: "1381604", - adSize: "640x480" + supplyId: "1381604" } }] } diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index cd2c46a5664..72f70ca0208 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -7,7 +7,6 @@ const REQUEST = { 'params': { 'adslotId': '1111', 'supplyId': '2222', - 'adSize': '728x90', 'targeting': { 'key1': 'value1', 'key2': 'value2', @@ -57,6 +56,7 @@ const RESPONSE = { id: 1111, price: 1, pid: 2222, + adsize: '728x90', adtype: 'BANNER' } @@ -88,8 +88,7 @@ describe('yieldlabBidAdapter', function () { const request = { 'params': { 'adslotId': '1111', - 'supplyId': '2222', - 'adSize': '728x90' + 'supplyId': '2222' } } expect(spec.isBidRequestValid(request)).to.equal(true) @@ -180,7 +179,7 @@ describe('yieldlabBidAdapter', function () { expect(result[0].netRevenue).to.equal(false) expect(result[0].ttl).to.equal(300) expect(result[0].referrer).to.equal('') - expect(result[0].ad).to.include(' @@ -126,14 +128,9 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse) { - const serverBody = serverResponse.body; - if (serverBody && utils.isArray(serverBody)) { - return utils._map(serverBody, function(bid) { - return buildBid(bid); - }); - } else { - return []; - } + const bids = serverResponse.body && serverResponse.body.bids; + + return Array.isArray(bids) ? bids.map(bid => buildBid(bid)) : [] } } diff --git a/modules/astraoneBidAdapter.md b/modules/astraoneBidAdapter.md index a7eaeeef5a4..e090cfe1e54 100644 --- a/modules/astraoneBidAdapter.md +++ b/modules/astraoneBidAdapter.md @@ -18,17 +18,17 @@ About us: https://astraone.io var adUnits = [{ code: 'test-div', mediaTypes: { - banner: { - sizes: [1, 1] - } + banner: { + sizes: [1, 1], + } }, bids: [{ - bidder: "astraone", - params: { - placement: "inImage", - placeId: "5af45ad34d506ee7acad0c26", - imageUrl: "https://creative.astraone.io/files/default_image-1-600x400.jpg" - } + bidder: "astraone", + params: { + placement: "inImage", + placeId: "5f477bf94d506ebe2c4240f3", + imageUrl: "https://creative.astraone.io/files/default_image-1-600x400.jpg" + } }] }]; ``` @@ -39,66 +39,69 @@ var adUnits = [{ - - Prebid.js Banner Example - - + + }, + bids: [{ + bidder: "astraone", + params: { + placement: "inImage", + placeId: "5f477bf94d506ebe2c4240f3", + imageUrl: "https://creative.astraone.io/files/default_image-1-600x400.jpg" + } + }] + }]; + + var pbjs = pbjs || {}; + pbjs.que = pbjs.que || []; + + pbjs.que.push(function() { + pbjs.addAdUnits(adUnits); + pbjs.requestBids({ + bidsBackHandler: function (e) { + if (pbjs.adserverRequestSent) return; + pbjs.adserverRequestSent = true; + var params = pbjs.getAdserverTargetingForAdUnitCode("test-div"); + var iframe = document.getElementById('test-div'); + + if (params && params['hb_adid']) { + iframe.parentElement.style.position = "relative"; + iframe.style.display = "block"; + pbjs.renderAd(iframe.contentDocument, params['hb_adid']); + } + } + }); + }); + -

Prebid.js InImage Banner Test

+

Prebid.js InImage Banner Test

-
- - -
+
+ + +
@@ -109,90 +112,91 @@ var adUnits = [{ - - Prebid.js Banner Example - - - - + + Prebid.js Banner gpt Example + + + + -

Prebid.js Banner Ad Unit Test

+

Prebid.js InImage Banner gpt Test

-
- +
+ - -
+ +
``` diff --git a/test/spec/modules/astraoneBidAdapter_spec.js b/test/spec/modules/astraoneBidAdapter_spec.js index e422f64b570..0e545081869 100644 --- a/test/spec/modules/astraoneBidAdapter_spec.js +++ b/test/spec/modules/astraoneBidAdapter_spec.js @@ -14,7 +14,7 @@ function getSlotConfigs(mediaTypes, params) { describe('AstraOne Adapter', function() { describe('isBidRequestValid method', function() { - const PLACE_ID = '5af45ad34d506ee7acad0c26'; + const PLACE_ID = '5f477bf94d506ebe2c4240f3'; const IMAGE_URL = 'https://creative.astraone.io/files/default_image-1-600x400.jpg'; describe('returns true', function() { @@ -176,21 +176,23 @@ describe('AstraOne Adapter', function() { describe('the bid is a banner', function() { it('should return a banner bid', function() { const serverResponse = { - body: [ - { - bidId: '2df8c0733f284e', - price: 0.5, - currency: 'USD', - content: { - content: 'html', - actionUrls: {}, - seanceId: '123123' - }, - width: 100, - height: 100, - ttl: 360 - } - ] + body: { + bids: [ + { + bidId: '2df8c0733f284e', + price: 0.5, + currency: 'USD', + content: { + content: 'html', + actionUrls: {}, + seanceId: '123123' + }, + width: 100, + height: 100, + ttl: 360 + } + ] + } } const bids = spec.interpretResponse(serverResponse) expect(bids.length).to.equal(1) From b7ec359ca242ceab4cd0c7e37307247974126e51 Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Thu, 28 Jan 2021 03:14:49 -0500 Subject: [PATCH 0555/1476] update banner ttl (#6228) --- modules/districtmDMXBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index b5969d4ed1e..f01c3c2ce9f 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -39,7 +39,7 @@ export const spec = { nBid.requestId = nBid.impid; nBid.width = nBid.w || width; nBid.height = nBid.h || height; - nBid.ttl = 360; + nBid.ttl = 300; nBid.mediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner'; if (nBid.mediaType) { nBid.vastXml = cleanVast(nBid.adm, nBid.nurl); From 4f2af660066308f166b10da2c11220205f45ab3b Mon Sep 17 00:00:00 2001 From: bretg Date: Thu, 28 Jan 2021 09:31:36 -0500 Subject: [PATCH 0556/1476] browsi: updating test parameters (#6048) --- modules/browsiRtdProvider.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/browsiRtdProvider.md b/modules/browsiRtdProvider.md index 0dd8c1d7609..9eed4b2b2d4 100644 --- a/modules/browsiRtdProvider.md +++ b/modules/browsiRtdProvider.md @@ -19,9 +19,9 @@ Configuration example for using RTD module with `browsi` provider "name": "browsi", "waitForIt": "true" "params": { - "url": "testUrl.com", - "siteKey": "testKey", - "pubKey": "testPub", + "url": "yield-manager.browsiprod.com", + "siteKey": "browsidemo", + "pubKey": "browsidemo" "keyName":"bv" } }] From 5f56b1875bb3072fee3645f7c582bfbb3d4b980a Mon Sep 17 00:00:00 2001 From: Amanda Dillon <41923726+agdillon@users.noreply.github.com> Date: Thu, 28 Jan 2021 08:23:51 -0700 Subject: [PATCH 0557/1476] SpotX Bid Adapter: default to 4/3 aspect ratio when response doesn't contain w or h (#6159) * Default to 4/3 aspect ratio when response doesn't contain w or h * SpotX bid adapter: reorder tests and remove extra assertions --- modules/spotxBidAdapter.js | 46 +++++++++++------------ test/spec/modules/spotxBidAdapter_spec.js | 43 +++++++++++++++++++-- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 5ed3884e977..c85a836435e 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -51,7 +51,7 @@ export const spec = { return false; } if (!utils.getBidIdParameter('slot', bid.params.outstream_options)) { - utils.logError(BIDDER_CODE + ': please define parameters slot outstream_options object in the configuration.'); + utils.logError(BIDDER_CODE + ': please define parameter slot in outstream_options object in the configuration.'); return false; } } @@ -382,7 +382,7 @@ export const spec = { } }); } catch (err) { - utils.logWarn('Prebid Error calling setRender or setEve,tHandlers on renderer', err); + utils.logWarn('Prebid Error calling setRender or setEventHandlers on renderer', err); } bid.renderer = renderer; } @@ -408,7 +408,7 @@ function createOutstreamScript(bid) { dataSpotXParams['data-spotx_content_page_url'] = bid.renderer.config.content_page_url; dataSpotXParams['data-spotx_ad_unit'] = 'incontent'; - utils.logMessage('[SPOTX][renderer] Default beahavior'); + utils.logMessage('[SPOTX][renderer] Default behavior'); if (utils.getBidIdParameter('ad_mute', bid.renderer.config.outstream_options)) { dataSpotXParams['data-spotx_ad_mute'] = '1'; } @@ -419,30 +419,26 @@ function createOutstreamScript(bid) { const playersizeAutoAdapt = utils.getBidIdParameter('playersize_auto_adapt', bid.renderer.config.outstream_options); if (playersizeAutoAdapt && utils.isBoolean(playersizeAutoAdapt) && playersizeAutoAdapt === true) { - if (bid.width && utils.isNumber(bid.width) && bid.height && utils.isNumber(bid.height)) { - const ratio = bid.width / bid.height; - const slotClientWidth = window.document.getElementById(slot).clientWidth; - let playerWidth = bid.renderer.config.player_width; - let playerHeight = bid.renderer.config.player_height; - let contentWidth = 0; - let contentHeight = 0; - if (slotClientWidth < playerWidth) { - playerWidth = slotClientWidth; - playerHeight = playerWidth / ratio; - } - if (ratio <= 1) { - contentWidth = Math.round(playerHeight * ratio); - contentHeight = playerHeight; - } else { - contentWidth = playerWidth; - contentHeight = Math.round(playerWidth / ratio); - } - - dataSpotXParams['data-spotx_content_width'] = '' + contentWidth; - dataSpotXParams['data-spotx_content_height'] = '' + contentHeight; + const ratio = bid.width && utils.isNumber(bid.width) && bid.height && utils.isNumber(bid.height) ? bid.width / bid.height : 4 / 3; + const slotClientWidth = window.document.getElementById(slot).clientWidth; + let playerWidth = bid.renderer.config.player_width; + let playerHeight = bid.renderer.config.player_height; + let contentWidth = 0; + let contentHeight = 0; + if (slotClientWidth < playerWidth) { + playerWidth = slotClientWidth; + playerHeight = playerWidth / ratio; + } + if (ratio <= 1) { + contentWidth = Math.round(playerHeight * ratio); + contentHeight = playerHeight; } else { - utils.logWarn('[SPOTX][renderer] PlayerSize auto adapt: bid.width and bid.height are incorrect'); + contentWidth = playerWidth; + contentHeight = Math.round(playerWidth / ratio); } + + dataSpotXParams['data-spotx_content_width'] = '' + contentWidth; + dataSpotXParams['data-spotx_content_height'] = '' + contentHeight; } const customOverride = utils.getBidIdParameter('custom_override', bid.renderer.config.outstream_options); diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index fb5ce63f543..927599ac986 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -488,7 +488,7 @@ describe('the spotx adapter', function () { }); }); - describe('oustreamRender', function() { + describe('outstreamRender', function() { var serverResponse, bidderRequestObj; beforeEach(function() { @@ -545,7 +545,7 @@ describe('the spotx adapter', function () { it('should attempt to insert the EASI script', function() { var scriptTag; sinon.stub(window.document, 'getElementById').returns({ - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script }) + appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) }); var responses = spec.interpretResponse(serverResponse, bidderRequestObj); @@ -573,7 +573,7 @@ describe('the spotx adapter', function () { nodeName: 'IFRAME', contentDocument: { body: { - appendChild: sinon.stub().callsFake(function(script) { scriptTag = script }) + appendChild: sinon.stub().callsFake(function(script) { scriptTag = script; }) } } }); @@ -598,5 +598,42 @@ describe('the spotx adapter', function () { expect(scriptTag.getAttribute('data-spotx_content_height')).to.equal('300'); window.document.getElementById.restore(); }); + + 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); + + 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(); + }); + + 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); + + 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(); + }); }); }); From a926dee9e108ca4b8792ba8992a9bca7c2f42781 Mon Sep 17 00:00:00 2001 From: YerkovichM <48519843+YerkovichM@users.noreply.github.com> Date: Thu, 28 Jan 2021 18:22:54 +0200 Subject: [PATCH 0558/1476] Extended ID permissions supported by bidder (#6112) * User id bidder permission scheme * styling * prebidServer support * - * fix * prebidServerBidAdapter take eidPermissions directly from userId module * - * unit tests * - * - * update * - * - * changed pbjs_bidders to pbjsBidders * changed pbjsBidders to bidders ext.prebid.data.eidPermissions to ext.prebid.data.eidpermissions * rerun circleci * rerun circleci * omitting eidPermission entry if 'bidders' is not configured Co-authored-by: myerkovich Co-authored-by: Marko Yerkovich --- integrationExamples/gpt/userId_example.html | 1 + modules/prebidServerBidAdapter/index.js | 17 ++- modules/userId/eids.js | 22 ++++ modules/userId/index.js | 47 +++++-- plugins/eslint/validateImports.js | 3 +- .../modules/prebidServerBidAdapter_spec.js | 46 ++++--- test/spec/modules/userId_spec.js | 117 +++++++++++++++++- 7 files changed, 212 insertions(+), 41 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index fa9b1e3b5fd..71299a4a6e1 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -236,6 +236,7 @@ }, { name: "sharedId", + // bidders: ["rubicon", "sampleBidders"], // to allow this ID for specific bidders params: { syncTime: 60 // in seconds, default is 24 hours }, diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8fb512ff7e2..7274912efb5 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -12,6 +12,7 @@ import includes from 'core-js-pure/features/array/includes.js'; import { S2S_VENDORS } from './config.js'; import { ajax } from '../../src/ajax.js'; import find from 'core-js-pure/features/array/find.js'; +import { getEidPermissions } from '../userId/index.js'; const getConfig = config.getConfig; @@ -455,7 +456,7 @@ export function resetWurlMap() { } const OPEN_RTB_PROTOCOL = { - buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig) { + buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig, requestedBidders) { let imps = []; let aliases = {}; const firstBidRequest = bidRequests[0]; @@ -700,6 +701,18 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'user.ext.eids', bidUserIdAsEids); } + const eidPermissions = getEidPermissions(); + if (utils.isArray(eidPermissions) && eidPermissions.length > 0) { + if (requestedBidders && utils.isArray(requestedBidders)) { + eidPermissions.forEach(i => { + if (i.bidders) { + i.bidders = i.bidders.filter(bidder => requestedBidders.includes(bidder)) + } + }); + } + utils.deepSetValue(request, 'ext.prebid.data.eidpermissions', eidPermissions); + } + if (bidRequests) { if (firstBidRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module @@ -958,7 +971,7 @@ export function PrebidServer() { queueSync(syncBidders, gdprConsent, uspConsent, s2sBidRequest.s2sConfig); } - const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig); + const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig, requestedBidders); const requestJson = request && JSON.stringify(request); if (request && requestJson) { ajax( diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 27665de4136..80750ccaae8 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -224,3 +224,25 @@ export function createEidsArray(bidRequestUserId) { } return eids; } + +/** + * @param {SubmoduleContainer[]} submodules + */ +export function buildEidPermissions(submodules) { + let eidPermissions = []; + submodules.filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length) + .forEach(i => { + Object.keys(i.idObj).forEach(key => { + if (utils.deepAccess(i, 'config.bidders') && Array.isArray(i.config.bidders) && + utils.deepAccess(USER_IDS_CONFIG, key + '.source')) { + eidPermissions.push( + { + source: USER_IDS_CONFIG[key].source, + bidders: i.config.bidders + } + ); + } + }); + }); + return eidPermissions; +} diff --git a/modules/userId/index.js b/modules/userId/index.js index 9294311de69..8f7d2a36699 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -136,7 +136,7 @@ import { getGlobal } from '../../src/prebidGlobal.js'; import { gdprDataHandler } from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; import { module, hook } from '../../src/hook.js'; -import { createEidsArray } from './eids.js'; +import { createEidsArray, buildEidPermissions } from './eids.js'; import { getCoreStorageManager } from '../../src/storageManager.js'; const MODULE_NAME = 'User ID'; @@ -214,6 +214,10 @@ export function setStoredValue(submodule, value) { } } +export function getEidPermissions() { + return buildEidPermissions(initializedSubmodules); +} + /** * @param {SubmoduleStorage} storage * @param {String|undefined} key optional key of the value @@ -433,6 +437,26 @@ function getCombinedSubmoduleIds(submodules) { return combinedSubmoduleIds; } +/** + * This function will create a combined object for bidder with allowed subModule Ids + * @param {SubmoduleContainer[]} submodules + * @param {string} bidder + */ +function getCombinedSubmoduleIdsForBidder(submodules, bidder) { + if (!Array.isArray(submodules) || !submodules.length || !bidder) { + return {}; + } + return submodules + .filter(i => !i.config.bidders || !utils.isArray(i.config.bidders) || i.config.bidders.includes(bidder)) + .filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length) + .reduce((carry, i) => { + Object.keys(i.idObj).forEach(key => { + carry[key] = i.idObj[key]; + }); + return carry; + }, {}); +} + /** * @param {AdUnit[]} adUnits * @param {SubmoduleContainer[]} submodules @@ -441,19 +465,18 @@ function addIdDataToAdUnitBids(adUnits, submodules) { if ([adUnits].some(i => !Array.isArray(i) || !i.length)) { return; } - const combinedSubmoduleIds = getCombinedSubmoduleIds(submodules); - const combinedSubmoduleIdsAsEids = createEidsArray(combinedSubmoduleIds); - if (Object.keys(combinedSubmoduleIds).length) { - adUnits.forEach(adUnit => { - if (adUnit.bids && utils.isArray(adUnit.bids)) { - adUnit.bids.forEach(bid => { + adUnits.forEach(adUnit => { + if (adUnit.bids && utils.isArray(adUnit.bids)) { + adUnit.bids.forEach(bid => { + const combinedSubmoduleIds = getCombinedSubmoduleIdsForBidder(submodules, bid.bidder); + if (Object.keys(combinedSubmoduleIds).length) { // create a User ID object on the bid, bid.userId = combinedSubmoduleIds; - bid.userIdAsEids = combinedSubmoduleIdsAsEids; - }); - } - }); - } + bid.userIdAsEids = createEidsArray(combinedSubmoduleIds); + } + }); + } + }); } /** diff --git a/plugins/eslint/validateImports.js b/plugins/eslint/validateImports.js index a39bf9b26d5..53f4ace8381 100644 --- a/plugins/eslint/validateImports.js +++ b/plugins/eslint/validateImports.js @@ -26,7 +26,8 @@ function flagErrors(context, node, importPath) { if ( path.dirname(absImportPath) === absModulePath || ( absImportPath.startsWith(absModulePath) && - path.basename(absImportPath) === 'index.js' + path.basename(absImportPath) === 'index.js' && + path.basename(absFileDir) !== 'prebidServerBidAdapter' ) ) { context.report(node, `import "${importPath}" cannot require module entry point`); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index f17cd3ab14f..4652bbb63f7 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -953,17 +953,15 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - brealtime: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - } + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.include({ + aliases: { + brealtime: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true } }); }); @@ -985,17 +983,15 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - [alias]: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true - } + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.include({ + aliases: { + [alias]: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true } }); }); @@ -1376,7 +1372,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, foo: 'bar', targeting: { @@ -1410,7 +1406,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, targeting: { includewinners: false, @@ -1446,7 +1442,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.equal({ + expect(requestBid.ext.prebid).to.deep.include({ auctiontimestamp: 1510852447530, cache: { vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index ed592c0cba5..1ae023ae947 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -2,6 +2,7 @@ import { attachIdSystem, auctionDelay, coreStorage, + getEidPermissions, init, requestBidsHook, setStoredConsentData, @@ -70,7 +71,7 @@ describe('User ID', function () { code, mediaTypes: {banner: {}, native: {}}, sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}, {bidder: 'anotherSampleBidder', params: {placementId: 'banner-only-bidder'}}] }; } @@ -1196,6 +1197,120 @@ describe('User ID', function () { }, {adUnits}); }); + it('eidPermissions fun with bidders', function (done) { + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test222', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([sharedIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [ + { + name: 'sharedId', + bidders: [ + 'sampleBidder' + ], + storage: { + type: 'cookie', + name: 'sharedid', + expires: 28 + } + } + ] + } + }); + + requestBidsHook(function () { + const eidPermissions = getEidPermissions(); + expect(eidPermissions).to.deep.equal( + [ + {source: 'sharedid.org', bidders: ['sampleBidder']} + ] + ); + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + if (bid.bidder === 'sampleBidder') { + expect(bid).to.have.deep.nested.property('userId.sharedid'); + expect(bid.userId.sharedid.id).to.equal('test222'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'sharedid.org', + uids: [ + { + id: 'test222', + atype: 1, + ext: { + third: 'test222' + } + } + ] + }); + } + if (bid.bidder === 'anotherSampleBidder') { + expect(bid).to.not.have.deep.nested.property('userId.sharedid'); + expect(bid).to.not.have.property('userIdAsEids'); + } + }); + }); + coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('eidPermissions fun without bidders', function (done) { + coreStorage.setCookie('sharedid', JSON.stringify({ + 'id': 'test222', + 'ts': 1590525289611 + }), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([sharedIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [ + { + name: 'sharedId', + storage: { + type: 'cookie', + name: 'sharedid', + expires: 28 + } + } + ] + } + }); + + requestBidsHook(function () { + const eidPermissions = getEidPermissions(); + expect(eidPermissions).to.deep.equal( + [] + ); + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.sharedid'); + expect(bid.userId.sharedid.id).to.equal('test222'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'sharedid.org', + uids: [ + { + id: 'test222', + atype: 1, + ext: { + third: 'test222' + } + }] + }); + }); + }); + coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + it('test hook from pubProvidedId config params', function (done) { setSubmoduleRegistry([pubProvidedIdSubmodule]); init(config); From 524efda0ec579d82ee29848b77bd829aead76107 Mon Sep 17 00:00:00 2001 From: samuel-palmer-relevant-digital <77437973+samuel-palmer-relevant-digital@users.noreply.github.com> Date: Thu, 28 Jan 2021 17:27:43 +0100 Subject: [PATCH 0559/1476] Relevant Yield analytics adapter (#6195) --- modules/relevantAnalyticsAdapter.js | 33 ++++++++++++++ modules/relevantAnalyticsAdapter.md | 13 ++++++ .../modules/relevantAnalyticsAdapter_spec.js | 43 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 modules/relevantAnalyticsAdapter.js create mode 100644 modules/relevantAnalyticsAdapter.md create mode 100644 test/spec/modules/relevantAnalyticsAdapter_spec.js diff --git a/modules/relevantAnalyticsAdapter.js b/modules/relevantAnalyticsAdapter.js new file mode 100644 index 00000000000..5917262c810 --- /dev/null +++ b/modules/relevantAnalyticsAdapter.js @@ -0,0 +1,33 @@ +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; + +const relevantAnalytics = adapter({ analyticsType: 'bundle', handler: 'on' }); + +const { enableAnalytics: orgEnableAnalytics } = relevantAnalytics; + +Object.assign(relevantAnalytics, { + /** + * Save event in the global array that will be consumed later by the Relevant Yield library + */ + track: ({ eventType: ev, args }) => { + window.relevantDigital.pbEventLog.push({ ev, args, ts: new Date() }); + }, + + /** + * Before forwarding the call to the original enableAnalytics function - + * create (if needed) the global array that is used to pass events to the Relevant Yield library + * by the 'track' function above. + */ + enableAnalytics: function(...args) { + window.relevantDigital = window.relevantDigital || {}; + window.relevantDigital.pbEventLog = window.relevantDigital.pbEventLog || []; + return orgEnableAnalytics.call(this, ...args); + }, +}); + +adapterManager.registerAnalyticsAdapter({ + adapter: relevantAnalytics, + code: 'relevant', +}); + +export default relevantAnalytics; diff --git a/modules/relevantAnalyticsAdapter.md b/modules/relevantAnalyticsAdapter.md new file mode 100644 index 00000000000..e6383fa77e1 --- /dev/null +++ b/modules/relevantAnalyticsAdapter.md @@ -0,0 +1,13 @@ +# Overview + +Module Name: Relevant Yield Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: [support@relevant-digital.com](mailto:support@relevant-digital.com) + +# Description + +Analytics adapter to be used with [Relevant Yield](https://www.relevant-digital.com/relevantyield) + +Contact [sales@relevant-digital.com](mailto:sales@relevant-digital.com) for information. diff --git a/test/spec/modules/relevantAnalyticsAdapter_spec.js b/test/spec/modules/relevantAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..3e31db2d7dc --- /dev/null +++ b/test/spec/modules/relevantAnalyticsAdapter_spec.js @@ -0,0 +1,43 @@ +import relevantAnalytics from '../../../modules/relevantAnalyticsAdapter.js'; +import adapterManager from 'src/adapterManager'; +import events from 'src/events'; +import constants from 'src/constants.json' +import { expect } from 'chai'; + +describe('Relevant Analytics Adapter', () => { + beforeEach(() => { + adapterManager.enableAnalytics({ + provider: 'relevant' + }); + }); + + afterEach(() => { + relevantAnalytics.disableAnalytics(); + }); + + it('should pass all events to the global array', () => { + // Given + const testEvents = [ + { ev: constants.EVENTS.AUCTION_INIT, args: { test: 1 } }, + { ev: constants.EVENTS.BID_REQUESTED, args: { test: 2 } }, + ]; + + // When + testEvents.forEach(({ ev, args }) => ( + events.emit(ev, args) + )); + + // Then + const eventQueue = (window.relevantDigital || {}).pbEventLog; + expect(eventQueue).to.be.an('array'); + expect(eventQueue.length).to.be.at.least(testEvents.length); + + // The last events should be our test events + const myEvents = eventQueue.slice(-testEvents.length); + testEvents.forEach(({ ev, args }, idx) => { + const actualEvent = myEvents[idx]; + expect(actualEvent.ev).to.eql(ev); + expect(actualEvent.args).to.eql(args); + }); + }); +}); From 136ad4cee32bda212ceee67d92afe494ecbd16d8 Mon Sep 17 00:00:00 2001 From: pm-shashank-jain <40654031+pm-shashank-jain@users.noreply.github.com> Date: Thu, 28 Jan 2021 23:34:31 +0530 Subject: [PATCH 0560/1476] Pubmatic: fix issue where using an adUnit outstream renderer throws an error (#6152) --- modules/pubmaticBidAdapter.js | 4 ++-- modules/pubmaticBidAdapter.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index b5514ab0344..c70ecb4af27 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -861,8 +861,8 @@ export const spec = { utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); return false; } - if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU)) { - utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids outstreamAU is required. Rejecting bid: `, bid); + if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { + utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); return false; } } else { diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index 0ef89a22cbd..a34df148630 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -25,7 +25,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required, must be wrapped in quotes - oustreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream'. This value can be get by BlueBillyWig Team. + oustreamAU: 'renderer_test_pubmatic', // required if mediaTypes-> video-> context is 'outstream' and optional if renderer is defined in adUnits or in mediaType video. This value can be get by BlueBillyWig Team. adSlot: 'pubmatic_test2', // optional pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional From f61311769823d0431cd90a05c125d88905c0555e Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:51:51 +0100 Subject: [PATCH 0561/1476] Malltv Bid Adapter : added data object as a param (#6232) * Updated malltv adapter * Updated markdown * Added test for malltvBidAdapter --- modules/malltvBidAdapter.js | 7 ++- modules/malltvBidAdapter.md | 63 +++++++++++++++------- test/spec/modules/malltvBidAdapter_spec.js | 30 +++++++++-- 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 776e3f6458b..7deffe6c07a 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -31,6 +31,7 @@ export const spec = { let bidderRequestId = ''; let url = ''; let contents = []; + let data = {}; let placements = validBidRequests.map(bidRequest => { if (!propertyId) { propertyId = bidRequest.params.propertyId; } @@ -38,7 +39,8 @@ export const spec = { if (!storageId && bidRequest.params) { storageId = bidRequest.params.storageId || ''; } if (!bidderRequestId) { bidderRequestId = bidRequest.bidderRequestId; } if (!url && bidderRequest) { url = bidderRequest.refererInfo.referer; } - if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents } + if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents; } + if (Object.keys(data).length === 0 && bidRequest.params.data && Object.keys(bidRequest.params.data).length !== 0) { data = bidRequest.params.data; } let adUnitId = bidRequest.adUnitCode; let placementId = bidRequest.params.placementId; @@ -61,7 +63,8 @@ export const spec = { url: url, requestid: bidderRequestId, placements: placements, - contents: contents + contents: contents, + data: data } return [{ diff --git a/modules/malltvBidAdapter.md b/modules/malltvBidAdapter.md index 3d419fa0916..e32eb54f90f 100644 --- a/modules/malltvBidAdapter.md +++ b/modules/malltvBidAdapter.md @@ -1,45 +1,68 @@ # Overview -Module Name: MallTV Bidder Adapter Module -Type: Bidder Adapter -Maintainer: drilon@gjirafa.com +Module Name: MallTV Bidder Adapter Module + +Type: Bidder Adapter + +Maintainer: arditb@gjirafa.com # Description MallTV Bidder Adapter for Prebid.js. # Test Parameters +```js var adUnits = [ { code: 'test-div', mediaTypes: { banner: { - sizes: [[300, 250], [300, 300]] + sizes: [ + [300, 250], + [300, 300] + ] } }, - bids: [ - { - bidder: 'malltv', - params: { - propertyId: '105134', - placementId: '846832' + bids: [{ + bidder: 'malltv', + params: { + propertyId: '105134', //Required + placementId: '846832', //Required + data: { //Optional + catalogs: [{ + catalogId: 9, + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } } } - ] + }] }, { code: 'test-div', mediaTypes: { - video: { + video: { context: 'instream' - } + } }, - bids: [ - { - bidder: 'malltv', - params: { - propertyId: '105134', - placementId: '846841' + bids: [{ + bidder: 'malltv', + params: { + propertyId: '105134', //Required + placementId: '846832', //Required + data: { //Optional + catalogs: [{ + catalogId: 9, + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } } } - ] + }] } ]; +``` diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js index e1e9ad867e7..ffe08ad1a5e 100644 --- a/test/spec/modules/malltvBidAdapter_spec.js +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -34,9 +34,7 @@ describe('malltvAdapterTest', () => { it('bidRequest without propertyId or placementId', () => { expect(spec.isBidRequestValid({ bidder: 'malltv', - params: { - propertyId: '{propertyId}', - } + params: {} })).to.equal(false); }); }); @@ -46,7 +44,17 @@ describe('malltvAdapterTest', () => { 'bidder': 'malltv', 'params': { 'propertyId': '{propertyId}', - 'placementId': '{placementId}' + 'placementId': '{placementId}', + 'data': { + 'catalogs': [{ + 'catalogId': 1, + 'items': ['1', '2', '3'] + }], + 'inventory': { + 'category': ['category1', 'category2'], + 'query': ['query'] + } + } }, 'adUnitCode': 'hb-leaderboard', 'transactionId': 'b6b889bb-776c-48fd-bc7b-d11a1cf0425e', @@ -86,6 +94,20 @@ describe('malltvAdapterTest', () => { expect(requestItem.data.placements[0].sizes).to.equal('300x250'); }); }); + + it('bidRequest data param', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach((requestItem) => { + expect(requestItem.data.data).to.exist; + expect(requestItem.data.data.catalogs).to.exist; + expect(requestItem.data.data.inventory).to.exist; + expect(requestItem.data.data.catalogs.length).to.equal(1); + expect(requestItem.data.data.catalogs[0].items.length).to.equal(3); + expect(Object.keys(requestItem.data.data.inventory).length).to.equal(2); + expect(requestItem.data.data.inventory.category.length).to.equal(2); + expect(requestItem.data.data.inventory.query.length).to.equal(1); + }); + }); }); describe('interpretResponse', () => { From 25dd35c99f404390242133b84245bb5cafeb7d7b Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Mon, 1 Feb 2021 06:27:04 -0500 Subject: [PATCH 0562/1476] support setting coopSync in s2sConfig (#6213) Co-authored-by: Mark Monday --- modules/prebidServerBidAdapter/index.js | 4 +++ .../modules/prebidServerBidAdapter_spec.js | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7274912efb5..cb059b809b7 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -201,6 +201,10 @@ function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { payload.us_privacy = uspConsent; } + if (typeof _s2sConfig.coopSync === 'boolean') { + payload.coopSync = _s2sConfig.coopSync; + } + const jsonPayload = JSON.stringify(payload); ajax(s2sConfig.syncEndpoint, (response) => { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 4652bbb63f7..7ebb570823a 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2434,5 +2434,36 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.bidders).to.deep.equal(['appnexus', 'rubicon']); }); + + it('should add cooperative sync flag to cookie_sync request if property is present', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.coopSync = false; + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + + let consentConfig = { s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); + + let bidRequest = utils.deepClone(BID_REQUESTS); + + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.coopSync).to.equal(false); + }); + + it('should not add cooperative sync flag to cookie_sync request if property is not present', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + + let consentConfig = { s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); + + let bidRequest = utils.deepClone(BID_REQUESTS); + + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.coopSync).to.be.undefined; + }); }); }); From e60d10b89e2380c949c3e1d6b388d8f460107f79 Mon Sep 17 00:00:00 2001 From: bretg Date: Mon, 1 Feb 2021 14:00:12 -0500 Subject: [PATCH 0563/1476] Revert "support setting coopSync in s2sConfig (#6213)" (#6249) This reverts commit 25dd35c99f404390242133b84245bb5cafeb7d7b. --- modules/prebidServerBidAdapter/index.js | 4 --- .../modules/prebidServerBidAdapter_spec.js | 31 ------------------- 2 files changed, 35 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index cb059b809b7..7274912efb5 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -201,10 +201,6 @@ function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { payload.us_privacy = uspConsent; } - if (typeof _s2sConfig.coopSync === 'boolean') { - payload.coopSync = _s2sConfig.coopSync; - } - const jsonPayload = JSON.stringify(payload); ajax(s2sConfig.syncEndpoint, (response) => { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 7ebb570823a..4652bbb63f7 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2434,36 +2434,5 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.bidders).to.deep.equal(['appnexus', 'rubicon']); }); - - it('should add cooperative sync flag to cookie_sync request if property is present', function () { - let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.coopSync = false; - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; - - let consentConfig = { s2sConfig: cookieSyncConfig }; - config.setConfig(consentConfig); - - let bidRequest = utils.deepClone(BID_REQUESTS); - - adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.coopSync).to.equal(false); - }); - - it('should not add cooperative sync flag to cookie_sync request if property is not present', function () { - let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; - - let consentConfig = { s2sConfig: cookieSyncConfig }; - config.setConfig(consentConfig); - - let bidRequest = utils.deepClone(BID_REQUESTS); - - adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); - let requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.coopSync).to.be.undefined; - }); }); }); From 802cfd0b7f221508ebb99682acea04e424dc4460 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 2 Feb 2021 14:08:40 -0500 Subject: [PATCH 0564/1476] pbsBidAdapter: change order of client syncs (#6248) * pbsBidAdapter: change order cookie_syncs Prebid Server places cookie-sync URLs in a specific order. PBJS was pulling them off in reverse order. * moving comment * reverting coopSync --- modules/prebidServerBidAdapter/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7274912efb5..04aedb63ae6 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -223,10 +223,14 @@ function doAllSyncs(bidders, s2sConfig) { return; } - const thisSync = bidders.pop(); + // pull the syncs off the list in the order that prebid server sends them + const thisSync = bidders.shift(); + + // if PBS reports this bidder doesn't have an ID, then call the sync and recurse to the next sync entry if (thisSync.no_cookie) { doPreBidderSync(thisSync.usersync.type, thisSync.usersync.url, thisSync.bidder, utils.bind.call(doAllSyncs, null, bidders, s2sConfig), s2sConfig); } else { + // bidder already has an ID, so just recurse to the next sync entry doAllSyncs(bidders, s2sConfig); } } From 99c1256f53cf95ed75f2d85ac2a249d801836ae9 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 2 Feb 2021 22:05:38 +0100 Subject: [PATCH 0565/1476] pass a flag back to ID5 servers if abTesting was enabled by the publisher for monitoring usage of the feature (#6170) --- modules/id5IdSystem.js | 16 ++++++++++- test/spec/modules/id5IdSystem_spec.js | 41 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 45ddc85da5f..baa2ff954dc 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -58,7 +58,7 @@ export const id5IdSubmodule = { } // check for A/B testing configuration and hide ID if in Control Group - let abConfig = (config && config.params && config.params.abTesting) || { enabled: false }; + let abConfig = getAbTestingConfig(config); let controlGroup = false; if ( abConfig.enabled === true && @@ -130,6 +130,10 @@ export const id5IdSubmodule = { 'v': '$prebid.version$' }; + if (getAbTestingConfig(config).enabled === true) { + utils.deepSetValue(data, 'features.ab', 1); + } + const resp = function (callback) { const callbacks = { success: response => { @@ -282,4 +286,14 @@ 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|undefined)} + */ +function getAbTestingConfig(config) { + return (config && config.params && config.params.abTesting) || { enabled: false }; +} + submodule('userId', id5IdSubmodule); diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 24f50db9980..59fa8977fc8 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -238,6 +238,47 @@ describe('ID5 ID System', function() { expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); }); + it('should call the ID5 server with ab feature = 1 when abTesting is turned on', function () { + let id5Config = getId5FetchConfig(); + id5Config.params.abTesting = { enabled: true, controlGroupPct: 10 } + + 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.features.ab).to.eq(1); + + request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + }); + + it('should call the ID5 server without ab feature when abTesting is turned off', function () { + let id5Config = getId5FetchConfig(); + id5Config.params.abTesting = { enabled: false, controlGroupPct: 10 } + + 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.features).to.be.undefined; + + request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + }); + + it('should call the ID5 server without ab feature when when abTesting is not set', function () { + 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.features).to.be.undefined; + + request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + }); + it('should store the privacy object from the ID5 server response', function () { let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; submoduleCallback(callbackSpy); From 62e2169ec72c17ee3e7eb70e8a1263c961bf5d1a Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 3 Feb 2021 00:18:09 -0500 Subject: [PATCH 0566/1476] appneuxs Bid Adapter - add support for identitylink userId (#6245) --- modules/appnexusBidAdapter.js | 46 +++++++++----------- test/spec/modules/appnexusBidAdapter_spec.js | 8 +++- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index a3cabe62d39..6feec56e919 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -240,34 +240,17 @@ export const spec = { }); } - const criteoId = utils.deepAccess(bidRequests[0], `userId.criteoId`); - let eids = []; - if (criteoId) { - eids.push({ - source: 'criteo.com', - id: criteoId - }); - } + if (bidRequests[0].userId) { + let eids = []; - const netidId = utils.deepAccess(bidRequests[0], `userId.netId`); - if (netidId) { - eids.push({ - source: 'netid.de', - id: netidId - }); - } - - const tdid = utils.deepAccess(bidRequests[0], `userId.tdid`); - if (tdid) { - eids.push({ - source: 'adserver.org', - id: tdid, - rti_partner: 'TDID' - }); - } + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.criteoId`), 'criteo.com', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.netId`), 'netid.de', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.idl_env`), 'liveramp.com', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.tdid`), 'adserver.org', 'TDID'); - if (eids.length) { - payload.eids = eids; + if (eids.length) { + payload.eids = eids; + } } if (tags[0].publisher_id) { @@ -1042,4 +1025,15 @@ function parseMediaType(rtbBid) { } } +function addUserId(eids, id, source, rti) { + if (id) { + if (rti) { + eids.push({ source, id, rti_partner: rti }); + } else { + eids.push({ source, id }); + } + } + return eids; +} + registerBidder(spec); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 81ff5daeaeb..3a3f4effcb8 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -827,7 +827,8 @@ describe('AppNexusAdapter', function () { userId: { tdid: 'sample-userid', criteoId: 'sample-criteo-userid', - netId: 'sample-netId-userid' + netId: 'sample-netId-userid', + idl_env: 'sample-idl-userid' } }); @@ -848,6 +849,11 @@ describe('AppNexusAdapter', function () { source: 'netid.de', id: 'sample-netId-userid', }); + + expect(payload.eids).to.deep.include({ + source: 'liveramp.com', + id: 'sample-idl-userid' + }) }); it('should populate iab_support object at the root level if omid support is detected', function () { From 6a8f95341ec9a3acb29bb3cf0d7433f5006cfa93 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 3 Feb 2021 00:37:38 -0500 Subject: [PATCH 0567/1476] Update britepoolIdSystem.md (#6254) Eliot from Britepool says you can set just the api key without any params (eg ssid or hash) --- modules/britepoolIdSystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/britepoolIdSystem.md b/modules/britepoolIdSystem.md index a15f601aee3..72edbe2324b 100644 --- a/modules/britepoolIdSystem.md +++ b/modules/britepoolIdSystem.md @@ -4,7 +4,7 @@ BritePool User ID Module. For assistance setting up your module please contact u ### Prebid Params -Individual params may be set for the BritePool User ID Submodule. At least one identifier must be set in the params. +Individual params may be set for the BritePool User ID Submodule. ``` pbjs.setConfig({ userSync: { From c95427605699cdd262280de4697229f7d2a1f43e Mon Sep 17 00:00:00 2001 From: Kotaro Shikata Date: Wed, 3 Feb 2021 14:41:28 +0900 Subject: [PATCH 0568/1476] UNICORN Adapter - accept multiple formats (#6255) * enable multiple formats add version * add banner w/h * fix w/h & spec --- modules/unicornBidAdapter.js | 18 ++++++-- test/spec/modules/unicornBidAdapter_spec.js | 51 +++++++++++++++------ 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index 33e60ad2789..2d4b5b53966 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -8,6 +8,7 @@ const BIDDER_CODE = 'unicorn'; const UNICORN_ENDPOINT = 'https://ds.uncn.jp/pb/0/bid.json'; const UNICORN_DEFAULT_CURRENCY = 'JPY'; const UNICORN_PB_COOKIE_KEY = '__pb_unicorn_aud'; +const UNICORN_PB_VERSION = '1.0'; /** * Placement ID and Account ID are required. @@ -47,12 +48,12 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { bidderRequest ); const imp = validBidRequests.map(br => { - const sizes = utils.parseSizesInput(br.sizes)[0]; return { id: br.bidId, banner: { - w: sizes.split('x')[0], - h: sizes.split('x')[1] + format: makeFormat(br.sizes), + w: br.sizes[0][0], + h: br.sizes[0][1], }, tagid: utils.deepAccess(br, 'params.placementId') || br.adUnitCode, secure: 1, @@ -84,7 +85,8 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { source: { ext: { stype: 'prebid_uncn', - bidder: BIDDER_CODE + bidder: BIDDER_CODE, + prebid_version: UNICORN_PB_VERSION } }, ext: { @@ -138,6 +140,14 @@ const getUid = () => { } }; +/** + * Make imp.banner.format + * @param {Array} arr + */ +const makeFormat = arr => arr.map((s) => { + return { w: s[0], h: s[1] }; +}); + export const spec = { code: BIDDER_CODE, aliases: ['uncn'], diff --git a/test/spec/modules/unicornBidAdapter_spec.js b/test/spec/modules/unicornBidAdapter_spec.js index 4c56c37700b..dcd446b2bb0 100644 --- a/test/spec/modules/unicornBidAdapter_spec.js +++ b/test/spec/modules/unicornBidAdapter_spec.js @@ -11,12 +11,12 @@ const bidRequests = [ }, mediaTypes: { banner: { - sizes: [[300, 250]] + sizes: [[300, 250], [336, 280]] } }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'ea0aa332-a6e1-4474-8180-83720e6b87bc', - sizes: [[300, 250]], + sizes: [[300, 250], [336, 280]], bidId: '226416e6e6bf41', bidderRequestId: '1f41cbdcbe58d5', auctionId: '77987c3a-9be9-4e43-985a-26fc91d84724', @@ -81,12 +81,12 @@ const validBidRequests = [ }, mediaTypes: { banner: { - sizes: [[300, 250]] + sizes: [[300, 250], [336, 280]] } }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'fbf94ccf-f377-4201-a662-32c2feb8ab6d', - sizes: [[300, 250]], + sizes: [[300, 250], [336, 280]], bidId: '2fb90842443e24', bidderRequestId: '123ae4cc3eeb7e', auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', @@ -156,12 +156,12 @@ const bidderRequest = { }, mediaTypes: { banner: { - sizes: [[300, 250]] + sizes: [[300, 250], [336, 280]] } }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'fbf94ccf-f377-4201-a662-32c2feb8ab6d', - sizes: [[300, 250]], + sizes: [[300, 250], [336, 280]], bidId: '2fb90842443e24', bidderRequestId: '123ae4cc3eeb7e', auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', @@ -234,8 +234,18 @@ const openRTBRequest = { { id: '216255f234b602', banner: { - w: '300', - h: '250' + w: 300, + h: 250, + format: [ + { + w: 300, + h: 250 + }, + { + w: 336, + h: 280 + } + ] }, secure: 1, bidfloor: 0, @@ -244,8 +254,14 @@ const openRTBRequest = { { id: '31e2b28ced2475', banner: { - w: '300', - h: '250' + w: 300, + h: 250, + format: [ + { + w: 300, + h: 250 + } + ] }, secure: 1, bidfloor: 0, @@ -254,8 +270,14 @@ const openRTBRequest = { { id: '40a333e047a9bd', banner: { - w: '300', - h: '250' + w: 300, + h: 250, + format: [ + { + w: 300, + h: 250 + } + ] }, secure: 1, bidfloor: 0, @@ -287,7 +309,8 @@ const openRTBRequest = { source: { ext: { stype: 'prebid_uncn', - bidder: 'unicorn' + bidder: 'unicorn', + prebid_version: '1.0' } } }; @@ -378,7 +401,7 @@ const request = { method: 'POST', url: 'https://ds.uncn.jp/pb/0/bid.json', data: - '{"id":"5ebea288-f13a-4754-be6d-4ade66c68877","at":1,"imp":[{"id":"216255f234b602","banner":{"w":"300","h":"250"},"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-0"},{"id":"31e2b28ced2475","banner":{"w":"300","h":"250"},"secure":1,"bidfloor":0"tagid":"/19968336/header-bid-tag-1"},{"id":"40a333e047a9bd","banner":{"w":"300","h":"250"},"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-2"}],"cur":"JPY","site":{"id":"uni-corn.net","publisher":{"id":12345},"domain":"uni-corn.net","page":"https://uni-corn.net/","ref":"https://uni-corn.net/"},"device":{"language":"ja","ua":"Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A5000) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36"},"user":{"id":"69d9e1c2-801e-4901-a665-fad467550fec"},"bcat":[],"source":{"ext":{"stype":"prebid_uncn","bidder":"unicorn"}}}' + '{"id":"5ebea288-f13a-4754-be6d-4ade66c68877","at":1,"imp":[{"id":"216255f234b602","banner":{"w":300,"h":250},"format":[{"w":300,"h":250},{"w":336,"h":280}],"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-0"},{"id":"31e2b28ced2475","banner":{"w":"300","h":"250"},"format":[{"w":"300","h":"250"}],"secure":1,"bidfloor":0"tagid":"/19968336/header-bid-tag-1"},{"id":"40a333e047a9bd","banner":{"w":300,"h":250},"format":[{"w":300,"h":250}],"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-2"}],"cur":"JPY","site":{"id":"uni-corn.net","publisher":{"id":12345},"domain":"uni-corn.net","page":"https://uni-corn.net/","ref":"https://uni-corn.net/"},"device":{"language":"ja","ua":"Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A5000) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36"},"user":{"id":"69d9e1c2-801e-4901-a665-fad467550fec"},"bcat":[],"source":{"ext":{"stype":"prebid_uncn","bidder":"unicorn","prebid_version":"1.0"}}}' }; const interpretedBids = [ From 8c0c7ab844c3ffff41fcc8350f9a98cb71d99c92 Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Wed, 3 Feb 2021 06:46:59 +0100 Subject: [PATCH 0569/1476] ATS-analytics - add comment clarifying ownership of atsAnalytics (#6257) --- modules/atsAnalyticsAdapter.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index 3e618692725..31e46dead89 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -7,6 +7,11 @@ import {getStorageManager} from '../src/storageManager.js'; export const storage = getStorageManager(); +/** + * Analytics adapter for - https://liveramp.com + * Maintainer - prebid@liveramp.com + */ + const analyticsType = 'endpoint'; // dev endpoints // const preflightUrl = 'https://analytics-check.publishersite.xyz/check/'; From 951f1e459cd5c619ab077dfc7981bb32e611ae6d Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 3 Feb 2021 00:52:59 -0500 Subject: [PATCH 0570/1476] [ParrableIdSystem] Ensure base64 payload is url-safe (#6258) * Added url safe base64 encoding * Added url safe base64 encoding test Co-authored-by: Victor --- modules/parrableIdSystem.js | 11 ++++++++++- test/spec/modules/parrableIdSystem_spec.js | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 7587962c62b..9aa2b251f2c 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -70,6 +70,15 @@ function isValidConfig(configParams) { return true; } +function encodeBase64UrlSafe(base64) { + const ENC = { + '+': '-', + '/': '_', + '=': '.' + }; + return base64.replace(/[+/=]/g, (m) => ENC[m]); +} + function readCookie() { const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); if (parrableIdStr) { @@ -182,7 +191,7 @@ function fetchId(configParams) { }; const searchParams = { - data: btoa(JSON.stringify(data)), + data: encodeBase64UrlSafe(btoa(JSON.stringify(data))), _rand: Math.random() }; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 5e62af9b2fa..6d0748b592d 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -136,6 +136,19 @@ describe('Parrable ID System', function() { expect(server.requests[0].url).to.contain('us_privacy=' + uspString); }); + it('xhr base64 safely encodes url data object', function() { + const urlSafeBase64EncodedData = '-_.'; + const btoaStub = sinon.stub(window, 'btoa').returns('+/='); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); + + getIdResult.callback(callbackSpy); + + let request = server.requests[0]; + let queryParams = utils.parseQS(request.url.split('?')[1]); + expect(queryParams.data).to.equal(urlSafeBase64EncodedData); + btoaStub.restore(); + }); + it('should log an error and continue to callback if ajax request errors', function () { let callBackSpy = sinon.spy(); let submoduleCallback = parrableIdSubmodule.getId({ params: {partner: 'prebid'} }).callback; From 17ce37606611012726576150bd1f185295f0032e Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Wed, 3 Feb 2021 06:57:21 +0100 Subject: [PATCH 0571/1476] Keywords + Screen resolution + CPU Core (#6259) Co-authored-by: sgimenez --- modules/richaudienceBidAdapter.js | 13 ++++++++++++- modules/richaudienceBidAdapter.md | 1 + test/spec/modules/richaudienceBidAdapter_spec.js | 7 +++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index a6b4202fc91..07de3e40594 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -49,7 +49,10 @@ export const spec = { timeout: config.getConfig('bidderTimeout'), user: raiSetEids(bid), demand: raiGetDemandType(bid), - videoData: raiGetVideoInfo(bid) + videoData: raiGetVideoInfo(bid), + scr_rsl: raiGetResolution(), + cpuc: (typeof window.navigator != 'undefined' ? window.navigator.hardwareConcurrency : null), + kws: (!utils.isEmpty(bid.params.keywords) ? bid.params.keywords : null) }; REFERER = (typeof bidderRequest.refererInfo.referer != 'undefined' ? encodeURIComponent(bidderRequest.refererInfo.referer) : null) @@ -242,3 +245,11 @@ function renderAd(bid) { window.raParams(raPlayerHB, raOutstreamHBPassback, true); } + +function raiGetResolution() { + let resolution = ''; + if (typeof window.screen != 'undefined') { + resolution = window.screen.width + 'x' + window.screen.height; + } + return resolution; +} diff --git a/modules/richaudienceBidAdapter.md b/modules/richaudienceBidAdapter.md index 932cdb8f8de..fbf59a0208a 100644 --- a/modules/richaudienceBidAdapter.md +++ b/modules/richaudienceBidAdapter.md @@ -39,6 +39,7 @@ Please reach out to your account manager for more information. "pid":"ADb1f40rmo", "supplyType":"site", "bidfloor":0.40, + "keywords": "bici=scott;coche=audi;coche=mercedes;" } }] } diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 90723fb863f..3b9f07b6efc 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -4,7 +4,6 @@ import { spec } from 'modules/richaudienceBidAdapter.js'; import {config} from 'src/config.js'; -import * as utils from 'src/utils.js'; describe('Richaudience adapter tests', function () { var DEFAULT_PARAMS_NEW_SIZES = [{ @@ -20,7 +19,8 @@ describe('Richaudience adapter tests', function () { params: { bidfloor: 0.5, pid: 'ADb1f40rmi', - supplyType: 'site' + supplyType: 'site', + keywords: 'coche=mercedes;coche=audi' }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, @@ -240,6 +240,9 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('transactionId').and.to.equal('29df2112-348b-4961-8863-1b33684d95e6'); expect(requestContent).to.have.property('timeout').and.to.equal(3000); expect(requestContent).to.have.property('numIframes').and.to.equal(0); + expect(typeof requestContent.scr_rsl === 'string') + expect(typeof requestContent.cpuc === 'number') + expect(requestContent).to.have.property('kws').and.to.equal('coche=mercedes;coche=audi'); }) it('Verify build request to prebid video inestream', function() { From 3e99b0dc100d23d50eba7f10b0c62ad334b1284b Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 3 Feb 2021 04:17:03 -0800 Subject: [PATCH 0572/1476] Rubicon Analytics: Fire event once gptSlots render (#6241) * Once all gam slots are back fire event * push to cmd queue --- modules/rubiconAnalyticsAdapter.js | 71 +++++++++---------- .../modules/rubiconAnalyticsAdapter_spec.js | 27 ------- 2 files changed, 33 insertions(+), 65 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index af5228b8b92..644a7d24e16 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -458,47 +458,35 @@ function updateRpaCookie() { return decodedRpaCookie; } -const gamEventFunctions = { - 'slotOnload': (auctionId, bid) => { - cache.auctions[auctionId].gamHasRendered[bid.adUnit.adUnitCode] = true; - }, - 'slotRenderEnded': (auctionId, bid, event) => { - if (event.isEmpty) { - cache.auctions[auctionId].gamHasRendered[bid.adUnit.adUnitCode] = true; - } - bid.adUnit.gam = utils.pick(event, [ - // these come in as `null` from Gpt, which when stringified does not get removed - // so set explicitly to undefined when not a number - 'advertiserId', advertiserId => utils.isNumber(advertiserId) ? advertiserId : undefined, - 'creativeId', creativeId => utils.isNumber(creativeId) ? creativeId : undefined, - 'lineItemId', lineItemId => utils.isNumber(lineItemId) ? lineItemId : undefined, - 'adSlot', () => event.slot.getAdUnitPath(), - 'isSlotEmpty', () => event.isEmpty || undefined - ]); - } -} - function subscribeToGamSlots() { - ['slotOnload', 'slotRenderEnded'].forEach(eventName => { - window.googletag.pubads().addEventListener(eventName, event => { - const isMatchingAdSlot = utils.isAdUnitCodeMatchingSlot(event.slot); - // loop through auctions and adUnits and mark the info - Object.keys(cache.auctions).forEach(auctionId => { - (Object.keys(cache.auctions[auctionId].bids) || []).forEach(bidId => { - let bid = cache.auctions[auctionId].bids[bidId]; - // if this slot matches this bids adUnit, add the adUnit info - if (isMatchingAdSlot(bid.adUnit.adUnitCode)) { - // mark this adUnit as having been rendered by gam - gamEventFunctions[eventName](auctionId, bid, event); - } - }); - // Now if all adUnits have gam rendered, send the payload - if (rubiConf.waitForGamSlots && !cache.auctions[auctionId].sent && Object.keys(cache.auctions[auctionId].gamHasRendered).every(adUnitCode => cache.auctions[auctionId].gamHasRendered[adUnitCode])) { - clearTimeout(cache.timeouts[auctionId]); - delete cache.timeouts[auctionId]; - sendMessage.call(rubiconAdapter, auctionId); + window.googletag.pubads().addEventListener('slotRenderEnded', event => { + const isMatchingAdSlot = utils.isAdUnitCodeMatchingSlot(event.slot); + // loop through auctions and adUnits and mark the info + Object.keys(cache.auctions).forEach(auctionId => { + (Object.keys(cache.auctions[auctionId].bids) || []).forEach(bidId => { + let bid = cache.auctions[auctionId].bids[bidId]; + // if this slot matches this bids adUnit, add the adUnit info + if (isMatchingAdSlot(bid.adUnit.adUnitCode)) { + // mark this adUnit as having been rendered by gam + cache.auctions[auctionId].gamHasRendered[bid.adUnit.adUnitCode] = true; + + bid.adUnit.gam = utils.pick(event, [ + // these come in as `null` from Gpt, which when stringified does not get removed + // so set explicitly to undefined when not a number + 'advertiserId', advertiserId => utils.isNumber(advertiserId) ? advertiserId : undefined, + 'creativeId', creativeId => utils.isNumber(creativeId) ? creativeId : undefined, + 'lineItemId', lineItemId => utils.isNumber(lineItemId) ? lineItemId : undefined, + 'adSlot', () => event.slot.getAdUnitPath(), + 'isSlotEmpty', () => event.isEmpty || undefined + ]); } }); + // Now if all adUnits have gam rendered, send the payload + if (rubiConf.waitForGamSlots && !cache.auctions[auctionId].sent && Object.keys(cache.auctions[auctionId].gamHasRendered).every(adUnitCode => cache.auctions[auctionId].gamHasRendered[adUnitCode])) { + clearTimeout(cache.timeouts[auctionId]); + delete cache.timeouts[auctionId]; + sendMessage.call(rubiconAdapter, auctionId); + } }); }); } @@ -576,6 +564,13 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { if (!cache.gpt.registered && utils.isGptPubadsDefined()) { subscribeToGamSlots(); cache.gpt.registered = true; + } else if (!cache.gpt.registered) { + cache.gpt.registered = true; + let googletag = window.googletag || {}; + googletag.cmd = googletag.cmd || []; + googletag.cmd.push(function() { + subscribeToGamSlots(); + }); } break; case BID_REQUESTED: diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 4bb2f6e217c..d8eb4fb0bdc 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1361,7 +1361,6 @@ describe('rubicon analytics adapter', function () { describe('with googletag enabled', function () { let gptSlot0, gptSlot1; let gptSlotRenderEnded0, gptSlotRenderEnded1; - let gptslotOnload0, gptslotOnload1; beforeEach(function () { mockGpt.enable(); gptSlot0 = mockGpt.makeSlot({code: '/19968336/header-bid-tag-0'}); @@ -1375,16 +1374,6 @@ describe('rubicon analytics adapter', function () { lineItemId: 3333 } }; - gptslotOnload0 = { - eventName: 'slotOnload', - params: { - slot: gptSlot0, - isEmpty: false, - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333 - } - }; gptSlot1 = mockGpt.makeSlot({code: '/19968336/header-bid-tag1'}); gptSlotRenderEnded1 = { @@ -1397,16 +1386,6 @@ describe('rubicon analytics adapter', function () { lineItemId: 6666 } }; - gptslotOnload1 = { - eventName: 'slotOnload', - params: { - slot: gptSlot1, - isEmpty: false, - advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333 - } - }; }); afterEach(function () { @@ -1536,12 +1515,6 @@ describe('rubicon analytics adapter', function () { mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params); mockGpt.emitEvent(gptSlotRenderEnded1.eventName, gptSlotRenderEnded1.params); - expect(server.requests.length).to.equal(0); - - // now emit slotOnload and it should send - mockGpt.emitEvent(gptslotOnload0.eventName, gptslotOnload0.params); - mockGpt.emitEvent(gptslotOnload1.eventName, gptslotOnload1.params); - expect(server.requests.length).to.equal(1); let request = server.requests[0]; let message = JSON.parse(request.requestBody); From d1900a9bb0070880890e0ab60311a8ad29ce60ab Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 3 Feb 2021 04:19:04 -0800 Subject: [PATCH 0573/1476] New dimension for tracking name of the matching adUnit pattern (#6252) --- modules/rubiconAnalyticsAdapter.js | 6 ++-- .../modules/rubiconAnalyticsAdapter_spec.js | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 644a7d24e16..679e86aa0a0 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -185,7 +185,8 @@ function sendMessage(auctionId, bidWonId) { 'dimensions', 'adserverTargeting', () => stringProperties(cache.targeting[bid.adUnit.adUnitCode] || {}), 'gam', - 'pbAdSlot' + 'pbAdSlot', + 'pattern' ]); adUnit.bids = []; adUnit.status = 'no-bid'; // default it to be no bid @@ -651,7 +652,8 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { return {adSlot: bid.fpd.context.adServer.adSlot} } }, - 'pbAdSlot', () => utils.deepAccess(bid, 'fpd.context.pbAdSlot') + 'pbAdSlot', () => utils.deepAccess(bid, 'fpd.context.pbAdSlot'), + 'pattern', () => utils.deepAccess(bid, 'fpd.context.aupName') ]) ]); return memo; diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index d8eb4fb0bdc..16d25ec400c 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1669,6 +1669,35 @@ describe('rubicon analytics adapter', function () { expect(timedOutBid).to.not.have.property('bidResponse'); }); + it('should pass aupName as pattern', function () { + let bidRequest = utils.deepClone(MOCK.BID_REQUESTED); + bidRequest.bids[0].fpd = { + context: { + aupName: '1234/mycoolsite/*&gpt_leaderboard&deviceType=mobile' + } + }; + bidRequest.bids[1].fpd = { + context: { + aupName: '1234/mycoolsite/*&gpt_skyscraper&deviceType=mobile' + } + }; + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, bidRequest); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + + clock.tick(SEND_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].pattern).to.equal('1234/mycoolsite/*&gpt_leaderboard&deviceType=mobile'); + expect(message.auctions[0].adUnits[1].pattern).to.equal('1234/mycoolsite/*&gpt_skyscraper&deviceType=mobile'); + }); + it('should successfully convert bid price to USD in parseBidResponse', function () { // Set the rates setConfig({ From f704369adf1aece9ad6fa887bd7135e944107f74 Mon Sep 17 00:00:00 2001 From: mefjush Date: Wed, 3 Feb 2021 14:26:47 +0100 Subject: [PATCH 0574/1476] Adhese Bid Adapter: Per adunit targets (#6256) * adpod category support test * Revert "adpod category support test" --- modules/adheseBidAdapter.js | 13 +++++++++---- test/spec/modules/adheseBidAdapter_spec.js | 14 ++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 80758668a95..5510db62950 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -22,15 +22,19 @@ export const spec = { } const { gdprConsent, refererInfo } = bidderRequest; - const targets = validBidRequests.map(bid => bid.params.data).reduce(mergeTargets, {}); const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; const id5Params = (getId5Id(validBidRequests)) ? { x5: [getId5Id(validBidRequests)] } : {}; - const slots = validBidRequests.map(bid => ({ slotname: bidToSlotName(bid) })); + const commonParams = { ...gdprParams, ...refererParams, ...id5Params }; + + const slots = validBidRequests.map(bid => ({ + slotname: bidToSlotName(bid), + parameters: cleanTargets(bid.params.data) + })); const payload = { slots: slots, - parameters: { ...targets, ...gdprParams, ...refererParams, ...id5Params } + parameters: commonParams } const account = getAccount(validBidRequests); @@ -110,7 +114,8 @@ function adResponse(bid, ad) { return bidResponse; } -function mergeTargets(targets, target) { +function cleanTargets(target) { + const targets = {}; if (target) { Object.keys(target).forEach(function (key) { const val = target[key]; diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 4d888db269d..0089a403749 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -74,31 +74,33 @@ describe('AdheseAdapter', function () { it('should include requested slots', function () { let req = spec.buildRequests([ minimalBid() ], bidderRequest); - expect(JSON.parse(req.data).slots).to.deep.include({ 'slotname': '_main_page_-leaderboard' }); + expect(JSON.parse(req.data).slots[0].slotname).to.equal('_main_page_-leaderboard'); }); it('should include all extra bid params', function () { let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }) ], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'ag': [ '25' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }); }); - it('should include duplicate bid params once', function () { + it('should assign bid params per slot', function () { let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' }) ], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({'ag': ['25']}).and.to.deep.include({ 'ci': [ 'gent' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }).and.not.to.deep.include({ 'ci': [ 'gent' ] }); + expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ag': [ '25' ] }).and.to.deep.include({ 'ci': [ 'gent' ] }); }); it('should split multiple target values', function () { let req = spec.buildRequests([ bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' }) ], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'ci': [ 'london', 'gent' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ci': [ 'london' ] }); + expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ci': [ 'gent' ] }); }); it('should filter out empty params', function () { let req = spec.buildRequests([ bidWithParams({ 'aa': [], 'bb': null, 'cc': '', 'dd': [ '', '' ], 'ee': [ 0, 1, null ], 'ff': 0, 'gg': [ 'x', 'y', '' ] }) ], bidderRequest); - let params = JSON.parse(req.data).parameters; + let params = JSON.parse(req.data).slots[0].parameters; expect(params).to.not.have.any.keys('aa', 'bb', 'cc', 'dd'); expect(params).to.deep.include({ 'ee': [ 0, 1 ], 'ff': [ 0 ], 'gg': [ 'x', 'y' ] }); }); From 97e5351c2ed0900557bc09277cce33a4db1a4678 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Wed, 3 Feb 2021 15:03:46 +0100 Subject: [PATCH 0575/1476] Gjirafa Bid Adapter: added data object as a param (#6231) * Added data parameter to gjirafaBidAdapter * Updated gjirafaBidAdapter markdown * Added test for gjirafaBidAdapter --- modules/gjirafaBidAdapter.js | 7 ++- modules/gjirafaBidAdapter.md | 62 ++++++++++++++------- test/spec/modules/gjirafaBidAdapter_spec.js | 30 ++++++++-- 3 files changed, 73 insertions(+), 26 deletions(-) diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index fc1232436b4..77589cd9071 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -31,6 +31,7 @@ export const spec = { let bidderRequestId = ''; let url = ''; let contents = []; + let data = {}; let placements = validBidRequests.map(bidRequest => { if (!propertyId) { propertyId = bidRequest.params.propertyId; } @@ -38,7 +39,8 @@ export const spec = { if (!storageId && bidRequest.params) { storageId = bidRequest.params.storageId || ''; } if (!bidderRequestId) { bidderRequestId = bidRequest.bidderRequestId; } if (!url && bidderRequest) { url = bidderRequest.refererInfo.referer; } - if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents } + if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents; } + if (Object.keys(data).length === 0 && bidRequest.params.data && Object.keys(bidRequest.params.data).length !== 0) { data = bidRequest.params.data; } let adUnitId = bidRequest.adUnitCode; let placementId = bidRequest.params.placementId; @@ -61,7 +63,8 @@ export const spec = { url: url, requestid: bidderRequestId, placements: placements, - contents: contents + contents: contents, + data: data } return [{ diff --git a/modules/gjirafaBidAdapter.md b/modules/gjirafaBidAdapter.md index deb06e74a27..fb4960d61f6 100644 --- a/modules/gjirafaBidAdapter.md +++ b/modules/gjirafaBidAdapter.md @@ -1,45 +1,67 @@ # Overview -Module Name: Gjirafa Bidder Adapter Module -Type: Bidder Adapter -Maintainer: drilon@gjirafa.com +Module Name: Gjirafa Bidder Adapter Module + +Type: Bidder Adapter + +Maintainer: arditb@gjirafa.com # Description Gjirafa Bidder Adapter for Prebid.js. # Test Parameters +```js var adUnits = [ { code: 'test-div', mediaTypes: { banner: { - sizes: [[728, 90]] + sizes: [ + [728, 90] + ] } }, - bids: [ - { - bidder: 'gjirafa', - params: { - propertyId: '105227', - placementId: '846841' + bids: [{ + bidder: 'gjirafa', + params: { + propertyId: '105227', //Required + placementId: '846841', //Required + data: { //Optional + catalogs: [{ + catalogId: 9, + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } } } - ] + }] }, { code: 'test-div', mediaTypes: { - video: { + video: { context: 'instream' - } + } }, - bids: [ - { - bidder: 'gjirafa', - params: { - propertyId: '105227', - placementId: '846836' + bids: [{ + bidder: 'gjirafa', + params: { + propertyId: '105227', //Required + placementId: '846836', //Required + data: { //Optional + catalogs: [{ + catalogId: 9, + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } } } - ] + }] } ]; +``` diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index db9b82e0a10..f0fb01f4398 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -34,9 +34,7 @@ describe('gjirafaAdapterTest', () => { it('bidRequest without propertyId or placementId', () => { expect(spec.isBidRequestValid({ bidder: 'gjirafa', - params: { - propertyId: '{propertyId}', - } + params: {} })).to.equal(false); }); }); @@ -46,7 +44,17 @@ describe('gjirafaAdapterTest', () => { 'bidder': 'gjirafa', 'params': { 'propertyId': '{propertyId}', - 'placementId': '{placementId}' + 'placementId': '{placementId}', + 'data': { + 'catalogs': [{ + 'catalogId': 1, + 'items': ['1', '2', '3'] + }], + 'inventory': { + 'category': ['category1', 'category2'], + 'query': ['query'] + } + } }, 'adUnitCode': 'hb-leaderboard', 'transactionId': 'b6b889bb-776c-48fd-bc7b-d11a1cf0425e', @@ -86,6 +94,20 @@ describe('gjirafaAdapterTest', () => { expect(requestItem.data.placements[0].sizes).to.equal('728x90'); }); }); + + it('bidRequest data param', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach((requestItem) => { + expect(requestItem.data.data).to.exist; + expect(requestItem.data.data.catalogs).to.exist; + expect(requestItem.data.data.inventory).to.exist; + expect(requestItem.data.data.catalogs.length).to.equal(1); + expect(requestItem.data.data.catalogs[0].items.length).to.equal(3); + expect(Object.keys(requestItem.data.data.inventory).length).to.equal(2); + expect(requestItem.data.data.inventory.category.length).to.equal(2); + expect(requestItem.data.data.inventory.query.length).to.equal(1); + }); + }); }); describe('interpretResponse', () => { From f8d42badebf950b7749f46de7ade1ce99a295fbe Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Wed, 3 Feb 2021 17:28:25 +0200 Subject: [PATCH 0576/1476] Fix a TypeError when message event source is not available (#6224) --- modules/inskinBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js index 2a55b5280db..3951e27c870 100644 --- a/modules/inskinBidAdapter.js +++ b/modules/inskinBidAdapter.js @@ -188,7 +188,7 @@ export const spec = { const id = 'ism_tag_' + Math.floor((Math.random() * 10e16)); window[id] = { - plr_AdSlot: e.source.frameElement, + plr_AdSlot: e.source && e.source.frameElement, bidId: e.data.bidId, bidPrice: bidsMap[e.data.bidId].price, serverResponse From 5504f12a3a29dc8f44a147443dca72fc9c214edc Mon Sep 17 00:00:00 2001 From: harpere Date: Wed, 3 Feb 2021 13:10:41 -0500 Subject: [PATCH 0577/1476] fix broken tests due to "encoded" base64 logic (#6268) --- test/spec/modules/parrableIdSystem_spec.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 6d0748b592d..046ab3a4005 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -80,6 +80,15 @@ describe('Parrable ID System', function() { let logErrorStub; let callbackSpy = sinon.spy(); + let decodeBase64UrlSafe = function (encBase64) { + const DEC = { + '-': '+', + '_': '/', + '.': '=' + }; + return encBase64.replace(/[-_.]/g, (m) => DEC[m]); + } + beforeEach(function() { logErrorStub = sinon.stub(utils, 'logError'); callbackSpy.resetHistory(); @@ -98,7 +107,7 @@ describe('Parrable ID System', function() { let request = server.requests[0]; let queryParams = utils.parseQS(request.url.split('?')[1]); - let data = JSON.parse(atob(queryParams.data)); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); expect(getIdResult.callback).to.be.a('function'); expect(request.url).to.contain('h.parrable.com'); From eed6db7cfa79e92ff030ca1154c04ce0b818fa96 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Wed, 3 Feb 2021 20:19:45 +0200 Subject: [PATCH 0578/1476] Adkernel Bid Adapter: stringads alias added (#6262) * Adkernel: stringads alias --- modules/adkernelBidAdapter.js | 2 +- test/spec/modules/adkernelBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 1b238f8d1c4..20ed65fe2e2 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 0bc14c877ab..4454aa00a71 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,7 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(10); + expect(spec.aliases).to.have.lengthOf(11); }); }); From 4d1e906c6a627fc7f249f8e8b9d2f2dd390963ec Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 3 Feb 2021 14:33:17 -0500 Subject: [PATCH 0579/1476] Revert "Extended ID permissions supported by bidder (#6112)" (#6269) This reverts commit a926dee9e108ca4b8792ba8992a9bca7c2f42781. --- integrationExamples/gpt/userId_example.html | 1 - modules/prebidServerBidAdapter/index.js | 17 +-- modules/userId/eids.js | 22 ---- modules/userId/index.js | 47 ++----- plugins/eslint/validateImports.js | 3 +- .../modules/prebidServerBidAdapter_spec.js | 46 +++---- test/spec/modules/userId_spec.js | 117 +----------------- 7 files changed, 41 insertions(+), 212 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 71299a4a6e1..fa9b1e3b5fd 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -236,7 +236,6 @@ }, { name: "sharedId", - // bidders: ["rubicon", "sampleBidders"], // to allow this ID for specific bidders params: { syncTime: 60 // in seconds, default is 24 hours }, diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 04aedb63ae6..d878b520c78 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -12,7 +12,6 @@ import includes from 'core-js-pure/features/array/includes.js'; import { S2S_VENDORS } from './config.js'; import { ajax } from '../../src/ajax.js'; import find from 'core-js-pure/features/array/find.js'; -import { getEidPermissions } from '../userId/index.js'; const getConfig = config.getConfig; @@ -460,7 +459,7 @@ export function resetWurlMap() { } const OPEN_RTB_PROTOCOL = { - buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig, requestedBidders) { + buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig) { let imps = []; let aliases = {}; const firstBidRequest = bidRequests[0]; @@ -705,18 +704,6 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'user.ext.eids', bidUserIdAsEids); } - const eidPermissions = getEidPermissions(); - if (utils.isArray(eidPermissions) && eidPermissions.length > 0) { - if (requestedBidders && utils.isArray(requestedBidders)) { - eidPermissions.forEach(i => { - if (i.bidders) { - i.bidders = i.bidders.filter(bidder => requestedBidders.includes(bidder)) - } - }); - } - utils.deepSetValue(request, 'ext.prebid.data.eidpermissions', eidPermissions); - } - if (bidRequests) { if (firstBidRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module @@ -975,7 +962,7 @@ export function PrebidServer() { queueSync(syncBidders, gdprConsent, uspConsent, s2sBidRequest.s2sConfig); } - const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig, requestedBidders); + const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig); const requestJson = request && JSON.stringify(request); if (request && requestJson) { ajax( diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 80750ccaae8..27665de4136 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -224,25 +224,3 @@ export function createEidsArray(bidRequestUserId) { } return eids; } - -/** - * @param {SubmoduleContainer[]} submodules - */ -export function buildEidPermissions(submodules) { - let eidPermissions = []; - submodules.filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length) - .forEach(i => { - Object.keys(i.idObj).forEach(key => { - if (utils.deepAccess(i, 'config.bidders') && Array.isArray(i.config.bidders) && - utils.deepAccess(USER_IDS_CONFIG, key + '.source')) { - eidPermissions.push( - { - source: USER_IDS_CONFIG[key].source, - bidders: i.config.bidders - } - ); - } - }); - }); - return eidPermissions; -} diff --git a/modules/userId/index.js b/modules/userId/index.js index 8f7d2a36699..9294311de69 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -136,7 +136,7 @@ import { getGlobal } from '../../src/prebidGlobal.js'; import { gdprDataHandler } from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; import { module, hook } from '../../src/hook.js'; -import { createEidsArray, buildEidPermissions } from './eids.js'; +import { createEidsArray } from './eids.js'; import { getCoreStorageManager } from '../../src/storageManager.js'; const MODULE_NAME = 'User ID'; @@ -214,10 +214,6 @@ export function setStoredValue(submodule, value) { } } -export function getEidPermissions() { - return buildEidPermissions(initializedSubmodules); -} - /** * @param {SubmoduleStorage} storage * @param {String|undefined} key optional key of the value @@ -437,26 +433,6 @@ function getCombinedSubmoduleIds(submodules) { return combinedSubmoduleIds; } -/** - * This function will create a combined object for bidder with allowed subModule Ids - * @param {SubmoduleContainer[]} submodules - * @param {string} bidder - */ -function getCombinedSubmoduleIdsForBidder(submodules, bidder) { - if (!Array.isArray(submodules) || !submodules.length || !bidder) { - return {}; - } - return submodules - .filter(i => !i.config.bidders || !utils.isArray(i.config.bidders) || i.config.bidders.includes(bidder)) - .filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length) - .reduce((carry, i) => { - Object.keys(i.idObj).forEach(key => { - carry[key] = i.idObj[key]; - }); - return carry; - }, {}); -} - /** * @param {AdUnit[]} adUnits * @param {SubmoduleContainer[]} submodules @@ -465,18 +441,19 @@ function addIdDataToAdUnitBids(adUnits, submodules) { if ([adUnits].some(i => !Array.isArray(i) || !i.length)) { return; } - adUnits.forEach(adUnit => { - if (adUnit.bids && utils.isArray(adUnit.bids)) { - adUnit.bids.forEach(bid => { - const combinedSubmoduleIds = getCombinedSubmoduleIdsForBidder(submodules, bid.bidder); - if (Object.keys(combinedSubmoduleIds).length) { + const combinedSubmoduleIds = getCombinedSubmoduleIds(submodules); + const combinedSubmoduleIdsAsEids = createEidsArray(combinedSubmoduleIds); + if (Object.keys(combinedSubmoduleIds).length) { + adUnits.forEach(adUnit => { + if (adUnit.bids && utils.isArray(adUnit.bids)) { + adUnit.bids.forEach(bid => { // create a User ID object on the bid, bid.userId = combinedSubmoduleIds; - bid.userIdAsEids = createEidsArray(combinedSubmoduleIds); - } - }); - } - }); + bid.userIdAsEids = combinedSubmoduleIdsAsEids; + }); + } + }); + } } /** diff --git a/plugins/eslint/validateImports.js b/plugins/eslint/validateImports.js index 53f4ace8381..a39bf9b26d5 100644 --- a/plugins/eslint/validateImports.js +++ b/plugins/eslint/validateImports.js @@ -26,8 +26,7 @@ function flagErrors(context, node, importPath) { if ( path.dirname(absImportPath) === absModulePath || ( absImportPath.startsWith(absModulePath) && - path.basename(absImportPath) === 'index.js' && - path.basename(absFileDir) !== 'prebidServerBidAdapter' + path.basename(absImportPath) === 'index.js' ) ) { context.report(node, `import "${importPath}" cannot require module entry point`); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 4652bbb63f7..f17cd3ab14f 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -953,15 +953,17 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.include({ - aliases: { - brealtime: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true + + expect(requestBid.ext).to.deep.equal({ + prebid: { + aliases: { + brealtime: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true + } } }); }); @@ -983,15 +985,17 @@ describe('S2S Adapter', function () { adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.include({ - aliases: { - [alias]: 'appnexus' - }, - auctiontimestamp: 1510852447530, - targeting: { - includebidderkeys: false, - includewinners: true + + expect(requestBid.ext).to.deep.equal({ + prebid: { + aliases: { + [alias]: 'appnexus' + }, + auctiontimestamp: 1510852447530, + targeting: { + includebidderkeys: false, + includewinners: true + } } }); }); @@ -1372,7 +1376,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.include({ + expect(requestBid.ext.prebid).to.deep.equal({ auctiontimestamp: 1510852447530, foo: 'bar', targeting: { @@ -1406,7 +1410,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.include({ + expect(requestBid.ext.prebid).to.deep.equal({ auctiontimestamp: 1510852447530, targeting: { includewinners: false, @@ -1442,7 +1446,7 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); - expect(requestBid.ext.prebid).to.deep.include({ + expect(requestBid.ext.prebid).to.deep.equal({ auctiontimestamp: 1510852447530, cache: { vastxml: 'vastxml-set-though-extPrebid.cache.vastXml' diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 1ae023ae947..ed592c0cba5 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -2,7 +2,6 @@ import { attachIdSystem, auctionDelay, coreStorage, - getEidPermissions, init, requestBidsHook, setStoredConsentData, @@ -71,7 +70,7 @@ describe('User ID', function () { code, mediaTypes: {banner: {}, native: {}}, sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}, {bidder: 'anotherSampleBidder', params: {placementId: 'banner-only-bidder'}}] + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] }; } @@ -1197,120 +1196,6 @@ describe('User ID', function () { }, {adUnits}); }); - it('eidPermissions fun with bidders', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test222', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'sharedId', - bidders: [ - 'sampleBidder' - ], - storage: { - type: 'cookie', - name: 'sharedid', - expires: 28 - } - } - ] - } - }); - - requestBidsHook(function () { - const eidPermissions = getEidPermissions(); - expect(eidPermissions).to.deep.equal( - [ - {source: 'sharedid.org', bidders: ['sampleBidder']} - ] - ); - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - if (bid.bidder === 'sampleBidder') { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid.id).to.equal('test222'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [ - { - id: 'test222', - atype: 1, - ext: { - third: 'test222' - } - } - ] - }); - } - if (bid.bidder === 'anotherSampleBidder') { - expect(bid).to.not.have.deep.nested.property('userId.sharedid'); - expect(bid).to.not.have.property('userIdAsEids'); - } - }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('eidPermissions fun without bidders', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test222', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'sharedId', - storage: { - type: 'cookie', - name: 'sharedid', - expires: 28 - } - } - ] - } - }); - - requestBidsHook(function () { - const eidPermissions = getEidPermissions(); - expect(eidPermissions).to.deep.equal( - [] - ); - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid.id).to.equal('test222'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [ - { - id: 'test222', - atype: 1, - ext: { - third: 'test222' - } - }] - }); - }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - it('test hook from pubProvidedId config params', function (done) { setSubmoduleRegistry([pubProvidedIdSubmodule]); init(config); From a4c6efae61ca4966d0f8eacfd700c83c45cde0f1 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 3 Feb 2021 15:14:49 -0500 Subject: [PATCH 0580/1476] Prebid 4.25.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d23415dca0..ceea005dfe6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.25.0-pre", + "version": "4.25.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 6cbb15e0248ed1e887ab2131b03e670443632cff Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 3 Feb 2021 15:51:29 -0500 Subject: [PATCH 0581/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ceea005dfe6..d8b6af04379 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.25.0", + "version": "4.26.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 28cbd2940a8cebb75987527168220e0a0d6b5a80 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 4 Feb 2021 07:34:34 +0100 Subject: [PATCH 0582/1476] smartxBidAdapter: new Feature - Made Out-Stream Player configurable (#6239) * 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 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 50 ++++++++++++++++++---- modules/smartxBidAdapter.md | 34 ++++++++++----- test/spec/modules/smartxBidAdapter_spec.js | 2 +- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 4409e4e9dfb..804b25d1afc 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -328,16 +328,49 @@ export const spec = { } function createOutstreamScript(bid) { - // const slot = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options); + // for SmartPlay 4.12 + function scPrebidClose(ele, completeCollapsed) { + if (completeCollapsed) { + document.getElementById(ele.id).style.display = 'none'; + } + } + + const confMinAdWidth = utils.getBidIdParameter('minAdWidth', bid.renderer.config.outstream_options) || 290; + const confMaxAdWidth = utils.getBidIdParameter('maxAdWidth', bid.renderer.config.outstream_options) || 900; + const confStartOpen = utils.getBidIdParameter('startOpen', bid.renderer.config.outstream_options) || 'false'; + const confEndingScreen = utils.getBidIdParameter('endingScreen', bid.renderer.config.outstream_options) || 'true'; + const confHeaderText = utils.getBidIdParameter('headerText', bid.renderer.config.outstream_options) || ''; + const confSkipOffset = utils.getBidIdParameter('skipOffset', bid.renderer.config.outstream_options) || 0; + const confDesiredBitrate = utils.getBidIdParameter('desiredBitrate', bid.renderer.config.outstream_options) || 1600; + const elementId = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options) || bid.adUnitCode; + + // for SmartPlay 4.12 + let initCollapsed = true; + let completeCollapsed = true; + if (confStartOpen === 'true') { + initCollapsed = false; + } + if (confEndingScreen === 'true') { + completeCollapsed = false; + } + utils.logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); - const elementId = bid.adUnitCode; + let smartPlayObj = { - minAdWidth: 290, - maxAdWidth: 900, - elementLocator: { - allowInViewport: false, - minimumElementWidth: 290, - scanPixelsBelowViewport: 800 + minAdWidth: confMinAdWidth, + maxAdWidth: confMaxAdWidth, + headerText: confHeaderText, + skipOffset: confSkipOffset, + behaviourMatrix: { + init: { + 'collapsed': initCollapsed + }, + complete: { + 'collapsed': completeCollapsed + } + }, + environmentVars: { + desiredBitrate: confDesiredBitrate, }, onStartCallback: function (m, n) { try { @@ -351,6 +384,7 @@ function createOutstreamScript(bid) { }, onEndCallback: function (m, n) { try { + scPrebidClose(n, completeCollapsed); // for SmartPlay 4.12 window.sc_smartIntxtEnd(n); } catch (f) {} }, diff --git a/modules/smartxBidAdapter.md b/modules/smartxBidAdapter.md index a53af839e2b..082a36f3dde 100644 --- a/modules/smartxBidAdapter.md +++ b/modules/smartxBidAdapter.md @@ -29,11 +29,18 @@ This adapter requires setup and approval from the smartclip team. publisherId: '11986', siteId: '22860', bidfloor: 0.3, - bidfloorcur: "EUR", + bidfloorcur: 'EUR', at: 2, - cur: ["EUR"], + cur: ['EUR'], outstream_options: { - slot: 'video1' + slot: 'video1', + minAdWidth: 290, + maxAdWidth: 900, + headerText: '', + skipOffset: 0, + startOpen: 'true', + endingScreen: 'true', + desiredBitrate: 1600, }, } }], @@ -57,11 +64,18 @@ This adapter requires setup and approval from the smartclip team. publisherId: '11986', siteId: '22860', bidfloor: 0.3, - bidfloorcur: "EUR", + bidfloorcur: 'EUR', at: 2, - cur: ["EUR"], + cur: ['EUR'], outstream_options: { - slot: 'video1' + slot: 'video1', + minAdWidth: 290, + maxAdWidth: 900, + headerText: '', + skipOffset: 0, + startOpen: 'true', + endingScreen: 'true', + desiredBitrate: 1600, }, user: { data: [{ @@ -104,9 +118,9 @@ This adapter requires setup and approval from the smartclip team. publisherId: '11986', siteId: '22860', bidfloor: 0.3, - bidfloorcur: "EUR", + bidfloorcur: 'EUR', at: 2, - cur: ["EUR"] + cur: ['EUR'] } }], }]; @@ -129,9 +143,9 @@ This adapter requires setup and approval from the smartclip team. publisherId: '11986', siteId: '22860', bidfloor: 0.3, - bidfloorcur: "EUR", + bidfloorcur: 'EUR', at: 2, - cur: ["EUR"], + cur: ['EUR'], user: { data: [{ id: 'emq', diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index efc6abcc5fa..82c6642bd74 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -493,7 +493,7 @@ describe('The smartx adapter', function () { }; }); - it('should attempt to insert the EASI script', function () { + it('should attempt to insert the script', function () { var scriptTag; sinon.stub(window.document, 'getElementById').returns({ appendChild: sinon.stub().callsFake(function (script) { From 11a925d5fb13c48b369155ff4a33ed397b3c5c66 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Wed, 3 Feb 2021 22:47:02 -0800 Subject: [PATCH 0583/1476] Idl1 (#6242) * Renaming idLibrary to idImportLibrary * Renaming idLibrary to idImportLibrary Co-authored-by: skocheri --- modules/{idLibrary.js => idImportLibrary.js} | 2 +- modules/{idLibrary.md => idImportLibrary.md} | 4 ++-- .../{idLibrary_spec.js => idImportLibrary_spec.js} | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) rename modules/{idLibrary.js => idImportLibrary.js} (98%) rename modules/{idLibrary.md => idImportLibrary.md} (94%) rename test/spec/modules/{idLibrary_spec.js => idImportLibrary_spec.js} (82%) diff --git a/modules/idLibrary.js b/modules/idImportLibrary.js similarity index 98% rename from modules/idLibrary.js rename to modules/idImportLibrary.js index 0d8616c3f88..2a3a86cf270 100644 --- a/modules/idLibrary.js +++ b/modules/idImportLibrary.js @@ -240,4 +240,4 @@ export function setConfig(config) { associateIds(); } -config.getConfig('idLibrary', config => setConfig(config.idLibrary)); +config.getConfig('idImportLibrary', config => setConfig(config.idImportLibrary)); diff --git a/modules/idLibrary.md b/modules/idImportLibrary.md similarity index 94% rename from modules/idLibrary.md rename to modules/idImportLibrary.md index 28d40c389f3..3dd78ee25d8 100644 --- a/modules/idLibrary.md +++ b/modules/idImportLibrary.md @@ -1,4 +1,4 @@ -# ID Library +# ID Import Library ## Configuration Options @@ -13,7 +13,7 @@ ```javascript pbjs.setConfig({ - idLibrary: { + idImportLibrary: { target: 'username', url: 'https://example.com', debounce: 250, diff --git a/test/spec/modules/idLibrary_spec.js b/test/spec/modules/idImportLibrary_spec.js similarity index 82% rename from test/spec/modules/idLibrary_spec.js rename to test/spec/modules/idImportLibrary_spec.js index 682c2df1e44..699c2c43a94 100644 --- a/test/spec/modules/idLibrary_spec.js +++ b/test/spec/modules/idImportLibrary_spec.js @@ -1,5 +1,5 @@ import * as utils from 'src/utils.js'; -import * as idlibrary from 'modules/idLibrary.js'; +import * as idImportlibrary from 'modules/idImportLibrary.js'; var expect = require('chai').expect; @@ -20,7 +20,7 @@ describe('currency', function () { utils.logInfo.restore(); utils.logError.restore(); fakeCurrencyFileServer.restore(); - idlibrary.setConfig({}); + idImportlibrary.setConfig({}); }); describe('setConfig', function () { @@ -35,26 +35,26 @@ describe('currency', function () { }); it('results when no config available', function () { - idlibrary.setConfig({}); + idImportlibrary.setConfig({}); sinon.assert.called(utils.logError); }); it('results with config available', function () { - idlibrary.setConfig({ 'url': 'URL' }); + idImportlibrary.setConfig({ 'url': 'URL' }); sinon.assert.called(utils.logInfo); }); it('results with config default debounce ', function () { let config = { 'url': 'URL' } - idlibrary.setConfig(config); + idImportlibrary.setConfig(config); expect(config.debounce).to.be.equal(250); }); it('results with config default fullscan ', function () { let config = { 'url': 'URL' } - idlibrary.setConfig(config); + idImportlibrary.setConfig(config); expect(config.fullscan).to.be.equal(false); }); it('results with config fullscan ', function () { let config = { 'url': 'URL', 'fullscan': true } - idlibrary.setConfig(config); + idImportlibrary.setConfig(config); expect(config.fullscan).to.be.equal(true); }); }); From 4e1be70d08f2b8e9c038abc1f74c486289f7182f Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Thu, 4 Feb 2021 08:02:27 +0100 Subject: [PATCH 0584/1476] Add the trade desk gvlid (#6263) UnifiedId only works with a vendor exception in the gdpr enforcement module. However this is not okay as the unifiedId matching endpoint doesn't take the consent into account and cookies are being dropped even without consent. --- modules/unifiedIdSystem.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index 3db4003c424..bc033f37992 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -18,6 +18,10 @@ export const unifiedIdSubmodule = { * @type {string} */ name: MODULE_NAME, + /** + * required for the gdpr enforcement module + */ + gvlid: 21, /** * decode the stored id value for passing to bid requests * @function From cca20295cd6d217422e0af2c5ce311523a0a9cf6 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Thu, 4 Feb 2021 03:47:15 -0800 Subject: [PATCH 0585/1476] pick up wrapper family detail (#6272) --- modules/rubiconAnalyticsAdapter.js | 3 ++- test/spec/modules/rubiconAnalyticsAdapter_spec.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 679e86aa0a0..f216cbd6235 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -167,9 +167,10 @@ function sendMessage(auctionId, bidWonId) { referrerHostname: rubiconAdapter.referrerHostname || getHostNameFromReferer(referrer), channel: 'web', }; - if (rubiConf.wrapperName || rubiConf.rule_name) { + if (rubiConf.wrapperName) { message.wrapper = { name: rubiConf.wrapperName, + family: rubiConf.wrapperFamily, rule: rubiConf.rule_name } } diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 16d25ec400c..71e5446ed06 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1754,7 +1754,8 @@ describe('rubicon analytics adapter', function () { describe('wrapper details passed in', () => { it('should correctly pass in the wrapper details if provided', () => { config.setConfig({rubicon: { - wrapperName: '1001_wrapperName', + wrapperName: '1001_wrapperName_exp.4', + wrapperFamily: '1001_wrapperName', rule_name: 'na-mobile' }}); @@ -1771,7 +1772,8 @@ describe('rubicon analytics adapter', function () { const request = server.requests[0]; const message = JSON.parse(request.requestBody); expect(message.wrapper).to.deep.equal({ - name: '1001_wrapperName', + name: '1001_wrapperName_exp.4', + family: '1001_wrapperName', rule: 'na-mobile' }); From da13bdaa09b1725519e38cecaf55d75270c6fb2c Mon Sep 17 00:00:00 2001 From: Adprime <64427228+Adprime@users.noreply.github.com> Date: Thu, 4 Feb 2021 16:24:51 -0500 Subject: [PATCH 0586/1476] Add user sync (#6244) * initial * fix * remove redundant language mod, use player sizes in video traff * test modify * fix * Adding Tests * add keywords param * log * log * log * fix * add idl * add idl * fix test * lint * lint * fix * lint * lint * lint * lint * add sync * fix Co-authored-by: Aigolkin1991 Co-authored-by: Aiholkin --- modules/adprimeBidAdapter.js | 20 ++++++++++++++++++++ test/spec/modules/adprimeBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js index 50303b82979..12d0410a821 100644 --- a/modules/adprimeBidAdapter.js +++ b/modules/adprimeBidAdapter.js @@ -4,6 +4,7 @@ import * as utils from '../src/utils.js'; const BIDDER_CODE = 'adprime'; const AD_URL = 'https://delta.adprime.com/?c=o&m=multi'; +const SYNC_URL = 'https://delta.adprime.com/?c=rtb&m=sync'; function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -104,6 +105,25 @@ export const spec = { } return response; }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncUrl = SYNC_URL + 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}`; + } + return [{ + type: 'image', + url: syncUrl + }]; + } + }; registerBidder(spec); diff --git a/test/spec/modules/adprimeBidAdapter_spec.js b/test/spec/modules/adprimeBidAdapter_spec.js index fe05634baae..8627941dc80 100644 --- a/test/spec/modules/adprimeBidAdapter_spec.js +++ b/test/spec/modules/adprimeBidAdapter_spec.js @@ -284,4 +284,14 @@ describe('AdprimebBidAdapter', function () { expect(serverResponses).to.be.an('array').that.is.empty; }); }); + describe('getUserSyncs', function () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', 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('image'); + expect(userSync[0].url).to.be.equal('https://delta.adprime.com/?c=rtb&m=sync'); + }); + }); }); From 1cd714f53b48a327c5b134915c965ca982638357 Mon Sep 17 00:00:00 2001 From: shikharsharma-zeotap Date: Fri, 5 Feb 2021 03:01:29 +0530 Subject: [PATCH 0587/1476] Zeotap id plus gvlid (#6260) * Add gvlid for ZeotapIdPlus module * Pass gvlid and module name to storage manager * add testcases to zeotapIdPlusIdSystem * remove unwanted code --- modules/zeotapIdPlusIdSystem.js | 20 ++++-- .../spec/modules/zeotapIdPlusIdSystem_spec.js | 64 ++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index d800286b00e..8f26cc827d6 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -9,23 +9,35 @@ import {submodule} from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; const ZEOTAP_COOKIE_NAME = 'IDP'; -export const storage = getStorageManager(); +const ZEOTAP_VENDOR_ID = 301; +const ZEOTAP_MODULE_NAME = 'zeotapIdPlus'; function readCookie() { - return storage.cookiesAreEnabled ? storage.getCookie(ZEOTAP_COOKIE_NAME) : null; + return storage.cookiesAreEnabled() ? storage.getCookie(ZEOTAP_COOKIE_NAME) : null; } function readFromLocalStorage() { - return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(ZEOTAP_COOKIE_NAME) : null; + return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(ZEOTAP_COOKIE_NAME) : null; } +export function getStorage() { + return getStorageManager(ZEOTAP_VENDOR_ID, ZEOTAP_MODULE_NAME); +} + +export const storage = getStorage(); + /** @type {Submodule} */ export const zeotapIdPlusSubmodule = { /** * used to link submodule with config * @type {string} */ - name: 'zeotapIdPlus', + name: ZEOTAP_MODULE_NAME, + /** + * Vendor ID of Zeotap + * @type {Number} + */ + gvlid: ZEOTAP_VENDOR_ID, /** * decode the stored id value for passing to bid requests * @function diff --git a/test/spec/modules/zeotapIdPlusIdSystem_spec.js b/test/spec/modules/zeotapIdPlusIdSystem_spec.js index 4f9e691f12e..9de6fa843bc 100644 --- a/test/spec/modules/zeotapIdPlusIdSystem_spec.js +++ b/test/spec/modules/zeotapIdPlusIdSystem_spec.js @@ -2,7 +2,8 @@ import { expect } from 'chai'; import find from 'core-js-pure/features/array/find.js'; import { config } from 'src/config.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; -import { storage, zeotapIdPlusSubmodule } from 'modules/zeotapIdPlusIdSystem.js'; +import { storage, getStorage, zeotapIdPlusSubmodule } from 'modules/zeotapIdPlusIdSystem.js'; +import * as storageManager from 'src/storageManager.js'; const ZEOTAP_COOKIE_NAME = 'IDP'; const ZEOTAP_COOKIE = 'THIS-IS-A-DUMMY-COOKIE'; @@ -43,6 +44,67 @@ function unsetLocalStorage() { } describe('Zeotap ID System', function() { + describe('Zeotap Module invokes StorageManager with appropriate arguments', function() { + let getStorageManagerSpy; + + beforeEach(function() { + getStorageManagerSpy = sinon.spy(storageManager, 'getStorageManager'); + }); + + it('when a stored Zeotap ID exists it is added to bids', function() { + let store = getStorage(); + expect(getStorageManagerSpy.calledOnce).to.be.true; + sinon.assert.calledWith(getStorageManagerSpy, 301, 'zeotapIdPlus'); + }); + }); + + describe('test method: getId calls storage methods to fetch ID', function() { + let cookiesAreEnabledStub; + let getCookieStub; + let localStorageIsEnabledStub; + let getDataFromLocalStorageStub; + + beforeEach(() => { + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + }); + + afterEach(() => { + storage.cookiesAreEnabled.restore(); + storage.getCookie.restore(); + storage.localStorageIsEnabled.restore(); + storage.getDataFromLocalStorage.restore(); + unsetCookie(); + unsetLocalStorage(); + }); + + it('should check if cookies are enabled', function() { + let id = zeotapIdPlusSubmodule.getId(); + expect(cookiesAreEnabledStub.calledOnce).to.be.true; + }); + + it('should call getCookie if cookies are enabled', function() { + cookiesAreEnabledStub.returns(true); + let id = zeotapIdPlusSubmodule.getId(); + expect(cookiesAreEnabledStub.calledOnce).to.be.true; + expect(getCookieStub.calledOnce).to.be.true; + sinon.assert.calledWith(getCookieStub, 'IDP'); + }); + + it('should check for localStorage if cookies are disabled', function() { + cookiesAreEnabledStub.returns(false); + localStorageIsEnabledStub.returns(true) + let id = zeotapIdPlusSubmodule.getId(); + expect(cookiesAreEnabledStub.calledOnce).to.be.true; + expect(getCookieStub.called).to.be.false; + expect(localStorageIsEnabledStub.calledOnce).to.be.true; + expect(getDataFromLocalStorageStub.calledOnce).to.be.true; + sinon.assert.calledWith(getDataFromLocalStorageStub, 'IDP'); + }); + }); + describe('test method: getId', function() { afterEach(() => { unsetCookie(); From da1a6e9b284a088439b0b5442fcb252dad42d585 Mon Sep 17 00:00:00 2001 From: nyakove <43004249+nyakove@users.noreply.github.com> Date: Thu, 4 Feb 2021 23:39:00 +0200 Subject: [PATCH 0588/1476] adWMG adapter: add new parameter, fix minor bugs (#6265) * Support floorCPM parameter, fix some minor bugs * fix space-in-parens circleci error * example fix * clean usersync URL * spaces * spaces * add new unit tests, compatibility with IE11 * remove logInfo Co-authored-by: Mikhail Dykun --- modules/adWMGBidAdapter.js | 8 +++-- modules/adWMGBidAdapter.md | 9 ++--- test/spec/modules/adWMGBidAdapter_spec.js | 40 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 3a0a8a22274..87c40db51e6 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -113,14 +113,18 @@ export const spec = { return bidResponses; }, getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - if (gdprConsent) { + if (gdprConsent && SYNC_ENDPOINT.indexOf('gdpr') === -1) { SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0)); } - if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (gdprConsent && typeof gdprConsent.consentString === 'string' && SYNC_ENDPOINT.indexOf('gdpr_consent') === -1) { SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'gdpr_consent', gdprConsent.consentString); } + if (SYNC_ENDPOINT.slice(-1) === '&') { + SYNC_ENDPOINT = SYNC_ENDPOINT.slice(0, -1); + } + /* if (uspConsent) { SYNC_ENDPOINT = utils.tryAppendQueryString(SYNC_ENDPOINT, 'us_privacy', uspConsent); } */ diff --git a/modules/adWMGBidAdapter.md b/modules/adWMGBidAdapter.md index 8c277b803db..ec5541e6168 100644 --- a/modules/adWMGBidAdapter.md +++ b/modules/adWMGBidAdapter.md @@ -12,10 +12,11 @@ Module that connects to adWMG demand sources to fetch bids. Supports 'banner' ad # Bid Parameters -| Key | Required | Example | Description | -| --- | -------- | ------- | ----------- | -| `publisherId` | yes | `'5cebea3c9eea646c7b623d5e'` | publisher ID from WMG Dashboard | -| `IABCategories` | no | `['IAB1', 'IAB5']` | IAB ad categories for adUnit | +| Key | Required | Example | Description | +| --------------- | -------- | -----------------------------| ------------------------------- | +| `publisherId` | yes | `'5cebea3c9eea646c7b623d5e'` | publisher ID from WMG Dashboard | +| `IABCategories` | no | `['IAB1', 'IAB5']` | IAB ad categories for adUnit | +| `floorCPM` | no | `0.5` | Floor price for adUnit | # Test Parameters diff --git a/test/spec/modules/adWMGBidAdapter_spec.js b/test/spec/modules/adWMGBidAdapter_spec.js index 5c2364d454c..1f881897fd8 100644 --- a/test/spec/modules/adWMGBidAdapter_spec.js +++ b/test/spec/modules/adWMGBidAdapter_spec.js @@ -288,5 +288,45 @@ describe('adWMGBidAdapter', function () { expect(syncs[0].url).includes('gdpr=1'); expect(syncs[0].url).includes(`gdpr_consent=${gdprConsent.consentString}`); }); + + it('should not add GDPR consent params twice', function() { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': true + }; + const gdprConsent = { + consentString: 'CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_X_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + }; + const gdprConsent2 = { + consentString: 'CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_7_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + }; + const serverResponse = {}; + let syncs = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent); + syncs = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent2); + expect(syncs[0].url.match(/gdpr/g).length).to.equal(2); // gdpr + gdpr_consent + expect(syncs[0].url.match(/gdpr_consent/g).length).to.equal(1); + }); + + it('should delete \'&\' symbol at the end of usersync URL', function() { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': true + }; + const gdprConsent = { + consentString: 'CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_X_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA', + vendorData: {}, + gdprApplies: true, + apiVersion: 2 + }; + const serverResponse = {}; + let syncs = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent); + expect(syncs[0].url.slice(-1)).to.not.equal('&'); + }); }); }); From 0a333497fe3d2cff6be93cb6cc465eaef3eb23a3 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 4 Feb 2021 17:02:02 -0500 Subject: [PATCH 0589/1476] [ParrableIdSystem] Supply iframe state to backend (#6278) * Add iframe detection * Remove forgotten .only Co-authored-by: Victor --- modules/parrableIdSystem.js | 3 ++- test/spec/modules/parrableIdSystem_spec.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 9aa2b251f2c..32543e3863b 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -187,7 +187,8 @@ function fetchId(configParams) { const data = { eid, trackers: configParams.partner.split(','), - url: refererInfo.referer + url: refererInfo.referer, + isIframe: utils.inIframe(), }; const searchParams = { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 046ab3a4005..f4f485affe9 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -116,7 +116,8 @@ describe('Parrable ID System', function() { expect(data).to.deep.equal({ eid: P_COOKIE_EID, trackers: P_CONFIG_MOCK.params.partner.split(','), - url: getRefererInfo().referer + url: getRefererInfo().referer, + isIframe: true }); server.requests[0].respond(200, From 8f776608ac1adcd2f909db78cdd67c73f8fa0515 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Fri, 5 Feb 2021 11:48:58 -0800 Subject: [PATCH 0590/1476] Bid Viewability Module (#6206) * introducing a new event, bidViewable * new module: bidViewability * details in bidViewability.md --- modules/bidViewability.js | 97 +++++++ modules/bidViewability.md | 49 ++++ src/adapterManager.js | 4 + src/constants.json | 3 +- test/spec/modules/bidViewability_spec.js | 297 +++++++++++++++++++++ test/spec/unit/core/adapterManager_spec.js | 32 +++ 6 files changed, 481 insertions(+), 1 deletion(-) create mode 100644 modules/bidViewability.js create mode 100644 modules/bidViewability.md create mode 100644 test/spec/modules/bidViewability_spec.js diff --git a/modules/bidViewability.js b/modules/bidViewability.js new file mode 100644 index 00000000000..c3b72cda8d4 --- /dev/null +++ b/modules/bidViewability.js @@ -0,0 +1,97 @@ +// This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Bidders and Analytics adapters +// GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent +// Does not work with other than GPT integration + +import { config } from '../src/config.js'; +import * as events from '../src/events.js'; +import { EVENTS } from '../src/constants.json'; +import { logWarn, isFn, triggerPixel } from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import adapterManager, { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; +import find from 'core-js-pure/features/array/find.js'; + +const MODULE_NAME = 'bidViewability'; +const CONFIG_ENABLED = 'enabled'; +const CONFIG_FIRE_PIXELS = 'firePixels'; +const CONFIG_CUSTOM_MATCH = 'customMatchFunction'; +const BID_VURL_ARRAY = 'vurls'; +const GPT_IMPRESSION_VIEWABLE_EVENT = 'impressionViewable'; + +export let isBidAdUnitCodeMatchingSlot = (bid, slot) => { + return (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode); +} + +export let getMatchingWinningBidForGPTSlot = (globalModuleConfig, slot) => { + return find(getGlobal().getAllWinningBids(), + // supports custom match function from config + bid => isFn(globalModuleConfig[CONFIG_CUSTOM_MATCH]) + ? globalModuleConfig[CONFIG_CUSTOM_MATCH](bid, slot) + : isBidAdUnitCodeMatchingSlot(bid, slot) + ) || null; +}; + +export let fireViewabilityPixels = (globalModuleConfig, bid) => { + if (globalModuleConfig[CONFIG_FIRE_PIXELS] === true && bid.hasOwnProperty(BID_VURL_ARRAY)) { + let queryParams = {}; + + const gdprConsent = gdprDataHandler.getConsentData(); + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { queryParams.gdpr = Number(gdprConsent.gdprApplies); } + if (gdprConsent.consentString) { queryParams.gdpr_consent = gdprConsent.consentString; } + if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } + } + + const uspConsent = uspDataHandler.getConsentData(); + if (uspConsent) { queryParams.us_privacy = uspConsent; } + + bid[BID_VURL_ARRAY].forEach(url => { + // add '?' if not present in URL + if (Object.keys(queryParams).length > 0 && url.indexOf('?') === -1) { + url += '?'; + } + // append all query params, `&key=urlEncoded(value)` + url += Object.keys(queryParams).reduce((prev, key) => prev += `&${key}=${encodeURIComponent(queryParams[key])}`, ''); + triggerPixel(url) + }); + } +}; + +export let logWinningBidNotFound = (slot) => { + logWarn(`bid details could not be found for ${slot.getSlotElementId()}, probable reasons: a non-prebid bid is served OR check the prebid.AdUnit.code to GPT.AdSlot relation.`); +}; + +export let impressionViewableHandler = (globalModuleConfig, slot, event) => { + let respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot); + if (respectiveBid === null) { + logWinningBidNotFound(slot); + } else { + // if config is enabled AND VURL array is present then execute each pixel + fireViewabilityPixels(globalModuleConfig, respectiveBid); + // trigger respective bidder's onBidViewable handler + adapterManager.callBidViewableBidder(respectiveBid.bidder, respectiveBid); + // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels + events.emit(EVENTS.BID_VIEWABLE, respectiveBid); + } +}; + +export let init = () => { + events.on(EVENTS.AUCTION_INIT, () => { + // read the config for the module + const globalModuleConfig = config.getConfig(MODULE_NAME) || {}; + // do nothing if module-config.enabled is not set to true + // this way we are adding a way for bidders to know (using pbjs.getConfig('bidViewability').enabled === true) whether this module is added in build and is enabled + if (globalModuleConfig[CONFIG_ENABLED] !== true) { + return; + } + // add the GPT event listener + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + window.googletag.cmd.push(() => { + window.googletag.pubads().addEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, function(event) { + impressionViewableHandler(globalModuleConfig, event.slot, event); + }); + }); + }); +} + +init() diff --git a/modules/bidViewability.md b/modules/bidViewability.md new file mode 100644 index 00000000000..78a1539fb1a --- /dev/null +++ b/modules/bidViewability.md @@ -0,0 +1,49 @@ +# Overview + +Module Name: bidViewability + +Purpose: Track when a bid is viewable + +Maintainer: harshad.mane@pubmatic.com + +# Description +- This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement `onBidViewable` method to capture this event +- Bidderes can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')``` +- GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent . This event is fired when an impression becomes viewable, according to the Active View criteria. +Refer: https://support.google.com/admanager/answer/4524488 +- The module does not work with adserver other than GAM with GPT integration +- Logic used to find a matching pbjs-bid for a GPT slot is ``` (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ``` this logic can be changed by using param ```customMatchFunction``` +- When a rendered PBJS bid is viewable the module will trigger BID_VIEWABLE event, which can be consumed by bidders and analytics adapters +- For the viewable bid if ```bid.vurls type array``` param is and module config ``` firePixels: true ``` is set then the URLs mentioned in bid.vurls will be executed. Please note that GDPR and USP related parameters will be added to the given URLs + +# Params +- enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable +- firePixels [optional] [type: boolean], when set to true, will fire the urls mentioned in bid.vurls which should be array of urls +- customMatchFunction [optional] [type: function(bid, slot)], when passed this function will be used to `find` the matching winning bid for the GPT slot. Default value is ` (bid, slot) => (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ` + +# Example of consuming BID_VIEWABLE event +``` + pbjs.onEvent('bidViewable', function(bid){ + console.log('got bid details in bidViewable event', bid); + }); + +``` + +# Example of using config +``` + pbjs.setConfig({ + bidViewability: { + enabled: true, + firePixels: true, + customMatchFunction: function(bid, slot){ + console.log('using custom match function....'); + return bid.adUnitCode === slot.getAdUnitPath(); + } + } + }); +``` + +# Please Note: +- Doesn't seems to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative +- Works with Banner, Outsteam, Native creatives + diff --git a/src/adapterManager.js b/src/adapterManager.js index 9c3ef7ac1d1..cb84607e130 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -606,4 +606,8 @@ adapterManager.callSetTargetingBidder = function(bidder, bid) { tryCallBidderMethod(bidder, 'onSetTargeting', bid); }; +adapterManager.callBidViewableBidder = function(bidder, bid) { + tryCallBidderMethod(bidder, 'onBidViewable', bid); +}; + export default adapterManager; diff --git a/src/constants.json b/src/constants.json index 4025e084682..e6b9687f911 100644 --- a/src/constants.json +++ b/src/constants.json @@ -38,7 +38,8 @@ "ADD_AD_UNITS": "addAdUnits", "AD_RENDER_FAILED": "adRenderFailed", "TCF2_ENFORCEMENT": "tcf2Enforcement", - "AUCTION_DEBUG": "auctionDebug" + "AUCTION_DEBUG": "auctionDebug", + "BID_VIEWABLE": "bidViewable" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", diff --git a/test/spec/modules/bidViewability_spec.js b/test/spec/modules/bidViewability_spec.js new file mode 100644 index 00000000000..211dec090a5 --- /dev/null +++ b/test/spec/modules/bidViewability_spec.js @@ -0,0 +1,297 @@ +import * as bidViewability from 'modules/bidViewability.js'; +import { config } from 'src/config.js'; +import * as events from 'src/events.js'; +import * as utils from 'src/utils.js'; +import * as sinon from 'sinon'; +import {expect, spy} from 'chai'; +import * as prebidGlobal from 'src/prebidGlobal.js'; +import { EVENTS } from 'src/constants.json'; +import adapterManager, { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; +import parse from 'url-parse'; + +const GPT_SLOT = { + getAdUnitPath() { + return '/harshad/Jan/2021/'; + }, + + getSlotElementId() { + return 'DIV-1'; + } +}; + +const PBJS_WINNING_BID = { + 'adUnitCode': '/harshad/Jan/2021/', + 'bidderCode': 'pubmatic', + 'bidder': 'pubmatic', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': 'id', + 'requestId': 1024, + 'source': 'client', + 'no_bid': false, + 'cpm': '1.1495', + 'ttl': 180, + 'creativeId': 'id', + 'netRevenue': true, + 'currency': 'USD', + 'vurls': [ + 'https://domain-1.com/end-point?a=1', + 'https://domain-2.com/end-point/', + 'https://domain-3.com/end-point?a=1' + ] +}; + +describe('#bidViewability', function() { + let gptSlot; + let pbjsWinningBid; + + beforeEach(function() { + gptSlot = Object.assign({}, GPT_SLOT); + pbjsWinningBid = Object.assign({}, PBJS_WINNING_BID); + }); + + describe('isBidAdUnitCodeMatchingSlot', function() { + it('match found by GPT Slot getAdUnitPath', function() { + expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(true); + }); + + it('match found by GPT Slot getSlotElementId', function() { + pbjsWinningBid.adUnitCode = 'DIV-1'; + expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(true); + }); + + it('match not found', function() { + pbjsWinningBid.adUnitCode = 'DIV-10'; + expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(false); + }); + }); + + describe('getMatchingWinningBidForGPTSlot', function() { + let winningBidsArray; + let sandbox + beforeEach(function() { + sandbox = sinon.sandbox.create(); + // mocking winningBidsArray + winningBidsArray = []; + sandbox.stub(prebidGlobal, 'getGlobal').returns({ + getAllWinningBids: function (number) { + return winningBidsArray; + } + }); + }); + + afterEach(function() { + sandbox.restore(); + }) + + it('should find a match by using customMatchFunction provided in config', function() { + // Needs config to be passed with customMatchFunction + let bidViewabilityConfig = { + customMatchFunction(bid, slot) { + return ('AD-' + slot.getAdUnitPath()) === bid.adUnitCode; + } + }; + let newWinningBid = Object.assign({}, PBJS_WINNING_BID, {adUnitCode: 'AD-' + PBJS_WINNING_BID.adUnitCode}); + // Needs pbjs.getWinningBids to be implemented with match + winningBidsArray.push(newWinningBid); + let wb = bidViewability.getMatchingWinningBidForGPTSlot(bidViewabilityConfig, gptSlot); + expect(wb).to.deep.equal(newWinningBid); + }); + + it('should NOT find a match by using customMatchFunction provided in config', function() { + // Needs config to be passed with customMatchFunction + let bidViewabilityConfig = { + customMatchFunction(bid, slot) { + return ('AD-' + slot.getAdUnitPath()) === bid.adUnitCode; + } + }; + // Needs pbjs.getWinningBids to be implemented without match; winningBidsArray is set to empty in beforeEach + let wb = bidViewability.getMatchingWinningBidForGPTSlot(bidViewabilityConfig, gptSlot); + expect(wb).to.equal(null); + }); + + it('should find a match by using default matching function', function() { + // Needs config to be passed without customMatchFunction + // Needs pbjs.getWinningBids to be implemented with match + winningBidsArray.push(PBJS_WINNING_BID); + let wb = bidViewability.getMatchingWinningBidForGPTSlot({}, gptSlot); + expect(wb).to.deep.equal(PBJS_WINNING_BID); + }); + + it('should NOT find a match by using default matching function', function() { + // Needs config to be passed without customMatchFunction + // Needs pbjs.getWinningBids to be implemented without match; winningBidsArray is set to empty in beforeEach + let wb = bidViewability.getMatchingWinningBidForGPTSlot({}, gptSlot); + expect(wb).to.equal(null); + }); + }); + + describe('fireViewabilityPixels', function() { + let sandbox; + let triggerPixelSpy; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); + }); + + afterEach(function() { + sandbox.restore(); + }); + + it('DO NOT fire pixels if NOT mentioned in module config', function() { + let moduleConfig = {}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + expect(triggerPixelSpy.callCount).to.equal(0); + }); + + it('fire pixels if mentioned in module config', function() { + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0]).to.equal(url); + }); + }); + + it('USP: should include the us_privacy key when USP Consent is available', function () { + let uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); + uspDataHandlerStub.returns('1YYY'); + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0].indexOf(url)).to.equal(0); + const testurl = parse(call.args[0]); + const queryObject = utils.parseQS(testurl.query); + expect(queryObject.us_privacy).to.equal('1YYY'); + }); + uspDataHandlerStub.restore(); + }); + + it('USP: should not include the us_privacy key when USP Consent is not available', function () { + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0].indexOf(url)).to.equal(0); + const testurl = parse(call.args[0]); + const queryObject = utils.parseQS(testurl.query); + expect(queryObject.us_privacy).to.equal(undefined); + }); + }); + + it('GDPR: should include the GDPR keys when GDPR Consent is available', function() { + let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + gdprDataHandlerStub.returns({ + gdprApplies: true, + consentString: 'consent', + addtlConsent: 'moreConsent' + }); + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0].indexOf(url)).to.equal(0); + const testurl = parse(call.args[0]); + const queryObject = utils.parseQS(testurl.query); + expect(queryObject.gdpr).to.equal('1'); + expect(queryObject.gdpr_consent).to.equal('consent'); + expect(queryObject.addtl_consent).to.equal('moreConsent'); + }); + gdprDataHandlerStub.restore(); + }); + + it('GDPR: should not include the GDPR keys when GDPR Consent is not available', function () { + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0].indexOf(url)).to.equal(0); + const testurl = parse(call.args[0]); + const queryObject = utils.parseQS(testurl.query); + expect(queryObject.gdpr).to.equal(undefined); + expect(queryObject.gdpr_consent).to.equal(undefined); + expect(queryObject.addtl_consent).to.equal(undefined); + }); + }); + + it('GDPR: should only include the GDPR keys for GDPR Consent fields with values', function () { + let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + gdprDataHandlerStub.returns({ + gdprApplies: true, + consentString: 'consent' + }); + let moduleConfig = {firePixels: true}; + bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0].indexOf(url)).to.equal(0); + const testurl = parse(call.args[0]); + const queryObject = utils.parseQS(testurl.query); + expect(queryObject.gdpr).to.equal('1'); + expect(queryObject.gdpr_consent).to.equal('consent'); + expect(queryObject.addtl_consent).to.equal(undefined); + }); + gdprDataHandlerStub.restore(); + }) + }); + + describe('impressionViewableHandler', function() { + let sandbox; + let triggerPixelSpy; + let eventsEmitSpy; + let logWinningBidNotFoundSpy; + let callBidViewableBidderSpy; + let winningBidsArray; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); + eventsEmitSpy = sandbox.spy(events, ['emit']); + callBidViewableBidderSpy = sandbox.spy(adapterManager, ['callBidViewableBidder']); + // mocking winningBidsArray + winningBidsArray = []; + sandbox.stub(prebidGlobal, 'getGlobal').returns({ + getAllWinningBids: function (number) { + return winningBidsArray; + } + }); + }); + + afterEach(function() { + sandbox.restore(); + }) + + it('matching winning bid is found', function() { + let moduleConfig = { + firePixels: true + }; + winningBidsArray.push(PBJS_WINNING_BID); + bidViewability.impressionViewableHandler(moduleConfig, GPT_SLOT, null); + // fire pixels should be called + PBJS_WINNING_BID.vurls.forEach((url, i) => { + let call = triggerPixelSpy.getCall(i); + expect(call.args[0]).to.equal(url); + }); + // adapterManager.callBidViewableBidder is called with required args + let call = callBidViewableBidderSpy.getCall(0); + expect(call.args[0]).to.equal(PBJS_WINNING_BID.bidder); + expect(call.args[1]).to.deep.equal(PBJS_WINNING_BID); + // EVENTS.BID_VIEWABLE is triggered + call = eventsEmitSpy.getCall(0); + expect(call.args[0]).to.equal(EVENTS.BID_VIEWABLE); + expect(call.args[1]).to.deep.equal(PBJS_WINNING_BID); + }); + + it('matching winning bid is NOT found', function() { + // fire pixels should NOT be called + expect(triggerPixelSpy.callCount).to.equal(0); + // adapterManager.callBidViewableBidder is NOT called + expect(callBidViewableBidderSpy.callCount).to.equal(0); + // EVENTS.BID_VIEWABLE is NOT triggered + expect(eventsEmitSpy.callCount).to.equal(0); + }); + }); +}); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 00a0579e676..25b2307b943 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -404,6 +404,38 @@ describe('adapterManager tests', function () { }); }); // end onSetTargeting + describe('onBidViewable', function () { + var criteoSpec = { onBidViewable: sinon.stub() } + var criteoAdapter = { + bidder: 'criteo', + getSpec: function() { return criteoSpec; } + } + before(function () { + config.setConfig({s2sConfig: { enabled: false }}); + }); + + beforeEach(function () { + adapterManager.bidderRegistry['criteo'] = criteoAdapter; + }); + + afterEach(function () { + delete adapterManager.bidderRegistry['criteo']; + }); + + it('should call spec\'s onBidViewable callback when callBidViewableBidder is called', function () { + const bids = [ + {bidder: 'criteo', params: {placementId: 'id'}}, + ]; + const adUnits = [{ + code: 'adUnit-code', + sizes: [[728, 90]], + bids + }]; + adapterManager.callBidViewableBidder(bids[0].bidder, bids[0]); + sinon.assert.called(criteoSpec.onBidViewable); + }); + }); // end onBidViewable + describe('S2S tests', function () { beforeEach(function () { config.setConfig({s2sConfig: CONFIG}); From 8d464b6715bf5995bb9ea455bf370cee61f743e7 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:05:10 -0500 Subject: [PATCH 0591/1476] disable webdriver tests in trionBidAdapter spec (#6280) --- test/spec/modules/trionBidAdapter_spec.js | 82 +++++++++++------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/test/spec/modules/trionBidAdapter_spec.js b/test/spec/modules/trionBidAdapter_spec.js index 596e8a3e2d9..ae329b4a028 100644 --- a/test/spec/modules/trionBidAdapter_spec.js +++ b/test/spec/modules/trionBidAdapter_spec.js @@ -146,47 +146,47 @@ describe('Trion adapter tests', function () { expect(bidUrlParams).to.include(getPublisherUrl()); }); - describe('webdriver', function () { - let originalWD; - - beforeEach(function () { - originalWD = window.navigator.webdriver; - }); - - afterEach(function () { - window.navigator['__defineGetter__']('webdriver', function () { - return originalWD; - }); - }); - - describe('is present', function () { - beforeEach(function () { - window.navigator['__defineGetter__']('webdriver', function () { - return 1; - }); - }); - - it('when there is non human traffic', function () { - let bidRequests = spec.buildRequests(TRION_BID_REQUEST); - let bidUrlParams = bidRequests[0].data; - expect(bidUrlParams).to.include('tr_wd=1'); - }); - }); - - describe('is not present', function () { - beforeEach(function () { - window.navigator['__defineGetter__']('webdriver', function () { - return 0; - }); - }); - - it('when there is not non human traffic', function () { - let bidRequests = spec.buildRequests(TRION_BID_REQUEST); - let bidUrlParams = bidRequests[0].data; - expect(bidUrlParams).to.include('tr_wd=0'); - }); - }); - }); + // describe('webdriver', function () { + // let originalWD; + + // beforeEach(function () { + // originalWD = window.navigator.webdriver; + // }); + + // afterEach(function () { + // window.navigator['__defineGetter__']('webdriver', function () { + // return originalWD; + // }); + // }); + + // describe('is present', function () { + // beforeEach(function () { + // window.navigator['__defineGetter__']('webdriver', function () { + // return 1; + // }); + // }); + + // it('when there is non human traffic', function () { + // let bidRequests = spec.buildRequests(TRION_BID_REQUEST); + // let bidUrlParams = bidRequests[0].data; + // expect(bidUrlParams).to.include('tr_wd=1'); + // }); + // }); + + // describe('is not present', function () { + // beforeEach(function () { + // window.navigator['__defineGetter__']('webdriver', function () { + // return 0; + // }); + // }); + + // it('when there is not non human traffic', function () { + // let bidRequests = spec.buildRequests(TRION_BID_REQUEST); + // let bidUrlParams = bidRequests[0].data; + // expect(bidUrlParams).to.include('tr_wd=0'); + // }); + // }); + // }); describe('document', function () { let originalHD; From ccd570bf76e13cc753f39ffe20465bfcdbb5f820 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Fri, 5 Feb 2021 15:08:30 -0500 Subject: [PATCH 0592/1476] [ParrableIdSystem] Supply Prebid library version to backend (#6279) * Add prebid version to data object * Renamed prebid to prebidVersion * Fix missing coma Co-authored-by: Victor --- modules/parrableIdSystem.js | 3 ++- test/spec/modules/parrableIdSystem_spec.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 32543e3863b..237708aec57 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -188,7 +188,8 @@ function fetchId(configParams) { eid, trackers: configParams.partner.split(','), url: refererInfo.referer, - isIframe: utils.inIframe(), + prebidVersion: '$prebid.version$', + isIframe: utils.inIframe() }; const searchParams = { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index f4f485affe9..9db5df2a518 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -117,6 +117,7 @@ describe('Parrable ID System', function() { eid: P_COOKIE_EID, trackers: P_CONFIG_MOCK.params.partner.split(','), url: getRefererInfo().referer, + prebidVersion: '$prebid.version$', isIframe: true }); From 86516aba8a6341cd6a82393c15438d0d73446d38 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Fri, 5 Feb 2021 15:11:36 -0500 Subject: [PATCH 0593/1476] [ParrableIdSystem] Accept list of partners as an array or string (#6277) * Accept partners as an array and fallthrough partner if no partners * Ensure that Parrable data object decodes with urlsafe base64 in tests * Fixed tests caused by typo in config property * Fix failing test due to accessing unexisting property 'partner' Co-authored-by: Victor --- modules/parrableIdSystem.js | 8 +- test/spec/modules/parrableIdSystem_spec.js | 89 ++++++++++++++++------ 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 237708aec57..afaf794513e 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -60,7 +60,7 @@ function isValidConfig(configParams) { utils.logError('User ID - parrableId submodule requires configParams'); return false; } - if (!configParams.partner) { + if (!configParams.partners && !configParams.partner) { utils.logError('User ID - parrableId submodule requires partner list'); return false; } @@ -183,10 +183,14 @@ function fetchId(configParams) { const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); + const partners = configParams.partners || configParams.partner + const trackers = typeof partners === 'string' + ? partners.split(',') + : partners; const data = { eid, - trackers: configParams.partner.split(','), + trackers, url: refererInfo.referer, prebidVersion: '$prebid.version$', isIframe: utils.inIframe() diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 9db5df2a518..5a10529778c 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -18,7 +18,7 @@ const P_XHR_EID = '01.1588030911.test-new-eid' const P_CONFIG_MOCK = { name: 'parrableId', params: { - partner: 'parrable_test_partner_123,parrable_test_partner_456' + partners: 'parrable_test_partner_123,parrable_test_partner_456' } }; @@ -74,21 +74,21 @@ function removeParrableCookie() { storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); } +function decodeBase64UrlSafe(encBase64) { + const DEC = { + '-': '+', + '_': '/', + '.': '=' + }; + return encBase64.replace(/[-_.]/g, (m) => DEC[m]); +} + describe('Parrable ID System', function() { describe('parrableIdSystem.getId()', function() { describe('response callback function', function() { let logErrorStub; let callbackSpy = sinon.spy(); - let decodeBase64UrlSafe = function (encBase64) { - const DEC = { - '-': '+', - '_': '/', - '.': '=' - }; - return encBase64.replace(/[-_.]/g, (m) => DEC[m]); - } - beforeEach(function() { logErrorStub = sinon.stub(utils, 'logError'); callbackSpy.resetHistory(); @@ -115,7 +115,7 @@ describe('Parrable ID System', function() { expect(queryParams).to.not.have.property('us_privacy'); expect(data).to.deep.equal({ eid: P_COOKIE_EID, - trackers: P_CONFIG_MOCK.params.partner.split(','), + trackers: P_CONFIG_MOCK.params.partners.split(','), url: getRefererInfo().referer, prebidVersion: '$prebid.version$', isIframe: true @@ -162,7 +162,7 @@ describe('Parrable ID System', function() { it('should log an error and continue to callback if ajax request errors', function () { let callBackSpy = sinon.spy(); - let submoduleCallback = parrableIdSubmodule.getId({ params: {partner: 'prebid'} }).callback; + let submoduleCallback = parrableIdSubmodule.getId({ params: {partners: 'prebid'} }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; expect(request.url).to.contain('h.parrable.com'); @@ -237,7 +237,7 @@ describe('Parrable ID System', function() { it('permits an impression when no timezoneFilter is configured', function() { expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', } })).to.have.property('callback'); }); @@ -249,7 +249,7 @@ describe('Parrable ID System', function() { writeParrableCookie({ eid: P_COOKIE_EID }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } @@ -265,7 +265,7 @@ describe('Parrable ID System', function() { Intl.DateTimeFormat.returns({ resolvedOptions }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { allowedZones: [ allowedZone ] } @@ -279,7 +279,7 @@ describe('Parrable ID System', function() { Intl.DateTimeFormat.returns({ resolvedOptions }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } @@ -293,7 +293,7 @@ describe('Parrable ID System', function() { Intl.DateTimeFormat.returns({ resolvedOptions }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedZones: [ blockedZone ] } @@ -307,7 +307,7 @@ describe('Parrable ID System', function() { Intl.DateTimeFormat.returns({ resolvedOptions }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { allowedZones: [ timezone ], blockedZones: [ timezone ] @@ -337,7 +337,7 @@ describe('Parrable ID System', function() { writeParrableCookie({ eid: P_COOKIE_EID }); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } @@ -351,7 +351,7 @@ describe('Parrable ID System', function() { Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { allowedOffsets: [ allowedOffset ] } @@ -365,7 +365,7 @@ describe('Parrable ID System', function() { Date.prototype.getTimezoneOffset.returns(allowedOffset * 60); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } @@ -378,7 +378,7 @@ describe('Parrable ID System', function() { Date.prototype.getTimezoneOffset.returns(blockedOffset * 60); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { blockedOffsets: [ blockedOffset ] } @@ -391,7 +391,7 @@ describe('Parrable ID System', function() { Date.prototype.getTimezoneOffset.returns(offset * 60); expect(parrableIdSubmodule.getId({ params: { - partner: 'prebid-test', + partners: 'prebid-test', timezoneFilter: { allowedOffset: [ offset ], blockedOffsets: [ offset ] @@ -468,4 +468,47 @@ describe('Parrable ID System', function() { }, { adUnits }); }); }); + + describe('partners parsing', () => { + let callbackSpy = sinon.spy(); + + const partnersTestCase = [ + { + name: '"partners" as an array', + config: { params: { partners: ['parrable_test_partner_123', 'parrable_test_partner_456'] } }, + expected: ['parrable_test_partner_123', 'parrable_test_partner_456'] + }, + { + name: '"partners" as a string list', + config: { params: { partners: 'parrable_test_partner_123,parrable_test_partner_456' } }, + expected: ['parrable_test_partner_123', 'parrable_test_partner_456'] + }, + { + name: '"partners" as a string', + config: { params: { partners: 'parrable_test_partner_123' } }, + expected: ['parrable_test_partner_123'] + }, + { + name: '"partner" as a string list', + config: { params: { partner: 'parrable_test_partner_123,parrable_test_partner_456' } }, + expected: ['parrable_test_partner_123', 'parrable_test_partner_456'] + }, + { + name: '"partner" as string', + config: { params: { partner: 'parrable_test_partner_123' } }, + expected: ['parrable_test_partner_123'] + }, + ]; + partnersTestCase.forEach(testCase => { + it(`accepts config property ${testCase.name}`, () => { + parrableIdSubmodule.getId(testCase.config).callback(callbackSpy); + + let request = server.requests[0]; + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.trackers).to.deep.equal(testCase.expected); + }); + }); + }); }); From b7dcdf9e412bff45b374fb4ce21d3ebd25652ece Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Sun, 7 Feb 2021 10:56:12 -0500 Subject: [PATCH 0594/1476] Rubicon Bid Adapter FPD Update (#6122) * Update to consolidate applying FPD to both banner and video requests. FPD will be merged using global defined FPD, ad unit FPD, and rubicon bidder param FPD. Validation logic with warning logs added * Refectored last push to: 1) Correct keywords bug 2) Revise error which looked for FPD in (user/context).ext.data as opposed to (user/context).data 3) General code cleanup * Consolidated other FPD data logic into new function * 1. Update to move pbadslot and adserver data into imp[] as opposed to parent. 2. Update to convert keywords passed through RP params to string if array found * Removed unnecessary conditional * Changed conditional to check for undefined type * Update to consolidate several lines of duplicate code into one location --- modules/rubiconBidAdapter.js | 160 ++++++++++---------- test/spec/modules/rubiconBidAdapter_spec.js | 63 +++++--- 2 files changed, 117 insertions(+), 106 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 2981bd4f3fa..395b7a693b2 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -254,46 +254,7 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); - const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); - if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { - const bidderData = { - bidders: [ bidderRequest.bidderCode ], - config: { - fpd: {} - } - }; - - if (!utils.isEmpty(siteData)) { - bidderData.config.fpd.site = siteData; - } - - if (!utils.isEmpty(userData)) { - bidderData.config.fpd.user = userData; - } - - utils.deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); - } - - /** - * Prebid AdSlot - * @type {(string|undefined)} - */ - const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.pbadslot', pbAdSlot); - } - - /** - * Copy GAM AdUnit and Name to imp - */ - ['name', 'adSlot'].forEach(name => { - /** @type {(string|undefined)} */ - const value = utils.deepAccess(bidRequest, `fpd.context.adserver.${name}`); - if (typeof value === 'string' && value) { - utils.deepSetValue(data.imp[0].ext, `context.data.adserver.${name.toLowerCase()}`, value); - } - }); + applyFPD(bidRequest, VIDEO, data); // if storedAuctionResponse has been set, pass SRID if (bidRequest.storedAuctionResponse) { @@ -547,49 +508,7 @@ export const spec = { data['us_privacy'] = encodeURIComponent(bidderRequest.uspConsent); } - // visitor properties - const visitorData = Object.assign({}, params.visitor, config.getConfig('fpd.user')); - Object.keys(visitorData).forEach((key) => { - if (visitorData[key] != null && key !== 'keywords') { - data[`tg_v.${key}`] = typeof visitorData[key] === 'object' && !Array.isArray(visitorData[key]) - ? JSON.stringify(visitorData[key]) - : visitorData[key].toString(); // initialize array; - } - }); - - // inventory properties - const inventoryData = Object.assign({}, params.inventory, config.getConfig('fpd.context')); - Object.keys(inventoryData).forEach((key) => { - if (inventoryData[key] != null && key !== 'keywords') { - data[`tg_i.${key}`] = typeof inventoryData[key] === 'object' && !Array.isArray(inventoryData[key]) - ? JSON.stringify(inventoryData[key]) - : inventoryData[key].toString(); - } - }); - - // keywords - const keywords = (params.keywords || []).concat( - utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || [], - utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || []); - data.kw = Array.isArray(keywords) && keywords.length ? keywords.join(',') : ''; - - /** - * Prebid AdSlot - * @type {(string|undefined)} - */ - const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - data['tg_i.pbadslot'] = pbAdSlot.replace(/^\/+/, ''); - } - - /** - * GAM Ad Unit - * @type {(string|undefined)} - */ - const gamAdUnit = utils.deepAccess(bidRequest, 'fpd.context.adServer.adSlot'); - if (typeof gamAdUnit === 'string' && gamAdUnit) { - data['tg_i.dfp_ad_unit_code'] = gamAdUnit.replace(/^\/+/, ''); - } + applyFPD(bidRequest, BANNER, data); if (config.getConfig('coppa') === true) { data['coppa'] = 1; @@ -949,6 +868,81 @@ function addVideoParameters(data, bidRequest) { data.imp[0].video.h = size[1] } +function applyFPD(bidRequest, mediaType, data) { + const bidFpd = { + user: {...bidRequest.params.visitor}, + context: {...bidRequest.params.inventory} + }; + + if (bidRequest.params.keywords) bidFpd.context.keywords = (utils.isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; + + let fpd = utils.mergeDeep({}, config.getConfig('fpd') || {}, bidRequest.fpd || {}, bidFpd); + + const map = {user: {banner: 'tg_v.', code: 'user'}, context: {banner: 'tg_i.', code: 'site'}, adserver: 'dfp_ad_unit_code'}; + let obj = {}; + let impData = {}; + let keywords = []; + const validate = function(prop, key) { + if (typeof prop === 'object' && !Array.isArray(prop)) { + utils.logWarn('Rubicon: Filtered FPD key: ', key, ': Expected value to be string, integer, or an array of strings/ints'); + } else if (typeof prop !== 'undefined') { + return (Array.isArray(prop)) ? prop.filter(value => { + if (typeof value !== 'object' && typeof value !== 'undefined') return value.toString(); + + utils.logWarn('Rubicon: Filtered value: ', value, 'for key', key, ': Expected value to be string, integer, or an array of strings/ints'); + }).toString() : prop.toString(); + } + }; + + Object.keys(fpd).filter(value => fpd[value] && map[value] && typeof fpd[value] === 'object').forEach((type) => { + obj[map[type].code] = Object.keys(fpd[type]).filter(value => typeof fpd[type][value] !== 'undefined').reduce((result, key) => { + if (key === 'keywords') { + if (!Array.isArray(fpd[type][key]) && mediaType === BANNER) fpd[type][key] = [fpd[type][key]] + + result[key] = fpd[type][key]; + + if (mediaType === BANNER) keywords = keywords.concat(fpd[type][key]); + } else if (key === 'data') { + utils.mergeDeep(result, {ext: {data: fpd[type][key]}}); + } else if (key === 'adServer' || key === 'pbAdSlot') { + (key === 'adServer') ? ['name', 'adSlot'].forEach(name => { + const value = validate(fpd[type][key][name]); + if (value) utils.deepSetValue(impData, `adserver.${name.toLowerCase()}`, value.replace(/^\/+/, '')) + }) : impData[key.toLowerCase()] = fpd[type][key].replace(/^\/+/, '') + } else { + utils.mergeDeep(result, {ext: {data: {[key]: fpd[type][key]}}}); + } + + return result; + }, {}); + + if (mediaType === BANNER) { + let duplicate = (typeof obj[map[type].code].ext === 'object' && obj[map[type].code].ext.data) || {}; + + Object.keys(duplicate).forEach((key) => { + const val = (key === 'adserver') ? duplicate.adserver.adslot : validate(duplicate[key], key); + + if (val) data[(map[key]) ? `${map[type][BANNER]}${map[key]}` : `${map[type][BANNER]}${key}`] = val; + }); + } + }); + + Object.keys(impData).forEach((key) => { + if (mediaType === BANNER) { + (map[key]) ? data[`tg_i.${map[key]}`] = impData[key].adslot : data[`tg_i.${key.toLowerCase()}`] = impData[key]; + } else { + utils.mergeDeep(data.imp[0], {ext: {context: {data: {[key]: impData[key]}}}}); + } + }); + + if (mediaType === BANNER) { + let kw = validate(keywords, 'keywords'); + if (kw) data.kw = kw; + } else { + utils.mergeDeep(data, obj); + } +} + /** * @param sizes * @returns {*} diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 6944034a787..8c25d97dada 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -826,16 +826,22 @@ describe('the rubicon adapter', function () { }); }); - it('should use first party data from getConfig over the bid params, if present', () => { + it('should merge first party data from getConfig with the bid params, if present', () => { const context = { - keywords: ['e', 'f'], - rating: '4-star' + keywords: 'e,f', + rating: '4-star', + data: { + page: 'home' + } }; const user = { - keywords: ['d'], gender: 'M', yob: '1984', - geo: {country: 'ca'} + geo: {country: 'ca'}, + keywords: 'd', + data: { + age: 40 + } }; sandbox.stub(config, 'getConfig').callsFake(key => { @@ -849,14 +855,15 @@ describe('the rubicon adapter', function () { }); const expectedQuery = { - 'kw': 'a,b,c,d,e,f', + 'kw': 'a,b,c,d', 'tg_v.ucat': 'new', 'tg_v.lastsearch': 'iphone', 'tg_v.likes': 'sports,video games', 'tg_v.gender': 'M', + 'tg_v.age': '40', 'tg_v.yob': '1984', - 'tg_v.geo': '{"country":"ca"}', - 'tg_i.rating': '4-star', + 'tg_i.rating': '5-star', + 'tg_i.page': 'home', 'tg_i.prodtype': 'tech,mobile', }; @@ -1865,11 +1872,17 @@ describe('the rubicon adapter', function () { createVideoBidderRequest(); const context = { - keywords: ['e', 'f'], + data: { + page: 'home' + }, + keywords: 'e,f', rating: '4-star' }; const user = { - keywords: ['d'], + data: { + age: 31 + }, + keywords: 'd', gender: 'M', yob: '1984', geo: {country: 'ca'} @@ -1885,18 +1898,22 @@ describe('the rubicon adapter', function () { return utils.deepAccess(config, key); }); - const expected = [{ - bidders: ['rubicon'], - config: { - fpd: { - site: Object.assign({}, bidderRequest.bids[0].params.inventory, context), - user: Object.assign({}, bidderRequest.bids[0].params.visitor, user) - } - } - }]; - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.ext.prebid.bidderconfig).to.deep.equal(expected); + + const expected = { + site: Object.assign({}, context, context.data, bidderRequest.bids[0].params.inventory), + user: Object.assign({}, user, user.data, bidderRequest.bids[0].params.visitor) + }; + + delete expected.site.data; + delete expected.user.data; + delete expected.site.keywords; + delete expected.user.keywords; + + expect(request.data.site.keywords).to.deep.equal('a,b,c'); + expect(request.data.user.keywords).to.deep.equal('d'); + expect(request.data.site.ext.data).to.deep.equal(expected.site); + expect(request.data.user.ext.data).to.deep.equal(expected.user); }); it('should include storedAuctionResponse in video bid request', function () { @@ -1935,7 +1952,7 @@ describe('the rubicon adapter', function () { createVideoBidderRequest(); bidderRequest.bids[0].fpd = { context: { - adserver: { + adServer: { adSlot: '1234567890', name: 'adServerName1' } @@ -2079,7 +2096,7 @@ describe('the rubicon adapter', function () { it('should not fail if keywords param is not an array', function () { bidderRequest.bids[0].params.keywords = 'a,b,c'; const slotParams = spec.createSlotParams(bidderRequest.bids[0], bidderRequest); - expect(slotParams.kw).to.equal(''); + expect(slotParams.kw).to.equal('a,b,c'); }); }); From 2640d0806a9b28a7e21e44dc46aeccbf83fdd0fd Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 8 Feb 2021 12:08:19 +0100 Subject: [PATCH 0595/1476] ID5 User Id Module: update a/b testing to be user based not request based (#6281) * convert A/B testing to be user-based, rather than request-based * update docs to say a/b testing is user based, not request based --- modules/id5IdSystem.js | 51 ++++++++++----- modules/id5IdSystem.md | 4 +- test/spec/modules/id5IdSystem_spec.js | 91 ++++++++++++++++++++------- 3 files changed, 105 insertions(+), 41 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index baa2ff954dc..bf2365b938c 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -18,6 +18,7 @@ const NB_EXP_DAYS = 30; export const ID5_STORAGE_NAME = 'id5id'; export const ID5_PRIVACY_STORAGE_NAME = `${ID5_STORAGE_NAME}_privacy`; const LOCAL_STORAGE = 'html5'; +const ABTEST_RESOLUTION = 10000; // order the legacy cookie names in reverse priority order so the last // cookie in the array is the most preferred to use @@ -58,27 +59,18 @@ export const id5IdSubmodule = { } // check for A/B testing configuration and hide ID if in Control Group - let abConfig = getAbTestingConfig(config); - let controlGroup = false; - if ( - abConfig.enabled === true && - (!utils.isNumber(abConfig.controlGroupPct) || - abConfig.controlGroupPct < 0 || - abConfig.controlGroupPct > 1) - ) { + const abConfig = getAbTestingConfig(config); + const controlGroup = isInControlGroup(universalUid, abConfig.controlGroupPct); + if (abConfig.enabled === true && typeof controlGroup === 'undefined') { // A/B Testing is enabled, but configured improperly, so skip A/B testing utils.logError('User ID - ID5 submodule: A/B Testing controlGroupPct must be a number >= 0 and <= 1! Skipping A/B Testing'); - } else if ( - abConfig.enabled === true && - Math.random() < abConfig.controlGroupPct - ) { + } else if (abConfig.enabled === true && controlGroup === true) { // A/B Testing is enabled and user is in the Control Group, so do not share the ID5 ID - utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - request is in the Control Group, so the ID5 ID is NOT exposed'); + utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - user is in the Control Group, so the ID5 ID is NOT exposed'); universalUid = linkType = 0; - controlGroup = true; } else if (abConfig.enabled === true) { // A/B Testing is enabled but user is not in the Control Group, so ID5 ID is shared - utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - request is NOT in the Control Group, so the ID5 ID is exposed'); + utils.logInfo('User ID - ID5 submodule: A/B Testing Enabled - user is NOT in the Control Group, so the ID5 ID is exposed'); } let responseObj = { @@ -91,7 +83,7 @@ export const id5IdSubmodule = { }; if (abConfig.enabled === true) { - utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', controlGroup); + utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', (typeof controlGroup === 'undefined' ? false : controlGroup)); } return responseObj; @@ -296,4 +288,31 @@ function getAbTestingConfig(config) { return (config && config.params && config.params.abTesting) || { enabled: false }; } +/** + * Return a consistant random number between 0 and ABTEST_RESOLUTION-1 for this user + * Falls back to plain random if no user provided + * @param {string} userId + * @returns {number} + */ +function abTestBucket(userId) { + if (userId) { + return ((utils.cyrb53Hash(userId) % ABTEST_RESOLUTION) + ABTEST_RESOLUTION) % ABTEST_RESOLUTION; + } else { + return Math.floor(Math.random() * ABTEST_RESOLUTION); + } +} + +/** + * Return a consistant boolean if this user is within the control group ratio provided + * @param {string} userId + * @param {number} controlGroupPct - Ratio [0,1] of users expected to be in the control group + * @returns {boolean} + */ +export function isInControlGroup(userId, controlGroupPct) { + if (!utils.isNumber(controlGroupPct) || controlGroupPct < 0 || controlGroupPct > 1) { + return undefined; + } + return abTestBucket(userId) < controlGroupPct * ABTEST_RESOLUTION; +} + submodule('userId', id5IdSubmodule); diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 6b2192834fa..6a662361492 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -1,6 +1,6 @@ # ID5 Universal ID -The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://console.id5.io/docs/public/prebid). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. +The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://wiki.id5.io/x/BIAZ). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. ## ID5 Universal ID Registration @@ -65,4 +65,4 @@ pbjs.setConfig({ Publishers may want to test the value of the ID5 ID with their downstream partners. While there are various ways to do this, A/B testing is a standard approach. Instead of publishers manually enabling or disabling the ID5 User ID Module based on their control group settings (which leads to fewer calls to ID5, reducing our ability to recognize the user), we have baked this in to our module directly. -To turn on A/B Testing, simply edit the configuration (see above table) to enable it and set what percentage of requests you would like to set for the control group. The control group is the set of requests where an ID5 ID will not be exposed in to bid adapters or in the various user id functions available on the `pbjs` global. An additional value of `ext.abTestingControlGroup` will be set to `true` or `false` that can be used to inform reporting systems that the request was in the control group or not. It's important to note that the control group is request based, and not user based. In other words, from one page view to another, a user may be in or out of the control group. +To turn on A/B Testing, simply edit the configuration (see above table) to enable it and set what percentage of users you would like to set for the control group. The control group is the set of user where an ID5 ID will not be exposed in to bid adapters or in the various user id functions available on the `pbjs` global. An additional value of `ext.abTestingControlGroup` will be set to `true` or `false` that can be used to inform reporting systems that the user was in the control group or not. It's important to note that the control group is user based, and not request based. In other words, from one page view to another, a user will always be in or out of the control group. diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 59fa8977fc8..7f104ff403d 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -7,7 +7,8 @@ import { expDaysStr, nbCacheName, getNbFromCache, - storeNbInCache + storeNbInCache, + isInControlGroup } from 'modules/id5IdSystem.js'; import { init, requestBidsHook, setSubmoduleRegistry, coreStorage } from 'modules/userId/index.js'; import { config } from 'src/config.js'; @@ -477,6 +478,9 @@ describe('ID5 ID System', function() { { enabled: true, controlGroupPct: true } ]; testInvalidAbTestingConfigsWithError.forEach((testAbTestingConfig) => { + it('should be undefined if ratio is invalid', () => { + expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.be.undefined; + }); it('should error if config is invalid, and always return an ID', function () { testConfig.params.abTesting = testAbTestingConfig; let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); @@ -493,6 +497,9 @@ describe('ID5 ID System', function() { { enabled: false, controlGroupPct: true } ]; testInvalidAbTestingConfigsWithoutError.forEach((testAbTestingConfig) => { + it('should be undefined if ratio is invalid', () => { + expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.be.undefined; + }); it('should not error if config is invalid but A/B testing is off, and always return an ID', function () { testConfig.params.abTesting = testAbTestingConfig; let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); @@ -509,6 +516,9 @@ describe('ID5 ID System', function() { { enabled: true, controlGroupPct: 1 } ]; testValidConfigs.forEach((testAbTestingConfig) => { + it('should not be undefined if ratio is valid', () => { + expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.not.be.undefined; + }); it('should not error if config is valid', function () { testConfig.params.abTesting = testAbTestingConfig; id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); @@ -555,34 +565,69 @@ describe('ID5 ID System', function() { randStub.restore(); }); - it('should expose ID when A/B testing is off', function () { - testConfig.params.abTesting = { - enabled: false, - controlGroupPct: 0.5 - }; + describe('IsInControlGroup', function () { + it('Nobody is in a 0% control group', function () { + expect(isInControlGroup('dsdndskhsdks', 0)).to.be.false; + expect(isInControlGroup('3erfghyuijkm', 0)).to.be.false; + expect(isInControlGroup('', 0)).to.be.false; + expect(isInControlGroup(undefined, 0)).to.be.false; + }); - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); - }); + it('Everybody is in a 100% control group', function () { + expect(isInControlGroup('dsdndskhsdks', 1)).to.be.true; + expect(isInControlGroup('3erfghyuijkm', 1)).to.be.true; + expect(isInControlGroup('', 1)).to.be.true; + expect(isInControlGroup(undefined, 1)).to.be.true; + }); - it('should expose ID when not in control group', function () { - testConfig.params.abTesting = { - enabled: true, - controlGroupPct: 0.1 - }; + it('Being in the control group must be consistant', function () { + const inControlGroup = isInControlGroup('dsdndskhsdks', 0.5); + expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; + expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; + expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; + }); - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + it('Control group ratio must be within a 10% error on a large sample', function () { + let nbInControlGroup = 0; + const sampleSize = 100; + for (let i = 0; i < sampleSize; i++) { + nbInControlGroup = nbInControlGroup + (isInControlGroup('R$*df' + i, 0.5) ? 1 : 0); + } + expect(nbInControlGroup).to.be.greaterThan(sampleSize / 2 - sampleSize / 10); + expect(nbInControlGroup).to.be.lessThan(sampleSize / 2 + sampleSize / 10); + }); }); - it('should not expose ID when in control group', function () { - testConfig.params.abTesting = { - enabled: true, - controlGroupPct: 0.5 - }; + describe('Decode', function() { + it('should expose ID when A/B testing is off', function () { + testConfig.params.abTesting = { + enabled: false, + controlGroupPct: 0.5 + }; - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + }); + + it('should expose ID when no one is in control group', function () { + testConfig.params.abTesting = { + enabled: true, + controlGroupPct: 0 + }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + }); + + it('should not expose ID when everyone is in control group', function () { + testConfig.params.abTesting = { + enabled: true, + controlGroupPct: 1 + }; + + let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); + }); }); }); }); From 727bf206f4e7c50215a3ae6ae2920cb304336953 Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Mon, 8 Feb 2021 15:32:53 +0100 Subject: [PATCH 0596/1476] Shared ID gdpr support (#6275) * SharedId gdpr support * Reverted commented locally failing tests --- modules/id5IdSystem.js | 3 +- modules/pubCommonIdSystem.js | 20 +++++++-- modules/sharedIdSystem.js | 21 +++++++-- modules/userId/index.js | 3 +- test/spec/modules/sharedIdSystem_spec.js | 55 ++++++++++++++++++++++++ test/spec/modules/userId_spec.js | 53 +++++++++++++++++++++++ 6 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 test/spec/modules/sharedIdSystem_spec.js diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index bf2365b938c..6dade7d01f0 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -166,10 +166,11 @@ export const id5IdSubmodule = { * It's permissible to return neither, one, or both fields. * @function extendId * @param {SubmoduleConfig} config + * @param {ConsentData|undefined} consentData * @param {Object} cacheIdObj - existing id, if any * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ - extendId(config, cacheIdObj) { + extendId(config, consentData, cacheIdObj) { const partnerId = (config && config.params && config.params.partner) || 0; incrementNb(partnerId); return cacheIdObj; diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js index cb0c07cefa8..339029120a8 100644 --- a/modules/pubCommonIdSystem.js +++ b/modules/pubCommonIdSystem.js @@ -136,6 +136,17 @@ function handleResponse(pubcid, callback, config) { } } +/** + * Builds and returns the shared Id URL with attached consent data if applicable + * @param {Object} consentData + * @return {string} + */ +function sharedIdUrl(consentData) { + if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return SHAREDID_URL; + + return `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}` +} + /** * Wraps pixelCallback in order to call sharedid sync * @param {string} pubcid Pubcommon id value @@ -144,12 +155,12 @@ function handleResponse(pubcid, callback, config) { * @return {function(...[*]=)} */ -function getIdCallback(pubcid, pixelCallback, config) { +function getIdCallback(pubcid, pixelCallback, config, consentData) { return function (callback) { if (typeof pixelCallback === 'function') { pixelCallback(); } - ajax(SHAREDID_URL, handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); + ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); } } @@ -227,7 +238,7 @@ export const pubCommonIdSubmodule = { } const pixelCallback = this.makeCallback(pixelUrl, newId); - const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config) : pixelCallback; + const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback; return {id: newId, callback: combinedCallback}; }, @@ -247,10 +258,11 @@ export const pubCommonIdSubmodule = { * * @function * @param {SubmoduleParams} [config] + * @param {ConsentData|undefined} consentData * @param {Object} storedId existing id * @returns {IdResponse|undefined} */ - extendId: function(config = {}, storedId) { + extendId: function(config = {}, consentData, storedId) { const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; if (extend) { diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 49cac46f1df..762454af5fa 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -276,6 +276,17 @@ function detectPrng(root) { return () => Math.random(); } +/** + * Builds and returns the shared Id URL with attached consent data if applicable + * @param {Object} consentData + * @return {string} + */ +function sharedIdUrl(consentData) { + if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return ID_SVC; + + return `${ID_SVC}?gdpr=1&gdpr_consent=${consentData.consentString}` +} + /** @type {Submodule} */ export const sharedIdSubmodule = { /** @@ -303,12 +314,13 @@ export const sharedIdSubmodule = { * performs action to obtain id and return a value. * @function * @param {SubmoduleConfig} [config] + * @param {ConsentData|undefined} consentData * @returns {sharedId} */ - getId(config) { + getId(config, consentData) { const resp = function (callback) { utils.logInfo('SharedId: Sharedid doesnt exists, new cookie creation'); - ajax(ID_SVC, idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true}); + ajax(sharedIdUrl(consentData), idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true}); }; return {callback: resp}; }, @@ -316,10 +328,11 @@ export const sharedIdSubmodule = { /** * performs actions even if the id exists and returns a value * @param config + * @param consentData * @param storedId * @returns {{callback: *}} */ - extendId(config, storedId) { + extendId(config, consentData, storedId) { const configParams = (config && config.params) || {}; utils.logInfo('SharedId: Existing shared id ' + storedId.id); const resp = function (callback) { @@ -329,7 +342,7 @@ export const sharedIdSubmodule = { const sharedIdPayload = {}; sharedIdPayload.sharedId = storedId.id; const payloadString = JSON.stringify(sharedIdPayload); - ajax(ID_SVC, existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true}); + ajax(sharedIdUrl(consentData), existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true}); } }; return {callback: resp}; diff --git a/modules/userId/index.js b/modules/userId/index.js index 9294311de69..da2c7b225fa 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -28,6 +28,7 @@ * It's permissible to return neither, one, or both fields. * @name Submodule#extendId * @param {SubmoduleConfig} config + * @param {ConsentData|undefined} consentData * @param {Object} storedId - existing id, if any * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ @@ -621,7 +622,7 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef response = submodule.submodule.getId(submodule.config, consentData, storedId); } else if (typeof submodule.submodule.extendId === 'function') { // If the id exists already, give submodule a chance to decide additional actions that need to be taken - response = submodule.submodule.extendId(submodule.config, storedId); + response = submodule.submodule.extendId(submodule.config, consentData, storedId); } if (utils.isPlainObject(response)) { diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js new file mode 100644 index 00000000000..904d6fe1c78 --- /dev/null +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -0,0 +1,55 @@ +import { + sharedIdSubmodule, +} from 'modules/sharedIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; + +let expect = require('chai').expect; + +describe('SharedId System', function() { + const SHAREDID_RESPONSE = {sharedId: 'testsharedid'}; + + describe('Xhr Requests from getId()', function() { + let callbackSpy = sinon.spy(); + + beforeEach(function() { + callbackSpy.resetHistory(); + }); + + afterEach(function () { + + }); + + it('should call shared id endpoint without consent data and handle a valid response', function () { + let submoduleCallback = sharedIdSubmodule.getId(undefined, undefined).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + expect(request.url).to.equal('https://id.sharedid.org/id'); + expect(request.withCredentials).to.be.true; + + request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); + + expect(callbackSpy.calledOnce).to.be.true; + expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId); + }); + + it('should call shared id endpoint with consent data and handle a valid response', function () { + let consentData = { + gdprApplies: true, + consentString: 'abc12345234', + }; + + let submoduleCallback = sharedIdSubmodule.getId(undefined, consentData).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + expect(request.url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234'); + expect(request.withCredentials).to.be.true; + + request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); + + expect(callbackSpy.calledOnce).to.be.true; + expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId); + }); + }); +}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index ed592c0cba5..0fcf03dde67 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -2037,6 +2037,8 @@ describe('User ID', function () { coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); + resetConsentData(); + delete window.__tcfapi; }); it('pubcid callback with url', function () { @@ -2171,6 +2173,57 @@ describe('User ID', function () { expect(server.requests[0].url).to.equal('https://id.sharedid.org/id'); expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null; }); + + it('verify sharedid called with consent data when gdpr applies', function () { + let adUnits = [getAdUnitMock()]; + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + let consentConfig = { + cmpApi: 'iab', + timeout: 7500, + allowAuctionWithoutConsent: false + }; + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); + + server.respondWith('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234', function(xhr) { + xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); + }); + server.respondImmediately = true; + + let testConsentData = { + tcString: 'abc12345234', + gdprApplies: true, + purposeOneTreatment: false, + eventStatus: 'tcloaded', + vendor: {consents: {887: true}}, + purpose: { + consents: { + 1: true + } + } + }; + + window.__tcfapi = function () { }; + sinon.stub(window, '__tcfapi').callsFake((...args) => { + args[2](testConsentData, true); + }); + + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(customCfg); + setConsentConfig(consentConfig); + + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + }, {adUnits}); + + expect(utils.triggerPixel.called).to.be.false; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + + expect(server.requests[0].url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234'); + expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid'); + }); }); describe('Set cookie behavior', function () { From ab9a8d276867182ff2fd191b6cd25c6182d55f55 Mon Sep 17 00:00:00 2001 From: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Date: Mon, 8 Feb 2021 22:05:21 +0530 Subject: [PATCH 0597/1476] Lemma:set mediaType key value (#6006) * 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. * Update lemmaBidAdapter.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter_spec.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.md Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.js Added user sync support into bid adapter. * updated include modules file extension. updated include modules js file extension. * Update lemmaBidAdapter_spec.js Added unit test for user sync feature. * Update lemmaBidAdapter.js Fixed format error. * Update lemmaBidAdapter_spec.js Fixed format error and typo error. * Set mediaType key value into bid object Set mediaType key value into the bid object. * Update lemmaBidAdapter.js remove duplicate function * Update lemmaBidAdapter.js Remove non supported code. * Update lemmaBidAdapter_spec.js Remove GDPR test cases. --- modules/lemmaBidAdapter.js | 27 +++++++++-------------- test/spec/modules/lemmaBidAdapter_spec.js | 18 --------------- 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/modules/lemmaBidAdapter.js b/modules/lemmaBidAdapter.js index 5941802f97d..926761e5ab2 100644 --- a/modules/lemmaBidAdapter.js +++ b/modules/lemmaBidAdapter.js @@ -62,18 +62,6 @@ export var spec = { }, getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { let syncurl = USER_SYNC + 'pid=' + pubId; - - // Attaching GDPR Consent Params in UserSync url - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - - // CCPA - if (uspConsent) { - syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); - } - if (syncOptions.iframeEnabled) { return [{ type: 'iframe', @@ -122,9 +110,9 @@ function parseRTBResponse(request, response) { newBid.dealId = bid.dealid; } if (req.imp && req.imp.length > 0) { - newBid.mediaType = req.mediaType; req.imp.forEach(robj => { if (bid.impid === robj.id) { + _checkMediaType(bid.adm, newBid); switch (newBid.mediaType) { case BANNER: break; @@ -277,10 +265,6 @@ function _getDeviceObject(request) { function setOtherParams(request, ortbRequest) { var params = request && request.params ? request.params : null; - if (request && request.gdprConsent) { - ortbRequest.regs = { ext: { gdpr: request.gdprConsent.gdprApplies ? 1 : 0 } }; - ortbRequest.user = { ext: { consent: request.gdprConsent.consentString } }; - } if (params) { ortbRequest.tmax = params.tmax; ortbRequest.bcat = params.bcat; @@ -424,4 +408,13 @@ function parse(rawResp) { return null; } +function _checkMediaType(adm, newBid) { + // Create a regex here to check the strings + var videoRegex = new RegExp(/VAST.*version/); + if (videoRegex.test(adm)) { + newBid.mediaType = VIDEO; + } else { + newBid.mediaType = BANNER; + } +} registerBidder(spec); diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js index a00c25d126c..a22f5650e50 100644 --- a/test/spec/modules/lemmaBidAdapter_spec.js +++ b/test/spec/modules/lemmaBidAdapter_spec.js @@ -346,24 +346,6 @@ describe('lemmaBidAdapter', function() { type: 'iframe', url: syncurl_iframe }]); }); - - it('CCPA/USP', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&us_privacy=1NYN` - }]); - }); - - it('GDPR', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=0&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=` - }]); - }); }); }); }); From 19b4885892a14a8177c3b45284351b3492b1b8ad Mon Sep 17 00:00:00 2001 From: ix-certification Date: Mon, 8 Feb 2021 11:41:28 -0500 Subject: [PATCH 0598/1476] added support for addtlConsent (#6005) Co-authored-by: Ix-Prebid-Support --- modules/ixBidAdapter.js | 6 ++++++ test/spec/modules/ixBidAdapter_spec.js | 27 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 97341fbfd78..48a2e741c5a 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -341,6 +341,12 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.user.ext = { consent: gdprConsent.consentString || '' }; + + if (gdprConsent.hasOwnProperty('addtlConsent') && gdprConsent.addtlConsent) { + r.user.ext.consented_providers_settings = { + consented_providers: gdprConsent.addtlConsent + } + } } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index edbcc5725ac..fca3f8f992e 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1772,5 +1772,32 @@ describe('IndexexchangeAdapter', function () { expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.regs.ext.us_privacy).to.equal('1YYN'); }); + + it('should contain `consented_providers_settings.consented_providers` & consent on user.ext when both are provided', function () { + const options = { + gdprConsent: { + consentString: '3huaa11=qu3198ae', + addtlConsent: '1~1.35.41.101', + } + }; + + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); + expect(requestWithConsent.user.ext.consented_providers_settings.consented_providers).to.equal('1~1.35.41.101'); + expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); + }); + + it('should not contain `consented_providers_settings.consented_providers` on user.ext when consent is not provided', function () { + const options = { + gdprConsent: { + addtlConsent: '1~1.35.41.101', + } + }; + + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); + expect(utils.deepAccess(requestWithConsent, 'user.ext.consented_providers_settings')).to.not.exist; + expect(utils.deepAccess(requestWithConsent, 'user.ext.consent')).to.not.exist; + }); }); }); From 1654d53b8c7676d652c3bd35c74ca7c5413bf3a7 Mon Sep 17 00:00:00 2001 From: Missena <78362128+dev-missena@users.noreply.github.com> Date: Mon, 8 Feb 2021 22:49:01 +0100 Subject: [PATCH 0599/1476] Add bid adapter for Missena (#6247) * adds support for getFloor of video mediaTypes * adds test for calling getFloor with correct mediaType * checks that _getFloor converts string floors to float * Add bid adapter for Missena * Use publisher demo token in tests * Add Missena global vendor ID to spec * Use apiKey in the current bidRequest * Add referer info to payload Co-authored-by: Nick Llerandi Co-authored-by: Brandon Ling <51931757+blingster7@users.noreply.github.com> Co-authored-by: Brandon Ling --- modules/missenaBidAdapter.js | 94 ++++++++++++++ modules/missenaBidAdapter.md | 70 +++++++++++ test/spec/modules/missenaBidAdapter_spec.js | 131 ++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 modules/missenaBidAdapter.js create mode 100644 modules/missenaBidAdapter.md create mode 100644 test/spec/modules/missenaBidAdapter_spec.js diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js new file mode 100644 index 00000000000..2b1d6bdb118 --- /dev/null +++ b/modules/missenaBidAdapter.js @@ -0,0 +1,94 @@ +import * as utils from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'missena'; +const ENDPOINT_URL = 'https://bid.missena.io/'; + +export const spec = { + aliases: [BIDDER_CODE], + code: BIDDER_CODE, + gvlid: 687, + supportedMediaTypes: [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. + */ + isBidRequestValid: function (bid) { + return typeof bid == 'object' && !!bid.params.apiKey; + }, + + /** + * 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) { + return validBidRequests.map((bidRequest) => { + const payload = { + request_id: bidRequest.bidId, + timeout: bidderRequest.timeout, + }; + + if (bidderRequest && bidderRequest.refererInfo) { + payload.referer = bidderRequest.refererInfo.referer; + payload.referer_canonical = bidderRequest.refererInfo.canonicalUrl; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + payload.consent_string = bidderRequest.gdprConsent.consentString; + payload.consent_required = bidderRequest.gdprConsent.gdprApplies; + } + + return { + method: 'POST', + url: + ENDPOINT_URL + + '?' + + utils.formatQS({ + t: bidRequest.params.apiKey, + }), + data: JSON.stringify(payload), + }; + }); + }, + + /** + * 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 bidResponses = []; + const response = serverResponse.body; + + if (response && !response.timeout && !!response.ad) { + bidResponses.push(response); + } + + return bidResponses; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ + onTimeout: function onTimeout(timeoutData) { + utils.logInfo('Missena - Timeout from adapter', timeoutData); + }, + + /** + * 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) { + utils.logInfo('Missena - Bid won', bid); + }, +}; + +registerBidder(spec); diff --git a/modules/missenaBidAdapter.md b/modules/missenaBidAdapter.md new file mode 100644 index 00000000000..d5fcacf04ab --- /dev/null +++ b/modules/missenaBidAdapter.md @@ -0,0 +1,70 @@ +# Overview + +``` +Module Name: Missena Bid Adapter +Module Type: Bidder Adapter +Maintainer: jney@missena.com +``` + +## Introduction + +Connects to Missena for bids. + +**Note:** this adapter doesn't support SafeFrame. + +Useful resources: + +- [README](../README.md#Build) +- [https://docs.prebid.org/dev-docs/bidder-adaptor.html](https://docs.prebid.org/dev-docs/bidder-adaptor.html) + +## Develop + +Setup the missena adapter in `integrationExamples/gpt/userId_example.html`. + +For example: + +```js +const AD_UNIT_CODE = "test-div"; +const PUBLISHER_MISSENA_TOKEN = "PA-34745704"; + +var adUnits = [ + { + code: AD_UNIT_CODE, + mediaTypes: { + banner: { + sizes: [1, 1], + }, + }, + bids: [ + { + bidder: "missena", + params: { + apiKey: PUBLISHER_MISSENA_TOKEN, + }, + }, + ], + }, +]; +``` + +Then start the demo app: + +```shell +gulp serve-fast --modules=missenaBidAdapter +``` + +And open [http://localhost:9999/integrationExamples/gpt/userId_example.html](http://localhost:9999/integrationExamples/gpt/userId_example.html) + +## Test + +```shell +gulp test --file test/spec/modules/missenaBidAdapter_spec.js +``` + +Add the `--watch` option to re-run unit tests whenever the source code changes. + +## Build + +```shell +gulp build --modules=missenaBidAdapter +``` diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js new file mode 100644 index 00000000000..026e79c6d5a --- /dev/null +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -0,0 +1,131 @@ +import { expect } from 'chai'; +import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('Missena Adapter', function () { + const adapter = newBidder(spec); + + const bidId = 'abc'; + + const bid = { + bidder: 'missena', + bidId: bidId, + sizes: [[1, 1]], + params: { + apiKey: 'PA-34745704', + }, + }; + + describe('codes', function () { + it('should return a bidder code of missena', function () { + expect(spec.code).to.equal('missena'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true if the apiKey param is present', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false if the apiKey is missing', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: {} })) + ).to.equal(false); + }); + + it('should return false if the apiKey is an empty string', function () { + expect( + spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })) + ).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const consentString = 'AAAAAAAAA=='; + + const bidderRequest = { + gdprConsent: { + consentString: consentString, + gdprApplies: true, + }, + refererInfo: { + referer: 'https://referer', + canonicalUrl: 'https://canonical', + }, + }; + + const requests = spec.buildRequests([bid, bid], bidderRequest); + const request = requests[0]; + const payload = JSON.parse(request.data); + + it('should return as many server requests as bidder requests', function () { + expect(requests.length).to.equal(2); + }); + + it('should have a post method', function () { + expect(request.method).to.equal('POST'); + }); + + it('should send the bidder id', function () { + expect(payload.request_id).to.equal(bidId); + }); + + it('should send referer information to the request', function () { + expect(payload.referer).to.equal('https://referer'); + expect(payload.referer_canonical).to.equal('https://canonical'); + }); + + it('should send gdpr consent information to the request', function () { + expect(payload.consent_string).to.equal(consentString); + expect(payload.consent_required).to.equal(true); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + }; + + const serverTimeoutResponse = { + requestId: bidId, + timeout: true, + ad: '', + }; + + const serverEmptyAdResponse = { + requestId: bidId, + cpm: 0.5, + currency: 'USD', + ad: '', + }; + + it('should return a proper bid response', function () { + const result = spec.interpretResponse({ body: serverResponse }, bid); + + expect(result.length).to.equal(1); + + expect(Object.keys(result[0])).to.have.members( + Object.keys(serverResponse) + ); + }); + + it('should return an empty response when the server answers with a timeout', function () { + const result = spec.interpretResponse( + { body: serverTimeoutResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + + it('should return an empty response when the server answers with an empty ad', function () { + const result = spec.interpretResponse( + { body: serverEmptyAdResponse }, + bid + ); + expect(result).to.deep.equal([]); + }); + }); +}); From c27ff14b31c5a7a08695eda7dc929c768dbbb809 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Tue, 9 Feb 2021 02:12:10 -0800 Subject: [PATCH 0600/1476] Gulp test file example added in readme (#6287) --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 40df62ccee4..3e541080bf9 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,11 @@ To run the unit tests: gulp test ``` +To run the unit tests for a perticular file (example for pubmaticBidAdapter_spec.js): +```bash +gulp test --file "test/spec/modules/pubmaticBidAdapter_spec.js" +``` + To generate and view the code coverage reports: ```bash From dde585bbd38d16157668a955690b00989dcf100b Mon Sep 17 00:00:00 2001 From: Pooja Pasawala Date: Tue, 9 Feb 2021 02:38:53 -0800 Subject: [PATCH 0601/1476] Sharethrough: Add support for ID5, Shared ID, and Live Intent ID (#6261) * Update prebid adapter universal ids to include ID5, SharedID, and LiveIntent ID. [#176447070](https://www.pivotaltracker.com/story/show/176447070) Co-authored-by: Mathieu Pheulpin * Addressing review [#176447070] * Quick rewrite [#176447070] * Address ID5 review, forward linkType to adserver * Reformatting SharedID to align with ID5 Co-authored-by: Mathieu Pheulpin --- modules/sharethroughBidAdapter.js | 48 +++++++++++++------ .../modules/sharethroughBidAdapter_spec.js | 42 +++++++++++++++- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 89484b1c68b..24be8673615 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; -const VERSION = '3.3.0'; +const VERSION = '3.3.1'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -31,6 +31,8 @@ export const sharethroughAdapterSpec = { strVersion: VERSION }; + Object.assign(query, handleUniversalIds(bidRequest)); + const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; query.secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1); @@ -46,20 +48,6 @@ export const sharethroughAdapterSpec = { query.us_privacy = bidderRequest.uspConsent } - if (bidRequest.userId && bidRequest.userId.tdid) { - query.ttduid = bidRequest.userId.tdid; - } - - if (bidRequest.userId && bidRequest.userId.pubcid) { - query.pubcid = bidRequest.userId.pubcid; - } else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { - query.pubcid = bidRequest.crumbs.pubcid; - } - - if (bidRequest.userId && bidRequest.userId.idl_env) { - query.idluid = bidRequest.userId.idl_env; - } - if (bidRequest.schain) { query.schain = JSON.stringify(bidRequest.schain); } @@ -147,6 +135,36 @@ export const sharethroughAdapterSpec = { onSetTargeting: (bid) => {} }; +function handleUniversalIds(bidRequest) { + if (!bidRequest.userId) return {}; + + const universalIds = {}; + + const ttd = utils.deepAccess(bidRequest, 'userId.tdid'); + if (ttd) universalIds.ttduid = ttd; + + const pubc = utils.deepAccess(bidRequest, 'userId.pubcid') || utils.deepAccess(bidRequest, 'crumbs.pubcid'); + if (pubc) universalIds.pubcid = pubc; + + const idl = utils.deepAccess(bidRequest, 'userId.idl_env'); + if (idl) universalIds.idluid = idl; + + const id5 = utils.deepAccess(bidRequest, 'userId.id5id.uid'); + if (id5) { + universalIds.id5uid = { id: id5 }; + const id5link = utils.deepAccess(bidRequest, 'userId.id5id.ext.linkType'); + if (id5link) universalIds.id5uid.linkType = id5link; + } + + const lipb = utils.deepAccess(bidRequest, 'userId.lipb.lipbid'); + if (lipb) universalIds.liuid = lipb; + + const shd = utils.deepAccess(bidRequest, 'userId.sharedid'); + if (shd) universalIds.shduid = shd; // object with keys: id & third + + return universalIds; +} + function getLargestSize(sizes) { function area(size) { return size[0] * size[1]; diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index cd9071a6098..b3451a09dde 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as utils from '../../../src/utils.js'; const spec = newBidder(sharethroughAdapterSpec).getSpec(); const bidRequests = [ @@ -15,7 +16,20 @@ const bidRequests = [ userId: { tdid: 'fake-tdid', pubcid: 'fake-pubcid', - idl_env: 'fake-identity-link' + idl_env: 'fake-identity-link', + id5id: { + uid: 'fake-id5id', + ext: { + linkType: 2 + } + }, + sharedid: { + id: 'fake-sharedid', + third: 'fake-sharedthird' + }, + lipb: { + lipbid: 'fake-lipbid' + } }, crumbs: { pubcid: 'fake-pubcid-in-crumbs-obj' @@ -329,6 +343,15 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); }); + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + + ' crumbs object of the bidrequest', function() { + const bidData = utils.deepClone(bidRequests); + delete bidData[0].userId.pubcid; + + const bidRequest = spec.buildRequests(bidData)[0]; + expect(bidRequest.data.pubcid).to.eq('fake-pubcid-in-crumbs-obj'); + }); + it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + ' crumbs object of the bidrequest', function() { const bidRequest = spec.buildRequests(bidRequests)[0]; @@ -341,6 +364,23 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.idluid).to.eq('fake-identity-link'); }); + it('should add the id5uid parameter if a bid request contains a value for ID5', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.id5uid.id).to.eq('fake-id5id'); + expect(bidRequest.data.id5uid.linkType).to.eq(2); + }); + + it('should add the shduid parameter if a bid request contains a value for Shared ID', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.shduid.id).to.eq('fake-sharedid'); + expect(bidRequest.data.shduid.third).to.eq('fake-sharedthird'); + }); + + it('should add the liuid parameter if a bid request contains a value for LiveIntent ID', function() { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.liuid).to.eq('fake-lipbid'); + }); + it('should add Sharethrough specific parameters', function() { const builtBidRequests = spec.buildRequests(bidRequests); expect(builtBidRequests[0]).to.deep.include({ From 73cfeb537a9ffbca707e80c1c171d14f57b46869 Mon Sep 17 00:00:00 2001 From: lowendavid <66423906+lowendavid@users.noreply.github.com> Date: Tue, 9 Feb 2021 11:39:14 +0100 Subject: [PATCH 0602/1476] SmartAdServer Bid Adapter: image sync and noAd (#6236) * SIM-889 Now we have image based sync * SIM-889 Added test to check noad and image sync * SIM-889 Fixing indenting issues --- modules/smartadserverBidAdapter.js | 12 +++- .../modules/smartadserverBidAdapter_spec.js | 69 ++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index ed9003e3b4d..bb9364c72c3 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -135,7 +135,7 @@ export const spec = { const bidResponses = []; let response = serverResponse.body; try { - if (response) { + if (response && !response.isNoAd) { const bidRequest = JSON.parse(bidRequestString.data); let bidResponse = { @@ -147,7 +147,8 @@ export const spec = { dealId: response.dealId, currency: response.currency, netRevenue: response.isNetCpm, - ttl: response.ttl + ttl: response.ttl, + dspPixels: response.dspPixels }; if (bidRequest.mediaType === VIDEO) { @@ -182,6 +183,13 @@ export const spec = { type: 'iframe', url: serverResponses[0].body.cSyncUrl }); + } else if (syncOptions.pixelEnabled && serverResponses.length > 0 && serverResponses[0].body.dspPixels !== undefined) { + serverResponses[0].body.dspPixels.forEach(function(pixel) { + syncs.push({ + type: 'image', + url: pixel + }); + }); } return syncs; } diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index e3bca240a47..749de43b9af 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -115,7 +115,41 @@ describe('Smart bid adapter tests', function () { ttl: 300, adUrl: 'http://awesome.fake.url', ad: '< --- awesome script --- >', - cSyncUrl: 'http://awesome.fake.csync.url' + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: false + } + }; + + var BID_RESPONSE_IS_NO_AD = { + body: { + cpm: 12, + width: 300, + height: 250, + creativeId: 'zioeufg', + currency: 'GBP', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake.url', + ad: '< --- awesome script --- >', + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: true + } + }; + + var BID_RESPONSE_IMAGE_SYNC = { + body: { + cpm: 12, + width: 300, + height: 250, + creativeId: 'zioeufg', + currency: 'GBP', + isNetCpm: true, + ttl: 300, + adUrl: 'http://awesome.fake.url', + ad: '< --- awesome script --- >', + cSyncUrl: 'http://awesome.fake.csync.url', + isNoAd: false, + dspPixels: ['pixelOne', 'pixelTwo', 'pixelThree'] } }; @@ -149,6 +183,18 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('ckid').and.to.equal(42); }); + it('Verify parse response with no ad', function () { + const request = spec.buildRequests(DEFAULT_PARAMS); + const bids = spec.interpretResponse(BID_RESPONSE_IS_NO_AD, request[0]); + expect(bids).to.have.lengthOf(0); + + expect(function () { + spec.interpretResponse(BID_RESPONSE_IS_NO_AD, { + 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]); @@ -258,6 +304,27 @@ describe('Smart bid adapter tests', function () { expect(syncs).to.have.lengthOf(0); }); + it('Verifies user sync using dspPixels', function () { + var syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE_IMAGE_SYNC]); + expect(syncs).to.have.lengthOf(3); + expect(syncs[0].type).to.equal('image'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE_IMAGE_SYNC]); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, []); + expect(syncs).to.have.lengthOf(0); + }); + describe('gdpr tests', function () { afterEach(function () { config.resetConfig(); From fae47aab59b29b54421b57cfc22ab9271b436ebf Mon Sep 17 00:00:00 2001 From: iskmerof Date: Wed, 10 Feb 2021 06:19:01 -0500 Subject: [PATCH 0603/1476] Add client Alias Adkernel (#6291) Adding "bcm" alias to Adkernel adapter --- modules/adkernelBidAdapter.js | 2 +- test/spec/modules/adkernelBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 20ed65fe2e2..03fa5c2b2d9 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads', 'bcm'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 4454aa00a71..0d772423a22 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -556,7 +556,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(11); + expect(spec.aliases).to.have.lengthOf(12); }); }); From 4a7b465d6bd640046b7da3675e203082caed2cd4 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 10 Feb 2021 06:55:26 -0500 Subject: [PATCH 0604/1476] [ParrableIdSystem] Add GVLID and handle TC Consent data (#6283) * Added GDPR support * Remove forgotten .only Co-authored-by: Victor --- modules/parrableIdSystem.js | 22 ++++++++++++--- test/spec/modules/parrableIdSystem_spec.js | 32 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index afaf794513e..f072de8736f 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -14,12 +14,13 @@ import { getStorageManager } from '../src/storageManager.js'; const PARRABLE_URL = 'https://h.parrable.com/prebid'; const PARRABLE_COOKIE_NAME = '_parrable_id'; +const PARRABLE_GVLID = 928; const LEGACY_ID_COOKIE_NAME = '_parrable_eid'; const LEGACY_OPTOUT_COOKIE_NAME = '_parrable_optout'; const ONE_YEAR_MS = 364 * 24 * 60 * 60 * 1000; const EXPIRE_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:00 GMT'; -const storage = getStorageManager(); +const storage = getStorageManager(PARRABLE_GVLID); function getExpirationDate() { const oneYearFromNow = new Date(utils.timestamp() + ONE_YEAR_MS); @@ -167,7 +168,7 @@ function shouldFilterImpression(configParams, parrableId) { return !isAllowed() || isBlocked(); } -function fetchId(configParams) { +function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; let parrableId = readCookie(); @@ -183,6 +184,8 @@ function fetchId(configParams) { const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); + const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); + const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; const partners = configParams.partners || configParams.partner const trackers = typeof partners === 'string' ? partners.split(',') @@ -198,6 +201,7 @@ function fetchId(configParams) { const searchParams = { data: encodeBase64UrlSafe(btoa(JSON.stringify(data))), + gdpr: gdprApplies ? 1 : 0, _rand: Math.random() }; @@ -205,6 +209,10 @@ function fetchId(configParams) { searchParams.us_privacy = uspString; } + if (gdprApplies) { + searchParams.gdpr_consent = gdprConsentString; + } + const options = { method: 'GET', withCredentials: true @@ -251,7 +259,7 @@ function fetchId(configParams) { callback, id: parrableId }; -}; +} /** @type {Submodule} */ export const parrableIdSubmodule = { @@ -260,6 +268,12 @@ export const parrableIdSubmodule = { * @type {string} */ name: 'parrableId', + /** + * Global Vendor List ID + * @type {number} + */ + gvlid: PARRABLE_GVLID, + /** * decode the stored id value for passing to bid requests * @function @@ -282,7 +296,7 @@ export const parrableIdSubmodule = { */ getId(config, gdprConsentData, currentStoredId) { const configParams = (config && config.params) || {}; - return fetchId(configParams); + return fetchId(configParams, gdprConsentData); } }; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 5a10529778c..f0aac7666f9 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -210,6 +210,38 @@ describe('Parrable ID System', function() { removeParrableCookie(); }); }); + + describe('GDPR consent', () => { + let callbackSpy = sinon.spy(); + + const config = { + params: { + partner: 'partner' + } + }; + + const gdprConsentTestCases = [ + { consentData: { gdprApplies: true, consentString: 'expectedConsentString' }, expected: { gdpr: 1, gdpr_consent: 'expectedConsentString' } }, + { consentData: { gdprApplies: false, consentString: 'expectedConsentString' }, expected: { gdpr: 0 } }, + { consentData: { gdprApplies: true, consentString: undefined }, expected: { gdpr: 1, gdpr_consent: '' } }, + { consentData: { gdprApplies: 'yes', consentString: 'expectedConsentString' }, expected: { gdpr: 0 } }, + { consentData: undefined, expected: { gdpr: 0 } } + ]; + + gdprConsentTestCases.forEach((testCase, index) => { + it(`should call user sync url with the gdprConsent - case ${index}`, () => { + parrableIdSubmodule.getId(config, testCase.consentData).callback(callbackSpy); + + if (testCase.expected.gdpr === 1) { + expect(server.requests[0].url).to.contain('gdpr=' + testCase.expected.gdpr); + expect(server.requests[0].url).to.contain('gdpr_consent=' + testCase.expected.gdpr_consent); + } else { + expect(server.requests[0].url).to.contain('gdpr=' + testCase.expected.gdpr); + expect(server.requests[0].url).to.not.contain('gdpr_consent'); + } + }) + }); + }); }); describe('parrableIdSystem.decode()', function() { From 8e2501e881d6f2790d474e1393b56d9af5400478 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 10 Feb 2021 16:24:04 -0500 Subject: [PATCH 0605/1476] 4.26.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8b6af04379..97020126830 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.26.0-pre", + "version": "4.26.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ff0b015c96c1c707ca66a66505a350c935adac62 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 10 Feb 2021 16:38:25 -0500 Subject: [PATCH 0606/1476] 4.27.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 97020126830..45493de7aea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.26.0", + "version": "4.27.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a037c5455b038b25c0be07a1625aac4761ef7f3e Mon Sep 17 00:00:00 2001 From: pnh-pubx <73683023+pnh-pubx@users.noreply.github.com> Date: Thu, 11 Feb 2021 04:34:49 +0530 Subject: [PATCH 0607/1476] Updated data mapping of winning bid and auction logs in pubxai analytics adapter (#6285) Co-authored-by: Phaneendra Hegde --- modules/pubxaiAnalyticsAdapter.js | 13 +- .../modules/pubxaiAnalyticsAdapter_spec.js | 164 ++++++------------ 2 files changed, 64 insertions(+), 113 deletions(-) diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 7e2f5061621..894752d607b 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -27,9 +27,13 @@ var pubxaiAnalyticsAdapter = Object.assign(adapter( if (eventType === CONSTANTS.EVENTS.BID_TIMEOUT) { args.forEach(item => { mapBidResponse(item, 'timeout'); }); } else if (eventType === CONSTANTS.EVENTS.AUCTION_INIT) { - events.auctionInit = args; + events.floorDetail = {}; + if (typeof args.bidderRequests[0].bids[0] !== 'undefined' && typeof args.bidderRequests[0].bids[0].floorData !== 'undefined') { + Object.assign(events.floorDetail, args.bidderRequests[0].bids[0].floorData); + } auctionTimestamp = args.timestamp; } else if (eventType === CONSTANTS.EVENTS.BID_REQUESTED) { + events.bids = []; mapBidRequests(args).forEach(item => { events.bids.push(item) }); } else if (eventType === CONSTANTS.EVENTS.BID_RESPONSE) { mapBidResponse(args, 'response'); @@ -56,6 +60,8 @@ function mapBidRequests(params) { adUnitCode: bid.adUnitCode, requestId: bid.bidderRequestId, auctionId: bid.auctionId, + placementId: bid.params.placementId, + floorData: bid.floorData, transactionId: bid.transactionId, sizes: utils.parseSizesInput(bid.mediaTypes.banner.sizes).toString(), renderStatus: 1, @@ -134,8 +140,9 @@ pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) { function send(data, status) { if (pubxaiAnalyticsAdapter.shouldFireEventRequest(initOptions.samplingRate)) { let location = utils.getWindowLocation(); - if (typeof data !== 'undefined' && typeof data.auctionInit !== 'undefined') { - Object.assign(data.auctionInit, { host: location.host, path: location.pathname, search: location.search }); + if (typeof data !== 'undefined') { + data.pageDetail = {}; + Object.assign(data.pageDetail, { host: location.host, path: location.pathname, search: location.search }); } data.initOptions = initOptions; diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 91c81dcae8d..95245f2c6c9 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -27,6 +27,8 @@ describe('pubxai analytics adapter', function() { pubxId: '6c415fc0-8b0e-4cf5-be73-01526a4db625' }; + let location = utils.getWindowLocation(); + let prebidEvent = { 'auctionInit': { 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', @@ -219,6 +221,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -381,6 +389,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -449,6 +463,12 @@ describe('pubxai analytics adapter', function() { 'originalCpm': 0.5, 'originalCurrency': 'USD', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -488,9 +508,13 @@ describe('pubxai analytics adapter', function() { 'params': [{ 'placementId': 13144370 }] - } + }, + 'pageDetail': { + 'host': location.host, + 'path': location.pathname, + 'search': location.search + }, }; - let location = utils.getWindowLocation(); let expectedAfterBid = { 'bids': [{ @@ -509,6 +533,12 @@ describe('pubxai analytics adapter', function() { 'mediaType': 'banner', 'statusMessage': 'Bid available', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -527,118 +557,21 @@ describe('pubxai analytics adapter', function() { 'timeToRespond': 267, 'responseTimestamp': 1603865707449, 'platform': navigator.platform, + 'placementId': 13144370, 'deviceType': getDeviceType() }], - 'auctionInit': { + 'pageDetail': { 'host': location.host, 'path': location.pathname, - 'search': location.search, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'timestamp': 1603865707180, - 'auctionStatus': 'inProgress', - 'adUnits': [{ - 'code': '/19968336/header-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ] - ] - } - }, - 'bids': [{ - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'floorData': { - 'skipped': false, - 'skipRate': 0, - 'modelVersion': 'new model 1.0', - 'location': 'fetch', - 'floorProvider': 'PubXFloor', - 'fetchStatus': 'success' - } - }], - 'sizes': [ - [ - 300, - 250 - ] - ], - 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294' - }], - 'adUnitCodes': [ - '/19968336/header-bid-tag-1' - ], - 'bidderRequests': [{ - 'bidderCode': 'appnexus', - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'bidderRequestId': '184cbc05bb90ba', - 'bids': [{ - 'bidder': 'appnexus', - 'params': { - 'placementId': 13144370 - }, - 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', - 'floorData': { - 'skipped': false, - 'skipRate': 0, - 'modelVersion': 'new model 1.0', - 'location': 'fetch', - 'floorProvider': 'PubXFloor', - 'fetchStatus': 'success' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ] - ] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': '41ec8eaf-3e7c-4a8b-8344-ab796ff6e294', - 'sizes': [ - [ - 300, - 250 - ] - ], - 'bidId': '248f9a4489835e', - 'bidderRequestId': '184cbc05bb90ba', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - }], - 'auctionStart': 1603865707180, - 'timeout': 1000, - 'refererInfo': { - 'referer': 'http://local-pnh.net:8080/stream/', - 'reachedTop': true, - 'isAmp': false, - 'numIframes': 0, - 'stack': [ - 'http://local-pnh.net:8080/stream/' - ], - 'canonicalUrl': null - }, - 'start': 1603865707182 - }], - 'noBids': [], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 1000, - 'config': { - 'samplingRate': '1', - 'pubxId': '6c415fc0-8b0e-4cf5-be73-01526a4db625' - } + 'search': location.search + }, + 'floorDetail': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false }, 'initOptions': initOptions }; @@ -660,6 +593,12 @@ describe('pubxai analytics adapter', function() { 'status': 'rendered', 'statusMessage': 'Bid available', 'floorData': { + 'fetchStatus': 'success', + 'floorProvider': 'PubXFloor', + 'location': 'fetch', + 'modelVersion': 'new model 1.0', + 'skipRate': 0, + 'skipped': false, 'floorValue': 0.4, 'floorRule': '/19968336/header-bid-tag-1|banner', 'floorCurrency': 'USD', @@ -680,6 +619,11 @@ describe('pubxai analytics adapter', function() { 'platform': navigator.platform, 'deviceType': getDeviceType() }, + 'pageDetail': { + 'host': location.host, + 'path': location.pathname, + 'search': location.search + }, 'initOptions': initOptions } From c9e8869ddd867a475efa4531d594ed422252aa8c Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 11 Feb 2021 14:41:38 +0300 Subject: [PATCH 0608/1476] Grid Bid Adapter: Added video protocols to the ad request (#6299) --- modules/gridBidAdapter.js | 6 +++++- test/spec/modules/gridBidAdapter_spec.js | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 6e610b67e0e..b296fe39ae4 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -365,7 +365,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { } function createVideoRequest(bid, mediaType) { - const {playerSize, mimes, durationRangeSec} = mediaType; + const {playerSize, mimes, durationRangeSec, protocols} = mediaType; const size = (playerSize || bid.sizes || [])[0]; if (!size) return; @@ -380,6 +380,10 @@ function createVideoRequest(bid, mediaType) { result.maxduration = durationRangeSec[1]; } + if (protocols && protocols.length) { + result.protocols = protocols; + } + return result; } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 084c67562e6..b4e87119111 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -105,7 +105,8 @@ describe('TheMediaGrid Adapter', function () { 'sizes': [[728, 90]], 'mediaTypes': { 'video': { - 'playerSize': [[400, 600]] + 'playerSize': [[400, 600]], + 'protocols': [1, 2, 3] }, 'banner': { 'sizes': [[728, 90]] @@ -281,7 +282,8 @@ describe('TheMediaGrid Adapter', function () { }, 'video': { 'w': 400, - 'h': 600 + 'h': 600, + 'protocols': [1, 2, 3] } }] }); From f672209110ef30b9cdf96048d4e8285d43716b4a Mon Sep 17 00:00:00 2001 From: bretg Date: Thu, 11 Feb 2021 06:52:17 -0500 Subject: [PATCH 0609/1476] Rubicon Bid Adapter: updated transactionId to auctionId for OpenRTB (#6298) --- modules/rubiconBidAdapter.js | 4 ++-- test/spec/modules/rubiconBidAdapter_spec.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 395b7a693b2..e9f25c9411e 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -153,7 +153,7 @@ export const spec = { test: config.getConfig('debug') ? 1 : 0, cur: ['USD'], source: { - tid: bidRequest.transactionId + tid: bidRequest.auctionId }, tmax: bidderRequest.timeout, imp: [{ @@ -424,7 +424,7 @@ export const spec = { 'rp_floor': (params.floor = parseFloat(params.floor)) >= 0.01 ? params.floor : undefined, 'rp_secure': '1', 'tk_flint': `${rubiConf.int_type || DEFAULT_INTEGRATION}_v$prebid.version$`, - 'x_source.tid': bidRequest.transactionId, + 'x_source.tid': bidRequest.auctionId, 'x_source.pchain': params.pchain, 'p_screen_res': _getScreenResolution(), 'tk_user_key': params.userId, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8c25d97dada..5b881299210 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -415,7 +415,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'x_source.pchain': 'GAM:11111-reseller1:22222', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', @@ -572,7 +572,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', @@ -895,7 +895,7 @@ describe('the rubicon adapter', function () { 'rp_secure': /[01]/, 'rand': '0.1', 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', @@ -2067,7 +2067,7 @@ describe('the rubicon adapter', function () { 'p_pos': 'atf', 'rp_secure': /[01]/, 'tk_flint': INTEGRATION, - 'x_source.tid': 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', + 'x_source.tid': 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', 'p_screen_res': /\d+x\d+/, 'tk_user_key': '12346', 'kw': 'a,b,c', From 2702e964cd0d540002450ddacd776f7c1b635794 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Fri, 12 Feb 2021 02:50:29 -0800 Subject: [PATCH 0610/1476] Fix for Issue 6117: Added Module Name in Build to Comments (#6297) --- gulpfile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index b7a9e442a8c..51536992bd0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,7 +31,7 @@ const execa = require('execa'); var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); -var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + ' */\n'; +var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + '\nModules: <%= modules %> */\n'; var port = 9999; const FAKE_SERVER_HOST = argv.host ? argv.host : 'localhost'; const FAKE_SERVER_PORT = 4444; @@ -157,12 +157,13 @@ function makeWebpackPkg() { const analyticsSources = helpers.getAnalyticsSources(); const moduleSources = helpers.getModulePaths(externalModules); + const modulesString = (externalModules.length > 0) ? externalModules.join(', ') : 'All available modules in current version.'; return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) .pipe(uglify()) - .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) + .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid, modules: modulesString }))) .pipe(gulp.dest('build/dist')); } From a6c3986c0a306f4dc50b149d4a5f617282741a96 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 12 Feb 2021 06:39:20 -0500 Subject: [PATCH 0611/1476] map tripleliftBidAdapter.js tl_source to bid.meta.mediaType (#6303) --- modules/tripleliftBidAdapter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index f97165f3d1c..029cdd68331 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -322,6 +322,14 @@ function _buildResponseObject(bidderRequest, bid) { if (bid.adomain && bid.adomain.length) { bidResponse.meta.advertiserDomains = bid.adomain; } + + if (bid.tl_source && bid.tl_source == 'hdx') { + bidResponse.meta.mediaType = 'banner'; + } + + if (bid.tl_source && bid.tl_source == 'tlx') { + bidResponse.meta.mediaType = 'native'; + } }; return bidResponse; } From 62ca969575e82eb1ae5416e412982d562d74c2d5 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Fri, 12 Feb 2021 15:54:43 +0100 Subject: [PATCH 0612/1476] Tappx Bid Adapter: new bidder adapter added (#6233) * ADD: tappx bid adapter * FIX: replace .includes by .indexOf * UPDATE: Expand the test coverage * FIX: format spacing tests * FIX: get auctionId from validBidRequests * UPDATE: add bannerMediaType tablet sizes * FIX: get timeout from bidderRequest.timeout * UPDATE: replace the way to get the hostname * UPDATE: adding support multiple bid requests in a single call * UPDATE: remove hardcoded test payload param Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 289 ++++++++++++++++++++++ modules/tappxBidAdapter.md | 37 +++ test/spec/modules/tappxBidAdapter_spec.js | 86 +++++++ 3 files changed, 412 insertions(+) create mode 100644 modules/tappxBidAdapter.js create mode 100644 modules/tappxBidAdapter.md create mode 100644 test/spec/modules/tappxBidAdapter_spec.js diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js new file mode 100644 index 00000000000..7782f151802 --- /dev/null +++ b/modules/tappxBidAdapter.js @@ -0,0 +1,289 @@ +'use strict'; + +import * as utils 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 = 'tappx'; +const TTL = 360; +const CUR = 'USD'; +var HOST; +var hostDomain; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [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. + */ + isBidRequestValid: function(bid) { + if ((bid.params == null) || (bid.params.endpoint == null) || (bid.params.tappxkey == null)) { + utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters. ${JSON.stringify(bid)}`); + return false; + } + return true; + }, + + /** + * Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. + * Make a server request from the list of BidRequests. + * + * @param {*} validBidRequests + * @param {*} bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let requests = []; + validBidRequests.forEach(oneValidRequest => { + requests.push(buildOneRequest(oneValidRequest, bidderRequest)); + }); + return requests; + }, + + /** + * Parse the response and generate one or more bid objects. + * + * @param {*} serverResponse + * @param {*} originalRequest + */ + interpretResponse: function(serverResponse, originalRequest) { + const responseBody = serverResponse.body; + if (!serverResponse.body) { + utils.logWarn('[TAPPX]: Empty response body HTTP 204, no bids'); + return []; + } + + const bids = []; + responseBody.seatbid.forEach(serverSeatBid => { + serverSeatBid.bid.forEach(serverBid => { + bids.push(interpretBannerBid(serverBid, originalRequest)); + }); + }); + + return bids; + }, + + /** + * If the publisher allows user-sync activity, the platform will call this function and the adapter may register pixels and/or iframe user syncs. + * + * @param {*} syncOptions + * @param {*} serverResponses + * @param {*} gdprConsent + */ + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let url = `https://${hostDomain}/cs/usersync.php?`; + + // GDPR & CCPA + if (gdprConsent) { + url += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + url += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + if (uspConsent) { + url += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + // SyncOptions + if (syncOptions.iframeEnabled) { + url += '&type=iframe' + return [{ + type: 'iframe', + url: url + }]; + } else { + url += '&type=img' + return [{ + type: 'image', + url: url + }]; + } + } +} + +/** + * Parse the response and generate one bid object. + * + * @param {object} serverBid Bid by OpenRTB 2.5 + * @returns {object} Prebid banner bidObject + */ +function interpretBannerBid(serverBid, request) { + return { + requestId: request.bids.bidId, + cpm: serverBid.price, + currency: serverBid.cur ? serverBid.cur : CUR, + width: serverBid.w, + height: serverBid.h, + ad: serverBid.adm, + ttl: TTL, + creativeId: serverBid.crid, + netRevenue: true, + mediaType: BANNER, + } +} + +/** +* Build and makes the request +* +* @param {*} validBidRequests +* @param {*} bidderRequest +* @return response ad +*/ +function buildOneRequest(validBidRequests, bidderRequest) { + HOST = utils.deepAccess(validBidRequests, 'params.host'); + hostDomain = HOST.split('/', 1)[0]; + + const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); + const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); + const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); + const bannerMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.banner'); + const { refererInfo } = bidderRequest; + + // let requests = []; + let payload = {}; + let publisher = {}; + let tagid; + let api = {}; + + // > App/Site object + if (utils.deepAccess(validBidRequests, 'params.app')) { + let app = {}; + app.name = utils.deepAccess(validBidRequests, 'params.app.name'); + app.bundle = utils.deepAccess(validBidRequests, 'params.app.bundle'); + app.domain = utils.deepAccess(validBidRequests, 'params.app.domain'); + publisher.name = utils.deepAccess(validBidRequests, 'params.app.publisher.name'); + publisher.domain = utils.deepAccess(validBidRequests, 'params.app.publisher.domain'); + tagid = `${app.name}_typeAdBanVid_${getOs()}`; + payload.app = app; + api[0] = utils.deepAccess(validBidRequests, 'params.api') ? utils.deepAccess(validBidRequests, 'params.api') : [3, 5]; + } else { + let site = {}; + site.name = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + site.bundle = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + site.domain = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + publisher.name = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + publisher.domain = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + tagid = `${site.name}_typeAdBanVid_${getOs()}`; + payload.site = site; + } + // < App/Site object + + // > Imp object + let imp = {}; + let w; + let h; + + if (bannerMediaType) { + let banner = {}; + w = bannerMediaType.sizes[0][0]; + h = bannerMediaType.sizes[0][1]; + banner.w = w; + banner.h = h; + 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 + } else { + banner.pos = 4 + } + + banner.api = api; + + let format = {}; + format[0] = {}; + format[0].w = w; + format[0].h = h; + banner.format = format; + + imp.banner = banner; + } + + imp.id = validBidRequests.bidId; + imp.tagid = tagid; + imp.secure = 1; + + imp.bidfloor = utils.deepAccess(validBidRequests, 'params.bidfloor'); + // < Imp object + + // > Device object + let device = {}; + // Mandatory + device.os = getOs(); + device.ip = 'peer'; + device.ua = navigator.userAgent; + device.ifa = validBidRequests.ifa; + + // Optional + device.h = screen.height; + device.w = screen.width; + device.dnt = utils.getDNT() ? 1 : 0; + device.language = getLanguage(); + device.make = navigator.vendor ? navigator.vendor : ''; + + let geo = {}; + geo.country = utils.deepAccess(validBidRequests, 'params.geo.country'); + // < Device object + + // > Params + let params = {}; + params.host = 'tappx.com'; + params.tappxkey = TAPPXKEY; + params.endpoint = ENDPOINT; + params.bidfloor = BIDFLOOR; + // < Params + + // > GDPR + let regs = {}; + regs.gdpr = 0; + if (!(bidderRequest.gdprConsent == null)) { + if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { regs.gdpr = bidderRequest.gdprConsent.gdprApplies; } + if (regs.gdpr) { regs.consent = bidderRequest.gdprConsent.consentString; } + } + + // CCPA + regs.ext = {}; + if (!(bidderRequest.uspConsent == null)) { + regs.ext.us_privacy = bidderRequest.uspConsent; + } + + // COPPA compliance + if (config.getConfig('coppa') === true) { + regs.coppa = config.getConfig('coppa') === true ? 1 : 0; + } + // < GDPR + + // > Payload + payload.id = validBidRequests.auctionId; + payload.test = utils.deepAccess(validBidRequests, 'params.test') ? 1 : 0; + payload.at = 1; + payload.tmax = bidderRequest.timeout ? bidderRequest.timeout : 600; + payload.bidder = BIDDER_CODE; + payload.imp = [imp]; + + payload.device = device; + payload.params = params; + payload.regs = regs; + // < Payload + + return { + method: 'POST', + url: `https://${HOST}/${ENDPOINT}?type_cnn=prebidjs`, + data: JSON.stringify(payload), + bids: validBidRequests + }; +} + +function getLanguage() { + const language = navigator.language ? 'language' : 'userLanguage'; + return navigator[language].split('-')[0]; +} + +function getOs() { + let ua = navigator.userAgent; + if (ua == null) { return 'unknown'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'ios'; } else if (ua.match(/Android/)) { return 'android'; } else if (ua.match(/Window/)) { return 'windows'; } else { return 'unknown'; } +} + +registerBidder(spec); diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md new file mode 100644 index 00000000000..d9ffd98b6c5 --- /dev/null +++ b/modules/tappxBidAdapter.md @@ -0,0 +1,37 @@ +# Overview +``` +Module Name: Tappx Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@tappx.com +``` + +# Description +Module that connects to :tappx demand sources. +Please use ```tappx``` as the bidder code. +Ads sizes available: [320,50], [300,250], [320,480], [1024,768], [728,90] + +# Banner Test Parameters +``` + var adUnits = [ + { + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [[320,50]] + } + }, + bids: [ + { + bidder: "tappx", + params: { + host: "testing.ssp.tappx.com/rtb/v2/", + tappxkey: "pub-1234-android-1234", + endpoint: "ZZ1234PBJS", + bidfloor: 0.005, + test: true // Optional for testing purposes + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js new file mode 100644 index 00000000000..c4410d8ce5e --- /dev/null +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -0,0 +1,86 @@ +import { assert } from 'chai'; +import {spec} from 'modules/tappxBidAdapter'; + +describe('Tappx adapter tests', function () { + describe('isBidRequestValid', function () { + let bid = { bidder: 'tappx', params: { host: 'testing.ssp.tappx.com', tappxkey: 'pub-1234-test-1234', endpoint: 'ZZ1234PBJS', bidfloor: 0.005 } }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + const bid = { + host: 'testing.ssp.tappx.com' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequest', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + // Web Test + let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 480]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + // App Test + let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5], 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 50]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'bidderRequestId': '1ae5c6a02684df', 'bids': [{'bidder': 'tappx', 'params': {'host': 'tests.tappx.com', 'tappxkey': 'pub-1234-test-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'banner-ad-div', 'transactionId': 'c44cdbde-ab6d-47a0-8dde-6b4ff7909a35', 'sizes': [[320, 50]], 'bidId': '2e3a5feb30cfe4', 'bidderRequestId': '1ae5c6a02684df', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1611308859094, 'timeout': 700, 'refererInfo': {'referer': 'http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; + + it('should add gdpr/usp consent information to the request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request[0].data); + + expect(payload.regs.gdpr).to.exist.and.to.be.true; + expect(payload.regs.consent).to.exist.and.to.equal(consentString); + expect(payload.regs.ext.us_privacy).to.exist; + }); + + it('should properly build a banner request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + + it('should properly build a banner request with app params', function () { + const request = spec.buildRequests(validAppBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + }); + + describe('interpretResponse', function () { + const bidRequest = { + data: {}, + bids: [ {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.05, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '47dd44e8-e7db-417c-a8f1-621a2e1a117d', 'sizes': [[320, 480]], 'bidId': '2170932097e505', 'bidderRequestId': '140ba7a1ab7aeb', 'auctionId': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0} ] + }; + + const serverResponse = {'body': {'id': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'bidid': 'bid3811165568213389257', 'seatbid': [{'seat': '1', 'group': 0, 'bid': [{'id': '3811165568213389257', 'impid': 1, 'price': 0.05, 'adm': "\t", 'w': 320, 'h': 480, 'lurl': 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', 'cid': '01744fbb521e9fb10ffea926190effea', 'crid': 'a13cf884e66e7c660afec059c89d98b6', 'adomain': []}]}], 'cur': 'USD'}, 'headers': {}}; + it('receive reponse with single placement', function () { + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = bids[0]; + expect(bid.cpm).to.exist; + expect(bid.ad).to.match(/^ ortb2.site.ATTR - fpd.context.data.ATTR --> ortb2.site.ext.data - fpd.user.ATTR --> ortb2.user.ATTR - fpd.user.data.ATTR --> ortb2.user.ext.data 2) gptPreAuction: a) move adunit.fpd to adunit.ortb2 b) adUnit.ortb2Imp.ext.data.adserver.{name, adSlot} c) pbAdSlot moves to AdUnit.ortb2Imp.ext.data.pbAdSlot 3) pbsBidAdapter a) merge the new ortb2 and AdUnit.ortb2Imp.ext objects into the OpenRTB JSON. b) therefore imp[].ext.context.data.pbadslot is now changed to imp[].ext.data.pbadslot (no context) c) read adUnit.ortb2Imp.ext.data.adserver from the new location. Output location is moved to imp[].ext.data.adserver (no context) * FPD 2.0 Update Update to adrelevantis adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to amx adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to avocet adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to criteo adapter to look at config.ortb2 instead of config.fpd * Update to correct imp fpd structure * Update to s2s adapter to coincide with imp fpd alteration * Update to consolidate several lines of duplicate code into one location * Slight modification for ortb2Imp to use ortb2Imp.ext as opposed to ortb2Imp.ext.data * FPD 2.0 Update Update to grid adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to inmar adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to luponmedia adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to smaato adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to triplelift adapter to look at config.ortb2 instead of config.fpd * Update to gptPreAuction to move over to imp level ortb2 * FPD 2.0 Update Update to triplelift adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update * FPD 2.0 Update Update to jwplayerRtd adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to admixer adapter to look at config.ortb2 instead of config.fpd * FPD 2.0 Update Update to rubicon adapter to look at config.ortb2 instead of config.fpd * Update to fix keyword bug * Added backwards compatibility functions for FPD both global/bidder and adunit level data * Update to utilize new backward functionality for fpd 2.0 * Removed extra new line * Update to include new backward functionality for FPD 2.0 data * Update to utilize new backward functionality to pass FPD 2.0 data * Update to utilize backward functionality to pass FPD 2.0 data * Update to utilize backward functionality to pass FPD 2.0 data * Update to utilize backward functionality to pass FPD 2.0 data * Update to utilize backward functionality to pass FPD 2.0 data * Update to utilize backward functionality to pass FPD 2.0 data * Fixed typo in fpd config object location * Uodate to utilize backward functionality to pass FPD 2.0 data * Update to change all ortb2Imp.ext.data.adserver.adSlot references to ortb2Imp.ext.data.adserver.adslot - all lowercase. Corresponding adapter and unit tests to adhere to these changes * Fixed typo * Fixed typo * FPD 2.0 update to rubicon adapter to pass iab values * Updates: 1) Change function name 2) addAdUnits always pass array 3) Remove unecessary comment 4) Bug fix for ortb2.user.data to be filtered on legacy fpd conversion --- modules/admixerBidAdapter.js | 8 +- modules/adrelevantisBidAdapter.js | 6 +- modules/amxBidAdapter.js | 2 +- modules/avocetBidAdapter.js | 2 +- modules/criteoBidAdapter.js | 11 +- modules/gptPreAuction.js | 32 +-- modules/gridBidAdapter.js | 4 +- modules/inmarBidAdapter.js | 2 +- modules/jwplayerRtdProvider.js | 4 +- modules/luponmediaBidAdapter.js | 7 +- modules/prebidServerBidAdapter/index.js | 60 +++--- modules/rubiconBidAdapter.js | 97 +++++---- modules/smaatoBidAdapter.js | 6 +- modules/tripleliftBidAdapter.js | 11 +- src/adapterManager.js | 2 +- src/config.js | 135 ++++++++++++- src/prebid.js | 6 +- test/spec/adUnits_spec.js | 11 + test/spec/config_spec.js | 15 ++ .../modules/adrelevantisBidAdapter_spec.js | 10 +- test/spec/modules/amxBidAdapter_spec.js | 20 +- test/spec/modules/criteoBidAdapter_spec.js | 24 ++- test/spec/modules/gptPreAuction_spec.js | 54 ++--- test/spec/modules/gridBidAdapter_spec.js | 10 + test/spec/modules/jwplayerRtdProvider_spec.js | 48 ++--- .../modules/prebidServerBidAdapter_spec.js | 110 ++++++---- test/spec/modules/rubiconBidAdapter_spec.js | 190 +++++++++++------- test/spec/modules/smaatoBidAdapter_spec.js | 6 +- .../spec/modules/tripleliftBidAdapter_spec.js | 20 +- 29 files changed, 583 insertions(+), 330 deletions(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index d2a6f9a639e..6cf738a0086 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -21,7 +21,7 @@ export const spec = { buildRequests: function (validRequest, bidderRequest) { const payload = { imps: [], - fpd: config.getConfig('fpd') + fpd: config.getLegacyFpd(config.getConfig('ortb2')) }; let endpointUrl; if (bidderRequest) { @@ -42,7 +42,11 @@ export const spec = { } } validRequest.forEach((bid) => { - payload.imps.push(bid); + let imp = {}; + Object.keys(bid).forEach(key => { + (key === 'ortb2Imp') ? imp.fpd = config.getLegacyImpFpd(bid[key]) : imp[key] = bid[key]; + }); + payload.imps.push(imp); }); const payloadString = JSON.stringify(payload); return { diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 5da941c65ca..c6298cffde9 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -120,11 +120,11 @@ export const spec = { payload.referrer_detection = refererinfo; } - let fpdcfg = config.getConfig('fpd') + let fpdcfg = config.getLegacyFpd(config.getConfig('ortb2')); if (fpdcfg && fpdcfg.context) { let fdata = { - keywords: fpdcfg.context.keywords, - category: fpdcfg.context.data.category + keywords: fpdcfg.context.keywords || '', + category: utils.deepAccess(fpdcfg, 'context.data.category') || '' } payload.fpd = fdata; } diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index a1fa202c154..497c2142b9b 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -259,7 +259,7 @@ export const spec = { d: '', m: createBidMap(bidRequests), cpp: config.getConfig('coppa') ? 1 : 0, - fpd: config.getConfig('fpd'), + fpd: config.getLegacyFpd(config.getConfig('ortb2')), eids: values(bidRequests.reduce((all, bid) => { // we only want unique ones in here if (bid == null || bid.userIdAsEids == null) { diff --git a/modules/avocetBidAdapter.js b/modules/avocetBidAdapter.js index 7a9e5062c0f..1283bb865d4 100644 --- a/modules/avocetBidAdapter.js +++ b/modules/avocetBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { const publisherDomain = config.getConfig('publisherDomain'); // First-party data from config - const fpd = config.getConfig('fpd'); + const fpd = config.getLegacyFpd(config.getConfig('ortb2')); // GDPR status and TCF consent string let tcfConsentString; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index e3a6b9eaa12..41cbb0670c8 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -28,7 +28,7 @@ export const spec = { gvlid: GVLID, supportedMediaTypes: [ BANNER, VIDEO, NATIVE ], - /** + /** f * @param {object} bid * @return {boolean} */ @@ -56,10 +56,11 @@ export const spec = { buildRequests: (bidRequests, bidderRequest) => { let url; let data; + let fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; Object.assign(bidderRequest, { - publisherExt: config.getConfig('fpd.context'), - userExt: config.getConfig('fpd.user'), + publisherExt: fpd.context, + userExt: fpd.user, ceh: config.getConfig('criteo.ceh') }); @@ -280,8 +281,8 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (bidRequest.params.zoneId) { slot.zoneid = bidRequest.params.zoneId; } - if (bidRequest.fpd && bidRequest.fpd.context) { - slot.ext = bidRequest.fpd.context; + if (utils.deepAccess(bidRequest, 'ortb2Imp.ext')) { + slot.ext = bidRequest.ortb2Imp.ext; } if (bidRequest.params.ext) { slot.ext = Object.assign({}, slot.ext, bidRequest.params.ext); diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js index 48b72671d6a..ee2b5406453 100644 --- a/modules/gptPreAuction.js +++ b/modules/gptPreAuction.js @@ -26,46 +26,48 @@ export const appendGptSlots = adUnits => { if (matchingAdUnitCode) { const adUnit = adUnitMap[matchingAdUnitCode]; - adUnit.fpd = adUnit.fpd || {}; - adUnit.fpd.context = adUnit.fpd.context || {}; + adUnit.ortb2Imp = adUnit.ortb2Imp || {}; + adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; + adUnit.ortb2Imp.ext.data = adUnit.ortb2Imp.ext.data || {}; - const context = adUnit.fpd.context; - context.adServer = context.adServer || {}; - context.adServer.name = 'gam'; - context.adServer.adSlot = slot.getAdUnitPath(); + const context = adUnit.ortb2Imp.ext.data; + context.adserver = context.adserver || {}; + context.adserver.name = 'gam'; + context.adserver.adslot = slot.getAdUnitPath(); } }); }; export const appendPbAdSlot = adUnit => { - adUnit.fpd = adUnit.fpd || {}; - adUnit.fpd.context = adUnit.fpd.context || {}; - const context = adUnit.fpd.context; + adUnit.ortb2Imp = adUnit.ortb2Imp || {}; + adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; + adUnit.ortb2Imp.ext.data = adUnit.ortb2Imp.ext.data || {}; + const context = adUnit.ortb2Imp.ext.data; const { customPbAdSlot } = _currentConfig; if (customPbAdSlot) { - context.pbAdSlot = customPbAdSlot(adUnit.code, utils.deepAccess(context, 'adServer.adSlot')); + context.pbadslot = customPbAdSlot(adUnit.code, utils.deepAccess(context, 'adserver.adslot')); return; } // use context.pbAdSlot if set - if (context.pbAdSlot) { + if (context.pbadslot) { return; } // use data attribute 'data-adslotid' if set try { const adUnitCodeDiv = document.getElementById(adUnit.code); if (adUnitCodeDiv.dataset.adslotid) { - context.pbAdSlot = adUnitCodeDiv.dataset.adslotid; + context.pbadslot = adUnitCodeDiv.dataset.adslotid; return; } } catch (e) {} // banner adUnit, use GPT adunit if defined - if (context.adServer) { - context.pbAdSlot = context.adServer.adSlot; + if (utils.deepAccess(context, 'adserver.adslot')) { + context.pbadslot = context.adserver.adslot; return; } - context.pbAdSlot = adUnit.code; + context.pbadslot = adUnit.code; }; export const makeBidRequestsHook = (fn, adUnits, ...args) => { diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 8ab2e2b8a95..964b34dcfa2 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -180,8 +180,8 @@ export const spec = { } const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + 'user': utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null }); if (configKeywords.length) { diff --git a/modules/inmarBidAdapter.js b/modules/inmarBidAdapter.js index 7583985b23c..e1edd935587 100755 --- a/modules/inmarBidAdapter.js +++ b/modules/inmarBidAdapter.js @@ -40,7 +40,7 @@ export const spec = { uspConsent: bidderRequest.uspConsent, currencyCode: config.getConfig('currency.adServerCurrency'), coppa: config.getConfig('coppa'), - firstPartyData: config.getConfig('fpd'), + firstPartyData: config.getLegacyFpd(config.getConfig('ortb2')), prebidVersion: '$prebid.version$' }; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index a29c4ce5fa7..8332e720ae7 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -144,7 +144,7 @@ function enrichBidRequest(bidReqConfig, onDone) { * @param {function} onDone */ export function enrichAdUnits(adUnits) { - const fpdFallback = config.getConfig('fpd.context.data.jwTargeting'); + const fpdFallback = config.getConfig('ortb2.site.ext.data.jwTargeting'); adUnits.forEach(adUnit => { const jwTargeting = extractPublisherParams(adUnit, fpdFallback); if (!jwTargeting || !Object.keys(jwTargeting).length) { @@ -170,7 +170,7 @@ function supportsInstreamVideo(mediaTypes) { export function extractPublisherParams(adUnit, fallback) { let adUnitTargeting; try { - adUnitTargeting = adUnit.fpd.context.data.jwTargeting; + adUnitTargeting = adUnit.ortb2Imp.ext.data.jwTargeting; } catch (e) {} if (!adUnitTargeting && !supportsInstreamVideo(adUnit.mediaTypes)) { diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 4f7fd2ae1a0..29b54f77fbb 100644 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -279,8 +279,9 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); - const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); + const fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; + const siteData = Object.assign({}, bidRequest.params.inventory, fpd.context); + const userData = Object.assign({}, bidRequest.params.visitor, fpd.user); if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { const bidderData = { @@ -301,7 +302,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { utils.deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); } - const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + const pbAdSlot = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); } diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7cfef6a0784..20cf93caae5 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -336,18 +336,18 @@ function addBidderFirstPartyDataToRequest(request) { const bidderConfig = config.getBidderConfig(); const fpdConfigs = Object.keys(bidderConfig).reduce((acc, bidder) => { const currBidderConfig = bidderConfig[bidder]; - if (currBidderConfig.fpd) { - const fpd = {}; - if (currBidderConfig.fpd.context) { - fpd.site = currBidderConfig.fpd.context; + if (currBidderConfig.ortb2) { + const ortb2 = {}; + if (currBidderConfig.ortb2.site) { + ortb2.site = currBidderConfig.ortb2.site; } - if (currBidderConfig.fpd.user) { - fpd.user = currBidderConfig.fpd.user; + if (currBidderConfig.ortb2.user) { + ortb2.user = currBidderConfig.ortb2.user; } acc.push({ bidders: [ bidder ], - config: { fpd } + config: { ortb2 } }); } return acc; @@ -618,23 +618,27 @@ const OPEN_RTB_PROTOCOL = { const imp = { id: adUnit.code, ext, secure: s2sConfig.secure }; - /** - * Prebid AdSlot - * @type {(string|undefined)} - */ - const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(imp, 'ext.context.data.pbadslot', pbAdSlot); - } - - /** - * Copy GAM AdUnit and Name to imp - */ - ['name', 'adSlot'].forEach(name => { - /** @type {(string|undefined)} */ - const value = utils.deepAccess(adUnit, `fpd.context.adserver.${name}`); - if (typeof value === 'string' && value) { - utils.deepSetValue(imp, `ext.context.data.adserver.${name.toLowerCase()}`, value); + const ortb2 = {...utils.deepAccess(adUnit, 'ortb2Imp.ext.data')}; + Object.keys(ortb2).forEach(prop => { + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + if (prop === 'pbadslot') { + if (typeof ortb2[prop] === 'string' && ortb2[prop]) utils.deepSetValue(imp, 'ext.data.pbadslot', ortb2[prop]); + } else if (prop === 'adserver') { + /** + * Copy GAM AdUnit and Name to imp + */ + ['name', 'adslot'].forEach(name => { + /** @type {(string|undefined)} */ + const value = utils.deepAccess(ortb2, `adserver.${name}`); + if (typeof value === 'string' && value) { + utils.deepSetValue(imp, `ext.data.adserver.${name.toLowerCase()}`, value); + } + }); + } else { + utils.deepSetValue(imp, `ext.data.${prop}`, ortb2[prop]); } }); @@ -763,12 +767,12 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'regs.coppa', 1); } - const commonFpd = getConfig('fpd') || {}; - if (commonFpd.context) { - utils.deepSetValue(request, 'site.ext.data', commonFpd.context); + const commonFpd = getConfig('ortb2') || {}; + if (commonFpd.site) { + utils.deepSetValue(request, 'site', commonFpd.site); } if (commonFpd.user) { - utils.deepSetValue(request, 'user.ext.data', commonFpd.user); + utils.deepSetValue(request, 'user', commonFpd.user); } addBidderFirstPartyDataToRequest(request); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 7297c82440f..9905498edee 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -871,21 +871,26 @@ function addVideoParameters(data, bidRequest) { } function applyFPD(bidRequest, mediaType, data) { - const bidFpd = { - user: {...bidRequest.params.visitor}, - context: {...bidRequest.params.inventory} + const BID_FPD = { + user: {ext: {data: {...bidRequest.params.visitor}}}, + site: {ext: {data: {...bidRequest.params.inventory}}} }; - if (bidRequest.params.keywords) bidFpd.context.keywords = (utils.isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; + if (bidRequest.params.keywords) BID_FPD.site.keywords = (utils.isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; - let fpd = utils.mergeDeep({}, config.getConfig('fpd') || {}, bidRequest.fpd || {}, bidFpd); - - const map = {user: {banner: 'tg_v.', code: 'user'}, context: {banner: 'tg_i.', code: 'site'}, adserver: 'dfp_ad_unit_code'}; - let obj = {}; - let impData = {}; - let keywords = []; + let fpd = utils.mergeDeep({}, config.getConfig('ortb2') || {}, BID_FPD); + let impData = utils.deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; + const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; const validate = function(prop, key) { - if (typeof prop === 'object' && !Array.isArray(prop)) { + if (key === 'data' && Array.isArray(prop)) { + return prop.filter(name => name.segment && utils.deepAccess(name, 'ext.taxonomyname').match(/iab/i)).map(value => { + let segments = value.segment.filter(obj => obj.id).reduce((result, obj) => { + result.push(obj.id); + return result; + }, []); + if (segments.length > 0) return segments.toString(); + }).toString(); + } else if (typeof prop === 'object' && !Array.isArray(prop)) { utils.logWarn('Rubicon: Filtered FPD key: ', key, ': Expected value to be string, integer, or an array of strings/ints'); } else if (typeof prop !== 'undefined') { return (Array.isArray(prop)) ? prop.filter(value => { @@ -895,53 +900,43 @@ function applyFPD(bidRequest, mediaType, data) { }).toString() : prop.toString(); } }; - - Object.keys(fpd).filter(value => fpd[value] && map[value] && typeof fpd[value] === 'object').forEach((type) => { - obj[map[type].code] = Object.keys(fpd[type]).filter(value => typeof fpd[type][value] !== 'undefined').reduce((result, key) => { - if (key === 'keywords') { - if (!Array.isArray(fpd[type][key]) && mediaType === BANNER) fpd[type][key] = [fpd[type][key]] - - result[key] = fpd[type][key]; - - if (mediaType === BANNER) keywords = keywords.concat(fpd[type][key]); - } else if (key === 'data') { - utils.mergeDeep(result, {ext: {data: fpd[type][key]}}); - } else if (key === 'adServer' || key === 'pbAdSlot') { - (key === 'adServer') ? ['name', 'adSlot'].forEach(name => { - const value = validate(fpd[type][key][name]); - if (value) utils.deepSetValue(impData, `adserver.${name.toLowerCase()}`, value.replace(/^\/+/, '')) - }) : impData[key.toLowerCase()] = fpd[type][key].replace(/^\/+/, '') - } else { - utils.mergeDeep(result, {ext: {data: {[key]: fpd[type][key]}}}); - } - - return result; - }, {}); - - if (mediaType === BANNER) { - let duplicate = (typeof obj[map[type].code].ext === 'object' && obj[map[type].code].ext.data) || {}; - - Object.keys(duplicate).forEach((key) => { - const val = (key === 'adserver') ? duplicate.adserver.adslot : validate(duplicate[key], key); - - if (val) data[(map[key]) ? `${map[type][BANNER]}${map[key]}` : `${map[type][BANNER]}${key}`] = val; - }); - } - }); + const addBannerData = function(obj, name, key) { + let val = validate(obj, key); + let loc = (MAP[key]) ? `${MAP[key]}` : (key === 'data') ? `${MAP[name]}iab` : `${MAP[name]}${key}`; + data[loc] = (data[loc]) ? data[loc].concat(',', val) : val; + } Object.keys(impData).forEach((key) => { - if (mediaType === BANNER) { - (map[key]) ? data[`tg_i.${map[key]}`] = impData[key].adslot : data[`tg_i.${key.toLowerCase()}`] = impData[key]; - } else { - utils.mergeDeep(data.imp[0], {ext: {context: {data: {[key]: impData[key]}}}}); + if (key === 'adserver') { + ['name', 'adslot'].forEach(prop => { + if (impData[key][prop]) impData[key][prop] = impData[key][prop].replace(/^\/+/, ''); + }); + } else if (key === 'pbadslot') { + impData[key] = impData[key].replace(/^\/+/, ''); } }); if (mediaType === BANNER) { - let kw = validate(keywords, 'keywords'); - if (kw) data.kw = kw; + ['site', 'user'].forEach(name => { + Object.keys(fpd[name]).forEach((key) => { + if (key !== 'ext') { + addBannerData(fpd[name][key], name, key); + } else if (fpd[name][key].data) { + Object.keys(fpd[name].ext.data).forEach((key) => { + addBannerData(fpd[name].ext.data[key], name, key); + }); + } + }); + }); + Object.keys(impData).forEach((key) => { + (key === 'adserver') ? addBannerData(impData[key].adslot, name, key) : addBannerData(impData[key], 'site', key); + }); } else { - utils.mergeDeep(data, obj); + if (Object.keys(impData).length) { + utils.mergeDeep(data.imp[0].ext, {data: impData}); + } + + utils.mergeDeep(data, fpd); } } diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index 93915689cee..fdb4d2df984 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -98,8 +98,10 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { } }; - Object.assign(request.user, config.getConfig('fpd.user')); - Object.assign(request.site, config.getConfig('fpd.context')); + let fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; + + Object.assign(request.user, fpd.user); + Object.assign(request.site, fpd.context); if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies === true) { utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index b681b0980ea..e8d248eea03 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -122,8 +122,8 @@ function _buildPostBody(bidRequests) { } else if (bidRequest.mediaTypes.banner) { imp.banner = { format: _sizes(bidRequest.sizes) }; }; - if (!utils.isEmpty(bidRequest.fpd)) { - imp.fpd = _getAdUnitFpd(bidRequest.fpd); + if (!utils.isEmpty(bidRequest.ortb2Imp)) { + imp.fpd = _getAdUnitFpd(bidRequest.ortb2Imp); } return imp; }); @@ -190,9 +190,10 @@ function _getGlobalFpd() { const fpd = {}; const context = {} const user = {}; + const ortbData = config.getLegacyFpd(config.getConfig('ortb2')) || {}; - const fpdContext = Object.assign({}, config.getConfig('fpd.context')); - const fpdUser = Object.assign({}, config.getConfig('fpd.user')); + const fpdContext = Object.assign({}, ortbData.context); + const fpdUser = Object.assign({}, ortbData.user); _addEntries(context, fpdContext); _addEntries(user, fpdUser); @@ -210,7 +211,7 @@ function _getAdUnitFpd(adUnitFpd) { const fpd = {}; const context = {}; - _addEntries(context, adUnitFpd.context); + _addEntries(context, adUnitFpd.ext); if (!utils.isEmpty(context)) { fpd.context = context; diff --git a/src/adapterManager.js b/src/adapterManager.js index cb84607e130..f7f5d821932 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -68,7 +68,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) } bid = Object.assign({}, bid, getDefinedParams(adUnit, [ - 'fpd', + 'ortb2Imp', 'mediaType', 'renderer', 'storedAuctionResponse' diff --git a/src/config.js b/src/config.js index daaf739bbbd..51184a8014d 100644 --- a/src/config.js +++ b/src/config.js @@ -324,6 +324,119 @@ export function newConfig() { return bidderConfig; } + /** + * Returns backwards compatible FPD data for modules + */ + function getLegacyFpd(obj) { + if (typeof obj !== 'object') return; + + let duplicate = {}; + + Object.keys(obj).forEach((type) => { + let prop = (type === 'site') ? 'context' : type; + duplicate[prop] = (prop === 'context' || prop === 'user') ? Object.keys(obj[type]).filter(key => key !== 'data').reduce((result, key) => { + if (key === 'ext') { + utils.mergeDeep(result, obj[type][key]); + } else { + utils.mergeDeep(result, {[key]: obj[type][key]}); + } + + return result; + }, {}) : obj[type]; + }); + + return duplicate; + } + + /** + * Returns backwards compatible FPD data for modules + */ + function getLegacyImpFpd(obj) { + if (typeof obj !== 'object') return; + + let duplicate = {}; + + if (utils.deepAccess(obj, 'ext.data')) { + Object.keys(obj.ext.data).forEach((key) => { + if (key === 'pbadslot') { + utils.mergeDeep(duplicate, {context: {pbAdSlot: obj.ext.data[key]}}); + } else if (key === 'adserver') { + utils.mergeDeep(duplicate, {context: {adServer: obj.ext.data[key]}}); + } else { + utils.mergeDeep(duplicate, {context: {data: {[key]: obj.ext.data[key]}}}); + } + }); + } + + return duplicate; + } + + /** + * Copy FPD over to OpenRTB standard format in config + */ + function convertFpd(opt) { + let duplicate = {}; + + Object.keys(opt).forEach((type) => { + let prop = (type === 'context') ? 'site' : type; + duplicate[prop] = (prop === 'site' || prop === 'user') ? Object.keys(opt[type]).reduce((result, key) => { + if (key === 'data') { + utils.mergeDeep(result, {ext: {data: opt[type][key]}}); + } else { + utils.mergeDeep(result, {[key]: opt[type][key]}); + } + + return result; + }, {}) : opt[type]; + }); + + return duplicate; + } + + /** + * Copy Impression FPD over to OpenRTB standard format in config + * Only accepts bid level context.data values with pbAdSlot and adServer exceptions + */ + function convertImpFpd(opt) { + let duplicate = {}; + + Object.keys(opt).filter(prop => prop === 'context').forEach((type) => { + Object.keys(opt[type]).forEach((key) => { + if (key === 'data') { + utils.mergeDeep(duplicate, {ext: {data: opt[type][key]}}); + } else { + if (typeof opt[type][key] === 'object' && !Array.isArray(opt[type][key])) { + Object.keys(opt[type][key]).forEach(data => { + utils.mergeDeep(duplicate, {ext: {data: {[key.toLowerCase()]: {[data.toLowerCase()]: opt[type][key][data]}}}}); + }); + } else { + utils.mergeDeep(duplicate, {ext: {data: {[key.toLowerCase()]: opt[type][key]}}}); + } + } + }); + }); + + return duplicate; + } + + /** + * Copy FPD over to OpenRTB standard format in each adunit + */ + function convertAdUnitFpd(arr) { + let convert = []; + + arr.forEach((adunit) => { + if (adunit.fpd) { + (adunit['ortb2Imp']) ? utils.mergeDeep(adunit['ortb2Imp'], convertImpFpd(adunit.fpd)) : adunit['ortb2Imp'] = convertImpFpd(adunit.fpd); + convert.push((({ fpd, ...duplicate }) => duplicate)(adunit)); + } else { + convert.push(adunit); + } + }); + + return convert; + } + /* * Sets configuration given an object containing key-value pairs and calls * listeners that were added by the `subscribe` function @@ -338,13 +451,14 @@ export function newConfig() { let topicalConfig = {}; topics.forEach(topic => { - let option = options[topic]; + let prop = (topic === 'fpd') ? 'ortb2' : topic; + let option = (topic === 'fpd') ? convertFpd(options[topic]) : options[topic]; - if (utils.isPlainObject(defaults[topic]) && utils.isPlainObject(option)) { - option = Object.assign({}, defaults[topic], option); + if (utils.isPlainObject(defaults[prop]) && utils.isPlainObject(option)) { + option = Object.assign({}, defaults[prop], option); } - topicalConfig[topic] = config[topic] = option; + topicalConfig[prop] = config[prop] = option; }); callSubscribers(topicalConfig); @@ -437,11 +551,13 @@ export function newConfig() { bidderConfig[bidder] = {}; } Object.keys(config.config).forEach(topic => { - let option = config.config[topic]; + let prop = (topic === 'fpd') ? 'ortb2' : topic; + let option = (topic === 'fpd') ? convertFpd(config.config[topic]) : config.config[topic]; + if (utils.isPlainObject(option)) { - bidderConfig[bidder][topic] = Object.assign({}, bidderConfig[bidder][topic] || {}, option); + bidderConfig[bidder][prop] = Object.assign({}, bidderConfig[bidder][prop] || {}, option); } else { - bidderConfig[bidder][topic] = option; + bidderConfig[bidder][prop] = option; } }); }); @@ -499,7 +615,10 @@ export function newConfig() { runWithBidder, callbackWithBidder, setBidderConfig, - getBidderConfig + getBidderConfig, + convertAdUnitFpd, + getLegacyFpd, + getLegacyImpFpd }; } diff --git a/src/prebid.js b/src/prebid.js index 3452107effd..6565c1610d8 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -595,11 +595,7 @@ $$PREBID_GLOBAL$$.requestBids.before(executeCallbacks, 49); */ $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.addAdUnits', arguments); - if (utils.isArray(adUnitArr)) { - $$PREBID_GLOBAL$$.adUnits.push.apply($$PREBID_GLOBAL$$.adUnits, adUnitArr); - } else if (typeof adUnitArr === 'object') { - $$PREBID_GLOBAL$$.adUnits.push(adUnitArr); - } + $$PREBID_GLOBAL$$.adUnits.push.apply($$PREBID_GLOBAL$$.adUnits, config.convertAdUnitFpd(utils.isArray(adUnitArr) ? adUnitArr : [adUnitArr])); // emit event events.emit(ADD_AD_UNITS); }; diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index 7dd48a13208..baa5b4ac8c4 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -23,6 +23,16 @@ describe('Publisher API _ AdUnits', function () { } ] }, { + fpd: { + context: { + pbAdSlot: 'adSlotTest', + data: { + inventory: [4], + keywords: 'foo,bar', + visitor: [1, 2, 3], + } + } + }, code: '/1996833/slot-2', sizes: [[468, 60]], bids: [ @@ -85,6 +95,7 @@ describe('Publisher API _ AdUnits', function () { it('the second adUnits value should be same with the adUnits that is added by $$PREBID_GLOBAL$$.addAdUnits();', function () { assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); + assert.deepEqual(adUnit2['ortb2Imp'], {'ext': {'data': {'pbadslot': 'adSlotTest', 'inventory': [4], 'keywords': 'foo,bar', 'visitor': [1, 2, 3]}}}, 'adUnit2 ortb2Imp'); assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 81ce966efb2..0b8dd6978cf 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -6,6 +6,8 @@ const utils = require('src/utils'); let getConfig; let setConfig; +let getBidderConfig; +let setBidderConfig; let setDefaults; describe('config API', function () { @@ -15,6 +17,8 @@ describe('config API', function () { const config = newConfig(); getConfig = config.getConfig; setConfig = config.setConfig; + getBidderConfig = config.getBidderConfig; + setBidderConfig = config.setBidderConfig; setDefaults = config.setDefaults; logErrorSpy = sinon.spy(utils, 'logError'); logWarnSpy = sinon.spy(utils, 'logWarn'); @@ -57,6 +61,17 @@ describe('config API', function () { expect(getConfig('foo')).to.eql({baz: 'qux'}); }); + it('moves fpd config into ortb2 properties', function () { + setConfig({fpd: {context: {keywords: 'foo,bar', data: {inventory: [1]}}}}); + expect(getConfig('ortb2')).to.eql({site: {keywords: 'foo,bar', ext: {data: {inventory: [1]}}}}); + expect(getConfig('fpd')).to.eql(undefined); + }); + + it('moves fpd bidderconfig into ortb2 properties', function () { + setBidderConfig({bidders: ['bidderA'], config: {fpd: {context: {keywords: 'foo,bar', data: {inventory: [1]}}}}}); + expect(getBidderConfig()).to.eql({'bidderA': {ortb2: {site: {keywords: 'foo,bar', ext: {data: {inventory: [1]}}}}}}); + }); + it('sets debugging', function () { setConfig({ debug: true }); expect(getConfig('debug')).to.be.true; diff --git a/test/spec/modules/adrelevantisBidAdapter_spec.js b/test/spec/modules/adrelevantisBidAdapter_spec.js index 11a6a14a353..596f3bade5d 100644 --- a/test/spec/modules/adrelevantisBidAdapter_spec.js +++ b/test/spec/modules/adrelevantisBidAdapter_spec.js @@ -224,12 +224,14 @@ describe('AdrelevantisAdapter', function () { let bidRequest = Object.assign({}, bidRequests[0]); sinon .stub(config, 'getConfig') - .withArgs('fpd') + .withArgs('ortb2') .returns({ - context: { + site: { keywords: 'US Open', - data: { - category: 'sports/tennis' + ext: { + data: { + category: 'sports/tennis' + } } } }); diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 766045b0f3e..0658fe9f33c 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -17,10 +17,26 @@ const embeddedTrackingPixel = `https://1x1.a-mo.net/hbx/g_impression?A=sample&B= const sampleNurl = 'https://example.exchange/nurl'; const sampleFPD = { + site: { + keywords: 'sample keywords', + ext: { + data: { + pageType: 'article' + } + } + }, + user: { + gender: 'O', + yob: 1982, + } +}; + +const legacySampleFPD = { context: { keywords: 'sample keywords', data: { pageType: 'article' + } }, user: { @@ -31,7 +47,7 @@ const sampleFPD = { const stubConfig = (withStub) => { const stub = sinon.stub(config, 'getConfig').callsFake( - (arg) => arg === 'fpd' ? sampleFPD : null + (arg) => arg === 'ortb2' ? sampleFPD : null ) withStub(); @@ -253,7 +269,7 @@ describe('AmxBidAdapter', () => { it('will forward first-party data', () => { stubConfig(() => { const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); - expect(data.fpd).to.deep.equal(sampleFPD) + expect(data.fpd).to.deep.equal(legacySampleFPD) }); }); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index e91d3b10abb..cad1e3f8114 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -820,14 +820,18 @@ describe('The Criteo bidding adapter', function () { it('should properly build a request with first party data', function () { const contextData = { keywords: ['power tools'], - data: { - pageType: 'article' + ext: { + data: { + pageType: 'article' + } } }; const userData = { gender: 'M', - data: { - registered: true + ext: { + data: { + registered: true + } } }; const bidRequests = [ @@ -842,8 +846,8 @@ describe('The Criteo bidding adapter', function () { bidfloor: 0.75 } }, - fpd: { - context: { + ortb2Imp: { + ext: { data: { someContextAttribute: 'abc' } @@ -854,8 +858,8 @@ describe('The Criteo bidding adapter', function () { sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context: contextData, + ortb2: { + site: contextData, user: userData } }; @@ -863,8 +867,8 @@ describe('The Criteo bidding adapter', function () { }); const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.publisher.ext).to.deep.equal(contextData); - expect(request.data.user.ext).to.deep.equal(userData); + expect(request.data.publisher.ext).to.deep.equal({keywords: ['power tools'], data: {pageType: 'article'}}); + expect(request.data.user.ext).to.deep.equal({gender: 'M', data: {registered: true}}); expect(request.data.slots[0].ext).to.deep.equal({ bidfloor: 0.75, data: { diff --git a/test/spec/modules/gptPreAuction_spec.js b/test/spec/modules/gptPreAuction_spec.js index 16b84467af2..c4a81c21d5c 100644 --- a/test/spec/modules/gptPreAuction_spec.js +++ b/test/spec/modules/gptPreAuction_spec.js @@ -30,34 +30,34 @@ describe('GPT pre-auction module', () => { '
test2
'; it('should be unchanged if already defined on adUnit', () => { - const adUnit = { fpd: { context: { pbAdSlot: '12345' } } }; + const adUnit = { ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('12345'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('12345'); }); it('should use adUnit.code if matching id exists', () => { - const adUnit = { code: 'foo1', fpd: { context: {} } }; + const adUnit = { code: 'foo1', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('bar1'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('bar1'); }); it('should use the gptSlot.adUnitPath if the adUnit.code matches a div id but does not have a data-adslotid', () => { - const adUnit = { code: 'foo3', mediaTypes: { banner: { sizes: [[250, 250]] } }, fpd: { context: { adServer: { name: 'gam', adSlot: '/baz' } } } }; + const adUnit = { code: 'foo3', mediaTypes: { banner: { sizes: [[250, 250]] } }, ortb2Imp: { ext: { data: { adserver: { name: 'gam', adslot: '/baz' } } } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('/baz'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('/baz'); }); it('should use the video adUnit.code (which *should* match the configured "adSlotName", but is not being tested) if there is no matching div with "data-adslotid" defined', () => { - const adUnit = { code: 'foo4', mediaTypes: { video: { sizes: [[250, 250]] } }, fpd: { context: {} } }; + const adUnit = { code: 'foo4', mediaTypes: { video: { sizes: [[250, 250]] } }, ortb2Imp: { ext: { data: {} } } }; adUnit.code = 'foo5'; appendPbAdSlot(adUnit, undefined); - expect(adUnit.fpd.context.pbAdSlot).to.equal('foo5'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('foo5'); }); it('should use the adUnit.code if all other sources failed', () => { - const adUnit = { code: 'foo4', fpd: { context: {} } }; + const adUnit = { code: 'foo4', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit, undefined); - expect(adUnit.fpd.context.pbAdSlot).to.equal('foo4'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('foo4'); }); it('should use the customPbAdSlot function if one is given', () => { @@ -67,32 +67,32 @@ describe('GPT pre-auction module', () => { } }); - const adUnit = { code: 'foo1', fpd: { context: {} } }; + const adUnit = { code: 'foo1', ortb2Imp: { ext: { data: {} } } }; appendPbAdSlot(adUnit); - expect(adUnit.fpd.context.pbAdSlot).to.equal('customPbAdSlotName'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('customPbAdSlotName'); }); }); describe('appendGptSlots', () => { it('should not add adServer object to context if no slots defined', () => { - const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + const adUnit = { code: 'adUnitCode', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.undefined; + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.undefined; }); it('should not add adServer object to context if no slot matches', () => { window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'adUnitCode', fpd: { context: {} } }; + const adUnit = { code: 'adUnitCode', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.undefined; + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.undefined; }); it('should add adServer object to context if matching slot is found', () => { window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'slotCode2', fpd: { context: {} } }; + const adUnit = { code: 'slotCode2', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.an('object'); - expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode2' }); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode2' }); }); it('should use the customGptSlotMatching function if one is given', () => { @@ -104,10 +104,10 @@ describe('GPT pre-auction module', () => { }); window.googletag.pubads().setSlots(testSlots); - const adUnit = { code: 'SlOtCoDe1', fpd: { context: {} } }; + const adUnit = { code: 'SlOtCoDe1', ortb2Imp: { ext: { data: {} } } }; appendGptSlots([adUnit]); - expect(adUnit.fpd.context.adServer).to.be.an('object'); - expect(adUnit.fpd.context.adServer).to.deep.equal({ name: 'gam', adSlot: 'slotCode1' }); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode1' }); }); }); @@ -164,23 +164,23 @@ describe('GPT pre-auction module', () => { it('should append PB Ad Slot and GPT Slot info to first-party data in each ad unit', () => { const testAdUnits = [{ code: 'adUnit1', - fpd: { context: { pbAdSlot: '12345' } } + ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }, { code: 'slotCode1', - fpd: { context: { pbAdSlot: '67890' } } + ortb2Imp: { ext: { data: { pbadslot: '67890' } } } }, { code: 'slotCode3', }]; const expectedAdUnits = [{ code: 'adUnit1', - fpd: { context: { pbAdSlot: '12345' } } + ortb2Imp: { ext: { data: { pbadslot: '12345' } } } }, { code: 'slotCode1', - fpd: { context: { pbAdSlot: '67890', adServer: { name: 'gam', adSlot: 'slotCode1' } } } + ortb2Imp: { ext: { data: { pbadslot: '67890', adserver: { name: 'gam', adslot: 'slotCode1' } } } } }, { code: 'slotCode3', - fpd: { context: { pbAdSlot: 'slotCode3', adServer: { name: 'gam', adSlot: 'slotCode3' } } } + ortb2Imp: { ext: { data: { pbadslot: 'slotCode3', adserver: { name: 'gam', adslot: 'slotCode3' } } } } }]; window.googletag.pubads().setSlots(testSlots); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index df119bb689d..2e8601bddf6 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -400,6 +400,16 @@ describe('TheMediaGrid Adapter', function () { expect(payload.site.content).to.deep.equal(jsContent); }); + it('should contain the keyword values if it present in ortb2.(site/user)', function () { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.ext.keywords).to.deep.equal([{'key': 'user', 'value': ['foo']}, {'key': 'context', 'value': ['bar']}]); + getConfigStub.restore(); + }); + it('shold 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); diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index f7beb6ba486..458e45e8ae7 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -229,8 +229,8 @@ describe('jwplayerRtdProvider', function() { const bid = {}; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: mediaIdWithSegment, @@ -298,8 +298,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -345,8 +345,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -392,8 +392,8 @@ describe('jwplayerRtdProvider', function() { } ]; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForFailure @@ -435,19 +435,19 @@ describe('jwplayerRtdProvider', function() { }); it('should include banner ad units that specify jwTargeting', function() { - const adUnit = { mediaTypes: { banner: {} }, fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { mediaTypes: { banner: {} }, ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); it('should include outstream ad units that specify jwTargeting', function() { - const adUnit = { mediaTypes: { video: { context: 'outstream' } }, fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { mediaTypes: { video: { context: 'outstream' } }, ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); it('should fallback to config when empty jwTargeting is defined in ad unit', function () { - const adUnit = { fpd: { context: { data: { jwTargeting: {} } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: {} } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.deep.equal(config); }); @@ -457,7 +457,7 @@ describe('jwplayerRtdProvider', function() { const expectedPlayerID = 'test_player_id'; const config = { playerID: 'bad_id', mediaID: 'bad_id' }; - const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID, playerID: expectedPlayerID } } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: { mediaID: expectedMediaID, playerID: expectedPlayerID } } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.have.property('mediaID', expectedMediaID); expect(targeting).to.have.property('playerID', expectedPlayerID); @@ -468,7 +468,7 @@ describe('jwplayerRtdProvider', function() { const expectedPlayerID = 'test_player_id'; const config = { playerID: expectedPlayerID, mediaID: 'bad_id' }; - const adUnit = { fpd: { context: { data: { jwTargeting: { mediaID: expectedMediaID } } } } }; + const adUnit = { ortb2Imp: { ext: { data: { jwTargeting: { mediaID: expectedMediaID } } } } }; const targeting = extractPublisherParams(adUnit, config); expect(targeting).to.have.property('mediaID', expectedMediaID); expect(targeting).to.have.property('playerID', expectedPlayerID); @@ -577,8 +577,8 @@ describe('jwplayerRtdProvider', function() { bidReqConfig = { adUnits: [ { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: validMediaIDs[0] @@ -591,8 +591,8 @@ describe('jwplayerRtdProvider', function() { ] }, { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: validMediaIDs[1] @@ -658,8 +658,8 @@ describe('jwplayerRtdProvider', function() { it('sets targeting data in proper structure', function () { const bid = {}; const adUnitWithMediaId = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForSuccess @@ -690,8 +690,8 @@ describe('jwplayerRtdProvider', function() { const adUnitCode = 'test_ad_unit'; const bid = {}; const adUnit = { - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { mediaID: testIdForFailure @@ -701,7 +701,7 @@ describe('jwplayerRtdProvider', function() { }, bids: [ bid ] }; - const expectedContentId = 'jw_' + adUnit.fpd.context.data.jwTargeting.mediaID; + const expectedContentId = 'jw_' + adUnit.ortb2Imp.ext.data.jwTargeting.mediaID; const expectedTargeting = { content: { id: expectedContentId @@ -732,8 +732,8 @@ describe('jwplayerRtdProvider', function() { const adUnitEmptyfpd = { code: 'test_ad_unit_empty_fpd', - fpd: { - context: { + ortb2Imp: { + ext: { id: 'sthg' } }, diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 9e20629fe45..babee7e10d7 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1607,7 +1607,29 @@ describe('S2S Adapter', function () { const expected = allowedBidders.map(bidder => ({ bidders: [ bidder ], - config: { fpd: { site: context, user } } + config: { + ortb2: { + site: { + content: { userrating: 4 }, + ext: { + data: { + pageType: 'article', + category: 'tools' + } + } + }, + user: { + yob: '1984', + geo: { country: 'ca' }, + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + } })); config.setConfig({ fpd: { context: commonContext, user: commonUser } }); @@ -1615,12 +1637,12 @@ describe('S2S Adapter', function () { adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.ext.prebid.bidderconfig).to.deep.equal(expected); - expect(parsedRequestBody.site.ext.data).to.deep.equal(commonContext); - expect(parsedRequestBody.user.ext.data).to.deep.equal(commonUser); + expect(parsedRequestBody.site).to.deep.equal(commonContext); + expect(parsedRequestBody.user).to.deep.equal(commonUser); }); describe('pbAdSlot config', function () { - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context\" is undefined', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); @@ -1630,30 +1652,32 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = {}; + bidRequest.ad_units[0].ortb2Imp = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should not send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { + it('should not send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" is empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - pbAdSlot: '' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + pbadslot: '' + } } }; @@ -1662,16 +1686,18 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.pbadslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.pbadslot'); }); - it('should send \"imp.ext.context.data.pbadslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { + it('should send \"imp.ext.data.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a non-empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - pbAdSlot: '/a/b/c' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + pbadslot: '/a/b/c' + } } }; @@ -1680,13 +1706,13 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.pbadslot'); - expect(parsedRequestBody.imp[0].ext.context.data.pbadslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.pbadslot'); + expect(parsedRequestBody.imp[0].ext.data.pbadslot).to.equal('/a/b/c'); }); }); describe('GAM ad unit config', function () { - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context\" is undefined', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); @@ -1696,31 +1722,33 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is undefined', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext.data.adserver.adslot\" is undefined', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = {}; + bidRequest.ad_units[0].ortb2Imp = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should not send \"imp.ext.context.data.adserver.adslot\" if \"fpd.context.adserver.adSlot\" is empty string', function () { + it('should not send \"imp.ext.data.adserver.adslot\" if \"ortb2Imp.ext.data.adserver.adslot\" is empty string', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - adServer: { - adSlot: '' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '' + } } } }; @@ -1730,18 +1758,20 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.data.adslot'); }); - it('should send both \"adslot\" and \"name\" from \"imp.ext.context.data.adserver\" if \"fpd.context.adserver.adSlot\" and \"fpd.context.adserver.name\" values are non-empty strings', function () { + it('should send both \"adslot\" and \"name\" from \"imp.ext.data.adserver\" if \"ortb2Imp.ext.data.adserver.adslot\" and \"ortb2Imp.ext.data.adserver.name\" values are non-empty strings', function () { const consentConfig = { s2sConfig: CONFIG }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].fpd = { - context: { - adserver: { - adSlot: '/a/b/c', - name: 'adserverName1' + bidRequest.ad_units[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '/a/b/c', + name: 'adserverName1' + } } } }; @@ -1751,10 +1781,10 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.adslot'); - expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adserver.name'); - expect(parsedRequestBody.imp[0].ext.context.data.adserver.adslot).to.equal('/a/b/c'); - expect(parsedRequestBody.imp[0].ext.context.data.adserver.name).to.equal('adserverName1'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.adserver.adslot'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.data.adserver.name'); + expect(parsedRequestBody.imp[0].ext.data.adserver.adslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0].ext.data.adserver.name).to.equal('adserverName1'); }); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8c25d97dada..36890a2891b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -827,27 +827,39 @@ describe('the rubicon adapter', function () { }); it('should merge first party data from getConfig with the bid params, if present', () => { - const context = { + const site = { keywords: 'e,f', rating: '4-star', - data: { - page: 'home' + ext: { + data: { + page: 'home' + } } }; const user = { + data: [{ + 'name': 'www.dataprovider1.com', + 'ext': { 'taxonomyname': 'IAB Audience Taxonomy' }, + 'segment': [ + { 'id': '687' }, + { 'id': '123' } + ] + }], gender: 'M', yob: '1984', geo: {country: 'ca'}, keywords: 'd', - data: { - age: 40 + ext: { + data: { + age: 40 + } } }; sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; @@ -861,8 +873,9 @@ describe('the rubicon adapter', function () { 'tg_v.likes': 'sports,video games', 'tg_v.gender': 'M', 'tg_v.age': '40', + 'tg_v.iab': '687,123', 'tg_v.yob': '1984', - 'tg_i.rating': '5-star', + 'tg_i.rating': '4-star,5-star', 'tg_i.page': 'home', 'tg_i.prodtype': 'tech,mobile', }; @@ -1287,12 +1300,12 @@ describe('the rubicon adapter', function () { describe('Prebid AdSlot', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property - if (bidderRequest.bids[0].hasOwnProperty('fpd')) { - delete bidderRequest.bids[0].fpd; + if (bidderRequest.bids[0].hasOwnProperty('ortb2Imp')) { + delete bidderRequest.bids[0].ortb2Imp; } }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context\" object is not valid', function () { + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1300,8 +1313,8 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" is undefined', function () { - bidderRequest.bids[0].fpd = {}; + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data.pbadslot\" is undefined', function () { + bidderRequest.bids[0].ortb2Imp = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1310,10 +1323,12 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot’'); }); - it('should not send \"tg_i.pbadslot’\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '' + it('should not send \"tg_i.pbadslot’\" if \"ortb2Imp.ext.data.pbadslot\" value is an empty string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '' + } } }; @@ -1324,10 +1339,12 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.pbadslot'); }); - it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: 'abc' + it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: 'abc' + } } } @@ -1339,12 +1356,14 @@ describe('the rubicon adapter', function () { expect(data['tg_i.pbadslot']).to.equal('abc'); }); - it('should send \"tg_i.pbadslot\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '/a/b/c' + it('should send \"tg_i.pbadslot\" if \"ortb2Imp.ext.data.pbadslot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '/a/b/c' + } } - }; + } const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1358,12 +1377,12 @@ describe('the rubicon adapter', function () { describe('GAM ad unit', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property - if (bidderRequest.bids[0].hasOwnProperty('fpd')) { - delete bidderRequest.bids[0].fpd; + if (bidderRequest.bids[0].hasOwnProperty('ortb2Imp')) { + delete bidderRequest.bids[0].ortb2Imp; } }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context\" object is not valid', function () { + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1371,8 +1390,8 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" is undefined', function () { - bidderRequest.bids[0].fpd = {}; + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data.adServer.adslot\" is undefined', function () { + bidderRequest.bids[0].ortb2Imp = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1381,11 +1400,13 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code’'); }); - it('should not send \"tg_i.dfp_ad_unit_code’\" if \"fpd.context.adServer.adSlot\" value is an empty string', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: '' + it('should not send \"tg_i.dfp_ad_unit_code’\" if \"ortb2Imp.ext.data.adServer.adslot\" value is an empty string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '' + } } } }; @@ -1397,11 +1418,13 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: 'abc' + it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: 'abc' + } } } } @@ -1414,11 +1437,13 @@ describe('the rubicon adapter', function () { expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.adServer.adSlot\" value is a valid string, but all leading slash characters should be removed', function () { - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: 'a/b/c' + it('should send \"tg_i.dfp_ad_unit_code\" if \"ortb2Imp.ext.data.adServer.adslot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: 'a/b/c' + } } } }; @@ -1871,27 +1896,36 @@ describe('the rubicon adapter', function () { it('should include first party data', () => { createVideoBidderRequest(); - const context = { - data: { - page: 'home' + const site = { + ext: { + data: { + page: 'home' + } + }, + content: { + data: [{foo: 'bar'}] }, keywords: 'e,f', - rating: '4-star' + rating: '4-star', + data: [{foo: 'bar'}] }; const user = { - data: { - age: 31 + ext: { + data: { + age: 31 + } }, keywords: 'd', gender: 'M', yob: '1984', - geo: {country: 'ca'} + geo: {country: 'ca'}, + data: [{foo: 'bar'}] }; sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; @@ -1901,19 +1935,19 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const expected = { - site: Object.assign({}, context, context.data, bidderRequest.bids[0].params.inventory), - user: Object.assign({}, user, user.data, bidderRequest.bids[0].params.visitor) + site: Object.assign({}, site, {keywords: bidderRequest.bids[0].params.keywords.join(',')}), + user: Object.assign({}, user), + siteData: Object.assign({}, site.ext.data, bidderRequest.bids[0].params.inventory), + userData: Object.assign({}, user.ext.data, bidderRequest.bids[0].params.visitor), }; - delete expected.site.data; - delete expected.user.data; - delete expected.site.keywords; - delete expected.user.keywords; + delete request.data.site.page; + delete request.data.site.content.language; expect(request.data.site.keywords).to.deep.equal('a,b,c'); expect(request.data.user.keywords).to.deep.equal('d'); - expect(request.data.site.ext.data).to.deep.equal(expected.site); - expect(request.data.user.ext.data).to.deep.equal(expected.user); + expect(request.data.site.ext.data).to.deep.equal(expected.siteData); + expect(request.data.user.ext.data).to.deep.equal(expected.userData); }); it('should include storedAuctionResponse in video bid request', function () { @@ -1932,29 +1966,33 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext.prebid.storedauctionresponse.id).to.equal('11111'); }); - it('should include pbAdSlot in bid request', function () { + it('should include pbadslot in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].fpd = { - context: { - pbAdSlot: '1234567890' + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + pbadslot: '1234567890' + } } - }; + } sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.pbadslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.data.pbadslot).to.equal('1234567890'); }); it('should include GAM ad unit in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].fpd = { - context: { - adServer: { - adSlot: '1234567890', - name: 'adServerName1' + bidderRequest.bids[0].ortb2Imp = { + ext: { + data: { + adserver: { + adslot: '1234567890', + name: 'adServerName1' + } } } }; @@ -1964,8 +2002,8 @@ describe('the rubicon adapter', function () { ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].ext.context.data.adserver.adslot).to.equal('1234567890'); - expect(request.data.imp[0].ext.context.data.adserver.name).to.equal('adServerName1'); + expect(request.data.imp[0].ext.data.adserver.adslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.data.adserver.name).to.equal('adServerName1'); }); it('should use the integration type provided in the config instead of the default', () => { diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 6af0a855800..1bc77fc9572 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -42,7 +42,7 @@ const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const context = { +const site = { keywords: 'power tools,drills' }; @@ -439,8 +439,8 @@ describe('smaatoBidAdapterTest', () => { it('sends fp data', () => { this.sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd: { - context, + ortb2: { + site, user } }; diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index eb410c2525d..30377ec0a5d 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -143,10 +143,10 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, - fpd: { - context: { - pbAdSlot: 'homepage-top-rect', + ortb2Imp: { + ext: { data: { + pbAdSlot: 'homepage-top-rect', adUnitSpecificAttribute: 123 } } @@ -663,11 +663,13 @@ describe('triplelift adapter', function () { const sens = null; const category = ['news', 'weather', 'hurricane']; const pmp_elig = 'true'; - const fpd = { - context: { + const ortb2 = { + site: { pmp_elig: pmp_elig, - data: { - category: category + ext: { + data: { + category: category + } } }, user: { @@ -676,7 +678,7 @@ describe('triplelift adapter', function () { } sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - fpd + ortb2 }; return utils.deepAccess(config, key); }); @@ -688,8 +690,8 @@ describe('triplelift adapter', function () { }); it('should send ad unit fpd if kvps are available', function() { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); - expect(request.data.imp[0].fpd.context).to.haveOwnProperty('pbAdSlot'); expect(request.data.imp[0].fpd.context).to.haveOwnProperty('data'); + expect(request.data.imp[0].fpd.context.data).to.haveOwnProperty('pbAdSlot'); expect(request.data.imp[0].fpd.context.data).to.haveOwnProperty('adUnitSpecificAttribute'); expect(request.data.imp[1].fpd).to.not.exist; }); From 15cf2f0cf9f863c6c88e4890e867e628f4c95ff0 Mon Sep 17 00:00:00 2001 From: Ryan Schweitzer <50628828+r-schweitzer@users.noreply.github.com> Date: Mon, 8 Mar 2021 11:57:29 +0000 Subject: [PATCH 0668/1476] PBS Bid Adapter: fix s2s alias collision with built-in adapter aliasing (#6379) * fixed overwriting of aliases for s2s * made change * added fix --- modules/prebidServerBidAdapter/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 20cf93caae5..088b5430f46 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -724,7 +724,7 @@ const OPEN_RTB_PROTOCOL = { } if (!utils.isEmpty(aliases)) { - request.ext.prebid.aliases = aliases; + request.ext.prebid.aliases = {...request.ext.prebid.aliases, ...aliases}; } const bidUserIdAsEids = utils.deepAccess(bidRequests, '0.bids.0.userIdAsEids'); From 83e82229398724ac2c38936e5b8afa3f7de66dd4 Mon Sep 17 00:00:00 2001 From: Pierre Turpin Date: Mon, 8 Mar 2021 16:03:08 +0100 Subject: [PATCH 0669/1476] Clean side-effect when checking that local storage is enabled (#6323) --- src/storageManager.js | 9 +++++-- test/spec/unit/core/storageManager_spec.js | 29 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/storageManager.js b/src/storageManager.js index 60e5a7706d0..66a0cf68cbf 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,4 +1,4 @@ -import { hook } from './hook.js'; +import {hook} from './hook.js'; import * as utils from './utils.js'; import includes from 'core-js-pure/features/array/includes.js'; @@ -110,7 +110,12 @@ export function newStorageManager({gvlid, moduleName, moduleType} = {}) { try { localStorage.setItem('prebid.cookieTest', '1'); return localStorage.getItem('prebid.cookieTest') === '1'; - } catch (error) {} + } catch (error) { + } finally { + try { + localStorage.removeItem('prebid.cookieTest'); + } catch (error) {} + } } return false; } diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index de09df5b196..5bb766217f5 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -46,16 +46,17 @@ describe('storage manager', function() { describe('localstorage forbidden access in 3rd-party context', function() { let errorLogSpy; - const originalLocalStorage = { get: () => window.localStorage }; + let originalLocalStorage; const localStorageMock = { get: () => { throw Error } }; beforeEach(function() { + originalLocalStorage = window.localStorage; Object.defineProperty(window, 'localStorage', localStorageMock); errorLogSpy = sinon.spy(utils, 'logError'); }); afterEach(function() { - Object.defineProperty(window, 'localStorage', originalLocalStorage); + Object.defineProperty(window, 'localStorage', { get: () => originalLocalStorage }); errorLogSpy.restore(); }) @@ -70,4 +71,28 @@ describe('storage manager', function() { sinon.assert.calledThrice(errorLogSpy); }) }) + + describe('localstorage is enabled', function() { + let localStorage; + + beforeEach(function() { + localStorage = window.localStorage; + localStorage.clear(); + }); + + afterEach(function() { + localStorage.clear(); + }) + + it('should remove side-effect after checking', function () { + const storage = getStorageManager(); + + localStorage.setItem('unrelated', 'dummy'); + const val = storage.localStorageIsEnabled(); + + expect(val).to.be.true; + expect(localStorage.length).to.be.eq(1); + expect(localStorage.getItem('unrelated')).to.be.eq('dummy'); + }); + }); }); From 3129a96f57141727c759f82189a348f6c37da19b Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Tue, 9 Mar 2021 05:18:34 -0500 Subject: [PATCH 0670/1476] updates docs and demo for fpd changes (#6302) Co-authored-by: karimJWP --- .../gpt/jwplayerRtdProvider_example.html | 48 +++++++++---------- modules/jwplayerRtdProvider.md | 12 +++-- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/integrationExamples/gpt/jwplayerRtdProvider_example.html b/integrationExamples/gpt/jwplayerRtdProvider_example.html index 75eb85a2d8c..41c27b70ece 100644 --- a/integrationExamples/gpt/jwplayerRtdProvider_example.html +++ b/integrationExamples/gpt/jwplayerRtdProvider_example.html @@ -10,32 +10,30 @@ var PREBID_TIMEOUT = 1000; var adUnits = [{ - code: 'div-gpt-ad-1460505748561-0', - fpd: { - context: { - data: { - jwTargeting: { - // Note: the following Ids are placeholders and should be replaced with your Ids. - playerID: '123', - mediaID: 'abc' + code: 'div-gpt-ad-1460505748561-0', + ortb2Imp: { + ext: { + data: { + jwTargeting: { + // Note: the following Ids are placeholders and should be replaced with your Ids. + playerID: '123', + mediaID: 'abc' + } + } } - }, - } - }, - - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]], - } - }, - // Replace this object to test a new Adapter! - bids: [{ - bidder: 'appnexus', - params: { - placementId: 13144370 - } - }] - + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }] }]; var pbjs = pbjs || {}; diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index ae09277979a..0723e8cbb6c 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -25,14 +25,14 @@ pbjs.setConfig({ } }); ``` -Lastly, include the content's media ID and/or the player's ID in the matching AdUnit's `fpd.context.data`: +Lastly, include the content's media ID and/or the player's ID in the matching AdUnit's `ortb2Imp.ext.data`: ```javascript const adUnit = { code: '/19968336/prebid_native_example_1', ... - fpd: { - context: { + ortb2Imp: { + ext: { data: { jwTargeting: { // Note: the following Ids are placeholders and should be replaced with your Ids. @@ -52,7 +52,7 @@ pbjs.que.push(function() { }); ``` -**Note**: You may also include `jwTargeting` information in the prebid config's `fpd.context.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. +**Note**: You may also include `jwTargeting` information in the prebid config's `ortb2.site.ext.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. ##Prefetching In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var and set `waitForIt` to `true`: @@ -117,3 +117,7 @@ To view an example: `http://localhost:9999/integrationExamples/gpt/jwplayerRtdProvider_example.html` **Note:** the mediaIds in the example are placeholder values; replace them with your existing IDs. + +#Maintainer info + +Maintained by JW Player. For any questions, comments or feedback please contact Karim Mourra, karim@jwplayer.com From 9b2d3c0872b946f70e19d1160200c69ff83e505f Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:50:53 +0530 Subject: [PATCH 0671/1476] Changed net revenue to True (#6387) --- 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 07e5b62ac26..41ca642c869 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -101,7 +101,7 @@ const NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS = [ } ] -const NET_REVENUE = false; +const NET_REVENUE = true; const dealChannelValues = { 1: 'PMP', 5: 'PREF', diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 37deb0bca9c..09573fe9f85 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -2614,7 +2614,7 @@ describe('PubMatic adapter', function () { } expect(response[0].dealId).to.equal(bidResponses.body.seatbid[0].bid[0].dealid); expect(response[0].currency).to.equal('USD'); - expect(response[0].netRevenue).to.equal(false); + expect(response[0].netRevenue).to.equal(true); expect(response[0].ttl).to.equal(300); expect(response[0].meta.networkId).to.equal(123); expect(response[0].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-987'); @@ -2638,7 +2638,7 @@ describe('PubMatic adapter', function () { } expect(response[1].dealId).to.equal(bidResponses.body.seatbid[1].bid[0].dealid); expect(response[1].currency).to.equal('USD'); - expect(response[1].netRevenue).to.equal(false); + expect(response[1].netRevenue).to.equal(true); expect(response[1].ttl).to.equal(300); expect(response[1].meta.networkId).to.equal(422); expect(response[1].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-789'); From a2282be216fae97323bc65a618099b49e1b4807d Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 9 Mar 2021 16:29:02 +0100 Subject: [PATCH 0672/1476] adagioBidAdapter: add Native support (#6368) --- modules/adagioBidAdapter.js | 116 +++++++++++++- modules/adagioBidAdapter.md | 60 ++++++- test/spec/modules/adagioBidAdapter_spec.js | 173 ++++++++++++++++++++- 3 files changed, 340 insertions(+), 9 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 6f7feec59c9..892411837dd 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -8,16 +8,16 @@ import sha256 from 'crypto-js/sha256.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { createEidsArray } from './userId/eids.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.6.0'; +export const VERSION = '2.7.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; -export const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; +export const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; export const GVLID = 617; @@ -687,6 +687,112 @@ function _renderer(bid) { }); } +function _parseNativeBidResponse(bid) { + if (!bid.admNative || !Array.isArray(bid.admNative.assets)) { + utils.logError(`${LOG_PREFIX} Invalid native response`); + return; + } + + const native = {} + + function addAssetDataValue(data) { + const map = { + 1: 'sponsoredBy', // sponsored + 2: 'body', // desc + 3: 'rating', + 4: 'likes', + 5: 'downloads', + 6: 'price', + 7: 'salePrice', + 8: 'phone', + 9: 'address', + 10: 'body2', // desc2 + 11: 'displayUrl', + 12: 'cta' + } + if (map.hasOwnProperty(data.type) && typeof data.value === 'string') { + native[map[data.type]] = data.value; + } + } + + // assets + bid.admNative.assets.forEach(asset => { + if (asset.title) { + native.title = asset.title.text + } else if (asset.data) { + addAssetDataValue(asset.data) + } else if (asset.img) { + switch (asset.img.type) { + case 1: + native.icon = { + url: asset.img.url, + width: asset.img.w, + height: asset.img.h + }; + break; + default: + native.image = { + url: asset.img.url, + width: asset.img.w, + height: asset.img.h + }; + break; + } + } + }); + + if (bid.admNative.link) { + if (bid.admNative.link.url) { + native.clickUrl = bid.admNative.link.url; + } + if (Array.isArray(bid.admNative.link.clickTrackers)) { + native.clickTrackers = bid.admNative.link.clickTrackers + } + } + + if (Array.isArray(bid.admNative.eventtrackers)) { + native.impressionTrackers = []; + bid.admNative.eventtrackers.forEach(tracker => { + // Only Impression events are supported. Prebid does not support Viewability events yet. + if (tracker.event !== 1) { + return; + } + + // methods: + // 1: image + // 2: js + // note: javascriptTrackers is a string. If there's more than one JS tracker in bid response, the last script will be used. + switch (tracker.method) { + case 1: + native.impressionTrackers.push(tracker.url); + break; + case 2: + native.javascriptTrackers = ``; + break; + } + }); + } else { + native.impressionTrackers = Array.isArray(bid.admNative.imptrackers) ? bid.admNative.imptrackers : []; + if (bid.admNative.jstracker) { + native.javascriptTrackers = bid.admNative.jstracker; + } + } + + if (bid.admNative.privacy) { + native.privacyLink = bid.admNative.privacy; + } + + if (bid.admNative.ext) { + native.ext = {} + + if (bid.admNative.ext.bvw) { + native.ext.adagio_bvw = bid.admNative.ext.bvw; + } + } + + bid.native = native +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -873,6 +979,10 @@ export const spec = { } } + if (bidObj.mediaType === NATIVE) { + _parseNativeBidResponse(bidObj); + } + bidObj.site = bidReq.params.site; bidObj.placement = bidReq.params.placement; bidObj.pagetype = bidReq.params.pagetype; diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index aa79338d79e..46656d88d37 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -38,8 +38,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. - 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 + 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 // Optional debug mode, used to get a bid response with expected cpm. debug: { enabled: true, @@ -78,8 +78,8 @@ Connects to Adagio demand source to fetch bids. category: 'sport', // Recommended. Category of the content displayed in the page. subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. postBid: false, // Optional. Use it in case of Post-bid integration only. - 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 + 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 video: { skip: 0 // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. @@ -91,6 +91,58 @@ Connects to Adagio demand source to fetch bids. } } }] + }, + { + code: 'article_native', + mediaTypes: { + native: { + // generic Prebid options + title: { + required: true, + len: 80 + }, + // … + // Custom Adagio data assets + ext: { + adagio_bvw: { + required: false + } + } + } + }, + bids: [{ + bidder: 'adagio', // Required + params: { + organizationId: '1002', // Required - Organization ID provided by Adagio. + site: 'adagio-io', // Required - Site Name provided by Adagio. + placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + adUnitElementId: 'article_native', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + + // The following params are limited to 30 characters, + // and can only contain the following characters: + // - alphanumeric (A-Z+a-z+0-9, case-insensitive) + // - dashes `-` + // - 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. + postBid: false, // Optional. Use it in case of Post-bid integration only. + 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 + // Optional OpenRTB Native 1.2 request object. Only `context`, `plcmttype` fields are supported. + native: { + context: 1, + plcmttype: 2 + }, + // Optional debug mode, used to get a bid response with expected cpm. + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 + } + } + }] } ]; diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 0a585caaa1a..999773f1a1f 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,5 +1,5 @@ import find from 'core-js-pure/features/array/find.js'; -import { expect } from 'chai'; +import { expect, util } from 'chai'; import { _features, internal as adagio, @@ -13,7 +13,8 @@ import { } from '../../../modules/adagioBidAdapter.js'; import { loadExternalScript } from '../../../src/adloader.js'; import * as utils from '../../../src/utils.js'; -import { config } from 'src/config.js'; +import { config } from '../../../src/config.js'; +import { NATIVE } from '../../../src/mediaTypes.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -879,6 +880,174 @@ describe('Adagio bid adapter', () => { expect(bidResponse.vastUrl).to.match(/^data:text\/xml;/) }); }); + + describe('Response with native add', () => { + const serverResponseWithNative = utils.deepClone(serverResponse) + serverResponseWithNative.body.bids[0].mediaType = 'native'; + serverResponseWithNative.body.bids[0].admNative = { + ver: '1.2', + link: { + url: 'https://i.am.a.click.url', + clickTrackers: [ + 'https://i.am.a.clicktracker.url' + ] + }, + privacy: 'http://www.myprivacyurl.url', + ext: { + bvw: 'test' + }, + eventtrackers: [ + { + event: 1, + method: 1, + url: 'https://eventrack.local/impression' + }, + { + event: 1, + method: 2, + url: 'https://eventrack.local/impression' + }, + { + event: 2, + method: 1, + url: 'https://eventrack.local/viewable-mrc50' + } + ], + assets: [ + { + required: 1, + title: { + text: 'My title' + } + }, + { + img: { + url: 'https://images.local/image.jpg', + w: 100, + h: 250 + } + }, + { + img: { + type: 1, + url: 'https://images.local/icon.png', + w: 40, + h: 40 + } + }, + { + data: { + type: 1, // sponsored + value: 'Adagio' + } + }, + { + data: { + type: 2, // desc / body + value: 'The super ad text' + } + }, + { + data: { + type: 3, // rating + value: '10 from 10' + } + }, + { + data: { + type: 11, // displayUrl + value: 'https://i.am.a.display.url' + } + } + ] + }; + + const bidRequestNative = utils.deepClone(bidRequest) + bidRequestNative.mediaTypes = { + native: { + sendTargetingKeys: false, + + clickUrl: { + required: true, + }, + title: { + required: true, + }, + body: { + required: true, + }, + sponsoredBy: { + required: false + }, + image: { + required: true + }, + icon: { + required: true + }, + privacyLink: { + required: false + }, + ext: { + adagio_bvw: {} + } + } + }; + + it('Should ignore native parsing due to missing raw admNative property', () => { + const alternateServerResponse = utils.deepClone(serverResponseWithNative); + delete alternateServerResponse.body.bids[0].admNative + const r = spec.interpretResponse(alternateServerResponse, bidRequestNative); + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).not.ok; + utilsMock.expects('logError').once(); + }); + + it('Should ignore native parsing due to invalid raw admNative.assets property', () => { + const alternateServerResponse = utils.deepClone(serverResponseWithNative); + alternateServerResponse.body.bids[0].admNative.assets = { title: { text: 'test' } }; + const r = spec.interpretResponse(alternateServerResponse, bidRequestNative); + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).not.ok; + utilsMock.expects('logError').once(); + }); + + it('Should handle and return a formated Native ad', () => { + const r = spec.interpretResponse(serverResponseWithNative, bidRequestNative); + const expected = { + displayUrl: 'https://i.am.a.display.url', + sponsoredBy: 'Adagio', + body: 'The super ad text', + rating: '10 from 10', + clickUrl: 'https://i.am.a.click.url', + title: 'My title', + impressionTrackers: [ + 'https://eventrack.local/impression' + ], + javascriptTrackers: '', + clickTrackers: [ + 'https://i.am.a.clicktracker.url' + ], + image: { + url: 'https://images.local/image.jpg', + width: 100, + height: 250 + }, + icon: { + url: 'https://images.local/icon.png', + width: 40, + height: 40 + }, + ext: { + adagio_bvw: 'test' + }, + privacyLink: 'http://www.myprivacyurl.url' + } + expect(r[0].mediaType).to.equal(NATIVE); + expect(r[0].native).ok; + expect(r[0].native).to.deep.equal(expected); + }); + }); }); describe('getUserSyncs()', function() { From 7635af1c9b2e143401284fbc511d18e748a6c749 Mon Sep 17 00:00:00 2001 From: rtuschkany <35923908+rtuschkany@users.noreply.github.com> Date: Wed, 10 Mar 2021 13:02:42 +0100 Subject: [PATCH 0673/1476] EIDS Support Update (#6394) --- modules/connectadBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index d1811a1b7d1..4fa2a56a004 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { // EIDS Support if (validBidRequests[0].userId) { - data.user.ext.eids = createEidsArray(validBidRequests[0].userId); + utils.deepSetValue(data, 'user.ext.eids', createEidsArray(validBidRequests[0].userId)); } validBidRequests.map(bid => { From 89d5d410bc4b62c2934f64cec10a6e1948329318 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:03:49 +0300 Subject: [PATCH 0674/1476] TheMediaGridNM Bid Adapter: fix trouble with alias (#6371) * 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 --- modules/gridNMBidAdapter.js | 1 - test/spec/modules/gridNMBidAdapter_spec.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index ffd6c1b250c..af1e9f84f43 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -134,7 +134,6 @@ export const spec = { } const bidResponse = { requestId: bid.bidId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 0dbaac0c526..2aec9713000 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -300,7 +300,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': 11, 'width': 300, 'height': 250, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -317,7 +316,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, From 20f3cd8972079231f81a02ad4aa24b317e4032dd Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 10 Mar 2021 07:28:35 -0800 Subject: [PATCH 0675/1476] Grab sourceAgnostic IDs first, then fallback to regular IDs (#6400) --- modules/rubiconAnalyticsAdapter.js | 4 +- .../modules/rubiconAnalyticsAdapter_spec.js | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 08ae8bf2dd8..f6724ffcc7a 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -482,8 +482,8 @@ function subscribeToGamSlots() { // these come in as `null` from Gpt, which when stringified does not get removed // so set explicitly to undefined when not a number 'advertiserId', advertiserId => utils.isNumber(advertiserId) ? advertiserId : undefined, - 'creativeId', creativeId => utils.isNumber(creativeId) ? creativeId : undefined, - 'lineItemId', lineItemId => utils.isNumber(lineItemId) ? lineItemId : undefined, + 'creativeId', creativeId => utils.isNumber(event.sourceAgnosticCreativeId) ? event.sourceAgnosticCreativeId : utils.isNumber(creativeId) ? creativeId : undefined, + 'lineItemId', lineItemId => utils.isNumber(event.sourceAgnosticLineItemId) ? event.sourceAgnosticLineItemId : utils.isNumber(lineItemId) ? lineItemId : undefined, 'adSlot', () => event.slot.getAdUnitPath(), 'isSlotEmpty', () => event.isEmpty || undefined ]); diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 136f5ef240a..a9c1eeef4de 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1436,8 +1436,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot0, isEmpty: false, advertiserId: 1111, - creativeId: 2222, - lineItemId: 3333 + sourceAgnosticCreativeId: 2222, + sourceAgnosticLineItemId: 3333 } }; @@ -1448,8 +1448,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot1, isEmpty: false, advertiserId: 4444, - creativeId: 5555, - lineItemId: 6666 + sourceAgnosticCreativeId: 5555, + sourceAgnosticLineItemId: 6666 } }; }); @@ -1515,8 +1515,8 @@ describe('rubicon analytics adapter', function () { slot: gptSlot1, isEmpty: false, advertiserId: 0, - creativeId: 0, - lineItemId: 0 + sourceAgnosticCreativeId: 0, + sourceAgnosticLineItemId: 0 } }]); expect(server.requests.length).to.equal(1); @@ -1540,6 +1540,38 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(expectedMessage); }); + it('should pick backup Ids if no sourceAgnostic available first', function () { + performStandardAuction([gptSlotRenderEnded0, { + eventName: 'slotRenderEnded', + params: { + slot: gptSlot1, + isEmpty: false, + advertiserId: 0, + lineItemId: 1234, + creativeId: 5678 + } + }]); + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + validate(message); + + let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE); + expectedMessage.auctions[0].adUnits[0].gam = { + advertiserId: 1111, + creativeId: 2222, + lineItemId: 3333, + adSlot: '/19968336/header-bid-tag-0' + }; + expectedMessage.auctions[0].adUnits[1].gam = { + advertiserId: 0, + creativeId: 5678, + lineItemId: 1234, + adSlot: '/19968336/header-bid-tag1' + }; + expect(message).to.deep.equal(expectedMessage); + }); + it('should correctly set adUnit for associated slots', function () { performStandardAuction([gptSlotRenderEnded0, gptSlotRenderEnded1]); expect(server.requests.length).to.equal(1); From b65ea7371bb4e0d91197c550c13526cb54b2d3a5 Mon Sep 17 00:00:00 2001 From: David Reischer Date: Wed, 10 Mar 2021 16:16:18 +0000 Subject: [PATCH 0676/1476] Permutive - add AC support for TrustX (#6393) --- .../gpt/permutiveRtdProvider_example.html | 19 ++- modules/permutiveRtdProvider.js | 7 + modules/permutiveRtdProvider.md | 1 + .../spec/modules/permutiveRtdProvider_spec.js | 147 ++++++++++++++++-- 4 files changed, 153 insertions(+), 21 deletions(-) diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html index 0814dcece5b..a06430bcdfa 100644 --- a/integrationExamples/gpt/permutiveRtdProvider_example.html +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -50,7 +50,7 @@ params: { placementId: 13144370, keywords: { - inline_kvs: ['1'] + test_kv: ['true'] } } }, @@ -64,7 +64,7 @@ area: ['home'] }, visitor: { - inline_kvs: ['1'] + test_kv: ['true'] } } }, @@ -78,12 +78,21 @@ { settings: {}, targeting: { - inline_kvs: ['1', '2', '3', '4'] + test_kv: ['true'] } } ], ozoneData: {} } + }, + { + bidder: 'trustx', + params: { + uid: 45, + keywords: { + test_kv: ['true'] + } + } } ] }, @@ -127,13 +136,13 @@ pbjs.setConfig({ debug: true, realTimeData: { - auctionDelay: 50, // maximum time for RTD modules to respond + auctionDelay: 80, // maximum time for RTD modules to respond dataProviders: [ { name: 'permutive', waitForIt: true, params: { - acBidders: ['appnexus', 'rubicon', 'ozone'], + acBidders: ['appnexus', 'rubicon', 'ozone', 'trustx'], maxSegs: 500, overwrites: { rubicon: function (bid, data, acEnabled, utils, defaultFn) { diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 8ec215d3cca..db431ed45a7 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -110,6 +110,13 @@ function getDefaultBidderFn (bidder) { deepSetValue(bid, 'params.customData.0.targeting.p_standard', data.ac) } + return bid + }, + trustx: function (bid, data, acEnabled) { + if (acEnabled && data.ac && data.ac.length) { + deepSetValue(bid, 'params.keywords.p_standard', data.ac) + } + return bid } } diff --git a/modules/permutiveRtdProvider.md b/modules/permutiveRtdProvider.md index 55bdf6420cf..fe8c34c1b5c 100644 --- a/modules/permutiveRtdProvider.md +++ b/modules/permutiveRtdProvider.md @@ -33,6 +33,7 @@ The below bidders are currently support by the Permutive RTD module. Please reac | Xandr | `appnexus` | Yes | Yes | | Magnite | `rubicon` | Yes | Yes | | Ozone | `ozone` | No | Yes | +| TrustX | `trustx` | No | Yes | * **First-party segments:** When enabling the respective Activation for a segment in Permutive, this module will automatically attach that segment 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 Permutive. Permutive segments will be sent in the `permutive` key-value. diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index d55bbc58056..cf1f3861eab 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -54,7 +54,7 @@ describe('permutiveRtdProvider', function () { }) } }) - it('sets segment targeting for Rubicon', function () { + it('sets segment targeting for Magnite', function () { const data = transformedTargeting() const adUnits = getAdUnits() const config = getConfig() @@ -93,10 +93,29 @@ describe('permutiveRtdProvider', function () { }) } }) + it('sets segment targeting for TrustX', function () { + const data = transformedTargeting() + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'trustx') { + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + } + }) + }) + } + }) }) describe('Custom segment targeting', function () { - it('sets custom segment targeting for Rubicon', function () { + it('sets custom segment targeting for Magnite', function () { const data = transformedTargeting() const adUnits = getAdUnits() const config = getConfig() @@ -129,6 +148,81 @@ describe('permutiveRtdProvider', function () { }) }) + describe('Existing key-value targeting', function () { + it('doesn\'t overwrite existing key-values for Xandr', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'appnexus') { + expect(deepAccess(params, 'keywords.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for Magnite', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'rubicon') { + expect(deepAccess(params, 'visitor.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for Ozone', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'ozone') { + expect(deepAccess(params, 'customData.0.targeting.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + it('doesn\'t overwrite existing key-values for TrustX', function () { + const adUnits = getAdUnits() + const config = getConfig() + + initSegments({ adUnits }, callback, config) + + function callback () { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid + + if (bidder === 'trustx') { + expect(deepAccess(params, 'keywords.test_kv')).to.eql(['true']) + } + }) + }) + } + }) + }) + describe('Permutive on page', function () { it('checks if Permutive is on page', function () { expect(isPermutiveOnPage()).to.equal(false) @@ -168,7 +262,7 @@ function getConfig () { name: 'permutive', waitForIt: true, params: { - acBidders: ['appnexus', 'rubicon', 'ozone'], + acBidders: ['appnexus', 'rubicon', 'ozone', 'trustx'], maxSegs: 500 } } @@ -197,15 +291,20 @@ function getTargetingData () { } function getAdUnits () { + const div_1_sizes = [ + [300, 250], + [300, 600] + ] + const div_2_sizes = [ + [728, 90], + [970, 250] + ] return [ { code: '/19968336/header-bid-tag-0', mediaTypes: { banner: { - sizes: [ - [300, 250], - [300, 600] - ] + sizes: div_1_sizes } }, bids: [ @@ -214,7 +313,7 @@ function getAdUnits () { params: { placementId: 13144370, keywords: { - inline_kvs: ['1'] + test_kv: ['true'] } } }, @@ -228,7 +327,7 @@ function getAdUnits () { area: ['home'] }, visitor: { - inline_kvs: ['1'] + test_kv: ['true'] } } }, @@ -242,12 +341,21 @@ function getAdUnits () { { settings: {}, targeting: { - inline_kvs: ['1', '2', '3', '4'] + test_kv: ['true'] } } ], ozoneData: {} } + }, + { + bidder: 'trustx', + params: { + uid: 45, + keywords: { + test_kv: ['true'] + } + } } ] }, @@ -255,17 +363,17 @@ function getAdUnits () { code: '/19968336/header-bid-tag-1', mediaTypes: { banner: { - sizes: [ - [728, 90], - [970, 250] - ] + sizes: div_2_sizes } }, bids: [ { bidder: 'appnexus', params: { - placementId: 13144370 + placementId: 13144370, + keywords: { + test_kv: ['true'] + } } }, { @@ -273,7 +381,14 @@ function getAdUnits () { params: { publisherId: 'OZONEGMG0001', siteId: '4204204209', - placementId: '0420420500' + placementId: '0420420500', + customData: [ + { + targeting: { + test_kv: ['true'] + } + } + ] } } ] From cdfc9dfeecd48fb9c7cdd116fc7ae976871f11c0 Mon Sep 17 00:00:00 2001 From: evanmsmrtb Date: Wed, 10 Mar 2021 12:56:49 -0600 Subject: [PATCH 0677/1476] SmartRTB Bid Adapter: add alias and update valid opts (#6365) * Add alias, update valid opts * Update bidder tests --- modules/smartrtbBidAdapter.js | 6 ++---- test/spec/modules/smartrtbBidAdapter_spec.js | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/modules/smartrtbBidAdapter.js b/modules/smartrtbBidAdapter.js index 12d5a0ae7da..de303f9e4b2 100644 --- a/modules/smartrtbBidAdapter.js +++ b/modules/smartrtbBidAdapter.js @@ -17,11 +17,9 @@ function getDomain () { export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ 'banner', 'video' ], - aliases: ['smrtb'], + aliases: ['rdigital'], isBidRequestValid: function(bid) { - return (bid.params.pubId !== null && - bid.params.medId !== null && - bid.params.zoneId !== null); + return (bid.params.pubId !== null || bid.params.zoneId !== null); }, buildRequests: function(validBidRequests, bidderRequest) { let stack = (bidderRequest.refererInfo && diff --git a/test/spec/modules/smartrtbBidAdapter_spec.js b/test/spec/modules/smartrtbBidAdapter_spec.js index cb5ceee0870..a7f30bdec6e 100644 --- a/test/spec/modules/smartrtbBidAdapter_spec.js +++ b/test/spec/modules/smartrtbBidAdapter_spec.js @@ -69,9 +69,6 @@ describe('SmartRTBBidAdapter', function () { it('should return a bidder code of smartrtb', function () { expect(spec.code).to.equal('smartrtb') }) - it('should alias smrtb', function () { - expect(spec.aliases.length > 0 && spec.aliases[0] === 'smrtb').to.be.true - }) }) describe('isBidRequestValid', function () { @@ -79,8 +76,8 @@ describe('SmartRTBBidAdapter', function () { expect(spec.isBidRequestValid(bannerRequest)).to.be.true }) - it('should return false if any zone id missing', function () { - expect(spec.isBidRequestValid(Object.assign(bannerRequest, { params: { zoneId: null } }))).to.be.false + it('should return false if any zone id and pub id missing', function () { + expect(spec.isBidRequestValid(Object.assign(bannerRequest, { params: { pubId: null, zoneId: null } }))).to.be.false }) }) From f1c45cd3a702f9b7b77da8d7be523756173809be Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 10 Mar 2021 13:17:06 -0800 Subject: [PATCH 0678/1476] Prebid 4.30.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dfd8a60b06d..befc6c79fbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.30.0-pre", + "version": "4.30.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 41b8607a004c111093a8143992b0191f1ff3bd44 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 10 Mar 2021 13:36:31 -0800 Subject: [PATCH 0679/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index befc6c79fbd..7943e00f3c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.30.0", + "version": "4.31.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f88a55addb04ebdcaa31d43cf121895ea89461e3 Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Thu, 11 Mar 2021 04:00:03 -0500 Subject: [PATCH 0680/1476] Sonobi Bid Adapter: send eids in bid request. (#6364) * unwrapping id5id uid. Added new eid param for user id modules * set userid to new variable * fixed spelling mistake in unit test Co-authored-by: Scott Menzer * copying userid object so the referenced object does not get updated. * using deepClone instead of spreading the top userId object Co-authored-by: Scott Menzer --- modules/sonobiBidAdapter.js | 16 +++- test/spec/modules/sonobiBidAdapter_spec.js | 85 +++++++++++++++++++++- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index a55992cec22..0e4bfb37829 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -1,10 +1,9 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage } from '../src/utils.js'; +import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, deepClone } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { userSync } from '../src/userSync.js'; - const BIDDER_CODE = 'sonobi'; const STR_ENDPOINT = 'https://apex.go.sonobi.com/trinity.json'; const PAGEVIEW_ID = generateUUID(); @@ -117,7 +116,18 @@ export const spec = { payload.schain = JSON.stringify(validBidRequests[0].schain) } if (deepAccess(validBidRequests[0], 'userId') && Object.keys(validBidRequests[0].userId).length > 0) { - payload.userid = JSON.stringify(validBidRequests[0].userId); + const userIds = deepClone(validBidRequests[0].userId); + + if (userIds.id5id) { + userIds.id5id = deepAccess(userIds, 'id5id.uid'); + } + + payload.userid = JSON.stringify(userIds); + } + + const eids = deepAccess(validBidRequests[0], 'userIdAsEids'); + if (Array.isArray(eids) && eids.length > 0) { + payload.eids = JSON.stringify(eids); } let keywords = validBidRequests[0].params.keywords; // a CSV of keywords diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 52821072a21..d1ac200394c 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -414,15 +414,92 @@ describe('SonobiBidAdapter', function () { expect(JSON.parse(bidRequests.data.schain)).to.deep.equal(bidRequest[0].schain) }); + it('should return a properly formatted request with eids as a JSON-encoded set of eids', function () { + bidRequest[0].userIdAsEids = [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]; + bidRequest[1].userIdAsEids = [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]; + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); + expect(bidRequests.method).to.equal('GET'); + expect(bidRequests.data.ref).not.to.be.empty; + expect(bidRequests.data.s).not.to.be.empty; + expect(JSON.parse(bidRequests.data.eids)).to.eql([ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '97b1ff9b-6bf1-41fc-95de-acfd33dbb95a', + 'atype': 1 + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01ERJ6W40EXJZNQJVJZWASEG7J', + 'atype': 1, + 'ext': { + 'third': '01ERJ6W40EXJZNQJVJZWASEG7J' + } + } + ] + } + ]); + }); + it('should return a properly formatted request with userid as a JSON-encoded set of User ID results', function () { - bidRequest[0].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; - bidRequest[1].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}; + bidRequest[0].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': {'uid': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ', 'ext': {'linkType': 2}}}; + bidRequest[1].userId = {'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': {'uid': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ', 'ext': {'linkType': 2}}}; const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.url).to.equal('https://apex.go.sonobi.com/trinity.json'); expect(bidRequests.method).to.equal('GET'); expect(bidRequests.data.ref).not.to.be.empty; expect(bidRequests.data.s).not.to.be.empty; - expect(JSON.parse(bidRequests.data.userid)).to.eql({'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101'}); + expect(JSON.parse(bidRequests.data.userid)).to.eql({'pubcid': 'abcd-efg-0101', 'tdid': 'td-abcd-efg-0101', 'id5id': 'ID5-ZHMOrVeUVTUKgrZ-a2YGxeh5eS_pLzHCQGYOEAiTBQ'}); }); it('should return a properly formatted request with userid omitted if there are no userIds', function () { @@ -469,7 +546,7 @@ describe('SonobiBidAdapter', function () { ]; const bidRequests = spec.buildRequests(bRequest, bidderRequests); expect(bidRequests.url).to.equal('https://iad-2-apex.go.sonobi.com/trinity.json'); - }) + }); }); describe('.interpretResponse', function () { From 8e764f666530bf8ea57f1438907adb2951aa6d6f Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Fri, 12 Mar 2021 16:48:38 +0700 Subject: [PATCH 0681/1476] Apacdex Bid Adapter: userId module support, show demo ads in debug mode & other maintenance (#6378) * Upgrade and maintenance apacdexBidAdapter * fix error and add unit test --- modules/apacdexBidAdapter.js | 145 ++++++++++++++++---- test/spec/modules/apacdexBidAdapter_spec.js | 101 +++++++++++++- 2 files changed, 221 insertions(+), 25 deletions(-) diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 2582e4788c1..62ae3f54125 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -1,4 +1,5 @@ import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'apacdex'; const CONFIG = { @@ -49,11 +50,34 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { + let siteId; + let schain; + let eids; + let geo; + let test; + var bids = JSON.parse(JSON.stringify(validBidRequests)) bidderConfig = CONFIG[bids[0].bidder]; - const payload = {}; + + test = config.getConfig('debug'); bids.forEach(bidReq => { + siteId = siteId || bidReq.params.siteId; + + if (bidReq.schain) { + schain = schain || bidReq.schain + } + + if (bidReq.userIdAsEids) { + eids = eids || bidReq.userIdAsEids + } + + if (bidReq.params && bidReq.params.geo) { + if (validateGeoObject(bidReq.params.geo)) { + geo = bidReq.params.geo; + } + } + var targetKey = 0; if (bySlotTargetKey[bidReq.adUnitCode] != undefined) { targetKey = bySlotTargetKey[bidReq.adUnitCode]; @@ -73,36 +97,55 @@ export const spec = { bidReq.targetKey = targetKey; }); + const payload = {}; + payload.tmax = bidderRequest.timeout; + if (test) { + payload.test = 1; + } + payload.device = {}; payload.device.ua = navigator.userAgent; - payload.device.height = window.top.innerHeight; - payload.device.width = window.top.innerWidth; + payload.device.height = window.screen.width; + payload.device.width = window.screen.height; payload.device.dnt = _getDoNotTrack(); payload.device.language = navigator.language; + var pageUrl = _extractTopWindowUrlFromBidderRequest(bidderRequest); payload.site = {}; - payload.site.id = bids[0].params.siteId; - payload.site.page = _extractTopWindowUrlFromBidderRequest(bidderRequest); + payload.site.id = siteId; + payload.site.page = pageUrl payload.site.referrer = _extractTopWindowReferrerFromBidderRequest(bidderRequest); - payload.site.hostname = window.top.location.hostname; + payload.site.hostname = getDomain(pageUrl); // Apply GDPR parameters to request. - payload.gdpr = {}; if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr = {}; payload.gdpr.gdprApplies = !!bidderRequest.gdprConsent.gdprApplies; if (bidderRequest.gdprConsent.consentString) { payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; } } - // Apply schain. - if (bids[0].schain) { - payload.schain = bids[0].schain - } + // Apply us_privacy. if (bidderRequest && bidderRequest.uspConsent) { payload.us_privacy = bidderRequest.uspConsent; } + // Apply schain. + if (schain) { + payload.schain = schain + } + + // Apply eids. + if (eids) { + payload.eids = eids + } + + // Apply geo + if (geo) { + payload.geo = geo; + } + payload.bids = bids; return { @@ -115,12 +158,12 @@ export const spec = { }, interpretResponse: function (serverResponse, bidRequest) { const serverBody = serverResponse.body; - const serverBids = serverBody.bids; - // check overall response - if (!serverBody || typeof serverBody !== 'object') { + if (!serverBody || !utils.isPlainObject(serverBody)) { return []; } - if (!serverBids || typeof serverBids !== 'object') { + + const serverBids = serverBody.bids; + if (!serverBids || !utils.isArray(serverBids)) { return []; } @@ -192,15 +235,25 @@ function _getBiggestSize(sizes) { } function _getDoNotTrack() { - if (window.top.doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack) { - if (window.top.doNotTrack == '1' || navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') { + try { + if (window.top.doNotTrack && window.top.doNotTrack == '1') { return 1; - } else { - return 0; } - } else { - return 0; - } + } catch (e) { } + + try { + if (navigator.doNotTrack && (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1')) { + return 1; + } + } catch (e) { } + + try { + if (navigator.msDoNotTrack && navigator.msDoNotTrack == '1') { + return 1; + } + } catch (e) { } + + return 0 } /** @@ -210,8 +263,11 @@ function _getDoNotTrack() { * @returns {string} */ function _extractTopWindowUrlFromBidderRequest(bidderRequest) { - if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { - return bidderRequest.refererInfo.canonicalUrl; + if (config.getConfig('pageUrl')) { + return config.getConfig('pageUrl'); + } + if (utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; } try { @@ -239,4 +295,45 @@ function _extractTopWindowReferrerFromBidderRequest(bidderRequest) { } } +/** + * Extracts the domain from given page url + * + * @param {string} url + * @returns {string} + */ +export function getDomain(pageUrl) { + if (config.getConfig('publisherDomain')) { + var publisherDomain = config.getConfig('publisherDomain'); + return publisherDomain.replace('http://', '').replace('https://', '').replace('www.', '').split(/[/?#:]/)[0]; + } + + if (!pageUrl) { + return pageUrl; + } + + return pageUrl.replace('http://', '').replace('https://', '').replace('www.', '').split(/[/?#:]/)[0]; +} + +/** + * Validate geo object + * + * @param {Object} geo + * @returns {boolean} + */ +export function validateGeoObject(geo) { + if (!utils.isPlainObject(geo)) { + return false; + } + if (!geo.lat) { + return false; + } + if (!geo.lon) { + return false; + } + if (!geo.accuracy) { + return false; + } + return true; +} + registerBidder(spec); diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index da9a050a8de..c3d0d025c0c 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -1,7 +1,8 @@ import { expect } from 'chai' -import { spec } from 'modules/apacdexBidAdapter.js' +import { spec, validateGeoObject, getDomain } from '../../../modules/apacdexBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; +import { config } from 'src/config.js'; describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) @@ -199,11 +200,34 @@ describe('ApacdexBidAdapter', function () { 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', + 'geo': {'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60} }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'targetKey': 0, 'bidId': '30b31c1838de1f', + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'p0cCLF9JazY1ZUFjazJRb3NKbEprVTcwZ0IwRUlGalBjOG9laUZNbFJ0ZGpOSnVFbE9VMjBNMzNBTzladGt4cUVGQzBybDY2Y1FqT1dkUkFsMmJIWDRHNjlvNXJjbiUyQlZDd1dOTmt6VlV2TDhRd0F0RTlBcmpyZU5WRHBPU25GQXpyMnlT', + 'atype': 1 + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': '2ae366c2-2576-45e5-bd21-72ed10598f17', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EZXQDVAPER4KE1VBS29XKV4Z', + 'atype': 1, + 'ext': { + 'third': '01EZXQDVAPER4KE1VBS29XKV4Z' + } + }] + }], }, { 'bidder': 'apacdex', @@ -300,10 +324,23 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.schain).to.deep.equal(bidRequest[0].schain) }); + it('should return a properly formatted request with eids defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.eids).to.deep.equal(bidRequest[0].userIdAsEids) + }); + it('should return a properly formatted request with geo defined', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.geo).to.deep.equal(bidRequest[0].params.geo) + }); it('should return a properly formatted request with us_privacy included', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); + it('should return a properly formatted request with pbjs_debug is true', function () { + config.setConfig({debug: true}); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.test).to.equal(1) + }); }); describe('.interpretResponse', function () { @@ -601,4 +638,66 @@ describe('ApacdexBidAdapter', function () { expect(spec.getUserSyncs({ pixelEnabled: true }, [])).to.have.length(0); }); }); + + describe('validateGeoObject', function () { + it('should return true if the geo object is valid', () => { + let geoObject = { + lat: 123.5624234, + lon: 23.6712341, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(true); + }); + + it('should return false if the geo object is not plain object', () => { + let geoObject = [{ + lat: 123.5624234, + lon: 23.6712341, + accuracy: 20 + }]; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing lat attribute', () => { + let geoObject = { + lon: 23.6712341, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing lon attribute', () => { + let geoObject = { + lat: 123.5624234, + accuracy: 20 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + + it('should return false if the geo object is missing accuracy attribute', () => { + let geoObject = { + lat: 123.5624234, + lon: 23.6712341 + }; + expect(validateGeoObject(geoObject)).to.equal(false); + }); + }); + + describe('getDomain', function () { + it('should return valid domain from publisherDomain config', () => { + let pageUrl = 'https://www.example.com/page/prebid/exam.html'; + config.setConfig({publisherDomain: pageUrl}); + expect(getDomain(pageUrl)).to.equal('example.com'); + }); + it('should return valid domain from pageUrl argument', () => { + let pageUrl = 'https://www.example.com/page/prebid/exam.html'; + config.setConfig({publisherDomain: ''}); + expect(getDomain(pageUrl)).to.equal('example.com'); + }); + it('should return undefined if pageUrl and publisherDomain not config', () => { + let pageUrl; + config.setConfig({publisherDomain: ''}); + expect(getDomain(pageUrl)).to.equal(pageUrl); + }); + }); }); From 72820960581e8298fc8580e3098e462105aee671 Mon Sep 17 00:00:00 2001 From: novatiq <79258366+novatiq@users.noreply.github.com> Date: Fri, 12 Mar 2021 16:01:35 +0200 Subject: [PATCH 0682/1476] Novatiq ID System: add snowflake userId submodule (#6350) * 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 --- modules/.submodules.json | 3 +- modules/novatiqIdSystem.js | 88 +++++++++++++++++++++++ modules/novatiqIdSystem.md | 36 ++++++++++ modules/userId/eids.js | 9 +++ modules/userId/eids.md | 7 ++ modules/userId/userId.md | 4 ++ test/spec/modules/novatiqIdSystem_spec.js | 70 ++++++++++++++++++ 7 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 modules/novatiqIdSystem.js create mode 100644 modules/novatiqIdSystem.md create mode 100644 test/spec/modules/novatiqIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index fc69dd276a3..a7cf1f54426 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -20,7 +20,8 @@ "fabrickIdSystem", "verizonMediaIdSystem", "pubProvidedIdSystem", - "tapadIdSystem" + "tapadIdSystem", + "novatiqIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js new file mode 100644 index 00000000000..fbfa6ca8abc --- /dev/null +++ b/modules/novatiqIdSystem.js @@ -0,0 +1,88 @@ +/** + * This module adds novatiqId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/novatiqIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; + +/** @type {Submodule} */ +export const novatiqIdSubmodule = { + +/** +* used to link submodule with config +* @type {string} +*/ + name: 'novatiq', + + /** +* decode the stored id value for passing to bid requests +* @function +* @returns {novatiq: {snowflake: string}} +*/ + decode(novatiqId, config) { + let responseObj = { + novatiq: { + snowflake: novatiqId + } + }; + return responseObj; + }, + + /** +* performs action to obtain id and return a value in the callback's response argument +* @function +* @param {SubmoduleConfig} config +* @returns {id: string} +*/ + getId(config) { + function snowflakeId(placeholder) { + return placeholder + ? (placeholder ^ Math.random() * 16 >> placeholder / 4).toString(16) + : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11 + 1e3).replace(/[018]/g, snowflakeId); + } + + const configParams = config.params || {}; + const srcId = this.getSrcId(configParams); + utils.logInfo('NOVATIQ Sync request used sourceid param: ' + srcId); + + let partnerhost; + partnerhost = window.location.hostname; + utils.logInfo('NOVATIQ partner hostname: ' + partnerhost); + + const novatiqId = snowflakeId(); + const url = 'https://spadsync.com/sync?sptoken=' + novatiqId + '&sspid=' + srcId + '&ssphost=' + partnerhost; + ajax(url, undefined, undefined, { method: 'GET', withCredentials: false }); + + utils.logInfo('NOVATIQ snowflake: ' + novatiqId); + return { 'id': novatiqId } + }, + + getSrcId(configParams) { + utils.logInfo('NOVATIQ Configured sourceid param: ' + configParams.sourceid); + + function isHex(str) { + var a = parseInt(str, 16); + return (a.toString(16) === str) + } + + let srcId; + if (typeof configParams.sourceid === 'undefined' || configParams.sourceid === null || configParams.sourceid === '') { + srcId = '000'; + utils.logInfo('NOVATIQ sourceid param set to value 000 due to undefined parameter or missing value in config section'); + } else if (configParams.sourceid.length < 3 || configParams.sourceid.length > 3) { + srcId = '001'; + utils.logInfo('NOVATIQ sourceid param set to value 001 due to wrong size in config section 3 chars max e.g. 1ab'); + } else if (isHex(configParams.sourceid) == false) { + srcId = '002'; + utils.logInfo('NOVATIQ sourceid param set to value 002 due to wrong format in config section expecting hex value only'); + } else { + srcId = configParams.sourceid; + } + return srcId + } +}; +submodule('userId', novatiqIdSubmodule); diff --git a/modules/novatiqIdSystem.md b/modules/novatiqIdSystem.md new file mode 100644 index 00000000000..ce561a696e3 --- /dev/null +++ b/modules/novatiqIdSystem.md @@ -0,0 +1,36 @@ +# Novatiq Snowflake ID + +Novatiq proprietary dynamic snowflake ID is a unique, non sequential and single use identifier for marketing activation. Our in network solution matches verification requests to telco network IDs, safely and securely inside telecom infrastructure. Novatiq snowflake ID can be used for identity validation and as a secured 1st party data delivery mechanism. + +## Novatiq Snowflake ID Configuration + +Enable by adding the Novatiq submodule to your Prebid.js package with: + +``` +gulp build --modules=novatiqIdSystem,userId +``` + +Module activation and configuration: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'novatiq', + params: { + sourceid '1a3', // change to the Partner Number you received from Novatiq + } + } + }], + auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules + } +}); +``` + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | Module identification: `"novatiq"` | `"novatiq"` | +| params | Required | Object | Configuration specifications for the Novatiq module. | | +| params.sourceid | Required | String | This is the Novatiq Partner Number obtained via Novatiq registration. | `1a3` | + +If you have any questions, please reach out to us at prebid@novatiq.com. diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 80750ccaae8..a7e5eaf6061 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -172,6 +172,15 @@ const USER_IDS_CONFIG = { 'tapadId': { source: 'tapad.com', atype: 1 + }, + + // Novatiq Snowflake + 'novatiq': { + getValue: function(data) { + return data.snowflake + }, + source: 'novatiq.com', + atype: 1 } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index b69c4b9bd5e..404066d53e4 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -148,6 +148,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 1 }] + }, + { + source: 'novatiq.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 267b3a60cea..a7f98fb39a0 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -182,6 +182,10 @@ pbjs.setConfig({ { name: "criteo", value: { "criteoId": "wK-fkF8zaEIlMkZMbHl3eFo4NEtoNmZaeXJtYkFjZlVuWjBhcjJMaTRYd3pZNSUyQnlKRHNGRXlpdzdjd3pjVzhjcSUyQmY4eTFzN3VSZjV1ZyUyRlA0U2ZiR0UwN2I4bDZRJTNEJTNE" } + }, + { + name: "novatiq", + value: { "snowflake": "81b001ec-8914-488c-a96e-8c220d4ee08895ef" } }], syncDelay: 5000 } diff --git a/test/spec/modules/novatiqIdSystem_spec.js b/test/spec/modules/novatiqIdSystem_spec.js new file mode 100644 index 00000000000..60c82626450 --- /dev/null +++ b/test/spec/modules/novatiqIdSystem_spec.js @@ -0,0 +1,70 @@ +import { novatiqIdSubmodule } from 'modules/novatiqIdSystem.js'; +import * as utils from 'src/utils.js'; +import { server } from 'test/mocks/xhr.js'; + +describe('novatiqIdSystem', function () { + describe('getSrcId', function() { + it('getSrcId should set srcId value to 000 due to undefined parameter in config section', function() { + const config = { params: { } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set srcId value to 000 due to missing value in config section', function() { + const config = { params: { sourceid: '' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set value to 000 due to null value in config section', function() { + const config = { params: { sourceid: null } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('000'); + }); + + it('getSrcId should set value to 001 due to wrong length in config section max 3 chars', function() { + const config = { params: { sourceid: '1234' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('001'); + }); + + it('getSrcId should set value to 002 due to wrong format in config section', function() { + const config = { params: { sourceid: '1xc' } }; + const configParams = config.params || {}; + const response = novatiqIdSubmodule.getSrcId(configParams); + expect(response).to.eq('002'); + }); + }); + + describe('getId', function() { + it('should log message if novatiqId has wrong format', function() { + const config = { params: { sourceid: '123' } }; + const response = novatiqIdSubmodule.getId(config); + expect(response.id).to.have.length(40); + }); + + it('should log message if novatiqId not provided', function() { + const config = { params: { sourceid: '123' } }; + const response = novatiqIdSubmodule.getId(config); + expect(response.id).should.be.not.empty; + }); + }); + + describe('decode', function() { + it('should log message if novatiqId has wrong format', function() { + const novatiqId = '81b001ec-8914-488c-a96e-8c220d4ee08895ef'; + const response = novatiqIdSubmodule.decode(novatiqId); + expect(response.novatiq.snowflake).to.have.length(40); + }); + + it('should log message if novatiqId has wrong format', function() { + const novatiqId = '81b001ec-8914-488c-a96e-8c220d4ee08895ef'; + const response = novatiqIdSubmodule.decode(novatiqId); + expect(response.novatiq.snowflake).should.be.not.empty; + }); + }); +}) From 4b0779a665d42eae01c8ef6bc02d0fc687da9565 Mon Sep 17 00:00:00 2001 From: lasloche <62240785+lasloche@users.noreply.github.com> Date: Mon, 15 Mar 2021 16:24:53 +0200 Subject: [PATCH 0683/1476] Rise Bid Adapter: add session_id & is_wrapper params to adapter (#6407) * add new params to rise adapter * add unit tests for isWrapper and sessionId adapter params --- modules/riseBidAdapter.js | 3 ++- test/spec/modules/riseBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index a891e0aa883..e3265ad5d3e 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -211,7 +211,8 @@ function generateParameters(bid, bidderRequest) { bid_id: utils.getBidIdParameter('bidId', bid), bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), transaction_id: utils.getBidIdParameter('transactionId', bid), - session_id: utils.getBidIdParameter('auctionId', bid), + session_id: params.sessionId || utils.getBidIdParameter('auctionId', bid), + is_wrapper: !!params.isWrapper, publisher_name: domain, site_domain: domain, bidder_version: BIDDER_VERSION diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 176437c4f27..b3257cbda9d 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -75,6 +75,8 @@ describe('riseAdapter', function () { bidderCode: 'rise', } + const customSessionId = '12345678'; + it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { @@ -83,6 +85,22 @@ describe('riseAdapter', function () { } }); + it('sends the is_wrapper query param', function () { + bidRequests[0].params.isWrapper = true; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.is_wrapper).to.equal(true); + } + }); + + it('sends the custom session id as a query param', function () { + bidRequests[0].params.sessionId = customSessionId; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.session_id).to.equal(customSessionId); + } + }); + it('sends bid request to test ENDPOINT via GET', function () { const requests = spec.buildRequests(testModeBidRequests, bidderRequest); for (const request of requests) { From da7872851f04e813eb165ea9c5923520421bc8ff Mon Sep 17 00:00:00 2001 From: lasloche <62240785+lasloche@users.noreply.github.com> Date: Mon, 15 Mar 2021 16:25:27 +0200 Subject: [PATCH 0684/1476] IronSource Bid Adapter: add session_id & is_wrapper params to adapter (#6408) * add new params * add unit tests for isWrapper and sessionId adapter params --- modules/ironsourceBidAdapter.js | 3 ++- test/spec/modules/ironsourceBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js index ba510e86e7f..5b8531d7a85 100644 --- a/modules/ironsourceBidAdapter.js +++ b/modules/ironsourceBidAdapter.js @@ -211,7 +211,8 @@ function generateParameters(bid, bidderRequest) { bid_id: utils.getBidIdParameter('bidId', bid), bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), transaction_id: utils.getBidIdParameter('transactionId', bid), - session_id: utils.getBidIdParameter('auctionId', bid), + session_id: params.sessionId || utils.getBidIdParameter('auctionId', bid), + is_wrapper: !!params.isWrapper, publisher_name: domain, site_domain: domain, bidder_version: BIDDER_VERSION diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js index 93c3a6fb7b9..cca928ff28b 100644 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ b/test/spec/modules/ironsourceBidAdapter_spec.js @@ -75,6 +75,8 @@ describe('ironsourceAdapter', function () { bidderCode: 'ironsource', } + const customSessionId = '12345678'; + it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { @@ -98,6 +100,22 @@ describe('ironsourceAdapter', function () { } }); + it('sends the is_wrapper query param', function () { + bidRequests[0].params.isWrapper = true; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.is_wrapper).to.equal(true); + } + }); + + it('sends the custom session id as a query param', function () { + bidRequests[0].params.sessionId = customSessionId; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.session_id).to.equal(customSessionId); + } + }); + it('should send the correct width and height', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); for (const request of requests) { From dc67fe8987886809ee1165a5b145fd7781dcd9d7 Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Mon, 15 Mar 2021 14:29:25 -0400 Subject: [PATCH 0685/1476] AMX Bid Adapter: add or update general adapter support and code refactoring (#6403) * AMX Bid adapter improvements * fix eslint issues (breaking CI) --- modules/amxBidAdapter.js | 115 +++++++++++------------- test/spec/modules/amxBidAdapter_spec.js | 87 ++++++++++++------ 2 files changed, 114 insertions(+), 88 deletions(-) diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 497c2142b9b..5794b52dce0 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -1,6 +1,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel } from '../src/utils.js'; +import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel, isFn, logError } from '../src/utils.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -9,7 +9,6 @@ const storage = getStorageManager(737, BIDDER_CODE); const SIMPLE_TLD_TEST = /\.com?\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; const VERSION = 'pba1.2.1'; -const xmlDTDRxp = /^\s*<\?xml[^\?]+\?>/; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; const AMUID_KEY = '__amuidpb'; @@ -45,11 +44,16 @@ function flatMap(input, mapFn) { .reduce((acc, item) => item != null && acc.concat(item), []) } -const generateDTD = (xmlDocument) => - ``; - const isVideoADM = (html) => html != null && VAST_RXP.test(html); -const getMediaType = (bid) => isVideoADM(bid.adm) ? VIDEO : BANNER; + +function getMediaType(bid) { + if (isVideoADM(bid.adm)) { + return VIDEO; + } + + return BANNER; +} + const nullOrType = (value, type) => value == null || (typeof value) === type // eslint-disable-line valid-typeof @@ -103,6 +107,32 @@ const trackEvent = (eventName, data) => eid: getUniqueIdentifierStr(), })}`); +const DEFAULT_MIN_FLOOR = 0; + +function ensureFloor(floorValue) { + return typeof floorValue === 'number' && isFinite(floorValue) && floorValue > 0.0 + ? floorValue : DEFAULT_MIN_FLOOR; +} + +function getFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.floor', DEFAULT_MIN_FLOOR); + } + + try { + const floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + bidRequest: bid + }); + return floor.floor; + } catch (e) { + logError('call to getFloor failed: ', e); + return DEFAULT_MIN_FLOOR; + } +} + function convertRequest(bid) { const size = largestSize(bid.sizes, bid.mediaTypes) || [0, 0]; const isVideoBid = bid.mediaType === VIDEO || VIDEO in bid.mediaTypes @@ -116,16 +146,21 @@ function convertRequest(bid) { bid.sizes, deepAccess(bid, `mediaTypes.${BANNER}.sizes`, []) || [], deepAccess(bid, `mediaTypes.${VIDEO}.sizes`, []) || [], - ] + ]; + + const videoData = deepAccess(bid, `mediaTypes.${VIDEO}`, {}) || {}; const params = { au, av, + vd: videoData, vr: isVideoBid, ms: multiSizes, aw: size[0], ah: size[1], tf: 0, + sc: bid.schain || {}, + f: ensureFloor(getFloor(bid)) }; if (typeof tid === 'string' && tid.length > 0) { @@ -143,52 +178,6 @@ function decorateADM(bid) { return bid.adm + impressions; } -function transformXmlSimple(bid) { - const pixels = [] - _each([bid.nurl].concat(bid.ext != null && bid.ext.himp != null ? bid.ext.himp : []), (pixel) => { - if (pixel != null) { - pixels.push(``) - } - }); - // find the current "Impression" here & slice ours in - const impressionIndex = bid.adm.indexOf(' url != null); - - _each(pixels, (pxl) => { - const imagePixel = doc.createElement('Impression'); - const cdata = doc.createCDATASection(pxl); - imagePixel.appendChild(cdata); - root.appendChild(imagePixel); - }); - - const dtdMatch = xmlDTDRxp.exec(bid.adm); - return (dtdMatch != null ? dtdMatch[0] : generateDTD(doc)) + getOuterHTML(doc.documentElement); -} - function resolveSize(bid, request, bidId) { if (bid.w != null && bid.w > 1 && bid.h != null && bid.h > 1) { return [bid.w, bid.h]; @@ -212,14 +201,16 @@ function values(source) { }); } +const isTrue = (boolValue) => + boolValue === true || boolValue === 1 || boolValue === 'true'; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid(bid) { return nullOrType(deepAccess(bid, 'params.endpoint', null), 'string') && - nullOrType(deepAccess(bid, 'params.tagId', null), 'string') && - nullOrType(deepAccess(bid, 'params.testMode', null), 'boolean'); + nullOrType(deepAccess(bid, 'params.tagId', null), 'string') }, buildRequests(bidRequests, bidderRequest) { @@ -239,8 +230,9 @@ export const spec = { brc: fbid.bidderRequestsCount || 0, bwc: fbid.bidderWinsCount || 0, trc: fbid.bidRequestsCount || 0, - tm: testMode, + tm: isTrue(testMode), V: '$prebid.version$', + vg: '$$PREBID_GLOBAL$$', i: (testMode && tagId != null) ? tagId : getID(loc), l: {}, f: 0.01, @@ -259,7 +251,8 @@ export const spec = { d: '', m: createBidMap(bidRequests), cpp: config.getConfig('coppa') ? 1 : 0, - fpd: config.getLegacyFpd(config.getConfig('ortb2')), + fpd2: config.getConfig('ortb2'), + tmax: config.getConfig('bidderTimeout'), eids: values(bidRequests.reduce((all, bid) => { // we only want unique ones in here if (bid == null || bid.userIdAsEids == null) { @@ -306,7 +299,6 @@ export const spec = { }, interpretResponse(serverResponse, request) { - // validate the body/response const response = serverResponse.body; if (response == null || typeof response === 'string') { return []; @@ -320,13 +312,14 @@ export const spec = { return flatMap(response.r[bidID], (siteBid) => siteBid.b.map((bid) => { const mediaType = getMediaType(bid); - // let ad = null; - let ad = mediaType === BANNER ? decorateADM(bid) : decorateVideoADM(bid); + const ad = mediaType === BANNER ? decorateADM(bid) : bid.adm; + if (ad == null) { return null; } const size = resolveSize(bid, request.data, bidID); + const defaultExpiration = mediaType === BANNER ? 240 : 300; return ({ requestId: bidID, @@ -341,7 +334,7 @@ export const spec = { advertiserDomains: bid.adomain, mediaType, }, - ttl: mediaType === VIDEO ? 90 : 70 + ttl: typeof bid.exp === 'number' ? bid.exp : defaultExpiration, }); })).filter((possibleBid) => possibleBid != null); }); diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 0658fe9f33c..863a8a1d0fc 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -31,20 +31,6 @@ const sampleFPD = { } }; -const legacySampleFPD = { - context: { - keywords: 'sample keywords', - data: { - pageType: 'article' - - } - }, - user: { - gender: 'O', - yob: 1982, - } -}; - const stubConfig = (withStub) => { const stub = sinon.stub(config, 'getConfig').callsFake( (arg) => arg === 'ortb2' ? sampleFPD : null @@ -74,6 +60,15 @@ const sampleBidRequestBase = { endpoint: 'https://httpbin.org/post', }, sizes: [[320, 50]], + getFloor(params) { + if (params.size == null || params.currency == null || params.mediaType == null) { + throw new Error(`getFloor called with incomplete params: ${JSON.stringify(params)}`) + } + return { + floor: 0.5, + currency: 'USD' + } + }, mediaTypes: { [BANNER]: { sizes: [[300, 250]] @@ -85,13 +80,28 @@ const sampleBidRequestBase = { auctionId: utils.getUniqueIdentifierStr(), }; +const schainConfig = { + ver: '1.0', + nodes: [{ + asi: 'greatnetwork.exchange', + sid: '000001', + hp: 1, + rid: 'bid_request_1', + domain: 'publisher.com' + }] +}; + const sampleBidRequestVideo = { ...sampleBidRequestBase, bidId: sampleRequestId + '_video', sizes: [[300, 150]], + schain: schainConfig, mediaTypes: { [VIDEO]: { - sizes: [[360, 250]] + sizes: [[360, 250]], + context: 'adpod', + adPodDurationSec: 90, + contentMode: 'live' } } }; @@ -120,6 +130,7 @@ const sampleServerResponse = { 'h': 600, 'id': '2014691335735134254', 'impid': '1', + 'exp': 90, 'price': 0.25, 'w': 300 }, @@ -139,6 +150,7 @@ const sampleServerResponse = { 'h': 1, 'id': '7735706981389902829', 'impid': '1', + 'exp': 90, 'price': 0.25, 'w': 1 }, @@ -160,8 +172,11 @@ describe('AmxBidAdapter', () => { expect(spec.isBidRequestValid({params: { tagId: 'test' }})).to.equal(true) }); - it('testMode is an optional boolean', () => { - expect(spec.isBidRequestValid({params: { testMode: 1 }})).to.equal(false) + it('testMode is an optional truthy value', () => { + expect(spec.isBidRequestValid({params: { testMode: 1 }})).to.equal(true) + expect(spec.isBidRequestValid({params: { testMode: 'true' }})).to.equal(true) + // ignore invalid values (falsy) + expect(spec.isBidRequestValid({params: { testMode: 'non-truthy-invalid-value' }})).to.equal(true) expect(spec.isBidRequestValid({params: { testMode: false }})).to.equal(true) }); @@ -195,6 +210,17 @@ describe('AmxBidAdapter', () => { expect(url).to.equal('https://prebid.a-mo.net/a/c') }); + it('will read the prebid version & global', () => { + const { data: { V: prebidVersion, vg: prebidGlobal } } = spec.buildRequests([{ + ...sampleBidRequestBase, + params: { + testMode: true + } + }], sampleBidderRequest); + expect(prebidVersion).to.equal('$prebid.version$') + expect(prebidGlobal).to.equal('$$PREBID_GLOBAL$$') + }); + it('reads test mode from the first bid request', () => { const { data } = spec.buildRequests([{ ...sampleBidRequestBase, @@ -269,7 +295,7 @@ describe('AmxBidAdapter', () => { it('will forward first-party data', () => { stubConfig(() => { const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); - expect(data.fpd).to.deep.equal(legacySampleFPD) + expect(data.fpd2).to.deep.equal(sampleFPD) }); }); @@ -315,20 +341,24 @@ describe('AmxBidAdapter', () => { expect(data.m[sampleRequestId]).to.deep.equal({ av: true, au: 'div-gpt-ad-example', + vd: {}, ms: [ [[320, 50]], [[300, 250]], [] ], aw: 300, + sc: {}, ah: 250, tf: 0, + f: 0.5, vr: false }); expect(data.m[sampleRequestId + '_2']).to.deep.equal({ av: true, aw: 300, au: 'div-gpt-ad-example', + sc: {}, ms: [ [[320, 50]], [[300, 250]], @@ -336,7 +366,9 @@ describe('AmxBidAdapter', () => { ], i: 'example', ah: 250, + vd: {}, tf: 0, + f: 0.5, vr: false, }); }); @@ -354,7 +386,15 @@ describe('AmxBidAdapter', () => { av: true, aw: 360, ah: 250, + sc: schainConfig, + vd: { + sizes: [[360, 250]], + context: 'adpod', + adPodDurationSec: 90, + contentMode: 'live' + }, tf: 0, + f: 0.5, vr: true }); }); @@ -401,7 +441,7 @@ describe('AmxBidAdapter', () => { }, width: 300, height: 600, // from the bid itself - ttl: 70, + ttl: 90, ad: sampleDisplayAd( `` + `` @@ -412,20 +452,13 @@ describe('AmxBidAdapter', () => { it('can parse a video ad', () => { const parsed = spec.interpretResponse({ body: sampleServerResponse }, baseRequest) expect(parsed.length).to.equal(2) - - // we should have display, video, display - const xml = parsed[1].vastXml - delete parsed[1].vastXml - - expect(xml).to.have.string(``) - expect(xml).to.have.string(``) - expect(parsed[1]).to.deep.equal({ ...baseBidResponse, meta: { ...baseBidResponse.meta, mediaType: VIDEO, }, + vastXml: sampleVideoAd(''), width: 300, height: 250, ttl: 90, From 670ab08c3e030cf04b94e324719c3ad159ebf309 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 16 Mar 2021 07:25:34 +0300 Subject: [PATCH 0686/1476] VOX Bid adapter, Hybrid Bid adapter: fix global var name to avoid conflicts with astraOne adapter. (#6416) Co-authored-by: Petrov Denis --- modules/hybridBidAdapter.js | 2 +- modules/voxBidAdapter.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index dd55483ef33..e7281086a92 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -164,7 +164,7 @@ function wrapAd(bid, bidData) { parentDocument.style.width = "100%"; } var _content = "${encodeURIComponent(JSON.stringify(bid.inImageContent))}"; - window._ao_ssp.registerInImage(JSON.parse(decodeURIComponent(_content))); + window._hyb_prebid_ssp.registerInImage(JSON.parse(decodeURIComponent(_content))); `; diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 450f270db31..73df9bb8b9b 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -129,7 +129,7 @@ function wrapInImageBanner(bid, bidData) { var s = document.getElementById("prebidrenderer"); s.onload = function () { var _html = "${encodeURIComponent(JSON.stringify(bid))}"; - window._ao_ssp.registerInImage(JSON.parse(decodeURIComponent(_html))); + window._hyb_prebid_ssp.registerInImage(JSON.parse(decodeURIComponent(_html))); } s.src = "https://st.hybrid.ai/prebidrenderer.js?t=" + Date.now(); if (parent.window.frames[window.name]) { @@ -157,7 +157,7 @@ function wrapBanner(bid, bidData) { var s = document.getElementById("prebidrenderer"); s.onload = function () { var _html = "${encodeURIComponent(JSON.stringify(bid))}"; - window._ao_ssp.registerAds(JSON.parse(decodeURIComponent(_html))); + window._hyb_prebid_ssp.registerAds(JSON.parse(decodeURIComponent(_html))); } s.src = "https://st.hybrid.ai/prebidrenderer.js?t=" + Date.now(); From 20bea36f5fc6cc5b8d5d0a7c82fa852e7e060eee Mon Sep 17 00:00:00 2001 From: Yevhenii Melnyk Date: Tue, 16 Mar 2021 05:31:55 +0100 Subject: [PATCH 0687/1476] LiveIntent Id System: fix for parsing response twice (#6418) * Don't parse response twice in LiveIntent Id submodule * Update the liveintent module test to have the 204 response --- modules/liveIntentIdSystem.js | 10 +--------- test/spec/modules/liveIntentIdSystem_spec.js | 17 ++++++++++------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index f87d67aae8e..5a955eefa92 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -166,15 +166,7 @@ export const liveIntentIdSubmodule = { const result = function(callback) { liveConnect.resolve( response => { - let responseObj = {}; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - utils.logError(error); - } - } - callback(responseObj); + callback(response); }, error => { utils.logError(`${MODULE_NAME}: ID fetch encountered an error: `, error); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 3b4e4b9d9a7..f1de2f3bf93 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -36,7 +36,7 @@ describe('LiveIntentId', function() { resetLiveIntentIdSubmodule(); }); - it('should initialize LiveConnect with a privacy string when getId, and include it in the resolution request', function() { + it('should initialize LiveConnect with a privacy string when getId, and include it in the resolution request', function () { uspConsentDataStub.returns('1YNY'); gdprConsentDataStub.returns({ gdprApplies: true, @@ -47,12 +47,16 @@ describe('LiveIntentId', function() { submoduleCallback(callBackSpy); let request = server.requests[1]; expect(request.url).to.match(/.*us_privacy=1YNY.*&gdpr=1&gdpr_consent=consentDataString.*/); + const response = { + unifiedId: 'a_unified_id', + segments: [123, 234] + } request.respond( 200, responseHeader, - JSON.stringify({}) + JSON.stringify(response) ); - expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledOnceWith(response)).to.be.true; }); it('should fire an event when getId', function() { @@ -131,11 +135,10 @@ describe('LiveIntentId', function() { let request = server.requests[1]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899'); request.respond( - 200, - responseHeader, - JSON.stringify({}) + 204, + responseHeader ); - expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.calledOnceWith({})).to.be.true; }); it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { From b9cd1a4e6a7c46d1095b8886f1e64c7d021603a3 Mon Sep 17 00:00:00 2001 From: Aleksa Trajkovic Date: Tue, 16 Mar 2021 13:54:50 +0100 Subject: [PATCH 0688/1476] PBJS Core: use mediaType renderer when backupOnly and no bid.renderer (#6419) * use mediaType renderer when backupOnly and no bid.renderer * check if necessary renderer properties are defined --- src/auction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auction.js b/src/auction.js index 217a50be3d6..7005d56827e 100644 --- a/src/auction.js +++ b/src/auction.js @@ -533,9 +533,9 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) { var renderer = null; // the renderer for the mediaType takes precendence - if (mediaTypeRenderer && mediaTypeRenderer.url && !(mediaTypeRenderer.backupOnly === true && mediaTypeRenderer.render)) { + if (mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render && !(mediaTypeRenderer.backupOnly === true && bid.renderer)) { renderer = mediaTypeRenderer; - } else if (adUnitRenderer && adUnitRenderer.url && !(adUnitRenderer.backupOnly === true && bid.renderer)) { + } else if (adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render && !(adUnitRenderer.backupOnly === true && bid.renderer)) { renderer = adUnitRenderer; } From 70a41a51ac945b90c47224c294e64fc35742d604 Mon Sep 17 00:00:00 2001 From: susyt Date: Tue, 16 Mar 2021 07:56:52 -0700 Subject: [PATCH 0689/1476] GumGum Bid Adapter: pass bidfloor currency in bidrequest (#6391) * adds support for zone and pubId params * adds support for iriscat field * sets mediatype depending on product id * Update doc for mediaType needed for video products * makes slot and invideo products avail for pubId * updates gumgum doc * lint * adds missing comma in gumgum doc * adds currency in ad request, adds unit test * readd the previous irisid changes * remove the only in testing --- modules/gumgumBidAdapter.js | 24 +++++++++++----------- test/spec/modules/gumgumBidAdapter_spec.js | 4 ++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 2b0f7e03d22..9a01cd21fa4 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -208,25 +208,24 @@ function _getVidParams (attributes) { * @param {Object} bid * @returns {Number} floor */ -function _getFloor (mediaTypes, bidfloor, bid) { +function _getFloor (mediaTypes, staticBidfloor, bid) { const curMediaType = Object.keys(mediaTypes)[0] || 'banner'; - let floor = bidfloor || 0; + const bidFloor = { floor: 0, currency: 'USD' }; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({ - currency: 'USD', + const { currency, floor } = bid.getFloor({ mediaType: curMediaType, size: '*' }); + floor && (bidFloor.floor = floor); + currency && (bidFloor.currency = currency); - if (typeof floorInfo === 'object' && - floorInfo.currency === 'USD' && - !isNaN(parseFloat(floorInfo.floor))) { - floor = Math.max(floor, parseFloat(floorInfo.floor)); + if (staticBidfloor && floor && currency === 'USD') { + bidFloor.floor = Math.max(staticBidfloor, parseFloat(floor)); } } - return floor; + return bidFloor; } /** @@ -250,7 +249,7 @@ function buildRequests (validBidRequests, bidderRequest) { transactionId, userId = {} } = bidRequest; - const bidFloor = _getFloor(mediaTypes, params.bidfloor, bidRequest); + const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest); let sizes = [1, 1]; let data = {}; @@ -265,8 +264,9 @@ function buildRequests (validBidRequests, bidderRequest) { data.pv = pageViewId; } - if (bidFloor) { - data.fp = bidFloor; + if (floor) { + data.fp = floor; + data.fpc = currency; } if (params.iriscat && typeof params.iriscat === 'string') { diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 0d37c8b1d25..3f5d32bcef7 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -258,6 +258,10 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.fp).to.equal(bidfloor); }); + it('should return a floor currency', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data.fpc).to.equal(floorTestData.currency); + }) }); it('sends bid request to ENDPOINT via GET', function () { From 46ecf2c41400d933ecdf77ba0c1e5513e1982d97 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 16 Mar 2021 21:37:28 -0700 Subject: [PATCH 0690/1476] Documentation: fixed a typo and sentence structure (#6421) --- modules/geoedgeRtdProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/geoedgeRtdProvider.md b/modules/geoedgeRtdProvider.md index e4aa046a97d..5414606612c 100644 --- a/modules/geoedgeRtdProvider.md +++ b/modules/geoedgeRtdProvider.md @@ -4,7 +4,7 @@ Module Name: Geoedge Rtd provider Module Type: Rtd Provider Maintainer: guy.books@geoedge.com -The Geoedge Realtime module let pusblishers to block bad ads such as automatic redirects, malware, offensive creatives and landing pages. +The Geoedge Realtime module lets publishers block bad ads such as automatic redirects, malware, offensive creatives and landing pages. To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key. ## Integration From 8beade60241293e09d745bec932719b59134b2f5 Mon Sep 17 00:00:00 2001 From: Daniel Liebner Date: Wed, 17 Mar 2021 01:03:06 -0400 Subject: [PATCH 0691/1476] Bid Glass Bid Adapter: pass options in bid request (#6424) * Added bidglass adapter + test * PR Review Updates: - Added formal params to getUserSyncs function definition - getUserSyncs now always returns an array - Improved unit test coverage * PR Review Updates: - Removed unused methods: getUserSyncs, onTimeout, onBidWon, onSetTargeting - Removed getUserSyncs unit test - Removed "dead code" - Removed some unnecessary comments - Fixed usage of parseInt * Bid Glass Bid Adapter: pass options in bid request --- modules/bidglassBidAdapter.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 6db35f184ca..3162228ce58 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -46,7 +46,7 @@ export const spec = { return window === window.top ? window.location.href : window.parent === window.top ? document.referrer : null; }; let getOrigins = function() { - var ori = ['https://' + window.location.hostname]; + var ori = [window.location.protocol + '//' + window.location.hostname]; if (window.location.ancestorOrigins) { for (var i = 0; i < window.location.ancestorOrigins.length; i++) { @@ -56,7 +56,7 @@ export const spec = { // Derive the parent origin var parts = document.referrer.split('/'); - ori.push('https://' + parts[2]); + ori.push(parts[0] + '//' + parts[2]); if (window.parent !== window.top) { // Additional unknown origins exist @@ -71,11 +71,15 @@ export const spec = { bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); bid.sizes = bid.sizes.filter(size => utils.isArray(size)); - // Stuff to send: [bid id, sizes, adUnitId] + var options = utils.deepClone(bid.params); + delete options.adUnitId; + + // Stuff to send: [bid id, sizes, adUnitId, options] imps.push({ bidId: bid.bidId, sizes: bid.sizes, - adUnitId: utils.getBidIdParameter('adUnitId', bid.params) + adUnitId: utils.getBidIdParameter('adUnitId', bid.params), + options: options }); }); From 990d287e3f158c2dca366c39c2f58d509137d8d1 Mon Sep 17 00:00:00 2001 From: Nick Peceniak Date: Wed, 17 Mar 2021 00:27:41 -0600 Subject: [PATCH 0692/1476] Spotx Bid Adapter: add publisher support for cache.ignoreBidderCacheKey (#6413) * Support ignoreBidderCacheKey in spotxBidAdapter * Update spotxBidAdapter_spec.js * Update spotxBidAdapter_spec.js Co-authored-by: Nick Peceniak --- modules/spotxBidAdapter.js | 16 ++++++++++--- test/spec/modules/spotxBidAdapter_spec.js | 28 +++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index c85a836435e..05e4e0ba1ef 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -215,6 +215,12 @@ export const spec = { } }; + // If the publisher asks to ignore the bidder cache key we need to return the full vast xml + // so that it can be cached on the publishes specified server. + if (!!config.getConfig('cache') && !!config.getConfig('cache.url') && (config.getConfig('cache.ignoreBidderCacheKey') === true)) { + requestPayload['ext']['wrap_response'] = 0; + } + if (utils.getBidIdParameter('number_of_ads', bid.params)) { requestPayload['ext']['number_of_ads'] = utils.getBidIdParameter('number_of_ads', bid.params); } @@ -336,14 +342,18 @@ export const spec = { ttl: 360, netRevenue: true, channel_id: serverResponseBody.id, - cache_key: spotxBid.ext.cache_key, - vastUrl: 'https://search.spotxchange.com/ad/vast.html?key=' + spotxBid.ext.cache_key, - videoCacheKey: spotxBid.ext.cache_key, mediaType: VIDEO, width: spotxBid.w, height: spotxBid.h }; + if (!!config.getConfig('cache') && !!config.getConfig('cache.url') && (config.getConfig('cache.ignoreBidderCacheKey') === true)) { + 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.meta = bid.meta || {}; if (spotxBid && spotxBid.adomain && spotxBid.adomain.length > 0) { bid.meta.advertiserDomains = spotxBid.adomain; diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 927599ac986..873914441aa 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -379,6 +379,32 @@ describe('the spotx adapter', function () { expect(request.data.site.page).to.equal('prebid.js'); }); + + it('should set ext.wrap_response to 0 when cache url is set and ignoreBidderCacheKey is true', function() { + var request; + + var origGetConfig = config.getConfig; + sinon.stub(config, 'getConfig').callsFake(function (key) { + if (key === 'cache') { + return { + url: 'prebidCacheLocation', + ignoreBidderCacheKey: true + }; + } + if (key === 'cache.url') { + return 'prebidCacheLocation'; + } + if (key === 'cache.ignoreBidderCacheKey') { + return true; + } + return origGetConfig.apply(config, arguments); + }); + + request = spec.buildRequests([bid], bidRequestObj)[0]; + + expect(request.data.ext.wrap_response).to.equal(0); + config.getConfig.restore(); + }); }); describe('interpretResponse', function() { @@ -469,7 +495,6 @@ describe('the spotx adapter', function () { expect(responses[0].requestId).to.equal(123); expect(responses[0].ttl).to.equal(360); expect(responses[0].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); - expect(responses[0].videoCacheKey).to.equal('cache123'); expect(responses[0].width).to.equal(400); expect(responses[1].cache_key).to.equal('cache124'); expect(responses[1].channel_id).to.equal(12345); @@ -483,7 +508,6 @@ describe('the spotx adapter', function () { expect(responses[1].requestId).to.equal(124); expect(responses[1].ttl).to.equal(360); expect(responses[1].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache124'); - expect(responses[1].videoCacheKey).to.equal('cache124'); expect(responses[1].width).to.equal(200); }); }); From a75f2f48356fb74aedf825971e1b06a619984443 Mon Sep 17 00:00:00 2001 From: BizzClick <73241175+BizzClick@users.noreply.github.com> Date: Wed, 17 Mar 2021 12:57:21 +0200 Subject: [PATCH 0693/1476] update prebid adapter. Add at, ccpa, gdpr and coppa support (#6405) --- modules/bizzclickBidAdapter.js | 19 ++++++++++++ test/spec/modules/bizzclickBidAdapter_spec.js | 29 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index 80d2f6b5395..2af9a7afed2 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -81,10 +81,12 @@ export const spec = { let data = { id: bidRequest.bidId, test: config.getConfig('debug') ? 1 : 0, + at: 1, cur: ['USD'], device: { w: winTop.screen.width, h: winTop.screen.height, + dnt: utils.getDNT() ? 1 : 0, language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '', }, site: { @@ -94,9 +96,26 @@ export const spec = { source: { tid: bidRequest.transactionId }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {} + }, + user: { + ext: {} + }, tmax: bidRequest.timeout, imp: [impObject], }; + if (bidRequest) { + if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { + utils.deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); + utils.deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); + } + + if (bidRequest.uspConsent !== undefined) { + utils.deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); + } + } bids.push(data) } return { diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js index 39ad4ae39c9..e0698c9eda8 100644 --- a/test/spec/modules/bizzclickBidAdapter_spec.js +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/bizzclickBidAdapter.js'; +import {config} from 'src/config.js'; const NATIVE_BID_REQUEST = { code: 'native_example', @@ -53,7 +54,11 @@ const BANNER_BID_REQUEST = { accountId: 'accountId' }, timeout: 1000, - + gdprConsent: { + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + gdprApplies: 1, + }, + uspConsent: 'uspConsent' } const bidRequest = { @@ -172,6 +177,22 @@ const NATIVE_BID_RESPONSE = { }; describe('BizzclickAdapter', function() { + describe('with COPPA', function() { + beforeEach(function() { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + }); + afterEach(function() { + config.getConfig.restore(); + }); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]); + expect(serverRequest.data[0].regs.coppa).to.equal(1); + }); + }); + describe('isBidRequestValid', function() { it('should return true when required params found', function () { expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); @@ -225,6 +246,12 @@ describe('BizzclickAdapter', function() { expect(request.method).to.equal('POST'); }); + it('check consent and ccpa string is set properly', function() { + expect(request.data[0].regs.ext.gdpr).to.equal(1); + expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString); + expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent); + }) + it('Returns valid URL', function () { expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); }); From b98707c541e473db84b8891ec67cdd59bda72233 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Thu, 18 Mar 2021 00:25:10 +0700 Subject: [PATCH 0694/1476] Qwarry Bid Adapter: add referer detection (#6412) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 2 +- test/spec/modules/qwarryBidAdapter_spec.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 7cb83520979..5ff48ec53f6 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -27,7 +27,7 @@ export const spec = { return { method: 'POST', url: ENDPOINT, - data: { requestId: bidderRequest.bidderRequestId, bids }, + data: { requestId: bidderRequest.bidderRequestId, bids, referer: bidderRequest.refererInfo.referer }, options: { contentType: 'application/json', customHeaders: { diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 91e3cf4bfdf..bc776f7ebe7 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -70,11 +70,12 @@ describe('qwarryBidAdapter', function () { describe('buildRequests', function () { let bidRequests = [REQUEST] - const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123' }) + const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123', refererInfo: { referer: 'http://test.com/path.html' } }) it('sends bid request to ENDPOINT via POST', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') + expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') From 24114d136537ff9f2f162f85adc45ef2e4a41433 Mon Sep 17 00:00:00 2001 From: SmartyAdsSSP <41569976+SmartyAdsSSP@users.noreply.github.com> Date: Wed, 17 Mar 2021 19:35:58 +0200 Subject: [PATCH 0695/1476] Smartyads Bid Adapter: add coppa field from config (#6402) * update adapter. Add coppa field from config * move stubs and restores for coppa tests --- modules/smartyadsBidAdapter.js | 2 ++ test/spec/modules/smartyadsBidAdapter_spec.js | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js index ff60d08e48b..610617155ed 100644 --- a/modules/smartyadsBidAdapter.js +++ b/modules/smartyadsBidAdapter.js @@ -1,5 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; const BIDDER_CODE = 'smartyads'; @@ -49,6 +50,7 @@ export const spec = { 'secure': 1, 'host': location.host, 'page': location.pathname, + 'coppa': config.getConfig('coppa') === true ? 1 : 0, 'placements': placements }; request.language.indexOf('-') != -1 && (request.language = request.language.split('-')[0]) diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 2780e88255d..8804050134a 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; import {spec} from '../../../modules/smartyadsBidAdapter.js'; +import { config } from '../../../src/config.js'; describe('SmartyadsAdapter', function () { let bid = { @@ -38,9 +39,10 @@ describe('SmartyadsAdapter', 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'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'coppa'); expect(data.deviceWidth).to.be.a('number'); expect(data.deviceHeight).to.be.a('number'); + expect(data.coppa).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'); @@ -57,6 +59,23 @@ describe('SmartyadsAdapter', function () { expect(data.placements).to.be.an('array').that.is.empty; }); }); + + describe('with COPPA', function() { + beforeEach(function() { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + }); + afterEach(function() { + config.getConfig.restore(); + }); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([bid]); + expect(serverRequest.data.coppa).to.equal(1); + }); + }); + describe('interpretResponse', function () { it('Should interpret banner response', function () { const banner = { From e0bd8e27d979ad9e7c4b9fe0be28a5f12d5f9a06 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 17 Mar 2021 15:15:24 -0400 Subject: [PATCH 0696/1476] PBS Bid Adapter: Fpd2.0 bug fix for first party data issue (#6428) * Bug fix for PBS data from FPD2.0 update: Merging request.site and request.user with site and user object in FPD. --- modules/prebidServerBidAdapter/index.js | 4 ++-- test/spec/modules/prebidServerBidAdapter_spec.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 088b5430f46..d3bbb347720 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -769,10 +769,10 @@ const OPEN_RTB_PROTOCOL = { const commonFpd = getConfig('ortb2') || {}; if (commonFpd.site) { - utils.deepSetValue(request, 'site', commonFpd.site); + utils.mergeDeep(request, {site: commonFpd.site}); } if (commonFpd.user) { - utils.deepSetValue(request, 'user', commonFpd.user); + utils.mergeDeep(request, {user: commonFpd.user}); } addBidderFirstPartyDataToRequest(request); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index babee7e10d7..45d96b96d66 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1631,13 +1631,14 @@ describe('S2S Adapter', function () { } } })); + const commonContextExpected = utils.mergeDeep({'page': 'http://mytestpage.com', 'publisher': {'id': '1'}}, commonContext); config.setConfig({ fpd: { context: commonContext, user: commonUser } }); config.setBidderConfig({ bidders: allowedBidders, config: { fpd: { context, user } } }); adapter.callBids(s2sBidRequest, 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(commonContext); + expect(parsedRequestBody.site).to.deep.equal(commonContextExpected); expect(parsedRequestBody.user).to.deep.equal(commonUser); }); From 04e49740cdd511cd024649d00bb74d6cc681b534 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 17 Mar 2021 15:26:26 -0400 Subject: [PATCH 0697/1476] Prebid 4.31.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7943e00f3c6..5af93962114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.31.0-pre", + "version": "4.31.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 76a78287df25c4914ee22e3516cb0f103bf88024 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 17 Mar 2021 15:46:06 -0400 Subject: [PATCH 0698/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5af93962114..ed13b6ae7da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.31.0", + "version": "4.32.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From c245959f925283eb5e356a2826dead5ca72f2c32 Mon Sep 17 00:00:00 2001 From: Junus Date: Thu, 18 Mar 2021 08:19:43 +0600 Subject: [PATCH 0699/1476] a4g Bid Adapter: delete adid and use crid if it exists (#6409) * Deleted adid * set crid if it's exist and added unit tests --- modules/a4gBidAdapter.js | 3 +- test/spec/modules/a4gBidAdapter_spec.js | 50 ++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index b7d8722e9f9..1961dba1f10 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -70,8 +70,7 @@ export const spec = { if (response.cpm > 0) { const bidResponse = { requestId: response.id, - creativeId: response.id, - adId: response.id, + creativeId: response.crid || response.id, cpm: response.cpm, width: response.width, height: response.height, diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index 3dccbb28426..cb05fa62ab6 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/a4gBidAdapter.js'; +import * as utils from 'src/utils.js'; describe('a4gAdapterTests', function () { describe('bidRequestValidity', function () { @@ -139,15 +140,54 @@ describe('a4gAdapterTests', function () { const bidResponse = { body: [{ - 'id': 'div-gpt-ad-1460505748561-0', + 'id': '51ef8751f9aead', 'ad': 'test ad', 'width': 320, 'height': 250, - 'cpm': 5.2 + 'cpm': 5.2, + 'crid': '111' }], headers: {} }; + it('should get correct bid response for banner ad', function () { + const expectedParse = [ + { + requestId: '51ef8751f9aead', + cpm: 5.2, + creativeId: '111', + width: 320, + height: 250, + ad: 'test ad', + currency: 'USD', + ttl: 120, + netRevenue: true + } + ]; + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + + it('should set creativeId to default value if not provided', function () { + const bidResponseWithoutCrid = utils.deepClone(bidResponse); + delete bidResponseWithoutCrid.body[0].crid; + const expectedParse = [ + { + requestId: '51ef8751f9aead', + cpm: 5.2, + creativeId: '51ef8751f9aead', + width: 320, + height: 250, + ad: 'test ad', + currency: 'USD', + ttl: 120, + netRevenue: true + } + ]; + const result = spec.interpretResponse(bidResponseWithoutCrid, bidRequest); + expect(result[0]).to.deep.equal(expectedParse[0]); + }) + it('required keys', function () { const result = spec.interpretResponse(bidResponse, bidRequest); @@ -169,5 +209,11 @@ describe('a4gAdapterTests', function () { expect(requiredKeys.indexOf(key) !== -1).to.equal(true); }); }) + + it('adId should not be equal to requestId', function () { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].requestId).to.not.equal(result[0].adId); + }) }); }); From 595fc0a08b5f97a319b81f8099394623218efe7c Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Thu, 18 Mar 2021 07:47:26 -0500 Subject: [PATCH 0700/1476] Mediawallah ID System: add openlink userId submodule (#5921) * My first commit * Removed unnecessary await operation * Bug fixes and compliance fixes * Fixing some formatting and naming * Updating code based on automated feedback. * Parking refactoring change for team review * update mwOpenLink module * remove .git 2 folder * Trying to force a change * update the PR comments * applying the changes * update submodules.json and userId.md * fix typo of module names * update module decode function and test code * update test codes * apply the suggestions from Prebid * fix count of modules. Co-authored-by: Eric Brown Co-authored-by: hanna Co-authored-by: hannapanova190119 <71532550+hannapanova190119@users.noreply.github.com> --- modules/.submodules.json | 1 + modules/mwOpenLinkIdSystem.js | 142 +++++++++++++++++++ modules/mwOpenLinkIdSystem.md | 43 ++++++ modules/userId/eids.js | 6 +- modules/userId/eids.md | 7 + modules/userId/userId.md | 7 + test/spec/modules/mwOpenLinkIdSystem_spec.js | 20 +++ test/spec/modules/userId_spec.js | 71 +++++++--- 8 files changed, 280 insertions(+), 17 deletions(-) create mode 100644 modules/mwOpenLinkIdSystem.js create mode 100644 modules/mwOpenLinkIdSystem.md create mode 100644 test/spec/modules/mwOpenLinkIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index a7cf1f54426..0f62627822a 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -20,6 +20,7 @@ "fabrickIdSystem", "verizonMediaIdSystem", "pubProvidedIdSystem", + "mwOpenLinkIdSystem", "tapadIdSystem", "novatiqIdSystem" ], diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js new file mode 100644 index 00000000000..b2381836d5d --- /dev/null +++ b/modules/mwOpenLinkIdSystem.js @@ -0,0 +1,142 @@ +/** + * This module adds MediaWallah OpenLink to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/mwOpenLinkIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const openLinkID = { + name: 'mwol', + cookie_expiration: (86400 * 1000 * 365 * 1) // 1 year +} + +const storage = getStorageManager(); + +function getExpirationDate() { + return (new Date(utils.timestamp() + openLinkID.cookie_expiration)).toGMTString(); +} + +function isValidConfig(configParams) { + if (!configParams) { + utils.logError('User ID - mwOlId submodule requires configParams'); + return false; + } + if (!configParams.accountId) { + utils.logError('User ID - mwOlId submodule requires accountId to be defined'); + return false; + } + if (!configParams.partnerId) { + utils.logError('User ID - mwOlId submodule requires partnerId to be defined'); + return false; + } + return true; +} + +function deserializeMwOlId(mwOlIdStr) { + const mwOlId = {}; + const mwOlIdArr = mwOlIdStr.split(','); + + mwOlIdArr.forEach(function(value) { + const pair = value.split(':'); + // unpack a value of 1 as true + mwOlId[pair[0]] = +pair[1] === 1 ? true : pair[1]; + }); + + return mwOlId; +} + +function serializeMwOlId(mwOlId) { + let components = []; + + if (mwOlId.eid) { + components.push('eid:' + mwOlId.eid); + } + if (mwOlId.ibaOptout) { + components.push('ibaOptout:1'); + } + if (mwOlId.ccpaOptout) { + components.push('ccpaOptout:1'); + } + + return components.join(','); +} + +function readCookie(name) { + if (!name) name = openLinkID.name; + const mwOlIdStr = storage.getCookie(name); + if (mwOlIdStr) { + return deserializeMwOlId(decodeURIComponent(mwOlIdStr)); + } + return null; +} + +function writeCookie(mwOlId) { + if (mwOlId) { + const mwOlIdStr = encodeURIComponent(serializeMwOlId(mwOlId)); + storage.setCookie(openLinkID.name, mwOlIdStr, getExpirationDate(), 'lax'); + } +} + +function register(configParams, olid) { + const { accountId, partnerId, uid } = configParams; + const url = 'https://ol.mediawallahscript.com/?account_id=' + accountId + + '&partner_id=' + partnerId + + '&uid=' + uid + + '&olid=' + olid + + '&cb=' + Math.random() + ; + ajax(url); +} + +function setID(configParams) { + if (!isValidConfig(configParams)) return undefined; + const mwOlId = readCookie(); + const newMwOlId = mwOlId ? utils.deepClone(mwOlId) : {eid: utils.generateUUID()}; + writeCookie(newMwOlId); + register(configParams, newMwOlId.eid); + return { + id: newMwOlId + }; +}; + +/* End MW */ + +export { writeCookie }; + +/** @type {Submodule} */ +export const mwOpenLinkIdSubModule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'mwOpenLinkId', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {MwOlId} mwOlId + * @return {(Object|undefined} + */ + decode(mwOlId) { + const id = mwOlId && utils.isPlainObject(mwOlId) ? mwOlId.eid : undefined; + return id ? { 'mwOpenLinkId': id } : undefined; + }, + + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [submoduleParams] + * @returns {id:MwOlId | undefined} + */ + getId(submoduleConfig) { + const submoduleConfigParams = (submoduleConfig && submoduleConfig.params) || {}; + if (!isValidConfig(submoduleConfigParams)) return undefined; + return setID(submoduleConfigParams); + } +}; + +submodule('userId', mwOpenLinkIdSubModule); diff --git a/modules/mwOpenLinkIdSystem.md b/modules/mwOpenLinkIdSystem.md new file mode 100644 index 00000000000..f55913f2983 --- /dev/null +++ b/modules/mwOpenLinkIdSystem.md @@ -0,0 +1,43 @@ +## MediaWallah openLink User ID Submodule + +OpenLink ID User ID Module generates a UUID that can be utilized to improve user matching. This module enables timely synchronization which handles MediaWallah optout. You must have a pre-existing relationship with MediaWallah prior to integrating. + +### Building Prebid with openLink Id Support +Your Prebid build must include the modules for both **userId** and **mwOpenLinkId** submodule. Follow the build instructions for Prebid as +explained in the top level README.md file of the Prebid source tree. + +ex: $ gulp build --modules=userId,mwOpenLinkIdSystem + +##$ MediaWallah openLink ID Example Configuration + +When the module is included, it's automatically enabled and saves an id to both cookie with an expiration time of 1 year. + +### Prebid Params + +Individual params may be set for the MediaWallah openLink User ID Submodule. At least accountId and partnerId must be set in the params. + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'mwOpenLinkId', + params: { + accountId: '1000', + partnerId: '1001', + uid: 'u-123xyz' + } + }] + } +}); +``` + +### Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the MediaWallah OpenLink ID User ID Module integration. + +| Params under usersync.userIds[]| Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | The name of this module. | `'mwOpenLinkId'` | +| params | Required | Object | Details for mwOLID syncing. | | +| params.accountId | Required | String | The MediaWallah assigned Account Id | `1000` | +| params.partnerId | Required | String | The MediaWallah assign Partner Id | `1001` | +| params.uid | Optional | String | Your unique Id for the user or browser. Used for matching | `u-123xyz` | \ No newline at end of file diff --git a/modules/userId/eids.js b/modules/userId/eids.js index a7e5eaf6061..9b26eff2ebf 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -169,11 +169,15 @@ const USER_IDS_CONFIG = { source: 'neustar.biz', atype: 1 }, + // MediaWallah OpenLink + 'mwOpenLinkId': { + source: 'mediawallahscript.com', + atype: 1 + }, 'tapadId': { source: 'tapad.com', atype: 1 }, - // Novatiq Snowflake 'novatiq': { getValue: function(data) { diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 404066d53e4..bd14ea0b9e7 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -142,6 +142,13 @@ userIdAsEids = [ atype: 1 }] }, + { + source: 'mediawallahscript.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, { source: 'tapad.com', uids: [{ diff --git a/modules/userId/userId.md b/modules/userId/userId.md index a7f98fb39a0..828b2993e40 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -77,6 +77,13 @@ pbjs.setConfig({ name: '_criteoId', expires: 1 } + }, { + name: 'mwOpenLinkId', + params: { + accountId: 0000, + partnerId: 0000, + uid: '12345xyz' + } }], syncDelay: 5000, auctionDelay: 1000 diff --git a/test/spec/modules/mwOpenLinkIdSystem_spec.js b/test/spec/modules/mwOpenLinkIdSystem_spec.js new file mode 100644 index 00000000000..fb082b8cd16 --- /dev/null +++ b/test/spec/modules/mwOpenLinkIdSystem_spec.js @@ -0,0 +1,20 @@ +import { writeCookie, mwOpenLinkIdSubModule } from 'modules/mwOpenLinkIdSystem.js'; + +const P_CONFIG_MOCK = { + params: { + accountId: '123', + partnerId: '123' + } +}; + +describe('mwOpenLinkId module', function () { + beforeEach(function() { + writeCookie(''); + }); + + it('getId() should return a MediaWallah openLink Id when the MediaWallah openLink first party cookie exists', function () { + writeCookie({eid: 'XX-YY-ZZ-123'}); + const id = mwOpenLinkIdSubModule.getId(P_CONFIG_MOCK); + expect(id).to.be.deep.equal({id: {eid: 'XX-YY-ZZ-123'}}); + }); +}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 8a7be41b2b5..95279380232 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -38,6 +38,7 @@ import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; import {haloIdSubmodule} from 'modules/haloIdSystem.js'; import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js'; import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; +import {mwOpenLinkIdSubModule} from 'modules/mwOpenLinkIdSystem.js'; import {tapadIdSubmodule} from 'modules/tapadIdSystem.js'; import {getPrebidInternal} from 'src/utils.js'; @@ -458,7 +459,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -466,14 +467,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -484,7 +485,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -501,15 +502,15 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 14 configurations should result in 14 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + it('config with 15 configurations should result in 15 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -549,17 +550,19 @@ describe('User ID', function () { name: 'zeotapIdPlus' }, { name: 'criteo' + }, { + name: 'mwOpenLinkId' }, { name: 'tapadId', storage: {name: 'tapad_id', type: 'cookie'} }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 14 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 15 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -574,7 +577,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -589,7 +592,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -1622,7 +1625,27 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId and Criteo have data to pass', function (done) { + it('test hook from mwOpenLinkId cookies', function (done) { + // simulate existing browser local storage values + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([mwOpenLinkIdSubModule]); + init(config); + config.setConfig(getConfigMock(['mwOpenLinkId', 'mwol', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); + }); + }); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId, Criteo and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1637,8 +1660,9 @@ describe('User ID', function () { }), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1651,6 +1675,7 @@ describe('User ID', function () { ['zeotapIdPlus', 'IDP', 'cookie'], ['haloId', 'haloId', 'cookie'], ['criteo', 'storage_criteo', 'cookie'], + ['mwOpenLinkId', 'mwol', 'cookie'], ['tapadId', 'tapad_id', 'cookie'])); requestBidsHook(function () { @@ -1691,8 +1716,11 @@ describe('User ID', function () { // also check that criteo id was copied to bid expect(bid).to.have.deep.nested.property('userId.criteoId'); expect(bid.userId.criteoId).to.equal('test_bidid'); + // also check that mwOpenLink id was copied to bid + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); - expect(bid.userIdAsEids.length).to.equal(11); + expect(bid.userIdAsEids.length).to.equal(12); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1706,11 +1734,12 @@ describe('User ID', function () { coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, criteo, netId and haloId have their modules added before and after init', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, criteo, netId, haloId and mwOpenLinkId have their modules added before and after init', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1725,6 +1754,7 @@ describe('User ID', function () { coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1744,6 +1774,7 @@ describe('User ID', function () { attachIdSystem(zeotapIdPlusSubmodule); attachIdSystem(haloIdSubmodule); attachIdSystem(criteoIdSubmodule); + attachIdSystem(mwOpenLinkIdSubModule); attachIdSystem(tapadIdSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], @@ -1757,6 +1788,7 @@ describe('User ID', function () { ['zeotapIdPlus', 'IDP', 'cookie'], ['haloId', 'haloId', 'cookie'], ['criteo', 'storage_criteo', 'cookie'], + ['mwOpenLinkId', 'mwol', 'cookie'], ['tapadId', 'tapad_id', 'cookie'])); requestBidsHook(function () { @@ -1800,7 +1832,11 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.criteoId'); expect(bid.userId.criteoId).to.equal('test_bidid'); - expect(bid.userIdAsEids.length).to.equal(11); + // also check that mwOpenLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); + + expect(bid.userIdAsEids.length).to.equal(12); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1814,6 +1850,7 @@ describe('User ID', function () { coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -2129,6 +2166,7 @@ describe('User ID', function () { // also check that haloId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.haloId'); expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userIdAsEids.length).to.equal(10); }); }); @@ -2143,6 +2181,7 @@ describe('User ID', function () { coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); From a1380fe5d93de9d7af4f92d040c54fa17d34719b Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:06:19 +0100 Subject: [PATCH 0701/1476] Documentation: Adjust desired bitrate examples smartx adapter (#6438) --- modules/smartxBidAdapter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/smartxBidAdapter.md b/modules/smartxBidAdapter.md index b25ce68bb6e..223e51763b9 100644 --- a/modules/smartxBidAdapter.md +++ b/modules/smartxBidAdapter.md @@ -40,7 +40,7 @@ This adapter requires setup and approval from the smartclip team. skipOffset: 0, startOpen: true, endingScreen: true, - desiredBitrate: 1600, + desiredBitrate: 800, }, } }], @@ -75,7 +75,7 @@ This adapter requires setup and approval from the smartclip team. skipOffset: 0, startOpen: true, endingScreen: true, - desiredBitrate: 1600, + desiredBitrate: 800, }, user: { data: [{ From 3eb1216281f25485fc22a5dfa4d61c6a4dfe9e21 Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Thu, 18 Mar 2021 19:14:28 +0100 Subject: [PATCH 0702/1476] Remove adId (autogenerated by Prebid) (#6441) --- modules/improvedigitalBidAdapter.js | 3 +-- test/spec/modules/improvedigitalBidAdapter_spec.js | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 0432adf2b2d..dc0911ff5da 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -11,7 +11,7 @@ const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js const VIDEO_TARGETING = ['skip', 'skipmin', 'skipafter']; export const spec = { - version: '7.2.0', + version: '7.3.0', code: BIDDER_CODE, gvlid: 253, aliases: ['id'], @@ -126,7 +126,6 @@ export const spec = { } // Common properties - bid.adId = bidObject.id; bid.cpm = parseFloat(bidObject.price); bid.creativeId = bidObject.crid; bid.currency = bidObject.currency ? bidObject.currency.toUpperCase() : 'USD'; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 4d0da695d95..f34a75ef8f3 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -750,7 +750,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBid = [ { 'ad': '', - 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, 'currency': 'USD', @@ -767,7 +766,6 @@ describe('Improve Digital Adapter Tests', function () { expectedBid[0], { 'ad': '', - 'adId': '1234', 'creativeId': '422033', 'cpm': 1.23, 'currency': 'USD', @@ -783,7 +781,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBidNative = [ { mediaType: 'native', - adId: '33e9500b21129f', creativeId: '422031', cpm: 1.45888594164456, currency: 'USD', @@ -832,7 +829,6 @@ describe('Improve Digital Adapter Tests', function () { const expectedBidInstreamVideo = [ { 'vastXml': '', - 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, 'currency': 'USD', From 24c4fb8b4b502777f8fc3b3b5f96c0e68aecb8dd Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Fri, 19 Mar 2021 02:00:32 +0100 Subject: [PATCH 0703/1476] RichAudience Bid Adapter: add render video in banner (#6392) --- modules/richaudienceBidAdapter.js | 62 +++++++----- modules/richaudienceBidAdapter.md | 2 +- .../modules/richaudienceBidAdapter_spec.js | 99 ++++++++++++++++--- 3 files changed, 121 insertions(+), 42 deletions(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 07de3e40594..37a9554e9a4 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -14,20 +14,20 @@ export const spec = { 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 - */ + * 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 && bid.params.pid && bid.params.supplyType); }, /*** - * Build a server request from the list of valid BidRequests - * @param {validBidRequests} is an array of the valid bids - * @param {bidderRequest} bidder request object - * @returns {ServerRequest} Info describing the request to the server - */ + * Build a server request from the list of valid BidRequests + * @param {validBidRequests} is an array of the valid bids + * @param {bidderRequest} bidder request object + * @returns {ServerRequest} Info describing the request to the server + */ buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bid => { var payload = { @@ -77,11 +77,11 @@ export const spec = { }); }, /*** - * Read the response from the server and build a list of bids - * @param {serverResponse} Response from the server. - * @param {bidRequest} Bid request object - * @returns {bidResponses} Array of bids which were nested inside the server - */ + * Read the response from the server and build a list of bids + * @param {serverResponse} Response from the server. + * @param {bidRequest} Bid request object + * @returns {bidResponses} Array of bids which were nested inside the server + */ interpretResponse: function (serverResponse, bidRequest) { const bidResponses = []; // try catch @@ -103,10 +103,16 @@ export const spec = { if (response.media_type === 'video') { bidResponse.vastXml = response.vastXML; try { - if (JSON.parse(bidRequest.data).videoData.format == 'outstream') { - bidResponse.renderer = Renderer.install({ - url: 'https://cdn3.richaudience.com/prebidVideo/player.js' - }); + if (bidResponse.vastXml != null) { + if (JSON.parse(bidRequest.data).videoData.format == 'outstream' || JSON.parse(bidRequest.data).videoData.format == 'banner') { + bidResponse.renderer = Renderer.install({ + id: bidRequest.bidId, + adunitcode: bidRequest.tagId, + loaded: false, + config: response.media_type, + url: 'https://cdn3.richaudience.com/prebidVideo/player.js' + }); + } bidResponse.renderer.setRender(renderer); } } catch (e) { @@ -121,13 +127,13 @@ export const spec = { return bidResponses }, /*** - * User Syncs - * - * @param {syncOptions} Publisher prebid configuration - * @param {serverResponses} Response from the server - * @param {gdprConsent} GPDR consent object - * @returns {Array} - */ + * User Syncs + * + * @param {syncOptions} Publisher prebid configuration + * @param {serverResponses} Response from the server + * @param {gdprConsent} GPDR consent object + * @returns {Array} + */ getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { const syncs = []; @@ -197,6 +203,10 @@ function raiGetVideoInfo(bid) { playerSize: bid.mediaTypes.video.playerSize, mimes: bid.mediaTypes.video.mimes }; + } else { + videoData = { + format: 'banner' + } } return videoData; } diff --git a/modules/richaudienceBidAdapter.md b/modules/richaudienceBidAdapter.md index fbf59a0208a..f888117b166 100644 --- a/modules/richaudienceBidAdapter.md +++ b/modules/richaudienceBidAdapter.md @@ -39,7 +39,7 @@ Please reach out to your account manager for more information. "pid":"ADb1f40rmo", "supplyType":"site", "bidfloor":0.40, - "keywords": "bici=scott;coche=audi;coche=mercedes;" + "keywords": "key1=value1;key2=value2;key3=value3;" } }] } diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 3b9f07b6efc..5deb2463523 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -20,7 +20,7 @@ describe('Richaudience adapter tests', function () { bidfloor: 0.5, pid: 'ADb1f40rmi', supplyType: 'site', - keywords: 'coche=mercedes;coche=audi' + keywords: 'key1=value1;key2=value2' }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, @@ -75,26 +75,19 @@ describe('Richaudience adapter tests', function () { user: {} }]; - var DEFAULT_PARAMS_VIDEO_OUT_PARAMS = [{ + var DEFAULT_PARAMS_BANNER_OUTSTREAM = [{ adUnitCode: 'test-div', bidId: '2c7c8e9c900244', mediaTypes: { - video: { - context: 'outstream', - playerSize: [640, 480], - mimes: ['video/mp4'] + banner: { + sizes: [[300, 250], [600, 300]] } }, bidder: 'richaudience', params: { bidfloor: 0.5, pid: 'ADb1f40rmi', - supplyType: 'site', - player: { - init: 'close', - end: 'close', - skin: 'dark' - } + supplyType: 'site' }, auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', bidRequestsCount: 1, @@ -242,7 +235,7 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('numIframes').and.to.equal(0); expect(typeof requestContent.scr_rsl === 'string') expect(typeof requestContent.cpuc === 'number') - expect(requestContent).to.have.property('kws').and.to.equal('coche=mercedes;coche=audi'); + expect(requestContent).to.have.property('kws').and.to.equal('key1=value1;key2=value2'); }) it('Verify build request to prebid video inestream', function() { @@ -262,8 +255,6 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('demand').and.to.equal('video'); expect(requestContent.videoData).to.have.property('format').and.to.equal('instream'); - // expect(requestContent.videoData.playerSize[0][0]).to.equal('640'); - // expect(requestContent.videoData.playerSize[0][0]).to.equal('480'); }) it('Verify build request to prebid video outstream', function() { @@ -281,6 +272,7 @@ describe('Richaudience adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('demand').and.to.equal('video'); expect(requestContent.videoData).to.have.property('format').and.to.equal('outstream'); }) @@ -623,9 +615,19 @@ describe('Richaudience adapter tests', function () { }); const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); + expect(bids).to.have.lengthOf(1); const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); expect(bid.mediaType).to.equal('video'); expect(bid.vastXml).to.equal(''); + expect(bid.cpm).to.equal(1.50); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); }); it('no banner media response outstream', function () { @@ -640,6 +642,35 @@ describe('Richaudience adapter tests', function () { } }); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.50); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastXml).to.equal(''); + expect(bid.renderer.url).to.equal('https://cdn3.richaudience.com/prebidVideo/player.js'); + expect(bid.cpm).to.equal(1.50); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + expect(bid.creativeId).to.equal('189198063'); + expect(bid.netRevenue).to.equal(true); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); + }); + + it('banner media and response VAST', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_BANNER_OUTSTREAM, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }, + refererInfo: { + referer: 'https://domain.com', + numIframes: 0 + } + }); + const bids = spec.interpretResponse(BID_RESPONSE_VIDEO, request[0]); const bid = bids[0]; expect(bid.mediaType).to.equal('video'); @@ -656,6 +687,16 @@ describe('Richaudience adapter tests', function () { expect(spec.aliases[0]).to.equal('ra'); }); + it('Verifies bidder gvlid', function () { + expect(spec.gvlid).to.equal(108); + }); + + it('Verifies bidder supportedMediaTypes', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(2); + expect(spec.supportedMediaTypes[0]).to.equal('banner'); + expect(spec.supportedMediaTypes[1]).to.equal('video'); + }); + it('Verifies if bid request is valid', function () { expect(spec.isBidRequestValid(DEFAULT_PARAMS_NEW_SIZES[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true); @@ -717,6 +758,34 @@ describe('Richaudience adapter tests', function () { bidfloor: 0.50, } })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + bidfloor: 0.50, + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + supplyType: 'site', + bidfloor: 0.50, + } + })).to.equal(true); + expect(spec.isBidRequestValid({ + params: { + supplyType: 'site', + bidfloor: 0.50, + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + } + })).to.equal(false); + expect(spec.isBidRequestValid({ + params: { + pid: ['1gCB5ZC4XL', '1a40xk8qSV'], + supplyType: 'site', + bidfloor: 0.50, + ifa: 'AAAAAAAAA-BBBB-CCCC-1111-222222220000', + } + })).to.equal(true); }); it('Verifies user syncs iframe', function () { From cd6f1a87b63ba4d0fd4ae42e6a4d3217a2da9d31 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Fri, 19 Mar 2021 02:58:20 -0700 Subject: [PATCH 0704/1476] Integration Example: ID import library example (#6434) * ID Import Library example * Fixing review comments Co-authored-by: skocheri --- .../gpt/idImportLibrary_example.html | 351 ++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 integrationExamples/gpt/idImportLibrary_example.html diff --git a/integrationExamples/gpt/idImportLibrary_example.html b/integrationExamples/gpt/idImportLibrary_example.html new file mode 100644 index 00000000000..da1581d1c89 --- /dev/null +++ b/integrationExamples/gpt/idImportLibrary_example.html @@ -0,0 +1,351 @@ + + + + + + + + + + + + + +Prebid + +

ID Import Library Example

+

Steps before logging in:

+ +
    +
  • Open console +
      +
    • For Mac, Command+Option+J
    • +
    • Windows/Linux, Control+Shift+J
    • +
    +
  • +
  • Search for 'ID-Library' in console
  • +
+ + + + + + + + + From 92c9f67c0bcad765d3058b00453bea417f8ee119 Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Fri, 19 Mar 2021 13:35:08 +0200 Subject: [PATCH 0705/1476] Mass Module: add module to support MASS protocol (#6332) * Initial implementation for the MASS module * Updated namespace and CDN location * Updated the data object passed to MASS bootloader * Fix linting issues * Added unit tests * Added a README for the MASS module. * Allow MASS be disabled via Prebid configuration * Only check deal ID for matching MASS bids * Updated docs * Update how we test for MASS bids * Thighten the test for MASS bids * Fix linting issues * Change deal ID prefix and add option to specify the bootloader location. Updates to docs. * Updated tests with the new META_MASS deal ID prefix * Update comment in modules/mass.js Co-authored-by: Scott Menzer * Additional information about the module * More specific description of external resource * Identify MASS bids by looking for a 'mass' flag in bid meta or testing deal IDs against a publisher defined pattern * Updated MASS module tests * Bug fixing, added integration example and increased test coverage * Fix integration example and add notice * Updated example page * Updated bootloaderUrl param name to renderUrl and removed its default value. Must be specfied in module config now. * Updated integration example for MASS * Update mass.md Updated disclaimer and synced with docs Co-authored-by: Scott Menzer Co-authored-by: massadmin <58946787+massadmin@users.noreply.github.com> --- integrationExamples/mass/index.html | 110 +++++++++++++++++++++++ modules/mass.js | 129 +++++++++++++++++++++++++++ modules/mass.md | 63 ++++++++++++++ test/spec/modules/mass_spec.js | 130 ++++++++++++++++++++++++++++ 4 files changed, 432 insertions(+) create mode 100644 integrationExamples/mass/index.html create mode 100644 modules/mass.js create mode 100644 modules/mass.md create mode 100644 test/spec/modules/mass_spec.js diff --git a/integrationExamples/mass/index.html b/integrationExamples/mass/index.html new file mode 100644 index 00000000000..80fe4cfb934 --- /dev/null +++ b/integrationExamples/mass/index.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + +
+

Note: for this example to work, you need access to a bid simulation tool from your MASS enabled Exchange partner.

+
+ +
+
+ + diff --git a/modules/mass.js b/modules/mass.js new file mode 100644 index 00000000000..14fe556a466 --- /dev/null +++ b/modules/mass.js @@ -0,0 +1,129 @@ +/** + * This module adds MASS support to Prebid.js. + */ + +import { config } from '../src/config.js'; +import { getHook } from '../src/hook.js'; +import find from 'core-js-pure/features/array/find.js'; + +export let listenerAdded = false; +export let massEnabled = false; + +const defaultCfg = { + dealIdPattern: /^MASS/i +}; +let cfg; + +const massBids = {}; + +init(); +config.getConfig('mass', config => init(config.mass)); + +/** + * Module init. + */ +export function init(customCfg) { + cfg = Object.assign({}, defaultCfg, customCfg); + + if (cfg.enabled === false) { + if (massEnabled) { + massEnabled = false; + getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + } + } else { + if (!massEnabled) { + getHook('addBidResponse').before(addBidResponseHook); + massEnabled = true; + } + } +} + +/** + * Before hook for 'addBidResponse'. + */ +export function addBidResponseHook(next, adUnitCode, bid) { + if (!isMassBid(bid) || !cfg.renderUrl) { + return next(adUnitCode, bid); + } + + const bidRequest = find(this.bidderRequest.bids, bidRequest => + bidRequest.bidId === bid.requestId + ); + + massBids[bid.requestId] = { + bidRequest, + bid, + adm: bid.ad + }; + + bid.ad = ' + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + + diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 7ddad134451..f3514f66b9b 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -14,11 +14,14 @@ const CENT_TO_DOLLAR_FACTOR = 100; const BANNER_TIME_TO_LIVE = 300; const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NET_REVENUE = true; + const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; +const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; + /** * Transform valid bid request config object to banner impression object that will be sent to ad server. * @@ -33,6 +36,8 @@ function bidToBannerImp(bid) { imp.banner.h = bid.params.size[1]; imp.banner.topframe = utils.inIframe() ? 0 : 1; + _applyFloor(bid, imp, BANNER); + return imp; } @@ -46,7 +51,7 @@ function bidToVideoImp(bid) { const imp = bidToImp(bid); const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video'); const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - const videoAdUnitWhitelist = [ + const videoAdUnitAllowlist = [ 'mimes', 'minduration', 'maxduration', 'protocols', 'protocol', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', @@ -68,12 +73,14 @@ function bidToVideoImp(bid) { } } - for (let adUnitProperty in videoAdUnitRef) { - if (videoAdUnitWhitelist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { + for (const adUnitProperty in videoAdUnitRef) { + if (videoAdUnitAllowlist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty]; } } + _applyFloor(bid, imp, VIDEO); + return imp; } @@ -92,12 +99,73 @@ function bidToImp(bid) { imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } - if (bid.params.hasOwnProperty('bidFloor') && bid.params.hasOwnProperty('bidFloorCur')) { - imp.bidfloor = bid.params.bidFloor; - imp.bidfloorcur = bid.params.bidFloorCur; + return imp; +} + +/** + * Gets priceFloors floors and IX adapter floors, + * Validates and sets the higher one on the impression + * @param {object} bid bid object + * @param {object} imp impression object + * @param {string} mediaType the impression ad type, one of the SUPPORTED_AD_TYPES + */ +function _applyFloor(bid, imp, mediaType) { + let adapterFloor = null; + let moduleFloor = null; + + if (bid.params.bidFloor && bid.params.bidFloorCur) { + adapterFloor = { floor: bid.params.bidFloor, currency: bid.params.bidFloorCur }; } - return imp; + if (utils.isFn(bid.getFloor)) { + let _mediaType = '*'; + let _size = '*'; + + if (mediaType && utils.contains(SUPPORTED_AD_TYPES, mediaType)) { + const { w: width, h: height } = imp[mediaType]; + _mediaType = mediaType; + _size = [width, height]; + } + try { + moduleFloor = bid.getFloor({ + mediaType: _mediaType, + size: _size + }); + } catch (err) { + // continue with no module floors + utils.logWarn('priceFloors module call getFloor failed, error : ', err); + } + } + + if (adapterFloor && moduleFloor) { + if (adapterFloor.currency !== moduleFloor.currency) { + utils.logWarn('The bid floor currency mismatch between IX params and priceFloors module config'); + return; + } + + if (adapterFloor.floor > moduleFloor.floor) { + imp.bidfloor = adapterFloor.floor; + imp.bidfloorcur = adapterFloor.currency; + imp.ext.fl = FLOOR_SOURCE.IX; + } else { + imp.bidfloor = moduleFloor.floor; + imp.bidfloorcur = moduleFloor.currency; + imp.ext.fl = FLOOR_SOURCE.PBJS; + } + return; + } + + if (moduleFloor) { + imp.bidfloor = moduleFloor.floor; + imp.bidfloorcur = moduleFloor.currency; + imp.ext.fl = FLOOR_SOURCE.PBJS; + } else if (adapterFloor) { + imp.bidfloor = adapterFloor.floor; + imp.bidfloorcur = adapterFloor.currency; + imp.ext.fl = FLOOR_SOURCE.IX; + } else { + utils.logInfo('IX Bid Adapter: No floors available, no floors applied'); + } } /** @@ -270,7 +338,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { if (identityInfo.hasOwnProperty(partnerName)) { let response = identityInfo[partnerName]; if (!response.responsePending && response.data && typeof response.data === 'object' && - Object.keys(response.data).length && !eidInfo.seenSources[response.data.source]) { + Object.keys(response.data).length && !eidInfo.seenSources[response.data.source]) { userEids.push(response.data); } } @@ -498,7 +566,7 @@ function buildIXDiag(validBidRequests) { iu: 0, nu: 0, ou: 0, - allU: 0, + allu: 0, ren: false, version: '$prebid.version$' }; @@ -534,7 +602,7 @@ function buildIXDiag(validBidRequests) { ixdiag.iu++; } - ixdiag.allU++; + ixdiag.allu++; } } @@ -610,16 +678,19 @@ function updateMissingSizes(validBidRequest, missingBannerSizes, imp) { } /** - * + * @param {object} bid ValidBidRequest object, used to adjust floor * @param {object} imp Impression object to be modified * @param {array} newSize The new size to be applied * @return {object} newImp Updated impression object */ -function createMissingBannerImp(imp, newSize) { +function createMissingBannerImp(bid, imp, newSize) { const newImp = utils.deepClone(imp); newImp.ext.sid = `${newSize[0]}x${newSize[1]}`; newImp.banner.w = newSize[0]; newImp.banner.h = newSize[1]; + + _applyFloor(bid, newImp, BANNER); + return newImp; } @@ -658,7 +729,7 @@ export const spec = { } if (!includesSize(bid.sizes, paramsSize) && !((mediaTypeVideoPlayerSize && includesSize(mediaTypeVideoPlayerSize, paramsSize)) || - (mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) { + (mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) { utils.logError('ix bidder params: bid size is not included in ad unit sizes or player size.'); return false; } @@ -730,13 +801,12 @@ export const spec = { if (!videoImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { videoImps[validBidRequest.transactionId].ixImps = []; } - videoImps[validBidRequest.transactionId].ixImps.push(bidToVideoImp(validBidRequest)); } } if (validBidRequest.mediaType === BANNER || - (utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) || - (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { + (utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) || + (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { let imp = bidToBannerImp(validBidRequest); if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { @@ -767,7 +837,7 @@ export const spec = { let origImp = missingBannerSizes[transactionId].impression; for (let i = 0; i < missingSizes.length; i++) { - let newImp = createMissingBannerImp(origImp, missingSizes[i]); + let newImp = createMissingBannerImp(validBidRequest, origImp, missingSizes[i]); bannerImps[transactionId].missingImps.push(newImp); bannerImps[transactionId].missingCount++; } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 6d7b3a397bd..6b7f35e7fd4 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -26,7 +26,7 @@ describe('IndexexchangeAdapter', function () { } ] }; - var div_many_sizes = [ + const div_many_sizes = [ [300, 250], [600, 410], [336, 280], @@ -94,6 +94,62 @@ describe('IndexexchangeAdapter', function () { [600, 40], [600, 30] ]; + + const ONE_VIDEO = [ + { + bidder: 'ix', + params: { + siteId: '456', + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [2] + }, + size: [400, 100] + }, + sizes: [[400, 100]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [[400, 100]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748562-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', + bidId: '1a2b3c4e', + bidderRequestId: '11a22b33c44e', + auctionId: '1aa2bb3cc4de', + schain: SAMPLE_SCHAIN + } + ]; + + const ONE_BANNER = [ + { + bidder: 'ix', + params: { + siteId: '123', + size: [300, 250] + }, + sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', + bidId: '1a2b3c4d', + bidderRequestId: '11a22b33c44d', + auctionId: '1aa2bb3cc4dd', + schain: SAMPLE_SCHAIN + } + ]; + const DEFAULT_BANNER_VALID_BID = [ { bidder: 'ix', @@ -1057,6 +1113,84 @@ describe('IndexexchangeAdapter', function () { expect(impression.ext.sid).to.equal(sidValue); }); + it('video impression has #priceFloors floors', function () { + const bid = utils.deepClone(ONE_VIDEO[0]); + const flr = 5.5 + const floorInfo = {floor: flr, currency: 'USD'}; + bid.getFloor = function () { + return floorInfo; + } + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(flr); + expect(imp1.bidfloorcur).to.equal('USD'); + expect(imp1.ext.fl).to.equal('p'); + }); + + it('banner imp has floors from #priceFloors module', function () { + const floor300x250 = 3.25 + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]) + + const floorInfo = { floor: floor300x250, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + + expect(imp1.bidfloorcur).to.equal('USD'); + expect(imp1.bidfloor).to.equal(floor300x250); + expect(imp1.ext.fl).to.equal('p'); + }); + + it('ix adapter floors chosen over #priceFloors ', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const floorhi = 4.5 + const floorlow = 3.5 + + bid.params.bidFloor = floorhi + bid.params.bidFloorCur = 'USD' + + const floorInfo = { floor: floorlow, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(floorhi); + expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(imp1.ext.fl).to.equal('x'); + }); + + it(' #priceFloors floors chosen over ix adapter floors', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const floorhi = 4.5 + const floorlow = 3.5 + + bid.params.bidFloor = floorlow + bid.params.bidFloorCur = 'USD' + + const floorInfo = { floor: floorhi, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + // check if floors are in imp + const requestBidFloor = spec.buildRequests([bid])[0]; + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(floorhi); + expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(imp1.ext.fl).to.equal('p'); + }); + it('impression should have bidFloor and bidFloorCur if configured', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; @@ -1066,6 +1200,73 @@ describe('IndexexchangeAdapter', function () { expect(impression.bidfloor).to.equal(bid.params.bidFloor); expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(impression.ext.fl).to.equal('x'); + }); + + it('missing sizes #priceFloors ', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + bid.mediaTypes.banner.sizes.push([500, 400]) + + const floorInfo = { floor: 3.25, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + sinon.spy(bid, 'getFloor'); + + const requestBidFloor = spec.buildRequests([bid])[0]; + // called getFloor with 300 x 250 + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); + + // called getFloor with 500 x 400 + expect(bid.getFloor.getCall(1).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(1).args[0].size[0]).to.equal(500); + expect(bid.getFloor.getCall(1).args[0].size[1]).to.equal(400); + + // both will have same floors due to mock getFloor + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(3.25); + expect(imp1.bidfloorcur).to.equal('USD'); + + const imp2 = JSON.parse(requestBidFloor.data.r).imp[1]; + expect(imp2.bidfloor).to.equal(3.25); + expect(imp2.bidfloorcur).to.equal('USD'); + }); + + it('#pricefloors inAdUnit, banner impressions should have floors', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + + const flr = 4.3 + bid.floors = { + currency: 'USD', + schema: { + delimiter: '|', + fields: ['mediaType', 'size'] + }, + values: { + 'banner|300x250': flr, + 'banner|600x500': 6.5, + 'banner|*': 7.5 + } + }; + const floorInfo = { floor: flr, currency: 'USD' }; + bid.getFloor = function () { + return floorInfo; + }; + + sinon.spy(bid, 'getFloor'); + + const requestBidFloor = spec.buildRequests([bid])[0]; + // called getFloor with 300 x 250 + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); + + const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(imp1.bidfloor).to.equal(flr); + expect(imp1.bidfloorcur).to.equal('USD'); }); it('payload without mediaType should have correct format and value', function () { @@ -1145,7 +1346,6 @@ describe('IndexexchangeAdapter', function () { const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; - expect(pageUrl).to.equal(expectedPageUrl); }); @@ -1492,7 +1692,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.mimes[0]).to.not.equal('video/override'); }); - it('should not add video adunit level properties in imp object if they are not whitelisted', function () { + it('should not add video adunit level properties in imp object if they are not allowlisted', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; bid.mediaTypes.video.random = true; @@ -1502,7 +1702,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.random).to.not.exist; }); - it('should add whitelisted adunit level video properties in imp object if they are not configured at params level', function () { + it('should add allowlisted adunit level video properties in imp object if they are not configured at params level', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; delete bid.params.video.protocols; @@ -1571,7 +1771,7 @@ describe('IndexexchangeAdapter', function () { expect(diagObj.ou).to.equal(1); expect(diagObj.ren).to.equal(false); expect(diagObj.mfu).to.equal(1); - expect(diagObj.allU).to.equal(1); + expect(diagObj.allu).to.equal(1); expect(diagObj.version).to.equal('$prebid.version$'); }); }); From f3fe966c84bd52764dae0e01c3d62c6aa00a77de Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 24 Mar 2021 11:21:06 -0400 Subject: [PATCH 0717/1476] PBS adapter: remove condition on gdpr consentstring which isnt required by the tcf2 spec when gdprapplies is false (#6429) * Update index.js * Update prebidServerBidAdapter_spec.js * Update prebidServerBidAdapter_spec.js * Update prebidServerBidAdapter_spec.js --- modules/prebidServerBidAdapter/index.js | 7 ++----- test/spec/modules/prebidServerBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index d3bbb347720..453f9118766 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -188,17 +188,14 @@ function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { } if (gdprConsent) { - // only populate gdpr field if we know CMP returned consent information (ie didn't timeout or have an error) - if (typeof gdprConsent.consentString !== 'undefined') { - payload.gdpr = (gdprConsent.gdprApplies) ? 1 : 0; - } + payload.gdpr = (gdprConsent.gdprApplies) ? 1 : 0; // attempt to populate gdpr_consent if we know gdprApplies or it may apply if (gdprConsent.gdprApplies !== false) { payload.gdpr_consent = gdprConsent.consentString; } } - // US Privace (CCPA) support + // US Privacy (CCPA) support if (uspConsent) { payload.us_privacy = uspConsent; } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 45d96b96d66..809e3933eb9 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -639,7 +639,7 @@ describe('S2S Adapter', function () { expect(requestBid.gdpr_consent).is.undefined; }); - it('checks gdpr info gets added to cookie_sync request: consent data unknown', function () { + it('checks gdpr info gets added to cookie_sync request: applies is false', function () { let cookieSyncConfig = utils.deepClone(CONFIG); cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; @@ -649,7 +649,7 @@ describe('S2S Adapter', function () { let gdprBidRequest = utils.deepClone(BID_REQUESTS); gdprBidRequest[0].gdprConsent = { consentString: undefined, - gdprApplies: undefined + gdprApplies: false }; const s2sBidRequest = utils.deepClone(REQUEST); @@ -658,7 +658,7 @@ describe('S2S Adapter', function () { adapter.callBids(s2sBidRequest, gdprBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); - expect(requestBid.gdpr).is.undefined; + expect(requestBid.gdpr).is.equal(0); expect(requestBid.gdpr_consent).is.undefined; }); }); From 9faeb687b1dd1088f95defb98e58df100d064f7b Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 24 Mar 2021 16:27:17 +0100 Subject: [PATCH 0718/1476] if the stored value was an object, then it could end up with a space before the {, causing the stored value not to be json parsed simply because of a space in the first character (#6467) --- 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 3253be42a76..be9883dae9c 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -247,7 +247,7 @@ function getStoredValue(storage, key = undefined) { } } // support storing a string or a stringified object - if (typeof storedValue === 'string' && storedValue.charAt(0) === '{') { + if (typeof storedValue === 'string' && storedValue.trim().charAt(0) === '{') { storedValue = JSON.parse(storedValue); } } catch (e) { From fa5c978fbe496717cbedd9582bfe6671e1f39f9d Mon Sep 17 00:00:00 2001 From: Bernhard Valenti Date: Wed, 24 Mar 2021 12:29:55 -0400 Subject: [PATCH 0719/1476] Kargo Bid Adapter: add gdpr support (#6447) --- modules/kargoBidAdapter.js | 38 ++++++++++--- test/spec/modules/kargoBidAdapter_spec.js | 65 +++++++++++++++++++---- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 03767efc135..610f4558139 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -3,17 +3,19 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); const BIDDER_CODE = 'kargo'; const HOST = 'https://krk.kargo.com'; -const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}'; +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 storage = getStorageManager(GVLID, BIDDER_CODE); let sessionId, lastPageUrl, requestCounter; export const spec = { + gvlid: GVLID, code: BIDDER_CODE, isBidRequestValid: function(bid) { if (!bid || !bid.params) { @@ -48,7 +50,7 @@ export const spec = { bidIDs: bidIds, bidSizes: bidSizes, prebidRawBidRequests: validBidRequests - }, spec._getAllMetadata(tdid, bidderRequest.uspConsent)); + }, spec._getAllMetadata(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent)); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { method: 'GET', @@ -85,15 +87,25 @@ export const spec = { } return bidResponses; }, - getUserSyncs: function(syncOptions) { + getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy) { const syncs = []; const seed = spec._generateRandomUuid(); const clientId = spec._getClientId(); + var gdpr = (gdprConsent && gdprConsent.gdprApplies) ? 1 : 0; + var gdprConsentString = (gdprConsent && gdprConsent.consentString) ? gdprConsent.consentString : ''; + // don't sync if opted out via usPrivacy + if (typeof usPrivacy == 'string' && usPrivacy.length == 4 && usPrivacy[0] == 1 && usPrivacy[2] == 'Y') { + return syncs; + } if (syncOptions.iframeEnabled && seed && clientId) { for (let i = 0; i < SYNC_COUNT; i++) { syncs.push({ type: 'iframe', - url: SYNC.replace('{UUID}', clientId).replace('{SEED}', seed).replace('{INDEX}', i) + url: SYNC.replace('{UUID}', clientId).replace('{SEED}', seed) + .replace('{INDEX}', i) + .replace('{GDPR}', gdpr) + .replace('{GDPR_CONSENT}', gdprConsentString) + .replace('{US_PRIVACY}', usPrivacy || '') }); } } @@ -183,7 +195,7 @@ export const spec = { } }, - _getUserIds(tdid, usp) { + _getUserIds(tdid, usp, gdpr) { const crb = spec._getCrb(); const userIds = { kargoID: crb.userId, @@ -192,6 +204,16 @@ export const spec = { optOut: crb.optOut, usp: usp }; + + try { + if (gdpr) { + userIds['gdpr'] = { + consent: gdpr.consentString || '', + applies: !!gdpr.gdprApplies, + } + } + } catch (e) { + } if (tdid) { userIds.tdID = tdid; } @@ -203,9 +225,9 @@ export const spec = { return crb.clientId; }, - _getAllMetadata(tdid, usp) { + _getAllMetadata(tdid, usp, gdpr) { return { - userIDs: spec._getUserIds(tdid, usp), + userIDs: spec._getUserIds(tdid, usp, gdpr), krux: spec._getKrux(), pageURL: window.location.href, rawCRB: spec._readCookie('krg_crb'), diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 9dbcca8e331..43968bbef5a 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -134,6 +134,21 @@ describe('kargo adapter tests', function () { noAdServerCurrency = true; } + function generateGDPR(applies, haveConsent) { + var data = { + consentString: 'gdprconsentstring', + gdprApplies: applies, + }; + return data; + } + + function generateGDPRExpect(applies, haveConsent) { + return { + consent: 'gdprconsentstring', + applies: applies, + }; + } + function initializeKruxUser() { setLocalStorageItem('kxkar_user', 'rsgr9pnij'); } @@ -221,7 +236,7 @@ describe('kargo adapter tests', function () { return spec._getSessionId(); } - function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie) { + function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie, expectedGDPR) { var base = { timeout: 200, requestCount: requestCount++, @@ -299,6 +314,10 @@ describe('kargo adapter tests', function () { rawCRBLocalStorage: expectedRawCRB }; + if (expectedGDPR) { + base.userIDs['gdpr'] = expectedGDPR; + } + if (excludeUserIds === true) { base.userIDs = { crbIDs: {}, @@ -317,12 +336,16 @@ describe('kargo adapter tests', function () { return base; } - function testBuildRequests(excludeTdid, expected) { + function testBuildRequests(excludeTdid, expected, gdpr) { var clonedBids = JSON.parse(JSON.stringify(bids)); if (excludeTdid) { delete clonedBids[0].userId.tdid; } - var request = spec.buildRequests(clonedBids, {timeout: 200, uspConsent: '1---', foo: 'bar'}); + var payload = { timeout: 200, uspConsent: '1---', foo: 'bar' }; + if (gdpr) { + payload['gdprConsent'] = gdpr + } + var request = spec.buildRequests(clonedBids, payload); expected.sessionId = getSessionId(); sessionIds.push(expected.sessionId); var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); @@ -431,6 +454,15 @@ describe('kargo adapter tests', function () { initializeKrgCrb(); testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle())); }); + + it('sends gdpr consent', function () { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgCrb(); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(true, true)), generateGDPR(true, true)); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(false, true)), generateGDPR(false, true)); + testBuildRequests(false, getExpectedKrakenParams(undefined, undefined, getKrgCrb(), getKrgCrbOldStyle(), generateGDPRExpect(false, false)), generateGDPR(false, false)); + }); }); describe('response handler', function() { @@ -558,8 +590,8 @@ describe('kargo adapter tests', function () { }); }); - function getUserSyncsWhenAllowed() { - return spec.getUserSyncs({iframeEnabled: true}); + function getUserSyncsWhenAllowed(gdprConsent, usPrivacy) { + return spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent, usPrivacy); } function getUserSyncsWhenForbidden() { @@ -574,17 +606,17 @@ describe('kargo adapter tests', function () { shouldSimulateOutdatedBrowser = true; } - function getSyncUrl(index) { + function getSyncUrl(index, gdprApplies, gdprConsentString, usPrivacy) { return { type: 'iframe', - url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}` + url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}&gdpr=${gdprApplies}&gdpr_consent=${gdprConsentString}&us_privacy=${usPrivacy}` }; } - function getSyncUrls() { + function getSyncUrls(gdprApplies, gdprConsentString, usPrivacy) { var syncs = []; for (var i = 0; i < 5; i++) { - syncs[i] = getSyncUrl(i); + syncs[i] = getSyncUrl(i, gdprApplies || 0, gdprConsentString || '', usPrivacy || ''); } return syncs; } @@ -606,6 +638,21 @@ describe('kargo adapter tests', function () { safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); }); + it('no user syncs when there is no us privacy consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YYY')).to.be.an('array').that.is.empty); + }); + + it('pass through us privacy consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YNY')).to.deep.equal(getSyncUrls(0, '', '1YNY'))); + }); + + it('pass through gdpr consent', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed({ gdprApplies: true, consentString: 'consentstring' })).to.deep.equal(getSyncUrls(1, 'consentstring', ''))); + }); + it('no user syncs when there is outdated browser', function() { turnOnClientId(); simulateOutdatedBrowser(); From 1dc7c05497514410f7abed5636f0d3bf43cffa5c Mon Sep 17 00:00:00 2001 From: Daniel Liebner Date: Wed, 24 Mar 2021 12:35:53 -0400 Subject: [PATCH 0720/1476] Bid Glass Adapter: Merge externally set targeting params (#6426) * Added bidglass adapter + test * PR Review Updates: - Added formal params to getUserSyncs function definition - getUserSyncs now always returns an array - Improved unit test coverage * PR Review Updates: - Removed unused methods: getUserSyncs, onTimeout, onBidWon, onSetTargeting - Removed getUserSyncs unit test - Removed "dead code" - Removed some unnecessary comments - Fixed usage of parseInt * Bid Glass Bid Adapter: pass options in bid request * Merge externally set targeting params * Updates to address gulp errors * Get `bidglass` reference from window --- modules/bidglassBidAdapter.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 3162228ce58..44f5cdf4384 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -67,18 +67,29 @@ export const spec = { return ori; }; + let bidglass = window['bidglass']; + utils._each(validBidRequests, function(bid) { bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); bid.sizes = bid.sizes.filter(size => utils.isArray(size)); + var adUnitId = utils.getBidIdParameter('adUnitId', bid.params); var options = utils.deepClone(bid.params); + delete options.adUnitId; + // Merge externally set targeting params + if (typeof bidglass === 'object' && bidglass.getTargeting) { + let targeting = bidglass.getTargeting(adUnitId, options.targeting); + + if (targeting && Object.keys(targeting).length > 0) options.targeting = targeting; + } + // Stuff to send: [bid id, sizes, adUnitId, options] imps.push({ bidId: bid.bidId, sizes: bid.sizes, - adUnitId: utils.getBidIdParameter('adUnitId', bid.params), + adUnitId: adUnitId, options: options }); }); From f03e95a736a7289d8d11d79d9d9c91212136ec49 Mon Sep 17 00:00:00 2001 From: Amit Aisikowitz <7425067+pixelgroup-israel@users.noreply.github.com> Date: Wed, 24 Mar 2021 19:21:23 +0200 Subject: [PATCH 0721/1476] Optimon Analytics Adapter: add new analytics adapter (#6333) * Publish the Optimon platform's analytics adapter for prebid Added js and md files for the analytics adapter. * Fix wrong content in MD file * Fix wrong content in MD file * Created unit testing to Optimon Analytics Adapter * Created unit testing to Optimon Analytics Adapter * Created unit testing to Optimon Analytics Adapter * Created unit testing to Optimon Analytics Adapter * Fixes ESlint styling * Removing DEF const * Created unit testing to Optimon Analytics Adapter * Created unit testing to Optimon Analytics Adapter --- modules/optimonAnalyticsAdapter.js | 25 ++++++++++++ modules/optimonAnalyticsAdapter.md | 13 ++++++ .../modules/optimonAnalyticsAdapter_spec.js | 40 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 modules/optimonAnalyticsAdapter.js create mode 100644 modules/optimonAnalyticsAdapter.md create mode 100644 test/spec/modules/optimonAnalyticsAdapter_spec.js diff --git a/modules/optimonAnalyticsAdapter.js b/modules/optimonAnalyticsAdapter.js new file mode 100644 index 00000000000..34b2778afc9 --- /dev/null +++ b/modules/optimonAnalyticsAdapter.js @@ -0,0 +1,25 @@ +/** +* +********************************************************* +* +* Optimon.io Prebid Analytics Adapter +* +********************************************************* +* +*/ + +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; + +const optimonAnalyticsAdapter = adapter({ + global: 'OptimonAnalyticsAdapter', + handler: 'on', + analyticsType: 'bundle' +}); + +adapterManager.registerAnalyticsAdapter({ + adapter: optimonAnalyticsAdapter, + code: 'optimon', +}); + +export default optimonAnalyticsAdapter; diff --git a/modules/optimonAnalyticsAdapter.md b/modules/optimonAnalyticsAdapter.md new file mode 100644 index 00000000000..4e2c00dfcab --- /dev/null +++ b/modules/optimonAnalyticsAdapter.md @@ -0,0 +1,13 @@ +# Overview + +Module Name: Optimon.io Prebid Analytics Adapter +Module Type: Analytics Adapter +Maintainer: hello@optimon.io + +# Description + +Start analyzing your Prebid performance by visiting our website [Optimon.io](https://optimon.io/?utm_source=prebid-org&utm_medium=analytics-adapter) or contact us directly by email: [hello@optimon.io](mailto:hello@optimon.io) to get started. + +# Platform Details + +[Optimon.io](https://optimon.io/?utm_source=prebid-org&utm_medium=analytics-adapter) is a Robust Alerting & Reporting Platform for Prebid and GAM that helps publishers make the right decisions by collecting data from your Google Ad Manager, Prebid, and other SSPs and providing smart insights and suggestions to optimize and maximize their overall yield and save manual work. diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..b5b76ce3fde --- /dev/null +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -0,0 +1,40 @@ +import * as utils from 'src/utils.js'; +import { expect } from 'chai'; +import optimonAnalyticsAdapter from '../../../modules/optimonAnalyticsAdapter.js'; +import adapterManager from 'src/adapterManager'; +import events from 'src/events'; +import constants from 'src/constants.json' + +const AD_UNIT_CODE = 'demo-adunit-1'; +const PUBLISHER_CONFIG = { + pubId: 'optimon_test', + pubAdxAccount: 123456789, + pubTimezone: 'Asia/Jerusalem' +}; + +describe('Optimon Analytics Adapter', () => { + const optmn_currentWindow = utils.getWindowSelf(); + let optmn_queue = []; + + beforeEach(() => { + optmn_currentWindow.OptimonAnalyticsAdapter = (...optmn_args) => optmn_queue.push(optmn_args); + adapterManager.enableAnalytics({ + provider: 'optimon' + }); + optmn_queue = [] + }); + + afterEach(() => { + optimonAnalyticsAdapter.disableAnalytics(); + }); + + 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) + + expect(optmn_queue.length).to.eql(3); + }); +}); From 95c56254811d54edb4e52a0d130b6ae4f1190534 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 24 Mar 2021 16:38:29 -0400 Subject: [PATCH 0722/1476] 4.32.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed13b6ae7da..82aa5bf6e0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.32.0-pre", + "version": "4.32.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 48cd16a9bed3a3872a7f481255d2083e4bc696b9 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 24 Mar 2021 17:30:05 -0400 Subject: [PATCH 0723/1476] 4.33.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82aa5bf6e0b..aca216767cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.32.0", + "version": "4.33.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 94faa28f2160d2fa8e8c1f3771e858c3d5c54240 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Thu, 25 Mar 2021 01:11:21 -0700 Subject: [PATCH 0724/1476] UID 2.0 Userid submodule (#6443) * UID 2.0 User id submodule * UID 2.0 User id submodule * UID 2.0 User id submodule * UID 2.0 User id submodule * Resolving merge conflicts and review comments * Updating documentation * Renaming module * Fixing review comments * Fixing review comments * Fixing review comments * Fixing review comments * Fixing review comments * Updating source uid2.com to uidapi.com Co-authored-by: skocheri --- integrationExamples/gpt/userId_example.html | 3 + modules/uid2IdSystem.js | 97 +++++++++++++++++++++ modules/uid2IdSystem.md | 24 +++++ modules/userId/eids.js | 7 ++ modules/userId/eids.md | 9 +- modules/userId/userId.md | 5 +- test/spec/modules/eids_spec.js | 14 +++ test/spec/modules/userId_spec.js | 93 +++++++++++++++----- 8 files changed, 228 insertions(+), 24 deletions(-) create mode 100644 modules/uid2IdSystem.js create mode 100644 modules/uid2IdSystem.md diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 71299a4a6e1..fae5ca2b539 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -277,6 +277,9 @@ { name: "criteo" }, + { + name: "uid2" + } ], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js new file mode 100644 index 00000000000..053b57cb76d --- /dev/null +++ b/modules/uid2IdSystem.js @@ -0,0 +1,97 @@ +/** + * This module adds uid2 ID support to the User ID module + * The {@link module:modules/userId} module is required. + * @module modules/uid2IdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {submodule} from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const MODULE_NAME = 'uid2'; +const GVLID = 887; +const LOG_PRE_FIX = 'UID2: '; +const ADVERTISING_COOKIE = '__uid2_advertising_token'; + +function readCookie() { + return storage.cookiesAreEnabled() ? storage.getCookie(ADVERTISING_COOKIE) : null; +} + +function readFromLocalStorage() { + return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(ADVERTISING_COOKIE) : null; +} + +function getStorage() { + return getStorageManager(GVLID, MODULE_NAME); +} + +const storage = getStorage(); + +const logInfo = createLogInfo(LOG_PRE_FIX); + +function createLogInfo(prefix) { + return function (...strings) { + utils.logInfo(prefix + ' ', ...strings); + } +} + +/** + * Encode the id + * @param value + * @returns {string|*} + */ +function encodeId(value) { + const result = {}; + if (value) { + const bidIds = { + id: value + } + result.uid2 = bidIds; + logInfo('Decoded value ' + JSON.stringify(result)); + return result; + } + return undefined; +} + +/** @type {Submodule} */ +export const uid2IdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * Vendor id of Prebid + * @type {Number} + */ + gvlid: GVLID, + /** + * 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 + */ + decode(value) { + return (value) ? encodeId(value) : undefined; + }, + + /** + * performs action to obtain id and return a value. + * @function + * @param {SubmoduleConfig} [config] + * @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} + }, + +}; + +// Register submodule for userId +submodule('userId', uid2IdSubmodule); diff --git a/modules/uid2IdSystem.md b/modules/uid2IdSystem.md new file mode 100644 index 00000000000..59149e562ac --- /dev/null +++ b/modules/uid2IdSystem.md @@ -0,0 +1,24 @@ +## UID 2.0 User ID Submodule + +UID 2.0 ID Module. + +### Prebid Params + +Individual params may be set for the UID 2.0 Submodule. At least one identifier must be set in the params. + +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'uid2' + }] + } +}); +``` +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the UID 2.0 User ID Module integration. + +| 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.O 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"}}` | diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 9b26eff2ebf..31879286675 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -185,6 +185,13 @@ const USER_IDS_CONFIG = { }, source: 'novatiq.com', atype: 1 + }, + 'uid2': { + source: 'uidapi.com', + atype: 3, + getValue: function(data) { + return data.id; + } } }; diff --git a/modules/userId/eids.md b/modules/userId/eids.md index bd14ea0b9e7..53d9196e255 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -162,6 +162,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 1 }] - } + }, + { + source: 'uidapi.com', + uids: [{ + id: 'some-random-id-value', + atype: 3 + }] + } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 828b2993e40..4038e9b00e4 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -84,6 +84,9 @@ pbjs.setConfig({ partnerId: 0000, uid: '12345xyz' } + }, { + name: 'uid2' + } }], syncDelay: 5000, auctionDelay: 1000 @@ -161,7 +164,7 @@ pbjs.setConfig({ type: 'html5', name: '_criteoId', expires: 1 - } + } }], syncDelay: 5000 } diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 659e5257e90..bd839124b6d 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -276,6 +276,20 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('uid2', function() { + const userId = { + uid2: {'id': 'Sample_AD_Token'} + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: 'Sample_AD_Token', + atype: 3 + }] + }); + }); it('pubProvidedId', function() { const userId = { pubProvidedId: [{ diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 95279380232..cfc3529496a 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -41,6 +41,7 @@ import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; import {mwOpenLinkIdSubModule} from 'modules/mwOpenLinkIdSystem.js'; import {tapadIdSubmodule} from 'modules/tapadIdSystem.js'; import {getPrebidInternal} from 'src/utils.js'; +import {uid2IdSubmodule} from 'modules/uid2IdSystem.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -459,7 +460,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -467,14 +468,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -485,7 +486,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -502,15 +503,15 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 15 configurations should result in 15 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + it('config with 15 configurations should result in 16 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -555,14 +556,16 @@ describe('User ID', function () { }, { name: 'tapadId', storage: {name: 'tapad_id', type: 'cookie'} + }, { + name: 'uid2' }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 15 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 16 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -577,7 +580,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -592,7 +595,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -1645,7 +1648,7 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId, Criteo and mwOpenLinkId have data to pass', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId, Criteo, UID 2.0 and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1661,8 +1664,9 @@ describe('User ID', function () { coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1676,7 +1680,8 @@ describe('User ID', function () { ['haloId', 'haloId', 'cookie'], ['criteo', 'storage_criteo', 'cookie'], ['mwOpenLinkId', 'mwol', 'cookie'], - ['tapadId', 'tapad_id', 'cookie'])); + ['tapadId', 'tapad_id', 'cookie'], + ['uid2', 'uid2id', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1719,8 +1724,10 @@ describe('User ID', function () { // also check that mwOpenLink id was copied to bid expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); - - expect(bid.userIdAsEids.length).to.equal(12); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); + expect(bid.userIdAsEids.length).to.equal(13); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1735,11 +1742,12 @@ describe('User ID', function () { coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, criteo, netId, haloId and mwOpenLinkId have their modules added before and after init', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, intentIqId, zeotapIdPlus, sharedId, criteo, netId, haloId, UID 2.0 and mwOpenLinkId have their modules added before and after init', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1755,6 +1763,7 @@ describe('User ID', function () { coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1776,6 +1785,7 @@ describe('User ID', function () { attachIdSystem(criteoIdSubmodule); attachIdSystem(mwOpenLinkIdSubModule); attachIdSystem(tapadIdSubmodule); + attachIdSystem(uid2IdSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1789,7 +1799,8 @@ describe('User ID', function () { ['haloId', 'haloId', 'cookie'], ['criteo', 'storage_criteo', 'cookie'], ['mwOpenLinkId', 'mwol', 'cookie'], - ['tapadId', 'tapad_id', 'cookie'])); + ['tapadId', 'tapad_id', 'cookie'], + ['uid2', 'uid2id', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1834,9 +1845,12 @@ describe('User ID', function () { // also check that mwOpenLink id data was copied to bid expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); - expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123') + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); ; - expect(bid.userIdAsEids.length).to.equal(12); + expect(bid.userIdAsEids.length).to.equal(13); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1851,6 +1865,7 @@ describe('User ID', function () { coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -2065,6 +2080,34 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from UID2 cookie', function (done) { + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([uid2IdSubmodule]); + init(config); + config.setConfig(getConfigMock(['uid2', 'uid2id', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.uid2'); + expect(bid.userId.uid2).to.have.deep.nested.property('id'); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: 'Sample_AD_Token', + atype: 3, + }] + }); + }); + }); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); it('should add new id system ', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); @@ -2080,8 +2123,9 @@ describe('User ID', function () { coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ @@ -2109,6 +2153,8 @@ describe('User ID', function () { name: 'haloId', storage: {name: 'haloId', type: 'cookie'} }, { name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }, { + name: 'uid2' }] } }); @@ -2166,8 +2212,11 @@ describe('User ID', function () { // also check that haloId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.haloId'); expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); - expect(bid.userIdAsEids.length).to.equal(10); + expect(bid.userIdAsEids.length).to.equal(11); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); From 78b3a8b099c4d05459d777d9b6cf6a37254ccdf5 Mon Sep 17 00:00:00 2001 From: h12media <65672347+h12media@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:00:58 +0300 Subject: [PATCH 0725/1476] H12media Bid Adapter: added new optional params, fixes, and various integration support (#6436) * Change module H12 Media * Change module H12 Media * Change module H12 Media * Change module H12 Media * Update module H12 Media * Update module H12 Media * Update module H12 Media * Update module H12 Media * Update module H12 Media * Update module H12 Media --- modules/h12mediaBidAdapter.js | 208 +++++++++++-------- test/spec/modules/h12mediaBidAdapter_spec.js | 135 ++++++++---- 2 files changed, 218 insertions(+), 125 deletions(-) diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index 0d2c22a3f68..7b736780226 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -1,6 +1,5 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'h12media'; const DEFAULT_URL = 'https://bidder.h12-media.com/prebid/'; const DEFAULT_CURRENCY = 'USD'; @@ -16,21 +15,31 @@ export const spec = { }, buildRequests: function(validBidRequests, bidderRequest) { - const requestUrl = validBidRequests[0].params.endpointdom || DEFAULT_URL; - const isiframe = !((window.self === window.top) || window.frameElement); + const isiframe = utils.inIframe(); const screenSize = getClientDimensions(); const docSize = getDocumentDimensions(); - const bidrequests = validBidRequests.map((bidRequest) => { + return validBidRequests.map((bidRequest) => { const bidderParams = bidRequest.params; - const adUnitElement = document.getElementById(bidRequest.adUnitCode); + const requestUrl = bidderParams.endpointdom || DEFAULT_URL; + let pubsubid = bidderParams.pubsubid || ''; + if (pubsubid && pubsubid.length > 32) { + utils.logError('Bidder param \'pubsubid\' should be not more than 32 chars.'); + pubsubid = ''; + } + const pubcontainerid = bidderParams.pubcontainerid; + const adUnitElement = document.getElementById(pubcontainerid || bidRequest.adUnitCode); const ishidden = !isVisible(adUnitElement); - const coords = { + const framePos = getFramePos(); + const coords = isiframe ? { + x: framePos[0], + y: framePos[1], + } : { x: adUnitElement && adUnitElement.getBoundingClientRect().x, y: adUnitElement && adUnitElement.getBoundingClientRect().y, }; - return { + const bidrequest = { bidId: bidRequest.bidId, transactionId: bidRequest.transactionId, adunitId: bidRequest.adUnitCode, @@ -40,33 +49,46 @@ export const spec = { adunitSize: bidRequest.mediaTypes.banner.sizes || [], coords, ishidden, + pubsubid, + pubcontainerid, }; - }); - return { - method: 'POST', - url: requestUrl, - options: {withCredentials: false}, - data: { - gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? Boolean(bidderRequest.gdprConsent.gdprApplies & 1) : false, - gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? bidderRequest.gdprConsent.consentString : '', - topLevelUrl: window.top.location.href, - refererUrl: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer : '', - isiframe, - version: '$prebid.version$', - visitorInfo: { - localTime: getLocalDateFormatted(), - dayOfWeek: new Date().getDay(), - screenWidth: screenSize[0], - screenHeight: screenSize[1], - docWidth: docSize[0], - docHeight: docSize[1], - scrollbarx: window.scrollX, - scrollbary: window.scrollY, + let windowTop; + try { + windowTop = window.top; + } catch (e) { + utils.logMessage(e); + windowTop = window; + } + + return { + method: 'POST', + url: requestUrl, + options: {withCredentials: true}, + data: { + gdpr: !!utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false), + gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.consentString', ''), + usp: !!utils.deepAccess(bidderRequest, 'uspConsent', false), + usp_cs: utils.deepAccess(bidderRequest, 'uspConsent', ''), + topLevelUrl: utils.deepAccess(bidderRequest, 'refererInfo.referer', ''), + refererUrl: windowTop.document.referrer, + isiframe, + version: '$prebid.version$', + ExtUserIDs: bidRequest.userId, + visitorInfo: { + localTime: getLocalDateFormatted(), + dayOfWeek: new Date().getDay(), + screenWidth: screenSize[0], + screenHeight: screenSize[1], + docWidth: docSize[0], + docHeight: docSize[1], + scrollbarx: windowTop.scrollX, + scrollbary: windowTop.scrollY, + }, + bidrequest, }, - bidrequests, - }, - }; + }; + }); }, interpretResponse: function(serverResponse, bidRequests) { @@ -74,29 +96,28 @@ export const spec = { try { const serverBody = serverResponse.body; if (serverBody) { - if (serverBody.bids) { - serverBody.bids.forEach(bidBody => { - const bidRequest = find(bidRequests.data.bidrequests, bid => bid.bidId === bidBody.bidId); - const bidResponse = { - currency: serverBody.currency || DEFAULT_CURRENCY, - netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE, - ttl: serverBody.ttl || DEFAULT_TTL, - requestId: bidBody.bidId, - cpm: bidBody.cpm, - width: bidBody.width, - height: bidBody.height, - creativeId: bidBody.creativeId, - ad: bidBody.ad, - meta: bidBody.meta, - mediaType: 'banner', - }; - if (bidRequest) { - bidResponse.pubid = bidRequest.pubid; - bidResponse.placementid = bidRequest.placementid; - bidResponse.size = bidRequest.size; - } - bidResponses.push(bidResponse); - }); + if (serverBody.bid) { + const bidBody = serverBody.bid; + const bidRequest = bidRequests.data.bidrequest; + const bidResponse = { + currency: serverBody.currency || DEFAULT_CURRENCY, + netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE, + ttl: serverBody.ttl || DEFAULT_TTL, + requestId: bidBody.bidId, + cpm: bidBody.cpm, + width: bidBody.width, + height: bidBody.height, + creativeId: bidBody.creativeId, + ad: bidBody.ad, + meta: bidBody.meta, + mediaType: 'banner', + }; + if (bidRequest) { + bidResponse.pubid = bidRequest.pubid; + bidResponse.placementid = bidRequest.placementid; + bidResponse.size = bidRequest.size; + } + bidResponses.push(bidResponse); } } return bidResponses; @@ -105,47 +126,50 @@ export const spec = { } }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { - const serverBody = serverResponses[0].body; + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) { const syncs = []; + const uspApplies = !!utils.deepAccess(usPrivacy, 'uspConsent', false); + const uspString = utils.deepAccess(usPrivacy, 'uspConsent', ''); gdprConsent = gdprConsent || { gdprApplies: false, consentString: '', }; - if (serverBody) { - if (serverBody.bids) { - serverBody.bids.forEach(bidBody => { - const userSyncUrls = bidBody.usersync || []; - const userSyncUrlProcess = url => { - return url - .replace('{gdpr}', gdprConsent.gdprApplies) - .replace('{gdpr_cs}', gdprConsent.consentString); - } + const userSyncUrlProcess = url => { + return url + .replace('{gdpr}', gdprConsent.gdprApplies) + .replace('{gdpr_cs}', gdprConsent.consentString) + .replace('{usp}', uspApplies) + .replace('{usp_cs}', uspString); + } - userSyncUrls.forEach(sync => { - if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) { - syncs.push({ - type: 'iframe', - url: userSyncUrlProcess(sync.url), - }); - } - if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) { - syncs.push({ - type: 'image', - url: userSyncUrlProcess(sync.url), - }); - } + serverResponses.forEach(serverResponse => { + const userSyncUrls = serverResponse.body.usersync || []; + userSyncUrls.forEach(sync => { + if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) { + syncs.push({ + type: 'iframe', + url: userSyncUrlProcess(sync.url), }); - }); - } - } + } + if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) { + syncs.push({ + type: 'image', + url: userSyncUrlProcess(sync.url), + }); + } + }) + }); return syncs; }, } function getContext(elem) { - return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined); + try { + return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined); + } catch (e) { + return undefined; + } } function isDefined(val) { @@ -206,4 +230,24 @@ function getLocalDateFormatted() { return `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())} ${two(d.getHours())}:${two(d.getMinutes())}:${two(d.getSeconds())}`; } +function getFramePos() { + let t = window; + let m = 0; + let frmLeft = 0; + let frmTop = 0; + do { + m = m + 1; + try { + if (m > 1) { + t = t.parent + } + frmLeft = frmLeft + t.frameElement.getBoundingClientRect().left; + frmTop = frmTop + t.frameElement.getBoundingClientRect().top; + } catch (o) { /* keep looping */ + } + } while ((m < 100) && (t.parent !== t.self)) + + return [frmLeft, frmTop]; +} + registerBidder(spec); diff --git a/test/spec/modules/h12mediaBidAdapter_spec.js b/test/spec/modules/h12mediaBidAdapter_spec.js index 08a83ce981f..9861069f260 100644 --- a/test/spec/modules/h12mediaBidAdapter_spec.js +++ b/test/spec/modules/h12mediaBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/h12mediaBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; describe('H12 Media Adapter', function () { const DEFAULT_CURRENCY = 'USD'; @@ -21,6 +22,7 @@ describe('H12 Media Adapter', function () { auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f313', params: { pubid: 123321, + pubsubid: 'pubsubtestid', }, }; @@ -72,34 +74,34 @@ describe('H12 Media Adapter', function () { currency: 'EUR', netRevenue: true, ttl: 500, - bids: [{ + bid: { bidId: validBid.bidId, cpm: 0.33, width: 300, height: 600, creativeId: '335566', ad: '
my ad
', - usersync: [ - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'}, - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'} - ], meta: { advertiserId: '54321', advertiserName: 'My advertiser', advertiserDomains: ['test.com'] } - }] + }, + usersync: [ + {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'}, + {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'} + ], }; const serverResponse2 = { - bids: [{ + bid: { bidId: validBid2.bidId, cpm: 0.33, width: 300, height: 600, creativeId: '335566', ad: '
my ad 2
', - }] + } }; function removeElement(id) { @@ -152,6 +154,10 @@ describe('H12 Media Adapter', function () { beforeEach(function () { sandbox = sinon.sandbox.create(); + sandbox.stub(frameElement, 'getBoundingClientRect').returns({ + left: 10, + top: 10, + }); }); afterEach(function () { @@ -186,36 +192,62 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({adunitSize: validBid.mediaTypes.banner.sizes}); + expect(requestsData).to.include({adunitSize: validBid.mediaTypes.banner.sizes}); }); it('should return empty bid size', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData2 = requests[1].data.bidrequest; + + expect(requestsData2).to.deep.include({adunitSize: []}); + }); + + it('should return pubsubid from params', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const requests = spec.buildRequests([validBid, validBid2], bidderRequest); + const requestsData = requests[0].data.bidrequest; + const requestsData2 = requests[1].data.bidrequest; + + expect(requestsData).to.include({pubsubid: 'pubsubtestid'}); + expect(requestsData2).to.include({pubsubid: ''}); + }); - expect(requestsData.bidrequests[1]).to.deep.include({adunitSize: []}); + it('should return empty for incorrect pubsubid from params', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const bidWithPub = {...validBid}; + bidWithPub.params.pubsubid = 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; // More than 32 chars + const requests = spec.buildRequests([bidWithPub], bidderRequest); + const requestsData = requests[0].data.bidrequest; + + expect(requestsData).to.include({pubsubid: ''}); }); it('should return bid size from params', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; + const requestsData2 = requests[1].data.bidrequest; - expect(requestsData.bidrequests[1]).to.include({size: validBid2.params.size}); + expect(requestsData).to.include({size: ''}); + expect(requestsData2).to.include({size: validBid2.params.size}); }); it('should return GDPR info', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; expect(requestsData).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); + expect(requestsData2).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); }); it('should not have error on empty GDPR', function () { @@ -223,9 +255,23 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid2.adUnitCode); const bidderRequestWithoutGDRP = {...bidderRequest, gdprConsent: null}; const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutGDRP); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; expect(requestsData).to.include({gdpr: false}); + expect(requestsData2).to.include({gdpr: false}); + }); + + it('should not have error on empty USP', function () { + createElementVisible(validBid.adUnitCode); + createElementVisible(validBid2.adUnitCode); + const bidderRequestWithoutUSP = {...bidderRequest, uspConsent: null}; + const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutUSP); + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; + + expect(requestsData).to.include({usp: false}); + expect(requestsData2).to.include({usp: false}); }); it('should create single POST', function () { @@ -233,7 +279,8 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - expect(requests.method).to.equal('POST'); + expect(requests[0].method).to.equal('POST'); + expect(requests[1].method).to.equal('POST'); }); }); @@ -241,42 +288,44 @@ describe('H12 Media Adapter', function () { it('should return coords', function () { createElementVisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.deep.include({coords: {x: 10, y: 10}}); + expect(requestsData).to.deep.include({coords: {x: 10, y: 10}}); }); - it('should define not iframe', function () { + it('should define iframe', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const requests = spec.buildRequests([validBid, validBid2], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data; + const requestsData2 = requests[1].data; - expect(requestsData).to.include({isiframe: false}); + expect(requestsData).to.include({isiframe: true}); + expect(requestsData2).to.include({isiframe: true}); }); it('should define visible element', function () { createElementVisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: false}); + expect(requestsData).to.include({ishidden: false}); }); it('should define invisible element', function () { createElementInvisible(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: true}); + expect(requestsData).to.include({ishidden: true}); }); it('should define hidden element', function () { createElementHidden(validBid.adUnitCode); const requests = spec.buildRequests([validBid], bidderRequest); - const requestsData = requests.data; + const requestsData = requests[0].data.bidrequest; - expect(requestsData.bidrequests[0]).to.include({ishidden: true}); + expect(requestsData).to.include({ishidden: true}); }); }); @@ -290,27 +339,27 @@ describe('H12 Media Adapter', function () { it('should return no bids if the response is empty', function () { const bidResponse = spec.interpretResponse({ body: [] }, { validBid }); - expect(bidResponse.length).to.equal(0); + expect(bidResponse).to.be.empty; }); it('should return valid bid responses', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse}, request); + const bidResponse = spec.interpretResponse({body: serverResponse}, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid.bidId, - ad: serverResponse.bids[0].ad, + ad: serverResponse.bid.ad, mediaType: 'banner', - creativeId: serverResponse.bids[0].creativeId, - cpm: serverResponse.bids[0].cpm, - width: serverResponse.bids[0].width, - height: serverResponse.bids[0].height, + creativeId: serverResponse.bid.creativeId, + cpm: serverResponse.bid.cpm, + width: serverResponse.bid.width, + height: serverResponse.bid.height, currency: 'EUR', netRevenue: true, ttl: 500, - meta: serverResponse.bids[0].meta, + meta: serverResponse.bid.meta, pubid: validBid.params.pubid }); }); @@ -319,17 +368,17 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse2}, request); + const bidResponse = spec.interpretResponse({body: serverResponse2}, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid2.bidId, - ad: serverResponse2.bids[0].ad, + ad: serverResponse2.bid.ad, mediaType: 'banner', - creativeId: serverResponse2.bids[0].creativeId, - cpm: serverResponse2.bids[0].cpm, - width: serverResponse2.bids[0].width, - height: serverResponse2.bids[0].height, - meta: serverResponse2.bids[0].meta, + creativeId: serverResponse2.bid.creativeId, + cpm: serverResponse2.bid.cpm, + width: serverResponse2.bid.width, + height: serverResponse2.bid.height, + meta: serverResponse2.bid.meta, pubid: validBid2.params.pubid, currency: DEFAULT_CURRENCY, netRevenue: DEFAULT_NET_REVENUE, From 76c86e8e21e93e091d1764dbf5b30306806d275b Mon Sep 17 00:00:00 2001 From: guiann Date: Thu, 25 Mar 2021 14:50:44 +0100 Subject: [PATCH 0726/1476] AdYouLike Bid Adapter: add video capabilities (#6398) * read and send getFloor value * improve robustness on OnEvent fields * add video mediatype to adyoulike adapter * improve robustness on OnEvent fields * handle instream cases * minor format fix * move the atob call to apply it on every Vast response * update adyoulike md file with video type * update adyoulike bidder doc * fix merge error on 'bid' var name * update adyoulike bidder doc --- modules/adyoulikeBidAdapter.js | 73 +++++++++++++++++++++++++++------- modules/adyoulikeBidAdapter.md | 24 ++++++----- 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index e46a9b6bed2..385ada65538 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -2,11 +2,12 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; const VERSION = '1.0'; const BIDDER_CODE = 'adyoulike'; const DEFAULT_DC = 'hb-api'; +const CURRENCY = 'USD'; const NATIVE_IMAGE = { image: { @@ -34,7 +35,7 @@ const NATIVE_IMAGE = { export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], aliases: ['ayl'], // short code /** * Determines whether or not the given bid request is valid. @@ -59,21 +60,28 @@ export const spec = { buildRequests: function (bidRequests, bidderRequest) { const payload = { Version: VERSION, - Bids: bidRequests.reduce((accumulator, bid) => { - let sizesArray = getSizeArray(bid); + Bids: bidRequests.reduce((accumulator, bidReq) => { + let mediatype = getMediatype(bidReq); + let sizesArray = getSizeArray(bidReq); let size = getSize(sizesArray); - accumulator[bid.bidId] = {}; - accumulator[bid.bidId].PlacementID = bid.params.placement; - accumulator[bid.bidId].TransactionID = bid.transactionId; - accumulator[bid.bidId].Width = size.width; - accumulator[bid.bidId].Height = size.height; - accumulator[bid.bidId].AvailableSizes = sizesArray.join(','); - if (bid.mediaTypes && bid.mediaTypes.native) { - let nativeReq = bid.mediaTypes.native; + accumulator[bidReq.bidId] = {}; + accumulator[bidReq.bidId].PlacementID = bidReq.params.placement; + accumulator[bidReq.bidId].TransactionID = bidReq.transactionId; + accumulator[bidReq.bidId].Width = size.width; + accumulator[bidReq.bidId].Height = size.height; + accumulator[bidReq.bidId].AvailableSizes = sizesArray.join(','); + if (typeof bidReq.getFloor === 'function') { + accumulator[bidReq.bidId].Pricing = getFloor(bidReq, size, mediatype); + } + if (mediatype === NATIVE) { + let nativeReq = bidReq.mediaTypes.native; if (nativeReq.type === 'image') { nativeReq = Object.assign({}, NATIVE_IMAGE, nativeReq); } - accumulator[bid.bidId].Native = nativeReq; + accumulator[bidReq.bidId].Native = nativeReq; + } + if (mediatype === VIDEO) { + accumulator[bidReq.bidId].Video = bidReq.mediaTypes.video; } return accumulator; }, {}), @@ -156,6 +164,31 @@ function getCanonicalUrl() { return ''; } +/* Get mediatype from bidRequest */ +function getMediatype(bidRequest) { + var type = BANNER; + + if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { + type = NATIVE; + } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + type = VIDEO; + } + + return type; +} +/* Get Floor price information */ +function getFloor(bidRequest, size, mediaType) { + const bidFloors = bidRequest.getFloor({ + currency: CURRENCY, + mediaType, + size: [ size.width, size.height ] + }); + + if (!isNaN(bidFloors.floor) && (bidFloors.currency === CURRENCY)) { + return bidFloors.floor; + } +} + /* Get information on page refresh */ function getPageRefreshed() { try { @@ -287,6 +320,14 @@ function getTrackers(eventsArray, jsTrackers) { return result; } +function getVideoAd(response) { + var adJson = {}; + if (typeof response.Ad === 'string') { + adJson = JSON.parse(response.Ad.match(/\/\*PREBID\*\/(.*)\/\*PREBID\*\//)[1]); + return utils.deepAccess(adJson, 'Content.MainVideo.Vast'); + } +} + function getNativeAssets(response, nativeConfig) { const native = {}; @@ -402,12 +443,16 @@ function createBid(response, bidRequests) { creativeId: response.CreativeID, cpm: response.Price, netRevenue: true, - currency: 'USD' + currency: CURRENCY }; if (request && request.Native) { bid.native = getNativeAssets(response, request.Native); bid.mediaType = 'native'; + } else if (request && request.Video) { + const vast64 = response.Vast || getVideoAd(response); + bid.vastXml = vast64 ? window.atob(vast64) : ''; + bid.mediaType = 'video'; } else { bid.width = response.Width; bid.height = response.Height; diff --git a/modules/adyoulikeBidAdapter.md b/modules/adyoulikeBidAdapter.md index d0e7fa8883b..edb47d25637 100644 --- a/modules/adyoulikeBidAdapter.md +++ b/modules/adyoulikeBidAdapter.md @@ -7,16 +7,20 @@ Maintainer: prebid@adyoulike.com # Description Module that connects to Adyoulike demand sources. -Banner formats are supported. +Banner, Native and Video ad formats are supported. # Test Parameters ``` - var adUnits = { + var adUnits = { "code": "test-div", "mediaTypes": { "banner": { "sizes": ["300x250"] }, + "video": { + context: "instream", + playerSize: [[640, 480]] + }, "native": { "image": { "required": true, @@ -48,12 +52,12 @@ Banner formats are supported. "sizes": [] } } - bids: [{ - bidder: "adyoulike", - params: { - placement: 194 f787b85c829fb8822cdaf1ae64435, - DC: "fra01", // Optional for set the data center name - } - }] - }; + }, + bids: [{ + bidder: "adyoulike", + params: { + placement: "e622af275681965d3095808561a1e510" + } + }] + }; ``` From b96f38003bf516f791ad0d90c21307b77bb4240a Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 25 Mar 2021 08:32:46 -0700 Subject: [PATCH 0727/1476] remove deprecated creative rendering example --- integrationExamples/gpt/creative_rendering.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/integrationExamples/gpt/creative_rendering.html b/integrationExamples/gpt/creative_rendering.html index aef8b7f1654..04d4736c631 100644 --- a/integrationExamples/gpt/creative_rendering.html +++ b/integrationExamples/gpt/creative_rendering.html @@ -1,9 +1,4 @@ - - - - + + + + + + + +

Prebid Display/Video Merged Auction with Adloox Integration

+ +

div-1

+
+ +
+ +

div-2

+
+ +
+ +

video-1

+
+ + + + diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js new file mode 100644 index 00000000000..3e92ae34004 --- /dev/null +++ b/modules/adlooxAnalyticsAdapter.js @@ -0,0 +1,288 @@ +/** + * This module provides [Adloox]{@link https://www.adloox.com/} Analytics + * The module will inject Adloox's verification JS tag alongside slot at bidWin + * @module modules/adlooxAnalyticsAdapter + */ + +import adapterManager from '../src/adapterManager.js'; +import adapter from '../src/AnalyticsAdapter.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { AUCTION_COMPLETED } from '../src/auction.js'; +import { EVENTS } from '../src/constants.json'; +import find from 'core-js-pure/features/array/find.js'; +import * as utils from '../src/utils.js'; + +const MODULE = 'adlooxAnalyticsAdapter'; + +const URL_JS = 'https://j.adlooxtracking.com/ads/js/tfav_adl_%%clientid%%.js'; + +const ADLOOX_VENDOR_ID = 93; + +const ADLOOX_MEDIATYPE = { + DISPLAY: 2, + VIDEO: 6 +}; + +const MACRO = {}; +MACRO['client'] = function(b, c) { + return c.client; +}; +MACRO['clientid'] = function(b, c) { + return c.clientid; +}; +MACRO['tagid'] = function(b, c) { + return c.tagid; +}; +MACRO['platformid'] = function(b, c) { + return c.platformid; +}; +MACRO['targetelt'] = function(b, c) { + return c.toselector(b); +}; +MACRO['creatype'] = function(b, c) { + return b.mediaType == 'video' ? ADLOOX_MEDIATYPE.VIDEO : ADLOOX_MEDIATYPE.DISPLAY; +}; +MACRO['pbAdSlot'] = function(b, c) { + const adUnit = find(auctionManager.getAdUnits(), a => b.adUnitCode === a.code); + return utils.deepAccess(adUnit, 'fpd.context.pbAdSlot') || utils.getGptSlotInfoForAdUnitCode(b.adUnitCode).gptSlot || b.adUnitCode; +}; + +const PARAMS_DEFAULT = { + 'id1': function(b) { return b.adUnitCode }, + 'id2': '%%pbAdSlot%%', + 'id3': function(b) { return b.bidder }, + 'id4': function(b) { return b.adId }, + 'id5': function(b) { return b.dealId }, + 'id6': function(b) { return b.creativeId }, + 'id7': function(b) { return b.size }, + 'id11': '$ADLOOX_WEBSITE' +}; + +const NOOP = function() {}; + +let analyticsAdapter = Object.assign(adapter({ analyticsType: 'endpoint' }), { + track({ eventType, args }) { + if (!analyticsAdapter[`handle_${eventType}`]) return; + + utils.logInfo(MODULE, 'track', eventType, args); + + analyticsAdapter[`handle_${eventType}`](args); + } +}); + +analyticsAdapter.context = null; + +analyticsAdapter.originEnableAnalytics = analyticsAdapter.enableAnalytics; +analyticsAdapter.enableAnalytics = function(config) { + analyticsAdapter.context = null; + + utils.logInfo(MODULE, 'config', config); + + if (!utils.isPlainObject(config.options)) { + utils.logError(MODULE, 'missing options'); + return; + } + if (!(config.options.js === undefined || utils.isStr(config.options.js))) { + utils.logError(MODULE, 'invalid js options value'); + return; + } + if (!(config.options.toselector === undefined || utils.isFn(config.options.toselector))) { + utils.logError(MODULE, 'invalid toselector options value'); + return; + } + if (!utils.isStr(config.options.client)) { + utils.logError(MODULE, 'invalid client options value'); + return; + } + if (!utils.isNumber(config.options.clientid)) { + utils.logError(MODULE, 'invalid clientid options value'); + return; + } + if (!utils.isNumber(config.options.tagid)) { + utils.logError(MODULE, 'invalid tagid options value'); + return; + } + if (!utils.isNumber(config.options.platformid)) { + utils.logError(MODULE, 'invalid platformid options value'); + return; + } + if (!(config.options.params === undefined || utils.isPlainObject(config.options.params))) { + utils.logError(MODULE, 'invalid params options value'); + return; + } + + analyticsAdapter.context = { + js: config.options.js || URL_JS, + toselector: config.options.toselector || function(bid) { + let code = utils.getGptSlotInfoForAdUnitCode(bid.adUnitCode).divId || bid.adUnitCode; + // https://mathiasbynens.be/notes/css-escapes + code = code.replace(/^\d/, '\\3$& '); + return `#${code}` + }, + client: config.options.client, + clientid: config.options.clientid, + tagid: config.options.tagid, + platformid: config.options.platformid, + params: [] + }; + + config.options.params = utils.mergeDeep({}, PARAMS_DEFAULT, config.options.params || {}); + Object + .keys(config.options.params) + .forEach(k => { + if (!Array.isArray(config.options.params[k])) { + config.options.params[k] = [ config.options.params[k] ]; + } + config.options.params[k].forEach(v => analyticsAdapter.context.params.push([ k, v ])); + }); + + Object.keys(COMMAND_QUEUE).forEach(commandProcess); + + analyticsAdapter.originEnableAnalytics(config); +} + +analyticsAdapter.originDisableAnalytics = analyticsAdapter.disableAnalytics; +analyticsAdapter.disableAnalytics = function() { + analyticsAdapter.context = null; + + analyticsAdapter.originDisableAnalytics(); +} + +analyticsAdapter.url = function(url, args, bid) { + // utils.formatQS outputs PHP encoded querystrings... (╯°□°)╯ ┻━┻ + function a2qs(a) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + function fixedEncodeURIComponent(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16); + }); + } + + const args = []; + let n = a.length; + while (n-- > 0) { + if (!(a[n][1] === undefined || a[n][1] === null || a[n][1] === false)) { + args.unshift(fixedEncodeURIComponent(a[n][0]) + (a[n][1] !== true ? ('=' + fixedEncodeURIComponent(a[n][1])) : '')); + } + } + + return args.join('&'); + } + + const macros = (str) => { + return str.replace(/%%([a-z]+)%%/gi, (match, p1) => MACRO[p1] ? MACRO[p1](bid, analyticsAdapter.context) : match); + }; + + url = macros(url); + args = args || []; + + let n = args.length; + while (n-- > 0) { + if (utils.isFn(args[n][1])) { + try { + args[n][1] = args[n][1](bid); + } catch (_) { + utils.logError(MODULE, 'macro', args[n][0], _.message); + args[n][1] = `ERROR: ${_.message}`; + } + } + if (utils.isStr(args[n][1])) { + args[n][1] = macros(args[n][1]); + } + } + + return url + a2qs(args); +} + +analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) { + if (!(auctionDetails.auctionStatus == AUCTION_COMPLETED && auctionDetails.bidsReceived.length > 0)) return; + analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = NOOP; + + utils.logMessage(MODULE, 'preloading verification JS'); + + const uri = utils.parseUrl(analyticsAdapter.url(`${analyticsAdapter.context.js}#`)); + + const link = document.createElement('link'); + link.setAttribute('href', `${uri.protocol}://${uri.host}${uri.pathname}`); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + utils.insertElement(link); +} + +analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) { + const sl = analyticsAdapter.context.toselector(bid); + let el; + try { + el = document.querySelector(sl); + } catch (_) { } + if (!el) { + utils.logWarn(MODULE, `unable to find ad unit code '${bid.adUnitCode}' slot using selector '${sl}' (use options.toselector to change), ignoring`); + return; + } + + utils.logMessage(MODULE, `measuring '${bid.mediaType}' unit at '${bid.adUnitCode}'`); + + const params = analyticsAdapter.context.params.concat([ + [ 'tagid', '%%tagid%%' ], + [ 'platform', '%%platformid%%' ], + [ 'fwtype', 4 ], + [ 'targetelt', '%%targetelt%%' ], + [ 'creatype', '%%creatype%%' ] + ]); + + loadExternalScript(analyticsAdapter.url(`${analyticsAdapter.context.js}#`, params, bid), 'adloox'); +} + +adapterManager.registerAnalyticsAdapter({ + adapter: analyticsAdapter, + code: 'adloox', + gvlid: ADLOOX_VENDOR_ID +}); + +export default analyticsAdapter; + +// src/events.js does not support custom events or handle races... (╯°□°)╯ ┻━┻ +const COMMAND_QUEUE = {}; +export const COMMAND = { + CONFIG: 'config', + URL: 'url', + TRACK: 'track' +}; +export function command(cmd, data, callback0) { + const cid = utils.getUniqueIdentifierStr(); + const callback = function() { + delete COMMAND_QUEUE[cid]; + if (callback0) callback0.apply(null, arguments); + }; + COMMAND_QUEUE[cid] = { cmd, data, callback }; + if (analyticsAdapter.context) commandProcess(cid); +} +function commandProcess(cid) { + const { cmd, data, callback } = COMMAND_QUEUE[cid]; + + utils.logInfo(MODULE, 'command', cmd, data); + + switch (cmd) { + case COMMAND.CONFIG: + const response = { + client: analyticsAdapter.context.client, + clientid: analyticsAdapter.context.clientid, + tagid: analyticsAdapter.context.tagid, + platformid: analyticsAdapter.context.platformid + }; + callback(response); + break; + case COMMAND.URL: + if (data.ids) data.args = data.args.concat(analyticsAdapter.context.params.filter(p => /^id([1-9]|10)$/.test(p[0]))); // not >10 + callback(analyticsAdapter.url(data.url, data.args, data.bid)); + break; + case COMMAND.TRACK: + analyticsAdapter.track(data); + callback(); // drain queue + break; + default: + utils.logWarn(MODULE, 'command unknown', cmd); + // do not callback as arguments are unknown and to aid debugging + } +} diff --git a/modules/adlooxAnalyticsAdapter.md b/modules/adlooxAnalyticsAdapter.md new file mode 100644 index 00000000000..0ca67f937f6 --- /dev/null +++ b/modules/adlooxAnalyticsAdapter.md @@ -0,0 +1,146 @@ +# Overview + + Module Name: Adloox Analytics Adapter + Module Type: Analytics Adapter + Maintainer: technique@adloox.com + +# Description + +Analytics adapter for adloox.com. Contact adops@adloox.com for information. + +This module can be used to track: + + * Display + * Native + * Video (see below for further instructions) + +The adapter adds an HTML ` - + + + - +

Prebid.js Test

Div-1
diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 37a9554e9a4..5e2a5e1bff5 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -31,7 +31,7 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bid => { var payload = { - bidfloor: bid.params.bidfloor, + bidfloor: raiGetFloor(bid, config), ifa: bid.params.ifa, pid: bid.params.pid, supplyType: bid.params.supplyType, @@ -141,11 +141,15 @@ export const spec = { var syncUrl = ''; var consent = ''; + var raiSync = {}; + + raiSync = raiGetSyncInclude(config); + if (gdprConsent && typeof gdprConsent.consentString === 'string' && typeof gdprConsent.consentString != 'undefined') { consent = `consentString=${gdprConsent.consentString}` } - if (syncOptions.iframeEnabled) { + if (syncOptions.iframeEnabled && raiSync.raiIframe != 'exclude') { syncUrl = 'https://sync.richaudience.com/dcf3528a0b8aa83634892d50e91c306e/?ord=' + rand if (consent != '') { syncUrl += `&${consent}` @@ -156,7 +160,7 @@ export const spec = { }); } - if (syncOptions.pixelEnabled && REFERER != null && syncs.length == 0) { + if (syncOptions.pixelEnabled && REFERER != null && syncs.length == 0 && raiSync.raiImage != 'exclude') { syncUrl = `https://sync.richaudience.com/bf7c142f4339da0278e83698a02b0854/?referrer=${REFERER}`; if (consent != '') { syncUrl += `&${consent}` @@ -263,3 +267,42 @@ function raiGetResolution() { } return resolution; } + +function raiGetSyncInclude(config) { + try { + let raConfig = null; + let raiSync = {}; + if (config.getConfig('userSync').filterSettings != null && typeof config.getConfig('userSync').filterSettings != 'undefined') { + raConfig = config.getConfig('userSync').filterSettings + if (raConfig.iframe != null && typeof raConfig.iframe != 'undefined') { + raiSync.raiIframe = raConfig.iframe.bidders == 'richaudience' || raConfig.iframe.bidders == '*' ? raConfig.iframe.filter : 'exclude'; + } + if (raConfig.image != null && typeof raConfig.image != 'undefined') { + raiSync.raiImage = raConfig.image.bidders == 'richaudience' || raConfig.image.bidders == '*' ? raConfig.image.filter : 'exclude'; + } + } + return raiSync; + } catch (e) { + return null; + } +} + +function raiGetFloor(bid, config) { + try { + let raiFloor; + if (bid.params.bidfloor != null) { + raiFloor = bid.params.bidfloor; + } else if (typeof bid.getFloor == 'function') { + let floorSpec = bid.getFloor({ + currency: config.getConfig('currency.adServerCurrency'), + mediaType: bid.mediaType.banner ? 'banner' : 'video', + size: '*' + }) + + raiFloor = floorSpec.floor; + } + return raiFloor + } catch (e) { + return 0 + } +} diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 5deb2463523..72410b71fb2 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -788,76 +788,343 @@ describe('Richaudience adapter tests', function () { })).to.equal(true); }); - it('Verifies user syncs iframe', function () { - var syncs = spec.getUserSyncs({ - iframeEnabled: true - }, [BID_RESPONSE], { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - gdprApplies: true + describe('userSync', function () { + it('Verifies user syncs iframe include', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'include'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(1); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); }); + it('Verifies user syncs iframe exclude', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'exclude'}}} + }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - syncs = spec.getUserSyncs({ - iframeEnabled: false - }, [BID_RESPONSE], { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - gdprApplies: true + var syncs = spec.getUserSyncs({ + iframeEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); }); - expect(syncs).to.have.lengthOf(0); - syncs = spec.getUserSyncs({ - iframeEnabled: true - }, [], {consentString: '', gdprApplies: false}); - expect(syncs).to.have.lengthOf(1); + it('Verifies user syncs image include', function () { + config.setConfig({ + 'userSync': {filterSettings: {image: {bidders: '*', filter: 'include'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); - syncs = spec.getUserSyncs({ - iframeEnabled: false - }, [], {consentString: '', gdprApplies: true}); - expect(syncs).to.have.lengthOf(0); + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: '', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); - config.setConfig({ - consentManagement: { - cmpApi: 'iab', - timeout: 5000, - allowAuctionWithoutConsent: true, + syncs = spec.getUserSyncs({ + iframeEnabled: false, pixelEnabled: true - } + }, [], { + consentString: null, + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); }); - }); - it('Verifies user syncs image', function () { - var syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [BID_RESPONSE], { - consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - - syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [BID_RESPONSE], { - consentString: '', - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - - syncs = spec.getUserSyncs({ - iframeEnabled: false, - pixelEnabled: true - }, [], { - consentString: null, - referer: 'http://domain.com', - gdprApplies: true - }) - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('image'); - }); + it('Verifies user syncs image exclude', function () { + config.setConfig({ + 'userSync': {filterSettings: {image: {bidders: '*', filter: 'exclude'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: '', + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: true + }, [], { + consentString: null, + referer: 'http://domain.com', + gdprApplies: true + }) + expect(syncs).to.have.lengthOf(0); + }); + + it('Verifies user syncs iframe/image include', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'include'}, image: {bidders: '*', filter: 'include'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(1); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); + }); + + it('Verifies user syncs iframe/image exclude', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'exclude'}, image: {bidders: '*', filter: 'exclude'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); + }); + + it('Verifies user syncs iframe exclude / image include', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'exclude'}, image: {bidders: '*', filter: 'include'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(1); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); + }); + + it('Verifies user syncs iframe include / image exclude', function () { + config.setConfig({ + 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'include'}, image: {bidders: '*', filter: 'exclude'}}} + }) + + var syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true}, + ); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true, + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [BID_RESPONSE], { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + }); + expect(syncs).to.have.lengthOf(0); + + syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [], {consentString: '', gdprApplies: false}); + expect(syncs).to.have.lengthOf(1); + + syncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, [], {consentString: '', gdprApplies: true}); + expect(syncs).to.have.lengthOf(0); + }); + }) }); From 05d87735631c76a90b658453640b3c86d983a0d8 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 14 Apr 2021 19:48:10 +0200 Subject: [PATCH 0816/1476] Prebid 4.35.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b8a4dc5fb4..67394ebdb33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.35.0-pre", + "version": "4.35.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a3790c27cf71d288cc3e96959110cbce88a0b9c8 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Wed, 14 Apr 2021 20:15:04 +0200 Subject: [PATCH 0817/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67394ebdb33..58ea0d7ba30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.35.0", + "version": "4.36.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From caead3ccccc448e4cd09d074fd9f8833f56fe9b3 Mon Sep 17 00:00:00 2001 From: Sourabh Gandhe Date: Thu, 15 Apr 2021 13:12:55 +0530 Subject: [PATCH 0818/1476] Deepintent ID System: add new ID module (#6537) * initial commit * feat(deepintent-dpes): adds deepintent user id module * chore(code-cleanup): removed console logs * eids config added * fix for passing the eids * docs added with minor change * tests added * remaining conflict resolution * kick off circle-ci tests manually * fix linting error * changed the atype to 3 * tests added for eids_spec.js * Change the language * added cacheIdObject signature * changed test cases * eIds passing added to adapter * docs changed removed params not required * doc added * docs added in userId base * user id tests added * lint fixes * lint fixes * code review comments fix Co-authored-by: Sourabh Gandhe Co-authored-by: ChinmoyDebnath Co-authored-by: Chris Huie --- modules/.submodules.json | 1 + modules/deepintentBidAdapter.js | 9 ++ modules/deepintentDpesIdSystem.js | 45 ++++++++ modules/deepintentDpesIdSystem.md | 43 +++++++ modules/userId/eids.js | 5 +- modules/userId/eids.md | 7 ++ modules/userId/userId.md | 14 +++ .../modules/deepintentDpesIdsystem_spec.js | 76 ++++++++++++ test/spec/modules/eids_spec.js | 12 ++ test/spec/modules/userId_spec.js | 108 +++++++++++++++--- 10 files changed, 302 insertions(+), 18 deletions(-) create mode 100644 modules/deepintentDpesIdSystem.js create mode 100644 modules/deepintentDpesIdSystem.md create mode 100644 test/spec/modules/deepintentDpesIdsystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index ffa3f9df353..7ad4bedde5c 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -16,6 +16,7 @@ "zeotapIdPlusIdSystem", "haloIdSystem", "quantcastIdSystem", + "deepintentDpesIdSystem", "nextrollIdSystem", "idxIdSystem", "fabrickIdSystem", diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index c4dc23cf912..9ec6c8e5bc2 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -49,6 +49,8 @@ export const spec = { utils.deepSetValue(openRtbBidRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); } + injectEids(openRtbBidRequest, validBidRequests); + return { method: 'POST', url: BIDDER_ENDPOINT, @@ -128,6 +130,13 @@ function buildUser(bid) { } } +function injectEids(openRtbBidRequest, validBidRequests) { + const bidUserIdAsEids = utils.deepAccess(validBidRequests, '0.userIdAsEids'); + if (utils.isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { + utils.deepSetValue(openRtbBidRequest, 'user.eids', bidUserIdAsEids); + } +} + function buildBanner(bid) { if (utils.deepAccess(bid, 'mediaTypes.banner')) { // Get Sizes from MediaTypes Object, Will always take first size, will be overrided by params for exact w,h diff --git a/modules/deepintentDpesIdSystem.js b/modules/deepintentDpesIdSystem.js new file mode 100644 index 00000000000..375c8c07ed1 --- /dev/null +++ b/modules/deepintentDpesIdSystem.js @@ -0,0 +1,45 @@ +/** + * This module adds DPES to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/deepintentDpesSystem + * @requires module:modules/userId + */ + +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const MODULE_NAME = 'deepintentId'; +export const storage = getStorageManager(null, MODULE_NAME); + +/** @type {Submodule} */ +export const deepintentDpesSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{value:string}} value + * @returns {{deepintentId:Object}} + */ + decode(value, config) { + return value ? { 'deepintentId': value } : undefined; + }, + + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} config + * @param {ConsentData|undefined} consentData + * @param {Object} cacheIdObj - existing id, if any + * @return {{id: string | undefined} | undefined} + */ + getId(config, consentData, cacheIdObj) { + return cacheIdObj; + } + +}; + +submodule('userId', deepintentDpesSubmodule); diff --git a/modules/deepintentDpesIdSystem.md b/modules/deepintentDpesIdSystem.md new file mode 100644 index 00000000000..2af0fe7446e --- /dev/null +++ b/modules/deepintentDpesIdSystem.md @@ -0,0 +1,43 @@ +# Deepintent DPES ID + +The Deepintent Id is a shared, healthcare identifier which helps publisher in absence of the 3rd Party cookie matching. This lets publishers set and bid with healthcare identity . Deepintent lets users protect their privacy through advertising value chain, where Healthcare identity when setting the identity takes in consideration of users choices, as well as when passing identity on the cookie itself privacy consent strings are checked. The healthcare identity when set is not stored on Deepintent's servers but is stored on users browsers itself. User can still opt out of the ads by https://option.deepintent.com/adchoices. + +## Deepintent DPES ID Registration + +The Deepintent DPES ID is free to use, but requires a simple registration with Deepintent. Please reach to prebid@deepintent.com to get started. +Once publisher registers with deepintents platform for healthcare identity Deepintent provides the Tag code to be placed on the page, this tag code works to capture and store information as per publishers and users agreement. DPES User ID module uses this stored id and passes it on the deepintent prebid adapter. + + +## Deepintent DPES ID Configuration + +First, make sure to add the Deepintent submodule to your Prebid.js package with: + +``` +gulp build --modules=deepintentDpesIdSystem,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'deepintentId', + storage: { + type: 'cookie', + name: '_dpes_id', + expires: 90 // storage lasts for 90 days, optional if storage type is html5 + } + }], + auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules + } +}); +``` + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | The name of this module: `"deepintentId"` | `"deepintentId"` | +| storage | Required | Object | Storage settings for how the User Id module will cache the Deepintent ID locally | | +| storage.type | Required | String | This is where the results of the user ID will be stored. Deepintent`"html5"` or `"cookie"`. | `"html5"` | +| storage.name | Required | String | The name of the local storage where the user ID will be stored. | `"_dpes_id"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. Deepintent recommends `90`. | `90` | \ No newline at end of file diff --git a/modules/userId/eids.js b/modules/userId/eids.js index a38417683ba..93d2ead15ea 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -207,7 +207,10 @@ const USER_IDS_CONFIG = { return data.id; } }, - + 'deepintentId': { + source: 'deepintent.com', + atype: 3 + }, // Admixer Id 'admixerId': { source: 'admixer.net', diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 93783a2db4d..a00aedcc52e 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -184,6 +184,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 3 }] + }, + { + source: 'deepintent.com', + uids: [{ + id: 'some-random-id-value', + atype: 3 + }] } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 79f547b179f..aef50eeccdf 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -222,6 +222,20 @@ pbjs.setConfig({ name: 'admixerId', expires: 30 } + },{ + name: "deepintentId", + storage: { + type: "html5", + name: "_dpes_id", + expires: 90 + } + },{ + name: "deepintentId", + storage: { + type: "cookie", + name: "_dpes_id", + expires: 90 + } }], syncDelay: 5000 } diff --git a/test/spec/modules/deepintentDpesIdsystem_spec.js b/test/spec/modules/deepintentDpesIdsystem_spec.js new file mode 100644 index 00000000000..7ea5553393c --- /dev/null +++ b/test/spec/modules/deepintentDpesIdsystem_spec.js @@ -0,0 +1,76 @@ +import { expect } from 'chai'; +import find from 'core-js-pure/features/array/find.js'; +import { storage, deepintentDpesSubmodule } from 'modules/deepintentDpesIdSystem.js'; +import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { config } from 'src/config.js'; + +const DI_COOKIE_NAME = '_dpes_id'; +const DI_COOKIE_STORED = '{"id":"2cf40748c4f7f60d343336e08f80dc99"}'; +const DI_COOKIE_OBJECT = {id: '2cf40748c4f7f60d343336e08f80dc99'}; + +const cookieConfig = { + name: 'deepintentId', + storage: { + type: 'cookie', + name: '_dpes_id', + expires: 28 + } +}; + +const html5Config = { + name: 'deepintentId', + storage: { + type: 'html5', + name: '_dpes_id', + expires: 28 + } +} + +describe('Deepintent DPES System', () => { + let getDataFromLocalStorageStub, localStorageIsEnabledStub; + let getCookieStub, cookiesAreEnabledStub; + + beforeEach(() => { + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + }); + + afterEach(() => { + getDataFromLocalStorageStub.restore(); + localStorageIsEnabledStub.restore(); + getCookieStub.restore(); + cookiesAreEnabledStub.restore(); + }); + + describe('Deepintent Dpes Sytsem: test "getId" method', () => { + it('Wrong config should fail the tests', () => { + // no config + expect(deepintentDpesSubmodule.getId()).to.be.eq(undefined); + expect(deepintentDpesSubmodule.getId({ })).to.be.eq(undefined); + + expect(deepintentDpesSubmodule.getId({params: {}, storage: {}})).to.be.eq(undefined); + expect(deepintentDpesSubmodule.getId({params: {}, storage: {type: 'cookie'}})).to.be.eq(undefined); + expect(deepintentDpesSubmodule.getId({params: {}, storage: {name: '_dpes_id'}})).to.be.eq(undefined); + }); + + it('Get value stored in cookie for getId', () => { + getCookieStub.withArgs(DI_COOKIE_NAME).returns(DI_COOKIE_STORED); + let diId = deepintentDpesSubmodule.getId(cookieConfig, undefined, DI_COOKIE_OBJECT); + expect(diId).to.deep.equal(DI_COOKIE_OBJECT); + }); + + it('provides the stored deepintentId if cookie is absent but present in local storage', () => { + getDataFromLocalStorageStub.withArgs(DI_COOKIE_NAME).returns(DI_COOKIE_STORED); + let idx = deepintentDpesSubmodule.getId(html5Config, undefined, DI_COOKIE_OBJECT); + expect(idx).to.deep.equal(DI_COOKIE_OBJECT); + }); + }); + + describe('Deepintent Dpes System : test "decode" method', () => { + it('Get the correct decoded value for dpes id', () => { + expect(deepintentDpesSubmodule.decode(DI_COOKIE_OBJECT, cookieConfig)).to.deep.equal({'deepintentId': {'id': '2cf40748c4f7f60d343336e08f80dc99'}}); + }); + }); +}); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 86a6dff2205..1ccaab2b302 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -189,6 +189,18 @@ describe('eids array generation for known sub-modules', function() { }); }); + it('deepintentId', function() { + const userId = { + deepintentId: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'deepintent.com', + uids: [{id: 'some-random-id-value', atype: 3}] + }); + }); + it('NetId', function() { const userId = { netId: 'some-random-id-value' diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 3c852f3af5c..d61d919a5ef 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -44,6 +44,7 @@ import {tapadIdSubmodule} from 'modules/tapadIdSystem.js'; import {getPrebidInternal} from 'src/utils.js'; import {uid2IdSubmodule} from 'modules/uid2IdSystem.js'; import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; +import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -462,7 +463,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -470,14 +471,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -488,7 +489,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -505,7 +506,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -513,7 +514,7 @@ describe('User ID', function () { }); it('config with 17 configurations should result in 18 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -565,14 +566,17 @@ describe('User ID', function () { }, { name: 'admixerId', storage: {name: 'admixerId', type: 'cookie'} + }, { + name: 'deepintentId', + storage: {name: 'deepintentId', type: 'cookie'} }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 18 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 19 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -587,7 +591,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -602,7 +606,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ userSync: { @@ -1702,6 +1706,53 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from deepintentId cookies', function (done) { + // simulate existing browser local storage values + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([deepintentDpesSubmodule]); + init(config); + config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.deep.equal('testdeepintentId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'deepintent.com', + uids: [{id: 'testdeepintentId', atype: 3}] + }); + }); + }); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook from deepintentId html5', function (done) { + // simulate existing browser local storage values + localStorage.setItem('deepintentId', 'testdeepintentId'); + localStorage.setItem('deepintentId_exp', ''); + + setSubmoduleRegistry([deepintentDpesSubmodule]); + init(config); + config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'deepintent.com', + uids: [{id: 'testdeepintentId', atype: 3}] + }); + }); + }); + localStorage.removeItem('deepintentId'); + done(); + }, {adUnits}); + }); + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId, Criteo, UID 2.0, admixerId and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1720,8 +1771,9 @@ describe('User ID', function () { coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1737,7 +1789,8 @@ describe('User ID', function () { ['mwOpenLinkId', 'mwol', 'cookie'], ['tapadId', 'tapad_id', 'cookie'], ['uid2', 'uid2id', 'cookie'], - ['admixerId', 'admixerId', 'cookie'])); + ['admixerId', 'admixerId', 'cookie'], + ['deepintentId', 'deepintentId', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1787,7 +1840,11 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.admixerId'); expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(bid.userIdAsEids.length).to.equal(14); + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); + + expect(bid.userIdAsEids.length).to.equal(15); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1804,6 +1861,7 @@ describe('User ID', function () { coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -1826,6 +1884,7 @@ describe('User ID', function () { coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1849,6 +1908,7 @@ describe('User ID', function () { attachIdSystem(tapadIdSubmodule); attachIdSystem(uid2IdSubmodule); attachIdSystem(admixerIdSubmodule); + attachIdSystem(deepintentDpesSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1864,7 +1924,8 @@ describe('User ID', function () { ['mwOpenLinkId', 'mwol', 'cookie'], ['tapadId', 'tapad_id', 'cookie'], ['uid2', 'uid2id', 'cookie'], - ['admixerId', 'admixerId', 'cookie'])); + ['admixerId', 'admixerId', 'cookie'], + ['deepintentId', 'deepintentId', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { @@ -1917,8 +1978,11 @@ describe('User ID', function () { // also check that admixerId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.admixerId'); expect(bid.userId.admixerId).to.equal('testadmixerId'); + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(bid.userIdAsEids.length).to.equal(14); + expect(bid.userIdAsEids.length).to.equal(15); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1935,6 +1999,7 @@ describe('User ID', function () { coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -2192,10 +2257,11 @@ describe('User ID', function () { coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('admixerId', 'testadmixerId', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('deepintentId', 'testdeepintentId', new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule]); init(config); config.setConfig({ @@ -2227,6 +2293,8 @@ describe('User ID', function () { name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} }, { name: 'uid2' + }, { + name: 'deepintentId', storage: {name: 'deepintentId', type: 'cookie'} }] } }); @@ -2291,7 +2359,12 @@ describe('User ID', function () { // also check that admixerId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.admixerId'); expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(bid.userIdAsEids.length).to.equal(12); + + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); + + expect(bid.userIdAsEids.length).to.equal(13); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -2305,6 +2378,7 @@ describe('User ID', function () { coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); done(); From bea2261291ffbb4b89100174975f100ad373440a Mon Sep 17 00:00:00 2001 From: Nick Peceniak Date: Thu, 15 Apr 2021 06:00:55 -0600 Subject: [PATCH 0819/1476] Update spotxBidAdpter renderer url to ensure onLoad is always called. (#6592) Co-authored-by: Nick Peceniak --- modules/spotxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 05e4e0ba1ef..4c9b50ca9db 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -365,7 +365,7 @@ export const spec = { const playersize = utils.deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); const renderer = Renderer.install({ id: 0, - url: '//', + url: '/', config: { adText: 'SpotX Outstream Video Ad via Prebid.js', player_width: playersize[0][0], From d18c6a874bf1465cf9f8427f65a2fa21c05a2454 Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Thu, 15 Apr 2021 17:00:24 +0300 Subject: [PATCH 0820/1476] VIS.X: add onSetTargeting, onBidWon & onTimeout handlers (#6532) --- modules/visxBidAdapter.js | 20 +++++++++++++++-- test/spec/modules/visxBidAdapter_spec.js | 28 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 725482d07c3..a5829b9cd9c 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -2,10 +2,14 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'visx'; -const ENDPOINT_URL = 'https://t.visx.net/hb'; +const BASE_URL = 'https://t.visx.net'; +const ENDPOINT_URL = BASE_URL + '/hb'; const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; -const ADAPTER_SYNC_URL = 'https://t.visx.net/push_sync'; +const ADAPTER_SYNC_URL = BASE_URL + '/push_sync'; +const TRACK_WIN_URL = BASE_URL + '/track/win'; +const TRACK_PENDING_URL = BASE_URL + '/track/pending'; +const TRACK_TIMEOUT_URL = BASE_URL + '/track/bid_timeout'; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -170,6 +174,18 @@ export const spec = { url: ADAPTER_SYNC_URL + (query.length ? '?' + query.join('&') : '') }]; } + }, + onSetTargeting: function(bid) { + // Call '/track/pending' with the corresponding bid.requestId + utils.triggerPixel(TRACK_PENDING_URL + '?requestId=' + bid.requestId); + }, + onBidWon: function(bid) { + // Call '/track/win' with the corresponding bid.requestId + utils.triggerPixel(TRACK_WIN_URL + '?requestId=' + bid.requestId); + }, + onTimeout: function(timeoutData) { + // Call '/track/bid_timeout' with timeout data + utils.triggerPixel(TRACK_TIMEOUT_URL + '?data=' + JSON.stringify(timeoutData)); } }; diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index a06f530e145..db885ad314e 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -2,6 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/visxBidAdapter.js'; import { config } from 'src/config.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; describe('VisxAdapter', function () { const adapter = newBidder(spec); @@ -656,4 +657,31 @@ describe('VisxAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); }); + describe('check trackers', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('onSetTargeting', function () { + const requestId = '111'; + spec.onSetTargeting({ requestId }); + expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/pending?requestId=' + requestId)).to.equal(true); + }); + + it('onBidWon', function () { + const requestId = '111'; + spec.onBidWon({ requestId }); + expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/win?requestId=' + requestId)).to.equal(true); + }); + + it('onTimeout', function () { + const data = { timeout: 3000, bidId: '23423', params: { uid: 1 } }; + spec.onTimeout(data); + expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/bid_timeout?data=' + JSON.stringify(data))).to.equal(true); + }); + }); }); From 6ff2cf7792beaf8b1c2577ed9b9360beb35054b0 Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Thu, 15 Apr 2021 08:05:40 -0700 Subject: [PATCH 0821/1476] Index Exchange Bid Adapter: resolve negative size bug (#6582) --- modules/ixBidAdapter.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 7b972aa37e6..0f7f967ef6b 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -492,7 +492,11 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { msd = impressions[transactionIds[i]].missingCount; } - trimImpressions(impressions[transactionIds[i]], MAX_REQ_SIZE - BASE_REQ_SIZE); + if (BASE_REQ_SIZE < MAX_REQ_SIZE) { + trimImpressions(impressions[transactionIds[i]], MAX_REQ_SIZE - BASE_REQ_SIZE); + } else { + utils.logError('ix bidder: Base request size has exceeded maximum request size.'); + } if (impressions[transactionIds[i]].hasOwnProperty('missingImps')) { msi = impressions[transactionIds[i]].missingImps.length; From 170c82d1ef3aadbf9ff3ffcde9594abeaf708ee5 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 15 Apr 2021 17:26:26 +0200 Subject: [PATCH 0822/1476] smartx Bid Adapter: Outstream render bugfix numeric elementId (#6588) --- modules/smartxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index b95b31934a2..6b34e499a99 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -379,7 +379,7 @@ function createOutstreamScript(bid) { smartPlayObj.adResponse = bid.vastContent; - const divID = '#' + elementId; + const divID = '[id="' + elementId + '"]'; var script = document.createElement('script'); script.src = 'https://dco.smartclip.net/?plc=7777778'; script.type = 'text/javascript'; From de4098269bb2b15e285fb1b7c9b1b383d2cd19f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20DEYM=C3=88S?= <47388595+MaxSmileWanted@users.noreply.github.com> Date: Fri, 16 Apr 2021 03:46:50 +0200 Subject: [PATCH 0823/1476] Update cookie sync call (#6567) --- modules/smilewantedBidAdapter.js | 31 ++++++++++++++----- .../modules/smilewantedBidAdapter_spec.js | 10 +++--- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index f965310abdd..fb05298a230 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -108,16 +108,31 @@ export const spec = { * @param {*} serverResponses A successful response from the server. * @return {Syncs[]} An array of syncs that should be executed. */ - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = [] - if (syncOptions.iframeEnabled && serverResponses.length > 0) { - if (serverResponses[0].body.cSyncUrl === 'https://csync.smilewanted.com') { - syncs.push({ - type: 'iframe', - url: serverResponses[0].body.cSyncUrl - }); + getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { + let params = ''; + + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + // add 'gdpr' only if 'gdprApplies' is defined + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + params += `?gdpr_consent=${gdprConsent.consentString}`; } } + + if (uspConsent) { + params += `${params ? '&' : '?'}us_privacy=${encodeURIComponent(uspConsent)}`; + } + + const syncs = [] + + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: 'https://csync.smilewanted.com' + params + }); + } + return syncs; } } diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 0ac242ce0e1..d0d8b65a42f 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -305,12 +305,12 @@ describe('smilewantedBidAdapterTests', function () { }); it('SmileWanted - Verify user sync', function () { - var syncs = spec.getUserSyncs({ - iframeEnabled: true - }, [BID_RESPONSE_DISPLAY]); + var syncs = spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: 'foo' + }, '1NYN'); expect(syncs).to.have.lengthOf(1); expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://csync.smilewanted.com'); + expect(syncs[0].url).to.equal('https://csync.smilewanted.com?gdpr_consent=foo&us_privacy=1NYN'); syncs = spec.getUserSyncs({ iframeEnabled: false @@ -320,6 +320,6 @@ describe('smilewantedBidAdapterTests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true }, []); - expect(syncs).to.have.lengthOf(0); + expect(syncs).to.have.lengthOf(1); }); }); From 8745d462ad2e8c95e2a90fd80926c3a7dfcdcdf6 Mon Sep 17 00:00:00 2001 From: Nick Peceniak Date: Fri, 16 Apr 2021 03:52:49 -0600 Subject: [PATCH 0824/1476] Add videoCacheKey back to bid response when using spotx as cache server (#6605) Co-authored-by: Nick Peceniak --- modules/spotxBidAdapter.js | 1 + test/spec/modules/spotxBidAdapter_spec.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 4c9b50ca9db..f3728058d18 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -352,6 +352,7 @@ export const spec = { } else { bid.cache_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; } bid.meta = bid.meta || {}; diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 873914441aa..cc94e37fdaa 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -495,6 +495,7 @@ describe('the spotx adapter', function () { expect(responses[0].requestId).to.equal(123); expect(responses[0].ttl).to.equal(360); expect(responses[0].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache123'); + expect(responses[0].videoCacheKey).to.equal('cache123'); expect(responses[0].width).to.equal(400); expect(responses[1].cache_key).to.equal('cache124'); expect(responses[1].channel_id).to.equal(12345); @@ -508,6 +509,7 @@ describe('the spotx adapter', function () { expect(responses[1].requestId).to.equal(124); expect(responses[1].ttl).to.equal(360); expect(responses[1].vastUrl).to.equal('https://search.spotxchange.com/ad/vast.html?key=cache124'); + expect(responses[1].videoCacheKey).to.equal('cache124'); expect(responses[1].width).to.equal(200); }); }); From f65fe744bc4b80990d83e699e00b92436ce92f3e Mon Sep 17 00:00:00 2001 From: jsfledd Date: Fri, 16 Apr 2021 07:48:09 -0700 Subject: [PATCH 0825/1476] Nativo Bid Adapter: add new bid adapter (#6542) * 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. --- modules/nativoBidAdapter.js | 307 +++++++++++++++++++++ modules/nativoBidAdapter.md | 40 +++ test/spec/modules/nativoBidAdapter_spec.js | 227 +++++++++++++++ 3 files changed, 574 insertions(+) create mode 100644 modules/nativoBidAdapter.js create mode 100644 modules/nativoBidAdapter.md create mode 100644 test/spec/modules/nativoBidAdapter_spec.js diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js new file mode 100644 index 00000000000..d396bd4d495 --- /dev/null +++ b/modules/nativoBidAdapter.js @@ -0,0 +1,307 @@ +import * as utils from '../src/utils.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER } from '../src/mediaTypes.js' +// import { config } from 'src/config' + +const BIDDER_CODE = 'nativo' +const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid' + +const TIME_TO_LIVE = 360 + +const SUPPORTED_AD_TYPES = [BANNER] + +const bidRequestMap = {} + +// Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html + +export const spec = { + code: BIDDER_CODE, + aliases: ['ntv'], // short code + supportedMediaTypes: SUPPORTED_AD_TYPES, + + /** + * 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 && !!bid.params.placementId + }, + + /** + * Called when the page asks Prebid.js for bids + * Make a server request from the list of BidRequests + * + * @param {Array} validBidRequests - An array of bidRequest objects, one for each AdUnit that your module is involved in. This array has been processed for special features like sizeConfig, so it’s the list that you should be looping through + * @param {Object} bidderRequest - The master bidRequest object. This object is useful because it carries a couple of bid parameters that are global to all the bids. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + const placementIds = [] + const placmentBidIdMap = {} + let placementId, pageUrl + validBidRequests.forEach((request) => { + pageUrl = pageUrl || request.params.url // Use the first url value found + placementId = request.params.placementId + placementIds.push(placementId) + placmentBidIdMap[placementId] = { + bidId: request.bidId, + size: getLargestSize(request.sizes), + } + }) + bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap + + if (!pageUrl) pageUrl = bidderRequest.refererInfo.referer + + let params = [ + { key: 'ntv_ptd', value: placementIds.toString() }, + { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + { + key: 'ntv_url', + value: encodeURIComponent(pageUrl), + }, + ] + + if (bidderRequest.gdprConsent) { + // Put on the beginning of the qs param array + params.unshift({ + key: 'ntv_gdpr_consent', + value: bidderRequest.gdprConsent.consentString, + }) + } + + if (bidderRequest.uspConsent) { + // Put on the beginning of the qs param array + params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) + } + + let serverRequest = { + method: 'GET', + url: BIDDER_ENDPOINT + arrayToQS(params), + } + + return serverRequest + }, + + /** + * Will be called when the browser has received the response from your server. + * The function will parse the response and create a bidResponse object containing one or more bids. + * The adapter should indicate no valid bids by returning an empty array. + * + * @param {Object} response - Data returned from the bidding server request endpoint + * @param {Object} request - The request object used to call the server request endpoint + * @return {Array} An array of bids which were nested inside the server. + */ + interpretResponse: function (response, request) { + // If the bid response was empty, return [] + if (!response || !response.body || utils.isEmpty(response.body)) return [] + + try { + const body = + typeof response.body === 'string' + ? JSON.parse(response.body) + : response.body + + const bidResponses = [] + const seatbids = body.seatbid + + // Step through and grab pertinent data + let bidResponse, adUnit + seatbids.forEach((seatbid) => { + seatbid.bid.forEach((bid) => { + adUnit = this.getRequestId(body.id, bid.impid) + bidResponse = { + requestId: adUnit.bidId, + cpm: bid.price, + currency: body.cur, + width: bid.w || adUnit.size[0], + height: bid.h || adUnit.size[1], + creativeId: bid.crid, + dealId: bid.id, + netRevenue: true, + ttl: bid.ttl || TIME_TO_LIVE, + ad: bid.adm, + meta: { + advertiserDomains: bid.adomain, + }, + } + + bidResponses.push(bidResponse) + }) + }) + + // Don't need the map anymore as it was unique for one request/response + delete bidRequestMap[body.id] + + return bidResponses + } catch (error) { + // If there is an error, return [] + return [] + } + }, + + /** + * All user ID sync activity should be done using the getUserSyncs callback of the BaseAdapter model. + * Given an array of all the responses from the server, getUserSyncs is used to determine which user syncs should occur. + * The order of syncs in the serverResponses array matters. The most important ones should come first, since publishers may limit how many are dropped on their page. + * @param {Object} syncOptions - Which user syncs are allowed? + * @param {Array} serverResponses - Array of server's responses + * @param {Object} gdprConsent - GDPR consent data + * @param {Object} uspConsent - USP consent data + * @return {Array} The user syncs which should be dropped. + */ + getUserSyncs: function ( + syncOptions, + serverResponses, + gdprConsent, + uspConsent + ) { + // Generate consent qs string + let params = '' + // GDPR + if (gdprConsent) { + params = appendQSParamString( + params, + 'gdpr', + gdprConsent.gdprApplies ? 1 : 0 + ) + params = appendQSParamString( + params, + 'gdpr_consent', + encodeURIComponent(gdprConsent.consentString || '') + ) + } + // CCPA + if (uspConsent) { + params = appendQSParamString( + params, + 'us_privacy', + encodeURIComponent(uspConsent.uspConsent) + ) + } + + // Get sync urls from the respnse and inject cinbsent params + const types = { + iframe: syncOptions.iframeEnabled, + image: syncOptions.pixelEnabled, + } + const syncs = [] + + let body + serverResponses.forEach((response) => { + // If the bid response was empty, return [] + if (!response || !response.body || utils.isEmpty(response.body)) { + return syncs + } + + body = + typeof response.body === 'string' + ? JSON.parse(response.body) + : response.body + + // Make sure we have valid content + if (!body || !body.seatbid || body.seatbid.length === 0) return + + body.seatbid.forEach((seatbid) => { + // Grab the syncs for each seatbid + seatbid.syncUrls.forEach((sync) => { + if (types[sync.type]) { + if (sync.url.trim() !== '') { + syncs.push({ + type: sync.type, + url: sync.url.replace('{GDPR_params}', params), + }) + } + } + }) + }) + }) + + return syncs + }, + + /** + * Will be called when an adpater timed out for an auction. + * Adapter can fire a ajax or pixel call to register a timeout at thier end. + * @param {Object} timeoutData - Timeout specific data + */ + onTimeout: function (timeoutData) {}, + + /** + * Will be called when a bid from the adapter won the auction. + * @param {Object} bid - The bid that won the auction + */ + onBidWon: function (bid) {}, + + /** + * Will be called when the adserver targeting has been set for a bid from the adapter. + * @param {Object} bidder - The bid of which the targeting has been set + */ + onSetTargeting: function (bid) {}, + + /** + * Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId + * @param {String} bidderRequestId - The unique ID value associated with the bidderRequest + * @param {String} placementId - The placement ID value from Nativo + * @returns {String} - The bidId value associated with the corresponding placementId + */ + getRequestId: function (bidderRequestId, placementId) { + return ( + bidRequestMap[bidderRequestId] && + bidRequestMap[bidderRequestId][placementId] + ) + }, +} +registerBidder(spec) + +// Utils +/** + * Append QS param to existing string + * @param {String} str - String to append to + * @param {String} key - Key to append + * @param {String} value - Value to append + * @returns + */ +function appendQSParamString(str, key, value) { + return str + `${str.length ? '&' : ''}${key}=${value}` +} + +/** + * Convert an object to query string parameters + * @param {Object} obj - Object to convert + * @returns + */ +function arrayToQS(arr) { + return ( + '?' + + arr.reduce((value, obj) => { + return appendQSParamString(value, obj.key, obj.value) + }, '') + ) +} + +/** + * Get the largest size array + * @param {Array} sizes - Array of size arrays + * @returns Size array with the largest area + */ +function getLargestSize(sizes, method = area) { + if (!sizes || sizes.length === 0) return [] + if (sizes.length === 1) return sizes[0] + + return sizes.reduce((prev, current) => { + if (method(current) > method(prev)) { + return current + } else { + return prev + } + }) +} + +/** + * Calculate the area + * @param {Array} size - [width, height] + * @returns The calculated area + */ +const area = (size) => size[0] * size[1] diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md new file mode 100644 index 00000000000..ec0980aae50 --- /dev/null +++ b/modules/nativoBidAdapter.md @@ -0,0 +1,40 @@ +# Overview + +``` +Module Name: Nativo Bid Adapter +Module Type: Bidder Adapter +Maintainer: prebiddev@nativo.com +``` + +# Description + +Module that connects to Nativo's demand sources + +# Dev + +gulp serve --modules=nativoBidAdapter + +# Test Parameters + +``` +var adUnits = [ + { + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'nativo', + params: { + placementId: 1125609, + url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html' + } + }] + + } + ]; + +``` diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js new file mode 100644 index 00000000000..e1132bf1b74 --- /dev/null +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -0,0 +1,227 @@ +import { expect } from 'chai' +import { spec } from 'modules/nativoBidAdapter.js' +// import { newBidder } from 'src/adapters/bidderFactory.js' +// import * as bidderFactory from 'src/adapters/bidderFactory.js' +// import { deepClone } from 'src/utils.js' +// import { config } from 'src/config.js' + +describe('nativoBidAdapterTests', function () { + describe('isBidRequestValid', function () { + let bid = { + bidder: 'nativo', + params: { + placementId: '10433394', + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + } + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + let bid2 = Object.assign({}, bid) + delete bid2.params + bid2.params = {} + expect(spec.isBidRequestValid(bid2)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: 'nativo', + params: { + placementId: '10433394', + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '27b02036ccfa6e', + bidderRequestId: '1372cd8bd8d6a8', + auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114', + transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff', + }, + ] + + it('url should contain query string parameters', function () { + 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.include('?') + expect(request.url).to.include('ntv_url') + expect(request.url).to.include('ntv_ptd') + }) + }) +}) + +describe('interpretResponse', function () { + let response = { + id: '126456', + seatbid: [ + { + seat: 'seat_0', + bid: [ + { + id: 'f70362ac-f3cf-4225-82a5-948b690927a6', + impid: '1', + price: 3.569, + adm: '', + h: 300, + w: 250, + cat: [], + adomain: ['test.com'], + crid: '1060_72_6760217', + }, + ], + }, + ], + cur: 'USD', + } + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '1F254428-AB11-4D5E-9887-567B3F952CA5', + cpm: 3.569, + currency: 'USD', + width: 300, + height: 250, + creativeId: '1060_72_6760217', + dealId: 'f70362ac-f3cf-4225-82a5-948b690927a6', + netRevenue: true, + ttl: 360, + ad: '', + meta: { + advertiserDomains: ['test.com'], + }, + }, + ] + + let bidderRequest = { + id: 123456, + bids: [ + { + params: { + placementId: 1 + } + }, + ], + } + + // mock + spec.getRequestId = () => 123456 + + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + expect(Object.keys(result[0])).to.have.deep.members( + Object.keys(expectedResponse[0]) + ) + }) + + it('handles nobid responses', function () { + let response = {} + let bidderRequest + + let result = spec.interpretResponse({ body: response }, { bidderRequest }) + expect(result.length).to.equal(0) + }) +}) + +describe('getUserSyncs', function () { + const response = [ + { + body: { + cur: 'USD', + id: 'a136dbd8-4387-48bf-b8e4-ff9c1d6056ee', + seatbid: [ + { + bid: [{}], + seat: 'seat_0', + syncUrls: [ + { + type: 'image', + url: 'pixel-tracker-test-url/?{GDPR_params}', + }, + { + type: 'iframe', + url: 'iframe-tracker-test-url/?{GDPR_params}', + }, + ], + }, + ], + }, + }, + ] + + const gdprConsent = { + gdprApplies: true, + consentString: '111111' + } + + const uspConsent = { + uspConsent: '1YYY' + } + + it('Returns empty array if no supported user syncs', function () { + let userSync = spec.getUserSyncs( + { + iframeEnabled: false, + pixelEnabled: false, + }, + response, + gdprConsent, + uspConsent + ) + expect(userSync).to.be.an('array').with.lengthOf(0) + }) + + it('Returns valid iframe user sync', function () { + let userSync = spec.getUserSyncs( + { + iframeEnabled: true, + pixelEnabled: false, + }, + response, + gdprConsent, + uspConsent + ) + 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.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + }) + + it('Returns valid URL and type', function () { + let userSync = spec.getUserSyncs( + { + iframeEnabled: false, + pixelEnabled: true, + }, + response, + gdprConsent, + uspConsent + ) + 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.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + }) +}) From 1b28481afff549fbe11e08b138ae58644ffe5d6d Mon Sep 17 00:00:00 2001 From: Nick Peceniak Date: Fri, 16 Apr 2021 09:58:50 -0600 Subject: [PATCH 0826/1476] Spotx Bid Adapter: Update endpoint to indicate request is from Prebid (#6593) --- modules/spotxBidAdapter.js | 3 ++- test/spec/modules/spotxBidAdapter_spec.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index f3728058d18..b60d25db4d6 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -291,9 +291,10 @@ export const spec = { if (!utils.isEmpty(userExt)) { requestPayload.user = { ext: userExt }; } + const urlQueryParams = 'src_sys=prebid' return { method: 'POST', - url: URL + channelId, + url: URL + channelId + '?' + urlQueryParams, data: requestPayload, bidRequest: bidderRequest }; diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index cc94e37fdaa..5d7b32eaeeb 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -102,7 +102,7 @@ describe('the spotx adapter', function () { it('should build a very basic request', function() { var 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'); + expect(request.url).to.equal('https://search.spotxchange.com/openrtb/2.3/dados/12345?src_sys=prebid'); expect(request.bidRequest).to.equal(bidRequestObj); expect(request.data.id).to.equal(12345); expect(request.data.ext.wrap_response).to.equal(1); From dd64734e46b5f9907cd14a8e2b287ac1cfe12aad Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 16 Apr 2021 13:16:53 -0700 Subject: [PATCH 0827/1476] Sharethrough Bid Adapter: add support for COPPA (#6602) * Pass COPPA flag to Ad Server [#177598971] * Send true instead of 1 * Upgrade adapter version number --- modules/sharethroughBidAdapter.js | 7 +++++- .../modules/sharethroughBidAdapter_spec.js | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 24be8673615..eef18288b17 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,7 +1,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; -const VERSION = '3.3.1'; +const VERSION = '3.3.2'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -48,6 +49,10 @@ export const sharethroughAdapterSpec = { query.us_privacy = bidderRequest.uspConsent } + if (config.getConfig('coppa') === true) { + query.coppa = true + } + if (bidRequest.schain) { query.schain = JSON.stringify(bidRequest.schain); } diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index b3451a09dde..57306580ecc 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -2,6 +2,7 @@ import { expect } from 'chai'; import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from '../../../src/utils.js'; +import { config } from 'src/config'; const spec = newBidder(sharethroughAdapterSpec).getSpec(); const bidRequests = [ @@ -441,6 +442,29 @@ describe('sharethrough adapter spec', function() { const builtBidRequest = spec.buildRequests([bidRequest])[0]; expect(builtBidRequest.data).to.not.include.any.keys('bidfloor'); }); + + describe('coppa', function() { + it('should add coppa to request if enabled', function() { + config.setConfig({coppa: true}); + const bidRequest = Object.assign({}, bidRequests[0]); + const builtBidRequest = spec.buildRequests([bidRequest])[0]; + expect(builtBidRequest.data.coppa).to.eq(true); + }); + + it('should not add coppa to request if disabled', function() { + config.setConfig({coppa: false}); + const bidRequest = Object.assign({}, bidRequests[0]); + const builtBidRequest = spec.buildRequests([bidRequest])[0]; + expect(builtBidRequest.data.coppa).to.be.undefined; + }); + + it('should not add coppa to request if unknown value', function() { + config.setConfig({coppa: 'something'}); + const bidRequest = Object.assign({}, bidRequests[0]); + const builtBidRequest = spec.buildRequests([bidRequest])[0]; + expect(builtBidRequest.data.coppa).to.be.undefined; + }); + }); }); describe('.interpretResponse', function() { From ef00f9b681b7b2f4d507fd6e359d01a726facbbc Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Fri, 16 Apr 2021 22:27:47 +0200 Subject: [PATCH 0828/1476] tappx Bid Adapter: add video instream support and update testing (#6580) * tappxBidAdapter : update tests adding video * tappxBidAdapter : add video instream * tappxBidAdapter : update tappx md doc * tappxBidAdapter: Fix Newline required eol-last * tappxBidAdapter: update tests User sync and video reqs * tappxBidAdapter: Extra space after fix Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 81 +++++++++++++---- modules/tappxBidAdapter.md | 33 +++++++ test/spec/modules/tappxBidAdapter_spec.js | 102 ++++++++++++++++++++-- 3 files changed, 195 insertions(+), 21 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 3677c3ce4c9..1228fbafaad 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -2,20 +2,22 @@ import * as utils 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 { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10329'; +const TAPPX_BIDDER_VERSION = '0.1.10413'; const TYPE_CNN = 'prebidjs'; +const VIDEO_SUPPORT = ['instream']; + var HOST; var hostDomain; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -24,11 +26,7 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - if ((bid.params == null) || (bid.params.endpoint == null) || (bid.params.tappxkey == null)) { - utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters. ${JSON.stringify(bid)}`); - return false; - } - return true; + return validBasic(bid) && validMediaType(bid) }, /** @@ -63,7 +61,7 @@ export const spec = { const bids = []; responseBody.seatbid.forEach(serverSeatBid => { serverSeatBid.bid.forEach(serverBid => { - bids.push(interpretBannerBid(serverBid, originalRequest)); + bids.push(interpretBid(serverBid, originalRequest)); }); }); @@ -106,25 +104,66 @@ export const spec = { } } +function validBasic(bid) { + if ( + (bid.params == null) || + (bid.params.endpoint == null) || + (bid.params.tappxkey == null)) { + utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters.`); + return false; + } + return true; +} + +function validMediaType(bid) { + const video = utils.deepAccess(bid, 'mediaTypes.video'); + + // Video validations + if (typeof video != 'undefined') { + if (VIDEO_SUPPORT.indexOf(video.context) === -1) { + utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters for Video. Only "instream" is suported.`); + return false; + } + } + + return true; +} + /** * Parse the response and generate one bid object. * * @param {object} serverBid Bid by OpenRTB 2.5 * @returns {object} Prebid banner bidObject */ -function interpretBannerBid(serverBid, request) { - return { +function interpretBid(serverBid, request) { + let bidReturned = { requestId: request.bids.bidId, cpm: serverBid.price, currency: serverBid.cur ? serverBid.cur : CUR, width: serverBid.w, height: serverBid.h, - ad: serverBid.adm, ttl: TTL, creativeId: serverBid.crid, netRevenue: true, - mediaType: BANNER, } + + if (typeof serverBid.dealId != 'undefined') { bidReturned.dealId = serverBid.dealId } + + if (typeof request.bids.mediaTypes != 'undefined' && typeof request.bids.mediaTypes.video != 'undefined') { + bidReturned.vastXml = serverBid.adm; + bidReturned.vastUrl = serverBid.lurl; + bidReturned.ad = serverBid.adm; + bidReturned.mediaType = VIDEO; + } else { + bidReturned.ad = serverBid.adm; + bidReturned.mediaType = BANNER; + } + + if (typeof bidReturned.adomain != 'undefined' || bidReturned.adomain != null) { + bidReturned.meta = { advertiserDomains: request.bids.adomain }; + } + + return bidReturned; } /** @@ -136,14 +175,14 @@ function interpretBannerBid(serverBid, request) { */ function buildOneRequest(validBidRequests, bidderRequest) { HOST = utils.deepAccess(validBidRequests, 'params.host'); - let hostInfo = getHostInfo(HOST) - // hostDomain = HOST.split('/', 1)[0]; + let hostInfo = getHostInfo(HOST); hostDomain = hostInfo.domain; const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); const bannerMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.banner'); + const videoMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.video'); const { refererInfo } = bidderRequest; // let requests = []; @@ -205,6 +244,18 @@ function buildOneRequest(validBidRequests, bidderRequest) { imp.banner = banner; } + if (videoMediaType) { + let video = {}; + w = videoMediaType.playerSize[0][0]; + h = videoMediaType.playerSize[0][1]; + video.w = w; + video.h = h; + + video.mimes = videoMediaType.mimes; + + imp.video = video; + } + imp.id = validBidRequests.bidId; imp.tagid = tagid; imp.secure = 1; diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index d9ffd98b6c5..e6581a67d06 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -7,6 +7,7 @@ Maintainer: prebid@tappx.com # Description Module that connects to :tappx demand sources. +Suppots Banner and Instream Video. Please use ```tappx``` as the bidder code. Ads sizes available: [320,50], [300,250], [320,480], [1024,768], [728,90] @@ -35,3 +36,35 @@ Ads sizes available: [320,50], [300,250], [320,480], [1024,768], [728,90] } ]; ``` + + +# Video Test Parameters +``` + var adUnits = [ + { + code: 'video-ad-div', + renderer: { + options: { + text: "Tappx instream Video" + } + }, + mediaTypes: { + video: { + context: "instream", + mimes : [ "video/mp4", "application/javascript" ], + playerSize: [320, 250] + } + }, + bids: [{ + bidder: 'tappx', + params: { + host: "testing.ssp.tappx.com/rtb/v2/", + tappxkey: "pub-1234-desktop-1234", + endpoint: "VZ12TESTCTV", + bidfloor: 0.005, + test: true + } + }] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 1d3f9676d09..378b391a4eb 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -53,7 +53,7 @@ const c_BIDREQUEST = { } ] }; -const c_SERVERRESPONSE = { +const c_SERVERRESPONSE_B = { body: { id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', bidid: 'bid3811165568213389257', @@ -82,10 +82,40 @@ const c_SERVERRESPONSE = { }, headers: {} }; + +const c_SERVERRESPONSE_V = { + body: { + id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + bidid: 'bid3811165568213389257', + seatbid: [ + { + seat: '1', + group: 0, + bid: [ + { + id: '3811165568213389257', + impid: 1, + price: 0.05, + adm: "Tappx<\/AdSystem>Tappx<\/AdTitle><\/Impression><\/Error>00:00:22<\/Duration><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/TrackingEvents><\/ClickThrough><\/ClickTracking><\/VideoClicks><\/MediaFile><\/MediaFiles><\/Linear><\/Creative><\/Creatives><\/InLine><\/Ad><\/VAST>", + 'lurl': 'https:\/\/ssp.api.tappx.com\/rtb\/RTBv2Loss?id=5001829913749291152&ep=VZ12TESTCTV&au=test&bu=localhost&sz=6x6&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', + cid: '01744fbb521e9fb10ffea926190effea', + crid: 'a13cf884e66e7c660afec059c89d98b6', + adomain: [ + ], + }, + ], + }, + ], + cur: 'USD', + }, + headers: {} +}; + const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; -const c_BIDDERREQUEST = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; +const c_BIDDERREQUEST_B = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; +const c_BIDDERREQUEST_V = {'method': 'POST', 'url': 'https://testing.ssp.tappx.com/rtb/v2//VZ12TESTCTV?type_cnn=prebidjs&v=0.1.10329', 'data': '{"site":{"name":"localhost","bundle":"localhost","domain":"localhost"},"user":{"ext":{}},"id":"e807363f-3095-43a8-a4a6-f44196cb7318","test":1,"at":1,"tmax":1000,"bidder":"tappx","imp":[{"video":{"w":320,"h":250,"mimes":["video/mp4","application/javascript"]},"id":"28f49c71b13f2f","tagid":"localhost_typeAdBanVid_windows","secure":1,"bidfloor":0.005,"ext":{"bidder":{"tappxkey":"pub-1234-desktop-1234","endpoint":"VZ12TESTCTV","host":"testing.ssp.tappx.com/rtb/v2/"}}}],"device":{"os":"windows","ip":"peer","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36","h":864,"w":1536,"dnt":0,"language":"en","make":"Google Inc."},"params":{"host":"tappx.com","bidfloor":0.005},"regs":{"gdpr":0,"ext":{}}}', 'bids': {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com/rtb/v2/', 'tappxkey': 'pub-1234-desktop-1234', 'endpoint': 'VZ12TESTCTV', 'bidfloor': 0.005, 'test': true}, 'crumbs': {'pubcid': 'dccfe922-3823-4676-b7b2-e5ed8743154e'}, 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video-ad-div'}}}, 'renderer': {'options': {'text': 'Tappx Outstream Video'}}, 'mediaTypes': {'video': {'context': 'instream', 'mimes': ['video/mp4', 'application/javascript'], 'playerSize': [[320, 250]]}}, 'adUnitCode': 'video-ad-div', 'transactionId': 'ed41c805-d14c-49c3-954d-26b98b2aa2c2', 'sizes': [[320, 250]], 'bidId': '28f49c71b13f2f', 'bidderRequestId': '1401710496dc7', 'auctionId': 'e807363f-3095-43a8-a4a6-f44196cb7318', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}} describe('Tappx bid adapter', function () { /** @@ -102,6 +132,16 @@ describe('Tappx bid adapter', function () { delete badBidRequest.bids[0].params.endpoint; assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); }); + + it('should return false for not instream requests', function () { + let badBidRequest_v = c_BIDDERREQUEST_V; + delete badBidRequest_v.bids.mediaTypes.banner; + badBidRequest_v.bids.mediaTypes.video = {}; + badBidRequest_v.bids.mediaTypes.video.context = 'outstream'; + badBidRequest_v.bids.mediaTypes.video.mimes = [ 'video/mp4', 'application/javascript' ]; + badBidRequest_v.bids.mediaTypes.video.playerSize = [320, 250]; + assert.isFalse(spec.isBidRequestValid(badBidRequest_v.bids)); + }); }); /** @@ -110,10 +150,12 @@ describe('Tappx bid adapter', function () { describe('buildRequest', function () { // Web Test let validBidRequests = c_VALIDBIDREQUESTS; + let validBidRequests_V = c_VALIDBIDREQUESTS; // App Test let validAppBidRequests = c_VALIDBIDAPPREQUESTS; - let bidderRequest = c_BIDDERREQUEST; + let bidderRequest = c_BIDDERREQUEST_B; + let bidderRequest_V = c_BIDDERREQUEST_V; it('should add gdpr/usp consent information to the request', function () { const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -138,6 +180,20 @@ describe('Tappx bid adapter', function () { expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); }); + it('should properly build a video request', function () { + const request = spec.buildRequests(validBidRequests_V, bidderRequest_V); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + it('should set user eids array', function () { const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -165,17 +221,51 @@ describe('Tappx bid adapter', function () { * INTERPRET RESPONSE TESTS */ describe('interpretResponse', function () { - it('receive reponse with single placement', function () { - const bids = spec.interpretResponse(c_SERVERRESPONSE, c_BIDREQUEST); + it('receive banner reponse with single placement', function () { + const bids = spec.interpretResponse(c_SERVERRESPONSE_B, c_BIDDERREQUEST_B); const bid = bids[0]; expect(bid.cpm).to.exist; expect(bid.ad).to.match(/^' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + adm: '', + w: 300, + h: 250, + adomain: ['adomain.com'], + cat: ['IAB1-4', 'IAB8-16', 'IAB25-5'], + ext: { + crType: 'banner', + advertiser_id: '777', + advertiser_name: 'advertiser', + agency_name: 'agency' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; let BANNER_RESPONSE_WITHDEALID = { - body: [ - { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 300, - height: 250, - deal_id: '7722', - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - ad: '' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + dealid: '7722', + adm: '', + w: 300, + h: 250, + adomain: ['adomain.com'], + ext: { + crType: 'banner' + } + } + ] + }], + cur: 'USD' + } }; let VIDEO_RESPONSE = { - body: [ - { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 640, - height: 360, - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - vastUrl: 'vastContentUrl' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + nurl: 'vastContentUrl', + adomain: ['adomain.com'], + w: 640, + h: 360, + ext: { + crType: 'video' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; - let NATIVE_RESPONSE = { - body: [ + let NATIVE_RESPONSEob = { + assets: [ { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 0, - height: 0, - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - nativeResponse: { - assets: [ - { - id: 1, - required: 0, - title: { - text: 'titleContent' - } - }, - { - id: 2, - required: 0, - img: { - url: 'imageContent', - w: 600, - h: 600 - } - }, - { - id: 3, - required: 0, - data: { - label: 'DESC', - value: 'descriptionContent' - } - }, - { - id: 0, - required: 0, - data: { - label: 'SPONSORED', - value: 'sponsoredByContent' - } - }, - { - id: 5, - required: 0, - icon: { - url: 'iconContent', - w: 400, - h: 400 - } - } - ], - link: { - url: 'linkContent' - }, - imptrackers: ['impressionTracker1', 'impressionTracker2'] + id: 1, + required: 0, + title: { + text: 'titleContent' + } + }, + { + id: 2, + required: 0, + img: { + url: 'imageContent', + w: 600, + h: 600 + } + }, + { + id: 3, + required: 0, + data: { + label: 'DESC', + value: 'descriptionContent' + } + }, + { + id: 0, + required: 0, + data: { + label: 'SPONSORED', + value: 'sponsoredByContent' + } + }, + { + id: 5, + required: 0, + icon: { + url: 'iconContent', + w: 400, + h: 400 } } ], - header: {someheader: 'fakedata'} + link: { + url: 'linkContent' + }, + imptrackers: ['impressionTracker1', 'impressionTracker2'] + } + + let NATIVE_RESPONSE = { + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + w: 0, + h: 0, + adm: JSON.stringify(NATIVE_RESPONSEob), + adomain: ['adomain.com'], + ext: { + crType: 'native' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; it('handles regular responses', function () { + expect(BANNER_RESPONSE).to.exist; + expect(BANNER_RESPONSE.body).to.exist; + expect(BANNER_RESPONSE.body.id).to.exist; + expect(BANNER_RESPONSE.body.seatbid[0]).to.exist; let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST); expect(result).to.have.lengthOf(1); @@ -452,26 +490,30 @@ describe('AdxcgAdapter', function () { expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(250); expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); + expect(result[0].cpm).to.be.within(0.45, 0.46); expect(result[0].ad).to.equal(''); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); expect(result[0].ttl).to.equal(300); expect(result[0].dealId).to.not.exist; + expect(result[0].meta.advertiserDomains[0]).to.equal('adomain.com'); + expect(result[0].meta.advertiserId).to.be.eql('777'); + expect(result[0].meta.advertiserName).to.be.eql('advertiser'); + expect(result[0].meta.agencyName).to.be.eql('agency'); + expect(result[0].meta.advertiserDomains).to.be.eql(['adomain.com']); + expect(result[0].meta.secondaryCatIds).to.be.eql(['IAB1-4', 'IAB8-16', 'IAB25-5']); }); it('handles regular responses with dealid', function () { - let result = spec.interpretResponse( - BANNER_RESPONSE_WITHDEALID, - BIDDER_REQUEST - ); + let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID); expect(result).to.have.lengthOf(1); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(250); expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); + // expect(result[0].cpm).to.equal(0.45); + expect(result[0].cpm).to.be.within(0.45, 0.46); expect(result[0].ad).to.equal(''); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); @@ -479,7 +521,7 @@ describe('AdxcgAdapter', function () { }); it('handles video responses', function () { - let result = spec.interpretResponse(VIDEO_RESPONSE, BIDDER_REQUEST); + let result = spec.interpretResponse(VIDEO_RESPONSE); expect(result).to.have.lengthOf(1); expect(result[0].width).to.equal(640); @@ -494,17 +536,19 @@ describe('AdxcgAdapter', function () { }); it('handles native responses', function () { - let result = spec.interpretResponse(NATIVE_RESPONSE, BIDDER_REQUEST); + let result = spec.interpretResponse(NATIVE_RESPONSE); expect(result[0].width).to.equal(0); expect(result[0].height).to.equal(0); - expect(result[0].mediaType).to.equal('native'); + expect(result[0].creativeId).to.equal(42); expect(result[0].cpm).to.equal(0.45); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); expect(result[0].ttl).to.equal(300); + expect(result[0].mediaType).to.equal('native'); + expect(result[0].native.clickUrl).to.equal('linkContent'); expect(result[0].native.impressionTrackers).to.deep.equal([ 'impressionTracker1', @@ -545,4 +589,65 @@ describe('AdxcgAdapter', function () { ); }); }); + + describe('on bidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function () { + utils.triggerPixel.restore(); + }); + it('should replace burl for banner', function () { + const burl = 'burl=${' + 'AUCTION_PRICE}'; + const bid = { + 'bidderCode': 'adxcg', + 'width': 0, + 'height': 0, + 'statusMessage': 'Bid available', + 'adId': '3d0b6ff1dda89', + 'requestId': '2a423489e058a1', + 'mediaType': 'banner', + 'source': 'client', + 'ad': burl, + 'cpm': 0.66, + 'creativeId': '353538_591471', + 'currency': 'USD', + 'dealId': '', + 'netRevenue': true, + 'ttl': 300, + // 'nurl': nurl, + 'burl': burl, + 'isBurl': true, + 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', + 'responseTimestamp': 1556867386065, + 'requestTimestamp': 1556867385916, + 'bidder': 'adxcg', + 'adUnitCode': 'div-gpt-ad-1555415275793-0', + 'timeToRespond': 149, + 'pbLg': '0.50', + 'pbMg': '0.60', + 'pbHg': '0.66', + 'pbAg': '0.65', + 'pbDg': '0.66', + 'pbCg': '', + 'size': '0x0', + 'adserverTargeting': { + 'hb_bidder': 'mgid', + 'hb_adid': '3d0b6ff1dda89', + 'hb_pb': '0.66', + 'hb_size': '0x0', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_banner_title': 'TITLE', + 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', + 'hb_banner_icon': 'IconURL', + 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89' + }, + 'status': 'targetingSet', + 'params': [{'adzoneid': '20'}] + }; + spec.onBidWon(bid); + expect(bid.burl).to.deep.equal(burl); + }); + }); }); From a460125d5f63115d12ebcc27ec6cbab808ab6942 Mon Sep 17 00:00:00 2001 From: mwehr-zeta <70167335+mwehr-zeta@users.noreply.github.com> Date: Wed, 21 Apr 2021 07:33:45 -0400 Subject: [PATCH 0846/1476] Zeta bid adapter: add params to bid request (#6614) * Submit Zeta Adapter to Prebid * comments addressed * demo changes * additional polishing * additional polishing * Update hello_world.html * remove extraneous changes to hello_world.html * no, really this time * additional polishing * add unit test * update to include additional OpenRTB fields and objects * Update to include addtional OpenRTB fields and objects --- modules/zetaBidAdapter.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index 09d631b3d18..b168bf581d0 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -71,12 +71,22 @@ export const spec = { }; let payload = { id: bidderRequest.auctionId, - cur: [DEFAULT_CUR], imp: [impData], site: params.site ? params.site : {}, + app: params.app ? params.app : {}, device: params.device ? params.device : {}, user: params.user ? params.user : {}, - app: params.app ? params.app : {}, + at: params.at, + tmax: params.tmax, + wseat: params.wseat, + bseat: params.bseat, + allimps: params.allimps, + cur: [DEFAULT_CUR], + wlang: params.wlang, + bcat: params.bcat, + badv: params.badv, + bapp: params.bapp, + source: params.source ? params.source : {}, ext: { definerId: params.definerId } From 8ca05946a63863378cfb7d7da155d10979843c01 Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Wed, 21 Apr 2021 14:44:39 +0300 Subject: [PATCH 0847/1476] oneVideo Bid Adapter: content object mapping bug fix (VDEFECT-5405) (#6633) --- modules/oneVideoBidAdapter.js | 6 ++-- test/spec/modules/oneVideoBidAdapter_spec.js | 34 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 3bf14eb11cb..60d56cfc8ad 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.0.6', + VERSION: '3.0.7', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -312,7 +312,7 @@ function getRequestData(bid, consentData, bidRequest) { } } if (bid.params.video.content && utils.isPlainObject(bid.params.video.content)) { - bidData.imp[0].content = {}; + bidData.site.content = {}; const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; const contentArrayKeys = ['cat']; @@ -324,7 +324,7 @@ function getRequestData(bid, consentData, bidRequest) { (contentObjectKeys.indexOf(contentKey) > -1 && utils.isPlainObject(bid.params.video.content[contentKey])) || (contentArrayKeys.indexOf(contentKey) > -1 && utils.isArray(bid.params.video.content[contentKey]) && bid.params.video.content[contentKey].every(catStr => utils.isStr(catStr)))) { - bidData.imp[0].content[contentKey] = bid.params.video.content[contentKey]; + bidData.site.content[contentKey] = bid.params.video.content[contentKey]; } else { utils.logMessage('oneVideo bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); } diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 9ee1045a6e8..903bc191b47 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -221,7 +221,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.0.6'; + const VERSION = '3.0.7'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -387,37 +387,37 @@ describe('OneVideoBidAdapter', function () { bidRequest.params.video.content = null; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.undefined; + expect(data.site.content).to.be.undefined; }); it('should not accept content object if value is is Array ', function () { bidRequest.params.video.content = []; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.undefined; + expect(data.site.content).to.be.undefined; }); it('should not accept content object if value is Number ', function () { bidRequest.params.video.content = 123456; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.undefined; + expect(data.site.content).to.be.undefined; }); it('should not accept content object if value is String ', function () { bidRequest.params.video.content = 'keyValuePairs'; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.undefined; + expect(data.site.content).to.be.undefined; }); it('should not accept content object if value is Boolean ', function () { bidRequest.params.video.content = true; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.undefined; + expect(data.site.content).to.be.undefined; }); it('should accept content object if value is Object ', function () { bidRequest.params.video.content = {}; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.a('object'); + expect(data.site.content).to.be.a('object'); }); it('should not append unsupported content object keys', function () { @@ -428,7 +428,7 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.empty; + expect(data.site.content).to.be.empty; }); it('should not append content string parameters if value is not string ', function () { @@ -443,8 +443,8 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.a('object'); - expect(data.imp[0].content).to.be.empty + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty }); it('should not append content Number parameters if value is not Number ', function () { bidRequest.params.video.content = { @@ -456,8 +456,8 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.a('object'); - expect(data.imp[0].content).to.be.empty + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty }); it('should not append content Array parameters if value is not Array ', function () { bidRequest.params.video.content = { @@ -465,8 +465,8 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.a('object'); - expect(data.imp[0].content).to.be.empty + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty }); it('should not append content ext if value is not Object ', function () { bidRequest.params.video.content = { @@ -474,8 +474,8 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.be.a('object'); - expect(data.imp[0].content).to.be.empty + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty }); it('should append supported parameters if value match validations ', function () { bidRequest.params.video.content = { @@ -498,7 +498,7 @@ describe('OneVideoBidAdapter', function () { }; const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; - expect(data.imp[0].content).to.deep.equal(bidRequest.params.video.content); + expect(data.site.content).to.deep.equal(bidRequest.params.video.content); }); }); }); From eff2ae65438e8d580dacf530d83e1997e0c753d9 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 21 Apr 2021 10:26:32 -0400 Subject: [PATCH 0848/1476] PR_REVIEW: added check for bidder name validity (#6491) * added check for bidder name validity * adding aliases in-scope for the validity check --- PR_REVIEW.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 0519cbb7b6e..84131c177a3 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -38,6 +38,10 @@ General gulp commands include separate commands for serving the codebase on a bu Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/bidder-adaptor.html Follow steps above for general review process. In addition, please verify the following: +- Verify the biddercode and aliases are valid: + - Lower case alphanumeric with the only special character allowed is underscore. + - The bidder code should be unique for the first 6 characters + - Reserved words that cannot be used as bidder names: all, context, data, general, prebid, and skadn - Verify that bidder has submitted valid bid params and that bids are being received. - Verify that bidder is not manipulating the prebid.js auction in any way or doing things that go against the principles of the project. If unsure check with the Tech Lead. - Verify that code re-use is being done properly and that changes introduced by a bidder don't impact other bidders. From dc6b450f3e15b5414dd3beb4e156b0116733945a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bendeg=C3=BAz=20=C3=81cs?= <30595431+acsbendi@users.noreply.github.com> Date: Wed, 21 Apr 2021 16:36:02 +0200 Subject: [PATCH 0849/1476] Kobler Bid Adapter: add new bid adapter (#6479) * Implemented Kobler bidder adapter. * Added missing '' to dealId example in parameters table. * Added information on supporting the Floors module. * Implemented tests for isBidRequestValid. * Implemented tests for inherited functions. * Removed unnecessary conditions and quotation marks. * Added TODO about deciding what to do in debug mode. * Added TODO about checking which currencies are allowed. * Added information on parameters read from the first bid only. * Fixed missing indexing operator when checking if mainSize is 0x0. * Implemented tests for buildRequests. * Implemented tests for interpretResponse. * Implemented tests for onBidWon. * Implemented tests for onTimeout. * Added some missing semicolons. * Removed TODO about allowed currencies. * Removed setting test in debug mode and related TODOs. * Removed optional pos parameter. * Removed optional bidfloor parameter and use floorPrice instead of floorprice. * Added support for multiple deal ID parameters. * Fixed formatting. * Added more explanation about the value of position param. * Moved pos property into Kobler-specific banner extension. * Simplifications based on PR comments. * Use getRefererInfo to get page URL for timeout notifications. * Removed TODO about auction type. * Added information on how to generate a sample bid. * Removed reading currency from currency.adServerCurrency. --- modules/koblerBidAdapter.js | 231 +++++++ modules/koblerBidAdapter.md | 67 ++ test/spec/modules/koblerBidAdapter_spec.js | 694 +++++++++++++++++++++ 3 files changed, 992 insertions(+) create mode 100644 modules/koblerBidAdapter.js create mode 100644 modules/koblerBidAdapter.md create mode 100644 test/spec/modules/koblerBidAdapter_spec.js diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js new file mode 100644 index 00000000000..cc5b374af95 --- /dev/null +++ b/modules/koblerBidAdapter.js @@ -0,0 +1,231 @@ +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {getRefererInfo} from '../src/refererDetection.js'; + +const BIDDER_CODE = 'kobler'; +const BIDDER_ENDPOINT = 'https://bid.essrtb.com/bid/prebid_rtb_call'; +const TIMEOUT_NOTIFICATION_ENDPOINT = 'https://bid.essrtb.com/notify/prebid_timeout'; +const SUPPORTED_CURRENCY = 'USD'; +const DEFAULT_TIMEOUT = 1000; +const TIME_TO_LIVE_IN_SECONDS = 10 * 60; + +export const isBidRequestValid = function (bid) { + return !!(bid && bid.bidId && bid.params && bid.params.placementId); +}; + +export const buildRequests = function (validBidRequests, bidderRequest) { + return { + method: 'POST', + url: BIDDER_ENDPOINT, + data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), + options: { + contentType: 'application/json' + } + }; +}; + +export const interpretResponse = function (serverResponse) { + const res = serverResponse.body; + const bids = [] + if (res) { + res.seatbid.forEach(sb => { + sb.bid.forEach(b => { + bids.push({ + requestId: b.impid, + cpm: b.price, + currency: res.cur, + width: b.w, + height: b.h, + creativeId: b.crid, + dealId: b.dealid, + netRevenue: true, + ttl: TIME_TO_LIVE_IN_SECONDS, + ad: b.adm, + nurl: b.nurl, + meta: { + advertiserDomains: b.adomain + } + }) + }) + }); + } + return bids; +}; + +export const onBidWon = function (bid) { + const cpm = bid.cpm || 0; + const adServerPrice = utils.deepAccess(bid, 'adserverTargeting.hb_pb', 0); + if (utils.isStr(bid.nurl) && bid.nurl !== '') { + const winNotificationUrl = utils.replaceAuctionPrice(bid.nurl, cpm) + .replace(/\${AD_SERVER_PRICE}/g, adServerPrice); + utils.triggerPixel(winNotificationUrl); + } +}; + +export const onTimeout = function (timeoutDataArray) { + if (utils.isArray(timeoutDataArray)) { + const refererInfo = getRefererInfo(); + const pageUrl = (refererInfo && refererInfo.referer) + ? refererInfo.referer + : window.location.href; + timeoutDataArray.forEach(timeoutData => { + const query = utils.parseQueryStringParameters({ + ad_unit_code: timeoutData.adUnitCode, + auction_id: timeoutData.auctionId, + bid_id: timeoutData.bidId, + timeout: timeoutData.timeout, + placement_id: utils.deepAccess(timeoutData, 'params.0.placementId'), + page_url: pageUrl, + }); + const timeoutNotificationUrl = `${TIMEOUT_NOTIFICATION_ENDPOINT}?${query}`; + utils.triggerPixel(timeoutNotificationUrl); + }); + } +}; + +function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { + const imps = validBidRequests.map(buildOpenRtbImpObject); + const timeout = bidderRequest.timeout || config.getConfig('bidderTimeout') || DEFAULT_TIMEOUT; + const pageUrl = (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) + ? bidderRequest.refererInfo.referer + : window.location.href; + + const request = { + id: bidderRequest.auctionId, + at: 1, + tmax: timeout, + cur: [SUPPORTED_CURRENCY], + imp: imps, + device: { + devicetype: getDevice(), + geo: getGeo(validBidRequests[0]) + }, + site: { + page: pageUrl, + }, + test: getTest(validBidRequests[0]) + }; + + return JSON.stringify(request); +} + +function buildOpenRtbImpObject(validBidRequest) { + const sizes = getSizes(validBidRequest); + const mainSize = sizes[0]; + const floorInfo = getFloorInfo(validBidRequest, mainSize); + + return { + id: validBidRequest.bidId, + banner: { + format: buildFormatArray(sizes), + w: mainSize[0], + h: mainSize[1], + ext: { + kobler: { + pos: getPosition(validBidRequest) + } + } + }, + tagid: validBidRequest.params.placementId, + bidfloor: floorInfo.floor, + bidfloorcur: floorInfo.currency, + pmp: buildPmpObject(validBidRequest) + }; +} + +function getDevice() { + const ws = utils.getWindowSelf(); + const ua = ws.navigator.userAgent; + + if (/(tablet|ipad|playbook|silk|android 3.0|xoom|sch-i800|kindle)|(android(?!.*mobi))/i + .test(ua.toLowerCase())) { + return 5; // tablet + } + if (/(android|bb\d+|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([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i + .test(ua.toLowerCase())) { + return 4; // phone + } + return 2; // personal computers +} + +function getGeo(validBidRequest) { + if (validBidRequest.params.zip) { + return { + zip: validBidRequest.params.zip + }; + } + return {}; +} + +function getTest(validBidRequest) { + return validBidRequest.params.test ? 1 : 0; +} + +function getSizes(validBidRequest) { + const sizes = utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes', validBidRequest.sizes); + if (utils.isArray(sizes) && sizes.length > 0) { + return sizes; + } + + return [[0, 0]]; +} + +function buildFormatArray(sizes) { + return sizes.map(size => { + return { + w: size[0], + h: size[1] + }; + }); +} + +function getPosition(validBidRequest) { + return parseInt(validBidRequest.params.position) || 0; +} + +function getFloorInfo(validBidRequest, mainSize) { + if (typeof validBidRequest.getFloor === 'function') { + const sizeParam = mainSize[0] === 0 && mainSize[1] === 0 ? '*' : mainSize; + return validBidRequest.getFloor({ + currency: SUPPORTED_CURRENCY, + mediaType: BANNER, + size: sizeParam + }); + } else { + return { + currency: SUPPORTED_CURRENCY, + floor: getFloorPrice(validBidRequest) + }; + } +} + +function getFloorPrice(validBidRequest) { + return parseFloat(validBidRequest.params.floorPrice) || 0.0; +} + +function buildPmpObject(validBidRequest) { + if (validBidRequest.params.dealIds) { + return { + deals: validBidRequest.params.dealIds.map(dealId => { + return { + id: dealId + }; + }) + }; + } + return {}; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + onBidWon, + onTimeout +}; + +registerBidder(spec); diff --git a/modules/koblerBidAdapter.md b/modules/koblerBidAdapter.md new file mode 100644 index 00000000000..7a7b2388367 --- /dev/null +++ b/modules/koblerBidAdapter.md @@ -0,0 +1,67 @@ +# Overview + +**Module Name**: Kobler Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: bidding-support@kobler.no + +# Description + +Connects to Kobler's demand sources. + +This adapter currently only supports Banner Ads. + +# Parameters + +| Parameter (in `params`) | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| placementId | Required | String | The identifier of the placement, it has to be issued by Kobler. | `'xjer0ch8'` | +| zip | Optional | String | Zip code of the user or the medium. When multiple ad units are submitted together, it is enough to set this parameter on the first one. | `'102 22'` | +| test | Optional | Boolean | Whether the request is for testing only. When multiple ad units are submitted together, it is enough to set this parameter on the first one. Defaults to false. | `true` | +| floorPrice | Optional | Float | Floor price in CPM and USD. Can be used as an alternative to the [Floors module](https://docs.prebid.org/dev-docs/modules/floors.html), which is also supported by this adapter. Defaults to 0. | `5.0` | +| position | Optional | Integer | The position of the ad unit. Can be used to differentiate between ad units if the same placement ID is used across multiple ad units. The first ad unit should have a `position` of 0, the second one should have a `position` of 1 and so on. Defaults to 0. | `1` | +| dealIds | Optional | Array of Strings | Array of deal IDs. | `['abc328745', 'mxw243253']` | + +# Test Parameters +```javascript + const adUnits = [{ + code: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + banner: { + sizes: [[320, 250], [300, 250]], + } + }, + bids: [{ + bidder: 'kobler', + params: { + placementId: 'k5H7et3R0' + } + }] + }]; +``` + +In order to see a sample bid from Kobler (without a proper setup), you have to also do the following: +- Change the [`refererInfo` function](https://github.com/prebid/Prebid.js/blob/master/src/refererDetection.js) to return `'https://www.tv2.no/a/11734615'` as a [`referer`](https://github.com/prebid/Prebid.js/blob/caead3ccccc448e4cd09d074fd9f8833f56fe9b3/src/refererDetection.js#L169). This is necessary because Kobler only bids on recognized articles. +- Change the adapter's [`BIDDER_ENDPOINT`](https://github.com/prebid/Prebid.js/blob/master/modules/koblerBidAdapter.js#L8) to `'https://bid-service.dev.essrtb.com/bid/prebid_rtb_call'`. This endpoint belongs to the development server that is set up to always return a bid for the correct `placementId` and page URL combination. + +# Test Optional Parameters +```javascript + const adUnits = [{ + code: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + banner: { + sizes: [[320, 250], [300, 250]], + } + }, + bids: [{ + bidder: 'kobler', + params: { + placementId: 'k5H7et3R0', + zip: '102 22', + test: true, + floorPrice: 5.0, + position: 1, + dealIds: ['abc328745', 'mxw243253'] + } + }] + }]; +``` diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js new file mode 100644 index 00000000000..725c9ece118 --- /dev/null +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -0,0 +1,694 @@ +import {expect} from 'chai'; +import {spec} from 'modules/koblerBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import {getRefererInfo} from 'src/refererDetection.js'; + +function createBidderRequest(auctionId, timeout, pageUrl) { + return { + auctionId: auctionId || 'c1243d83-0bed-4fdb-8c76-42b456be17d0', + timeout: timeout || 2000, + refererInfo: { + referer: pageUrl || 'example.com' + } + }; +} + +function createValidBidRequest(params, bidId, sizes) { + return { + adUnitCode: 'adunit-code', + bidId: bidId || '22c4871113f461', + bidder: 'kobler', + bidderRequestId: '15246a574e859f', + bidRequestsCount: 1, + bidderRequestsCount: 1, + mediaTypes: { + banner: { + sizes: sizes || [[300, 250], [320, 100]] + } + }, + params: params || { + placementId: 'tpw58278' + }, + transactionTd: '04314114-15bd-4638-8664-bdb8bdc60bff' + }; +} + +describe('KoblerAdapter', function () { + describe('inherited functions', function () { + it('exists and is a function', function () { + const adapter = newBidder(spec); + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should not accept a request without bidId as valid', function () { + const bid = { + params: { + someParam: 'abc' + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without params as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589' + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without placementId as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + params: { + someParam: 'abc' + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should accept a request with bidId and placementId as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + params: { + someParam: 'abc', + placementId: '8bde0923-1409-4253-9594-495b58d931ba' + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + }); + + describe('buildRequests', function () { + it('should read data from bidder request', function () { + const testUrl = 'kobler.no'; + const auctionId = '8319af54-9795-4642-ba3a-6f57d6ff9100'; + const timeout = 5000; + const validBidRequests = [createValidBidRequest()]; + const bidderRequest = createBidderRequest(auctionId, timeout, testUrl); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.tmax).to.be.equal(timeout); + expect(openRtbRequest.id).to.be.equal(auctionId); + expect(openRtbRequest.site.page).to.be.equal(testUrl); + }); + + it('should read data from valid bid requests', function () { + const firstSize = [400, 800]; + const secondSize = [450, 950]; + const sizes = [firstSize, secondSize]; + const placementId = 'tsjs86325'; + const bidId = '3a56a019-4835-4f75-811c-76fac6853a2c'; + const validBidRequests = [ + createValidBidRequest( + { + placementId: placementId + }, + bidId, + sizes + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(1); + expect(openRtbRequest.imp[0].id).to.be.equal(bidId); + expect(openRtbRequest.imp[0].tagid).to.be.equal(placementId); + expect(openRtbRequest.imp[0].banner.w).to.be.equal(firstSize[0]); + expect(openRtbRequest.imp[0].banner.h).to.be.equal(firstSize[1]); + expect(openRtbRequest.imp[0].banner.format.length).to.be.equal(2); + expect(openRtbRequest.imp[0].banner.format[0].w).to.be.equal(firstSize[0]); + expect(openRtbRequest.imp[0].banner.format[0].h).to.be.equal(firstSize[1]); + expect(openRtbRequest.imp[0].banner.format[1].w).to.be.equal(secondSize[0]); + expect(openRtbRequest.imp[0].banner.format[1].h).to.be.equal(secondSize[1]); + }); + + it('should use 0x0 as default size', function () { + const validBidRequests = [ + createValidBidRequest( + undefined, + undefined, + [] + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(1); + expect(openRtbRequest.imp[0].banner.w).to.be.equal(0); + expect(openRtbRequest.imp[0].banner.h).to.be.equal(0); + expect(openRtbRequest.imp[0].banner.format.length).to.be.equal(1); + expect(openRtbRequest.imp[0].banner.format[0].w).to.be.equal(0); + expect(openRtbRequest.imp[0].banner.format[0].h).to.be.equal(0); + }); + + it('should use 0 as default position', function () { + const validBidRequests = [createValidBidRequest()]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(1); + expect(openRtbRequest.imp[0].banner.ext.kobler.pos).to.be.equal(0); + }); + + it('should read zip from valid bid requests', function () { + const zip = '700 02'; + const validBidRequests = [ + createValidBidRequest( + { + placementId: 'nmah8324234', + zip: zip + } + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.device.geo.zip).to.be.equal(zip); + }); + + it('should read test from valid bid requests', function () { + const validBidRequests = [ + createValidBidRequest( + { + placementId: 'zwop842799', + test: true + } + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.test).to.be.equal(1); + }); + + it('should read floorPrice from valid bid requests', function () { + const floorPrice = 4.343; + const validBidRequests = [ + createValidBidRequest( + { + placementId: 'oqr3224234', + floorPrice: floorPrice + } + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(1); + expect(openRtbRequest.imp[0].bidfloor).to.be.equal(floorPrice); + }); + + it('should read position from valid bid requests', function () { + const placementId = 'yzksf234592'; + const validBidRequests = [ + createValidBidRequest( + { + placementId: placementId, + position: 1 + } + ), + createValidBidRequest( + { + placementId: placementId, + position: 2 + } + ), + createValidBidRequest( + { + placementId: placementId, + position: 3 + } + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(3); + expect(openRtbRequest.imp[0].banner.ext.kobler.pos).to.be.equal(1); + expect(openRtbRequest.imp[0].tagid).to.be.equal(placementId); + expect(openRtbRequest.imp[1].banner.ext.kobler.pos).to.be.equal(2); + expect(openRtbRequest.imp[1].tagid).to.be.equal(placementId); + expect(openRtbRequest.imp[2].banner.ext.kobler.pos).to.be.equal(3); + expect(openRtbRequest.imp[2].tagid).to.be.equal(placementId); + }); + + it('should read dealIds from valid bid requests', function () { + const dealIds1 = ['78214682234823']; + const dealIds2 = ['89913861235234', '27368423545328640']; + const validBidRequests = [ + createValidBidRequest( + { + placementId: 'rsl1239823', + dealIds: dealIds1 + } + ), + createValidBidRequest( + { + placementId: 'pqw234232', + dealIds: dealIds2 + } + ) + ]; + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(2); + expect(openRtbRequest.imp[0].pmp.deals.length).to.be.equal(1); + expect(openRtbRequest.imp[0].pmp.deals[0].id).to.be.equal(dealIds1[0]); + expect(openRtbRequest.imp[1].pmp.deals.length).to.be.equal(2); + expect(openRtbRequest.imp[1].pmp.deals[0].id).to.be.equal(dealIds2[0]); + expect(openRtbRequest.imp[1].pmp.deals[1].id).to.be.equal(dealIds2[1]); + }); + + it('should read timeout from config', function () { + const timeout = 4000; + const validBidRequests = [createValidBidRequest()]; + // No timeout field + const bidderRequest = { + auctionId: 'c1243d83-0bed-4fdb-8c76-42b456be17d0', + refererInfo: { + referer: 'example.com' + } + }; + config.setConfig({ + bidderTimeout: timeout + }); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.tmax).to.be.equal(timeout); + }); + + it('should read floor price using floors module', function () { + const floorPriceFor580x400 = 6.5148; + const floorPriceForAnySize = 4.2343; + const validBidRequests = [ + createValidBidRequest(undefined, '98efe127-f926-4dde-b988-db8e5dba5a76', [[580, 400]]), + createValidBidRequest(undefined, 'c7698d4a-94f4-4a6b-a928-7e1facfbf752', []) + ]; + validBidRequests.forEach(validBidRequest => { + validBidRequest.getFloor = function (params) { + let floorPrice; + if (utils.isArray(params.size) && params.size[0] === 580 && params.size[1] === 400) { + floorPrice = floorPriceFor580x400; + } else if (params.size === '*') { + floorPrice = floorPriceForAnySize + } else { + floorPrice = 0 + } + return { + currency: params.currency, + floor: floorPrice + } + } + }) + const bidderRequest = createBidderRequest(); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + expect(openRtbRequest.imp.length).to.be.equal(2); + expect(openRtbRequest.imp[0].id).to.be.equal('98efe127-f926-4dde-b988-db8e5dba5a76'); + expect(openRtbRequest.imp[0].bidfloor).to.be.equal(floorPriceFor580x400); + expect(openRtbRequest.imp[1].id).to.be.equal('c7698d4a-94f4-4a6b-a928-7e1facfbf752'); + expect(openRtbRequest.imp[1].bidfloor).to.be.equal(floorPriceForAnySize); + }); + + it('should create whole OpenRTB request', function () { + const validBidRequests = [ + createValidBidRequest( + { + placementId: 'pcha322364', + zip: '0015', + floorPrice: 5.6234, + position: 1, + dealIds: ['623472534328234'] + }, + '953ee65d-d18a-484f-a840-d3056185a060', + [[400, 600]] + ), + createValidBidRequest( + { + placementId: 'sdfgoi32y4', + floorPrice: 3.2543, + position: 2, + dealIds: ['92368234753283', '263845832942'] + }, + '8320bf79-9d90-4a17-87c6-5d505706a921', + [[400, 500], [200, 250], [300, 350]] + ), + createValidBidRequest( + { + placementId: 'gwms2738647', + position: 3 + }, + 'd0de713b-32e3-4191-a2df-a007f08ffe72', + [[800, 900]] + ) + ]; + const bidderRequest = createBidderRequest( + '9ff580cf-e10e-4b66-add7-40ac0c804e21', + 4500, + 'bid.kobler.no' + ); + + const result = spec.buildRequests(validBidRequests, bidderRequest); + const openRtbRequest = JSON.parse(result.data); + + const expectedOpenRtbRequest = { + id: '9ff580cf-e10e-4b66-add7-40ac0c804e21', + at: 1, + tmax: 4500, + cur: ['USD'], + imp: [ + { + id: '953ee65d-d18a-484f-a840-d3056185a060', + banner: { + format: [ + { + w: 400, + h: 600 + } + ], + w: 400, + h: 600, + ext: { + kobler: { + pos: 1 + } + } + }, + tagid: 'pcha322364', + bidfloor: 5.6234, + bidfloorcur: 'USD', + pmp: { + deals: [ + { + id: '623472534328234' + } + ] + } + }, + { + id: '8320bf79-9d90-4a17-87c6-5d505706a921', + banner: { + format: [ + { + w: 400, + h: 500 + }, + { + w: 200, + h: 250 + }, + { + w: 300, + h: 350 + } + ], + w: 400, + h: 500, + ext: { + kobler: { + pos: 2 + } + } + }, + tagid: 'sdfgoi32y4', + bidfloor: 3.2543, + bidfloorcur: 'USD', + pmp: { + deals: [ + { + id: '92368234753283' + }, + { + id: '263845832942' + } + ] + } + }, + { + id: 'd0de713b-32e3-4191-a2df-a007f08ffe72', + banner: { + format: [ + { + w: 800, + h: 900 + } + ], + w: 800, + h: 900, + ext: { + kobler: { + pos: 3 + } + } + }, + tagid: 'gwms2738647', + bidfloor: 0, + bidfloorcur: 'USD', + pmp: {} + } + ], + device: { + devicetype: 2, + geo: { + zip: '0015' + } + }, + site: { + page: 'bid.kobler.no' + }, + test: 0 + }; + + expect(openRtbRequest).to.deep.equal(expectedOpenRtbRequest); + }); + }); + + describe('interpretResponse', function () { + it('should handle empty body', function () { + const responseWithEmptyBody = { + body: undefined + }; + const bids = spec.interpretResponse(responseWithEmptyBody) + + expect(bids.length).to.be.equal(0); + }); + + it('should generate bids from OpenRTB response', function () { + const responseWithTwoBids = { + body: { + seatbid: [ + { + bid: [ + { + impid: '6194ddef-89a4-404f-9efd-6b718fc23308', + price: 7.981, + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + crid: 'edea9b03-3a57-41aa-9c00-abd673e22006', + dealid: '', + w: 320, + h: 250, + adm: '', + adomain: [ + 'https://kobler.no' + ] + }, + { + impid: '2ec0b40f-d3ca-4ba5-8ce3-48290565690f', + price: 6.71234, + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + crid: 'fa2d5af7-2678-4204-9023-44c526160742', + dealid: '2783483223432342', + w: 580, + h: 400, + adm: '', + adomain: [ + 'https://bid.kobler.no' + ] + } + ] + } + ], + cur: 'USD' + } + }; + const bids = spec.interpretResponse(responseWithTwoBids) + + const expectedBids = [ + { + requestId: '6194ddef-89a4-404f-9efd-6b718fc23308', + cpm: 7.981, + currency: 'USD', + width: 320, + height: 250, + creativeId: 'edea9b03-3a57-41aa-9c00-abd673e22006', + dealId: '', + netRevenue: true, + ttl: 600, + ad: '', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + meta: { + advertiserDomains: [ + 'https://kobler.no' + ] + } + }, + { + requestId: '2ec0b40f-d3ca-4ba5-8ce3-48290565690f', + cpm: 6.71234, + currency: 'USD', + width: 580, + height: 400, + creativeId: 'fa2d5af7-2678-4204-9023-44c526160742', + dealId: '2783483223432342', + netRevenue: true, + ttl: 600, + ad: '', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + meta: { + advertiserDomains: [ + 'https://bid.kobler.no' + ] + } + } + ]; + expect(bids).to.deep.equal(expectedBids); + }); + }); + + describe('onBidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('Should not trigger pixel if bid does not contain nurl', function () { + spec.onBidWon({}); + + expect(utils.triggerPixel.called).to.be.false; + }); + + it('Should not trigger pixel if nurl is empty', function () { + spec.onBidWon({ + nurl: '' + }); + + expect(utils.triggerPixel.called).to.be.false; + }); + + it('Should trigger pixel with replaced nurl if nurl is not empty', function () { + spec.onBidWon({ + cpm: 8.341, + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + adserverTargeting: { + hb_pb: 8 + } + }); + + expect(utils.triggerPixel.callCount).to.be.equal(1); + expect(utils.triggerPixel.firstCall.args[0]).to.be.equal( + 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&asp=8' + ); + }); + }); + + describe('onTimeout', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('Should not trigger pixel if timeout data is not array', function () { + spec.onTimeout(null); + + expect(utils.triggerPixel.called).to.be.false; + }); + + it('Should not trigger pixel if timeout data is empty', function () { + spec.onTimeout([]); + + expect(utils.triggerPixel.called).to.be.false; + }); + + it('Should trigger pixel with query parameters if timeout data not empty', function () { + spec.onTimeout([ + { + adUnitCode: 'adunit-code', + auctionId: 'a1fba829-dd41-409f-acfb-b7b0ac5f30c6', + bidId: 'ef236c6c-e934-406b-a877-d7be8e8a839a', + timeout: 100, + params: [ + { + placementId: 'xrwg62731', + } + ], + }, + { + adUnitCode: 'adunit-code-2', + auctionId: 'a1fba829-dd41-409f-acfb-b7b0ac5f30c6', + bidId: 'ca4121c8-9a4a-46ba-a624-e9b64af206f2', + timeout: 100, + params: [ + { + placementId: 'bc482234', + } + ], + } + ]); + + expect(utils.triggerPixel.callCount).to.be.equal(2); + expect(utils.triggerPixel.getCall(0).args[0]).to.be.equal( + 'https://bid.essrtb.com/notify/prebid_timeout?ad_unit_code=adunit-code&' + + 'auction_id=a1fba829-dd41-409f-acfb-b7b0ac5f30c6&bid_id=ef236c6c-e934-406b-a877-d7be8e8a839a&timeout=100&' + + 'placement_id=xrwg62731&page_url=' + encodeURIComponent(getRefererInfo().referer) + ); + expect(utils.triggerPixel.getCall(1).args[0]).to.be.equal( + 'https://bid.essrtb.com/notify/prebid_timeout?ad_unit_code=adunit-code-2&' + + 'auction_id=a1fba829-dd41-409f-acfb-b7b0ac5f30c6&bid_id=ca4121c8-9a4a-46ba-a624-e9b64af206f2&timeout=100&' + + 'placement_id=bc482234&page_url=' + encodeURIComponent(getRefererInfo().referer) + ); + }); + }); +}); From e23cb2cdbdfefd32c1ba18c73a037f04a19d8e3c Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Wed, 21 Apr 2021 16:41:06 +0200 Subject: [PATCH 0850/1476] sspBC Bid Adapter: update to v4.8, bugfixes, & support for sending params.publisherId (#6575) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add sspbc adapter * tests for sspbc adapter * sspBC adapter v4.5: set correct creativeId, add adomain to bid.meta, set test mode in adexchange, read site SN from bid response * sspBC adapter v4.5: set meta.advertiserDomains, update test to expect bid.meta * sspBC Adapter: add ajax tests (test ad with & without gdpr) * sspBC Adapter: remove ajax tests * Update adapter to v4.6 Update adapter to v4.6 - add notification endpoint - send bidWon and onTimeout notifications - send CMP version to user sync endpoint * Remove console logs for notification events * Change payload data in onTimeout event * Update tests for sspBC adapter Update tests for sspBC adapter: - add onBidWon test - add onTimeout test - alter getUserSyncs test * Update sspBC adapter to v4.7; enable oneCodeId mode; change module name to ensure combatibility with prebid.org downloader * sspBc adapter: Bug fixes in v4.7 - change notification format, fix oneCode detection data, convert slot number to int * sspbc adapter: fix creating bid.crid, when not present in server response * sspbc adapter: add publisher id to payload * sspbc adapter: fix onecode issues (when bid.params is present, but incomplete) * sspbc adapter: code cleanup * sspbc adapter: ver up (4.8) * sspbc-adapter: update doc * [sspbc-adapter] update test settings Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 77 +++++++++++++++++++++++++------------- modules/sspBCBidAdapter.md | 13 +++---- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index b6e5b88e0f4..41191b3123a 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -2,14 +2,13 @@ import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import strIncludes from 'core-js-pure/features/string/includes.js'; const BIDDER_CODE = 'sspBC'; const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.7'; +const BIDDER_VERSION = '4.8'; const W = window; const { navigator } = W; const oneCodeDetection = {}; @@ -151,11 +150,13 @@ function mapBanner(slot) { } function mapImpression(slot) { + const { adUnitCode, bidId, params } = slot; + const { id, siteId } = params || {}; const imp = { - id: (slot.params && slot.params.id) ? slot.params.id : 'bidid-' + slot.bidId, + id: id && siteId ? id : 'bidid-' + bidId, banner: mapBanner(slot), /* native: mapNative(slot), */ - tagid: slot.adUnitCode, + tagid: adUnitCode, }; const bidfloor = (slot.params && slot.params.bidFloor) ? parseFloat(slot.params.bidFloor) : undefined; @@ -255,6 +256,7 @@ const spec = { } const siteId = setOnAny(validBidRequests, 'params.siteId'); + const publisherId = setOnAny(validBidRequests, 'params.publisherId'); const page = setOnAny(validBidRequests, 'params.page') || bidderRequest.refererInfo.referer; const domain = setOnAny(validBidRequests, 'params.domain') || utils.parseUrl(page).hostname; const tmax = setOnAny(validBidRequests, 'params.tmax') ? parseInt(setOnAny(validBidRequests, 'params.tmax'), 10) : TMAX; @@ -270,7 +272,13 @@ const spec = { const payload = { id: bidderRequest.auctionId, - site: { id: siteId, page, domain, ref }, + site: { + id: siteId, + publisher: publisherId ? { id: publisherId } : undefined, + page, + domain, + ref + }, imp: validBidRequests.map(slot => mapImpression(slot)), tmax, user: {}, @@ -290,6 +298,7 @@ const spec = { }, interpretResponse(serverResponse, request) { + const { bidderRequest } = request; const response = serverResponse.body; const bids = []; const site = JSON.parse(request.data).site; // get page and referer data from request @@ -297,50 +306,68 @@ const spec = { let seat; if (response.seatbid !== undefined) { + /* + Match response to request, by comparing bid id's + 'bidid-' prefix indicates oneCode (parameterless) request and response + */ response.seatbid.forEach(seatbid => { seat = seatbid.seat; seatbid.bid.forEach(serverBid => { - const bidRequest = request.bidderRequest.bids.filter(b => { - const bidId = b.params ? b.params.id : 'bidid-' + b.bidId; - return bidId === serverBid.impid; + // get data from bid response + const { adomain, crid, impid, exp, ext, price, w, h } = serverBid; + + const bidRequest = bidderRequest.bids.filter(b => { + const { bidId, params } = b; + const { id, siteId } = params || {}; + const currentBidId = id && siteId ? id : 'bidid-' + bidId; + return currentBidId === impid; })[0]; - site.slot = bidRequest && bidRequest.params ? bidRequest.params.slotid : undefined; - if (serverBid.ext) { + // get data from linked bidRequest + const { bidId, params } = bidRequest || {}; + + // get slot id for current bid + site.slot = params && params.id; + + if (ext) { /* bid response might include ext object containing siteId / slotId, as detected by OneCode update site / slot data in this case */ - site.id = serverBid.ext.siteid || site.id; - site.slot = serverBid.ext.slotid || site.slot; + const { siteid, slotid } = ext; + site.id = siteid || site.id; + site.slot = slotid || site.slot; } - if (bidRequest && site.id && !strIncludes(site.id, 'bidid')) { - // store site data for future notification - oneCodeDetection[bidRequest.bidId] = [site.id, site.slot]; + if (bidRequest && site.id && !site.id.includes('bidid')) { + // found a matching request; add this bid - const bidFloor = (bidRequest.params && bidRequest.params.bidFloor) ? bidRequest.params.bidFloor : 0; + // store site data for future notification + oneCodeDetection[bidId] = [site.id, site.slot]; const bid = { - requestId: bidRequest.bidId, - creativeId: serverBid.crid || 'mcad_' + request.bidderRequest.auctionId + '_' + request.bidderRequest.params.id, - cpm: serverBid.price, + requestId: bidId, + creativeId: crid || 'mcad_' + bidderRequest.auctionId + '_' + site.slot, + cpm: price, currency: response.cur, - ttl: serverBid.exp || 300, - width: serverBid.w, - height: serverBid.h, + ttl: exp || 300, + width: w, + height: h, bidderCode: BIDDER_CODE, mediaType: 'banner', meta: { - advertiserDomains: serverBid.adomain, + advertiserDomains: adomain, networkName: seat, }, netRevenue: true, - ad: renderCreative(site, response.id, serverBid, seat, request.bidderRequest), + ad: renderCreative(site, response.id, serverBid, seat, bidderRequest), }; if (bid.cpm > 0) { - if (bid.cpm >= bidFloor) { + // check bidFloor (if present in params) + const { bidFloor } = params || {}; + + if (!bidFloor || bid.cpm >= bidFloor) { bids.push(bid); } else { utils.logWarn('Discarding bid due to bidFloor setting', bid.cpm, bidFloor); diff --git a/modules/sspBCBidAdapter.md b/modules/sspBCBidAdapter.md index 67a2ba1c7ba..f22e8e6c458 100644 --- a/modules/sspBCBidAdapter.md +++ b/modules/sspBCBidAdapter.md @@ -16,10 +16,12 @@ Required parameters: Optional parameters: - site id - adslot id +- publisher id - domain - page - tmax - bidFloor +- test # Test Parameters ``` @@ -29,18 +31,15 @@ var adUnits = [ code: 'banner-div', mediaTypes: { banner: { - sizes: [[300, 250], [300,600]] + sizes: [[300, 250]] } }, bids: [{ bidder: 'sspBC', params: { - id: '006', // optional - siteId: '235911', // optional - domain: 'somesite.pl', // optional - page: 'somesite.pl/somepage.html', // optional - tmax: 250, // optional - bidFloor: 0.1 // optional + id: "006", + siteId: "235911", + test: 1 } }] } From ae44f9e483e73c73466e22bce3146539cd7d8ced Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 21 Apr 2021 21:31:06 -0400 Subject: [PATCH 0851/1476] Create module registry: update build to add installModules array to pbjs global (#6601) * - Remove module list comment on build - Add pbjs.installedModules with macro to prebid.js - Update gruntfile to replace macro with module array (if empty array all modules included)) * Removed unused code * Removed comma * Recommitting changes * Updated regex to look for either '|" in macro name. Seems there is a difference between dev/prod --- gulpfile.js | 13 ++++++++++--- src/prebid.js | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index ac8b8c2dcd5..9bf378779d1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,7 +31,7 @@ const execa = require('execa'); var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); -var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + '\nModules: <%= modules %> */\n'; +var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + '*/\n'; var port = 9999; const FAKE_SERVER_HOST = argv.host ? argv.host : 'localhost'; const FAKE_SERVER_PORT = 4444; @@ -134,6 +134,12 @@ function watch(done) { done(); }; +function makeModuleList(modules) { + return modules.map(module => { + return '"' + module + '"' + }); +} + function makeDevpackPkg() { var cloned = _.cloneDeep(webpackConfig); cloned.devtool = 'source-map'; @@ -145,6 +151,7 @@ function makeDevpackPkg() { return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) + .pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules))) .pipe(gulp.dest('build/dev')) .pipe(connect.reload()); } @@ -157,13 +164,13 @@ function makeWebpackPkg() { const analyticsSources = helpers.getAnalyticsSources(); const moduleSources = helpers.getModulePaths(externalModules); - const modulesString = getModulesListToAddInBanner(externalModules); return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) .pipe(uglify()) - .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid, modules: modulesString }))) + .pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules))) + .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid}))) .pipe(gulp.dest('build/dist')); } diff --git a/src/prebid.js b/src/prebid.js index 8c5e465174f..2211e8ec2b5 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -43,6 +43,9 @@ $$PREBID_GLOBAL$$.libLoaded = true; $$PREBID_GLOBAL$$.version = 'v$prebid.version$'; utils.logInfo('Prebid.js v$prebid.version$ loaded'); +// modules list generated from build +$$PREBID_GLOBAL$$.installedModules = ['v$prebid.modulesList$']; + // create adUnit array $$PREBID_GLOBAL$$.adUnits = $$PREBID_GLOBAL$$.adUnits || []; From 3356e38690ee261953d7e33c49329829b344c639 Mon Sep 17 00:00:00 2001 From: pratik-synacor <64602199+pratik-synacor@users.noreply.github.com> Date: Thu, 22 Apr 2021 16:31:38 +0530 Subject: [PATCH 0852/1476] SynacorMedia Bid Adapter: Create bid.params.video object if it's not already present on the video request since it's an optional property (#6637) --- modules/synacormediaBidAdapter.js | 3 ++ .../modules/synacormediaBidAdapter_spec.js | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index d14fee67b23..aaff637c790 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -157,6 +157,9 @@ export const spec = { pos }; if (bid.mediaTypes.video) { + if (!bid.params.video) { + bid.params.video = {}; + } this.setValidVideoParams(bid.mediaTypes.video, bid.params.video); } if (bid.params.video) { diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 2ead2fb689e..688f1c2c090 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -576,6 +576,50 @@ describe('synacormediaBidAdapter ', function () { } ]); }); + it('should create params.video object if not present on bid request and move any video params in the mediaTypes object to it', function () { + let validBidRequestVideo = { + bidder: 'synacormedia', + params: { + seatId: 'prebid', + tagId: '1234' + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [[ 640, 480 ]], + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'] + } + }, + adUnitCode: 'video1', + transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', + sizes: [[ 640, 480 ]], + bidId: '2624fabbb078e8', + bidderRequestId: '117954d20d7c9c', + auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', + src: 'client', + bidRequestsCount: 1 + }; + + let req = spec.buildRequests([validBidRequestVideo], bidderRequest); + expect(req.data.imp).to.eql([ + { + video: { + h: 480, + pos: 0, + w: 640, + startdelay: 1, + linearity: 1, + placement: 1, + mimes: ['video/mp4'] + }, + id: 'v2624fabbb078e8-640x480', + tagid: '1234', + } + ]); + }); it('should contain the CCPA privacy string when UspConsent is in bidder request', function () { // banner test let req = spec.buildRequests([validBidRequest], bidderRequestWithCCPA); From b65a567d8f5ad0c11cafee1651d9723ea231d832 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 22 Apr 2021 07:12:22 -0400 Subject: [PATCH 0853/1476] Prebid Core: Readme.md import name change (#6638) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Delete iasBidAdapter.js * Add files via upload * Update README.md * Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d87b70710b7..23fc8342bc5 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,12 @@ Or for Babel 6: Then you can use Prebid.js as any other npm depedendency ```javascript -import prebid from 'prebid.js'; +import pbjs from 'prebid.js'; import 'prebid.js/modules/rubiconBidAdapter'; // imported modules will register themselves automatically with prebid import 'prebid.js/modules/appnexusBidAdapter'; -prebid.processQueue(); // required to process existing pbjs.queue blocks and setup any further pbjs.queue execution +pbjs.processQueue(); // required to process existing pbjs.queue blocks and setup any further pbjs.queue execution -prebid.requestBids({ +pbjs.requestBids({ ... }) From 08b7e8db877a33ef880f417d271a102f3fff41ae Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 22 Apr 2021 07:19:37 -0400 Subject: [PATCH 0854/1476] PBS Bid Adapter: Stop overriding s2sconfig.enabled from vendor defaults (#6622) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Delete iasBidAdapter.js * Add files via upload * Update index.js --- modules/prebidServerBidAdapter/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index ff326f25840..1649733997d 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -89,7 +89,7 @@ config.setDefaults({ * @return {boolean} */ function updateConfigDefaultVendor(option) { - if (option.defaultVendor) { + if (option.defaultVendor && option.enabled !== false) { let vendor = option.defaultVendor; let optionKeys = Object.keys(option); if (S2S_VENDORS[vendor]) { From f870a084213857f27b280ce9cda82df7f387751e Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Thu, 22 Apr 2021 07:25:30 -0400 Subject: [PATCH 0855/1476] RP Bid Adapter: Bug fix for parsing ortb2.user.data (#6643) * Bug fix when parsing FPD data. Check for taxonomyname existence prior to match * Lint check --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index be71028f3ad..86e6525af85 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -904,7 +904,8 @@ function applyFPD(bidRequest, mediaType, data) { const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; const validate = function(prop, key) { if (key === 'data' && Array.isArray(prop)) { - return prop.filter(name => name.segment && utils.deepAccess(name, 'ext.taxonomyname').match(/iab/i)).map(value => { + return prop.filter(name => name.segment && utils.deepAccess(name, 'ext.taxonomyname') && + utils.deepAccess(name, 'ext.taxonomyname').match(/iab/i)).map(value => { let segments = value.segment.filter(obj => obj.id).reduce((result, obj) => { result.push(obj.id); return result; From 3ea23dc093246c40041bdaa3a8a1590481691fd7 Mon Sep 17 00:00:00 2001 From: Fridoom007 Date: Thu, 22 Apr 2021 18:05:14 +0300 Subject: [PATCH 0856/1476] AdRiver Bid Adapter: add new bid adapter (#6514) * AdRiver Bid Adapter: initial prebid.js integration * Added AdRiver Bid Adapter * AdRiver Bid Adapter: update getting floor, via getFloor() * Added internal method _getFloor() * Update test for getFloor() * Remove old currency logic * AdRiver Bid Adapter: update adriverBidAdapter.md * Delete old test parameters * AdRiver Bid Adapter: add meta.advertiserDomains * Added parameter meta.advertiserDomains to interpretResponse * Update test for meta.advertiserDomains --- modules/adriverBidAdapter.js | 188 ++++++++++++ modules/adriverBidAdapter.md | 20 ++ test/spec/modules/adriverBidAdapter_spec.js | 323 ++++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 modules/adriverBidAdapter.js create mode 100644 modules/adriverBidAdapter.md create mode 100644 test/spec/modules/adriverBidAdapter_spec.js diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js new file mode 100644 index 00000000000..af0a401b355 --- /dev/null +++ b/modules/adriverBidAdapter.js @@ -0,0 +1,188 @@ +// ADRIVER BID ADAPTER for Prebid 1.13 +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'adriver'; +const ADRIVER_BID_URL = 'https://pb.adriver.ru/cgi-bin/bid.cgi'; +const TIME_TO_LIVE = 3000; + +export const spec = { + + code: BIDDER_CODE, + + /** + * 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.siteid; + }, + + buildRequests: function (validBidRequests) { + utils.logInfo('validBidRequests', validBidRequests); + + let win = utils.getWindowLocation(); + let customID = Math.round(Math.random() * 999999999) + '-' + Math.round(new Date() / 1000) + '-1-46-'; + let siteId = utils.getBidIdParameter('siteid', validBidRequests[0].params) + ''; + let currency = utils.getBidIdParameter('currency', validBidRequests[0].params); + currency = 'RUB'; + + const payload = { + 'at': 1, + 'cur': [currency], + 'site': { + 'name': win.origin, + 'domain': win.hostname, + 'id': siteId, + 'page': win.href + }, + 'id': customID, + 'user': { + 'buyerid': 0 + }, + 'device': { + 'ip': '195.209.111.14', + 'ua': window.navigator.userAgent + }, + 'imp': [] + }; + + utils._each(validBidRequests, (bid) => { + utils._each(bid.sizes, (sizes) => { + let width; + let height; + let par; + + let floorAndCurrency = _getFloor(bid, currency, sizes); + + let bidFloor = floorAndCurrency.floor; + let dealId = utils.getBidIdParameter('dealid', bid.params); + if (typeof sizes[0] === 'number' && typeof sizes[1] === 'number') { + width = sizes[0]; + height = sizes[1]; + } + par = { + 'id': bid.params.placementId, + 'ext': {'query': 'bn=15&custom=111=' + bid.bidId}, + 'banner': { + 'w': width || undefined, + 'h': height || undefined + }, + 'bidfloor': bidFloor || 0, + 'bidfloorcur': floorAndCurrency.currency, + 'secure': 0 + }; + if (dealId) { + par.pmp = { + 'private_auction': 1, + 'deals': [{ + 'id': dealId, + 'bidfloor': bidFloor || 0, + 'bidfloorcur': currency + }] + }; + } + utils.logInfo('par', par); + payload.imp.push(par); + }); + }); + + const payloadString = JSON.stringify(payload); + + return { + method: 'POST', + url: ADRIVER_BID_URL, + data: payloadString, + }; + }, + + interpretResponse: function (serverResponse, bidRequest) { + utils.logInfo('serverResponse.body.seatbid', serverResponse.body.seatbid); + const bidResponses = []; + let nurl = 0; + utils._each(serverResponse.body.seatbid, (seatbid) => { + utils.logInfo('_each', seatbid); + var bid = seatbid.bid[0]; + if (bid.nurl !== undefined) { + nurl = bid.nurl.split('://'); + nurl = window.location.protocol + '//' + nurl[1]; + nurl = nurl.replace(/\$\{AUCTION_PRICE\}/, bid.price); + } + + if (bid.price >= 0 && bid.impid !== undefined && nurl !== 0 && bid.dealid === undefined) { + let bidResponse = { + requestId: bid.ext || undefined, + cpm: bid.price, + width: bid.w, + height: bid.h, + creativeId: bid.impid || undefined, + currency: serverResponse.body.cur, + netRevenue: true, + ttl: TIME_TO_LIVE, + meta: { + advertiserDomains: bid.adomain + }, + ad: '' + }; + utils.logInfo('bidResponse', bidResponse); + bidResponses.push(bidResponse); + } + }); + return bidResponses; + } + +}; +registerBidder(spec); + +/** + * Gets bidfloor + * @param {Object} bid + * @param currencyPar + * @param sizes + * @returns {Object} floor + */ +function _getFloor(bid, currencyPar, sizes) { + const curMediaType = bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner'; + let floor = 0; + const currency = currencyPar || 'RUB'; + + let currencyResult = ''; + + let isSize = false; + + if (typeof sizes[0] === 'number' && typeof sizes[1] === 'number') { + isSize = true; + } + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: currency, + mediaType: curMediaType, + size: isSize ? sizes : '*' + }); + + if (typeof floorInfo === 'object' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = floorInfo.floor; + } + + if (typeof floorInfo === 'object' && floorInfo.currency) { + currencyResult = floorInfo.currency; + } + } + + if (!currencyResult) { + currencyResult = currency; + } + + if (floor == null) { + floor = 0; + } + + return { + floor: floor, + currency: currencyResult + }; +} diff --git a/modules/adriverBidAdapter.md b/modules/adriverBidAdapter.md new file mode 100644 index 00000000000..e5a8af28647 --- /dev/null +++ b/modules/adriverBidAdapter.md @@ -0,0 +1,20 @@ +# Overview + +Module Name: AdRiver Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@adriver.ru + +# Description + +Module that connects to AdRiver's demand sources. + +# Test Parameters + +bids: [{ + bidder: 'adriver', + params: { + siteid: '216200', + placementId: '55:test_placement', + dealid: 'dealidTest' + } +}] diff --git a/test/spec/modules/adriverBidAdapter_spec.js b/test/spec/modules/adriverBidAdapter_spec.js new file mode 100644 index 00000000000..c16bc5df5cb --- /dev/null +++ b/test/spec/modules/adriverBidAdapter_spec.js @@ -0,0 +1,323 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adriverBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { auctionManager } from 'src/auctionManager.js'; +const ENDPOINT = 'https://pb.adriver.ru/cgi-bin/bid.cgi'; + +describe('adriverAdapter', 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': 'adriver', + 'params': { + 'placementId': '55:test_placement', + 'siteid': 'testSiteID' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600], [300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + let getAdUnitsStub; + const floor = 3; + + let bidRequests = [ + { + 'bidder': 'adriver', + 'params': { + 'placementId': '55:test_placement', + 'siteid': 'testSiteID', + 'dealid': 'dealidTest' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600], [300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'transactionId': '04f2659e-c005-4eb1-a57c-fa93145e3843' + } + ]; + + let floorTestData = { + 'currency': 'USD', + 'floor': floor + }; + bidRequests[0].getFloor = _ => { + return floorTestData; + }; + + beforeEach(function() { + getAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits').callsFake(function() { + return []; + }); + }); + + afterEach(function() { + getAdUnitsStub.restore(); + }); + + it('should exist currency', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.cur).to.exist; + }); + + it('should exist at', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.at).to.exist; + expect(payload.at).to.deep.equal(1); + }); + + it('should parse imp', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.imp[0]).to.exist; + expect(payload.imp[0].id).to.deep.equal('55:test_placement'); + + expect(payload.imp[0].ext).to.exist; + expect(payload.imp[0].ext.query).to.deep.equal('bn=15&custom=111=' + '30b31c1838de1e'); + + expect(payload.imp[0].banner).to.exist; + expect(payload.imp[0].banner.w).to.deep.equal(300); + expect(payload.imp[0].banner.h).to.deep.equal(250); + }); + + it('should parse pmp', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].pmp).to.exist; + + expect(payload.imp[0].pmp.deals).to.exist; + + expect(payload.imp[0].pmp.deals[0].bidfloor).to.exist; + expect(payload.imp[0].pmp.deals[0].bidfloor).to.deep.equal(3); + + expect(payload.imp[0].pmp.deals[0].bidfloorcur).to.exist; + expect(payload.imp[0].pmp.deals[0].bidfloorcur).to.deep.equal('RUB'); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponse', function () { + let bfStub; + before(function() { + bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); + }); + + after(function() { + bfStub.restore(); + }); + + let response = { + 'id': '221594457-1615288400-1-46-', + 'bidid': 'D8JW8XU8-L5m7qFMNQGs7i1gcuPvYMEDOKsktw6e9uLy5Eebo9HftVXb0VpKj4R2dXa93i6QmRhjextJVM4y1SqodMAh5vFOb_eVkHA', + 'seatbid': [{ + 'bid': [{ + 'id': '1', + 'impid': '/19968336/header-bid-tag-0', + 'price': 4.29, + 'h': 250, + 'w': 300, + 'adid': '7121351', + 'adomain': ['http://ikea.com'], + 'nurl': 'https://ad.adriver.ru/cgi-bin/erle.cgi?expid=D8JW8XU8-L5m7qFMNQGs7i1gcuPvYMEDOKsktw6e9uLy5Eebo9HftVXb0VpKj4R2dXa93i6QmRhjextJVM4y1SqodMAh5vFOb_eVkHA&bid=7121351&wprc=4.29&tuid=-1&custom=207=/19968336/header-bid-tag-0', + 'cid': '717570', + 'ext': '2c262a7058758d' + }] + }, { + 'bid': [{ + 'id': '1', + 'impid': '/19968336/header-bid-tag-0', + 'price': 17.67, + 'h': 600, + 'w': 300, + 'adid': '7121369', + 'adomain': ['http://ikea.com'], + 'nurl': 'https://ad.adriver.ru/cgi-bin/erle.cgi?expid=DdtToXX5cpTaMMxrJSEsOsUIXt3WmC3jOvuNI5DguDrY8edFG60Jg1M-iMkVNKQ4OiAdHSLPJLQQXMUXZfI9VbjMoGCb-zzOTPiMpshI&bid=7121369&wprc=17.67&tuid=-1&custom=207=/19968336/header-bid-tag-0', + 'cid': '717570', + 'ext': '2c262a7058758d' + }] + }], + 'cur': 'RUB' + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '2c262a7058758d', + cpm: 4.29, + width: 300, + height: 250, + creativeId: '/19968336/header-bid-tag-0', + currency: 'RUB', + netRevenue: true, + ttl: 3000, + meta: { + advertiserDomains: ['http://ikea.com'] + }, + ad: '' + } + ]; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + }; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let response = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); + + describe('function _getFloor', function () { + let bidRequests = [ + { + bidder: 'adriver', + params: { + placementId: '55:test_placement', + siteid: 'testSiteID', + dealid: 'dealidTest', + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600], [300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + } + ]; + + const floorTestData = { + 'currency': 'RUB', + 'floor': 1.50 + }; + + const bitRequestStandard = JSON.parse(JSON.stringify(bidRequests)); + + bitRequestStandard[0].getFloor = () => { + return floorTestData; + }; + + it('valid BidRequests', function () { + const request = spec.buildRequests(bitRequestStandard); + const payload = JSON.parse(request.data); + + expect(typeof bitRequestStandard[0].getFloor).to.equal('function'); + expect(payload.imp[0].bidfloor).to.equal(1.50); + expect(payload.imp[0].bidfloorcur).to.equal('RUB'); + }); + + const bitRequestEmptyCurrency = JSON.parse(JSON.stringify(bidRequests)); + + const floorTestDataEmptyCurrency = { + 'currency': 'RUB', + 'floor': 1.50 + }; + + bitRequestEmptyCurrency[0].getFloor = () => { + return floorTestDataEmptyCurrency; + }; + + it('empty currency', function () { + const request = spec.buildRequests(bitRequestEmptyCurrency); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].bidfloor).to.equal(1.50); + expect(payload.imp[0].bidfloorcur).to.equal('RUB'); + }); + + const bitRequestFloorNull = JSON.parse(JSON.stringify(bidRequests)); + + const floorTestDataFloorNull = { + 'currency': '', + 'floor': null + }; + + bitRequestFloorNull[0].getFloor = () => { + return floorTestDataFloorNull; + }; + + it('empty floor', function () { + const request = spec.buildRequests(bitRequestFloorNull); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].bidfloor).to.equal(0); + }); + + const bitRequestGetFloorNotFunction = JSON.parse(JSON.stringify(bidRequests)); + + bitRequestGetFloorNotFunction[0].getFloor = 0; + + it('bid.getFloor is not a function', function () { + const request = spec.buildRequests(bitRequestGetFloorNotFunction); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].bidfloor).to.equal(0); + expect(payload.imp[0].bidfloorcur).to.equal('RUB'); + }); + + const bitRequestGetFloorBySized = JSON.parse(JSON.stringify(bidRequests)); + + bitRequestGetFloorBySized[0].getFloor = (requestParams = {currency: 'USD', mediaType: '*', size: '*'}) => { + if (requestParams.size.length === 2 && requestParams.size[0] === 300 && requestParams.size[1] === 250) { + return { + 'currency': 'RUB', + 'floor': 3.33 + } + } else { + return {} + } + }; + + it('bid.getFloor get size', function () { + const request = spec.buildRequests(bitRequestGetFloorBySized); + const payload = JSON.parse(request.data); + + expect(payload.imp[0].bidfloor).to.equal(3.33); + expect(payload.imp[0].bidfloorcur).to.equal('RUB'); + expect(payload.imp[0].bidfloorcur).to.equal('RUB'); + }); + }); +}); From a1d83abdf117d4b7fbc04083e0d972368944afba Mon Sep 17 00:00:00 2001 From: ym-abaranov <78230460+ym-abaranov@users.noreply.github.com> Date: Thu, 22 Apr 2021 09:48:11 -0700 Subject: [PATCH 0857/1476] hotfix - placement issue fix (#6641) --- modules/yieldmoBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index b307f534b18..3cc0ab1194c 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -242,7 +242,7 @@ function createNewVideoBid(response, bidRequest) { }, }; - if (imp.placement && imp.placement !== 1) { + if (imp.video.placement && imp.video.placement !== 1) { const renderer = Renderer.install({ url: OUTSTREAM_VIDEO_PLAYER_URL, config: { From 5729c8c2f6118b74059ef97d6321335ff54bc005 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 22 Apr 2021 09:59:50 -0700 Subject: [PATCH 0858/1476] Prebid 4.36.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58ea0d7ba30..31cd746d1bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.36.0-pre", + "version": "4.36.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f96dfcc76bc775cad50d4ab62f0aa141307af9e5 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Thu, 22 Apr 2021 13:00:53 -0400 Subject: [PATCH 0859/1476] PBJS RP adapter: pass analytics flag to server (#6644) --- modules/rubiconBidAdapter.js | 6 +++++ test/spec/modules/rubiconBidAdapter_spec.js | 30 +++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 86e6525af85..ccb713f8978 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -3,6 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; +import { getGlobal } from '../src/prebidGlobal.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; @@ -196,6 +197,11 @@ export const spec = { } } + let modules = (getGlobal()).installedModules; + if (modules && (!modules.length || modules.indexOf('rubiconAnalyticsAdapter') !== -1)) { + utils.deepSetValue(data, 'ext.prebid.analytics', [{ 'adapter': 'rubicon', 'client-analytics': true }]); + } + let bidFloor; if (typeof bidRequest.getFloor === 'function' && !rubiConf.disableFloors) { let floorInfo; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index f53cde3c8ab..5ec90fd5f64 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -371,6 +371,7 @@ describe('the rubicon adapter', function () { utils.logError.restore(); config.resetConfig(); resetRubiConf(); + delete $$PREBID_GLOBAL$$.installedModules; }); describe('MAS mapping / ordering', function () { @@ -1664,6 +1665,35 @@ describe('the rubicon adapter', function () { expect(request.data.ext.prebid.multibid).to.deep.equal(expected); }); + it('should pass client analytics to PBS endpoint if all modules included', function () { + createVideoBidderRequest(); + $$PREBID_GLOBAL$$.installedModules = []; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let payload = request.data; + + expect(payload.ext.prebid.analytics).to.not.be.undefined; + expect(payload.ext.prebid.analytics).to.deep.equal([{'adapter': 'rubicon', 'client-analytics': true}]); + }); + + it('should pass client analytics to PBS endpoint if rubicon analytics adapter is included', function () { + createVideoBidderRequest(); + $$PREBID_GLOBAL$$.installedModules = ['rubiconBidAdapter', 'rubiconAnalyticsAdapter']; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let payload = request.data; + + expect(payload.ext.prebid.analytics).to.not.be.undefined; + expect(payload.ext.prebid.analytics).to.deep.equal([{'adapter': 'rubicon', 'client-analytics': true}]); + }); + + it('should not pass client analytics to PBS endpoint if rubicon analytics adapter is not included', function () { + createVideoBidderRequest(); + $$PREBID_GLOBAL$$.installedModules = ['rubiconBidAdapter']; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let payload = request.data; + + expect(payload.ext.prebid.analytics).to.be.undefined; + }); + it('should send video exp param correctly when set', function () { createVideoBidderRequest(); config.setConfig({s2sConfig: {defaultTtl: 600}}); From e01e5ad6e50ac78fa372c838fad848e15d115b39 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 22 Apr 2021 10:40:09 -0700 Subject: [PATCH 0860/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31cd746d1bb..de3ad74eb51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.36.0", + "version": "4.37.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7f0e486f0f583a21be0ef70e7562d69c784176b9 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Thu, 22 Apr 2021 20:41:59 +0300 Subject: [PATCH 0861/1476] Adkernel: new alias (#6608) --- modules/adkernelBidAdapter.js | 2 +- test/spec/modules/adkernelBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index b6d82aec976..3e795d176d2 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -53,7 +53,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { export const spec = { code: 'adkernel', gvlid: GVLID, - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads', 'bcm'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads', 'bcm', 'engageadx'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 9881acc68df..aee24719c9c 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -565,7 +565,7 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(12); + expect(spec.aliases).to.be.an('array').that.is.not.empty; }); }); From 9026c5dc6c0cc8b088f4618f14fa1e2788620d82 Mon Sep 17 00:00:00 2001 From: mjaworskiccx <50406214+mjaworskiccx@users.noreply.github.com> Date: Fri, 23 Apr 2021 13:25:42 +0200 Subject: [PATCH 0862/1476] Ccx Bid Adapter: adomain support (#6595) --- modules/ccxBidAdapter.js | 5 +++++ test/spec/modules/ccxBidAdapter_spec.js | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index ee15d6bb3ec..37b6fdc3e98 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -120,6 +120,11 @@ function _buildResponse (bid, currency, ttl) { currency: currency } + resp.meta = {}; + if (bid.adomain && bid.adomain.length > 0) { + resp.meta.advertiserDomains = bid.adomain; + } + if (bid.ext.type === 'video') { resp.vastXml = bid.adm } else { diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js index f14612629b1..ef86b391e39 100644 --- a/test/spec/modules/ccxBidAdapter_spec.js +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -337,7 +337,10 @@ describe('ccxAdapter', function () { netRevenue: false, ttl: 5, currency: 'PLN', - ad: '' + ad: '', + meta: { + advertiserDomains: ['clickonometrics.com'] + } }, { requestId: '2e56e1af51a5d8', @@ -348,7 +351,10 @@ describe('ccxAdapter', function () { netRevenue: false, ttl: 5, currency: 'PLN', - vastXml: '' + vastXml: '', + meta: { + advertiserDomains: ['clickonometrics.com'] + } } ]; expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); @@ -366,7 +372,10 @@ describe('ccxAdapter', function () { netRevenue: false, ttl: 5, currency: 'PLN', - ad: '' + ad: '', + meta: { + advertiserDomains: ['clickonometrics.com'] + } } ]; expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); From 071b453375ce55cf470ea7c99f5461bfa130c353 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Fri, 23 Apr 2021 19:06:07 +0700 Subject: [PATCH 0863/1476] Qwarry Bid Adapter: added gdpr field (#6635) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 3 ++- test/spec/modules/qwarryBidAdapter_spec.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index d19ad0b4fe4..e2782d82512 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -33,7 +33,8 @@ export const spec = { if (bidderRequest && bidderRequest.gdprConsent) { payload.gdprConsent = { consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : false, - consentString: bidderRequest.gdprConsent.consentString + consentString: bidderRequest.gdprConsent.consentString, + gdpr: bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0 } } diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index f15d7b488cc..5d56203131a 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -86,6 +86,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }) + expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', gdpr: 1 }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') expect(bidderRequest.url).to.equal(ENDPOINT) From 7dba60cce0c083321047746013663cb51af25895 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Fri, 23 Apr 2021 16:51:42 +0300 Subject: [PATCH 0864/1476] Adf Bid Adapter: rename adformOpenRTB adapter; add former name as alias (#6642) --- .../{adformOpenRTBBidAdapter.js => adfBidAdapter.js} | 5 +++-- .../{adformOpenRTBBidAdapter.md => adfBidAdapter.md} | 6 +++--- ...enRTBBidAdapter_spec.js => adfBidAdapter_spec.js} | 12 +++++++++--- 3 files changed, 15 insertions(+), 8 deletions(-) rename modules/{adformOpenRTBBidAdapter.js => adfBidAdapter.js} (97%) rename modules/{adformOpenRTBBidAdapter.md => adfBidAdapter.md} (90%) rename test/spec/modules/{adformOpenRTBBidAdapter_spec.js => adfBidAdapter_spec.js} (98%) diff --git a/modules/adformOpenRTBBidAdapter.js b/modules/adfBidAdapter.js similarity index 97% rename from modules/adformOpenRTBBidAdapter.js rename to modules/adfBidAdapter.js index 3270fb5865a..8b3550e6108 100644 --- a/modules/adformOpenRTBBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -10,8 +10,9 @@ import { import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -const BIDDER_CODE = 'adformOpenRTB'; +const BIDDER_CODE = 'adf'; const GVLID = 50; +const BIDDER_ALIAS = [ { code: 'adformOpenRTB', gvlid: GVLID } ]; const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; const NATIVE_PARAMS = { title: { @@ -47,6 +48,7 @@ const NATIVE_PARAMS = { export const spec = { code: BIDDER_CODE, + aliases: BIDDER_ALIAS, gvlid: GVLID, supportedMediaTypes: [ NATIVE ], isBidRequestValid: bid => !!bid.params.mid, @@ -170,7 +172,6 @@ export const spec = { netRevenue: bid.netRevenue === 'net', currency: cur, mediaType: NATIVE, - bidderCode: BIDDER_CODE, native: parseNative(bidResponse) }; } diff --git a/modules/adformOpenRTBBidAdapter.md b/modules/adfBidAdapter.md similarity index 90% rename from modules/adformOpenRTBBidAdapter.md rename to modules/adfBidAdapter.md index 0dd98ad07b8..190aa91ea57 100644 --- a/modules/adformOpenRTBBidAdapter.md +++ b/modules/adfBidAdapter.md @@ -1,13 +1,13 @@ # Overview -Module Name: Adform OpenRTB Adapter +Module Name: Adf Adapter Module Type: Bidder Adapter Maintainer: Scope.FL.Scripts@adform.com # Description Module that connects to Adform demand sources to fetch bids. -Only native format is supported. Using OpenRTB standard. +Only native format is supported. Using OpenRTB standard. Previous adapter name - adformOpenRTB. # Test Parameters ``` @@ -42,7 +42,7 @@ Only native format is supported. Using OpenRTB standard. } }, bids: [{ - bidder: 'adformOpenRTB', + bidder: 'adf', params: { mid: 606169, // required adxDomain: 'adx.adform.net', // optional diff --git a/test/spec/modules/adformOpenRTBBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js similarity index 98% rename from test/spec/modules/adformOpenRTBBidAdapter_spec.js rename to test/spec/modules/adfBidAdapter_spec.js index 05788183e29..2c141d31bf8 100644 --- a/test/spec/modules/adformOpenRTBBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -1,14 +1,21 @@ // jshint esversion: 6, es3: false, node: true import {assert, expect} from 'chai'; -import {spec} from 'modules/adformOpenRTBBidAdapter.js'; +import {spec} from 'modules/adfBidAdapter.js'; import { NATIVE } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; -describe('AdformOpenRTB adapter', function () { +describe('Adf adapter', function () { let serverResponse, bidRequest, bidResponses; let bids = []; + describe('backwards-compatibility', function () { + it('should have adformOpenRTB alias defined', function () { + assert.equal(spec.aliases[0].code, 'adformOpenRTB'); + assert.equal(spec.aliases[0].gvlid, 50); + }); + }); + describe('isBidRequestValid', function () { let bid = { 'bidder': 'adformOpenRTB', @@ -567,7 +574,6 @@ describe('AdformOpenRTB adapter', function () { assert.deepEqual(bids[0].netRevenue, false); assert.deepEqual(bids[0].currency, serverResponse.body.cur); assert.deepEqual(bids[0].mediaType, 'native'); - assert.deepEqual(bids[0].bidderCode, 'adformOpenRTB'); }); it('should set correct native params', function () { const bid = [ From d3c7731f4dcd8c946236afc54dc1d748a3c1c9d8 Mon Sep 17 00:00:00 2001 From: susyt Date: Fri, 23 Apr 2021 11:25:35 -0700 Subject: [PATCH 0865/1476] GumGum Bid Adapter: use ad response sizes when found (#6649) * adds meta field to bidresponse * adds meta mediatype and advertiserdomain default * use response sizes in bidresponse --- modules/gumgumBidAdapter.js | 11 ++++++++--- test/spec/modules/gumgumBidAdapter_spec.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 606f8335a19..4786fd04b15 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -384,7 +384,9 @@ function interpretResponse (serverResponse, bidRequest) { ad: { price: 0, id: 0, - markup: '' + markup: '', + width: 0, + height: 0 }, pag: { pvid: 0 @@ -399,7 +401,9 @@ function interpretResponse (serverResponse, bidRequest) { price: cpm, id: creativeId, markup, - cur + cur, + width: responseWidth, + height: responseHeight }, cw: wrapper, pag: { @@ -415,7 +419,8 @@ function interpretResponse (serverResponse, bidRequest) { let product = data.pi let mediaType = (product === 6 || product === 7) ? VIDEO : BANNER let isTestUnit = (product === 3 && data.si === 9) - let sizes = utils.parseSizesInput(bidRequest.sizes) + // use response sizes if available + let sizes = responseWidth && responseHeight ? [`${responseWidth}x${responseHeight}`] : utils.parseSizesInput(bidRequest.sizes) let [width, height] = sizes[0].split('x') let metaData = { advertiserDomains: advertiserDomains || [], diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 2365fddd01f..a7b18a16173 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -564,6 +564,25 @@ describe('gumgumAdapter', function () { expect(result.length).to.equal(0); }); + it('uses response width and height', function () { + const result = spec.interpretResponse({ body: serverResponse }, bidRequest)[0]; + expect(result.width).to.equal(serverResponse.ad.width.toString()); + expect(result.height).to.equal(serverResponse.ad.height.toString()); + }); + + it('defaults to use bidRequest sizes when width and height are not found', function () { + const { ad, jcsi, pag, thms, meta } = serverResponse + const noAdSizes = { ...ad } + delete noAdSizes.width + delete noAdSizes.height + const responseWithoutSizes = { jcsi, pag, thms, meta, ad: noAdSizes } + const request = { ...bidRequest, sizes: [[100, 200]] } + const result = spec.interpretResponse({ body: responseWithoutSizes }, request)[0]; + + expect(result.width).to.equal(request.sizes[0][0].toString()) + expect(result.height).to.equal(request.sizes[0][1].toString()) + }); + it('returns 1x1 when eligible product and size available', function () { let inscreenBidRequest = { id: 12346, From 6c0142abd4831db5416bedd4a7291dfb7b477bc8 Mon Sep 17 00:00:00 2001 From: Serhii Mozhaiskyi <57671975+smozhaiskyi-rubi@users.noreply.github.com> Date: Fri, 23 Apr 2021 22:02:13 +0300 Subject: [PATCH 0866/1476] Rubicon Bid Adapter: add outstream rendering (#6469) * Magnite renderer support for Oustream ads * add functions for hiding ad units * Add unit tests * adding open source location of renderer * better minification Co-authored-by: bretg --- modules/rubiconBidAdapter.js | 66 +++++++++ test/spec/modules/rubiconBidAdapter_spec.js | 147 ++++++++++++++++++++ 2 files changed, 213 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index ccb713f8978..b8670706b60 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -3,10 +3,13 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; +import { Renderer } from '../src/Renderer.js'; import { getGlobal } from '../src/prebidGlobal.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; +const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.0.0.js'; +// renderer code at https://github.com/rubicon-project/apex2 let rubiConf = {}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire @@ -651,6 +654,11 @@ export const spec = { if (bid.adm) { bidObject.vastXml = bid.adm; } if (bid.nurl) { bidObject.vastUrl = bid.nurl; } if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; } + + const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); + if (videoContext.toLowerCase() === 'outstream') { + bidObject.renderer = outstreamRenderer(bidObject); + } } else { utils.logWarn('Rubicon: video response received non-video media type'); } @@ -810,6 +818,64 @@ function _renderCreative(script, impId) { `; } +function hideGoogleAdsDiv(adUnit) { + const el = adUnit.querySelector("div[id^='google_ads']"); + if (el) { + el.style.setProperty('display', 'none'); + } +} + +function hideSmartAdServerIframe(adUnit) { + const el = adUnit.querySelector("script[id^='sas_script']"); + const nextSibling = el && el.nextSibling; + if (nextSibling && nextSibling.localName === 'iframe') { + nextSibling.style.setProperty('display', 'none'); + } +} + +function renderBid(bid) { + // hide existing ad units + const adUnitElement = document.getElementById(bid.adUnitCode); + hideGoogleAdsDiv(adUnitElement); + hideSmartAdServerIframe(adUnitElement); + + // configure renderer + const config = bid.renderer.getConfig(); + bid.renderer.push(() => { + window.MagniteApex.renderAd({ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + placement: { + attachTo: `#${bid.adUnitCode}`, + align: config.align || 'center', + position: config.position || 'append' + }, + closeButton: config.closeButton || false, + label: config.label || undefined, + collapse: config.collapse || true + }); + }); +} + +function outstreamRenderer(rtbBid) { + const renderer = Renderer.install({ + id: rtbBid.adId, + url: rubiConf.rendererUrl || DEFAULT_RENDERER_URL, + config: rubiConf.rendererConfig || {}, + loaded: false, + adUnitCode: rtbBid.adUnitCode + }); + + try { + renderer.setRender(renderBid); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + function parseSizes(bid, mediaType) { let params = bid.params; if (mediaType === 'video') { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 5ec90fd5f64..6b061e3157d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -3050,6 +3050,153 @@ describe('the rubicon adapter', function () { }); }); + describe('for outstream video', function () { + const sandbox = sinon.createSandbox(); + beforeEach(function () { + createVideoBidderRequestOutstream(); + config.setConfig({rubicon: { + rendererConfig: { + align: 'left', + closeButton: true + }, + rendererUrl: 'https://example.test/renderer.js' + }}); + window.MagniteApex = { + renderAd: function() { + return null; + } + } + }); + + afterEach(function () { + sandbox.restore(); + delete window.MagniteApex; + }); + + it('should register a successful bid', function () { + let response = { + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'outstream_video1', + adomain: ['test.com'], + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201, + advid: 12345 + } + }, + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } + } + }], + group: 0, + seat: 'rubicon' + }], + }; + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + + expect(bids).to.be.lengthOf(1); + + expect(bids[0].seatBidId).to.equal('0'); + expect(bids[0].creativeId).to.equal('4259970'); + expect(bids[0].cpm).to.equal(2); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); + expect(bids[0].mediaType).to.equal('video'); + expect(bids[0].meta.mediaType).to.equal('video'); + expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); + expect(bids[0].meta.advertiserId).to.equal(12345); + expect(bids[0].bidderCode).to.equal('rubicon'); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].width).to.equal(640); + expect(bids[0].height).to.equal(320); + // check custom renderer + expect(typeof bids[0].renderer).to.equal('object'); + expect(bids[0].renderer.getConfig()).to.deep.equal({ + align: 'left', + closeButton: true + }); + expect(bids[0].renderer.url).to.equal('https://example.test/renderer.js'); + }); + + it('should render ad with Magnite renderer', function () { + let response = { + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'outstream_video1', + adomain: ['test.com'], + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201, + advid: 12345 + } + }, + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } + }, + nurl: 'https://test.com/vast.xml' + }], + group: 0, + seat: 'rubicon' + }], + }; + + sinon.spy(window.MagniteApex, 'renderAd'); + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + const bid = bids[0]; + bid.adUnitCode = 'outstream_video1_placement'; + const adUnit = document.createElement('div'); + adUnit.id = bid.adUnitCode; + document.body.appendChild(adUnit); + + bid.renderer.render(bid); + + const renderCall = window.MagniteApex.renderAd.getCall(0); + expect(renderCall.args[0]).to.deep.equal({ + closeButton: true, + collapse: true, + height: 320, + label: undefined, + placement: { + align: 'left', + attachTo: '#outstream_video1_placement', + position: 'append', + }, + vastUrl: 'https://test.com/vast.xml', + width: 640 + }); + // cleanup + adUnit.remove(); + }); + }); + describe('config with integration type', () => { it('should use the integration type provided in the config instead of the default', () => { config.setConfig({rubicon: {int_type: 'testType'}}); From 15c31c89f5a6e1a85a1d6d26090afc40b4c7d795 Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Fri, 23 Apr 2021 22:19:51 -0400 Subject: [PATCH 0867/1476] Halo RTD submodule: update docs (#6651) --- modules/haloRtdProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/haloRtdProvider.md b/modules/haloRtdProvider.md index 02dc6577e49..4307618bb60 100644 --- a/modules/haloRtdProvider.md +++ b/modules/haloRtdProvider.md @@ -16,7 +16,7 @@ and the bidstream in real-time during the bid request cycle. Compile the Halo RTD module into your Prebid build: -`gulp build --modules=userId,unifiedIdSystem,rtdModule,audigentRtdProvider,appnexusBidAdapter` +`gulp build --modules=userId,unifiedIdSystem,rtdModule,haloRtdProvider,appnexusBidAdapter` Add the Halo RTD provider to your Prebid config. In this example we will configure publisher 1234 to retrieve segments from Audigent. See the From 722afa1efd40165f3fc813441a4d8705df63fb5c Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Sat, 24 Apr 2021 21:57:25 +0500 Subject: [PATCH 0868/1476] sid into tags for ZetaSsp (#6636) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 3 ++- modules/zetaSspBidAdapter.md | 3 ++- test/spec/modules/zetaSspBidAdapter_spec.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index d6f2039dff1..8f4d8995800 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -57,7 +57,8 @@ export const spec = { user: params.user ? params.user : {}, app: params.app ? params.app : {}, ext: { - tags: params.tags ? params.tags : {} + tags: params.tags ? params.tags : {}, + sid: params.sid ? params.sid : {} } }; diff --git a/modules/zetaSspBidAdapter.md b/modules/zetaSspBidAdapter.md index 332f4b302d0..d2950bce6b9 100644 --- a/modules/zetaSspBidAdapter.md +++ b/modules/zetaSspBidAdapter.md @@ -30,7 +30,8 @@ Module that connects to Zeta's SSP buyeruid: 12345 }, tags: { - someTag: 123 + someTag: 123, + sid: 'publisherId' }, test: 1 } diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 49fab977a06..bdfc64c3234 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -19,7 +19,8 @@ describe('Zeta Ssp Bid Adapter', function() { buyeruid: 12345 }, tags: { - someTag: 123 + someTag: 123, + sid: 'publisherId' }, test: 1 } From 4d842d28c7b54df7e1850148d8f9836fce6629a4 Mon Sep 17 00:00:00 2001 From: David Reischer Date: Mon, 26 Apr 2021 13:26:50 +0100 Subject: [PATCH 0869/1476] Add module name to storage manager; catch errors (#6596) --- modules/permutiveRtdProvider.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index db431ed45a7..91e88d3e4e1 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -8,10 +8,11 @@ import { getGlobal } from '../src/prebidGlobal.js' import { submodule } from '../src/hook.js' import { getStorageManager } from '../src/storageManager.js' -import { deepSetValue, deepAccess, isFn, mergeDeep } from '../src/utils.js' +import { deepSetValue, deepAccess, isFn, mergeDeep, logError } from '../src/utils.js' import includes from 'core-js-pure/features/array/includes.js' +const MODULE_NAME = 'permutive' -export const storage = getStorageManager() +export const storage = getStorageManager(null, MODULE_NAME) function init (config, userConsent) { return true @@ -60,13 +61,19 @@ function setSegments (reqBidsConfigObj, config) { customFn(bid, data, acEnabled, utils, defaultFn) } else if (defaultFn) { defaultFn(bid, data, acEnabled) - } else { - } }) }) } +function makeSafe (fn) { + try { + fn() + } catch (e) { + logError(e) + } +} + function getCustomBidderFn (config, bidder) { const overwriteFn = deepAccess(config, `params.overwrites.${bidder}`) @@ -170,8 +177,12 @@ function readSegments (key) { /** @type {RtdSubmodule} */ export const permutiveSubmodule = { - name: 'permutive', - getBidRequestData: initSegments, + name: MODULE_NAME, + getBidRequestData: function (reqBidsConfigObj, callback, customConfig) { + makeSafe(function () { + initSegments(reqBidsConfigObj, callback, customConfig) + }) + }, init: init } From c408a6effe986cd7d2033e2f001a756be1ed8699 Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Mon, 26 Apr 2021 16:42:58 +0200 Subject: [PATCH 0870/1476] Adnuntius Bid Adatpter: Meta field advertiser domain update. (#6652) * RTD Provider rebase * wrongly merged to master * Added meta field for advertiser domains. --- modules/adnuntiusBidAdapter.js | 4 ++++ test/spec/modules/adnuntiusBidAdapter_spec.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index e5878ad047d..56f1bb02981 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -56,6 +56,10 @@ export const spec = { height: Number(bid.creativeHeight), creativeId: bid.creativeId, currency: (bid.bid) ? bid.bid.currency : 'EUR', + meta: { + advertiserDomains: (bid.destinationUrls.destination) ? [bid.destinationUrls.destination.split('/')[2]] : [] + + }, netRevenue: false, ttl: 360, ad: adUnit.html diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 44afa5c59e4..d234a345b5c 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -123,6 +123,9 @@ describe('adnuntiusBidAdapter', function () { 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].ad).to.equal(serverResponse.body.adUnits[0].html); expect(interpretedResponse[0].ttl).to.equal(360); }); From 40b7ec0f2321d41a353bb808459cf34e9f5099df Mon Sep 17 00:00:00 2001 From: Roberto Hsu Wu Date: Mon, 26 Apr 2021 13:30:08 -0300 Subject: [PATCH 0871/1476] Gnet Bid Adapter: add new bid adapter (#6536) * Add files via upload * Add files via upload * Change params on gnetBidder * ADJ - Use parseSizesInput to get sizes ADJ - Check serverResponse object ADJ - Remove getUserSyncs function * ADJ - Change prebid endpoint * ADJ - Change endpoint on test --- modules/gnetBidAdapter.js | 101 ++++++++++++++++ modules/gnetBidAdapter.md | 33 ++++++ test/spec/modules/gnetBidAdapter_spec.js | 145 +++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 modules/gnetBidAdapter.js create mode 100644 modules/gnetBidAdapter.md create mode 100644 test/spec/modules/gnetBidAdapter_spec.js diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js new file mode 100644 index 00000000000..3469c897a6a --- /dev/null +++ b/modules/gnetBidAdapter.js @@ -0,0 +1,101 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'gnet'; +const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [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. + */ + isBidRequestValid: function (bid) { + return !!(bid.params.websiteId && bid.params.externalId); + }, + + /** + * 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 bidRequests = []; + const referer = bidderRequest.refererInfo.referer; + + utils._each(validBidRequests, (request) => { + const data = {}; + + data.referer = referer; + data.adUnitCode = request.adUnitCode; + data.bidId = request.bidId; + data.transactionId = request.transactionId; + + data.sizes = utils.parseSizesInput(request.sizes); + + data.params = request.params; + + const payloadString = JSON.stringify(data); + + bidRequests.push({ + method: 'POST', + url: ENDPOINT, + mode: 'no-cors', + options: { + withCredentials: false, + }, + data: payloadString + }); + }); + + return bidRequests; + }, + + /** + * 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, requests) { + if (typeof serverResponse !== 'object') { + return []; + } + + const res = serverResponse && serverResponse.body; + + if (utils.isEmpty(res)) { + return []; + } + + if (res.bids) { + const bids = []; + utils._each(res.bids, (bidData) => { + const bid = { + requestId: bidData.bidId, + cpm: bidData.cpm, + currency: bidData.currency, + width: bidData.width, + height: bidData.height, + ad: bidData.ad, + ttl: 300, + creativeId: bidData.creativeId, + netRevenue: true, + }; + bids.push(bid); + }); + + return bids; + } + + return []; + }, +}; + +registerBidder(spec); diff --git a/modules/gnetBidAdapter.md b/modules/gnetBidAdapter.md new file mode 100644 index 00000000000..6dac9be17b6 --- /dev/null +++ b/modules/gnetBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +``` +Module Name: Gnet Bidder Adapter +Module Type: Bidder Adapter +Maintainer: roberto.wu@grumft.com +``` + +# Description + +Module that connects to Example's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: '/150790500/4_ZONA_IAB_300x250_5', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'gnet', + params: { + websiteId: '4', + externalId: '4d52cccf30309282256012cf30309282' + } + } + ] + } + ]; \ No newline at end of file diff --git a/test/spec/modules/gnetBidAdapter_spec.js b/test/spec/modules/gnetBidAdapter_spec.js new file mode 100644 index 00000000000..40f8ad50d78 --- /dev/null +++ b/test/spec/modules/gnetBidAdapter_spec.js @@ -0,0 +1,145 @@ +import { + expect +} from 'chai'; +import { + spec +} from 'modules/gnetBidAdapter.js'; +import { + newBidder +} from 'src/adapters/bidderFactory.js'; + +const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; + +describe('gnetAdapter', 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: 'gnet', + params: { + websiteId: '4', + externalId: '4d52cccf30309282256012cf30309282' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + bidder: 'gnet', + params: { + websiteId: '4', + externalId: '4d52cccf30309282256012cf30309282' + }, + adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', + sizes: [ + [300, 250], + ], + bidId: '2a19afd5173318', + bidderRequestId: '1f4001782ac16c', + auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', + transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' + }]; + + const bidderRequest = { + refererInfo: { + referer: 'https://gnetproject.com/' + } + }; + + it('sends bid request to ENDPOINT via POST', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].data).to.equal(JSON.stringify({ + 'referer': 'https://gnetproject.com/', + 'adUnitCode': '/150790500/4_ZONA_IAB_300x250_5', + 'bidId': '2a19afd5173318', + 'transactionId': '894bdff6-61ec-4bec-a5a9-f36a5bfccef5', + 'sizes': ['300x250'], + 'params': { + 'websiteId': '4', + 'externalId': '4d52cccf30309282256012cf30309282' + } + })); + }); + }); + + describe('interpretResponse', function () { + const bidderRequests = [{ + bidder: 'gnet', + params: { + clientId: '123456' + }, + adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', + sizes: [ + [300, 250], + ], + bidId: '2a19afd5173318', + bidderRequestId: '1f4001782ac16c', + auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', + transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' + }]; + + it('should get correct banner bid response', function () { + const response = { + bids: [ + { + bidId: '2a19afd5173318', + cpm: 0.1, + currency: 'BRL', + width: 300, + height: 250, + ad: '

I am an ad

', + creativeId: '173560700', + } + ] + }; + + const expectedResponse = [ + { + requestId: '2a19afd5173318', + cpm: 0.1, + currency: 'BRL', + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 300, + creativeId: '173560700', + netRevenue: true + } + ]; + + const result = spec.interpretResponse({ + body: response + }, bidderRequests); + expect(result).to.have.lengthOf(1); + expect(result).to.deep.have.same.members(expectedResponse); + }); + + it('handles nobid responses', function () { + const response = ''; + + const result = spec.interpretResponse({ + body: response + }, bidderRequests); + expect(result.length).to.equal(0); + }); + }); +}); From 960c669f78ec79c9400b2a7860fea9b8fcffc0c9 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Mon, 26 Apr 2021 20:22:05 +0300 Subject: [PATCH 0872/1476] TheMediaGrid Bid Adapter: added support of PBAdSlot module (#6609) * 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 --- modules/gridBidAdapter.js | 8 ++++- test/spec/modules/gridBidAdapter_spec.js | 42 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 964b34dcfa2..994244b8a50 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -76,7 +76,7 @@ export const spec = { if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); @@ -98,6 +98,12 @@ export const spec = { divid: adUnitCode } }; + if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { + impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot; + } + } if (bidFloor) { impObj.bidfloor = bidFloor; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 2e8601bddf6..6cdef43b150 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -428,6 +428,48 @@ describe('TheMediaGrid Adapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); + it('should contain imp[].ext.data.adserver if available', function() { + const ortb2Imp = [{ + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/111111/slot' + }, + pbadslot: '/111111/slot' + } + } + }, { + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/222222/slot' + }, + pbadslot: '/222222/slot' + } + } + }]; + const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { + return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + }); + const request = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].ext).to.deep.equal({ + divid: bidRequests[0].adUnitCode, + data: ortb2Imp[0].ext.data, + gpid: ortb2Imp[0].ext.data.adserver.adslot + }); + expect(payload.imp[1].ext).to.deep.equal({ + divid: bidRequests[1].adUnitCode, + data: ortb2Imp[1].ext.data, + gpid: ortb2Imp[1].ext.data.adserver.adslot + }); + expect(payload.imp[2].ext).to.deep.equal({ + divid: bidRequests[2].adUnitCode + }); + }); describe('floorModule', function () { const floorTestData = { 'currency': 'USD', From d6a2ed7d151ca31e81fe2e6e61c61aec96c165a0 Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Mon, 26 Apr 2021 21:18:53 -0400 Subject: [PATCH 0873/1476] Sonobi Bid Adapter: added Coppa Flag check (#6631) * Sonobi - Added Coppa Flag check * added unit test for sonobi coppa flag --- modules/sonobiBidAdapter.js | 6 ++++++ test/spec/modules/sonobiBidAdapter_spec.js | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 0e4bfb37829..e5bd76eba9f 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -140,6 +140,12 @@ export const spec = { payload.us_privacy = bidderRequest.uspConsent; } + if (config.getConfig('coppa') === true) { + payload.coppa = 1; + } else { + payload.coppa = 0; + } + // If there is no key_maker data, then don't make the request. if (isEmpty(data)) { return null; diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index d1ac200394c..0314ffb71c1 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -2,10 +2,10 @@ import { expect } from 'chai' import { spec, _getPlatform } from 'modules/sonobiBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import {userSync} from '../../../src/userSync.js'; +import { config } from 'src/config.js'; describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) - describe('.code', function () { it('should return a bidder code of sonobi', function () { expect(spec.code).to.equal('sonobi') @@ -304,6 +304,20 @@ describe('SonobiBidAdapter', function () { uspConsent: 'someCCPAString' }; + it('should populate coppa as 1 if set in config', function () { + config.setConfig({coppa: true}); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + + expect(bidRequests.data.coppa).to.equal(1); + }); + + it('should populate coppa as 0 if set in config', function () { + config.setConfig({coppa: false}); + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + + expect(bidRequests.data.coppa).to.equal(0); + }); + it('should return a properly formatted request', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests) const bidRequestsPageViewID = spec.buildRequests(bidRequest, bidderRequests) From f3624ebe0a21f525af7c5bf315c55641b38ab603 Mon Sep 17 00:00:00 2001 From: supportGothamad <75782642+supportGothamad@users.noreply.github.com> Date: Tue, 27 Apr 2021 15:05:27 +0300 Subject: [PATCH 0874/1476] GothamAds Bid Adapter: add at, ccpa, gdpr and coppa support (#6470) * update gothamAds adapter * update GothamAdsAdapter according to commetns. Add meta for adomains --- modules/gothamadsBidAdapter.js | 61 ++++++-- test/spec/modules/gothamadsBidAdapter_spec.js | 133 ++++++++++++------ 2 files changed, 138 insertions(+), 56 deletions(-) diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index f2d2a9f5261..ff6fa5221f3 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -1,12 +1,19 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'gothamads'; const ACCOUNTID_MACROS = '[account_id]'; const URL_ENDPOINT = `https://us-e-node1.gothamads.com/bid?pass=${ACCOUNTID_MACROS}&integration=prebidjs`; -const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_ASSET_IDS = { + 0: 'title', + 2: 'icon', + 3: 'image', + 5: 'sponsoredBy', + 4: 'body', + 1: 'cta' +}; const NATIVE_PARAMS = { title: { id: 0, @@ -94,9 +101,23 @@ export const spec = { source: { tid: bidRequest.transactionId }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {} + }, tmax: bidRequest.timeout, imp: [impObject], }; + + if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { + utils.deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); + utils.deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); + } + + if (bidRequest.uspConsent !== undefined) { + utils.deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); + } + bids.push(data) } return { @@ -114,10 +135,10 @@ export const spec = { */ interpretResponse: (serverResponse) => { if (!serverResponse || !serverResponse.body) return [] - let GothamAdskResponse = serverResponse.body; + let GothamAdsResponse = serverResponse.body; let bids = []; - for (let response of GothamAdskResponse) { + for (let response of GothamAdsResponse) { let mediaType = response.seatbid[0].bid[0].ext && response.seatbid[0].bid[0].ext.mediaType ? response.seatbid[0].bid[0].ext.mediaType : BANNER; let bid = { @@ -133,16 +154,21 @@ export const spec = { mediaType: mediaType }; + bid.meta = {}; + if (response.seatbid[0].bid[0].adomain && response.seatbid[0].bid[0].adomain.length > 0) { + bid.meta.advertiserDomains = response.seatbid[0].bid[0].adomain; + } + switch (mediaType) { case VIDEO: - bid.vastXml = response.seatbid[0].bid[0].adm - bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl - break + bid.vastXml = response.seatbid[0].bid[0].adm; + bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl; + break; case NATIVE: - bid.native = parseNative(response.seatbid[0].bid[0].adm) - break + bid.native = parseNative(response.seatbid[0].bid[0].adm); + break; default: - bid.ad = response.seatbid[0].bid[0].adm + bid.ad = response.seatbid[0].bid[0].adm; } bids.push(bid); @@ -164,18 +190,27 @@ const checkRequestType = (bidRequest, type) => { } const parseNative = admObject => { - const { assets, link, imptrackers, jstracker } = admObject.native; + const { + assets, + link, + imptrackers, + jstracker + } = admObject.native; const result = { clickUrl: link.url, clickTrackers: link.clicktrackers || undefined, impressionTrackers: imptrackers || undefined, - javascriptTrackers: jstracker ? [ jstracker ] : undefined + javascriptTrackers: jstracker ? [jstracker] : undefined }; assets.forEach(asset => { const kind = NATIVE_ASSET_IDS[asset.id]; const content = kind && asset[NATIVE_PARAMS[kind].name]; if (content) { - result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; + result[kind] = content.text || content.value || { + url: content.url, + width: content.w, + height: content.h + }; } }); diff --git a/test/spec/modules/gothamadsBidAdapter_spec.js b/test/spec/modules/gothamadsBidAdapter_spec.js index c638e7a0e3e..f0a3ea253f3 100644 --- a/test/spec/modules/gothamadsBidAdapter_spec.js +++ b/test/spec/modules/gothamadsBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/gothamadsBidAdapter.js'; +import { config } from 'src/config.js'; const NATIVE_BID_REQUEST = { code: 'native_example', @@ -44,7 +45,10 @@ const BANNER_BID_REQUEST = { code: 'banner_example', mediaTypes: { banner: { - sizes: [[300, 250], [300, 600]] + sizes: [ + [300, 250], + [300, 600] + ] } }, bidder: 'gothamads', @@ -53,7 +57,11 @@ const BANNER_BID_REQUEST = { accountId: 'accountId' }, timeout: 1000, - + gdprConsent: { + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + gdprApplies: 1, + }, + uspConsent: 'uspConsent' } const bidRequest = { @@ -65,26 +73,27 @@ const bidRequest = { const VIDEO_BID_REQUEST = { code: 'video1', sizes: [640, 480], - mediaTypes: { video: { - minduration: 0, - maxduration: 999, - boxingallowed: 1, - skip: 0, - mimes: [ - 'application/javascript', - 'video/mp4' - ], - w: 1920, - h: 1080, - protocols: [ - 2 - ], - linearity: 1, - api: [ - 1, - 2 - ] - } + mediaTypes: { + video: { + minduration: 0, + maxduration: 999, + boxingallowed: 1, + skip: 0, + mimes: [ + 'application/javascript', + 'video/mp4' + ], + w: 1920, + h: 1080, + protocols: [ + 2 + ], + linearity: 1, + api: [ + 1, + 2 + ] + } }, bidder: 'gothamads', @@ -148,20 +157,29 @@ const NATIVE_BID_RESPONSE = { impid: 'request_imp_id', price: 5, adomain: ['example.com'], - adm: { native: + adm: { + native: { + assets: [{ + id: 0, + title: 'dummyText' + }, + { + id: 3, + image: imgData + }, { - assets: [ - {id: 0, title: 'dummyText'}, - {id: 3, image: imgData}, - { - id: 5, - data: {value: 'organization.name'} - } - ], - link: {url: 'example.com'}, - imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], - jstracker: 'tracker1.com' + id: 5, + data: { + value: 'organization.name' + } } + ], + link: { + url: 'example.com' + }, + imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], + jstracker: 'tracker1.com' + } }, crid: 'crid', ext: { @@ -171,8 +189,23 @@ const NATIVE_BID_RESPONSE = { }], }; -describe('GothamAdsAdapter', function() { - describe('isBidRequestValid', function() { +describe('GothamAdsAdapter', function () { + describe('with COPPA', function () { + beforeEach(function () { + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + }); + afterEach(function () { + config.getConfig.restore(); + }); + + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]); + expect(serverRequest.data[0].regs.coppa).to.equal(1); + }); + }); + describe('isBidRequestValid', function () { it('should return true when required params found', function () { expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); }); @@ -221,6 +254,12 @@ describe('GothamAdsAdapter', function() { expect(request.data).to.exist; }); + it('check consent and ccpa string is set properly', function () { + expect(request.data[0].regs.ext.gdpr).to.equal(1); + expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString); + expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent); + }); + it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); }); @@ -250,7 +289,7 @@ describe('GothamAdsAdapter', function() { }); describe('interpretResponse', function () { - it('Empty response must return empty array', function() { + it('Empty response must return empty array', function () { const emptyResponse = null; let response = spec.interpretResponse(emptyResponse); @@ -273,6 +312,7 @@ describe('GothamAdsAdapter', function() { creativeId: BANNER_BID_RESPONSE.seatbid[0].bid[0].crid, dealId: BANNER_BID_RESPONSE.seatbid[0].bid[0].dealid, mediaType: 'banner', + meta: BANNER_BID_RESPONSE.seatbid[0].bid[0].adomain, ad: BANNER_BID_RESPONSE.seatbid[0].bid[0].adm } @@ -281,13 +321,14 @@ describe('GothamAdsAdapter', function() { 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'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); expect(dataItem.ad).to.equal(expectedBidResponse.ad); expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); expect(dataItem.netRevenue).to.be.true; + expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta); expect(dataItem.currency).to.equal(expectedBidResponse.currency); expect(dataItem.width).to.equal(expectedBidResponse.width); expect(dataItem.height).to.equal(expectedBidResponse.height); @@ -309,6 +350,7 @@ describe('GothamAdsAdapter', function() { creativeId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid, dealId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].dealid, mediaType: 'video', + meta: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adomain, vastXml: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm, vastUrl: VIDEO_BID_RESPONSE.seatbid[0].bid[0].ext.vastUrl } @@ -318,12 +360,13 @@ describe('GothamAdsAdapter', function() { expect(videoResponses).to.be.an('array').that.is.not.empty; let dataItem = videoResponses[0]; expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml) + expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml); expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta); expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal(expectedBidResponse.currency); expect(dataItem.width).to.equal(expectedBidResponse.width); @@ -345,8 +388,11 @@ describe('GothamAdsAdapter', function() { netRevenue: true, creativeId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].crid, dealId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].dealid, + meta: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adomain, mediaType: 'native', - native: {clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url} + native: { + clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url + } } let nativeResponses = spec.interpretResponse(nativeResponse); @@ -354,11 +400,12 @@ describe('GothamAdsAdapter', function() { expect(nativeResponses).to.be.an('array').that.is.not.empty; let dataItem = nativeResponses[0]; expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl) + expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl); expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta); expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal(expectedBidResponse.currency); From cef4c899710733e6c8097a06efc3849a425a0a54 Mon Sep 17 00:00:00 2001 From: Thomas Skaarnes Nikitin <48713095+thomas-netric@users.noreply.github.com> Date: Tue, 27 Apr 2021 14:15:34 +0200 Subject: [PATCH 0875/1476] Rubicon Bid Adapter: Added new size - Id 558 (640x640) (#6658) --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b8670706b60..6c6ac83318a 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -109,7 +109,8 @@ var sizeMap = { 288: '640x380', 548: '500x1000', 550: '980x480', - 552: '300x200' + 552: '300x200', + 558: '640x640' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From a0241781ef02ffac7e991b43e2ef2f6a65f9e23a Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 27 Apr 2021 08:48:38 -0400 Subject: [PATCH 0876/1476] AOL, RTBHouse, RhythmOne, Teads, OpenX and DeepIntent Bid Adapters: add support for meta.advertiserDomains (#6656) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Delete iasBidAdapter.js * Add files via upload * Update deepintentBidAdapter.js * Update deepintentBidAdapter_spec.js * Update rtbhouseBidAdapter.js * Update rtbhouseBidAdapter_spec.js * Update deepintentBidAdapter.js * Update rtbhouseBidAdapter.js * Update rtbhouseBidAdapter.js * Update rtbhouseBidAdapter_spec.js * Update deepintentBidAdapter.js * Update rhythmoneBidAdapter.js * Update rhythmoneBidAdapter_spec.js * Update aolBidAdapter.js * Update aolBidAdapter_spec.js * Update aolBidAdapter.js * Update aolBidAdapter.js * Update teadsBidAdapter.js * Update teadsBidAdapter_spec.js * Update openxBidAdapter_spec.js * Update openxBidAdapter.js * Update openxBidAdapter.js * Update openxBidAdapter_spec.js * Update openxBidAdapter.js --- modules/aolBidAdapter.js | 3 +++ modules/deepintentBidAdapter.js | 3 +++ modules/openxBidAdapter.js | 10 ++++++---- modules/rhythmoneBidAdapter.js | 3 +++ modules/rtbhouseBidAdapter.js | 6 ++++++ modules/teadsBidAdapter.js | 3 +++ test/spec/modules/aolBidAdapter_spec.js | 3 +++ test/spec/modules/deepintentBidAdapter_spec.js | 1 + test/spec/modules/openxBidAdapter_spec.js | 10 +++++----- test/spec/modules/rhythmoneBidAdapter_spec.js | 1 + test/spec/modules/rtbhouseBidAdapter_spec.js | 2 ++ test/spec/modules/teadsBidAdapter_spec.js | 6 ++++++ 12 files changed, 42 insertions(+), 9 deletions(-) diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index c899da32340..5a5d5e6f417 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -417,6 +417,9 @@ export const spec = { currency: response.cur || 'USD', dealId: bidData.dealid, netRevenue: true, + meta: { + advertiserDomains: bidData && bidData.adomain ? bidData.adomain : [] + }, ttl: bidRequest.ttl }; }, diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index 9ec6c8e5bc2..a6a6cac6570 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -88,6 +88,9 @@ function formatResponse(bid) { width: bid && bid.w ? bid.w : 0, height: bid && bid.h ? bid.h : 0, ad: bid && bid.adm ? bid.adm : '', + meta: { + advertiserDomains: bid && bid.adomain ? bid.adomain : [] + }, creativeId: bid && bid.crid ? bid.crid : undefined, netRevenue: false, currency: bid && bid.cur ? bid.cur : 'USD', diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 460a721cb8b..8a455f6fa25 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -13,7 +13,6 @@ const DEFAULT_CURRENCY = 'USD'; export const USER_ID_CODE_TO_QUERY_ARG = { britepoolid: 'britepoolid', // BritePool ID criteoId: 'criteoid', // CriteoID - digitrustid: 'digitrustid', // DigiTrust fabrickId: 'nuestarid', // Fabrick ID by Nuestar haloId: 'audigentid', // Halo ID from Audigent id5id: 'id5id', // ID5 ID @@ -158,6 +157,12 @@ function createBannerBidResponses(oxResponseObj, {bids, startTime}) { bidResponse.meta.brandId = adUnit.brand_id; } + if (adUnit.adomain && length(adUnit.adomain) > 0) { + bidResponse.meta.advertiserDomains = adUnit.adomain; + } else { + bidResponse.meta.advertiserDomains = []; + } + if (adUnit.adv_id) { bidResponse.meta.dspid = adUnit.adv_id; } @@ -283,9 +288,6 @@ function appendUserIdsToQueryParams(queryParams, userIds) { if (USER_ID_CODE_TO_QUERY_ARG.hasOwnProperty(userIdProviderKey)) { switch (userIdProviderKey) { - case 'digitrustid': - queryParams[key] = utils.deepAccess(userIdObjectOrValue, 'data.id'); - break; case 'lipb': queryParams[key] = userIdObjectOrValue.lipbid; break; diff --git a/modules/rhythmoneBidAdapter.js b/modules/rhythmoneBidAdapter.js index 1acbcfd0463..fa090044f05 100644 --- a/modules/rhythmoneBidAdapter.js +++ b/modules/rhythmoneBidAdapter.js @@ -253,6 +253,9 @@ function RhythmOneBidAdapter() { cpm: parseFloat(bid.price), width: bid.w, height: bid.h, + meta: { + advertiserDomains: bid.adomain + }, creativeId: bid.crid, currency: 'USD', netRevenue: true, diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 3337c3f1b59..6b8fe05e8bb 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -302,6 +302,9 @@ function interpretBannerBid(serverBid) { width: serverBid.w, height: serverBid.h, ttl: TTL, + meta: { + advertiserDomains: serverBid.adomain + }, netRevenue: true, currency: 'USD' } @@ -320,6 +323,9 @@ function interpretNativeBid(serverBid) { width: 1, height: 1, ttl: TTL, + meta: { + advertiserDomains: serverBid.adomain + }, netRevenue: true, currency: 'USD', native: interpretNativeAd(serverBid.adm), diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index aad7f6762c4..32be7e62bbd 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -99,6 +99,9 @@ export const spec = { currency: bid.currency, netRevenue: true, ttl: bid.ttl, + meta: { + advertiserDomains: bid && bid.adomain ? bid.adomain : [] + }, ad: bid.ad, requestId: bid.bidId, creativeId: bid.creativeId, diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 8e74e19f420..78be0618594 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -160,6 +160,9 @@ describe('AolAdapter', function () { currency: 'USD', dealId: 'deal-id', netRevenue: true, + meta: { + advertiserDomains: [] + }, ttl: bidRequest.ttl }); }); diff --git a/test/spec/modules/deepintentBidAdapter_spec.js b/test/spec/modules/deepintentBidAdapter_spec.js index 6d9b883e2bb..fcf7056fb3f 100644 --- a/test/spec/modules/deepintentBidAdapter_spec.js +++ b/test/spec/modules/deepintentBidAdapter_spec.js @@ -202,6 +202,7 @@ describe('Deepintent adapter', function () { expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h); expect(bResponse[0].currency).to.equal('USD'); expect(bResponse[0].netRevenue).to.equal(false); + expect(bResponse[0].meta.advertiserDomains).to.deep.equal(['deepintent.com']); expect(bResponse[0].ttl).to.equal(300); expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid); expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 580fd3dec93..7391c8826bc 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1043,7 +1043,6 @@ describe('OpenxAdapter', function () { const EXAMPLE_DATA_BY_ATTR = { britepoolid: '1111-britepoolid', criteoId: '1111-criteoId', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, fabrickId: '1111-fabrickid', haloId: '1111-haloid', id5id: {uid: '1111-id5id'}, @@ -1099,9 +1098,6 @@ describe('OpenxAdapter', function () { let userIdValue; // handle cases where userId key refers to an object switch (userIdProviderKey) { - case 'digitrustid': - userIdValue = EXAMPLE_DATA_BY_ATTR.digitrustid.data.id; - break; case 'lipb': userIdValue = EXAMPLE_DATA_BY_ATTR.lipb.lipbid; break; @@ -1574,7 +1570,11 @@ describe('OpenxAdapter', function () { expect(bid.meta.brandId).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.brand_id); }); - it('should return a brand ID', function () { + it('should return an adomain', function () { + expect(bid.meta.advertiserDomains).to.deep.equal([]); + }); + + it('should return a dsp ID', function () { expect(bid.meta.dspid).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.adv_id); }); }); diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index d9342332e61..93735c019e1 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -157,6 +157,7 @@ describe('rhythmone adapter tests', function () { expect(bid.width).to.equal(800); expect(bid.height).to.equal(600); expect(bid.vastUrl).to.equal('https://testdomain/rmp/placementid/0/path?reqId=1636037'); + expect(bid.meta.advertiserDomains).to.deep.equal(['test.com']); expect(bid.mediaType).to.equal('video'); expect(bid.creativeId).to.equal('cr-vid'); expect(bid.currency).to.equal('USD'); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index efaed87dd15..77dfff35c4a 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -424,6 +424,7 @@ describe('RTBHouseAdapter', () => { 'mediaType': 'banner', 'currency': 'USD', 'ttl': 300, + 'meta': { advertiserDomains: ['rtbhouse.com'] }, 'netRevenue': true } ]; @@ -486,6 +487,7 @@ describe('RTBHouseAdapter', () => { it('should contain native assets in valid format', () => { const bids = spec.interpretResponse({body: response}, {}); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['rtbhouse.com']); expect(bids[0].native).to.deep.equal({ title: 'Title text', clickUrl: encodeURIComponent('https://example.com'), diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index a6ae17ec8ff..3e1c3e78903 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -494,6 +494,9 @@ describe('teadsBidAdapter', () => { 'height': 250, 'currency': 'USD', 'netRevenue': true, + 'meta': { + advertiserDomains: [] + }, 'ttl': 360, 'ad': AD_SCRIPT, 'requestId': '3ede2a3fa0db94', @@ -505,6 +508,9 @@ describe('teadsBidAdapter', () => { 'height': 200, 'currency': 'USD', 'netRevenue': true, + 'meta': { + advertiserDomains: [] + }, 'ttl': 360, 'ad': AD_SCRIPT, 'requestId': '4fef3b4gb1ec15', From 1f046823dd05f5b8c6d9094aad1350a67f35a6ee Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 27 Apr 2021 18:29:48 +0200 Subject: [PATCH 0877/1476] Adnuntius Bid Adapter: bugfix to set undefined if no bids (#6662) * RTD Provider rebase * wrongly merged to master * Added meta field for advertiser domains. * Fixing bug where losing bids throw undefined into the response. * Wrapped if-statement --- modules/adnuntiusBidAdapter.js | 8 ++++---- test/spec/modules/adnuntiusBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 56f1bb02981..8752e37a96f 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -68,10 +68,10 @@ export const spec = { } else return response }, {}); - const bidResponse = bidRequest.bid.map(bid => bid.bidId) - .reduce((request, adunitId) => - request.concat(bidResponsesById[adunitId]) - , []); + const bidResponse = bidRequest.bid.map(bid => bid.bidId).reduce((request, adunitId) => { + if (bidResponsesById[adunitId]) { request.push(bidResponsesById[adunitId]) } + return request + }, []); return bidResponse }, diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index d234a345b5c..62073fc6aaa 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -78,8 +78,15 @@ describe('adnuntiusBidAdapter', function () { 'lineItemId': 'scyjdyv3mzgdsnpf', 'layoutId': 'sw6gtws2rdj1kwby', 'layoutName': 'Responsive image' - } + }, + ] + }, + { + 'auId': '000000000008b6bc', + 'targetId': '456', + 'matchedAdCount': 0, + 'responseId': 'adn-rsp-1460129238', } ] } @@ -115,7 +122,6 @@ describe('adnuntiusBidAdapter', function () { it('should return valid response when passed valid server response', function () { const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); const ad = serverResponse.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)); From 71d75c4f212995ec49e9928f59ff19ee539a8b31 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 27 Apr 2021 11:53:22 -0700 Subject: [PATCH 0878/1476] pass auctionId (#6664) --- modules/rubiconAnalyticsAdapter.js | 3 ++- test/spec/modules/rubiconAnalyticsAdapter_spec.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index f06af144b54..3237facb2e7 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -231,7 +231,8 @@ function sendMessage(auctionId, bidWonId) { clientTimeoutMillis: auctionCache.timeout, samplingFactor, accountId, - adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]) + adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]), + requestId: auctionId }; // pick our of top level floor data we want to send! diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 3cfaa448472..df8bf03b56a 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -374,6 +374,7 @@ const ANALYTICS_MESSAGE = { 'referrerHostname': 'www.test.com', 'auctions': [ { + 'requestId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', 'clientTimeoutMillis': 3000, 'serverTimeoutMillis': 1000, 'accountId': 1001, From a96f1f953269ebe8be3795a036a27d6b38cf9ffa Mon Sep 17 00:00:00 2001 From: jackhsiehucf <77815341+jackhsiehucf@users.noreply.github.com> Date: Wed, 28 Apr 2021 16:58:36 +0800 Subject: [PATCH 0879/1476] ucfunnel Bid Adapter: add support for Unified ID 2 (#6647) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel fix error message in debug mode * ucfunnel adapter add bidfloor parameter * ucfunnel adapter support CCPA * ucfunnel adapter native support clicktrackers * ucfunnel adapter change cookie sync setting * ucfunnel adapter update request parameter * Update ucfunnelBidAdapter * ucfunnel adapter add currency in ad response * ucfunnel adapter support uid2 Co-authored-by: root Co-authored-by: Ryan Chou Co-authored-by: ucfunnel Co-authored-by: jack.hsieh --- modules/ucfunnelBidAdapter.js | 5 +++++ test/spec/modules/ucfunnelBidAdapter_spec.js | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 4003c6ed5fd..9b9134a8ef0 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -331,6 +331,11 @@ function addUserId(bidData, userId) { bidData[userIdProviderKey + '_linkType'] = userIdObjectOrValue.ext.linkType; } break; + case 'uid2': + if (userIdObjectOrValue.id) { + bidData['eids'] = userIdProviderKey + ',' + userIdObjectOrValue.id + } + break; default: bidData[userIdProviderKey] = userIdObjectOrValue; break; diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index a2939eb95c3..d7e82338ff3 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -16,7 +16,8 @@ const userId = { 'pubcid': 'd8aa10fa-d86c-451d-aad8-5f16162a9e64', 'sharedid': {'id': '01ESHXW4HD29KMF387T63JQ9H5', 'third': '01ESHXW4HD29KMF387T63JQ9H5'}, 'tdid': 'D6885E90-2A7A-4E0F-87CB-7734ED1B99A3', - 'haloId': {} + 'haloId': {}, + 'uid2': {'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888'} } const validBannerBidReq = { @@ -158,6 +159,7 @@ describe('ucfunnel Adapter', function () { expect(data.adid).to.equal('ad-34BBD2AA24B678BBFD4E7B9EE3B872D'); expect(data.w).to.equal(width); expect(data.h).to.equal(height); + expect(data.eids).to.equal('uid2,eb33b0cb-8d35-4722-b9c0-1a31d4064888'); expect(data.schain).to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'); }); From 3419fc9e74397ff4cf5a264bf22d6d290eba5436 Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Wed, 28 Apr 2021 15:27:53 +0300 Subject: [PATCH 0880/1476] Intent IQ ID Systems: first party updates (#6618) * New features in iiq * intentIqIdSystem.js updated logic & tests * Tests fix * api update * tests update --- modules/intentIqIdSystem.js | 129 ++++++++++++++++++--- test/spec/modules/intentIqIdSystem_spec.js | 60 +++++++--- 2 files changed, 159 insertions(+), 30 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index c22a6dbc7aa..98732da95d5 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -8,32 +8,111 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js' +import {getStorageManager} from '../src/storageManager.js'; + +const PCID_EXPIRY = 365; const MODULE_NAME = 'intentIqId'; +export const FIRST_PARTY_KEY = '_iiq_fdata'; + +export const storage = getStorageManager(undefined, MODULE_NAME); const NOT_AVAILABLE = 'NA'; /** - * Verify the id is valid - Id value or Not Found (ignore not available response) - * @param id - * @returns {boolean|*|boolean} + * Verify the response is valid - Id value or Not Found (ignore not available response) + * @param response + * @param respJson - parsed json response + * @returns {boolean} */ -function isValidId(id) { - return id && id != '' && id != NOT_AVAILABLE && isValidResponse(id); +function isValidResponse(response, respJson) { + if (!response || response == '' || response === NOT_AVAILABLE) { + // Empty or NA response + return false; + } else if (respJson && (respJson.RESULT === NOT_AVAILABLE || respJson.data == '' || respJson.data === NOT_AVAILABLE)) { + // Response type is json with value NA + return false; + } else { return true; } } /** - * Ignore not available response JSON - * @param obj + * Verify the response json is valid + * @param respJson - parsed json response * @returns {boolean} */ -function isValidResponse(obj) { +function isValidResponseJson(respJson) { + if (respJson && 'data' in respJson) { + return true; + } else { return false; } +} + +/** + * Generate standard UUID string + * @return {string} + */ +function generateGUID() { + let d = new Date().getTime(); + const guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + return guid; +} + +/** + * Read Intent IQ data from cookie or local storage + * @param key + * @return {string} + */ +export function readData(key) { try { - obj = JSON.parse(obj); - return obj && obj['RESULT'] != NOT_AVAILABLE; + if (storage.hasLocalStorage()) { + return storage.getDataFromLocalStorage(key); + } + if (storage.cookiesAreEnabled()) { + return storage.getCookie(key); + } + } catch (error) { + utils.logError(error); + } +} + +/** + * Store Intent IQ data in either cookie or local storage + * expiration date: 365 days + * @param key + * @param {string} value IntentIQ ID value to sintentIqIdSystem_spec.jstore + */ +function storeData(key, value) { + try { + utils.logInfo(MODULE_NAME + ': storing data: key=' + key + ' value=' + value); + + if (value) { + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(key, value); + } + const expiresStr = (new Date(Date.now() + (PCID_EXPIRY * (60 * 60 * 24 * 1000)))).toUTCString(); + if (storage.cookiesAreEnabled()) { + storage.setCookie(key, value, expiresStr, 'LAX'); + } + } } catch (error) { utils.logError(error); - return true; + } +} + +/** + * Parse json if possible, else return null + * @param data + * @param {object|null} + */ +function tryParse(data) { + try { + return JSON.parse(data); + } catch (err) { + utils.logError(err); + return null; } } @@ -51,7 +130,7 @@ export const intentIqIdSubmodule = { * @returns {{intentIqId: {string}}|undefined} */ decode(value) { - return isValidId(value) ? { 'intentIqId': value } : undefined; + return isValidResponse(value, undefined) ? { 'intentIqId': value } : undefined; }, /** * performs action to obtain id and return a value in the callback's response argument @@ -66,22 +145,42 @@ export const intentIqIdSubmodule = { return; } + // Read Intent IQ 1st party id or generate it if none exists + let firstPartyData = tryParse(readData(FIRST_PARTY_KEY)); + if (!firstPartyData || !firstPartyData.pcid) { + const firstPartyId = generateGUID(); + firstPartyData = { 'pcid': firstPartyId }; + storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); + } + // use protocol relative urls for http or https let url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; url += configParams.pcid ? '&pcid=' + encodeURIComponent(configParams.pcid) : ''; url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; + if (firstPartyData) { + url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : ''; + url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : ''; + } const resp = function (callback) { const callbacks = { success: response => { - if (isValidId(response)) { - callback(response); + let respJson = tryParse(response); + if (isValidResponse(response, respJson) && isValidResponseJson(respJson)) { + // Store pid field if found in response json + if (firstPartyData && 'pcid' in firstPartyData && 'pid' in respJson) { + firstPartyData = { + 'pcid': firstPartyData.pcid, + 'pid': respJson.pid } + storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); + } + callback(respJson.data); } else { callback(); } }, error: error => { - utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + utils.logError(MODULE_NAME + ': ID fetch encountered an error', error); callback(); } }; diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 4b45342cb6f..7e819195b9f 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; +import {intentIqIdSubmodule, readData, FIRST_PARTY_KEY} from 'modules/intentIqIdSystem.js'; import * as utils from 'src/utils.js'; import {server} from 'test/mocks/xhr.js'; @@ -46,7 +46,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -55,9 +55,9 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should ignore NA and invalid responses', function () { - let resp = JSON.stringify({'RESULT': 'NA'}); - expect(intentIqIdSubmodule.decode(resp)).to.equal(undefined); + it('should ignore NA and invalid responses in decode', function () { + // let resp = JSON.stringify({'RESULT': 'NA'}); + // expect(intentIqIdSubmodule.decode(resp)).to.equal(undefined); expect(intentIqIdSubmodule.decode('NA')).to.equal(undefined); expect(intentIqIdSubmodule.decode('')).to.equal(undefined); expect(intentIqIdSubmodule.decode(undefined)).to.equal(undefined); @@ -68,7 +68,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(paiConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -82,7 +82,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(pcidConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -96,7 +96,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -110,7 +110,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 204, responseHeader, @@ -118,7 +118,6 @@ describe('IntentIQ tests', function () { ); expect(callBackSpy.calledOnce).to.be.true; expect(request.response).to.equal(''); - expect(logErrorStub.calledOnce).to.not.be.true; }); it('should log an error and continue to callback if ajax request errors', function () { @@ -126,7 +125,7 @@ describe('IntentIQ tests', function () { let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 503, responseHeader, @@ -135,17 +134,48 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should log an error and continue to callback if ajax request errors', function () { + it('should ignore {RESULT: NA} in get id', function () { let callBackSpy = sinon.spy(); let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1'); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( - 503, + 200, responseHeader, - 'Unavailable' + JSON.stringify({RESULT: 'NA'}) + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.args[0][0]).to.be.undefined; + }); + + it('should ignore NA in get id', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); + request.respond( + 200, + responseHeader, + 'NA' + ); + expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.args[0][0]).to.be.undefined; + }); + + it('should parse result from json response', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11&iiqidtype=2&iiqpcid='); + request.respond( + 200, + responseHeader, + JSON.stringify({pid: 'test_pid', data: 'test_personid'}) ); expect(callBackSpy.calledOnce).to.be.true; + expect(callBackSpy.args[0][0]).to.be.eq('test_personid'); }); }); From 9dee62404e815c2960a1e8c89619cb9d4a268df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Wed, 28 Apr 2021 16:55:26 +0200 Subject: [PATCH 0881/1476] Outbrain Bid Adapter: fix usersync query parameter formatting (#6668) --- modules/outbrainBidAdapter.js | 10 +++++---- test/spec/modules/outbrainBidAdapter_spec.js | 22 ++++++-------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index 304cdf2088c..c0af09c2b50 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -147,18 +147,20 @@ export const spec = { getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { const syncs = []; let syncUrl = config.getConfig('outbrain.usersyncUrl'); + + let query = []; if (syncOptions.pixelEnabled && syncUrl) { if (gdprConsent) { - syncUrl += '&gdpr=' + (gdprConsent.gdprApplies & 1); - syncUrl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + query.push('gdpr=' + (gdprConsent.gdprApplies & 1)); + query.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '')); } if (uspConsent) { - syncUrl += '&us_privacy=' + encodeURIComponent(uspConsent); + query.push('us_privacy=' + encodeURIComponent(uspConsent)); } syncs.push({ type: 'image', - url: syncUrl + url: syncUrl + (query.length ? '?' + query.join('&') : '') }); } return syncs; diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index 9671adf826e..4bdd657f419 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -493,19 +493,9 @@ describe('Outbrain Adapter', function () { config.resetConfig() }) - it('should return user sync if pixel enabled', function () { - const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) - }) it('should return user sync if pixel enabled with outbrain config', function () { - config.resetConfig() - config.setConfig({ - outbrain: { - usersyncUrl: 'https://usersync-url.com', - } - }) const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) + expect(ret).to.deep.equal([{type: 'image', url: usersyncUrl}]) }) it('should not return user sync if pixel disabled', function () { @@ -521,25 +511,25 @@ describe('Outbrain Adapter', function () { it('should pass GDPR consent', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo` + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo` }]); expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=0&gdpr_consent=foo` + type: 'image', url: `${usersyncUrl}?gdpr=0&gdpr_consent=foo` }]); expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=` + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=` }]); }); it('should pass US consent', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&us_privacy=1NYN` + type: 'image', url: `${usersyncUrl}?us_privacy=1NYN` }]); }); it('should pass GDPR and US consent', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` }]); }); }) From 43203d277440afda89ef8673a6c334ead648f1a9 Mon Sep 17 00:00:00 2001 From: John Rosendahl Date: Wed, 28 Apr 2021 10:00:01 -0600 Subject: [PATCH 0882/1476] Sovrn Bid Adapter: added FPD support (#6639) * added FPD support * using proper package-lock.json --- modules/sovrnBidAdapter.js | 28 +-- test/spec/modules/sovrnBidAdapter_spec.js | 220 ++++++++++++---------- 2 files changed, 131 insertions(+), 117 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 9ec4d339753..708cb110dbd 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -2,6 +2,7 @@ import * as utils from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' import { createEidsArray } from './userId/eids.js'; +import {config} from '../src/config.js'; export const spec = { code: 'sovrn', @@ -72,30 +73,29 @@ export const spec = { bidfloor: floorInfo.floor } - const segmentsString = utils.getBidIdParameter('segments', bid.params) + imp.ext = utils.getBidIdParameter('ext', bid.ortb2Imp) || undefined + const segmentsString = utils.getBidIdParameter('segments', bid.params) if (segmentsString) { - imp.ext = { - deals: segmentsString.split(',').map(deal => deal.trim()) - } + imp.ext = imp.ext || {} + imp.ext.deals = segmentsString.split(',').map(deal => deal.trim()) } + sovrnImps.push(imp) + }) - sovrnImps.push(imp); - }); - - const page = bidderRequest.refererInfo.referer + const fpd = config.getConfig('ortb2') || {} + const site = fpd.site || {} + site.page = bidderRequest.refererInfo.referer // clever trick to get the domain - const domain = utils.parseUrl(page).hostname + site.domain = utils.parseUrl(site.page).hostname const sovrnBidReq = { id: utils.getUniqueIdentifierStr(), imp: sovrnImps, - site: { - page, - domain - } - }; + site: site, + user: fpd.user || {} + } if (schain) { sovrnBidReq.source = { diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 84a0069c0b4..1050fdf4856 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -1,13 +1,14 @@ import {expect} from 'chai'; -import {LogError, spec} from 'modules/sovrnBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import {spec} from 'modules/sovrnBidAdapter.js'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js' const ENDPOINT = `https://ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$`; const adUnitBidRequest = { 'bidder': 'sovrn', 'params': { - 'tagid': '403370' + 'tagid': 403370 }, 'adUnitCode': 'adunit-code', 'sizes': [ @@ -18,82 +19,61 @@ const adUnitBidRequest = { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', } +const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + } +}; describe('sovrnBidAdapter', function() { - const adapter = newBidder(spec); - describe('isBidRequestValid', function () { - const bid = { - 'bidder': 'sovrn', - 'params': { - 'tagid': '403370' - }, - '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); + expect(spec.isBidRequestValid(adUnitBidRequest)).to.equal(true); }); it('should return false when tagid not passed correctly', function () { - bid.params.tagid = 'ABCD'; - expect(spec.isBidRequestValid(bid)).to.equal(false); + const bid = {...adUnitBidRequest} + const params = adUnitBidRequest.params + bid.params = {...params} + bid.params.tagid = 'ABCD' + expect(spec.isBidRequestValid(bid)).to.equal(false) }); it('should return false when require params are not passed', function () { - let bid = Object.assign({}, bid); + const bid = {...adUnitBidRequest} bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); describe('buildRequests', function () { - const bidRequests = [{ - 'bidder': 'sovrn', - 'params': { - 'tagid': '403370' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', - } - }; - const request = spec.buildRequests(bidRequests, bidderRequest); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('attaches source and version to endpoint URL as query params', function () { - expect(request.url).to.equal(ENDPOINT) - }); - - it('sets the proper banner object', function() { - const payload = JSON.parse(request.data) - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) - expect(payload.imp[0].banner.w).to.equal(1) - expect(payload.imp[0].banner.h).to.equal(1) - }) - - it('includes the ad unit code int the request', function() { - const payload = JSON.parse(request.data); - expect(payload.imp[0].adunitcode).to.equal('adunit-code') + describe('basic bid parameters', function() { + const bidRequests = [adUnitBidRequest]; + const request = spec.buildRequests(bidRequests, bidderRequest); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('attaches source and version to endpoint URL as query params', function () { + expect(request.url).to.equal(ENDPOINT) + }); + + it('sets the proper banner object', function() { + const payload = JSON.parse(request.data) + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + expect(payload.imp[0].banner.w).to.equal(1) + expect(payload.imp[0].banner.h).to.equal(1) + }) + + it('includes the ad unit code int the request', function() { + const payload = JSON.parse(request.data); + expect(payload.imp[0].adunitcode).to.equal('adunit-code') + }) + + it('converts tagid to string', function () { + expect(request.data).to.contain('"tagid":"403370"') + }); }) it('accepts a single array as a size', function() { @@ -109,11 +89,6 @@ describe('sovrnBidAdapter', function() { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475' }] - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', - } - } const request = spec.buildRequests(singleSize, bidderRequest) const payload = JSON.parse(request.data) expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]) @@ -149,7 +124,7 @@ describe('sovrnBidAdapter', function() { it('sends gdpr info if exists', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let bidderRequest = { + const bidderRequest = { 'bidderCode': 'sovrn', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', @@ -162,9 +137,9 @@ describe('sovrnBidAdapter', function() { referer: 'http://example.com/page.html', } }; - bidderRequest.bids = bidRequests; + bidderRequest.bids = [adUnitBidRequest]; - const data = JSON.parse(spec.buildRequests(bidRequests, bidderRequest).data); + const data = JSON.parse(spec.buildRequests([adUnitBidRequest], bidderRequest).data); expect(data.regs.ext.gdpr).to.exist.and.to.be.a('number'); expect(data.regs.ext.gdpr).to.equal(1); @@ -173,8 +148,8 @@ describe('sovrnBidAdapter', function() { }); it('should send us_privacy if bidderRequest has a value for uspConsent', function () { - let uspString = '1NYN'; - let bidderRequest = { + const uspString = '1NYN'; + const bidderRequest = { 'bidderCode': 'sovrn', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', @@ -184,39 +159,13 @@ describe('sovrnBidAdapter', function() { referer: 'http://example.com/page.html', } }; - bidderRequest.bids = bidRequests; + bidderRequest.bids = [adUnitBidRequest]; - const data = JSON.parse(spec.buildRequests(bidRequests, bidderRequest).data); + const data = JSON.parse(spec.buildRequests([adUnitBidRequest], bidderRequest).data); expect(data.regs.ext['us_privacy']).to.equal(uspString); }); - it('converts tagid to string', function () { - const ivBidRequests = [{ - 'bidder': 'sovrn', - 'params': { - 'tagid': 403370, - 'iv': 'vet' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com/page.html', - } - }; - const request = spec.buildRequests(ivBidRequests, bidderRequest); - - expect(request.data).to.contain('"tagid":"403370"') - }); - it('should add schain if present', function() { const schainRequests = [{ 'bidder': 'sovrn', @@ -243,7 +192,7 @@ describe('sovrnBidAdapter', function() { } ] } - }].concat(bidRequests); + }].concat(adUnitBidRequest); const bidderRequest = { refererInfo: { referer: 'http://example.com/page.html', @@ -272,7 +221,7 @@ describe('sovrnBidAdapter', function() { 'criteoId': 'A_CRITEO_ID', 'tdid': 'SOMESORTOFID', } - }].concat(bidRequests); + }].concat(adUnitBidRequest); const bidderRequest = { refererInfo: { referer: 'http://example.com/page.html', @@ -293,6 +242,7 @@ describe('sovrnBidAdapter', function() { }); it('should ignore empty segments', function() { + const request = spec.buildRequests([adUnitBidRequest], bidderRequest) const payload = JSON.parse(request.data) expect(payload.imp[0].ext).to.be.undefined }) @@ -337,6 +287,70 @@ describe('sovrnBidAdapter', function() { const payload = JSON.parse(request.data) expect(payload.imp[0].bidfloor).to.equal(2.00) }) + describe('First Party Data', function () { + let sandbox + + beforeEach(function() { + sandbox = sinon.sandbox.create() + }) + afterEach(function() { + sandbox.restore() + }) + it('should provide first party data if provided', function() { + sandbox.stub(config, 'getConfig').callsFake(key => { + const cfg = { + ortb2: { + site: { + keywords: 'test keyword' + }, + user: { + data: 'some user data' + } + } + }; + return utils.deepAccess(cfg, key); + }); + const request = spec.buildRequests([adUnitBidRequest], bidderRequest) + const payload = JSON.parse(request.data) + expect(payload.user.data).to.equal('some user data') + expect(payload.site.keywords).to.equal('test keyword') + expect(payload.site.page).to.equal('http://example.com/page.html') + expect(payload.site.domain).to.equal('example.com') + }) + it('should append impression first party data', function () { + const fpdBid = {...adUnitBidRequest} + fpdBid.ortb2Imp = { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + } + const request = spec.buildRequests([fpdBid], bidderRequest) + const payload = JSON.parse(request.data) + expect(payload.imp[0].ext.data.pbadslot).to.equal('homepage-top-rect') + expect(payload.imp[0].ext.data.adUnitSpecificAttribute).to.equal('123') + }) + it('should not overwrite deals when impression fpd is present', function() { + const fpdBid = {...adUnitBidRequest} + fpdBid.params = {...adUnitBidRequest.params} + fpdBid.params.segments = 'seg1, seg2' + fpdBid.ortb2Imp = { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + } + const request = spec.buildRequests([fpdBid], bidderRequest) + const payload = JSON.parse(request.data) + expect(payload.imp[0].ext.data.pbadslot).to.equal('homepage-top-rect') + expect(payload.imp[0].ext.data.adUnitSpecificAttribute).to.equal('123') + expect(payload.imp[0].ext.deals).to.deep.equal(['seg1', 'seg2']) + }) + }) }); describe('interpretResponse', function () { From 97964ef56d39b618ab1152e2e7edfd283beb6247 Mon Sep 17 00:00:00 2001 From: Reinout Stevens Date: Wed, 28 Apr 2021 18:41:51 +0200 Subject: [PATCH 0883/1476] Consent Management Module: TCFApi in Iframe callId fix (#6634) * fix random id * lint * fix for real Co-authored-by: Reinout Stevens --- modules/consentManagement.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 67af2baf959..6a13e73b8a2 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -199,13 +199,13 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) { let apiName = (cmpVersion === 2) ? '__tcfapi' : '__cmp'; - let callId = Math.random() + ''; let callName = `${apiName}Call`; /* Setup up a __cmp function to do the postMessage and stash the callback. - This function behaves (from the caller's perspective identicially to the in-frame __cmp call */ + This function behaves (from the caller's perspective identicially to the in-frame __cmp call */ if (cmpVersion === 2) { window[apiName] = function (cmd, cmpVersion, callback, arg) { + let callId = Math.random() + ''; let msg = { [callName]: { command: cmd, @@ -226,6 +226,7 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) { window[apiName](commandName, cmpVersion, moduleCallback); } else { window[apiName] = function (cmd, arg, callback) { + let callId = Math.random() + ''; let msg = { [callName]: { command: cmd, From e5f9f57c2499ab50086a0590d3aa907f436b7980 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 28 Apr 2021 12:47:46 -0400 Subject: [PATCH 0884/1476] add support for non-purpose1 consent domains to appnexus and PBS bid adapters (#6484) * add support for non-purpose1 consent domains * fix logic and consentdata to clientSideSyncs * update openx vendor config --- .../gpt/prebidServer_example.html | 2 +- modules/appnexusBidAdapter.js | 19 ++-- modules/prebidServerBidAdapter/config.js | 30 ++++- modules/prebidServerBidAdapter/index.js | 64 +++++++++-- test/spec/modules/appnexusBidAdapter_spec.js | 4 +- .../modules/prebidServerBidAdapter_spec.js | 103 ++++++++++++------ 6 files changed, 157 insertions(+), 65 deletions(-) diff --git a/integrationExamples/gpt/prebidServer_example.html b/integrationExamples/gpt/prebidServer_example.html index 7761178efa8..37902edd979 100644 --- a/integrationExamples/gpt/prebidServer_example.html +++ b/integrationExamples/gpt/prebidServer_example.html @@ -56,10 +56,10 @@ s2sConfig : { accountId : '1', enabled : true, //default value set to false + defaultVendor: 'appnexus', bidders : ['appnexus'], timeout : 1000, //default value is 1000 adapter : 'prebidServer', //if we have any other s2s adapter, default value is s2s - endpoint : 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' } }); diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 6feec56e919..deb091fb2b7 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -11,6 +11,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; +const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', 'skippable', 'playback_method', 'frameworks', 'context', 'skipoffset']; const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; @@ -53,9 +54,9 @@ const NATIVE_MAPPING = { }; const SOURCE = 'pbjs'; const MAX_IMPS_PER_REQUEST = 15; -const mappingFileUrl = 'https://acdn.adnxs.com/prebid/appnexus-mapping/mappings.json'; +const mappingFileUrl = 'https://acdn.adnxs-simple.com/prebid/appnexus-mapping/mappings.json'; const SCRIPT_TAG_START = ' { + if (utils.isStr(option[prop])) { + let temp = option[prop]; + option[prop] = { p1Consent: temp, noP1Consent: temp }; + } + }); +} + /** * @param {(S2SConfig[]|S2SConfig)} options */ @@ -135,6 +146,7 @@ function setS2sConfig(options) { const activeBidders = []; const optionsValid = normalizedOptions.every((option, i, array) => { + formatUrlParams(options); const updateSuccess = updateConfigDefaultVendor(option); if (updateSuccess !== false) { const valid = validateConfigRequiredProps(option); @@ -205,7 +217,7 @@ function queueSync(bidderCodes, gdprConsent, uspConsent, s2sConfig) { } const jsonPayload = JSON.stringify(payload); - ajax(s2sConfig.syncEndpoint, + ajax(getMatchingConsentUrl(s2sConfig.syncEndpoint, gdprConsent), (response) => { try { response = JSON.parse(response); @@ -285,11 +297,20 @@ function doBidderSync(type, url, bidder, done) { * * @param {Array} bidders a list of bidder names */ -function doClientSideSyncs(bidders) { +function doClientSideSyncs(bidders, gdprConsent, uspConsent) { bidders.forEach(bidder => { let clientAdapter = adapterManager.getBidAdapter(bidder); if (clientAdapter && clientAdapter.registerSyncs) { - clientAdapter.registerSyncs([]); + config.runWithBidder( + bidder, + utils.bind.call( + clientAdapter.registerSyncs, + clientAdapter, + [], + gdprConsent, + uspConsent + ) + ); } }); } @@ -978,6 +999,29 @@ function bidWonHandler(bid) { } } +function hasPurpose1Consent(gdprConsent) { + let result = true; + if (gdprConsent) { + if (gdprConsent.gdprApplies && gdprConsent.apiVersion === 2) { + result = !!(utils.deepAccess(gdprConsent, 'vendorData.purpose.consents.1') === true); + } + } + return result; +} + +function getMatchingConsentUrl(urlProp, gdprConsent) { + return hasPurpose1Consent(gdprConsent) ? urlProp.p1Consent : urlProp.noP1Consent; +} + +function getConsentData(bidRequests) { + let gdprConsent, uspConsent; + if (Array.isArray(bidRequests) && bidRequests.length > 0) { + gdprConsent = bidRequests[0].gdprConsent; + uspConsent = bidRequests[0].uspConsent; + } + return { gdprConsent, uspConsent }; +} + /** * Bidder adapter for Prebid Server */ @@ -987,6 +1031,7 @@ export function PrebidServer() { /* Prebid executes this function when the page asks to send out bid requests */ baseAdapter.callBids = function(s2sBidRequest, bidRequests, addBidResponse, done, ajax) { const adUnits = utils.deepClone(s2sBidRequest.ad_units); + let { gdprConsent, uspConsent } = getConsentData(bidRequests); // at this point ad units should have a size array either directly or mapped so filter for that const validAdUnits = adUnits.filter(unit => @@ -1000,13 +1045,7 @@ export function PrebidServer() { .filter(utils.uniques); if (Array.isArray(_s2sConfigs)) { - if (s2sBidRequest.s2sConfig && s2sBidRequest.s2sConfig.syncEndpoint) { - let gdprConsent, uspConsent; - if (Array.isArray(bidRequests) && bidRequests.length > 0) { - gdprConsent = bidRequests[0].gdprConsent; - uspConsent = bidRequests[0].uspConsent; - } - + if (s2sBidRequest.s2sConfig && s2sBidRequest.s2sConfig.syncEndpoint && getMatchingConsentUrl(s2sBidRequest.s2sConfig.syncEndpoint, gdprConsent)) { let syncBidders = s2sBidRequest.s2sConfig.bidders .map(bidder => adapterManager.aliasRegistry[bidder] || bidder) .filter((bidder, index, array) => (array.indexOf(bidder) === index)); @@ -1019,7 +1058,7 @@ export function PrebidServer() { utils.logInfo('BidRequest: ' + requestJson); if (request && requestJson) { ajax( - s2sBidRequest.s2sConfig.endpoint, + getMatchingConsentUrl(s2sBidRequest.s2sConfig.endpoint, gdprConsent), { success: response => handleResponse(response, requestedBidders, bidRequests, addBidResponse, done, s2sBidRequest.s2sConfig), error: done @@ -1035,6 +1074,7 @@ export function PrebidServer() { function handleResponse(response, requestedBidders, bidderRequests, addBidResponse, done, s2sConfig) { let result; let bids = []; + let { gdprConsent, uspConsent } = getConsentData(bidderRequests); try { result = JSON.parse(response); @@ -1061,7 +1101,7 @@ export function PrebidServer() { } done(); - doClientSideSyncs(requestedBidders); + doClientSideSyncs(requestedBidders, gdprConsent, uspConsent); } // Listen for bid won to call wurl diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 3a3f4effcb8..935bc23be03 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -796,7 +796,7 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); - it('should set withCredentials to false if purpose 1 consent is not given', function () { + it('should set simple domain variant if purpose 1 consent is not given', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { 'bidderCode': 'appnexus', @@ -819,7 +819,7 @@ describe('AppNexusAdapter', function () { bidderRequest.bids = bidRequests; const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.options).to.deep.equal({withCredentials: false}); + expect(request.url).to.equal('https://ib.adnxs-simple.com/ut/v3/prebid'); }); it('should populate eids when supported userIds are available', function () { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 937d67677d9..b7be4948b0e 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -15,7 +15,10 @@ let CONFIG = { bidders: ['appnexus'], timeout: 1000, cacheMarkup: 2, - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + noP1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }; const REQUEST = { @@ -513,7 +516,7 @@ describe('S2S Adapter', function () { it('should default video placement if not defined and instream', function () { let ortb2Config = utils.deepClone(CONFIG); - ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + ortb2Config.endpoint.p1Consent = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; config.setConfig({ s2sConfig: ortb2Config }); @@ -592,7 +595,7 @@ describe('S2S Adapter', function () { it('check gdpr info gets added into cookie_sync request: have consent data', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; config.setConfig(consentConfig); @@ -618,7 +621,7 @@ describe('S2S Adapter', function () { it('check gdpr info gets added into cookie_sync request: have consent data but gdprApplies is false', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; config.setConfig(consentConfig); @@ -641,7 +644,7 @@ describe('S2S Adapter', function () { it('checks gdpr info gets added to cookie_sync request: applies is false', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; config.setConfig(consentConfig); @@ -690,7 +693,7 @@ describe('S2S Adapter', function () { it('is added to cookie_sync request when in bidRequest', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; config.setConfig({ s2sConfig: cookieSyncConfig }); let uspBidRequest = utils.deepClone(BID_REQUESTS); @@ -742,7 +745,7 @@ describe('S2S Adapter', function () { it('is added to cookie_sync request when in bidRequest', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; config.setConfig({ s2sConfig: cookieSyncConfig }); let consentBidRequest = utils.deepClone(BID_REQUESTS); @@ -789,7 +792,9 @@ describe('S2S Adapter', function () { it('adds device and app objects to request for OpenRTB', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }); const _config = { @@ -1094,7 +1099,9 @@ describe('S2S Adapter', function () { it('skips pbs alias when skipPbsAliasing is enabled in adapter', function() { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }); config.setConfig({ s2sConfig: s2sConfig }); @@ -1123,7 +1130,9 @@ describe('S2S Adapter', function () { it('skips dynamic aliases to request when skipPbsAliasing enabled', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }); config.setConfig({ s2sConfig: s2sConfig }); @@ -1155,7 +1164,9 @@ describe('S2S Adapter', function () { it('converts appnexus params to expected format for PBS', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }); config.setConfig({ s2sConfig: s2sConfig }); @@ -1184,7 +1195,7 @@ describe('S2S Adapter', function () { it('adds limit to the cookie_sync request if userSyncLimit is greater than 0', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; cookieSyncConfig.userSyncLimit = 1; const s2sBidRequest = utils.deepClone(REQUEST); @@ -1203,7 +1214,7 @@ describe('S2S Adapter', function () { it('does not add limit to cooke_sync request if userSyncLimit is missing or 0', function () { let cookieSyncConfig = utils.deepClone(CONFIG); - cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; config.setConfig({ s2sConfig: cookieSyncConfig }); const s2sBidRequest = utils.deepClone(REQUEST); @@ -2020,7 +2031,9 @@ describe('S2S Adapter', function () { it('handles OpenRTB video responses', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); @@ -2042,7 +2055,9 @@ describe('S2S Adapter', function () { it('handles response cache from ext.prebid.cache.vastXml', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2071,7 +2086,9 @@ describe('S2S Adapter', function () { it('add adserverTargeting object to bids when ext.prebid.targeting is defined', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2102,7 +2119,9 @@ describe('S2S Adapter', function () { it('handles response cache from ext.prebid.targeting', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2130,7 +2149,9 @@ describe('S2S Adapter', function () { it('handles response cache from ext.prebid.targeting with wurl', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2157,7 +2178,9 @@ describe('S2S Adapter', function () { it('handles response cache from ext.prebid.targeting with wurl and removes invalid targeting', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2192,7 +2215,9 @@ describe('S2S Adapter', function () { it('add request property pbsBidId with ext.prebid.bidid value', function () { const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); @@ -2216,7 +2241,9 @@ describe('S2S Adapter', function () { bidId: '123' }); const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + endpoint: { + p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' + } }); config.setConfig({ s2sConfig }); @@ -2260,7 +2287,9 @@ describe('S2S Adapter', function () { config.setConfig({ s2sConfig: Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }) }); }); @@ -2349,7 +2378,9 @@ describe('S2S Adapter', function () { bidders: ['appnexus'], timeout: 1000, adapter: 'prebidServer', - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }; config.setConfig({ s2sConfig: options }); @@ -2362,7 +2393,9 @@ describe('S2S Adapter', function () { enabled: true, timeout: 1000, adapter: 's2s', - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + endpoint: { + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + } }; config.setConfig({ s2sConfig: options }); @@ -2409,8 +2442,8 @@ describe('S2S Adapter', function () { expect(vendorConfig).to.have.property('adapter', 'prebidServer'); expect(vendorConfig.bidders).to.deep.equal(['appnexus']); expect(vendorConfig.enabled).to.be.true; - expect(vendorConfig).to.have.property('endpoint', 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'); - expect(vendorConfig).to.have.property('syncEndpoint', 'https://prebid.adnxs.com/pbs/v1/cookie_sync'); + expect(vendorConfig.endpoint).to.deep.equal({p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', noP1Consent: 'https://prebid.adnxs-simple.com/pbs/v1/openrtb2/auction'}); + expect(vendorConfig.syncEndpoint).to.deep.equal({p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync', noP1Consent: 'https://prebid.adnxs-simple.com/pbs/v1/cookie_sync'}); expect(vendorConfig).to.have.property('timeout', 750); }); @@ -2430,8 +2463,8 @@ describe('S2S Adapter', function () { expect(vendorConfig).to.have.property('adapter', 'prebidServer'); expect(vendorConfig.bidders).to.deep.equal(['rubicon']); expect(vendorConfig.enabled).to.be.true; - expect(vendorConfig).to.have.property('endpoint', 'https://prebid-server.rubiconproject.com/openrtb2/auction'); - expect(vendorConfig).to.have.property('syncEndpoint', 'https://prebid-server.rubiconproject.com/cookie_sync'); + expect(vendorConfig.endpoint).to.deep.equal({p1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction', noP1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction'}); + expect(vendorConfig.syncEndpoint).to.deep.equal({p1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync', noP1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync'}); expect(vendorConfig).to.have.property('timeout', 750); }); @@ -2450,8 +2483,8 @@ describe('S2S Adapter', function () { 'bidders': ['rubicon'], 'defaultVendor': 'rubicon', 'enabled': true, - 'endpoint': 'https://prebid-server.rubiconproject.com/openrtb2/auction', - 'syncEndpoint': 'https://prebid-server.rubiconproject.com/cookie_sync', + 'endpoint': {p1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction', noP1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction'}, + 'syncEndpoint': {p1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync', noP1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync'}, 'timeout': 750 }) }); @@ -2472,8 +2505,8 @@ describe('S2S Adapter', function () { accountId: 'abc', bidders: ['rubicon'], defaultVendor: 'rubicon', - endpoint: 'https://prebid-server.rubiconproject.com/openrtb2/auction', - syncEndpoint: 'https://prebid-server.rubiconproject.com/cookie_sync', + endpoint: {p1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction', noP1Consent: 'https://prebid-server.rubiconproject.com/openrtb2/auction'}, + syncEndpoint: {p1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync', noP1Consent: 'https://prebid-server.rubiconproject.com/cookie_sync'}, }) }); @@ -2521,7 +2554,7 @@ describe('S2S Adapter', function () { // Add syncEndpoint so that the request goes to the User Sync endpoint // Modify the bidders property to include an alias for Rubicon adapter - s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + s2sConfig.syncEndpoint = {p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync'}; s2sConfig.bidders = ['appnexus', 'rubicon-alias']; const s2sBidRequest = utils.deepClone(REQUEST); @@ -2592,7 +2625,7 @@ describe('S2S Adapter', function () { it('should add cooperative sync flag to cookie_sync request if property is present', function () { let s2sConfig = utils.deepClone(CONFIG); s2sConfig.coopSync = false; - s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + s2sConfig.syncEndpoint = {p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync'}; const s2sBidRequest = utils.deepClone(REQUEST); s2sBidRequest.s2sConfig = s2sConfig; @@ -2607,7 +2640,7 @@ describe('S2S Adapter', function () { it('should not add cooperative sync flag to cookie_sync request if property is not present', function () { let s2sConfig = utils.deepClone(CONFIG); - s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + s2sConfig.syncEndpoint = {p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync'}; const s2sBidRequest = utils.deepClone(REQUEST); s2sBidRequest.s2sConfig = s2sConfig; From 09dcc38c0633510e1b504c631ba8bb7e1b0010f9 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Wed, 28 Apr 2021 19:48:00 +0300 Subject: [PATCH 0885/1476] TheMediaGrid Bid Adapter: Coppa support (#6655) * 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 * TheMediaGrid Bid Adapter: support coppa --- modules/gridBidAdapter.js | 11 +++++++++-- test/spec/modules/gridBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 994244b8a50..e7b42fc48e1 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -1,8 +1,8 @@ import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.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 { config } from '../src/config.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; @@ -219,6 +219,13 @@ export const spec = { request.regs.ext.us_privacy = uspConsent; } + if (config.getConfig('coppa') === true) { + if (!request.regs) { + request.regs = {}; + } + request.regs.coppa = 1; + } + return { method: 'POST', url: ENDPOINT_URL, diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 6cdef43b150..f2e70614562 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -428,6 +428,16 @@ describe('TheMediaGrid Adapter', function () { 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); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('coppa', 1); + getConfigStub.restore(); + }); it('should contain imp[].ext.data.adserver if available', function() { const ortb2Imp = [{ ext: { From 581a699c3b479761af81917a6b29b32fb869000e Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Wed, 28 Apr 2021 09:48:48 -0700 Subject: [PATCH 0886/1476] Trigger AUCTION_DEBUG from utils.logWarn with type set to WARNING (#6645) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * emit AUCTION_DEBUG for logWarn with type WARNING * fixed the unit test case --- src/utils.js | 1 + test/spec/AnalyticsAdapter_spec.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils.js b/src/utils.js index bd3257ab7e7..7d273807ce7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -264,6 +264,7 @@ export function logWarn() { if (debugTurnedOn() && consoleWarnExists) { console.warn.apply(console, decorateLog(arguments, 'WARNING:')); } + events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'WARNING', arguments: arguments}); } export function logError() { diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 71fb9f87fa0..2b36848bd8f 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -49,8 +49,10 @@ FEATURE: Analytics Adapters API events.emit(eventType, args); adapter.enableAnalytics(); - let result = JSON.parse(server.requests[0].requestBody); - expect(result).to.deep.equal({args: {wat: 'wot'}, eventType: 'bidResponse'}); + // 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'}}); }); describe(`WHEN an event occurs after enable analytics\n`, function () { From 75bb9e316fe330d885b93209107ce01b7821b134 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 28 Apr 2021 19:27:08 +0200 Subject: [PATCH 0887/1476] Tappx Bid Adapter: optional ext on request (#6659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * UPDATE: add initial UID * UPDATE: UID change user ext * UPDATE: UID clean logs * UPDATE: add host info * UPDATE: tappx bid adapter universal id * UPDATE: fix bidder param * UPDATE: tappxBidAdapter tests * tappxBidAdapter - fix spacing * tappxBidAdapter: add test user eids array * tappxBidAdapter: update eids array * FIX: package-lock.json * Conversant adapter: add adomain, remove digitrust (#6495) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Update conversantBidAdapter.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Rads Bid Adapter: add GDPR support & user sync support (#6455) * Proxistore Bid Adapter: add cookieless url endpoint & use floor module (#6427) * use floor module * call cookieless endpoint when necessary * test endpoint url * change url endpoint * delete console log * fix tests * add language to url * use ortb interface * unit test * update test unit * create proxistore module * add unit tests and documentation * delete modules * delete module * add proxistore rtd submodule * delete proxistore module * spacing * change url * AdYoulike Bid Adapter: Add an "Insertion" tracking for Native mediatype (#6481) * add insertion event * add missing campaign ID parameter * update unit test with new tracking checked * Dspx Bid Adapter : add user sync support (#6456) * Add sync support for dspx adapter * Dspx Bid Adapter : add user sync support Co-authored-by: Alexander * Multibid Module: add new module to handle multiple bids from single bidder & update rubicon adapter (#6404) * Multibid module - create new module - Expands the number of key value pairs going to the ad server in the normal Prebid way by establishing the concept of a "dynamic alias" First commit * Continued updates from 1st commit * Adding logWarn for filtered bids * Update to include passing multibid configuration to PBS requests * Update to rubicon bid adapter to pass query param rp_maxbids value taken from bidderRequest.bidLimit * Update to config to look for camelcase property names according to spec. These convert to all lowercase when passed to PBS endpoint * Adjust RP adapter to always include maxbids value - default is 1 * Added support for bidders array in multibid config * Fixed floor comparison to be <= bid cpm as oppossed to just < bid cpm. Updated md file to fix camelCase tpyo * Update to include originalBidderRequest in video call to prebid cache * Update to ignore adpod bids from multibid and allow them to return as normal bids * Adding uid2 to submodules.json (#6508) * NextRoll ID System: add new ID module (#6396) * Add Nextroll ID Module * Add nextroll to eids * Make configuration value names consistent with Adapter Module * Use parnerId instead of sellerId * Add nextroll to userId and eids md files * Remove storage configuration * Rename nextroll -> nextrollId * Add nextrollId to common ID specs * Qwarry Bid Adapter: add GDPR and consent string handling (#6489) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: pro-nsk <32703851+pro-nsk@users.noreply.github.com> * Zemanta Bid Adapter: add support for new params & consent strings to usersync URL (#6468) * add gvl id to spec * add support for bcat and badv params * add consent strings to usersync url * add bcat and badv params to doc * Automatad Bid Adapter: Add meta.advertiserDomains to bid response (#6509) * added bid meta with advertiserDomains * Adhese Bid Adapter: add support for caching video content (#6501) * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * Adhese Bid Adapter: cache video content Co-authored-by: Tim Sturtewagen Co-authored-by: Mateusz * update apacdex unit test to disable debug mode (#6511) * Telaria: not setting adid (#6507) * Prebid 4.33.0 Release * increment pre version * rubicon: removing maxduration as a required bidder parameter (#6513) * Zemanta adapter: add advertiserDomains (#6517) * Lemma Bid Adapter: accepting the floor to use the getFloor function (#6497) * 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. * Update lemmaBidAdapter.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter_spec.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.md Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.js Added user sync support into bid adapter. * updated include modules file extension. updated include modules js file extension. * Update lemmaBidAdapter_spec.js Added unit test for user sync feature. * Update lemmaBidAdapter.js Fixed format error. * Update lemmaBidAdapter_spec.js Fixed format error and typo error. * Set mediaType key value into bid object Set mediaType key value into the bid object. * Update lemmaBidAdapter.js remove duplicate function * Update lemmaBidAdapter.js Remove non supported code. * Update lemmaBidAdapter_spec.js Remove GDPR test cases. * Update lemmaBidAdapter.js Made changes for accepting the floor to use the getFloor function * Update lemmaBidAdapter.js correct undefined keyword name. * Update lemmaBidAdapter_spec.js Added test coverage floor value * Update lemmaBidAdapter.js Remove trailing spaces on lines 379 and 381. * Update lemmaBidAdapter_spec.js Added getFloor function test case changes, Please review it. * Update lemmaBidAdapter_spec.js * Update lemmaBidAdapter.js * Update lemmaBidAdapter.js Fixed lint issue. * Update lemmaBidAdapter_spec.js Fixed test cases. * Update lemmaBidAdapter_spec.js Made suggested changes. Please review it. Co-authored-by: Abhijit Mane * Mediasquare Bid Adapter: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event (#6480) * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * removing status as it does not seem populated when called * add tests * Update nextroll ID variable name to match published ID module (#6519) * Merkle User ID Module: updates to user id submodule (#6503) * AdKernel Bid/Analytics Adapters: user privacy related changes (#6488) * SynacorMedia: remove adId from the bid response (#6520) * Rubicon: making doc data types consistent (#6526) * Synacormedia Bid Adapter: add meta.advertiserDomains (#6527) * Adloox Analytics Adapter: add new analytics adapter (#6308) * gulp: fix supplying list of browsers to test against The following now works: gulp test --browserstack --nolint --nolintfix --browsers=bs_ie_11_windows_10 --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo,RtdProvider}_spec.js' * instreamTracking: unit test tidy From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * adloaderStub: expose stub for other unit tests to use From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * Adloox analytic module * Seedtag adapter: Fixing bug preventing to receive the right params onTimeout. (#6525) * adot bid adapter: add publisher path from bidder config to endpoint url (#6476) * Admixer ID System: add userId submodule (#6238) * Migrating to Prebid 1.0 * Migrating to Prebid 1.0 * Fix spec * add gdpr and usp * remove changes in gdpr_hello_world.html * Update gdpr_hello_world.html add spaces * add user syncs * remove comments * tests * admixer id system * admixer id system * admixer id system eids.md userId.md * admixer id system .submodules.json * admixer id system Co-authored-by: atkachov * PBJS Core: call custom render func after loadscript if provided (#6422) * Pubxai Analytics Adapter: bug fixes and code revamp (#6474) * Updated PubxAiAnalytics adapter - Bug fixes and Code restructuring * Updated endpoint URLs * Changed array.includes to array.indexOf to fix IE issue * Code cleanup and changes as suggested. * Updated browser testing order and edge browser token * PBJS Core: canBidderRegisterSync ignoring iframe sync disabled by default (#6535) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Update userSync.js * Update userSync_spec.js * Added automatic tzo and targetId to adserver request. (#6534) * Impactify Bid Adapter: add new bid adapter (#6518) * Add impactify adapter with MD file * Add impactify adapter * Prebid 4.34.0 Release * Increment pre version * Prebid server adapter: add config for openx hosting (#6530) * Yieldmo adapter: add meta data to bids (#6550) * Smartx Bid Adapter: Add meta.advertiserDomains support (#6547) * Onevideo / Adap.tv Adapter: updated example configuration (#6546) * Mass Deal Rendering Module: support multiple custom configs for dealId and rendering (#6500) * ZetaSsp Bid Adapter: add new bid adapter (#6432) * Adnuntius Bid Adapter: Fix for bid too low. (#6557) * Added automatic tzo and targetId to adserver request. * Fixing issues with bid price being too low. * Fixing issues with bid price being too low. * ReadPeak Bid Adapter: fix api issues, add gdpr consent, & getfloor module support (#6548) * BetweenBidAdatper: added sharedid support (#6531) * adWMG Bid Adapter: update endpoints for cookie sync (#6544) * Support floorCPM parameter, fix some minor bugs * fix space-in-parens circleci error * example fix * clean usersync URL * spaces * spaces * add new unit tests, compatibility with IE11 * remove logInfo * Check for floorCPM value * Check params before sending * New endpoints * code format * new endpoint for cookie sync * update tests Co-authored-by: Mikhail Dykun * Yieldmo Bid Adapter: add support for the floors module (#6541) * Sortable Bid Adapter: add eids support (#6565) * Add Sortable adapter for Prebid 3.x Update tests to reflect changes. * Add .js in imports * hostname not host: don't include port * Trivial change to trigger build: failure wasn't our adapter * More failures in other adapters * PR Feedback - use https for URL - fix examples in markdown - request to endpoint should work now * Feedback: add native and video examples * Update unit tests Co-authored-by: Shannon Broekhoven * Outbrain Bid Adapter: replacing Zemanta (#6558) * Sirdata Real-time Data Module: add new RTD module (#6515) * Logicad Bid Adapter: add support for userid modules (#6529) * ATS-identityLinkIdSystem - add use3P config property to control firing of 3P envelope endpoint (#6568) * Proxistore Bid Adapter: add support for tcf v2 consent (#6543) * use tcf v2 consent * set cosentGiven to false and test Gdpr api v2 * BlueBillyWig Bid Adapter: add renderer customization options (#6540) * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Update bluebillywigBidAdapter test parameters to match renderer to rendererCode rename * Blue Billywig - Also pass through site config with OpenRTB request * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Code quality update, always hit user syncs, improved video params * Remove unnecessary export * Add rendererSettings param to bluebillywig adapter * Kick off CircleCi tests manually Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie * OpenX Bid Adapter: Set Deal ID for video requests (#6573) * 33Across Bid Adapter: add support for User ID modules (#6554) * pubGENIUS bid adapter: support floor module (#6555) * Welect Bid Adapter: update url of API (#6570) * update api url * update api url in tests * Bright Mountain Media Bid Adapter: change bidder code to bmtm; alias old name (#6574) * Adtelligent Bid Adapter: add adUrl support & new alias (#6559) * add adUrl support * add adUrl test * Bright Mountain Media Bid Adapter: Change Endpoint URL (#6576) * tappxBidAdapter: update * tasppxBidAdapter: add video * tappxBidAdapter: update video * tappxBidAdapter: update name interpret banner * tappxBidAdapter: add tests for video * tappxBidAdapter: add adomain * tappxBidAdapter: update adapter version * tappxBidAdapter: update interpretBid adomain and dealid * tappxBidAdapter: update isBidRequestValid * tappxBidAdapter: update tests. Adding video to isBidRequestValid * tappxBidAdapter: update doc .md file * Tappx - Allow optional ext object * Tappx - CircleCI hotfixes * Tappx - CircleCI hotfixes2 * Tappx - Avoid outBrain Co-authored-by: marc_tappx Co-authored-by: Patrick McCann Co-authored-by: onlsol <48312668+onlsol@users.noreply.github.com> Co-authored-by: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Co-authored-by: guiann Co-authored-by: Alexander Co-authored-by: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Co-authored-by: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Co-authored-by: Abimael Martinez Co-authored-by: artemiokost Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Co-authored-by: Rok Sušnik Co-authored-by: Kanchika - Automatad Co-authored-by: Paweł L Co-authored-by: Tim Sturtewagen Co-authored-by: Mateusz Co-authored-by: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Co-authored-by: bretg Co-authored-by: Jason Snellbaker Co-authored-by: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Co-authored-by: Abhijit Mane Co-authored-by: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Co-authored-by: Denis Logachov Co-authored-by: RAJKUMAR NATARAJAN Co-authored-by: Alexander Clouter Co-authored-by: Laura Morillo-Velarde Co-authored-by: Giudici-a <34242194+Giudici-a@users.noreply.github.com> Co-authored-by: Galphimbl Co-authored-by: atkachov Co-authored-by: Jérémie Girault Co-authored-by: Phaneendra Hegde Co-authored-by: Mikael Lundin Co-authored-by: Thomas Co-authored-by: Mike Chowla Co-authored-by: Deivydas Šabaras Co-authored-by: ym-atsymuk <81176595+ym-atsymuk@users.noreply.github.com> Co-authored-by: Skylinar <53079123+Skylinar@users.noreply.github.com> Co-authored-by: Adam Browning <19834421+adam-browning@users.noreply.github.com> Co-authored-by: Catalin Ciocov Co-authored-by: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Co-authored-by: readpeaktuomo <66239046+readpeaktuomo@users.noreply.github.com> Co-authored-by: Ignat Khaylov Co-authored-by: nyakove <43004249+nyakove@users.noreply.github.com> Co-authored-by: Mikhail Dykun Co-authored-by: ym-dlabuzov <81709888+ym-dlabuzov@users.noreply.github.com> Co-authored-by: karentnarvaez <61426156+karentnarvaez@users.noreply.github.com> Co-authored-by: Shannon Broekhoven Co-authored-by: nouchy <33549554+nouchy@users.noreply.github.com> Co-authored-by: logicad Co-authored-by: mamatic <52153441+mamatic@users.noreply.github.com> Co-authored-by: Klaas-Jan Boon Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie Co-authored-by: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Co-authored-by: Aparna Rao Co-authored-by: Meng <5110935+edmonl@users.noreply.github.com> Co-authored-by: Nick Duitz <42961155+nduitz@users.noreply.github.com> Co-authored-by: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Co-authored-by: Gena Co-authored-by: Albert Grandes --- modules/tappxBidAdapter.js | 6 +++++- modules/tappxBidAdapter.md | 2 +- test/spec/modules/tappxBidAdapter_spec.js | 25 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 1228fbafaad..1cf5a0f8be7 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -181,6 +181,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); + const BIDEXTRA = utils.deepAccess(validBidRequests, 'params.ext'); const bannerMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.banner'); const videoMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.video'); const { refererInfo } = bidderRequest; @@ -266,6 +267,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { bidder.tappxkey = TAPPXKEY; bidder.endpoint = ENDPOINT; bidder.host = hostInfo.url; + bidder.ext = (typeof BIDEXTRA == 'object') ? BIDEXTRA : undefined; imp.ext = {}; imp.ext.bidder = bidder; @@ -336,7 +338,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.params = params; payload.regs = regs; // < Payload - + utils.logMessage('---------------------'); + utils.logMessage(JSON.stringify(payload)); + utils.logMessage('---------------------'); return { method: 'POST', url: `https://${HOST}/${ENDPOINT}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index e6581a67d06..a07bb2d88d1 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -67,4 +67,4 @@ Ads sizes available: [320,50], [300,250], [320,480], [1024,768], [728,90] }] } ]; -``` \ No newline at end of file +``` diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 378b391a4eb..8197c4f44dd 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -215,6 +215,31 @@ describe('Tappx bid adapter', function () { expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); }); + + it('should properly build a ext optional object', function() { + let extBidRequest = c_VALIDBIDREQUESTS; + extBidRequest[0].params.ext = {'optionalData': '1234'}; + let extBidderRequest = c_BIDDERREQUEST_B; + extBidderRequest.bids[0].ext = {'optionalData': '1234'}; + + const request = spec.buildRequests(extBidRequest, extBidderRequest); + const data = JSON.parse(request[0].data); + expect(data.imp[0].ext.bidder.ext).to.be.an('object'); + expect(data.imp[0].ext.bidder.ext.optionalData).to.be.equal('1234'); + }); + + it('should ignore ext optional if is not a object', function() { + let badExtBidRequest = c_VALIDBIDREQUESTS; + badExtBidRequest[0].params.ext = 'stringValue'; + let badExtBidderRequest = c_BIDDERREQUEST_B; + badExtBidderRequest.bids[0].ext = 'stringValue'; + + const request = spec.buildRequests(badExtBidRequest, badExtBidderRequest); + const data = JSON.parse(request[0].data); + expect(data.imp[0].ext.bidder.ext).not.to.be.an('string'); + expect(data.imp[0].ext.bidder.ext).to.be.an('undefined'); + expect(data.imp[0].ext.bidder).to.not.have.property('ext') + }); }); /** From 75d8c527a2e70efb68ba464aaffb5661c311ece0 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 28 Apr 2021 13:46:52 -0400 Subject: [PATCH 0888/1476] Prebid 4.37.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de3ad74eb51..5a16fe6798b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.37.0-pre", + "version": "4.37.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From c21ad7e264140c42e8e492e2315b2ad225e8c9a7 Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Thu, 29 Apr 2021 01:09:32 +0300 Subject: [PATCH 0889/1476] oneVideo Bid Adapter: Price Floors Module Support (SAPR-16735) (#6672) --- modules/oneVideoBidAdapter.js | 20 +++- test/spec/modules/oneVideoBidAdapter_spec.js | 103 +++++++++++++++++-- 2 files changed, 112 insertions(+), 11 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 60d56cfc8ad..e57c9d12ff7 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.0.7', + VERSION: '3.1.0', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -165,14 +165,17 @@ function getRequestData(bid, consentData, bidRequest) { let loc = bidRequest.refererInfo.referer; let page = (bid.params.site && bid.params.site.page) ? (bid.params.site.page) : (loc.href); let ref = (bid.params.site && bid.params.site.referrer) ? bid.params.site.referrer : bidRequest.refererInfo.referer; + let getFloorRequestObject = { + currency: bid.params.cur || 'USD', + mediaType: 'video', + size: '*' + }; let bidData = { id: utils.generateUUID(), at: 2, - cur: bid.cur || 'USD', imp: [{ id: '1', secure: isSecure(), - bidfloor: bid.params.bidfloor, ext: { hb: 1, prebidver: '$prebid.version$', @@ -226,6 +229,7 @@ function getRequestData(bid, consentData, bidRequest) { bidData.imp[0].video.linearity = 1; bidData.imp[0].video.protocols = bid.params.video.protocols || [2, 5]; } else if (bid.params.video.display == 1) { + getFloorRequestObject.mediaType = 'banner'; bidData.imp[0].banner = { mimes: bid.params.video.mimes, w: bid.params.video.playerWidth, @@ -244,6 +248,15 @@ function getRequestData(bid, consentData, bidRequest) { bidData.imp[0].banner.ext.minduration = bid.params.video.minduration } } + + if (utils.isFn(bid.getFloor)) { + let floorData = bid.getFloor(getFloorRequestObject); + bidData.imp[0].bidfloor = floorData.floor; + bidData.cur = floorData.currency; + } else { + bidData.imp[0].bidfloor = bid.params.bidfloor; + }; + if (bid.params.video.inventoryid) { bidData.imp[0].ext.inventoryid = bid.params.video.inventoryid } @@ -294,6 +307,7 @@ function getRequestData(bid, consentData, bidRequest) { } } if (bid.params.video.e2etest) { + utils.logMessage('+++ oneVideoBidAdapter: E2E test mode enabled. \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); bidData.imp[0].bidfloor = null; bidData.imp[0].video.w = 300; bidData.imp[0].video.h = 250; diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 903bc191b47..3f5304dce0a 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -1,10 +1,5 @@ -import { - expect -} from 'chai'; -import { - spec -} from 'modules/oneVideoBidAdapter.js'; -import * as utils from 'src/utils.js'; +import { expect } from 'chai'; +import { spec } from 'modules/oneVideoBidAdapter.js'; describe('OneVideoBidAdapter', function () { let bidRequest; @@ -221,7 +216,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.0.7'; + const VERSION = '3.1.0'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -503,6 +498,98 @@ describe('OneVideoBidAdapter', function () { }); }); + describe('price floor module validations', function () { + beforeEach(function () { + bidRequest.getFloor = (floorObj) => { + return { + floor: bidRequest.floors.values[floorObj.mediaType + '|640x480'], + currency: floorObj.currency, + mediaType: floorObj.mediaType + } + } + }); + + it('should get bidfloor from getFloor method', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.floors = { + currency: 'EUR', + values: { + 'video|640x480': 5.55 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = requests[0].data; + expect(data.cur).is.a('string'); + expect(data.cur).to.equal('EUR'); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + + it('should use adUnit/module currency & floor instead of bid.params.bidfloor', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.params.bidfloor = 3.33; + bidRequest.floors = { + currency: 'EUR', + values: { + 'video|640x480': 5.55 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = requests[0].data; + expect(data.cur).is.a('string'); + expect(data.cur).to.equal('EUR'); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + + it('should load banner instead of video floor when DAP is active bid.params.video.display = 1', function () { + bidRequest.params.video.display = 1; + bidRequest.params.cur = 'EUR'; + bidRequest.mediaTypes = { + banner: { + sizes: [ + [640, 480] + ] + } + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|640x480': 2.22, + 'video|640x480': 9.99 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = requests[0].data; + expect(data.cur).is.a('string'); + expect(data.cur).to.equal('EUR'); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(2.22); + }) + + it('should load video floor when multi-format adUnit is present', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.mediaTypes.banner = { + sizes: [ + [640, 480] + ] + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|640x480': 2.22, + 'video|640x480': 9.99 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = requests[0].data; + expect(data.cur).is.a('string'); + expect(data.cur).to.equal('EUR'); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(9.99); + }) + }) + describe('spec.interpretResponse', function () { it('should return no bids if the response is not valid', function () { const bidResponse = spec.interpretResponse({ From d9a78013ee361d9e2a7ad6e85f8a4e6efc297ee0 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Thu, 29 Apr 2021 11:37:23 +0200 Subject: [PATCH 0890/1476] Spread adUnit.ortb2Imp.ext into imp object (#6494) --- modules/prebidServerBidAdapter/index.js | 10 +++++-- .../modules/prebidServerBidAdapter_spec.js | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 4e396717cb2..38a499c794e 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -625,6 +625,7 @@ const OPEN_RTB_PROTOCOL = { } // get bidder params in form { : {...params} } + // initialize reduce function with the user defined `ext` properties on the ad unit const ext = adUnit.bids.reduce((acc, bid) => { const adapter = adapterManager.bidderRegistry[bid.bidder]; if (adapter && adapter.getSpec().transformBidParams) { @@ -632,7 +633,7 @@ const OPEN_RTB_PROTOCOL = { } acc[bid.bidder] = (s2sConfig.adapterOptions && s2sConfig.adapterOptions[bid.bidder]) ? Object.assign({}, bid.params, s2sConfig.adapterOptions[bid.bidder]) : bid.params; return acc; - }, {}); + }, {...utils.deepAccess(adUnit, 'ortb2Imp.ext')}); const imp = { id: adUnit.code, ext, secure: s2sConfig.secure }; @@ -643,7 +644,12 @@ const OPEN_RTB_PROTOCOL = { * @type {(string|undefined)} */ if (prop === 'pbadslot') { - if (typeof ortb2[prop] === 'string' && ortb2[prop]) utils.deepSetValue(imp, 'ext.data.pbadslot', ortb2[prop]); + if (typeof ortb2[prop] === 'string' && ortb2[prop]) { + utils.deepSetValue(imp, 'ext.data.pbadslot', ortb2[prop]); + } else { + // remove pbadslot property if it doesn't meet the spec + delete imp.ext.data.pbadslot; + } } else if (prop === 'adserver') { /** * Copy GAM AdUnit and Name to imp diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index b7be4948b0e..a6afed8ba3e 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1825,6 +1825,32 @@ describe('S2S Adapter', function () { }); }); + describe('ext.prebid config', function () { + it('should send \"imp.ext.prebid.storedrequest.id\" if \"ortb2Imp.ext.prebid.storedrequest.id\" is set', function () { + const consentConfig = { s2sConfig: CONFIG }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + const storedRequestId = 'my-id'; + bidRequest.ad_units[0].ortb2Imp = { + ext: { + prebid: { + storedrequest: { + id: storedRequestId + } + } + } + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.prebid.storedrequest.id'); + expect(parsedRequestBody.imp[0].ext.prebid.storedrequest.id).to.equal(storedRequestId); + }); + }); + describe('response handler', function () { beforeEach(function () { sinon.stub(utils, 'triggerPixel'); From 98964095eba4141ceef8a7ba7347e2125528b33c Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 29 Apr 2021 12:07:53 -0400 Subject: [PATCH 0891/1476] appnexus bid adapter - ensure withCredentials is always passed (#6675) --- modules/appnexusBidAdapter.js | 5 ++++- test/spec/modules/appnexusBidAdapter_spec.js | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index deb091fb2b7..a48e3c0fde3 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -483,7 +483,10 @@ function hasPurpose1Consent(bidderRequest) { function formatRequest(payload, bidderRequest) { let request = []; - let options = {}; + let options = { + withCredentials: true + }; + let endpointUrl = URL; if (!hasPurpose1Consent(bidderRequest)) { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 935bc23be03..2b7c5835604 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -644,7 +644,7 @@ describe('AppNexusAdapter', function () { bidderRequest.bids = bidRequests; const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.options).to.be.empty; + expect(request.options).to.deep.equal({withCredentials: true}); const payload = JSON.parse(request.data); expect(payload.gdpr_consent).to.exist; @@ -796,6 +796,12 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); + it('should always set withCredentials: true on the request.options', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + const request = spec.buildRequests([bidRequest]); + expect(request.options.withCredentials).to.equal(true); + }); + it('should set simple domain variant if purpose 1 consent is not given', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { From e2062447abd900e1b7ad028eaa3bae6913c57ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Fern=C3=A1ndez?= Date: Thu, 29 Apr 2021 19:24:46 +0000 Subject: [PATCH 0892/1476] Axonix Bid Adapter: Fixed interpretResponse, support email (#6667) * Fixed interpretResponse, support email * onBidWon gets single bids * replaceAuctionPrice call fix * Version bump --- modules/axonixBidAdapter.js | 25 +++--- modules/axonixBidAdapter.md | 2 +- test/spec/modules/axonixBidAdapter_spec.js | 100 +++++++++++---------- 3 files changed, 65 insertions(+), 62 deletions(-) diff --git a/modules/axonixBidAdapter.js b/modules/axonixBidAdapter.js index 224486467f3..daaac27e6a4 100644 --- a/modules/axonixBidAdapter.js +++ b/modules/axonixBidAdapter.js @@ -5,7 +5,7 @@ import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'axonix'; -const BIDDER_VERSION = '1.0.1'; +const BIDDER_VERSION = '1.0.2'; const CURRENCY = 'USD'; const DEFAULT_REGION = 'us-east-1'; @@ -140,15 +140,17 @@ export const spec = { }, interpretResponse: function(serverResponse) { - if (!utils.isArray(serverResponse)) { + const response = serverResponse ? serverResponse.body : []; + + if (!utils.isArray(response)) { return []; } const responses = []; - for (const response of serverResponse) { - if (response.requestId) { - responses.push(Object.assign(response, { + for (const resp of response) { + if (resp.requestId) { + responses.push(Object.assign(resp, { ttl: config.getConfig('_bidderTimeout') })); } @@ -171,15 +173,12 @@ export const spec = { } }, - onBidWon: function(bids) { - for (const bid of bids) { - const { nurl } = bid || {}; + onBidWon: function(bid) { + const { nurl } = bid || {}; - if (bid.nurl) { - utils.replaceAuctionPrice(nurl, bid.cpm) - utils.triggerPixel(nurl); - }; - } + if (bid.nurl) { + utils.triggerPixel(utils.replaceAuctionPrice(nurl, bid.cpm)); + }; } } diff --git a/modules/axonixBidAdapter.md b/modules/axonixBidAdapter.md index 1ff59f17828..7a4606d5502 100644 --- a/modules/axonixBidAdapter.md +++ b/modules/axonixBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name : Axonix Bidder Adapter Module Type : Bidder Adapter -Maintainer : support-prebid@axonix.com +Maintainer : support+prebid@axonix.com ``` # Description diff --git a/test/spec/modules/axonixBidAdapter_spec.js b/test/spec/modules/axonixBidAdapter_spec.js index aac1cbe08ff..a952d527600 100644 --- a/test/spec/modules/axonixBidAdapter_spec.js +++ b/test/spec/modules/axonixBidAdapter_spec.js @@ -71,47 +71,51 @@ describe('AxonixBidAdapter', function () { }; const BANNER_RESPONSE = { - requestId: 'f08b3a8dcff747eabada295dcf94eee0', - cpm: 6, - currency: 'USD', - width: 300, - height: 250, - ad: '', - creativeId: 'abc', - netRevenue: false, - meta: { - networkId: 'nid', - advertiserDomains: [ - 'https://the.url' - ], - secondaryCatIds: [ - 'IAB1' - ], - mediaType: 'banner' - }, - nurl: 'https://win.url' + body: [{ + requestId: 'f08b3a8dcff747eabada295dcf94eee0', + cpm: 6, + currency: 'USD', + width: 300, + height: 250, + ad: '', + creativeId: 'abc', + netRevenue: false, + meta: { + networkId: 'nid', + advertiserDomains: [ + 'https://the.url' + ], + secondaryCatIds: [ + 'IAB1' + ], + mediaType: 'banner' + }, + nurl: 'https://win.url' + }] }; const VIDEO_RESPONSE = { - requestId: 'f08b3a8dcff747eabada295dcf94eee0', - cpm: 6, - currency: 'USD', - width: 300, - height: 250, - ad: '', - creativeId: 'abc', - netRevenue: false, - meta: { - networkId: 'nid', - advertiserDomains: [ - 'https://the.url' - ], - secondaryCatIds: [ - 'IAB1' - ], - mediaType: 'video' - }, - nurl: 'https://win.url' + body: [{ + requestId: 'f08b3a8dcff747eabada295dcf94eee0', + cpm: 6, + currency: 'USD', + width: 300, + height: 250, + ad: '', + creativeId: 'abc', + netRevenue: false, + meta: { + networkId: 'nid', + advertiserDomains: [ + 'https://the.url' + ], + secondaryCatIds: [ + 'IAB1' + ], + mediaType: 'video' + }, + nurl: 'https://win.url' + }] }; describe('inherited functions', function () { @@ -311,21 +315,21 @@ describe('AxonixBidAdapter', function () { it('ignores unparseable responses', function() { expect(spec.interpretResponse('invalid')).to.be.an('array').that.is.empty; expect(spec.interpretResponse(['invalid'])).to.be.an('array').that.is.empty; - expect(spec.interpretResponse([{ invalid: 'object' }])).to.be.an('array').that.is.empty; + expect(spec.interpretResponse({ body: [{ invalid: 'object' }] })).to.be.an('array').that.is.empty; }); it('parses banner responses', function () { - const response = spec.interpretResponse([BANNER_RESPONSE]); + const response = spec.interpretResponse(BANNER_RESPONSE); expect(response).to.be.an('array').that.is.not.empty; - expect(response[0]).to.equal(BANNER_RESPONSE); + expect(response[0]).to.equal(BANNER_RESPONSE.body[0]); }); it('parses 1 video responses', function () { - const response = spec.interpretResponse([VIDEO_RESPONSE]); + const response = spec.interpretResponse(VIDEO_RESPONSE); expect(response).to.be.an('array').that.is.not.empty; - expect(response[0]).to.equal(VIDEO_RESPONSE); + expect(response[0]).to.equal(VIDEO_RESPONSE.body[0]); }); it.skip('parses 1 native responses', function () { @@ -346,17 +350,17 @@ describe('AxonixBidAdapter', function () { }); it('called once', function () { - spec.onBidWon(spec.interpretResponse([BANNER_RESPONSE])); + spec.onBidWon(BANNER_RESPONSE.body[0]); expect(utils.triggerPixel.calledOnce).to.equal(true); }); it('called false', function () { - spec.onBidWon([{ cpm: '2.21' }]); + spec.onBidWon({ cpm: '2.21' }); expect(utils.triggerPixel.called).to.equal(false); }); it('when there is no notification expected server side, none is called', function () { - var response = spec.onBidWon([]); + var response = spec.onBidWon({}); expect(utils.triggerPixel.called).to.equal(false); expect(response).to.be.an('undefined') }); @@ -364,11 +368,11 @@ describe('AxonixBidAdapter', function () { describe('onTimeout', function () { it('banner response', () => { - spec.onTimeout(spec.interpretResponse([BANNER_RESPONSE])); + spec.onTimeout(spec.interpretResponse(BANNER_RESPONSE)); }); it('video response', () => { - spec.onTimeout(spec.interpretResponse([VIDEO_RESPONSE])); + spec.onTimeout(spec.interpretResponse(VIDEO_RESPONSE)); }); }); }); From e93f4555c3faf2ad2ade6626a12dbb66d59bde45 Mon Sep 17 00:00:00 2001 From: cs83 Date: Fri, 30 Apr 2021 13:06:27 +0300 Subject: [PATCH 0893/1476] Smartico Bid Adapter: add new bid adapter (#6486) * Adding smartico adapter * bug #6486 fix, added maintainer email * bug #6486 fix, modified test parameters * bug #6486 fix, modified test parameters #2 * #6486 applied review related updates & fixes * #6486 applied review related updates & fixes #2 * #6486 applied review related updates & fixes #3 Co-authored-by: Dmitri --- modules/smarticoBidAdapter.js | 116 ++++++++++++++++++ modules/smarticoBidAdapter.md | 32 +++++ test/spec/modules/smarticoBidAdapter_spec.js | 118 +++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 modules/smarticoBidAdapter.js create mode 100644 modules/smarticoBidAdapter.md create mode 100644 test/spec/modules/smarticoBidAdapter_spec.js diff --git a/modules/smarticoBidAdapter.js b/modules/smarticoBidAdapter.js new file mode 100644 index 00000000000..9107ce5f908 --- /dev/null +++ b/modules/smarticoBidAdapter.js @@ -0,0 +1,116 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; + +const SMARTICO_CONFIG = { + bidRequestUrl: 'https://trmads.eu/preBidRequest', + widgetUrl: 'https://trmads.eu/get', + method: 'POST' +} + +const BIDDER_CODE = 'smartico'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid && bid.params && bid.params.token && bid.params.placementId); + }, + buildRequests: function (validBidRequests, bidderRequest) { + var i + var j + var bid + var bidParam + var bidParams = [] + var sizes + var frameWidth = Math.round(window.screen.width) + var frameHeight = Math.round(window.screen.height) + for (i = 0; i < validBidRequests.length; i++) { + bid = validBidRequests[i] + if (bid.sizes) { + sizes = bid.sizes + } else if (typeof (BANNER) != 'undefined' && bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + sizes = bid.mediaTypes[BANNER].sizes + } else if (frameWidth && frameHeight) { + sizes = [[frameWidth, frameHeight]] + } else { + sizes = [] + } + for (j = 0; j < sizes.length; j++) { + bidParam = { + token: bid.params.token || '', + bidId: bid.bidId, + 'banner-format-width': sizes[j][0], + 'banner-format-height': sizes[j][1] + } + if (bid.params.bannerFormat) { + bidParam['banner-format'] = bid.params.bannerFormat + } + if (bid.params.language) { + bidParam.language = bid.params.language + } + if (bid.params.region) { + bidParam.region = bid.params.region + } + if (bid.params.regions && (bid.params.regions instanceof String || (bid.params.regions instanceof Array && bid.params.regions.length))) { + bidParam.regions = bid.params.regions + if (bidParam.regions instanceof Array) { + bidParam.regions = bidParam.regions.join(',') + } + } + bidParams.push(bidParam) + } + } + + var ServerRequestObjects = { + method: SMARTICO_CONFIG.method, + url: SMARTICO_CONFIG.bidRequestUrl, + bids: validBidRequests, + data: {bidParams: bidParams, auctionId: bidderRequest.auctionId, origin: window.location.origin} + } + + return ServerRequestObjects; + }, + interpretResponse: function (serverResponse, bidRequest) { + var i + var bid + var bidObject + var url + var html + var ad + var token + var language + var scriptId + var bidResponses = [] + + for (i = 0; i < serverResponse.length; i++) { + ad = serverResponse[i]; + bid = find(bidRequest.bids, bid => bid.bidId === ad.bidId) + if (bid) { + token = bid.params.token || '' + + language = bid.params.language || SMARTICO_CONFIG.language || '' + + scriptId = encodeURIComponent('smartico-widget-' + bid.params.placementId + '-' + i) + + url = SMARTICO_CONFIG.widgetUrl + '?token=' + encodeURIComponent(token) + '&auction-id=' + encodeURIComponent(bid.auctionId) + '&from-auction-buffer=1&own_session=1&ad=' + encodeURIComponent(ad.id) + '&scriptid=' + scriptId + (ad.bannerFormatAlias ? '&banner-format=' + encodeURIComponent(ad.bannerFormatAlias) : '') + (language ? '&language=' + encodeURIComponent(language) : '') + + html = '', @@ -479,12 +471,12 @@ describe('validate native', function () { afterEach(function () {}); - it('should reject bid if no image sizes are defined', function () { + it('should accept bid if no image sizes are defined', function () { let result = nativeBidIsValid(validBid, bidReq); expect(result).to.be.true; result = nativeBidIsValid(noIconDimBid, bidReq); - expect(result).to.be.false; + expect(result).to.be.true; result = nativeBidIsValid(noImgDimBid, bidReq); - expect(result).to.be.false; + expect(result).to.be.true; }); }); From f9272fc06c8f64bb542951be99499098a6b02e5a Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 12 May 2021 10:55:27 -0700 Subject: [PATCH 0940/1476] Prebid Server Bid Adapter: Bugfix for not taking defaultVendor enabled (#6740) * quick fix for enabled s2s bug * move enabled check outside of defaultVendor if --- modules/prebidServerBidAdapter/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 38a499c794e..4ca61cae452 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -72,7 +72,6 @@ let eidPermissions; * @type {S2SDefaultConfig} */ const s2sDefaultConfig = { - enabled: false, timeout: 1000, maxBids: 1, adapter: 'prebidServer', @@ -89,7 +88,7 @@ config.setDefaults({ * @return {boolean} */ function updateConfigDefaultVendor(option) { - if (option.defaultVendor && option.enabled !== false) { + if (option.defaultVendor) { let vendor = option.defaultVendor; let optionKeys = Object.keys(option); if (S2S_VENDORS[vendor]) { @@ -105,6 +104,8 @@ function updateConfigDefaultVendor(option) { return false; } } + // this is how we can know if user / defaultVendor has set it, or if we should default to false + return option.enabled = typeof option.enabled === 'boolean' ? option.enabled : false; } /** @@ -163,6 +164,7 @@ function setS2sConfig(options) { return true; } } + utils.logWarn('prebidServer: s2s config is disabled'); return false; }); From f999c0da11e286d9361bb09b8faedf8db30ce70a Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 12 May 2021 15:35:56 -0400 Subject: [PATCH 0941/1476] Prebid 4.39.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a04743eab39..4085ca3df08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.39.0-pre", + "version": "4.39.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5215ade32747facd4fb4c4f932d44b9b49c9daa7 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 12 May 2021 15:52:46 -0400 Subject: [PATCH 0942/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4085ca3df08..05f68f4a009 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.39.0", + "version": "4.40.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b132a4ba9d10a78bdea1a04fc2088d0957126385 Mon Sep 17 00:00:00 2001 From: John Salis Date: Thu, 13 May 2021 08:46:35 -0400 Subject: [PATCH 0943/1476] Beachfront Bid Adapter: add schain support (#6751) * add schain support to beachfront adapter * remove unnecessary mock tests for outstream player Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 11 ++ modules/beachfrontBidAdapter.md | 2 +- .../spec/modules/beachfrontBidAdapter_spec.js | 101 ++++++++---------- 3 files changed, 55 insertions(+), 59 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 44755a78864..8ddc0ca5ba9 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -351,6 +351,9 @@ function createVideoRequestData(bid, bidderRequest) { regs: { ext: {} }, + source: { + ext: {} + }, user: { ext: {} }, @@ -367,6 +370,10 @@ function createVideoRequestData(bid, bidderRequest) { payload.user.ext.consent = consentString; } + if (bid.schain) { + payload.source.ext.schain = bid.schain; + } + if (eids.length > 0) { payload.user.ext.eids = eids; } @@ -416,6 +423,10 @@ function createBannerRequestData(bids, bidderRequest) { payload.gdprConsent = consentString; } + if (bids[0] && bids[0].schain) { + payload.schain = bids[0].schain; + } + SUPPORTED_USER_IDS.forEach(({ key, queryParam }) => { let id = bids[0] && bids[0].userId && bids[0].userId[key]; if (id) { diff --git a/modules/beachfrontBidAdapter.md b/modules/beachfrontBidAdapter.md index 0a6b8b73da4..a2eb79ee331 100644 --- a/modules/beachfrontBidAdapter.md +++ b/modules/beachfrontBidAdapter.md @@ -4,7 +4,7 @@ Module Name: Beachfront Bid Adapter Module Type: Bidder Adapter -Maintainer: john@beachfront.com +Maintainer: prebid@beachfront.com # Description diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 43c71dd6349..c7ae5c799ac 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,5 +1,4 @@ import { expect } from 'chai'; -import sinon from 'sinon'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter.js'; import { parseUrl } from 'src/utils.js'; @@ -278,6 +277,27 @@ describe('BeachfrontAdapter', function () { expect(data.user.ext.consent).to.equal(consentString); }); + it('must add schain data to the request', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.schain = schain; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.source.ext.schain).to.deep.equal(schain); + }); + it('must add the Trade Desk User ID to the request', () => { const tdid = '4321'; const bidRequest = bidRequests[0]; @@ -446,6 +466,27 @@ describe('BeachfrontAdapter', function () { expect(data.gdprConsent).to.equal(consentString); }); + it('must add schain data to the request', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.schain = schain; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.schain).to.deep.equal(schain); + }); + it('must add the Trade Desk User ID to the request', () => { const tdid = '4321'; const bidRequest = bidRequests[0]; @@ -654,63 +695,7 @@ describe('BeachfrontAdapter', function () { id: bidRequest.bidId, url: OUTSTREAM_SRC }); - }); - - it('should initialize a player for outstream bids', () => { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { - video: { - context: 'outstream', - playerSize: [ width, height ] - } - }; - const serverResponse = { - bidPrice: 5.00, - url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - crid: '123abc' - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - window.Beachfront = { Player: sinon.spy() }; - bidResponse.adUnitCode = bidRequest.adUnitCode; - bidResponse.renderer.render(bidResponse); - sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match({ - adTagUrl: bidResponse.vastUrl, - width: bidResponse.width, - height: bidResponse.height, - expandInView: false, - collapseOnComplete: true - })); - delete window.Beachfront; - }); - - it('should configure outstream player settings from the bidder params', () => { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { - video: { - context: 'outstream', - playerSize: [ width, height ] - } - }; - bidRequest.params.player = { - expandInView: true, - collapseOnComplete: false, - progressColor: 'green' - }; - const serverResponse = { - bidPrice: 5.00, - url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - crid: '123abc' - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - window.Beachfront = { Player: sinon.spy() }; - bidResponse.adUnitCode = bidRequest.adUnitCode; - bidResponse.renderer.render(bidResponse); - sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match(bidRequest.params.player)); - delete window.Beachfront; + expect(bidResponse.renderer.render).to.be.a('function'); }); }); From 73690781de088db65e8871a9a0437fbb95a2986a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Mar=C3=ADn?= Date: Thu, 13 May 2021 15:03:40 +0200 Subject: [PATCH 0944/1476] fix: Webpack v5 complain about named export from JSON modules (#6755) --- modules/gdprEnforcement.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index adbccd8666d..02a2da3a7a4 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -12,7 +12,7 @@ import { registerSyncInner } from '../src/adapters/bidderFactory.js'; import { getHook } from '../src/hook.js'; import { validateStorageEnforcement } from '../src/storageManager.js'; import events from '../src/events.js'; -import { EVENTS } from '../src/constants.json'; +import CONSTANTS from '../src/constants.json'; const TCF2 = { 'purpose1': { id: 1, name: 'storage' }, @@ -345,10 +345,10 @@ function emitTCF2FinalResults() { analyticsBlocked: formatArray(analyticsBlocked) }; - events.emit(EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); + events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); } -events.on(EVENTS.AUCTION_END, emitTCF2FinalResults); +events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults); /* Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find(). From 752e0c2757227f50313554c4af467eacb44a5c76 Mon Sep 17 00:00:00 2001 From: Danny Khatib Date: Thu, 13 May 2021 07:58:50 -0700 Subject: [PATCH 0945/1476] Pbs bid adapter: constants import styling for webpack v5 upgrade (#6723) * mapping spotx dealid to bid object * using proper syntax to import default json module * kick off tests * es lint fix Co-authored-by: Danny Khatib Co-authored-by: Chris Huie <3444727+ChrisHuie@users.noreply.github.com> --- modules/prebidServerBidAdapter/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 4ca61cae452..d91858ed9b2 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -1,7 +1,7 @@ import Adapter from '../../src/adapter.js'; import { createBid } from '../../src/bidfactory.js'; import * as utils from '../../src/utils.js'; -import { STATUS, S2S, EVENTS } from '../../src/constants.json'; +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'; @@ -16,7 +16,7 @@ import { getPrebidInternal } from '../../src/utils.js'; const getConfig = config.getConfig; -const TYPE = S2S.SRC; +const TYPE = CONSTANTS.S2S.SRC; let _syncCount = 0; const DEFAULT_S2S_TTL = 60; const DEFAULT_S2S_CURRENCY = 'USD'; @@ -840,7 +840,7 @@ const OPEN_RTB_PROTOCOL = { } const cpm = bid.price; - const status = cpm !== 0 ? STATUS.GOOD : STATUS.NO_BID; + const status = cpm !== 0 ? CONSTANTS.STATUS.GOOD : CONSTANTS.STATUS.NO_BID; let bidObject = createBid(status, bidRequest || { bidder: seatbid.seat, src: TYPE @@ -1099,7 +1099,7 @@ export function PrebidServer() { } }); - bidderRequests.forEach(bidderRequest => events.emit(EVENTS.BIDDER_DONE, bidderRequest)); + bidderRequests.forEach(bidderRequest => events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest)); } catch (error) { utils.logError(error); } @@ -1113,7 +1113,7 @@ export function PrebidServer() { } // Listen for bid won to call wurl - events.on(EVENTS.BID_WON, bidWonHandler); + events.on(CONSTANTS.EVENTS.BID_WON, bidWonHandler); return Object.assign(this, { callBids: baseAdapter.callBids, From 3f02a15968d16d1aa9cddc48ed73098cc5677006 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Thu, 13 May 2021 14:31:40 -0400 Subject: [PATCH 0946/1476] First Party Data module: Add new module and two submodules to populate defaults and validate ortb2 (#6452) * Creating fpd module * Continued work on FPD module. - Data validation - Pubcid optout check - Misc Fixes * Revert userId update. Committed in error * Added first party data unit tests and fixed bug * Added an unsubscribe for tests to run properly * Reworked logic to use bidderRequests hook to update global/bidder configs instead of subscribing - former method was preventing tests from completing properly * Merge master * Removing unused references. Fixing device data to point to device.h/device.w * Update to include opt out configuration for enrichments/validations * Modified logic to use ortb2 configuration mapping. This will allow for entries to be added/removed/modified from configuration as opposed to be specifically called out in the validation functions * Removed LGTM unneeded defensive code for check on object 'cur' * Remove unused conditional * Fix lint error * Updates to remove currency enrichment as well as optout for user object * Added optout flag to user.yob and user.gender fields * Added test for arbitrary values Added more comments * Broke module out into module and two submodules * Updated cur to validate as an array of strings not just a string Updated comments --- modules/.submodules.json | 4 + modules/enrichmentFpdModule.js | 107 ++++ modules/fpdModule/index.js | 58 +++ modules/fpdModule/index.md | 46 ++ modules/validationFpdModule/config.js | 125 +++++ modules/validationFpdModule/index.js | 232 +++++++++ test/spec/modules/enrichmentFpdModule_spec.js | 97 ++++ test/spec/modules/fpdModule_spec.js | 464 ++++++++++++++++++ test/spec/modules/validationFpdModule_spec.js | 313 ++++++++++++ 9 files changed, 1446 insertions(+) create mode 100644 modules/enrichmentFpdModule.js create mode 100644 modules/fpdModule/index.js create mode 100644 modules/fpdModule/index.md create mode 100644 modules/validationFpdModule/config.js create mode 100644 modules/validationFpdModule/index.js create mode 100644 test/spec/modules/enrichmentFpdModule_spec.js create mode 100644 test/spec/modules/fpdModule_spec.js create mode 100644 test/spec/modules/validationFpdModule_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 7760d31cfff..a8804321278 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -41,5 +41,9 @@ "reconciliationRtdProvider", "geoedgeRtdProvider", "sirdataRtdProvider" + ], + "fpdModule": [ + "enrichmentFpdModule", + "validationFpdModule" ] } diff --git a/modules/enrichmentFpdModule.js b/modules/enrichmentFpdModule.js new file mode 100644 index 00000000000..a1d815917e0 --- /dev/null +++ b/modules/enrichmentFpdModule.js @@ -0,0 +1,107 @@ +/** + * This module sets default values and validates ortb2 first part data + * @module modules/firstPartyData + */ +import * as utils from '../src/utils.js'; +import { submodule } from '../src/hook.js' +import { getRefererInfo } from '../src/refererDetection.js' + +let ortb2 = {}; +let win = (window === window.top) ? window : window.top; + +/** + * Checks for referer and if exists merges into ortb2 global data + */ +function setReferer() { + if (getRefererInfo().referer) utils.mergeDeep(ortb2, { site: { ref: getRefererInfo().referer } }); +} + +/** + * Checks for canonical url and if exists merges into ortb2 global data + */ +function setPage() { + if (getRefererInfo().canonicalUrl) utils.mergeDeep(ortb2, { site: { page: getRefererInfo().canonicalUrl } }); +} + +/** + * Checks for canonical url and if exists retrieves domain and merges into ortb2 global data + */ +function setDomain() { + let parseDomain = function(url) { + if (!url || typeof url !== 'string' || url.length === 0) return; + + var match = url.match(/^(?:https?:\/\/)?(?:www\.)?(.*?(?=(\?|\#|\/|$)))/i); + + return match && match[1]; + }; + + let domain = parseDomain(getRefererInfo().canonicalUrl) + + if (domain) utils.mergeDeep(ortb2, { site: { domain: 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; + } + + utils.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) utils.mergeDeep(ortb2, { site: { keywords: keywords.content.replace(/\s/g, '') } }); +} + +/** + * Resets modules global ortb2 data + */ +const resetOrtb2 = () => { ortb2 = {} }; + +function runEnrichments() { + setReferer(); + setPage(); + setDomain(); + setDimensions(); + setKeywords(); + + return ortb2; +} + +/** + * Sets default values to ortb2 if exists and adds currency and ortb2 setConfig callbacks on init + */ +export function initSubmodule(fpdConf, data) { + resetOrtb2(); + + return (!fpdConf.skipEnrichments) ? utils.mergeDeep(runEnrichments(), data) : data; +} + +/** @type {firstPartyDataSubmodule} */ +export const enrichmentsSubmodule = { + name: 'enrichments', + queue: 2, + init: initSubmodule +} + +submodule('firstPartyData', enrichmentsSubmodule) diff --git a/modules/fpdModule/index.js b/modules/fpdModule/index.js new file mode 100644 index 00000000000..427547a4e4d --- /dev/null +++ b/modules/fpdModule/index.js @@ -0,0 +1,58 @@ +/** + * This module sets default values and validates ortb2 first part data + * @module modules/firstPartyData + */ +import { config } from '../../src/config.js'; +import { module, getHook } from '../../src/hook.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; +import { addBidderRequests } from '../../src/auction.js'; + +let submodules = []; + +/** + * enable submodule in User ID + * @param {RtdSubmodule} submodule + */ +export function registerSubmodules(submodule) { + submodules.push(submodule); +} + +export function init() { + let modConf = config.getConfig('firstPartyData') || {}; + let ortb2 = config.getConfig('ortb2') || {}; + + submodules.sort((a, b) => { + return ((a.queue || 1) - (b.queue || 1)); + }).forEach(submodule => { + ortb2 = submodule.init(modConf, ortb2); + }); + + config.setConfig({ortb2}); +} + +/** + * BidderRequests hook to intiate module and reset modules ortb2 data object + */ +function addBidderRequestHook(fn, bidderRequests) { + init(); + fn.call(this, bidderRequests); + // Removes hook after run + addBidderRequests.getHooks({ hook: addBidderRequestHook }).remove(); +} + +/** + * Sets bidderRequests hook + */ +function setupHook() { + getHook('addBidderRequests').before(addBidderRequestHook); +} + +module('firstPartyData', registerSubmodules); + +// Runs setupHook on initial load +setupHook(); + +/** + * Global function to reinitiate module + */ +(getGlobal()).refreshFpd = setupHook; diff --git a/modules/fpdModule/index.md b/modules/fpdModule/index.md new file mode 100644 index 00000000000..638c966883a --- /dev/null +++ b/modules/fpdModule/index.md @@ -0,0 +1,46 @@ +# Overview + +``` +Module Name: First Party Data Module +``` + +# Description + +Module to perform the following functions to allow for consistent set of first party data using the following submodules. + +Enrichment Submodule: +- populate available data into object: referer, meta-keywords, cur + +Validation Submodule: +- verify OpenRTB datatypes, remove/warn any that are likely to choke downstream readers +- verify that certain OpenRTB attributes are not specified +- optionally suppress user FPD based on the existence of _pubcid_optout + + +1. Module initializes on first load and set bidRequestHook +2. When hook runs, corresponding submodule init functions are run to perform enrichments/validations dependant on submodule +3. After hook complete, it is disabled - meaning module only runs on first auction +4. To reinitiate the module, run pbjs.refreshFPD(), which allows module to rerun as if initial load + + +This module will automatically run first party data enrichments and validations dependant on which submodules are included. There is no configuration required. In order to load the module and submodule(s) and opt out of either enrichements or validations, use the below opt out configuration + +# Module Control Configuration + +``` + +pbjs.setConfig({ + firstPartyData: { + skipValidations: true, // default to false + skipEnrichments: true // default to false + } +}); + +``` + +# Requirements + +At least one of the submodules must be included in order to successfully run the corresponding above operations. + +enrichmentFpdModule +validationFpdModule \ No newline at end of file diff --git a/modules/validationFpdModule/config.js b/modules/validationFpdModule/config.js new file mode 100644 index 00000000000..f6adfea70eb --- /dev/null +++ b/modules/validationFpdModule/config.js @@ -0,0 +1,125 @@ +/** + * Data type map + */ +const TYPES = { + string: 'string', + object: 'object', + number: 'number', +}; + +/** + * Template to define what ortb2 attributes should be validated + * Accepted fields: + * -- invalid - {Boolean} if true, field is not valid + * -- type - {String} valid data type of field + * -- isArray - {Boolean} if true, field must be an array + * -- childType - {String} used in conjuction with isArray: true, defines valid type of array indices + * -- children - {Object} defines child properties needed to be validated (used only if type: object) + * -- required - {Array} array of strings defining any required properties for object (used only if type: object) + * -- optoutApplies - {Boolean} if true, optout logic will filter if applicable (currently only applies to user object) + */ +export const ORTB_MAP = { + imp: { + invalid: true + }, + cur: { + type: TYPES.object, + isArray: true, + childType: TYPES.string + }, + device: { + type: TYPES.object, + children: { + w: { type: TYPES.number }, + h: { type: TYPES.number } + } + }, + site: { + type: TYPES.object, + children: { + name: { type: TYPES.string }, + domain: { type: TYPES.string }, + page: { type: TYPES.string }, + ref: { type: TYPES.string }, + keywords: { type: TYPES.string }, + search: { type: TYPES.string }, + cat: { + type: TYPES.object, + isArray: true, + childType: TYPES.string + }, + sectioncat: { + type: TYPES.object, + isArray: true, + childType: TYPES.string + }, + pagecat: { + type: TYPES.object, + isArray: true, + childType: TYPES.string + }, + content: { + type: TYPES.object, + isArray: false, + children: { + data: { + type: TYPES.object, + isArray: true, + childType: TYPES.object, + required: ['name', 'segment'], + children: { + segment: { + type: TYPES.object, + isArray: true, + childType: TYPES.object, + required: ['id'], + children: { + id: { type: TYPES.string } + } + }, + name: { type: TYPES.string }, + ext: { type: TYPES.object }, + } + } + } + }, + publisher: { + type: TYPES.object, + isArray: false + }, + } + }, + user: { + type: TYPES.object, + children: { + yob: { + type: TYPES.number, + optoutApplies: true + }, + gender: { + type: TYPES.string, + optoutApplies: true + }, + keywords: { type: TYPES.string }, + data: { + type: TYPES.object, + isArray: true, + childType: TYPES.object, + required: ['name', 'segment'], + children: { + segment: { + type: TYPES.object, + isArray: true, + childType: TYPES.object, + required: ['id'], + children: { + id: { type: TYPES.string } + } + }, + name: { type: TYPES.string }, + ext: { type: TYPES.object }, + } + } + } + } +} diff --git a/modules/validationFpdModule/index.js b/modules/validationFpdModule/index.js new file mode 100644 index 00000000000..c23f7e09316 --- /dev/null +++ b/modules/validationFpdModule/index.js @@ -0,0 +1,232 @@ +/** + * This module sets default values and validates ortb2 first part data + * @module modules/firstPartyData + */ +import { config } from '../../src/config.js'; +import * as utils from '../../src/utils.js'; +import { ORTB_MAP } from './config.js'; +import { submodule } from '../../src/hook.js'; +import { getStorageManager } from '../../src/storageManager.js'; + +const STORAGE = getStorageManager(); +let optout; + +/** + * Check if data passed is empty + * @param {*} value to test against + * @returns {Boolean} is value empty + */ +function isEmptyData(data) { + let check = true; + + if (typeof data === 'object' && !utils.isEmpty(data)) { + check = false; + } else if (typeof data !== 'object' && (utils.isNumber(data) || data)) { + check = false; + } + + return check; +} + +/** + * Check if required keys exist in data object + * @param {Object} data object + * @param {Array} array of required keys + * @param {String} object path (for printing warning) + * @param {Number} index of object value in the data array (for printing warning) + * @returns {Boolean} is requirements fulfilled + */ +function getRequiredData(obj, required, parent, i) { + let check = true; + + required.forEach(key => { + if (!obj[key] || isEmptyData(obj[key])) { + check = false; + utils.logWarn(`Filtered ${parent}[] value at index ${i} in ortb2 data: missing required property ${key}`); + } + }); + + return check; +} + +/** + * Check if data type is valid + * @param {*} value to test against + * @param {Object} object containing type definition and if should be array bool + * @returns {Boolean} is type fulfilled + */ +function typeValidation(data, mapping) { + let check = false; + + switch (mapping.type) { + case 'string': + if (typeof data === 'string') check = true; + break; + case 'number': + if (typeof data === 'number' && isFinite(data)) check = true; + break; + case 'object': + if (typeof data === 'object') { + if ((Array.isArray(data) && mapping.isArray) || (!Array.isArray(data) && !mapping.isArray)) check = true; + } + break; + } + + return check; +} + +/** + * Validates ortb2 data arrays and filters out invalid data + * @param {Array} ortb2 data array + * @param {Object} object defining child type and if array + * @param {String} config path of data array + * @param {String} parent path for logging warnings + * @returns {Array} validated/filtered data + */ +export function filterArrayData(arr, child, path, parent) { + arr = arr.filter((index, i) => { + let check = typeValidation(index, {type: child.type, isArray: child.isArray}); + + if (check && Array.isArray(index) === Boolean(child.isArray)) { + return true; + } + + utils.logWarn(`Filtered ${parent}[] value at index ${i} in ortb2 data: expected type ${child.type}`); + }).filter((index, i) => { + let requiredCheck = true; + let mapping = utils.deepAccess(ORTB_MAP, path); + + if (mapping && mapping.required) requiredCheck = getRequiredData(index, mapping.required, parent, i); + + if (requiredCheck) return true; + }).reduce((result, value, i) => { + let typeBool = false; + let mapping = utils.deepAccess(ORTB_MAP, path); + + switch (child.type) { + case 'string': + result.push(value); + break; + case 'object': + if (mapping && mapping.children) { + let validObject = validateFpd(value, path + '.children.', parent + '.'); + if (Object.keys(validObject).length) { + let requiredCheck = getRequiredData(validObject, mapping.required, parent, i); + + if (requiredCheck) { + result.push(validObject); + typeBool = true; + } + } + } else { + result.push(value); + typeBool = true; + } + break; + } + + if (!typeBool) utils.logWarn(`Filtered ${parent}[] value at index ${i} in ortb2 data: expected type ${child.type}`); + + return result; + }, []); + + return arr; +} + +/** + * Validates ortb2 object and filters out invalid data + * @param {Object} ortb2 object + * @param {String} config path of data array + * @param {String} parent path for logging warnings + * @returns {Object} validated/filtered data + */ +export function validateFpd(fpd, path = '', parent = '') { + if (!fpd) return {}; + + // Filter out imp property if exists + let validObject = Object.assign({}, Object.keys(fpd).filter(key => { + let mapping = utils.deepAccess(ORTB_MAP, path + key); + + if (!mapping || !mapping.invalid) return key; + + utils.logWarn(`Filtered ${parent}${key} property in ortb2 data: invalid property`); + }).filter(key => { + let mapping = utils.deepAccess(ORTB_MAP, path + key); + // let typeBool = false; + let typeBool = (mapping) ? typeValidation(fpd[key], {type: mapping.type, isArray: mapping.isArray}) : true; + + if (typeBool || !mapping) return key; + + utils.logWarn(`Filtered ${parent}${key} property in ortb2 data: expected type ${(mapping.isArray) ? 'array' : mapping.type}`); + }).reduce((result, key) => { + let mapping = utils.deepAccess(ORTB_MAP, path + key); + let modified = {}; + + if (mapping) { + if (mapping.optoutApplies && optout) { + utils.logWarn(`Filtered ${parent}${key} data: pubcid optout found`); + return result; + } + + modified = (mapping.type === 'object' && !mapping.isArray) + ? validateFpd(fpd[key], path + key + '.children.', parent + key + '.') + : (mapping.isArray && mapping.childType) + ? filterArrayData(fpd[key], { type: mapping.childType, isArray: mapping.childisArray }, path + key, parent + key) : fpd[key]; + + // Check if modified data has data and return + (!isEmptyData(modified)) ? result[key] = modified + : utils.logWarn(`Filtered ${parent}${key} property in ortb2 data: empty data found`); + } else { + result[key] = fpd[key]; + } + + return result; + }, {})); + + // Return validated data + return validObject; +} + +/** + * Run validation on global and bidder config data for ortb2 + */ +function runValidations(data) { + let conf = validateFpd(data); + + let bidderDuplicate = { ...config.getBidderConfig() }; + + Object.keys(bidderDuplicate).forEach(bidder => { + let modConf = Object.keys(bidderDuplicate[bidder]).reduce((res, key) => { + let valid = (key !== 'ortb2') ? bidderDuplicate[bidder][key] : validateFpd(bidderDuplicate[bidder][key]); + + if (valid) res[key] = valid; + + return res; + }, {}); + + if (Object.keys(modConf).length) config.setBidderConfig({ bidders: [bidder], config: modConf }); + }); + + return conf; +} + +/** + * Sets default values to ortb2 if exists and adds currency and ortb2 setConfig callbacks on init + */ +export function initSubmodule(fpdConf, data) { + // Checks for existsnece of pubcid optout cookie/storage + // if exists, filters user data out + optout = (STORAGE.cookiesAreEnabled() && STORAGE.getCookie('_pubcid_optout')) || + (STORAGE.hasLocalStorage() && STORAGE.getDataFromLocalStorage('_pubcid_optout')); + + return (!fpdConf.skipValidations) ? runValidations(data) : data; +} + +/** @type {firstPartyDataSubmodule} */ +export const validationSubmodule = { + name: 'validation', + queue: 1, + init: initSubmodule +} + +submodule('firstPartyData', validationSubmodule) diff --git a/test/spec/modules/enrichmentFpdModule_spec.js b/test/spec/modules/enrichmentFpdModule_spec.js new file mode 100644 index 00000000000..e5271143f2c --- /dev/null +++ b/test/spec/modules/enrichmentFpdModule_spec.js @@ -0,0 +1,97 @@ +import { expect } from 'chai'; +import * as utils from 'src/utils.js'; +import { getRefererInfo } from 'src/refererDetection.js'; +import { initSubmodule } from 'modules/enrichmentFpdModule.js'; + +describe('the first party data enrichment module', function() { + let width; + let widthStub; + let height; + let heightStub; + let querySelectorStub; + let canonical; + let keywords; + + before(function() { + canonical = document.createElement('link'); + canonical.rel = 'canonical'; + keywords = document.createElement('meta'); + keywords.name = 'keywords'; + }); + + beforeEach(function() { + 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; + }); + }); + + afterEach(function() { + widthStub.restore(); + heightStub.restore(); + querySelectorStub.restore(); + canonical = document.createElement('link'); + canonical.rel = 'canonical'; + keywords = document.createElement('meta'); + keywords.name = 'keywords'; + }); + + it('adds ref and device values', function() { + width = 800; + height = 500; + + let validated = initSubmodule({}, {}); + + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + expect(validated.device).to.deep.equal({ w: 800, h: 500 }); + expect(validated.site.keywords).to.be.undefined; + }); + + it('adds page and domain values if canonical url exists', function() { + width = 800; + height = 500; + canonical.href = 'https://www.domain.com/path?query=12345'; + + let validated = initSubmodule({}, {}); + + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); + expect(validated.site.domain).to.equal('domain.com'); + 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 = initSubmodule({}, {}); + + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + expect(validated.device).to.deep.equal({ w: 800, h: 500 }); + 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 = initSubmodule({}, {device: {w: 1200, h: 700}, site: {ref: 'https://someUrl.com', page: 'test.com'}}); + + expect(validated.site.ref).to.equal('https://someUrl.com'); + expect(validated.site.page).to.equal('test.com'); + expect(validated.site.domain).to.be.undefined; + expect(validated.device).to.deep.equal({ w: 1200, h: 700 }); + expect(validated.site.keywords).to.be.undefined; + }); +}); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js new file mode 100644 index 00000000000..c2a6c41835e --- /dev/null +++ b/test/spec/modules/fpdModule_spec.js @@ -0,0 +1,464 @@ +import {expect} from 'chai'; +import * as utils from 'src/utils.js'; +import {config} from 'src/config.js'; +import {getRefererInfo} from 'src/refererDetection.js'; +import {init, registerSubmodules} from 'modules/fpdModule/index.js'; +import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; +import * as validationModule from 'modules/validationFpdModule/index.js'; + +let enrichments = { + name: 'enrichments', + queue: 2, + init: enrichmentModule.initSubmodule +}; +let validations = { + name: 'validations', + queue: 1, + init: validationModule.initSubmodule +}; + +describe('the first party data module', function () { + let ortb2 = { + device: { + h: 911, + w: 1733 + }, + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar', + ext: 'string' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + let conf = { + device: { + h: 500, + w: 750 + }, + user: { + keywords: 'test1, test2', + gender: 'f', + data: [{ + segment: [{ + id: 'test' + }], + name: 'alt' + }] + }, + site: { + ref: 'domain.com', + page: 'www.domain.com/test', + ext: { + data: { + inventory: ['first'] + } + } + } + }; + + afterEach(function () { + config.resetConfig(); + }); + + describe('first party data intitializing', function () { + let width; + let widthStub; + let height; + let heightStub; + let querySelectorStub; + let canonical; + let keywords; + + before(function() { + canonical = document.createElement('link'); + canonical.rel = 'canonical'; + keywords = document.createElement('meta'); + keywords.name = 'keywords'; + }); + + beforeEach(function() { + 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; + }); + }); + + afterEach(function() { + widthStub.restore(); + heightStub.restore(); + querySelectorStub.restore(); + canonical = document.createElement('link'); + canonical.rel = 'canonical'; + keywords = document.createElement('meta'); + keywords.name = 'keywords'; + }); + + it('sets default referer and dimension values to ortb2 data', function () { + registerSubmodules(enrichments); + registerSubmodules(validations); + + let validated; + + width = 1120; + height = 750; + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + expect(validated.device).to.deep.equal({w: 1120, h: 750}); + expect(validated.site.keywords).to.be.undefined; + }); + + it('sets page and domain values to ortb2 data if canonical link exists', function () { + let validated; + + canonical.href = 'https://www.domain.com/path?query=12345'; + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); + expect(validated.site.domain).to.equal('domain.com'); + expect(validated.device).to.deep.to.equal({w: 1120, h: 750}); + expect(validated.site.keywords).to.be.undefined; + }); + + it('sets keyword values to ortb2 data if keywords meta exists', function () { + let validated; + + keywords.content = 'value1,value2,value3'; + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + expect(validated.device).to.deep.to.equal({w: 1120, h: 750}); + expect(validated.site.keywords).to.equal('value1,value2,value3'); + }); + + it('only sets values that do not exist in ortb2 config', function () { + let validated; + + config.setConfig({ortb2: {site: {ref: 'https://testpage.com', domain: 'newDomain.com'}}}); + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal('https://testpage.com'); + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.equal('newDomain.com'); + expect(validated.device).to.deep.to.equal({w: 1120, h: 750}); + expect(validated.site.keywords).to.be.undefined; + }); + + it('filters ortb2 data that is set', function () { + let validated; + let conf = { + ortb2: { + 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(conf); + canonical.href = 'https://www.domain.com/path?query=12345'; + width = 1120; + height = 750; + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal(getRefererInfo().referer); + 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; + let conf = { + ortb2: { + site: { + ref: 'https://referer.com' + } + } + }; + + config.setConfig(conf); + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal('https://referer.com'); + }); + + it('should allow overwrite default data with setConfig', function () { + let validated; + let conf = { + ortb2: { + site: { + ref: 'https://referer.com' + } + } + }; + + config.setConfig(conf); + + init(); + + validated = config.getConfig('ortb2'); + expect(validated.site.ref).to.equal('https://referer.com'); + }); + + it('should filter all data', function () { + let validated; + let conf = { + 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}}); + + config.setConfig({ortb2: conf}); + + init(); + + validated = config.getConfig('ortb2'); + expect(validated).to.deep.equal({}); + }); + + it('should add enrichments but not alter any arbitrary ortb2 data', function () { + let validated; + let conf = { + site: { + ext: { + data: { + inventory: ['value1'] + } + } + }, + user: { + ext: { + data: { + visitor: ['value2'] + } + } + }, + cur: ['USD'] + }; + + config.setConfig({ortb2: conf}); + + init(); + + validated = config.getConfig('ortb2'); + 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 validated; + let conf = { + bidders: ['bidderA', 'bidderB'], + config: { + ortb2: { + site: { + keywords: 'other', + ref: 'https://domain.com' + }, + user: { + keywords: 'test', + data: [{ + segment: [{id: 4}], + name: 't' + }] + } + } + } + }; + + config.setBidderConfig(conf); + + init(); + + validated = config.getBidderConfig(); + expect(validated.bidderA.ortb2).to.not.be.undefined; + expect(validated.bidderA.ortb2.user.data).to.be.undefined; + expect(validated.bidderA.ortb2.user.keywords).to.equal('test'); + expect(validated.bidderA.ortb2.site.keywords).to.equal('other'); + expect(validated.bidderA.ortb2.site.ref).to.equal('https://domain.com'); + }); + + it('should not filter bidderConfig data as it is valid', function () { + let validated; + let conf = { + bidders: ['bidderA', 'bidderB'], + config: { + ortb2: { + site: { + keywords: 'other', + ref: 'https://domain.com' + }, + user: { + keywords: 'test', + data: [{ + segment: [{id: 'data1_id'}], + name: 'data1' + }] + } + } + } + }; + + config.setBidderConfig(conf); + + init(); + + validated = config.getBidderConfig(); + expect(validated.bidderA.ortb2).to.not.be.undefined; + expect(validated.bidderA.ortb2.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); + expect(validated.bidderA.ortb2.user.keywords).to.equal('test'); + expect(validated.bidderA.ortb2.site.keywords).to.equal('other'); + expect(validated.bidderA.ortb2.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 conf = { + site: { + keywords: 'other' + }, + user: { + keywords: 'test', + data: [{ + segment: [{id: 'data1_id'}], + name: 'data1' + }] + } + } + ; + + config.setConfig({ortb2: conf}); + + init(); + + validated = config.getConfig(); + expect(validated.ortb2).to.not.be.undefined; + expect(validated.ortb2.device).to.be.undefined; + expect(validated.ortb2.site.ref).to.be.undefined; + expect(validated.ortb2.site.page).to.be.undefined; + expect(validated.ortb2.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 conf = { + site: { + keywords: 'other' + }, + user: { + keywords: 'test', + data: [{ + segment: [{id: 'nonfiltered'}] + }] + } + } + ; + + config.setConfig({ortb2: conf}); + + init(); + + validated = config.getConfig(); + expect(validated.ortb2).to.not.be.undefined; + expect(validated.ortb2.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); + }); + }); +}); diff --git a/test/spec/modules/validationFpdModule_spec.js b/test/spec/modules/validationFpdModule_spec.js new file mode 100644 index 00000000000..9e8072cb9ed --- /dev/null +++ b/test/spec/modules/validationFpdModule_spec.js @@ -0,0 +1,313 @@ +import {expect} from 'chai'; +import * as utils from 'src/utils.js'; +import { + filterArrayData, + validateFpd +} from 'modules/validationFpdModule/index.js'; + +describe('the first party data validation module', function () { + let ortb2 = { + device: { + h: 911, + w: 1733 + }, + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar', + ext: 'string' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + let conf = { + device: { + h: 500, + w: 750 + }, + user: { + keywords: 'test1, test2', + gender: 'f', + data: [{ + segment: [{ + id: 'test' + }], + name: 'alt' + }] + }, + site: { + ref: 'domain.com', + page: 'www.domain.com/test', + ext: { + data: { + inventory: ['first'] + } + } + } + }; + + describe('filtering first party array data', function () { + it('returns empty array if no valid data', function () { + let arr = [{}]; + let path = 'site.children.cat'; + let child = {type: 'string'}; + let parent = 'site'; + let key = 'cat'; + let validated = filterArrayData(arr, child, path, parent, key); + expect(validated).to.deep.equal([]); + }); + + it('filters invalid type of array data', function () { + let arr = ['foo', {test: 1}]; + let path = 'site.children.cat'; + let child = {type: 'string'}; + let parent = 'site'; + let key = 'cat'; + let validated = filterArrayData(arr, child, path, parent, key); + expect(validated).to.deep.equal(['foo']); + }); + + it('filters all data for missing required children', function () { + let arr = [{test: 1}]; + let path = 'site.children.content.children.data'; + let child = {type: 'object'}; + let parent = 'site'; + let key = 'data'; + let validated = filterArrayData(arr, child, path, parent, key); + expect(validated).to.deep.equal([]); + }); + + it('filters all data for invalid required children types', function () { + let arr = [{name: 'foo', segment: 1}]; + let path = 'site.children.content.children.data'; + let child = {type: 'object'}; + let parent = 'site'; + let key = 'data'; + let validated = filterArrayData(arr, child, path, parent, key); + expect(validated).to.deep.equal([]); + }); + + it('returns only data with valid required nested children types', function () { + let arr = [{name: 'foo', segment: [{id: '1'}, {id: 2}, 'foobar']}]; + let path = 'site.children.content.children.data'; + let child = {type: 'object'}; + let parent = 'site'; + let key = 'data'; + let validated = filterArrayData(arr, child, path, parent, key); + expect(validated).to.deep.equal([{name: 'foo', segment: [{id: '1'}]}]); + }); + }); + + describe('validating first party data', function () { + it('filters user.data[0].ext for incorrect type', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + let expected = { + device: { + h: 911, + w: 1733 + }, + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + + it('filters user and site for empty data', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + let expected = { + device: { + h: 911, + w: 1733 + } + }; + + duplicate.user.data = []; + duplicate.site.content.data = []; + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + + it('filters user for empty valid segment values', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + let expected = { + device: { + h: 911, + w: 1733 + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + duplicate.user.data[0].segment.push({test: 3}); + duplicate.user.data[0].segment[0] = {foo: 'bar'}; + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + + it('filters user.data[0].ext and site.content.data[0].segement[1] for invalid data', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + let expected = { + device: { + h: 911, + w: 1733 + }, + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + duplicate.site.content.data[0].segment.push({test: 3}); + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + + it('filters device for invalid data types', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + duplicate.device = { + h: '1', + w: '1' + } + + let expected = { + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + duplicate.site.content.data[0].segment.push({test: 3}); + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + + it('filters cur for invalid data type', function () { + let validated; + let duplicate = utils.deepClone(ortb2); + duplicate.cur = 8; + + let expected = { + device: { + h: 911, + w: 1733 + }, + user: { + data: [{ + segment: [{ + id: 'foo' + }], + name: 'bar' + }] + }, + site: { + content: { + data: [{ + segment: [{ + id: 'test' + }], + name: 'content', + ext: { + foo: 'bar' + } + }] + } + } + }; + + duplicate.site.content.data[0].segment.push({test: 3}); + + validated = validateFpd(duplicate); + expect(validated).to.deep.equal(expected); + }); + }); +}); From 2b8f888dc11d727f49867309f37e492d65eaee96 Mon Sep 17 00:00:00 2001 From: htang555 Date: Fri, 14 May 2021 07:26:42 -0400 Subject: [PATCH 0947/1476] Datablocks bid adapter: update adapter to conform to new bid server's format (#6696) * update datablocks bid adapter * remove TODO and fix linting errors * updated readme and changed insights to ortb2 * fixed ortb2 change Co-authored-by: John Mayor --- modules/datablocksAnalyticsAdapter.js | 2 +- modules/datablocksBidAdapter.js | 862 ++++++++++++------ modules/datablocksBidAdapter.md | 30 +- .../spec/modules/datablocksBidAdapter_spec.js | 601 +++++++----- 4 files changed, 988 insertions(+), 507 deletions(-) diff --git a/modules/datablocksAnalyticsAdapter.js b/modules/datablocksAnalyticsAdapter.js index 5e977155284..3e4e9e95a4f 100644 --- a/modules/datablocksAnalyticsAdapter.js +++ b/modules/datablocksAnalyticsAdapter.js @@ -16,4 +16,4 @@ adapterManager.registerAnalyticsAdapter({ code: 'datablocks' }); -export default datablocksAdapter; +export default datablocksAdapter; \ No newline at end of file diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index b00a3eae659..038a521308d 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,330 +1,634 @@ -import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -const NATIVE_MAP = { - 'body': 2, - 'body2': 10, - 'price': 6, - 'displayUrl': 11, - 'cta': 12 -}; -const NATIVE_IMAGE = [{ - id: 1, - required: 1, +import { config } from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +export const storage = getStorageManager(); + +const NATIVE_ID_MAP = {}; +const NATIVE_PARAMS = { title: { - len: 140 - } -}, { - id: 2, - required: 1, - img: { type: 3 } -}, { - id: 3, - required: 1, - data: { - type: 11 - } -}, { - id: 4, - required: 0, - data: { + id: 1, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + body: { + id: 4, + name: 'data', type: 2 + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + cta: { + id: 6, + type: 12, + name: 'data' + }, + body2: { + id: 7, + name: 'data', + type: 10 + }, + rating: { + id: 8, + name: 'data', + type: 3 + }, + likes: { + id: 9, + name: 'data', + type: 4 + }, + downloads: { + id: 10, + name: 'data', + type: 5 + }, + displayUrl: { + id: 11, + name: 'data', + type: 11 + }, + price: { + id: 12, + name: 'data', + type: 6 + }, + salePrice: { + id: 13, + name: 'data', + type: 7 + }, + address: { + id: 14, + name: 'data', + type: 9 + }, + phone: { + id: 15, + name: 'data', + type: 8 } -}, { - id: 5, - required: 0, - img: { type: 1 } -}, { - id: 6, - required: 0, - data: { - type: 12 - } -}]; +}; -const VIDEO_PARAMS = ['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']; +Object.keys(NATIVE_PARAMS).forEach((key) => { + NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key; +}); +// DEFINE THE PREBID BIDDER SPEC export const spec = { - supportedMediaTypes: [BANNER, NATIVE, VIDEO], + supportedMediaTypes: [BANNER, NATIVE], code: 'datablocks', + + // DATABLOCKS SCOPED OBJECT + db_obj: {metrics_host: 'prebid.datablocks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, + + // STORE THE DATABLOCKS BUYERID IN STORAGE + store_dbid: function(dbid) { + let stored = false; + + // CREATE 1 YEAR EXPIRY DATE + let d = new Date(); + d.setTime(Date.now() + (365 * 24 * 60 * 60 * 1000)); + + // TRY TO STORE IN COOKIE + if (storage.cookiesAreEnabled) { + storage.setCookie('_db_dbid', dbid, d.toUTCString(), 'None', null); + stored = true; + } + + // TRY TO STORE IN LOCAL STORAGE + if (storage.localStorageIsEnabled) { + storage.setDataInLocalStorage('_db_dbid', dbid); + stored = true; + } + + return stored; + }, + + // FETCH DATABLOCKS BUYERID FROM STORAGE + get_dbid: function() { + let dbId = ''; + if (storage.cookiesAreEnabled) { + dbId = storage.getCookie('_db_dbid') || ''; + } + + if (!dbId && storage.localStorageIsEnabled) { + dbId = storage.getDataFromLocalStorage('_db_dbid') || ''; + } + return dbId; + }, + + // STORE SYNCS IN STORAGE + store_syncs: function(syncs) { + if (storage.localStorageIsEnabled) { + let syncObj = {}; + syncs.forEach(sync => { + syncObj[sync.id] = sync.uid; + }); + + // FETCH EXISTING SYNCS AND MERGE NEW INTO STORAGE + let storedSyncs = this.get_syncs(); + storage.setDataInLocalStorage('_db_syncs', JSON.stringify(Object.assign(storedSyncs, syncObj))); + + return true; + } + }, + + // GET SYNCS FROM STORAGE + get_syncs: function() { + if (storage.localStorageIsEnabled) { + let syncData = storage.getDataFromLocalStorage('_db_syncs'); + if (syncData) { + return JSON.parse(syncData); + } else { + return {}; + } + } else { + return {}; + } + }, + + // ADD METRIC DATA TO THE METRICS RESPONSE QUEUE + queue_metric: function(metric) { + if (typeof metric === 'object') { + // PUT METRICS IN THE QUEUE + this.db_obj.metrics.push(metric); + + // RESET PREVIOUS TIMER + if (this.db_obj.metrics_timer) { + clearTimeout(this.db_obj.metrics_timer); + } + + // SETUP THE TIMER TO FIRE BACK THE DATA + let scope = this; + this.db_obj.metrics_timer = setTimeout(function() { + scope.send_metrics(); + }, this.db_obj.metrics_queue_time); + + return true; + } else { + return false; + } + }, + + // POST CONSOLIDATED METRICS BACK TO SERVER + send_metrics: function() { + // POST TO SERVER + ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), {method: 'POST', withCredentials: true}); + + // RESET THE QUEUE OF METRIC DATA + this.db_obj.metrics = []; + + return true; + }, + + // GET BASIC CLIENT INFORMATION + get_client_info: function () { + let botTest = new BotClientTests(); + let win = utils.getWindowTop(); + return { + 'wiw': win.innerWidth, + 'wih': win.innerHeight, + 'saw': screen ? screen.availWidth : null, + 'sah': screen ? screen.availHeight : null, + 'scd': screen ? screen.colorDepth : null, + 'sw': screen ? screen.width : null, + 'sh': screen ? screen.height : null, + 'whl': win.history.length, + 'wxo': win.pageXOffset, + 'wyo': win.pageYOffset, + 'wpr': win.devicePixelRatio, + 'is_bot': botTest.doTests(), + 'is_hid': win.document.hidden, + 'vs': win.document.visibilityState + }; + }, + + // LISTEN FOR GPT VIEWABILITY EVENTS + get_viewability: function(bid) { + // ONLY RUN ONCE IF PUBLISHER HAS OPTED IN + if (!this.db_obj.vis_optout && !this.db_obj.vis_run) { + this.db_obj.vis_run = true; + + // ADD GPT EVENT LISTENERS + let scope = this; + if (utils.isGptPubadsDefined()) { + if (typeof window['googletag'].pubads().addEventListener == 'function') { + window['googletag'].pubads().addEventListener('impressionViewable', function(event) { + scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + }); + window['googletag'].pubads().addEventListener('slotRenderEnded', function(event) { + scope.queue_metric({type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + }) + } + } + } + }, + + // VALIDATE THE BID REQUEST isBidRequestValid: function(bid) { - return !!(bid.params.host && bid.params.sourceId && - bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); + // SET GLOBAL VARS FROM BIDDER CONFIG + this.db_obj.source_id = bid.params.source_id; + if (bid.params.vis_optout) { + this.db_obj.vis_optout = true; + } + + return !!(bid.params.source_id && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native)); }, - buildRequests: function(validBidRequests, bidderRequest) { - if (!validBidRequests.length) { return []; } - let imps = {}; - let site = {}; - let device = {}; - let refurl = utils.parseUrl(bidderRequest.referrer); - let requests = []; + // GENERATE THE RTB REQUEST + buildRequests: function(validRequests, bidderRequest) { + // RETURN EMPTY IF THERE ARE NO VALID REQUESTS + if (!validRequests.length) { + return []; + } - validBidRequests.forEach(bidRequest => { + // CONVERT PREBID NATIVE REQUEST OBJ INTO RTB OBJ + function createNativeRequest(bid) { + const assets = []; + if (bid.nativeParams) { + Object.keys(bid.nativeParams).forEach((key) => { + if (NATIVE_PARAMS[key]) { + const {name, type, id} = NATIVE_PARAMS[key]; + const assetObj = type ? {type} : {}; + let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key]; + if (len) { + assetObj.len = len; + } + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + let wmin = aRatios.min_width || 0; + let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + assetObj.wmin = wmin; + assetObj.hmin = hmin; + } + if (sizes && sizes.length) { + sizes = [].concat(...sizes); + assetObj.w = sizes[0]; + assetObj.h = sizes[1]; + } + const asset = {required: required ? 1 : 0, id}; + asset[name] = assetObj; + assets.push(asset); + } + }); + } + return { + ver: '1.2', + request: { + assets: assets, + context: 1, + plcmttype: 1, + ver: '1.2' + } + } + } + let imps = []; + // ITERATE THE VALID REQUESTS AND GENERATE IMP OBJECT + validRequests.forEach(bidRequest => { + // BUILD THE IMP OBJECT let imp = { id: bidRequest.bidId, - tagid: bidRequest.adUnitCode, - secure: window.location.protocol == 'https:' + tagid: bidRequest.params.tagid || bidRequest.adUnitCode, + placement_id: bidRequest.params.placement_id || 0, + secure: window.location.protocol == 'https:', + ortb2: utils.deepAccess(bidRequest, `ortb2Imp`) || {}, + floor: {} } + // CHECK FOR FLOORS + if (typeof bidRequest.getFloor === 'function') { + imp.floor = bidRequest.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + } + + // BUILD THE SIZES if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { - let sizes = bidRequest.mediaTypes.banner.sizes; - if (sizes.length == 1) { + let sizes = utils.getAdUnitSizes(bidRequest); + if (sizes.length) { imp.banner = { w: sizes[0][0], - h: sizes[0][1] - } - } else if (sizes.length > 1) { - imp.banner = { + h: sizes[0][1], format: sizes.map(size => ({ w: size[0], h: size[1] })) }; - } else { - return; - } - } else if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { - let nativeImp = bidRequest.mediaTypes.native; - - if (nativeImp.type) { - let nativeAssets = []; - switch (nativeImp.type) { - case 'image': - nativeAssets = NATIVE_IMAGE; - break; - default: - return; - } - imp.native = JSON.stringify({ assets: nativeAssets }); - } else { - let nativeAssets = []; - let nativeKeys = Object.keys(nativeImp); - nativeKeys.forEach((nativeKey, index) => { - let required = !!nativeImp[nativeKey].required; - let assetId = index + 1; - switch (nativeKey) { - case 'title': - nativeAssets.push({ - id: assetId, - required: required, - title: { - len: nativeImp[nativeKey].len || 140 - } - }); - break; - case 'body': // desc - case 'body2': // desc2 - case 'price': - case 'display_url': - let data = { - id: assetId, - required: required, - data: { - type: NATIVE_MAP[nativeKey] - } - } - if (nativeImp[nativeKey].data && nativeImp[nativeKey].data.len) { data.data.len = nativeImp[nativeKey].data.len; } - - nativeAssets.push(data); - break; - case 'image': - if (nativeImp[nativeKey].sizes && nativeImp[nativeKey].sizes.length) { - nativeAssets.push({ - id: assetId, - required: required, - image: { - type: 3, - w: nativeImp[nativeKey].sizes[0], - h: nativeImp[nativeKey].sizes[1] - } - }) - } - } - }); - imp.native = { - request: JSON.stringify({native: {assets: nativeAssets}}) - }; - } - } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { - let video = bidRequest.mediaTypes.video; - let sizes = video.playerSize || bidRequest.sizes || []; - if (sizes.length && Array.isArray(sizes[0])) { - imp.video = { - w: sizes[0][0], - h: sizes[0][1] - }; - } else if (sizes.length == 2 && !Array.isArray(sizes[0])) { - imp.video = { - w: sizes[0], - h: sizes[1] - }; - } else { - return; - } - - if (video.durationRangeSec) { - if (Array.isArray(video.durationRangeSec)) { - if (video.durationRangeSec.length == 1) { - imp.video.maxduration = video.durationRangeSec[0]; - } else if (video.durationRangeSec.length == 2) { - imp.video.minduration = video.durationRangeSec[0]; - imp.video.maxduration = video.durationRangeSec[1]; - } - } else { - imp.video.maxduration = video.durationRangeSec; - } - } - if (bidRequest.params.video) { - Object.keys(bidRequest.params.video).forEach(k => { - if (VIDEO_PARAMS.indexOf(k) > -1) { - imp.video[k] = bidRequest.params.video[k]; - } - }) + // ADD TO THE LIST OF IMP REQUESTS + imps.push(imp); } + } else if (utils.deepAccess(bidRequest, `mediaTypes.native`)) { + // ADD TO THE LIST OF IMP REQUESTS + imp.native = createNativeRequest(bidRequest); + imps.push(imp); } - let host = bidRequest.params.host; - let sourceId = bidRequest.params.sourceId; - imps[host] = imps[host] || {}; - let hostImp = imps[host][sourceId] = imps[host][sourceId] || { imps: [] }; - hostImp.imps.push(imp); - hostImp.subid = hostImp.imps.subid || bidRequest.params.subid || 'blank'; - hostImp.path = 'search'; - hostImp.idParam = 'sid'; - hostImp.protocol = '//'; }); - // Generate Site obj - site.domain = refurl.hostname; - site.page = refurl.protocol + '://' + refurl.hostname + refurl.pathname; + // RETURN EMPTY IF THERE WERE NO PROPER ADUNIT REQUESTS TO BE MADE + if (!imps.length) { + return []; + } + + // GENERATE SITE OBJECT + let site = { + domain: window.location.host, + page: bidderRequest.refererInfo.referer, + schain: validRequests[0].schain || {}, + ext: { + p_domain: config.getConfig('publisherDomain'), + rt: bidderRequest.refererInfo.reachedTop, + frames: bidderRequest.refererInfo.numIframes, + stack: bidderRequest.refererInfo.stack, + timeout: config.getConfig('bidderTimeout') + }, + } + + // ADD REF URL IF FOUND if (self === top && document.referrer) { site.ref = document.referrer; } + + // ADD META KEYWORDS IF FOUND let keywords = document.getElementsByTagName('meta')['keywords']; if (keywords && keywords.content) { site.keywords = keywords.content; } - // Generate Device obj. - device.ip = 'peer'; - device.ua = window.navigator.userAgent; - device.js = 1; - device.language = ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en'; - - RtbRequest(device, site, imps).forEach(formatted => { - requests.push({ - method: 'POST', - url: formatted.url, - data: formatted.body, - options: { - withCredentials: false + // GENERATE DEVICE OBJECT + let device = { + ip: 'peer', + ua: window.navigator.userAgent, + js: 1, + language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', + buyerid: this.get_dbid() || 0, + ext: { + pb_eids: validRequests[0].userIdAsEids || {}, + syncs: this.get_syncs() || {}, + coppa: config.getConfig('coppa') || 0, + gdpr: bidderRequest.gdprConsent || {}, + usp: bidderRequest.uspConsent || {}, + client_info: this.get_client_info(), + ortb2: config.getConfig('ortb2') || {} + } + }; + + let sourceId = validRequests[0].params.source_id || 0; + let host = validRequests[0].params.host || 'prebid.datablocks.net'; + + // RETURN WITH THE REQUEST AND PAYLOAD + return { + method: 'POST', + url: `https://${sourceId}.${host}/openrtb/?sid=${sourceId}`, + data: { + id: bidderRequest.auctionId, + imp: imps, + site: site, + device: device + }, + options: { + withCredentials: true + } + }; + }, + + // INITIATE USER SYNCING + getUserSyncs: function(options, rtbResponse, gdprConsent) { + const syncs = []; + let bidResponse = rtbResponse[0].body; + let scope = this; + + // LISTEN FOR SYNC DATA FROM IFRAME TYPE SYNC + window.addEventListener('message', function (event) { + if (event.data.sentinel && event.data.sentinel === 'dblks_syncData') { + // STORE FOUND SYNCS + if (event.data.syncs) { + scope.store_syncs(event.data.syncs); } - }) + } }); - return requests; - - function RtbRequest(device, site, imps) { - let collection = []; - Object.keys(imps).forEach(host => { - let sourceIds = imps[host]; - Object.keys(sourceIds).forEach(sourceId => { - let impObj = sourceIds[sourceId]; - collection.push({ - url: `https://${host}/${impObj.path}/?${impObj.idParam}=${sourceId}`, - body: { - id: bidderRequest.auctionId, - imp: impObj.imps, - site: Object.assign({ id: impObj.subid || 'blank' }, site), - device: Object.assign({}, device) - } - }) - }) + + // POPULATE GDPR INFORMATION + let gdprData = { + gdpr: 0, + gdprConsent: '' + } + if (typeof gdprConsent === 'object') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprData.gdpr = Number(gdprConsent.gdprApplies); + gdprData.gdprConsent = gdprConsent.consentString; + } else { + gdprData.gdprConsent = gdprConsent.consentString; + } + } + + // EXTRACT BUYERID COOKIE VALUE FROM BID RESPONSE AND PUT INTO STORAGE + let dbBuyerId = this.get_dbid() || ''; + if (bidResponse.ext && bidResponse.ext.buyerid) { + dbBuyerId = bidResponse.ext.buyerid; + this.store_dbid(dbBuyerId); + } + + // EXTRACT USERSYNCS FROM BID RESPONSE + if (bidResponse.ext && bidResponse.ext.syncs) { + bidResponse.ext.syncs.forEach(sync => { + if (checkValid(sync)) { + syncs.push(addParams(sync)); + } }) + } + + // APPEND PARAMS TO SYNC URL + function addParams(sync) { + // PARSE THE URL + let url = new URL(sync.url); + let urlParams = Object.assign({}, Object.fromEntries(url.searchParams)); + + // APPLY EXTRA VARS + urlParams.gdpr = gdprData.gdpr; + urlParams.gdprConsent = gdprData.gdprConsent; + urlParams.bidid = bidResponse.bidid; + urlParams.id = bidResponse.id; + urlParams.uid = dbBuyerId; + + // REBUILD URL + sync.url = `${url.origin}${url.pathname}?${Object.keys(urlParams).map(key => key + '=' + encodeURIComponent(urlParams[key])).join('&')}`; - return collection; + // RETURN THE REBUILT URL + return sync; } + + // ENSURE THAT THE SYNC TYPE IS VALID AND HAS PERMISSION + function checkValid(sync) { + if (!sync.type || !sync.url) { + return false; + } + switch (sync.type) { + case 'iframe': + return options.iframeEnabled; + case 'image': + return options.pixelEnabled; + default: + return false; + } + } + return syncs; }, - interpretResponse: function(serverResponse, bidRequest) { - if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { - return []; + + // DATABLOCKS WON THE AUCTION - REPORT SUCCESS + onBidWon: function(bid) { + this.queue_metric({type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl}); + }, + + // TARGETING HAS BEEN SET + onSetTargeting: function(bid) { + // LISTEN FOR VIEWABILITY EVENTS + this.get_viewability(bid); + }, + + // PARSE THE RTB RESPONSE AND RETURN FINAL RESULTS + interpretResponse: function(rtbResponse, bidRequest) { + // CONVERT NATIVE RTB RESPONSE INTO PREBID RESPONSE + function parseNative(native) { + const {assets, link, imptrackers, jstracker} = native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || [], + impressionTrackers: imptrackers || [], + javascriptTrackers: jstracker ? [jstracker] : [] + }; + + (assets || []).forEach((asset) => { + const {id, img, data, title} = asset; + const key = NATIVE_ID_MAP[id]; + if (key) { + if (!utils.isEmpty(title)) { + result.title = title.text + } else if (!utils.isEmpty(img)) { + result[key] = { + url: img.url, + height: img.h, + width: img.w + } + } else if (!utils.isEmpty(data)) { + result[key] = data.value; + } + } + }); + + return result; } - let body = serverResponse.body; - - let bids = body.seatbid - .map(seatbid => seatbid.bid) - .reduce((memo, bid) => memo.concat(bid), []); - let req = bidRequest.data; - let reqImps = req.imp; - - return bids.map(rtbBid => { - let imp; - for (let i in reqImps) { - let testImp = reqImps[i] - if (testImp.id == rtbBid.impid) { - imp = testImp; + + let bids = []; + let resBids = utils.deepAccess(rtbResponse, 'body.seatbid') || []; + resBids.forEach(bid => { + let resultItem = {requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360}; + + let mediaType = utils.deepAccess(bid, 'ext.mtype') || ''; + switch (mediaType) { + case 'banner': + bids.push(Object.assign({}, resultItem, {mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm})); + break; + + case 'native': + let nativeResult = JSON.parse(bid.adm); + bids.push(Object.assign({}, resultItem, {mediaType: NATIVE, native: parseNative(nativeResult.native)})); + break; + + default: break; - } } - let br = { - requestId: rtbBid.impid, - cpm: rtbBid.price, - creativeId: rtbBid.crid, - currency: rtbBid.currency || 'USD', - netRevenue: true, - ttl: 360 - }; - if (!imp) { - return br; - } else if (imp.banner) { - br.mediaType = BANNER; - br.width = rtbBid.w; - br.height = rtbBid.h; - br.ad = rtbBid.adm; - } else if (imp.native) { - br.mediaType = NATIVE; - - let reverseNativeMap = {}; - let nativeKeys = Object.keys(NATIVE_MAP); - nativeKeys.forEach(k => { - reverseNativeMap[NATIVE_MAP[k]] = k; - }); + }) - let idMap = {}; - let nativeReq = JSON.parse(imp.native.request); - if (nativeReq.native && nativeReq.native.assets) { - nativeReq.native.assets.forEach(asset => { - if (asset.data) { idMap[asset.id] = reverseNativeMap[asset.data.type]; } - }) + return bids; + } +}; + +// DETECT BOTS +export class BotClientTests { + constructor() { + this.tests = { + headless_chrome: function() { + if (self.navigator) { + if (self.navigator.webdriver) { + return true; + } } - const nativeResponse = JSON.parse(rtbBid.adm); - const { assets, link, imptrackers, jstrackers } = nativeResponse.native; - const result = { - clickUrl: link.url, - clickTrackers: link.clicktrackers || undefined, - impressionTrackers: imptrackers || undefined, - javascriptTrackers: jstrackers ? [jstrackers] : undefined - }; - assets.forEach(asset => { - if (asset.title) { - result.title = asset.title.text; - } else if (asset.img) { - result.image = asset.img.url; - } else if (idMap[asset.id]) { - result[idMap[asset.id]] = asset.data.value; + return false; + }, + user_agent: function() { + try { + var re = new RegExp('(googlebot\/|bot|Googlebot-Mobile|Googlebot-Image|Google favicon|Mediapartners-Google|bingbot|slurp|java|wget|curl|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|dotbot|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|Twitterbot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis)', 'i'); + if (re.test(navigator.userAgent)) { + return true; } - }) - br.native = result; - } else if (imp.video) { - br.mediaType = VIDEO; - br.width = rtbBid.w; - br.height = rtbBid.h; - if (rtbBid.adm) { br.vastXml = rtbBid.adm; } else if (rtbBid.nurl) { br.vastUrl = rtbBid.nurl; } + return false; + } catch (e) { + return false; + } + }, + + selenium: function () { + let response = false; + + if (window && document) { + let results = [ + 'webdriver' in window, + '_Selenium_IDE_Recorder' in window, + 'callSelenium' in window, + '_selenium' in window, + '__webdriver_script_fn' in document, + '__driver_evaluate' in document, + '__webdriver_evaluate' in document, + '__selenium_evaluate' in document, + '__fxdriver_evaluate' in document, + '__driver_unwrapped' in document, + '__webdriver_unwrapped' in document, + '__selenium_unwrapped' in document, + '__fxdriver_unwrapped' in document, + '__webdriver_script_func' in document, + document.documentElement.getAttribute('selenium') !== null, + document.documentElement.getAttribute('webdriver') !== null, + document.documentElement.getAttribute('driver') !== null + ]; + + results.forEach(result => { + if (result === true) { + response = true; + } + }) + } + + return response; + }, + } + } + + doTests() { + let response = false; + for (const [, t] of Object.entries(this.tests)) { + if (t() === true) { + response = true; } - return br; - }); + } + return response; } +} -}; +// INIT OUR BIDDER WITH PREBID registerBidder(spec); diff --git a/modules/datablocksBidAdapter.md b/modules/datablocksBidAdapter.md index e30cd361974..2730443d72d 100644 --- a/modules/datablocksBidAdapter.md +++ b/modules/datablocksBidAdapter.md @@ -8,8 +8,8 @@ Maintainer: support@datablocks.net # Description -Connects to Datablocks Version 5 Platform -Banner Native and Video +Connects to Datablocks Exchange +Banner and Native # Test Parameters @@ -27,12 +27,13 @@ Banner Native and Video { bidder: 'datablocks', params: { - sourceId: 12345, + source_id: 12345, host: 'prebid.datablocks.net' } } ] - }, { + }, + { code: 'native-div', mediaTypes : { native: { @@ -44,28 +45,9 @@ Banner Native and Video { bidder: 'datablocks', params: { - sourceId: 12345, + source_id: 12345, host: 'prebid.datablocks.net' } - }, { - code: 'video-div', - mediaTypes : { - video: { - playerSize:[500,400], - durationRangeSec:[15,30], - context: "linear" - } - }, - bids: [ - { - bidder: 'datablocks', - params: { - sourceId: 12345, - host: 'prebid.datablocks.net', - video: { - mimes:["video/flv"] - } - } } ] } diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 18b8aac7371..147000f2363 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -1,12 +1,15 @@ import { expect } from 'chai'; import { spec } from '../../../modules/datablocksBidAdapter.js'; +import { BotClientTests } from '../../../modules/datablocksBidAdapter.js'; +import { getStorageManager } from '../../../src/storageManager.js'; +export let storage = getStorageManager(); -let bid = { +const bid = { bidId: '2dd581a2b6281d', bidder: 'datablocks', bidderRequestId: '145e1d6a7837c9', params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -24,12 +27,12 @@ let bid = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe5542e8e1' }; -let bid2 = { +const bid2 = { bidId: '2dd581a2b624324g', bidder: 'datablocks', bidderRequestId: '145e1d6a7837543', params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -43,7 +46,7 @@ let bid2 = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe55425432' }; -let nativeBid = { +const nativeBid = { adUnitCode: '/19968336/header-bid-tag-0', auctionId: '160c78a4-f808-410f-b682-d8728f3a79ee', bidId: '332045ee374a99', @@ -78,41 +81,18 @@ let nativeBid = { } }, params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f6' } -let videoBid = { - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '160c78a4-f808-410f-b682-d8728f3a79e1', - bidId: '332045ee374b99', - bidder: 'datablocks', - bidderRequestId: '15d9012765e36d', - mediaTypes: { - video: { - context: 'instream', - playerSize: [501, 400], - durationRangeSec: [15, 60] - } - }, - params: { - sourceId: 7560, - host: 'v5demo.datablocks.net', - video: { - minduration: 14 - } - }, - transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f7' -} - const bidderRequest = { auctionId: '8bfef1be-d3ac-4d18-8859-754c7b4cf017', auctionStart: Date.now(), biddeCode: 'datablocks', bidderRequestId: '10c47a5fc3c41', - bids: [bid, bid2, nativeBid, videoBid], + bids: [bid, bid2, nativeBid], refererInfo: { numIframes: 0, reachedTop: true, @@ -123,208 +103,423 @@ const bidderRequest = { timeout: 10000 }; -let resObject = { +const res_object = { body: { - id: '10c47a5fc3c41', - bidid: '166895245-28-11347-1', - seatbid: [{ - seat: '7560', - bid: [{ - id: '1090738570', - impid: '2966b257c81d27', - price: 24.000000, - adm: 'RON', - cid: '55', - adid: '177654', - crid: '177656', - cat: [], - api: [], - w: 300, - h: 250 - }, { - id: '1090738571', - impid: '2966b257c81d28', - price: 24.000000, - adm: 'RON', - cid: '55', - adid: '177654', - crid: '177656', - cat: [], - api: [], - w: 728, - h: 90 - }, { - id: '1090738570', - impid: '15d9012765e36c', - price: 24.000000, - adm: '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"Example Title"}},{"id":2,"required":1,"data":{"value":"Example Body"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', - cid: '132145', - adid: '154321', - crid: '177432', - cat: [], - api: [] - }, { - id: '1090738575', - impid: '15d9012765e36f', - price: 25.000000, - cid: '12345', - adid: '12345', - crid: '123456', - nurl: 'https://click.v5demo.datablocks.net/m//?fcid=435235435432', - cat: [], - api: [], - w: 500, - h: 400 - }] - }], - cur: 'USD', - ext: {} + 'id': '10c47a5fc3c41', + 'bidid': '217868445-30021-19053-0', + 'seatbid': [ + { + 'id': '22621593137287', + 'impid': '1', + 'adm': 'John is great', + 'adomain': ['medianet.com'], + 'price': 0.430000, + 'cid': '2524568', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'type': 'CPM', + 'mtype': 'banner' + } + }, + { + 'id': '22645215457415', + 'impid': '2', + 'adm': 'john is the best', + 'adomain': ['td.com'], + 'price': 0.580000, + 'cid': '2524574', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'w': 728, + 'h': 90, + 'ext': { + 'type': 'CPM', + 'mtype': 'banner' + } + }, + + { + 'id': '22645215457416', + 'impid': '3', + 'adm': '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"John is amazing"}},{"id":5,"required":1,"data":{"value":"Sponsored by John"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/", "h":"360", "w":"360"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', + 'adomain': ['td.com'], + 'price': 10.00, + 'cid': '2524574', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'ext': { + 'type': 'CPM', + 'mtype': 'native' + } + } + ], + 'cur': 'USD', + 'ext': { + 'version': '1.2.93', + 'buyerid': '1234567', + 'syncs': [ + { + 'type': 'iframe', + 'url': 'https://s.0cf.io' + }, + { + 'type': 'image', + 'url': 'https://us.dblks.net/set_uid/' + } + ] + } } -}; -let bidRequest = { +} + +let bid_request = { method: 'POST', - url: 'https://v5demo.datablocks.net/search/?sid=7560', + url: 'https://prebid.datablocks.net/openrtb/?sid=2523014', options: { - withCredentials: false + withCredentials: true }, data: { - device: { - ip: 'peer', - ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) Ap…ML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', - js: 1, - language: 'en' - }, - id: '10c47a5fc3c41', - imp: [{ - banner: { w: 300, h: 250 }, - id: '2966b257c81d27', - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - banner: { w: 728, h: 90 }, - id: '2966b257c81d28', - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - id: '15d9012765e36c', - native: {request: '{"native":{"assets":[{"id":"1","required":true,"title":{"len":140}},{"id":"2","required":true,"data":{"type":2}},{"id":"3","img":{"w":728,"h":90,"type":3}}]}}'}, - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - id: '15d9012765e36f', - video: {w: 500, h: 400, minduration: 15, maxduration: 60}, - secure: false, - tagid: '/19968336/header-bid-tag-0' - }], - site: { - domain: '', - id: 'blank', - page: 'https://v5demo.datablocks.net/test' - } + 'id': 'c09c6e47-8bdb-4884-a46d-93165322b368', + 'imp': [{ + 'id': '1', + 'tagid': '/19968336/header-bid-tag-0', + 'placement_id': 0, + 'secure': true, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{ + 'w': 300, + 'h': 250 + }, { + 'w': 300, + 'h': 600 + }] + } + }, { + 'id': '2', + 'tagid': '/19968336/header-bid-tag-1', + 'placement_id': 12345, + 'secure': true, + 'banner': { + 'w': 729, + 'h': 90, + 'format': [{ + 'w': 729, + 'h': 90 + }, { + 'w': 970, + 'h': 250 + }] + } + }, { + 'id': '3', + 'tagid': '/19968336/prebid_multiformat_test', + 'placement_id': 0, + 'secure': true, + 'native': { + 'ver': '1.2', + 'request': { + 'assets': [{ + 'required': 1, + 'id': 1, + 'title': {} + }, { + 'required': 1, + 'id': 3, + 'img': { + 'type': 3 + } + }, { + 'required': 1, + 'id': 5, + 'data': { + 'type': 1 + } + }], + 'context': 1, + 'plcmttype': 1, + 'ver': '1.2' + } + } + }], + 'site': { + 'domain': 'test.datablocks.net', + 'page': 'https://test.datablocks.net/index.html', + 'schain': {}, + 'ext': { + 'p_domain': 'https://test.datablocks.net', + 'rt': true, + 'frames': 0, + 'stack': ['https://test.datablocks.net/index.html'], + 'timeout': 3000 + }, + 'keywords': 'HTML, CSS, JavaScript' + }, + 'device': { + 'ip': 'peer', + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36', + 'js': 1, + 'language': 'en', + 'buyerid': '1234567', + 'ext': { + 'pb_eids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'test', + 'atype': 1 + }] + }], + 'syncs': { + '1000': 'db_4044853', + '1001': true + }, + 'coppa': 0, + 'gdpr': {}, + 'usp': {}, + 'client_info': { + 'wiw': 2560, + 'wih': 1281, + 'saw': 2560, + 'sah': 1417, + 'scd': 24, + 'sw': 2560, + 'sh': 1440, + 'whl': 4, + 'wxo': 0, + 'wyo': 0, + 'wpr': 2, + 'is_bot': false, + 'is_hid': false, + 'vs': 'hidden' + }, + 'fpd': {} + } + } } } describe('DatablocksAdapter', function() { + describe('All needed functions are available', function() { + it(`isBidRequestValid is present and type function`, function () { + expect(spec.isBidRequestValid).to.exist.and.to.be.a('function') + }); + + it(`buildRequests is present and type function`, function () { + expect(spec.buildRequests).to.exist.and.to.be.a('function') + }); + + it(`getUserSyncs is present and type function`, function () { + expect(spec.getUserSyncs).to.exist.and.to.be.a('function') + }); + + it(`onBidWon is present and type function`, function () { + expect(spec.onBidWon).to.exist.and.to.be.a('function') + }); + + it(`onSetTargeting is present and type function`, function () { + expect(spec.onSetTargeting).to.exist.and.to.be.a('function') + }); + + it(`interpretResponse is present and type function`, function () { + expect(spec.interpretResponse).to.exist.and.to.be.a('function') + }); + + it(`store_dbid is present and type function`, function () { + expect(spec.store_dbid).to.exist.and.to.be.a('function') + }); + + it(`get_dbid is present and type function`, function () { + expect(spec.get_dbid).to.exist.and.to.be.a('function') + }); + + it(`store_syncs is present and type function`, function () { + expect(spec.store_syncs).to.exist.and.to.be.a('function') + }); + + it(`get_syncs is present and type function`, function () { + expect(spec.get_syncs).to.exist.and.to.be.a('function') + }); + + it(`queue_metric is present and type function`, function () { + expect(spec.queue_metric).to.exist.and.to.be.a('function') + }); + + it(`send_metrics is present and type function`, function () { + expect(spec.send_metrics).to.exist.and.to.be.a('function') + }); + + it(`get_client_info is present and type function`, function () { + expect(spec.get_client_info).to.exist.and.to.be.a('function') + }); + + it(`get_viewability is present and type function`, function () { + expect(spec.get_viewability).to.exist.and.to.be.a('function') + }); + }); + + describe('get / store dbid', function() { + it('Should return true / undefined', function() { + expect(spec.store_dbid('12345')).to.be.true; + expect(spec.get_dbid()).to.be.a('string'); + }); + }) + + describe('get / store syncs', function() { + it('Should return true / array', function() { + expect(spec.store_syncs([{id: 1, uid: 'test'}])).to.be.true; + expect(spec.get_syncs()).to.be.a('object'); + }); + }) + + describe('queue / send metrics', function() { + it('Should return true', function() { + expect(spec.queue_metric({type: 'test'})).to.be.true; + expect(spec.queue_metric('string')).to.be.false; + expect(spec.send_metrics()).to.be.true; + }); + }) + + describe('get_viewability', function() { + it('Should return undefined', function() { + expect(spec.get_viewability()).to.equal(undefined); + }); + }) + + describe('get client info', function() { + it('Should return object', function() { + let client_info = spec.get_client_info() + expect(client_info).to.be.a('object'); + expect(client_info).to.have.all.keys('wiw', 'wih', 'saw', 'sah', 'scd', 'sw', 'sh', 'whl', 'wxo', 'wyo', 'wpr', 'is_bot', 'is_hid', 'vs'); + }); + + it('bot test should return boolean', function() { + let bot_test = new BotClientTests(); + expect(bot_test.doTests()).to.be.a('boolean'); + }); + }) + describe('isBidRequestValid', function() { - it('Should return true when sourceId and Host are set', function() { + it('Should return true when source_id and Host are set', function() { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when host/sourceId is not set', function() { + it('Should return false when host/source_id is not set', function() { let moddedBid = Object.assign({}, bid); - delete moddedBid.params.sourceId; - delete moddedBid.params.host; - expect(spec.isBidRequestValid(bid)).to.be.false; + delete moddedBid.params.source_id; + expect(spec.isBidRequestValid(moddedBid)).to.be.false; + }); + + it('Should return true when viewability reporting is opted out', function() { + let moddedBid = Object.assign({}, bid); + moddedBid.params.vis_optout = true; + spec.isBidRequestValid(moddedBid); + expect(spec.db_obj.vis_optout).to.be.true; + }); + }) + + describe('getUserSyncs', function() { + it('Should return array of syncs', function() { + expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [res_object], {gdprApplies: true, gdpr: 1, gdpr_consent: 'consent_string'}, {})).to.an('array'); + }); + }); + + describe('onSetTargeting', function() { + it('Should return undefined', function() { + expect(spec.onSetTargeting()).to.equal(undefined); + }); + }); + + describe('onBidWon', function() { + it('Should return undefined', function() { + let won_bid = {params: [{source_id: 1}], requestId: 1, adUnitCode: 'unit', auctionId: 1, size: '300x250', cpm: 10, adserverTargeting: {hb_pb: 10}, timeToRespond: 10, ttl: 10}; + expect(spec.onBidWon(won_bid)).to.equal(undefined); }); }); describe('buildRequests', function() { - let requests = spec.buildRequests([bid, bid2, nativeBid, videoBid], bidderRequest); + let request = spec.buildRequests([bid, bid2, nativeBid], bidderRequest); + + expect(request).to.exist; + it('Returns POST method', function() { + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function() { + expect(request.url).to.exist; + expect(request.url).to.equal('https://7560.v5demo.datablocks.net/openrtb/?sid=7560'); + }); + it('Creates an array of request objects', function() { - expect(requests).to.be.an('array').that.is.not.empty; + expect(request.data.imp).to.be.an('array').that.is.not.empty; }); - requests.forEach(request => { - expect(request).to.exist; - it('Returns POST method', function() { - expect(request.method).to.exist; - expect(request.method).to.equal('POST'); - }); - it('Returns valid URL', function() { - expect(request.url).to.exist; - expect(request.url).to.equal('https://v5demo.datablocks.net/search/?sid=7560'); - }); + it('Should be a valid openRTB request', function() { + let data = request.data; - it('Should be a valid openRTB request', function() { - let data = request.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); - expect(data.id).to.be.a('string'); - - let imps = data['imp']; - imps.forEach((imp, index) => { - let curBid = bidderRequest.bids[index]; - if (imp.banner) { - expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid'); - expect(imp.banner).to.be.a('object'); - } else if (imp.native) { - expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid'); - expect(imp.native).to.have.all.keys('request'); - expect(imp.native.request).to.be.a('string'); - let native = JSON.parse(imp.native.request); - expect(native).to.be.a('object'); - } else if (imp.video) { - expect(imp).to.have.all.keys('video', 'id', 'secure', 'tagid'); - expect(imp.video).to.have.all.keys('w', 'h', 'minduration', 'maxduration') - } else { - expect(true).to.equal(false); - } - - expect(imp.id).to.be.a('string'); - expect(imp.id).to.equal(curBid.bidId); - expect(imp.tagid).to.be.a('string'); - expect(imp.tagid).to.equal(curBid.adUnitCode); - expect(imp.secure).to.equal(false); - }) - - expect(data.device.ip).to.equal('peer'); - }); - }) + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); + expect(data.id).to.be.a('string'); + expect(data.imp).to.be.a('array'); + expect(data.device.ip).to.equal('peer'); + + let imps = data['imp']; + imps.forEach((imp, index) => { + let curBid = bidderRequest.bids[index]; + if (imp.banner) { + expect(imp.banner).to.be.a('object'); + expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); + } else if (imp.native) { + expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); + expect(imp.native).to.have.all.keys('request', 'ver'); + expect(imp.native.request).to.be.a('object'); + } else { + expect(true).to.equal(false); + } + + expect(imp.id).to.be.a('string'); + expect(imp.id).to.equal(curBid.bidId); + expect(imp.tagid).to.be.a('string'); + expect(imp.tagid).to.equal(curBid.adUnitCode); + expect(imp.secure).to.equal(false); + }) + }); it('Returns empty data if no valid requests are passed', function() { - let request = spec.buildRequests([]); - expect(request).to.be.an('array').that.is.empty; + let test_request = spec.buildRequests([]); + expect(test_request).to.be.an('array').that.is.empty; }); }); + describe('interpretResponse', function() { - let serverResponses = spec.interpretResponse(resObject, bidRequest); + let response = spec.interpretResponse(res_object, bid_request); + it('Returns an array of valid server responses if response object is valid', function() { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(Object.keys(dataItem)).to.include('cpm', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType', 'requestId'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - - if (dataItem.mediaType == 'banner') { - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - } else if (dataItem.mediaType == 'native') { - expect(dataItem.native.title).to.be.a('string'); - expect(dataItem.native.body).to.be.a('string'); - expect(dataItem.native.clickUrl).to.be.a('string'); - } else if (dataItem.mediaType == 'video') { - expect(dataItem.vastUrl).to.be.a('string'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); + expect(response).to.be.an('array').that.is.not.empty; + + response.forEach(bid => { + expect(parseInt(bid.requestId)).to.be.a('number').greaterThan(0); + expect(bid.cpm).to.be.a('number'); + expect(bid.creativeId).to.be.a('string'); + expect(bid.currency).to.be.a('string'); + expect(bid.netRevenue).to.be.a('boolean'); + expect(bid.ttl).to.be.a('number'); + expect(bid.mediaType).to.be.a('string'); + + if (bid.mediaType == 'banner') { + expect(bid.width).to.be.a('number'); + expect(bid.height).to.be.a('number'); + expect(bid.ad).to.be.a('string'); + } else if (bid.mediaType == 'native') { + expect(bid.native).to.be.a('object'); } - } + }) + it('Returns an empty array if invalid response is passed', function() { serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; From 78a00fd21c5f29e37f02d2d30fa28ca8ffbaa9e8 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Fri, 14 May 2021 08:33:09 -0400 Subject: [PATCH 0948/1476] Update to videoCache to include auction Id in vasttrack payload (#6757) --- src/videoCache.js | 1 + test/spec/videoCache_spec.js | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/videoCache.js b/src/videoCache.js index 9f1fd7e4117..9e378d90574 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -71,6 +71,7 @@ function toStorageRequest(bid) { if (config.getConfig('cache.vasttrack')) { payload.bidder = bid.bidder; payload.bidid = bid.requestId; + payload.aid = bid.auctionId; // function has a thisArg set to bidderRequest for accessing the auctionStart if (utils.isPlainObject(this) && this.hasOwnProperty('auctionStart')) { payload.timestamp = this.auctionStart; diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index 6bb214af8a0..554db3ebe4e 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -201,13 +201,15 @@ describe('The video cache', function () { ttl: 25, customCacheKey: customKey1, requestId: '12345abc', - bidder: 'appnexus' + bidder: 'appnexus', + auctionId: '1234-56789-abcde' }, { vastXml: vastXml2, ttl: 25, customCacheKey: customKey2, requestId: 'cba54321', - bidder: 'rubicon' + bidder: 'rubicon', + auctionId: '1234-56789-abcde' }]; store(bids, function () { }); @@ -222,6 +224,7 @@ describe('The video cache', function () { ttlseconds: 25, key: customKey1, bidid: '12345abc', + aid: '1234-56789-abcde', bidder: 'appnexus' }, { type: 'xml', @@ -229,6 +232,7 @@ describe('The video cache', function () { ttlseconds: 25, key: customKey2, bidid: 'cba54321', + aid: '1234-56789-abcde', bidder: 'rubicon' }] }; @@ -254,13 +258,15 @@ describe('The video cache', function () { ttl: 25, customCacheKey: customKey1, requestId: '12345abc', - bidder: 'appnexus' + bidder: 'appnexus', + auctionId: '1234-56789-abcde' }, { vastXml: vastXml2, ttl: 25, customCacheKey: customKey2, requestId: 'cba54321', - bidder: 'rubicon' + bidder: 'rubicon', + auctionId: '1234-56789-abcde' }]; store(bids, function () { }, getMockBidRequest()); @@ -276,6 +282,7 @@ describe('The video cache', function () { key: customKey1, bidid: '12345abc', bidder: 'appnexus', + aid: '1234-56789-abcde', timestamp: 1510852447530 }, { type: 'xml', @@ -284,6 +291,7 @@ describe('The video cache', function () { key: customKey2, bidid: 'cba54321', bidder: 'rubicon', + aid: '1234-56789-abcde', timestamp: 1510852447530 }] }; From 3c5ce3e8841a40ed311a404b46af3ddc36a866b4 Mon Sep 17 00:00:00 2001 From: Chris Huie <3444727+ChrisHuie@users.noreply.github.com> Date: Fri, 14 May 2021 06:37:26 -0700 Subject: [PATCH 0949/1476] Revert "Datablocks bid adapter: update adapter to conform to new bid server's format (#6696)" (#6763) This reverts commit 2b8f888dc11d727f49867309f37e492d65eaee96. --- modules/datablocksAnalyticsAdapter.js | 2 +- modules/datablocksBidAdapter.js | 862 ++++++------------ modules/datablocksBidAdapter.md | 30 +- .../spec/modules/datablocksBidAdapter_spec.js | 601 +++++------- 4 files changed, 507 insertions(+), 988 deletions(-) diff --git a/modules/datablocksAnalyticsAdapter.js b/modules/datablocksAnalyticsAdapter.js index 3e4e9e95a4f..5e977155284 100644 --- a/modules/datablocksAnalyticsAdapter.js +++ b/modules/datablocksAnalyticsAdapter.js @@ -16,4 +16,4 @@ adapterManager.registerAnalyticsAdapter({ code: 'datablocks' }); -export default datablocksAdapter; \ No newline at end of file +export default datablocksAdapter; diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 038a521308d..b00a3eae659 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,634 +1,330 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { ajax } from '../src/ajax.js'; -export const storage = getStorageManager(); - -const NATIVE_ID_MAP = {}; -const NATIVE_PARAMS = { +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +const NATIVE_MAP = { + 'body': 2, + 'body2': 10, + 'price': 6, + 'displayUrl': 11, + 'cta': 12 +}; +const NATIVE_IMAGE = [{ + id: 1, + required: 1, title: { - id: 1, - name: 'title' - }, - icon: { - id: 2, - type: 1, - name: 'img' - }, - image: { - id: 3, - type: 3, - name: 'img' - }, - body: { - id: 4, - name: 'data', - type: 2 - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - }, - cta: { - id: 6, - type: 12, - name: 'data' - }, - body2: { - id: 7, - name: 'data', - type: 10 - }, - rating: { - id: 8, - name: 'data', - type: 3 - }, - likes: { - id: 9, - name: 'data', - type: 4 - }, - downloads: { - id: 10, - name: 'data', - type: 5 - }, - displayUrl: { - id: 11, - name: 'data', + len: 140 + } +}, { + id: 2, + required: 1, + img: { type: 3 } +}, { + id: 3, + required: 1, + data: { type: 11 - }, - price: { - id: 12, - name: 'data', - type: 6 - }, - salePrice: { - id: 13, - name: 'data', - type: 7 - }, - address: { - id: 14, - name: 'data', - type: 9 - }, - phone: { - id: 15, - name: 'data', - type: 8 } -}; +}, { + id: 4, + required: 0, + data: { + type: 2 + } +}, { + id: 5, + required: 0, + img: { type: 1 } +}, { + id: 6, + required: 0, + data: { + type: 12 + } +}]; -Object.keys(NATIVE_PARAMS).forEach((key) => { - NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key; -}); +const VIDEO_PARAMS = ['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']; -// DEFINE THE PREBID BIDDER SPEC export const spec = { - supportedMediaTypes: [BANNER, NATIVE], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], code: 'datablocks', - - // DATABLOCKS SCOPED OBJECT - db_obj: {metrics_host: 'prebid.datablocks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, - - // STORE THE DATABLOCKS BUYERID IN STORAGE - store_dbid: function(dbid) { - let stored = false; - - // CREATE 1 YEAR EXPIRY DATE - let d = new Date(); - d.setTime(Date.now() + (365 * 24 * 60 * 60 * 1000)); - - // TRY TO STORE IN COOKIE - if (storage.cookiesAreEnabled) { - storage.setCookie('_db_dbid', dbid, d.toUTCString(), 'None', null); - stored = true; - } - - // TRY TO STORE IN LOCAL STORAGE - if (storage.localStorageIsEnabled) { - storage.setDataInLocalStorage('_db_dbid', dbid); - stored = true; - } - - return stored; - }, - - // FETCH DATABLOCKS BUYERID FROM STORAGE - get_dbid: function() { - let dbId = ''; - if (storage.cookiesAreEnabled) { - dbId = storage.getCookie('_db_dbid') || ''; - } - - if (!dbId && storage.localStorageIsEnabled) { - dbId = storage.getDataFromLocalStorage('_db_dbid') || ''; - } - return dbId; - }, - - // STORE SYNCS IN STORAGE - store_syncs: function(syncs) { - if (storage.localStorageIsEnabled) { - let syncObj = {}; - syncs.forEach(sync => { - syncObj[sync.id] = sync.uid; - }); - - // FETCH EXISTING SYNCS AND MERGE NEW INTO STORAGE - let storedSyncs = this.get_syncs(); - storage.setDataInLocalStorage('_db_syncs', JSON.stringify(Object.assign(storedSyncs, syncObj))); - - return true; - } - }, - - // GET SYNCS FROM STORAGE - get_syncs: function() { - if (storage.localStorageIsEnabled) { - let syncData = storage.getDataFromLocalStorage('_db_syncs'); - if (syncData) { - return JSON.parse(syncData); - } else { - return {}; - } - } else { - return {}; - } - }, - - // ADD METRIC DATA TO THE METRICS RESPONSE QUEUE - queue_metric: function(metric) { - if (typeof metric === 'object') { - // PUT METRICS IN THE QUEUE - this.db_obj.metrics.push(metric); - - // RESET PREVIOUS TIMER - if (this.db_obj.metrics_timer) { - clearTimeout(this.db_obj.metrics_timer); - } - - // SETUP THE TIMER TO FIRE BACK THE DATA - let scope = this; - this.db_obj.metrics_timer = setTimeout(function() { - scope.send_metrics(); - }, this.db_obj.metrics_queue_time); - - return true; - } else { - return false; - } - }, - - // POST CONSOLIDATED METRICS BACK TO SERVER - send_metrics: function() { - // POST TO SERVER - ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), {method: 'POST', withCredentials: true}); - - // RESET THE QUEUE OF METRIC DATA - this.db_obj.metrics = []; - - return true; - }, - - // GET BASIC CLIENT INFORMATION - get_client_info: function () { - let botTest = new BotClientTests(); - let win = utils.getWindowTop(); - return { - 'wiw': win.innerWidth, - 'wih': win.innerHeight, - 'saw': screen ? screen.availWidth : null, - 'sah': screen ? screen.availHeight : null, - 'scd': screen ? screen.colorDepth : null, - 'sw': screen ? screen.width : null, - 'sh': screen ? screen.height : null, - 'whl': win.history.length, - 'wxo': win.pageXOffset, - 'wyo': win.pageYOffset, - 'wpr': win.devicePixelRatio, - 'is_bot': botTest.doTests(), - 'is_hid': win.document.hidden, - 'vs': win.document.visibilityState - }; - }, - - // LISTEN FOR GPT VIEWABILITY EVENTS - get_viewability: function(bid) { - // ONLY RUN ONCE IF PUBLISHER HAS OPTED IN - if (!this.db_obj.vis_optout && !this.db_obj.vis_run) { - this.db_obj.vis_run = true; - - // ADD GPT EVENT LISTENERS - let scope = this; - if (utils.isGptPubadsDefined()) { - if (typeof window['googletag'].pubads().addEventListener == 'function') { - window['googletag'].pubads().addEventListener('impressionViewable', function(event) { - scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); - }); - window['googletag'].pubads().addEventListener('slotRenderEnded', function(event) { - scope.queue_metric({type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); - }) - } - } - } - }, - - // VALIDATE THE BID REQUEST isBidRequestValid: function(bid) { - // SET GLOBAL VARS FROM BIDDER CONFIG - this.db_obj.source_id = bid.params.source_id; - if (bid.params.vis_optout) { - this.db_obj.vis_optout = true; - } - - return !!(bid.params.source_id && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native)); + return !!(bid.params.host && bid.params.sourceId && + bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); }, + buildRequests: function(validBidRequests, bidderRequest) { + if (!validBidRequests.length) { return []; } - // GENERATE THE RTB REQUEST - buildRequests: function(validRequests, bidderRequest) { - // RETURN EMPTY IF THERE ARE NO VALID REQUESTS - if (!validRequests.length) { - return []; - } + let imps = {}; + let site = {}; + let device = {}; + let refurl = utils.parseUrl(bidderRequest.referrer); + let requests = []; - // CONVERT PREBID NATIVE REQUEST OBJ INTO RTB OBJ - function createNativeRequest(bid) { - const assets = []; - if (bid.nativeParams) { - Object.keys(bid.nativeParams).forEach((key) => { - if (NATIVE_PARAMS[key]) { - const {name, type, id} = NATIVE_PARAMS[key]; - const assetObj = type ? {type} : {}; - let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key]; - if (len) { - assetObj.len = len; - } - if (aRatios && aRatios[0]) { - aRatios = aRatios[0]; - let wmin = aRatios.min_width || 0; - let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; - assetObj.wmin = wmin; - assetObj.hmin = hmin; - } - if (sizes && sizes.length) { - sizes = [].concat(...sizes); - assetObj.w = sizes[0]; - assetObj.h = sizes[1]; - } - const asset = {required: required ? 1 : 0, id}; - asset[name] = assetObj; - assets.push(asset); - } - }); - } - return { - ver: '1.2', - request: { - assets: assets, - context: 1, - plcmttype: 1, - ver: '1.2' - } - } - } - let imps = []; - // ITERATE THE VALID REQUESTS AND GENERATE IMP OBJECT - validRequests.forEach(bidRequest => { - // BUILD THE IMP OBJECT + validBidRequests.forEach(bidRequest => { let imp = { id: bidRequest.bidId, - tagid: bidRequest.params.tagid || bidRequest.adUnitCode, - placement_id: bidRequest.params.placement_id || 0, - secure: window.location.protocol == 'https:', - ortb2: utils.deepAccess(bidRequest, `ortb2Imp`) || {}, - floor: {} + tagid: bidRequest.adUnitCode, + secure: window.location.protocol == 'https:' } - // CHECK FOR FLOORS - if (typeof bidRequest.getFloor === 'function') { - imp.floor = bidRequest.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*' - }); - } - - // BUILD THE SIZES if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { - let sizes = utils.getAdUnitSizes(bidRequest); - if (sizes.length) { + let sizes = bidRequest.mediaTypes.banner.sizes; + if (sizes.length == 1) { imp.banner = { w: sizes[0][0], - h: sizes[0][1], + h: sizes[0][1] + } + } else if (sizes.length > 1) { + imp.banner = { format: sizes.map(size => ({ w: size[0], h: size[1] })) }; + } else { + return; + } + } else if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { + let nativeImp = bidRequest.mediaTypes.native; + + if (nativeImp.type) { + let nativeAssets = []; + switch (nativeImp.type) { + case 'image': + nativeAssets = NATIVE_IMAGE; + break; + default: + return; + } + imp.native = JSON.stringify({ assets: nativeAssets }); + } else { + let nativeAssets = []; + let nativeKeys = Object.keys(nativeImp); + nativeKeys.forEach((nativeKey, index) => { + let required = !!nativeImp[nativeKey].required; + let assetId = index + 1; + switch (nativeKey) { + case 'title': + nativeAssets.push({ + id: assetId, + required: required, + title: { + len: nativeImp[nativeKey].len || 140 + } + }); + break; + case 'body': // desc + case 'body2': // desc2 + case 'price': + case 'display_url': + let data = { + id: assetId, + required: required, + data: { + type: NATIVE_MAP[nativeKey] + } + } + if (nativeImp[nativeKey].data && nativeImp[nativeKey].data.len) { data.data.len = nativeImp[nativeKey].data.len; } + + nativeAssets.push(data); + break; + case 'image': + if (nativeImp[nativeKey].sizes && nativeImp[nativeKey].sizes.length) { + nativeAssets.push({ + id: assetId, + required: required, + image: { + type: 3, + w: nativeImp[nativeKey].sizes[0], + h: nativeImp[nativeKey].sizes[1] + } + }) + } + } + }); + imp.native = { + request: JSON.stringify({native: {assets: nativeAssets}}) + }; + } + } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + let video = bidRequest.mediaTypes.video; + let sizes = video.playerSize || bidRequest.sizes || []; + if (sizes.length && Array.isArray(sizes[0])) { + imp.video = { + w: sizes[0][0], + h: sizes[0][1] + }; + } else if (sizes.length == 2 && !Array.isArray(sizes[0])) { + imp.video = { + w: sizes[0], + h: sizes[1] + }; + } else { + return; + } + + if (video.durationRangeSec) { + if (Array.isArray(video.durationRangeSec)) { + if (video.durationRangeSec.length == 1) { + imp.video.maxduration = video.durationRangeSec[0]; + } else if (video.durationRangeSec.length == 2) { + imp.video.minduration = video.durationRangeSec[0]; + imp.video.maxduration = video.durationRangeSec[1]; + } + } else { + imp.video.maxduration = video.durationRangeSec; + } + } - // ADD TO THE LIST OF IMP REQUESTS - imps.push(imp); + if (bidRequest.params.video) { + Object.keys(bidRequest.params.video).forEach(k => { + if (VIDEO_PARAMS.indexOf(k) > -1) { + imp.video[k] = bidRequest.params.video[k]; + } + }) } - } else if (utils.deepAccess(bidRequest, `mediaTypes.native`)) { - // ADD TO THE LIST OF IMP REQUESTS - imp.native = createNativeRequest(bidRequest); - imps.push(imp); } + let host = bidRequest.params.host; + let sourceId = bidRequest.params.sourceId; + imps[host] = imps[host] || {}; + let hostImp = imps[host][sourceId] = imps[host][sourceId] || { imps: [] }; + hostImp.imps.push(imp); + hostImp.subid = hostImp.imps.subid || bidRequest.params.subid || 'blank'; + hostImp.path = 'search'; + hostImp.idParam = 'sid'; + hostImp.protocol = '//'; }); - // RETURN EMPTY IF THERE WERE NO PROPER ADUNIT REQUESTS TO BE MADE - if (!imps.length) { - return []; - } - - // GENERATE SITE OBJECT - let site = { - domain: window.location.host, - page: bidderRequest.refererInfo.referer, - schain: validRequests[0].schain || {}, - ext: { - p_domain: config.getConfig('publisherDomain'), - rt: bidderRequest.refererInfo.reachedTop, - frames: bidderRequest.refererInfo.numIframes, - stack: bidderRequest.refererInfo.stack, - timeout: config.getConfig('bidderTimeout') - }, - } - - // ADD REF URL IF FOUND + // Generate Site obj + site.domain = refurl.hostname; + site.page = refurl.protocol + '://' + refurl.hostname + refurl.pathname; if (self === top && document.referrer) { site.ref = document.referrer; } - - // ADD META KEYWORDS IF FOUND let keywords = document.getElementsByTagName('meta')['keywords']; if (keywords && keywords.content) { site.keywords = keywords.content; } - // GENERATE DEVICE OBJECT - let device = { - ip: 'peer', - ua: window.navigator.userAgent, - js: 1, - language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', - buyerid: this.get_dbid() || 0, - ext: { - pb_eids: validRequests[0].userIdAsEids || {}, - syncs: this.get_syncs() || {}, - coppa: config.getConfig('coppa') || 0, - gdpr: bidderRequest.gdprConsent || {}, - usp: bidderRequest.uspConsent || {}, - client_info: this.get_client_info(), - ortb2: config.getConfig('ortb2') || {} - } - }; - - let sourceId = validRequests[0].params.source_id || 0; - let host = validRequests[0].params.host || 'prebid.datablocks.net'; - - // RETURN WITH THE REQUEST AND PAYLOAD - return { - method: 'POST', - url: `https://${sourceId}.${host}/openrtb/?sid=${sourceId}`, - data: { - id: bidderRequest.auctionId, - imp: imps, - site: site, - device: device - }, - options: { - withCredentials: true - } - }; - }, - - // INITIATE USER SYNCING - getUserSyncs: function(options, rtbResponse, gdprConsent) { - const syncs = []; - let bidResponse = rtbResponse[0].body; - let scope = this; - - // LISTEN FOR SYNC DATA FROM IFRAME TYPE SYNC - window.addEventListener('message', function (event) { - if (event.data.sentinel && event.data.sentinel === 'dblks_syncData') { - // STORE FOUND SYNCS - if (event.data.syncs) { - scope.store_syncs(event.data.syncs); + // Generate Device obj. + device.ip = 'peer'; + device.ua = window.navigator.userAgent; + device.js = 1; + device.language = ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en'; + + RtbRequest(device, site, imps).forEach(formatted => { + requests.push({ + method: 'POST', + url: formatted.url, + data: formatted.body, + options: { + withCredentials: false } - } + }) }); - - // POPULATE GDPR INFORMATION - let gdprData = { - gdpr: 0, - gdprConsent: '' - } - if (typeof gdprConsent === 'object') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - gdprData.gdpr = Number(gdprConsent.gdprApplies); - gdprData.gdprConsent = gdprConsent.consentString; - } else { - gdprData.gdprConsent = gdprConsent.consentString; - } - } - - // EXTRACT BUYERID COOKIE VALUE FROM BID RESPONSE AND PUT INTO STORAGE - let dbBuyerId = this.get_dbid() || ''; - if (bidResponse.ext && bidResponse.ext.buyerid) { - dbBuyerId = bidResponse.ext.buyerid; - this.store_dbid(dbBuyerId); - } - - // EXTRACT USERSYNCS FROM BID RESPONSE - if (bidResponse.ext && bidResponse.ext.syncs) { - bidResponse.ext.syncs.forEach(sync => { - if (checkValid(sync)) { - syncs.push(addParams(sync)); - } + return requests; + + function RtbRequest(device, site, imps) { + let collection = []; + Object.keys(imps).forEach(host => { + let sourceIds = imps[host]; + Object.keys(sourceIds).forEach(sourceId => { + let impObj = sourceIds[sourceId]; + collection.push({ + url: `https://${host}/${impObj.path}/?${impObj.idParam}=${sourceId}`, + body: { + id: bidderRequest.auctionId, + imp: impObj.imps, + site: Object.assign({ id: impObj.subid || 'blank' }, site), + device: Object.assign({}, device) + } + }) + }) }) - } - - // APPEND PARAMS TO SYNC URL - function addParams(sync) { - // PARSE THE URL - let url = new URL(sync.url); - let urlParams = Object.assign({}, Object.fromEntries(url.searchParams)); - - // APPLY EXTRA VARS - urlParams.gdpr = gdprData.gdpr; - urlParams.gdprConsent = gdprData.gdprConsent; - urlParams.bidid = bidResponse.bidid; - urlParams.id = bidResponse.id; - urlParams.uid = dbBuyerId; - - // REBUILD URL - sync.url = `${url.origin}${url.pathname}?${Object.keys(urlParams).map(key => key + '=' + encodeURIComponent(urlParams[key])).join('&')}`; - // RETURN THE REBUILT URL - return sync; + return collection; } - - // ENSURE THAT THE SYNC TYPE IS VALID AND HAS PERMISSION - function checkValid(sync) { - if (!sync.type || !sync.url) { - return false; - } - switch (sync.type) { - case 'iframe': - return options.iframeEnabled; - case 'image': - return options.pixelEnabled; - default: - return false; - } - } - return syncs; - }, - - // DATABLOCKS WON THE AUCTION - REPORT SUCCESS - onBidWon: function(bid) { - this.queue_metric({type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl}); }, - - // TARGETING HAS BEEN SET - onSetTargeting: function(bid) { - // LISTEN FOR VIEWABILITY EVENTS - this.get_viewability(bid); - }, - - // PARSE THE RTB RESPONSE AND RETURN FINAL RESULTS - interpretResponse: function(rtbResponse, bidRequest) { - // CONVERT NATIVE RTB RESPONSE INTO PREBID RESPONSE - function parseNative(native) { - const {assets, link, imptrackers, jstracker} = native; - const result = { - clickUrl: link.url, - clickTrackers: link.clicktrackers || [], - impressionTrackers: imptrackers || [], - javascriptTrackers: jstracker ? [jstracker] : [] - }; - - (assets || []).forEach((asset) => { - const {id, img, data, title} = asset; - const key = NATIVE_ID_MAP[id]; - if (key) { - if (!utils.isEmpty(title)) { - result.title = title.text - } else if (!utils.isEmpty(img)) { - result[key] = { - url: img.url, - height: img.h, - width: img.w - } - } else if (!utils.isEmpty(data)) { - result[key] = data.value; - } - } - }); - - return result; + interpretResponse: function(serverResponse, bidRequest) { + if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { + return []; } - - let bids = []; - let resBids = utils.deepAccess(rtbResponse, 'body.seatbid') || []; - resBids.forEach(bid => { - let resultItem = {requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360}; - - let mediaType = utils.deepAccess(bid, 'ext.mtype') || ''; - switch (mediaType) { - case 'banner': - bids.push(Object.assign({}, resultItem, {mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm})); - break; - - case 'native': - let nativeResult = JSON.parse(bid.adm); - bids.push(Object.assign({}, resultItem, {mediaType: NATIVE, native: parseNative(nativeResult.native)})); - break; - - default: + let body = serverResponse.body; + + let bids = body.seatbid + .map(seatbid => seatbid.bid) + .reduce((memo, bid) => memo.concat(bid), []); + let req = bidRequest.data; + let reqImps = req.imp; + + return bids.map(rtbBid => { + let imp; + for (let i in reqImps) { + let testImp = reqImps[i] + if (testImp.id == rtbBid.impid) { + imp = testImp; break; - } - }) - - return bids; - } -}; - -// DETECT BOTS -export class BotClientTests { - constructor() { - this.tests = { - headless_chrome: function() { - if (self.navigator) { - if (self.navigator.webdriver) { - return true; - } } + } + let br = { + requestId: rtbBid.impid, + cpm: rtbBid.price, + creativeId: rtbBid.crid, + currency: rtbBid.currency || 'USD', + netRevenue: true, + ttl: 360 + }; + if (!imp) { + return br; + } else if (imp.banner) { + br.mediaType = BANNER; + br.width = rtbBid.w; + br.height = rtbBid.h; + br.ad = rtbBid.adm; + } else if (imp.native) { + br.mediaType = NATIVE; + + let reverseNativeMap = {}; + let nativeKeys = Object.keys(NATIVE_MAP); + nativeKeys.forEach(k => { + reverseNativeMap[NATIVE_MAP[k]] = k; + }); - return false; - }, - user_agent: function() { - try { - var re = new RegExp('(googlebot\/|bot|Googlebot-Mobile|Googlebot-Image|Google favicon|Mediapartners-Google|bingbot|slurp|java|wget|curl|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|dotbot|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|Twitterbot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis)', 'i'); - if (re.test(navigator.userAgent)) { - return true; - } - return false; - } catch (e) { - return false; - } - }, - - selenium: function () { - let response = false; - - if (window && document) { - let results = [ - 'webdriver' in window, - '_Selenium_IDE_Recorder' in window, - 'callSelenium' in window, - '_selenium' in window, - '__webdriver_script_fn' in document, - '__driver_evaluate' in document, - '__webdriver_evaluate' in document, - '__selenium_evaluate' in document, - '__fxdriver_evaluate' in document, - '__driver_unwrapped' in document, - '__webdriver_unwrapped' in document, - '__selenium_unwrapped' in document, - '__fxdriver_unwrapped' in document, - '__webdriver_script_func' in document, - document.documentElement.getAttribute('selenium') !== null, - document.documentElement.getAttribute('webdriver') !== null, - document.documentElement.getAttribute('driver') !== null - ]; - - results.forEach(result => { - if (result === true) { - response = true; - } + let idMap = {}; + let nativeReq = JSON.parse(imp.native.request); + if (nativeReq.native && nativeReq.native.assets) { + nativeReq.native.assets.forEach(asset => { + if (asset.data) { idMap[asset.id] = reverseNativeMap[asset.data.type]; } }) } - return response; - }, - } - } - - doTests() { - let response = false; - for (const [, t] of Object.entries(this.tests)) { - if (t() === true) { - response = true; + const nativeResponse = JSON.parse(rtbBid.adm); + const { assets, link, imptrackers, jstrackers } = nativeResponse.native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined, + impressionTrackers: imptrackers || undefined, + javascriptTrackers: jstrackers ? [jstrackers] : undefined + }; + assets.forEach(asset => { + if (asset.title) { + result.title = asset.title.text; + } else if (asset.img) { + result.image = asset.img.url; + } else if (idMap[asset.id]) { + result[idMap[asset.id]] = asset.data.value; + } + }) + br.native = result; + } else if (imp.video) { + br.mediaType = VIDEO; + br.width = rtbBid.w; + br.height = rtbBid.h; + if (rtbBid.adm) { br.vastXml = rtbBid.adm; } else if (rtbBid.nurl) { br.vastUrl = rtbBid.nurl; } } - } - return response; + return br; + }); } -} -// INIT OUR BIDDER WITH PREBID +}; registerBidder(spec); diff --git a/modules/datablocksBidAdapter.md b/modules/datablocksBidAdapter.md index 2730443d72d..e30cd361974 100644 --- a/modules/datablocksBidAdapter.md +++ b/modules/datablocksBidAdapter.md @@ -8,8 +8,8 @@ Maintainer: support@datablocks.net # Description -Connects to Datablocks Exchange -Banner and Native +Connects to Datablocks Version 5 Platform +Banner Native and Video # Test Parameters @@ -27,13 +27,12 @@ Banner and Native { bidder: 'datablocks', params: { - source_id: 12345, + sourceId: 12345, host: 'prebid.datablocks.net' } } ] - }, - { + }, { code: 'native-div', mediaTypes : { native: { @@ -45,9 +44,28 @@ Banner and Native { bidder: 'datablocks', params: { - source_id: 12345, + sourceId: 12345, host: 'prebid.datablocks.net' } + }, { + code: 'video-div', + mediaTypes : { + video: { + playerSize:[500,400], + durationRangeSec:[15,30], + context: "linear" + } + }, + bids: [ + { + bidder: 'datablocks', + params: { + sourceId: 12345, + host: 'prebid.datablocks.net', + video: { + mimes:["video/flv"] + } + } } ] } diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 147000f2363..18b8aac7371 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -1,15 +1,12 @@ import { expect } from 'chai'; import { spec } from '../../../modules/datablocksBidAdapter.js'; -import { BotClientTests } from '../../../modules/datablocksBidAdapter.js'; -import { getStorageManager } from '../../../src/storageManager.js'; -export let storage = getStorageManager(); -const bid = { +let bid = { bidId: '2dd581a2b6281d', bidder: 'datablocks', bidderRequestId: '145e1d6a7837c9', params: { - source_id: 7560, + sourceId: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -27,12 +24,12 @@ const bid = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe5542e8e1' }; -const bid2 = { +let bid2 = { bidId: '2dd581a2b624324g', bidder: 'datablocks', bidderRequestId: '145e1d6a7837543', params: { - source_id: 7560, + sourceId: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -46,7 +43,7 @@ const bid2 = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe55425432' }; -const nativeBid = { +let nativeBid = { adUnitCode: '/19968336/header-bid-tag-0', auctionId: '160c78a4-f808-410f-b682-d8728f3a79ee', bidId: '332045ee374a99', @@ -81,18 +78,41 @@ const nativeBid = { } }, params: { - source_id: 7560, + sourceId: 7560, host: 'v5demo.datablocks.net' }, transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f6' } +let videoBid = { + adUnitCode: '/19968336/header-bid-tag-0', + auctionId: '160c78a4-f808-410f-b682-d8728f3a79e1', + bidId: '332045ee374b99', + bidder: 'datablocks', + bidderRequestId: '15d9012765e36d', + mediaTypes: { + video: { + context: 'instream', + playerSize: [501, 400], + durationRangeSec: [15, 60] + } + }, + params: { + sourceId: 7560, + host: 'v5demo.datablocks.net', + video: { + minduration: 14 + } + }, + transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f7' +} + const bidderRequest = { auctionId: '8bfef1be-d3ac-4d18-8859-754c7b4cf017', auctionStart: Date.now(), biddeCode: 'datablocks', bidderRequestId: '10c47a5fc3c41', - bids: [bid, bid2, nativeBid], + bids: [bid, bid2, nativeBid, videoBid], refererInfo: { numIframes: 0, reachedTop: true, @@ -103,423 +123,208 @@ const bidderRequest = { timeout: 10000 }; -const res_object = { +let resObject = { body: { - 'id': '10c47a5fc3c41', - 'bidid': '217868445-30021-19053-0', - 'seatbid': [ - { - 'id': '22621593137287', - 'impid': '1', - 'adm': 'John is great', - 'adomain': ['medianet.com'], - 'price': 0.430000, - 'cid': '2524568', - 'adid': '0', - 'crid': '0', - 'cat': [], - 'w': 300, - 'h': 250, - 'ext': { - 'type': 'CPM', - 'mtype': 'banner' - } - }, - { - 'id': '22645215457415', - 'impid': '2', - 'adm': 'john is the best', - 'adomain': ['td.com'], - 'price': 0.580000, - 'cid': '2524574', - 'adid': '0', - 'crid': '0', - 'cat': [], - 'w': 728, - 'h': 90, - 'ext': { - 'type': 'CPM', - 'mtype': 'banner' - } - }, - - { - 'id': '22645215457416', - 'impid': '3', - 'adm': '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"John is amazing"}},{"id":5,"required":1,"data":{"value":"Sponsored by John"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/", "h":"360", "w":"360"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', - 'adomain': ['td.com'], - 'price': 10.00, - 'cid': '2524574', - 'adid': '0', - 'crid': '0', - 'cat': [], - 'ext': { - 'type': 'CPM', - 'mtype': 'native' - } - } - ], - 'cur': 'USD', - 'ext': { - 'version': '1.2.93', - 'buyerid': '1234567', - 'syncs': [ - { - 'type': 'iframe', - 'url': 'https://s.0cf.io' - }, - { - 'type': 'image', - 'url': 'https://us.dblks.net/set_uid/' - } - ] - } + id: '10c47a5fc3c41', + bidid: '166895245-28-11347-1', + seatbid: [{ + seat: '7560', + bid: [{ + id: '1090738570', + impid: '2966b257c81d27', + price: 24.000000, + adm: 'RON', + cid: '55', + adid: '177654', + crid: '177656', + cat: [], + api: [], + w: 300, + h: 250 + }, { + id: '1090738571', + impid: '2966b257c81d28', + price: 24.000000, + adm: 'RON', + cid: '55', + adid: '177654', + crid: '177656', + cat: [], + api: [], + w: 728, + h: 90 + }, { + id: '1090738570', + impid: '15d9012765e36c', + price: 24.000000, + adm: '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"Example Title"}},{"id":2,"required":1,"data":{"value":"Example Body"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', + cid: '132145', + adid: '154321', + crid: '177432', + cat: [], + api: [] + }, { + id: '1090738575', + impid: '15d9012765e36f', + price: 25.000000, + cid: '12345', + adid: '12345', + crid: '123456', + nurl: 'https://click.v5demo.datablocks.net/m//?fcid=435235435432', + cat: [], + api: [], + w: 500, + h: 400 + }] + }], + cur: 'USD', + ext: {} } -} - -let bid_request = { +}; +let bidRequest = { method: 'POST', - url: 'https://prebid.datablocks.net/openrtb/?sid=2523014', + url: 'https://v5demo.datablocks.net/search/?sid=7560', options: { - withCredentials: true + withCredentials: false }, data: { - 'id': 'c09c6e47-8bdb-4884-a46d-93165322b368', - 'imp': [{ - 'id': '1', - 'tagid': '/19968336/header-bid-tag-0', - 'placement_id': 0, - 'secure': true, - 'banner': { - 'w': 300, - 'h': 250, - 'format': [{ - 'w': 300, - 'h': 250 - }, { - 'w': 300, - 'h': 600 - }] - } - }, { - 'id': '2', - 'tagid': '/19968336/header-bid-tag-1', - 'placement_id': 12345, - 'secure': true, - 'banner': { - 'w': 729, - 'h': 90, - 'format': [{ - 'w': 729, - 'h': 90 - }, { - 'w': 970, - 'h': 250 - }] - } - }, { - 'id': '3', - 'tagid': '/19968336/prebid_multiformat_test', - 'placement_id': 0, - 'secure': true, - 'native': { - 'ver': '1.2', - 'request': { - 'assets': [{ - 'required': 1, - 'id': 1, - 'title': {} - }, { - 'required': 1, - 'id': 3, - 'img': { - 'type': 3 - } - }, { - 'required': 1, - 'id': 5, - 'data': { - 'type': 1 - } - }], - 'context': 1, - 'plcmttype': 1, - 'ver': '1.2' - } - } - }], - 'site': { - 'domain': 'test.datablocks.net', - 'page': 'https://test.datablocks.net/index.html', - 'schain': {}, - 'ext': { - 'p_domain': 'https://test.datablocks.net', - 'rt': true, - 'frames': 0, - 'stack': ['https://test.datablocks.net/index.html'], - 'timeout': 3000 - }, - 'keywords': 'HTML, CSS, JavaScript' - }, - 'device': { - 'ip': 'peer', - 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36', - 'js': 1, - 'language': 'en', - 'buyerid': '1234567', - 'ext': { - 'pb_eids': [{ - 'source': 'criteo.com', - 'uids': [{ - 'id': 'test', - 'atype': 1 - }] - }], - 'syncs': { - '1000': 'db_4044853', - '1001': true - }, - 'coppa': 0, - 'gdpr': {}, - 'usp': {}, - 'client_info': { - 'wiw': 2560, - 'wih': 1281, - 'saw': 2560, - 'sah': 1417, - 'scd': 24, - 'sw': 2560, - 'sh': 1440, - 'whl': 4, - 'wxo': 0, - 'wyo': 0, - 'wpr': 2, - 'is_bot': false, - 'is_hid': false, - 'vs': 'hidden' - }, - 'fpd': {} - } - } + device: { + ip: 'peer', + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) Ap…ML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', + js: 1, + language: 'en' + }, + id: '10c47a5fc3c41', + imp: [{ + banner: { w: 300, h: 250 }, + id: '2966b257c81d27', + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + banner: { w: 728, h: 90 }, + id: '2966b257c81d28', + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + id: '15d9012765e36c', + native: {request: '{"native":{"assets":[{"id":"1","required":true,"title":{"len":140}},{"id":"2","required":true,"data":{"type":2}},{"id":"3","img":{"w":728,"h":90,"type":3}}]}}'}, + secure: false, + tagid: '/19968336/header-bid-tag-0' + }, { + id: '15d9012765e36f', + video: {w: 500, h: 400, minduration: 15, maxduration: 60}, + secure: false, + tagid: '/19968336/header-bid-tag-0' + }], + site: { + domain: '', + id: 'blank', + page: 'https://v5demo.datablocks.net/test' + } } } describe('DatablocksAdapter', function() { - describe('All needed functions are available', function() { - it(`isBidRequestValid is present and type function`, function () { - expect(spec.isBidRequestValid).to.exist.and.to.be.a('function') - }); - - it(`buildRequests is present and type function`, function () { - expect(spec.buildRequests).to.exist.and.to.be.a('function') - }); - - it(`getUserSyncs is present and type function`, function () { - expect(spec.getUserSyncs).to.exist.and.to.be.a('function') - }); - - it(`onBidWon is present and type function`, function () { - expect(spec.onBidWon).to.exist.and.to.be.a('function') - }); - - it(`onSetTargeting is present and type function`, function () { - expect(spec.onSetTargeting).to.exist.and.to.be.a('function') - }); - - it(`interpretResponse is present and type function`, function () { - expect(spec.interpretResponse).to.exist.and.to.be.a('function') - }); - - it(`store_dbid is present and type function`, function () { - expect(spec.store_dbid).to.exist.and.to.be.a('function') - }); - - it(`get_dbid is present and type function`, function () { - expect(spec.get_dbid).to.exist.and.to.be.a('function') - }); - - it(`store_syncs is present and type function`, function () { - expect(spec.store_syncs).to.exist.and.to.be.a('function') - }); - - it(`get_syncs is present and type function`, function () { - expect(spec.get_syncs).to.exist.and.to.be.a('function') - }); - - it(`queue_metric is present and type function`, function () { - expect(spec.queue_metric).to.exist.and.to.be.a('function') - }); - - it(`send_metrics is present and type function`, function () { - expect(spec.send_metrics).to.exist.and.to.be.a('function') - }); - - it(`get_client_info is present and type function`, function () { - expect(spec.get_client_info).to.exist.and.to.be.a('function') - }); - - it(`get_viewability is present and type function`, function () { - expect(spec.get_viewability).to.exist.and.to.be.a('function') - }); - }); - - describe('get / store dbid', function() { - it('Should return true / undefined', function() { - expect(spec.store_dbid('12345')).to.be.true; - expect(spec.get_dbid()).to.be.a('string'); - }); - }) - - describe('get / store syncs', function() { - it('Should return true / array', function() { - expect(spec.store_syncs([{id: 1, uid: 'test'}])).to.be.true; - expect(spec.get_syncs()).to.be.a('object'); - }); - }) - - describe('queue / send metrics', function() { - it('Should return true', function() { - expect(spec.queue_metric({type: 'test'})).to.be.true; - expect(spec.queue_metric('string')).to.be.false; - expect(spec.send_metrics()).to.be.true; - }); - }) - - describe('get_viewability', function() { - it('Should return undefined', function() { - expect(spec.get_viewability()).to.equal(undefined); - }); - }) - - describe('get client info', function() { - it('Should return object', function() { - let client_info = spec.get_client_info() - expect(client_info).to.be.a('object'); - expect(client_info).to.have.all.keys('wiw', 'wih', 'saw', 'sah', 'scd', 'sw', 'sh', 'whl', 'wxo', 'wyo', 'wpr', 'is_bot', 'is_hid', 'vs'); - }); - - it('bot test should return boolean', function() { - let bot_test = new BotClientTests(); - expect(bot_test.doTests()).to.be.a('boolean'); - }); - }) - describe('isBidRequestValid', function() { - it('Should return true when source_id and Host are set', function() { + it('Should return true when sourceId and Host are set', function() { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when host/source_id is not set', function() { + it('Should return false when host/sourceId is not set', function() { let moddedBid = Object.assign({}, bid); - delete moddedBid.params.source_id; - expect(spec.isBidRequestValid(moddedBid)).to.be.false; - }); - - it('Should return true when viewability reporting is opted out', function() { - let moddedBid = Object.assign({}, bid); - moddedBid.params.vis_optout = true; - spec.isBidRequestValid(moddedBid); - expect(spec.db_obj.vis_optout).to.be.true; - }); - }) - - describe('getUserSyncs', function() { - it('Should return array of syncs', function() { - expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [res_object], {gdprApplies: true, gdpr: 1, gdpr_consent: 'consent_string'}, {})).to.an('array'); - }); - }); - - describe('onSetTargeting', function() { - it('Should return undefined', function() { - expect(spec.onSetTargeting()).to.equal(undefined); - }); - }); - - describe('onBidWon', function() { - it('Should return undefined', function() { - let won_bid = {params: [{source_id: 1}], requestId: 1, adUnitCode: 'unit', auctionId: 1, size: '300x250', cpm: 10, adserverTargeting: {hb_pb: 10}, timeToRespond: 10, ttl: 10}; - expect(spec.onBidWon(won_bid)).to.equal(undefined); + delete moddedBid.params.sourceId; + delete moddedBid.params.host; + expect(spec.isBidRequestValid(bid)).to.be.false; }); }); describe('buildRequests', function() { - let request = spec.buildRequests([bid, bid2, nativeBid], bidderRequest); - - expect(request).to.exist; - it('Returns POST method', function() { - expect(request.method).to.exist; - expect(request.method).to.equal('POST'); - }); - - it('Returns valid URL', function() { - expect(request.url).to.exist; - expect(request.url).to.equal('https://7560.v5demo.datablocks.net/openrtb/?sid=7560'); - }); - + let requests = spec.buildRequests([bid, bid2, nativeBid, videoBid], bidderRequest); it('Creates an array of request objects', function() { - expect(request.data.imp).to.be.an('array').that.is.not.empty; + expect(requests).to.be.an('array').that.is.not.empty; }); - it('Should be a valid openRTB request', function() { - let data = request.data; - - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); - expect(data.id).to.be.a('string'); - expect(data.imp).to.be.a('array'); - expect(data.device.ip).to.equal('peer'); - - let imps = data['imp']; - imps.forEach((imp, index) => { - let curBid = bidderRequest.bids[index]; - if (imp.banner) { - expect(imp.banner).to.be.a('object'); - expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); - } else if (imp.native) { - expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); - expect(imp.native).to.have.all.keys('request', 'ver'); - expect(imp.native.request).to.be.a('object'); - } else { - expect(true).to.equal(false); - } + requests.forEach(request => { + expect(request).to.exist; + it('Returns POST method', function() { + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + }); + it('Returns valid URL', function() { + expect(request.url).to.exist; + expect(request.url).to.equal('https://v5demo.datablocks.net/search/?sid=7560'); + }); - expect(imp.id).to.be.a('string'); - expect(imp.id).to.equal(curBid.bidId); - expect(imp.tagid).to.be.a('string'); - expect(imp.tagid).to.equal(curBid.adUnitCode); - expect(imp.secure).to.equal(false); - }) - }); + it('Should be a valid openRTB request', function() { + let data = request.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); + expect(data.id).to.be.a('string'); + + let imps = data['imp']; + imps.forEach((imp, index) => { + let curBid = bidderRequest.bids[index]; + if (imp.banner) { + expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid'); + expect(imp.banner).to.be.a('object'); + } else if (imp.native) { + expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid'); + expect(imp.native).to.have.all.keys('request'); + expect(imp.native.request).to.be.a('string'); + let native = JSON.parse(imp.native.request); + expect(native).to.be.a('object'); + } else if (imp.video) { + expect(imp).to.have.all.keys('video', 'id', 'secure', 'tagid'); + expect(imp.video).to.have.all.keys('w', 'h', 'minduration', 'maxduration') + } else { + expect(true).to.equal(false); + } + + expect(imp.id).to.be.a('string'); + expect(imp.id).to.equal(curBid.bidId); + expect(imp.tagid).to.be.a('string'); + expect(imp.tagid).to.equal(curBid.adUnitCode); + expect(imp.secure).to.equal(false); + }) + + expect(data.device.ip).to.equal('peer'); + }); + }) it('Returns empty data if no valid requests are passed', function() { - let test_request = spec.buildRequests([]); - expect(test_request).to.be.an('array').that.is.empty; + let request = spec.buildRequests([]); + expect(request).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', function() { - let response = spec.interpretResponse(res_object, bid_request); - + let serverResponses = spec.interpretResponse(resObject, bidRequest); it('Returns an array of valid server responses if response object is valid', function() { - expect(response).to.be.an('array').that.is.not.empty; - - response.forEach(bid => { - expect(parseInt(bid.requestId)).to.be.a('number').greaterThan(0); - expect(bid.cpm).to.be.a('number'); - expect(bid.creativeId).to.be.a('string'); - expect(bid.currency).to.be.a('string'); - expect(bid.netRevenue).to.be.a('boolean'); - expect(bid.ttl).to.be.a('number'); - expect(bid.mediaType).to.be.a('string'); - - if (bid.mediaType == 'banner') { - expect(bid.width).to.be.a('number'); - expect(bid.height).to.be.a('number'); - expect(bid.ad).to.be.a('string'); - } else if (bid.mediaType == 'native') { - expect(bid.native).to.be.a('object'); + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(Object.keys(dataItem)).to.include('cpm', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType', 'requestId'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + + if (dataItem.mediaType == 'banner') { + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + } else if (dataItem.mediaType == 'native') { + expect(dataItem.native.title).to.be.a('string'); + expect(dataItem.native.body).to.be.a('string'); + expect(dataItem.native.clickUrl).to.be.a('string'); + } else if (dataItem.mediaType == 'video') { + expect(dataItem.vastUrl).to.be.a('string'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); } - }) - + } it('Returns an empty array if invalid response is passed', function() { serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; From 4e61f9d6f15a2af7577498b65ef074e7ef21da11 Mon Sep 17 00:00:00 2001 From: Max Duval Date: Fri, 14 May 2021 15:26:13 +0100 Subject: [PATCH 0950/1476] clarify dependency on global rtdModule (#6759) As discussed with David from Permutive --- modules/permutiveRtdProvider.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/permutiveRtdProvider.md b/modules/permutiveRtdProvider.md index fe8c34c1b5c..3738c6e8be7 100644 --- a/modules/permutiveRtdProvider.md +++ b/modules/permutiveRtdProvider.md @@ -4,8 +4,11 @@ This submodule reads segments from Permutive and attaches them as targeting keys ## Usage Compile the Permutive RTD module into your Prebid build: ``` -gulp build --modules=permutiveRtdProvider +gulp build --modules=rtdModule,permutiveRtdProvider ``` + +> Note that the global RTD module, `rtdModule`, is a prerequisite of the Permutive RTD module. + You then need to enable the Permutive RTD in your Prebid configuration, using the below format: ```javascript From e533e1bbc8fa5c7fbc051b67c35c8aa488d89506 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Fri, 14 May 2021 11:06:40 -0400 Subject: [PATCH 0951/1476] Update to PBS bid adapter for video bids, pass along w and h respectively and filter out contxt and playerSize params as PBS does not use them (#6682) --- modules/prebidServerBidAdapter/index.js | 12 ++++++++++- .../modules/prebidServerBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index d91858ed9b2..616cec47ff1 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -601,7 +601,17 @@ const OPEN_RTB_PROTOCOL = { if (videoParams.context === 'instream' && !videoParams.hasOwnProperty('placement')) { videoParams.placement = 1; } - mediaTypes['video'] = videoParams; + + mediaTypes['video'] = Object.keys(videoParams).filter(param => param !== 'context') + .reduce((result, param) => { + if (param === 'playerSize') { + result.w = utils.deepAccess(videoParams, `${param}.0.0`); + result.h = utils.deepAccess(videoParams, `${param}.0.1`); + } else { + result[param] = videoParams[param]; + } + return result; + }, {}); } } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index a6afed8ba3e..147ade6c77b 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -530,6 +530,26 @@ describe('S2S Adapter', function () { expect(requestBid.imp[0].video.placement).to.equal(1); }); + it('converts video mediaType properties into openRTB format', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint.p1Consent = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + config.setConfig({ s2sConfig: ortb2Config }); + + let videoBid = utils.deepClone(VIDEO_REQUEST); + videoBid.ad_units[0].mediaTypes.video.context = 'instream'; + adapter.callBids(videoBid, BID_REQUESTS, addBidResponse, done, ajax); + + const requestBid = JSON.parse(server.requests[0].requestBody); + expect(requestBid.imp[0].banner).to.not.exist; + expect(requestBid.imp[0].video).to.exist; + expect(requestBid.imp[0].video.placement).to.equal(1); + expect(requestBid.imp[0].video.w).to.equal(640); + expect(requestBid.imp[0].video.h).to.equal(480); + expect(requestBid.imp[0].video.playerSize).to.be.undefined; + expect(requestBid.imp[0].video.context).to.be.undefined; + }); + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); From ea6d7d6c68e4c2906fb780ce0327fcdd3f2a93c0 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 14 May 2021 13:28:32 -0400 Subject: [PATCH 0952/1476] Sovrn and Sharethrough Bid Adapters: support for advertiserDomains (#6764) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Delete iasBidAdapter.js * Add files via upload * Update openxBidAdapter.js * Update openxBidAdapter.js * Update sovrnBidAdapter.js * Update sharethroughBidAdapter.js * Update sharethroughBidAdapter_spec.js * Update sovrnBidAdapter_spec.js * Update sovrnBidAdapter_spec.js * Update sharethroughBidAdapter_spec.js * Update sharethroughBidAdapter_spec.js * Update sharethroughBidAdapter_spec.js --- modules/sharethroughBidAdapter.js | 1 + modules/sovrnBidAdapter.js | 3 ++- test/spec/modules/sharethroughBidAdapter_spec.js | 5 +++-- test/spec/modules/sovrnBidAdapter_spec.js | 12 ++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index eef18288b17..68ccde0da46 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -109,6 +109,7 @@ export const sharethroughAdapterSpec = { currency: 'USD', netRevenue: true, ttl: 360, + meta: { advertiserDomains: creative.creative && creative.creative.adomain ? creative.creative.adomain : [] }, ad: generateAd(body, req) }]; }, diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 708cb110dbd..cba90e7d434 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -160,7 +160,8 @@ export const spec = { netRevenue: true, mediaType: BANNER, ad: decodeURIComponent(`${sovrnBid.adm}`), - ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90 + ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90, + meta: { advertiserDomains: sovrnBid && sovrnBid.adomain ? sovrnBid.adomain : [] } }); }); } diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index f741f985f9f..5c8e01536dd 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -473,7 +473,7 @@ describe('sharethrough adapter spec', function() { describe('.interpretResponse', function() { it('returns a correctly parsed out response', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.include( + expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.deep.include( { width: 1, height: 1, @@ -482,7 +482,8 @@ describe('sharethrough adapter spec', function() { dealId: 'aDealId', currency: 'USD', netRevenue: true, - ttl: 360 + ttl: 360, + meta: { advertiserDomains: [] } }); }); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 1050fdf4856..729c48c28f4 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -387,7 +387,8 @@ describe('sovrnBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': decodeURIComponent(`>`), - 'ttl': 60000 + 'ttl': 60000, + 'meta': { advertiserDomains: [] } }]; let result = spec.interpretResponse(response); @@ -407,7 +408,8 @@ describe('sovrnBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': decodeURIComponent(``), - 'ttl': 90 + 'ttl': 90, + 'meta': { advertiserDomains: [] } }]; let result = spec.interpretResponse(response); @@ -428,7 +430,8 @@ describe('sovrnBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': decodeURIComponent(``), - 'ttl': 90 + 'ttl': 90, + 'meta': { advertiserDomains: [] } }]; let result = spec.interpretResponse(response); @@ -449,7 +452,8 @@ describe('sovrnBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': decodeURIComponent(``), - 'ttl': 480 + 'ttl': 480, + 'meta': { advertiserDomains: [] } }]; let result = spec.interpretResponse(response); From 3d376682fefe470da71c80f9822026b4a7d7111e Mon Sep 17 00:00:00 2001 From: jackhsiehucf <77815341+jackhsiehucf@users.noreply.github.com> Date: Mon, 17 May 2021 19:04:42 +0800 Subject: [PATCH 0953/1476] ucfunnel Bid Adapter: add support for FLoC and Verizon Media ConnectID (#6744) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel fix error message in debug mode * ucfunnel adapter add bidfloor parameter * ucfunnel adapter support CCPA * ucfunnel adapter native support clicktrackers * ucfunnel adapter change cookie sync setting * ucfunnel adapter update request parameter * Update ucfunnelBidAdapter * ucfunnel adapter add currency in ad response * ucfunnel adapter support uid2 * ucfunnel Bid Adapter: add support for FLoC and Verizon Media ConnectID Co-authored-by: root Co-authored-by: Ryan Chou Co-authored-by: ucfunnel Co-authored-by: jack.hsieh --- modules/ucfunnelBidAdapter.js | 17 ++++++++++++++++- test/spec/modules/ucfunnelBidAdapter_spec.js | 7 +++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 9b9134a8ef0..734aba97789 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -300,6 +300,7 @@ function getRequestData(bid, bidderRequest) { } function addUserId(bidData, userId) { + bidData['eids'] = ''; utils._each(userId, (userIdObjectOrValue, userIdProviderKey) => { switch (userIdProviderKey) { case 'sharedid': @@ -333,7 +334,21 @@ function addUserId(bidData, userId) { break; case 'uid2': if (userIdObjectOrValue.id) { - bidData['eids'] = userIdProviderKey + ',' + userIdObjectOrValue.id + bidData['eids'] = (bidData['eids'].length > 0) + ? (bidData['eids'] + '!' + userIdProviderKey + ',' + userIdObjectOrValue.id) + : (userIdProviderKey + ',' + userIdObjectOrValue.id); + } + break; + case 'connectid': + if (userIdObjectOrValue) { + bidData['eids'] = (bidData['eids'].length > 0) + ? (bidData['eids'] + '!verizonMediaId,' + userIdObjectOrValue) + : ('verizonMediaId,' + userIdObjectOrValue); + } + break; + case 'flocId': + if (userIdObjectOrValue.id) { + bidData['cid'] = userIdObjectOrValue.id; } break; default: diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index d7e82338ff3..bee420f40d4 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -17,7 +17,9 @@ const userId = { 'sharedid': {'id': '01ESHXW4HD29KMF387T63JQ9H5', 'third': '01ESHXW4HD29KMF387T63JQ9H5'}, 'tdid': 'D6885E90-2A7A-4E0F-87CB-7734ED1B99A3', 'haloId': {}, - 'uid2': {'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888'} + 'uid2': {'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888'}, + 'flocId': {'id': '12144', 'version': 'chrome.1.1'}, + 'connectid': '4567' } const validBannerBidReq = { @@ -159,7 +161,8 @@ describe('ucfunnel Adapter', function () { expect(data.adid).to.equal('ad-34BBD2AA24B678BBFD4E7B9EE3B872D'); expect(data.w).to.equal(width); expect(data.h).to.equal(height); - expect(data.eids).to.equal('uid2,eb33b0cb-8d35-4722-b9c0-1a31d4064888'); + expect(data.eids).to.equal('uid2,eb33b0cb-8d35-4722-b9c0-1a31d4064888!verizonMediaId,4567'); + expect(data.cid).to.equal('12144'); expect(data.schain).to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'); }); From 9c0a4bf4f72512542423799afe028470548d3c06 Mon Sep 17 00:00:00 2001 From: Max Duval Date: Mon, 17 May 2021 16:31:56 +0100 Subject: [PATCH 0954/1476] Permutive Rtd Submodule: register submodule in submodules list (#6768) --- modules/.submodules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/.submodules.json b/modules/.submodules.json index a8804321278..0a10044a78e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,10 +36,11 @@ ], "rtdModule": [ "browsiRtdProvider", + "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", + "permutiveRtdProvider", "reconciliationRtdProvider", - "geoedgeRtdProvider", "sirdataRtdProvider" ], "fpdModule": [ From eaad22d72fe6cea5301e253c25aa7a5ee1046acf Mon Sep 17 00:00:00 2001 From: John Salis Date: Mon, 17 May 2021 14:04:48 -0400 Subject: [PATCH 0955/1476] Beachfront Bid Adapter: update beachfront example docs (#6774) Co-authored-by: John Salis --- modules/beachfrontBidAdapter.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/modules/beachfrontBidAdapter.md b/modules/beachfrontBidAdapter.md index a2eb79ee331..9de415f8fc5 100644 --- a/modules/beachfrontBidAdapter.md +++ b/modules/beachfrontBidAdapter.md @@ -18,7 +18,8 @@ Module that connects to Beachfront's demand sources mediaTypes: { video: { context: 'instream', - playerSize: [ 640, 360 ] + playerSize: [640, 360], + mimes: ['video/mp4', 'application/javascript'] } }, bids: [ @@ -26,10 +27,7 @@ Module that connects to Beachfront's demand sources bidder: 'beachfront', params: { bidfloor: 0.01, - appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76', - video: { - mimes: [ 'video/mp4', 'application/javascript' ] - } + appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76' } } ] @@ -37,7 +35,7 @@ Module that connects to Beachfront's demand sources code: 'test-banner', mediaTypes: { banner: { - sizes: [ 300, 250 ] + sizes: [300, 250] } }, bids: [ @@ -61,10 +59,11 @@ Module that connects to Beachfront's demand sources mediaTypes: { video: { context: 'outstream', - playerSize: [ 640, 360 ] + playerSize: [640, 360], + mimes: ['video/mp4', 'application/javascript'] }, banner: { - sizes: [ 300, 250 ] + sizes: [300, 250] } }, bids: [ @@ -74,7 +73,6 @@ Module that connects to Beachfront's demand sources video: { bidfloor: 0.01, appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76', - mimes: [ 'video/mp4', 'application/javascript' ] }, banner: { bidfloor: 0.01, @@ -95,7 +93,8 @@ Module that connects to Beachfront's demand sources mediaTypes: { video: { context: 'outstream', - playerSize: [ 640, 360 ] + playerSize: [ 640, 360 ], + mimes: ['video/mp4', 'application/javascript'] } }, bids: [ @@ -104,8 +103,7 @@ Module that connects to Beachfront's demand sources params: { video: { bidfloor: 0.01, - appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76', - mimes: [ 'video/mp4', 'application/javascript' ] + appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76' }, player: { progressColor: '#50A8FA', From 59313368e39276193963f19f702e2d79761ee1d2 Mon Sep 17 00:00:00 2001 From: hdwmsconfig Date: Mon, 17 May 2021 15:29:54 -0300 Subject: [PATCH 0956/1476] Eplanning Bid Adapter: Adjust endpoint parameter formatting (#6772) --- modules/eplanningBidAdapter.js | 3 +-- test/spec/modules/eplanningBidAdapter_spec.js | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index d62d1ee46e8..98a0e290575 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -58,7 +58,6 @@ export const spec = { rnd: rnd, e: spaces.str, ur: pageUrl || FILE, - r: 'pbjs', pbv: '$prebid.version$', ncb: '1', vs: spaces.vs @@ -85,7 +84,7 @@ export const spec = { } const userIds = (getGlobal()).getUserIds(); for (var id in userIds) { - params[id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); + params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); } } diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 2ab26cfe57d..1d3a8344170 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -324,11 +324,6 @@ describe('E-Planning Adapter', function () { expect(method).to.equal('GET'); }); - it('should return r parameter with value pbjs', function () { - const r = spec.buildRequests(bidRequests, bidderRequest).data.r; - expect(r).to.equal('pbjs'); - }); - it('should return pbv parameter with value prebid version', function () { const pbv = spec.buildRequests(bidRequests, bidderRequest).data.pbv; expect(pbv).to.equal('$prebid.version$'); @@ -922,9 +917,9 @@ describe('E-Planning Adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const dataRequest = request.data; - expect('D6885E90-2A7A-4E0F-87CB-7734ED1B99A3').to.equal(dataRequest.tdid); - expect('c29cb2ae-769d-42f6-891a-f53cadee823d').to.equal(dataRequest.pubcid); - expect(expected_id5id).to.equal(dataRequest.id5id); + expect('D6885E90-2A7A-4E0F-87CB-7734ED1B99A3').to.equal(dataRequest.e_tdid); + expect('c29cb2ae-769d-42f6-891a-f53cadee823d').to.equal(dataRequest.e_pubcid); + expect(expected_id5id).to.equal(dataRequest.e_id5id); }); }); }); From a9232fdb8530d2480fec22a900e300755d486d4f Mon Sep 17 00:00:00 2001 From: John Salis Date: Mon, 17 May 2021 14:35:27 -0400 Subject: [PATCH 0957/1476] Beachfront Bid Adapter: add Unified ID 2.0 support (#6770) --- modules/beachfrontBidAdapter.js | 7 +++-- .../spec/modules/beachfrontBidAdapter_spec.js | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 8ddc0ca5ba9..7466b3d6a68 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -19,7 +19,8 @@ export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; export const SUPPORTED_USER_IDS = [ { key: 'tdid', source: 'adserver.org', rtiPartner: 'TDID', queryParam: 'tdid' }, - { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' } + { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' }, + { key: 'uid2.id', source: 'uidapi.com', rtiPartner: 'UID2', queryParam: 'uid2' } ]; let appId = ''; @@ -279,7 +280,7 @@ function getEids(bid) { function getUserId(bid) { return ({ key, source, rtiPartner }) => { - let id = bid.userId && bid.userId[key]; + let id = utils.deepAccess(bid, `userId.${key}`); return id ? formatEid(id, source, rtiPartner) : null; }; } @@ -428,7 +429,7 @@ function createBannerRequestData(bids, bidderRequest) { } SUPPORTED_USER_IDS.forEach(({ key, queryParam }) => { - let id = bids[0] && bids[0].userId && bids[0].userId[key]; + let id = utils.deepAccess(bids, `0.userId.${key}`) if (id) { payload[queryParam] = id; } diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index c7ae5c799ac..605ccc464cb 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -333,6 +333,24 @@ describe('BeachfrontAdapter', function () { }] }); }); + + it('must add the Unified ID 2.0 to the request', () => { + const uid2 = { id: '4321' }; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.userId = { uid2 }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.user.ext.eids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: uid2.id, + ext: { + rtiPartner: 'UID2' + } + }] + }); + }); }); describe('for banner bids', function () { @@ -506,6 +524,16 @@ describe('BeachfrontAdapter', function () { const data = requests[0].data; expect(data.idl).to.equal(idl_env); }); + + it('must add the Unified ID 2.0 to the request', () => { + const uid2 = { id: '4321' }; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.userId = { uid2 }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.uid2).to.equal(uid2.id); + }); }); describe('for multi-format bids', function () { From 5aec9205e7995774a15938c41e6523d2b65cb426 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Mon, 17 May 2021 14:51:06 -0400 Subject: [PATCH 0958/1476] appnexus bid adapter - create stub for meta.advertiserDomains (#6753) --- modules/appnexusBidAdapter.js | 5 +++++ test/spec/modules/appnexusBidAdapter_spec.js | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index f3c53e6fcb3..d28bf391aa5 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -578,6 +578,11 @@ function newBid(serverBid, rtbBid, bidderRequest) { } }; + // WE DON'T FULLY SUPPORT THIS ATM - future spot for adomain code; creating a stub for 5.0 compliance + if (rtbBid.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [] }); + } + if (rtbBid.advertiser_id) { bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); } diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 919b36ff71a..c875cba12bc 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1280,6 +1280,21 @@ describe('AppNexusAdapter', function () { } let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); - }) + }); + + it('should add advertiserDomains', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].adomain = ['123']; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); + expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + }); }); }); From 4d90d7ad6813dbb3981323455f9e01c994679e09 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Mon, 17 May 2021 13:49:06 -0700 Subject: [PATCH 0959/1476] Conversant adapter - picks up additional params from mediaTypes.video (#6775) --- modules/conversantBidAdapter.js | 7 ++++--- modules/conversantBidAdapter.md | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 74cd97ad019..806f276fb72 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -33,10 +33,11 @@ export const spec = { } if (isVideoRequest(bid)) { - if (!bid.params.mimes) { + const mimes = bid.params.mimes || utils.deepAccess(bid, 'mediaTypes.video.mimes'); + if (!mimes) { // Give a warning but let it pass utils.logWarn(BIDDER_CODE + ': mimes should be specified for videos'); - } else if (!utils.isArray(bid.params.mimes) || !bid.params.mimes.every(s => utils.isStr(s))) { + } else if (!utils.isArray(mimes) || !mimes.every(s => utils.isStr(s))) { utils.logWarn(BIDDER_CODE + ': mimes must be an array of strings'); return false; } @@ -90,7 +91,7 @@ export const spec = { copyOptProperty(bid.params.position, video, 'pos'); copyOptProperty(bid.params.mimes || videoData.mimes, video, 'mimes'); - copyOptProperty(bid.params.maxduration, video, 'maxduration'); + copyOptProperty(bid.params.maxduration || videoData.maxduration, video, 'maxduration'); copyOptProperty(bid.params.protocols || videoData.protocols, video, 'protocols'); copyOptProperty(bid.params.api || videoData.api, video, 'api'); diff --git a/modules/conversantBidAdapter.md b/modules/conversantBidAdapter.md index fba793adad2..07d9abf918b 100644 --- a/modules/conversantBidAdapter.md +++ b/modules/conversantBidAdapter.md @@ -29,17 +29,17 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [640, 480] + playerSize: [640, 480], + api: [2], + protocols: [1, 2], + mimes: ['video/mp4'] } }, bids: [{ bidder: "conversant", params: { site_id: '108060', - api: [2], - protocols: [1, 2], - white_label_url: 'https://web.hb.ad.cpe.dotomi.com/s2s/header/24', - mimes: ['video/mp4'] + white_label_url: 'https://web.hb.ad.cpe.dotomi.com/s2s/header/24' } }] }]; From d2195cad1fbef8bea600ea02f2975c4df1072105 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Tue, 18 May 2021 17:40:12 +0900 Subject: [PATCH 0960/1476] Relaido Adapter : size of the banner is checked and the banner can be bid on. (#6776) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * take size each mediaType Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- modules/relaidoBidAdapter.js | 70 ++++++++++----------- test/spec/modules/relaidoBidAdapter_spec.js | 35 ++++++++++- 2 files changed, 67 insertions(+), 38 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index c77afbe6ec5..92709b7c047 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.2'; +const ADAPTER_VERSION = '1.0.3'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -17,21 +17,17 @@ function isBidRequestValid(bid) { utils.logWarn('placementId param is reqeuired.'); return false; } - if (hasVideoMediaType(bid)) { - if (!isVideoValid(bid)) { - utils.logWarn('Invalid mediaType video.'); - return false; - } - } else if (hasBannerMediaType(bid)) { - if (!isBannerValid(bid)) { - utils.logWarn('Invalid mediaType banner.'); - return false; - } + if (hasVideoMediaType(bid) && isVideoValid(bid)) { + return true; } else { - utils.logWarn('Invalid mediaTypes input banner or video.'); - return false; + utils.logWarn('Invalid mediaType video.'); } - return true; + if (hasBannerMediaType(bid) && isBannerValid(bid)) { + return true; + } else { + utils.logWarn('Invalid mediaType banner.'); + } + return false; } function buildRequests(validBidRequests, bidderRequest) { @@ -43,7 +39,21 @@ function buildRequests(validBidRequests, bidderRequest) { const bidDomain = bidRequest.params.domain || BIDDER_DOMAIN; const bidUrl = `https://${bidDomain}/bid/v1/prebid/${placementId}`; const uuid = getUuid(); - const mediaType = getMediaType(bidRequest); + let mediaType = ''; + let width = 0; + let height = 0; + + if (hasVideoMediaType(bidRequest) && isVideoValid(bidRequest)) { + const playerSize = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize')); + width = playerSize[0][0]; + height = playerSize[0][1]; + mediaType = VIDEO; + } else if (hasBannerMediaType(bidRequest) && isBannerValid(bidRequest)) { + const sizes = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')); + width = sizes[0][0]; + height = sizes[0][1]; + mediaType = BANNER; + } let payload = { version: ADAPTER_VERSION, @@ -57,18 +67,10 @@ function buildRequests(validBidRequests, bidderRequest) { transaction_id: bidRequest.transactionId, media_type: mediaType, uuid: uuid, + width: width, + height: height }; - if (hasVideoMediaType(bidRequest)) { - const playerSize = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize')); - payload.width = playerSize[0][0]; - payload.height = playerSize[0][1]; - } else if (hasBannerMediaType(bidRequest)) { - const sizes = getValidSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')); - payload.width = sizes[0][0]; - payload.height = sizes[0][1]; - } - // It may not be encoded, so add it at the end of the payload payload.ref = bidderRequest.refererInfo.referer; @@ -83,10 +85,9 @@ function buildRequests(validBidRequests, bidderRequest) { player: bidRequest.params.player, width: payload.width, height: payload.height, - mediaType: mediaType, + mediaType: payload.media_type }); } - return bidRequests; } @@ -162,6 +163,7 @@ function onTimeout(data) { auction_id: utils.deepAccess(data, '0.auctionId'), bid_id: utils.deepAccess(data, '0.bidId'), ad_unit_code: utils.deepAccess(data, '0.adUnitCode'), + version: ADAPTER_VERSION, ref: window.location.href, }).replace(/\&$/, ''); const bidDomain = utils.deepAccess(data, '0.params.0.domain') || BIDDER_DOMAIN; @@ -248,15 +250,6 @@ export function isMobile() { return false; } -function getMediaType(bid) { - if (hasVideoMediaType(bid)) { - return VIDEO; - } else if (hasBannerMediaType(bid)) { - return BANNER; - } - return ''; -} - function hasBannerMediaType(bid) { return !!utils.deepAccess(bid, 'mediaTypes.banner'); } @@ -272,7 +265,10 @@ function getValidSizes(sizes) { if (utils.isArray(sizes[i]) && sizes[i].length == 2) { const width = sizes[i][0]; const height = sizes[i][1]; - if ((width >= 300 && height >= 250) || (width == 1 && height == 1)) { + if (width == 1 && height == 1) { + return [[1, 1]]; + } + if ((width >= 300 && height >= 250)) { result.push([width, height]); } } diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index ebc62752f16..91aa6b05e6e 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -208,10 +208,18 @@ describe('RelaidoAdapter', function () { }); it('should build bid requests by banner', function () { + setUAMobile(); bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ + [320, 180] + ] + }, banner: { sizes: [ - [640, 360] + [640, 360], + [1, 1] ] } }; @@ -221,6 +229,31 @@ describe('RelaidoAdapter', function () { expect(request.mediaType).to.equal('banner'); }); + it('should take 1x1 size', function () { + setUAMobile(); + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ + [320, 180] + ] + }, + banner: { + sizes: [ + [640, 360], + [1, 1] + ] + } + }; + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + expect(bidRequests).to.have.lengthOf(1); + const request = bidRequests[0]; + + // eslint-disable-next-line no-console + console.log(bidRequests); + expect(request.width).to.equal(1); + }); + it('The referrer should be the last', function () { const bidRequests = spec.buildRequests([bidRequest], bidderRequest); expect(bidRequests).to.have.lengthOf(1); From 6c6affed808e75191c6c6717bb133d8075d76cce Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 18 May 2021 14:54:15 +0600 Subject: [PATCH 0961/1476] ZetaSspBidAdapter fix typo (#6777) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 8f4d8995800..e267942862b 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -4,8 +4,8 @@ import {BANNER} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; const BIDDER_CODE = 'zeta_global_ssp'; -const ENDPOINT_URL = 'https:/ssp.disqus.com/bid'; -const USER_SYNC_URL = 'https:/ssp.disqus.com/match'; +const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; +const USER_SYNC_URL = 'https://ssp.disqus.com/match'; const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; From e92009dd749979ed5af00db2d2be2d8a28acd0b4 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 18 May 2021 12:35:11 +0200 Subject: [PATCH 0962/1476] Adagio Bid Adapter: handle meta.advertiserDomains (and more) (#6781) Related to #6650 --- modules/adagioBidAdapter.js | 6 ++- test/spec/modules/adagioBidAdapter_spec.js | 51 +++++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 134303bf400..66653567dab 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -14,7 +14,7 @@ import { OUTSTREAM } from '../src/video.js'; export const BIDDER_CODE = 'adagio'; export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.8.0'; +export const VERSION = '2.10.0'; export const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; export const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; @@ -1019,6 +1019,10 @@ export const spec = { const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId)); if (bidReq) { + bidObj.meta = utils.deepAccess(bidObj, 'meta', {}); + bidObj.meta.mediaType = bidObj.mediaType; + bidObj.meta.advertiserDomains = (Array.isArray(bidObj.aDomain) && bidObj.aDomain.length) ? bidObj.aDomain : []; + if (bidObj.mediaType === VIDEO) { const mediaTypeContext = utils.deepAccess(bidReq, 'mediaTypes.video.context'); // Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`. diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 3707c19e471..6ee42e47950 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -755,7 +755,14 @@ describe('Adagio bid adapter', () => { netRevenue: true, requestId: 'c180kg4267tyqz', ttl: 360, - width: 300 + width: 300, + aDomain: ['advertiser.com'], + mediaType: 'banner', + meta: { + advertiserId: '80', + advertiserName: 'An Advertiser', + networkId: '110' + } }] } }; @@ -821,13 +828,53 @@ describe('Adagio bid adapter', () => { pagetype: 'ARTICLE', category: 'NEWS', subcategory: 'SPORT', - environment: 'desktop' + environment: 'desktop', + aDomain: ['advertiser.com'], + mediaType: 'banner', + meta: { + advertiserId: '80', + advertiserName: 'An Advertiser', + advertiserDomains: ['advertiser.com'], + networkId: '110', + mediaType: 'banner' + } }]; expect(spec.interpretResponse(serverResponse, bidRequest)).to.be.an('array'); expect(spec.interpretResponse(serverResponse, bidRequest)).to.deep.equal(expectedResponse); }); + it('Meta props should be limited if no bid.meta is provided', function() { + const altServerResponse = utils.deepClone(serverResponse); + delete altServerResponse.body.bids[0].meta; + + let expectedResponse = [{ + ad: '
', + cpm: 1, + creativeId: 'creativeId', + currency: 'EUR', + height: 250, + netRevenue: true, + requestId: 'c180kg4267tyqz', + ttl: 360, + width: 300, + placement: 'PAVE_ATF', + site: 'SITE-NAME', + pagetype: 'ARTICLE', + category: 'NEWS', + subcategory: 'SPORT', + environment: 'desktop', + aDomain: ['advertiser.com'], + mediaType: 'banner', + meta: { + advertiserDomains: ['advertiser.com'], + mediaType: 'banner' + } + }]; + + expect(spec.interpretResponse(altServerResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + it('should populate ADAGIO queue with ssp-data', function() { sandbox.stub(Date, 'now').returns(12345); From 3b6442e8889fdb7e58748af7d8e809a112f7c7ab Mon Sep 17 00:00:00 2001 From: pm-shashank-jain <40654031+pm-shashank-jain@users.noreply.github.com> Date: Tue, 18 May 2021 17:59:23 +0530 Subject: [PATCH 0963/1476] PubMatic Adapter: add support for FloCID (#6749) * changes to support native in pubmaticbid adapter * Removed port from endpoint * Removed protocol from endpoint * Formatting * Fix request payload * Updated test case * Changed request and response as per ortb spec * Change in request and response * Removed comments and extra code * Code Review comments * Code Review Comments and Test cases for request and response * Removed data type as all data asset types are handled * Code Review Changes * Code Review Comments * Supporting both banner and native and sending 0x0 in case of native * Bug Fixes * Bug response not processed by prebid * Change warning message * Fixed typo * Do not send request in case of invalid native bid * Do not send request in case of invalid native requests * objects converted to strings in log for debug purposes * Fixed logic to check for required parmas * Fixed typo for stringify * documentation for native * Review comments from Prebid * Typo * Typo * Updated pub id for native * Code Review * Support for pubid * Test Cases for PubCommonId in PubMatic adapter * Delete yarn.lock * Rename adaptermanager.js to adapterManager.js * Rename yieldNexusBidAdapter.js to yieldnexusBidAdapter.js * Rename yieldNexusBidAdapter.md to yieldnexusBidAdapter.md * Rename yieldNexusBidAdapter_spec.js to yieldnexusBidAdapter_spec.yieldnexusBidAdaptera * Rename yieldnexusBidAdapter_spec.yieldnexusBidAdaptera to yieldnexusBidAdapter_spec.js * bluebillywig outstream player support in pubmatic adapter * removed pubcommon id test cases * BBW Renderer * Pubmatic should work with provided renderer * Review Comments * changes for flocId * code review comments * fixed for eids * code review comments * unit test cases for floc * updated md file * code review comment --- modules/pubmaticBidAdapter.js | 68 +++++++++++++++++++- test/spec/modules/pubmaticBidAdapter_spec.js | 62 ++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index c6ecea48abf..ff934204b43 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -107,6 +107,11 @@ const dealChannelValues = { 5: 'PREF', 6: 'PMPG' }; + +const FLOC_FORMAT = { + 'EID': 1, + 'SEGMENT': 2 +} // BB stands for Blue BillyWig const BB_RENDERER = { bootstrapPlayer: function(bid) { @@ -708,8 +713,67 @@ function _addFloorFromFloorModule(impObj, bid) { impObj.bidfloor = ((!isNaN(bidFloor) && bidFloor > 0) ? bidFloor : UNDEFINED); } +function _getFlocId(validBidRequests, flocFormat) { + var flocIdObject = null; + var flocId = utils.deepAccess(validBidRequests, '0.userId.flocId'); + if (flocId && flocId.id) { + switch (flocFormat) { + case FLOC_FORMAT.SEGMENT: + flocIdObject = { + id: 'FLOC', + name: 'FLOC', + ext: { + ver: flocId.version + }, + segment: [{ + id: flocId.id, + name: 'chrome.com', + value: flocId.id.toString() + }] + } + break; + case FLOC_FORMAT.EID: + default: + flocIdObject = { + source: 'chrome.com', + uids: [ + { + atype: 1, + id: flocId.id, + ext: { + ver: flocId.version + } + }, + ] + } + break; + } + } + return flocIdObject; +} + +function _handleFlocId(payload, validBidRequests) { + var flocObject = _getFlocId(validBidRequests, FLOC_FORMAT.SEGMENT); + if (flocObject) { + if (!payload.user) { + payload.user = {}; + } + if (!payload.user.data) { + payload.user.data = []; + } + payload.user.data.push(flocObject); + } +} + function _handleEids(payload, validBidRequests) { - const bidUserIdAsEids = utils.deepAccess(validBidRequests, '0.userIdAsEids'); + let bidUserIdAsEids = utils.deepAccess(validBidRequests, '0.userIdAsEids'); + let flocObject = _getFlocId(validBidRequests, FLOC_FORMAT.EID); + if (flocObject) { + if (!bidUserIdAsEids) { + bidUserIdAsEids = []; + } + bidUserIdAsEids.push(flocObject); + } if (utils.isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { utils.deepSetValue(payload, 'user.eids', bidUserIdAsEids); } @@ -1029,7 +1093,7 @@ export const spec = { _handleDealCustomTargetings(payload, dctrArr, validBidRequests); _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); - + _handleFlocId(payload, validBidRequests); // First Party Data const commonFpd = config.getConfig('ortb2') || {}; if (commonFpd.site) { diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 28e79a69e55..e8948fdc2d3 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -2096,6 +2096,68 @@ describe('PubMatic adapter', function () { expect(data.user.eids).to.equal(undefined); }); }); + + describe('FlocId', function() { + it('send the FlocId if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.flocId = { + id: '1234', + version: 'chrome1.1' + } + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'chrome.com', + 'uids': [{ + 'id': '1234', + 'atype': 1, + 'ext': { + 'ver': 'chrome1.1' + } + }] + }]); + expect(data.user.data).to.deep.equal([{ + id: 'FLOC', + name: 'FLOC', + ext: { + ver: 'chrome1.1' + }, + segment: [{ + id: '1234', + name: 'chrome.com', + value: '1234' + }] + }]); + }); + + it('appnend the flocId if userIds are present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.netId = 'netid-user-id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + bidRequests[0].userId.flocId = { + id: '1234', + version: 'chrome1.1' + } + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'netid.de', + 'uids': [{ + 'id': 'netid-user-id', + 'atype': 1 + }] + }, { + 'source': 'chrome.com', + 'uids': [{ + 'id': '1234', + 'atype': 1, + 'ext': { + 'ver': 'chrome1.1' + } + }] + }]); + }); + }); }); it('Request params check for video ad', function () { From 4319b59cc1287901d16dd42b8b8191beb37d8947 Mon Sep 17 00:00:00 2001 From: PWyrembak Date: Tue, 18 May 2021 18:17:32 +0300 Subject: [PATCH 0964/1476] TrustX Bid Adapter: added additional sync url (#6771) --- modules/trustxBidAdapter.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 3116400edf7..9f8de30a0d4 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -7,6 +7,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://sofia.trustx.org/hb'; const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson?sp=trustx'; +const ADDITIONAL_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -93,12 +94,32 @@ export const spec = { if (errorMessage) utils.logError(errorMessage); return bidResponses; }, - getUserSyncs: function(syncOptions) { + getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { if (syncOptions.pixelEnabled) { - return [{ + const syncsPerBidder = config.getConfig('userSync.syncsPerBidder'); + let params = []; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params.push(`gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`); + } else { + params.push(`gdpr_consent=${gdprConsent.consentString}`); + } + } + if (uspConsent) { + params.push(`us_privacy=${uspConsent}`); + } + const stringParams = params.join('&'); + const syncs = [{ type: 'image', - url: ADAPTER_SYNC_URL + url: ADAPTER_SYNC_URL + (stringParams ? `?${stringParams}` : '') }]; + if (syncsPerBidder > 1) { + syncs.push({ + type: 'image', + url: ADDITIONAL_SYNC_URL + (stringParams ? `&${stringParams}` : '') + }); + } + return syncs; } } } From c198fa7212bee455ee3c3b19d7add1037e40b1c0 Mon Sep 17 00:00:00 2001 From: Stefano <50023896+bkse-stefanodechicchis@users.noreply.github.com> Date: Tue, 18 May 2021 18:42:38 +0200 Subject: [PATCH 0965/1476] Bucksense Bid Adapter: add adomain to adapter (#6779) * Update bucksenseBidAdapter.js Added support for advertiserDomains * Update bucksenseBidAdapter_spec.js Co-authored-by: Patrick McCann --- modules/bucksenseBidAdapter.js | 6 +++++- test/spec/modules/bucksenseBidAdapter_spec.js | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js index 3f327e62121..46d0dfe3590 100644 --- a/modules/bucksenseBidAdapter.js +++ b/modules/bucksenseBidAdapter.js @@ -90,6 +90,7 @@ export const spec = { var sCurrency = oResponse.currency || 'USD'; var bNetRevenue = oResponse.netRevenue || true; var sAd = oResponse.ad || ''; + var sAdomains = oResponse.adomains || []; if (request && sRequestID.length == 0) { utils.logInfo(WHO + ' interpretResponse() - use RequestID from Placments'); @@ -110,7 +111,10 @@ export const spec = { creativeId: sCreativeID, currency: sCurrency, netRevenue: bNetRevenue, - ad: sAd + ad: sAd, + meta: { + advertiserDomains: sAdomains + } }; bidResponses.push(bidResponse); } else { diff --git a/test/spec/modules/bucksenseBidAdapter_spec.js b/test/spec/modules/bucksenseBidAdapter_spec.js index f49a63d2003..b977c3a9dd1 100644 --- a/test/spec/modules/bucksenseBidAdapter_spec.js +++ b/test/spec/modules/bucksenseBidAdapter_spec.js @@ -128,7 +128,10 @@ describe('Bucksense Adapter', function() { 'creativeId': 'creative002', 'currency': 'USD', 'netRevenue': false, - 'ad': '
' + 'ad': '
', + 'meta': { + 'advertiserDomains': ['http://www.bucksense.com/'] + } } }; }); From 0c8a30820f25d06a400e75fcff1de676dd6c42f1 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Tue, 18 May 2021 13:33:54 -0400 Subject: [PATCH 0966/1476] Update to PBS Bid Adapter and RP Bid Adapter to pass PBJS version in auction requests (#6767) --- modules/prebidServerBidAdapter/index.js | 3 ++ modules/rubiconBidAdapter.js | 4 +++ .../modules/prebidServerBidAdapter_spec.js | 30 +++++++++++++++++++ test/spec/modules/rubiconBidAdapter_spec.js | 2 ++ 4 files changed, 39 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 616cec47ff1..ec5d05f0fe0 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -732,6 +732,9 @@ const OPEN_RTB_PROTOCOL = { } }; + // Sets pbjs version, can be overwritten below if channel exists in s2sConfig.extPrebid + request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}) + // s2sConfig video.ext.prebid is passed through openrtb to PBS if (s2sConfig.extPrebid && typeof s2sConfig.extPrebid === 'object') { request.ext.prebid = Object.assign(request.ext.prebid, s2sConfig.extPrebid); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9fb112eb765..078b5404baf 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -174,6 +174,10 @@ export const spec = { }], ext: { prebid: { + channel: { + name: 'pbjs', + version: $$PREBID_GLOBAL$$.version + }, cache: { vastxml: { returnCreative: rubiConf.returnVast === true diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 147ade6c77b..6833daf49cf 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1143,6 +1143,10 @@ describe('S2S Adapter', function () { targeting: { includebidderkeys: false, includewinners: true + }, + channel: { + name: 'pbjs', + version: 'v$prebid.version$' } } }); @@ -1177,6 +1181,10 @@ describe('S2S Adapter', function () { targeting: { includebidderkeys: false, includewinners: true + }, + channel: { + name: 'pbjs', + version: 'v$prebid.version$' } } }); @@ -1630,6 +1638,28 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.ext.prebid.multibid).to.deep.equal(expected); }); + it('sets and passes pbjs version in request if channel does not exist in s2sConfig', () => { + const s2sBidRequest = utils.deepClone(REQUEST); + const bidRequests = utils.deepClone(BID_REQUESTS); + + adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); + + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + expect(parsedRequestBody.ext.prebid.channel).to.deep.equal({name: 'pbjs', version: 'v$prebid.version$'}); + }); + + it('does not set pbjs version in request if channel does exist in s2sConfig', () => { + const s2sBidRequest = utils.deepClone(REQUEST); + const bidRequests = utils.deepClone(BID_REQUESTS); + + utils.deepSetValue(s2sBidRequest, 's2sConfig.extPrebid.channel', {test: 1}); + + adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); + + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + expect(parsedRequestBody.ext.prebid.channel).to.deep.equal({test: 1}); + }); + it('passes first party data in request', () => { const s2sBidRequest = utils.deepClone(REQUEST); const bidRequests = utils.deepClone(BID_REQUESTS); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 5669e03759a..1964d0d6ecc 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1520,6 +1520,8 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skip).to.equal(1); expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); + // should contain version + expect(post.ext.prebid.channel).to.deep.equal({name: 'pbjs', version: 'v$prebid.version$'}); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); // EIDs should exist expect(post.user.ext).to.have.property('eids').that.is.an('array'); From ff72a3df486801ce24eb927d654d299220d9e1ca Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 18 May 2021 17:11:50 -0400 Subject: [PATCH 0967/1476] AOL Bid Adapter: Remove client side support for already deprecated endpoint floor support (#6743) --- modules/aolBidAdapter.js | 4 ---- test/spec/modules/aolBidAdapter_spec.js | 19 ------------------- 2 files changed, 23 deletions(-) diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 5a5d5e6f417..14b529f4973 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -271,10 +271,6 @@ export const spec = { formatMarketplaceDynamicParams(params = {}, consentData = {}) { let queryParams = {}; - if (params.bidFloor) { - queryParams.bidfloor = params.bidFloor; - } - Object.assign(queryParams, this.formatKeyValues(params.keyValues)); Object.assign(queryParams, this.formatConsentData(consentData)); diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 78be0618594..50622180ad0 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -369,18 +369,6 @@ describe('AolAdapter', function () { expect(request.url).not.to.contain('bidfloor='); }); - it('should return url with bidFloor option if it is present', function () { - let bidRequest = createCustomBidRequest({ - params: { - placement: 1234567, - network: '9599.1', - bidFloor: 0.80 - } - }); - let [request] = spec.buildRequests(bidRequest.bids); - expect(request.url).to.contain('bidfloor=0.8'); - }); - it('should return url with key values if keyValues param is present', function () { let bidRequest = createCustomBidRequest({ params: { @@ -781,13 +769,6 @@ describe('AolAdapter', function () { }); expect(spec.formatMarketplaceDynamicParams()).to.be.equal('param1=val1;param2=val2;param3=val3;'); }); - - it('should return formatted bid floor param when it is present', function () { - let params = { - bidFloor: 0.45 - }; - expect(spec.formatMarketplaceDynamicParams(params)).to.be.equal('bidfloor=0.45;'); - }); }); describe('formatOneMobileDynamicParams()', function () { From 5fb4041298c6f42d69b9f92d30d9c5cfccf1d235 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 19 May 2021 11:53:12 +0200 Subject: [PATCH 0968/1476] tappx Bid Adapter: update isBidRequestValid and fix request url (#6761) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * UPDATE: add initial UID * UPDATE: UID change user ext * UPDATE: UID clean logs * UPDATE: add host info * UPDATE: tappx bid adapter universal id * UPDATE: fix bidder param * UPDATE: tappxBidAdapter tests * tappxBidAdapter - fix spacing * tappxBidAdapter: add test user eids array * tappxBidAdapter: update eids array * FIX: package-lock.json * Conversant adapter: add adomain, remove digitrust (#6495) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Update conversantBidAdapter.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Rads Bid Adapter: add GDPR support & user sync support (#6455) * Proxistore Bid Adapter: add cookieless url endpoint & use floor module (#6427) * use floor module * call cookieless endpoint when necessary * test endpoint url * change url endpoint * delete console log * fix tests * add language to url * use ortb interface * unit test * update test unit * create proxistore module * add unit tests and documentation * delete modules * delete module * add proxistore rtd submodule * delete proxistore module * spacing * change url * AdYoulike Bid Adapter: Add an "Insertion" tracking for Native mediatype (#6481) * add insertion event * add missing campaign ID parameter * update unit test with new tracking checked * Dspx Bid Adapter : add user sync support (#6456) * Add sync support for dspx adapter * Dspx Bid Adapter : add user sync support Co-authored-by: Alexander * Multibid Module: add new module to handle multiple bids from single bidder & update rubicon adapter (#6404) * Multibid module - create new module - Expands the number of key value pairs going to the ad server in the normal Prebid way by establishing the concept of a "dynamic alias" First commit * Continued updates from 1st commit * Adding logWarn for filtered bids * Update to include passing multibid configuration to PBS requests * Update to rubicon bid adapter to pass query param rp_maxbids value taken from bidderRequest.bidLimit * Update to config to look for camelcase property names according to spec. These convert to all lowercase when passed to PBS endpoint * Adjust RP adapter to always include maxbids value - default is 1 * Added support for bidders array in multibid config * Fixed floor comparison to be <= bid cpm as oppossed to just < bid cpm. Updated md file to fix camelCase tpyo * Update to include originalBidderRequest in video call to prebid cache * Update to ignore adpod bids from multibid and allow them to return as normal bids * Adding uid2 to submodules.json (#6508) * NextRoll ID System: add new ID module (#6396) * Add Nextroll ID Module * Add nextroll to eids * Make configuration value names consistent with Adapter Module * Use parnerId instead of sellerId * Add nextroll to userId and eids md files * Remove storage configuration * Rename nextroll -> nextrollId * Add nextrollId to common ID specs * Qwarry Bid Adapter: add GDPR and consent string handling (#6489) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: pro-nsk <32703851+pro-nsk@users.noreply.github.com> * Zemanta Bid Adapter: add support for new params & consent strings to usersync URL (#6468) * add gvl id to spec * add support for bcat and badv params * add consent strings to usersync url * add bcat and badv params to doc * Automatad Bid Adapter: Add meta.advertiserDomains to bid response (#6509) * added bid meta with advertiserDomains * Adhese Bid Adapter: add support for caching video content (#6501) * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * Adhese Bid Adapter: cache video content Co-authored-by: Tim Sturtewagen Co-authored-by: Mateusz * update apacdex unit test to disable debug mode (#6511) * Telaria: not setting adid (#6507) * Prebid 4.33.0 Release * increment pre version * rubicon: removing maxduration as a required bidder parameter (#6513) * Zemanta adapter: add advertiserDomains (#6517) * Lemma Bid Adapter: accepting the floor to use the getFloor function (#6497) * 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. * Update lemmaBidAdapter.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter_spec.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.md Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.js Added user sync support into bid adapter. * updated include modules file extension. updated include modules js file extension. * Update lemmaBidAdapter_spec.js Added unit test for user sync feature. * Update lemmaBidAdapter.js Fixed format error. * Update lemmaBidAdapter_spec.js Fixed format error and typo error. * Set mediaType key value into bid object Set mediaType key value into the bid object. * Update lemmaBidAdapter.js remove duplicate function * Update lemmaBidAdapter.js Remove non supported code. * Update lemmaBidAdapter_spec.js Remove GDPR test cases. * Update lemmaBidAdapter.js Made changes for accepting the floor to use the getFloor function * Update lemmaBidAdapter.js correct undefined keyword name. * Update lemmaBidAdapter_spec.js Added test coverage floor value * Update lemmaBidAdapter.js Remove trailing spaces on lines 379 and 381. * Update lemmaBidAdapter_spec.js Added getFloor function test case changes, Please review it. * Update lemmaBidAdapter_spec.js * Update lemmaBidAdapter.js * Update lemmaBidAdapter.js Fixed lint issue. * Update lemmaBidAdapter_spec.js Fixed test cases. * Update lemmaBidAdapter_spec.js Made suggested changes. Please review it. Co-authored-by: Abhijit Mane * Mediasquare Bid Adapter: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event (#6480) * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * removing status as it does not seem populated when called * add tests * Update nextroll ID variable name to match published ID module (#6519) * Merkle User ID Module: updates to user id submodule (#6503) * AdKernel Bid/Analytics Adapters: user privacy related changes (#6488) * SynacorMedia: remove adId from the bid response (#6520) * Rubicon: making doc data types consistent (#6526) * Synacormedia Bid Adapter: add meta.advertiserDomains (#6527) * Adloox Analytics Adapter: add new analytics adapter (#6308) * gulp: fix supplying list of browsers to test against The following now works: gulp test --browserstack --nolint --nolintfix --browsers=bs_ie_11_windows_10 --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo,RtdProvider}_spec.js' * instreamTracking: unit test tidy From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * adloaderStub: expose stub for other unit tests to use From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * Adloox analytic module * Seedtag adapter: Fixing bug preventing to receive the right params onTimeout. (#6525) * adot bid adapter: add publisher path from bidder config to endpoint url (#6476) * Admixer ID System: add userId submodule (#6238) * Migrating to Prebid 1.0 * Migrating to Prebid 1.0 * Fix spec * add gdpr and usp * remove changes in gdpr_hello_world.html * Update gdpr_hello_world.html add spaces * add user syncs * remove comments * tests * admixer id system * admixer id system * admixer id system eids.md userId.md * admixer id system .submodules.json * admixer id system Co-authored-by: atkachov * PBJS Core: call custom render func after loadscript if provided (#6422) * Pubxai Analytics Adapter: bug fixes and code revamp (#6474) * Updated PubxAiAnalytics adapter - Bug fixes and Code restructuring * Updated endpoint URLs * Changed array.includes to array.indexOf to fix IE issue * Code cleanup and changes as suggested. * Updated browser testing order and edge browser token * PBJS Core: canBidderRegisterSync ignoring iframe sync disabled by default (#6535) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Update userSync.js * Update userSync_spec.js * Added automatic tzo and targetId to adserver request. (#6534) * Impactify Bid Adapter: add new bid adapter (#6518) * Add impactify adapter with MD file * Add impactify adapter * Prebid 4.34.0 Release * Increment pre version * Prebid server adapter: add config for openx hosting (#6530) * Yieldmo adapter: add meta data to bids (#6550) * Smartx Bid Adapter: Add meta.advertiserDomains support (#6547) * Onevideo / Adap.tv Adapter: updated example configuration (#6546) * Mass Deal Rendering Module: support multiple custom configs for dealId and rendering (#6500) * ZetaSsp Bid Adapter: add new bid adapter (#6432) * Adnuntius Bid Adapter: Fix for bid too low. (#6557) * Added automatic tzo and targetId to adserver request. * Fixing issues with bid price being too low. * Fixing issues with bid price being too low. * ReadPeak Bid Adapter: fix api issues, add gdpr consent, & getfloor module support (#6548) * BetweenBidAdatper: added sharedid support (#6531) * adWMG Bid Adapter: update endpoints for cookie sync (#6544) * Support floorCPM parameter, fix some minor bugs * fix space-in-parens circleci error * example fix * clean usersync URL * spaces * spaces * add new unit tests, compatibility with IE11 * remove logInfo * Check for floorCPM value * Check params before sending * New endpoints * code format * new endpoint for cookie sync * update tests Co-authored-by: Mikhail Dykun * Yieldmo Bid Adapter: add support for the floors module (#6541) * Sortable Bid Adapter: add eids support (#6565) * Add Sortable adapter for Prebid 3.x Update tests to reflect changes. * Add .js in imports * hostname not host: don't include port * Trivial change to trigger build: failure wasn't our adapter * More failures in other adapters * PR Feedback - use https for URL - fix examples in markdown - request to endpoint should work now * Feedback: add native and video examples * Update unit tests Co-authored-by: Shannon Broekhoven * Outbrain Bid Adapter: replacing Zemanta (#6558) * Sirdata Real-time Data Module: add new RTD module (#6515) * Logicad Bid Adapter: add support for userid modules (#6529) * ATS-identityLinkIdSystem - add use3P config property to control firing of 3P envelope endpoint (#6568) * Proxistore Bid Adapter: add support for tcf v2 consent (#6543) * use tcf v2 consent * set cosentGiven to false and test Gdpr api v2 * BlueBillyWig Bid Adapter: add renderer customization options (#6540) * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Update bluebillywigBidAdapter test parameters to match renderer to rendererCode rename * Blue Billywig - Also pass through site config with OpenRTB request * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Code quality update, always hit user syncs, improved video params * Remove unnecessary export * Add rendererSettings param to bluebillywig adapter * Kick off CircleCi tests manually Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie * OpenX Bid Adapter: Set Deal ID for video requests (#6573) * 33Across Bid Adapter: add support for User ID modules (#6554) * pubGENIUS bid adapter: support floor module (#6555) * Welect Bid Adapter: update url of API (#6570) * update api url * update api url in tests * Bright Mountain Media Bid Adapter: change bidder code to bmtm; alias old name (#6574) * Adtelligent Bid Adapter: add adUrl support & new alias (#6559) * add adUrl support * add adUrl test * Bright Mountain Media Bid Adapter: Change Endpoint URL (#6576) * tappxBidAdapter: update * tasppxBidAdapter: add video * tappxBidAdapter: update video * tappxBidAdapter: update name interpret banner * tappxBidAdapter: add tests for video * tappxBidAdapter: add adomain * tappxBidAdapter: update adapter version * tappxBidAdapter: update interpretBid adomain and dealid * tappxBidAdapter: update isBidRequestValid * tappxBidAdapter: update tests. Adding video to isBidRequestValid * tappxBidAdapter: update doc .md file * tappxBidAdapter: update isBidRequestValid * tappxBidAdapter: update ads sizes available * tappxBidAdapter: update isBidRequestValid * tappxBidAdapter: update host depending tappx endpoint * tappxBidAdapter: update tappx adapter version * tappxBidAdapter: add EOL * revert outbrain cahnges to untrack in this pr * tappxBidAdapter: update isBidRequestValid tests * tappxBidAdapter: fix circleci error Co-authored-by: marc_tappx Co-authored-by: Patrick McCann Co-authored-by: onlsol <48312668+onlsol@users.noreply.github.com> Co-authored-by: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Co-authored-by: guiann Co-authored-by: Alexander Co-authored-by: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Co-authored-by: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Co-authored-by: Abimael Martinez Co-authored-by: artemiokost Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Co-authored-by: Rok Sušnik Co-authored-by: Kanchika - Automatad Co-authored-by: Paweł L Co-authored-by: Tim Sturtewagen Co-authored-by: Mateusz Co-authored-by: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Co-authored-by: bretg Co-authored-by: Jason Snellbaker Co-authored-by: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Co-authored-by: Abhijit Mane Co-authored-by: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Co-authored-by: Denis Logachov Co-authored-by: RAJKUMAR NATARAJAN Co-authored-by: Alexander Clouter Co-authored-by: Laura Morillo-Velarde Co-authored-by: Giudici-a <34242194+Giudici-a@users.noreply.github.com> Co-authored-by: Galphimbl Co-authored-by: atkachov Co-authored-by: Jérémie Girault Co-authored-by: Phaneendra Hegde Co-authored-by: Mikael Lundin Co-authored-by: Thomas Co-authored-by: Mike Chowla Co-authored-by: Deivydas Šabaras Co-authored-by: ym-atsymuk <81176595+ym-atsymuk@users.noreply.github.com> Co-authored-by: Skylinar <53079123+Skylinar@users.noreply.github.com> Co-authored-by: Adam Browning <19834421+adam-browning@users.noreply.github.com> Co-authored-by: Catalin Ciocov Co-authored-by: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Co-authored-by: readpeaktuomo <66239046+readpeaktuomo@users.noreply.github.com> Co-authored-by: Ignat Khaylov Co-authored-by: nyakove <43004249+nyakove@users.noreply.github.com> Co-authored-by: Mikhail Dykun Co-authored-by: ym-dlabuzov <81709888+ym-dlabuzov@users.noreply.github.com> Co-authored-by: karentnarvaez <61426156+karentnarvaez@users.noreply.github.com> Co-authored-by: Shannon Broekhoven Co-authored-by: nouchy <33549554+nouchy@users.noreply.github.com> Co-authored-by: logicad Co-authored-by: mamatic <52153441+mamatic@users.noreply.github.com> Co-authored-by: Klaas-Jan Boon Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie Co-authored-by: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Co-authored-by: Aparna Rao Co-authored-by: Meng <5110935+edmonl@users.noreply.github.com> Co-authored-by: Nick Duitz <42961155+nduitz@users.noreply.github.com> Co-authored-by: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Co-authored-by: Gena Co-authored-by: Chris Huie <3444727+ChrisHuie@users.noreply.github.com> --- modules/tappxBidAdapter.js | 57 +++++++++++++++-------- modules/tappxBidAdapter.md | 2 +- test/spec/modules/tappxBidAdapter_spec.js | 34 ++++++++++++-- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index d8f6654a567..566795a204b 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,11 +8,10 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10420'; +const TAPPX_BIDDER_VERSION = '0.1.10514'; const TYPE_CNN = 'prebidjs'; const VIDEO_SUPPORT = ['instream']; -var HOST; var hostDomain; export const spec = { @@ -105,13 +104,31 @@ export const spec = { } function validBasic(bid) { - if ( - (bid.params == null) || - (bid.params.endpoint == null) || - (bid.params.tappxkey == null)) { + if (bid.params == null) { utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters.`); return false; } + + if (bid.params.tappxkey == null) { + utils.logWarn(`[TAPPX]: Please review the mandatory Tappxkey parameter.`); + return false; + } + + if (bid.params.host == null) { + utils.logWarn(`[TAPPX]: Please review the mandatory Host parameter.`); + return false; + } + + let classicEndpoint = true + if ((new RegExp(`^(vz.*|zz.*)\.*$`, 'i')).test(bid.params.host)) { + classicEndpoint = false + } + + if (classicEndpoint && bid.params.endpoint == null) { + utils.logWarn(`[TAPPX]: Please review the mandatory endpoint Tappx parameters.`); + return false; + } + return true; } @@ -174,11 +191,10 @@ function interpretBid(serverBid, request) { * @return response ad */ function buildOneRequest(validBidRequests, bidderRequest) { - HOST = utils.deepAccess(validBidRequests, 'params.host'); - let hostInfo = getHostInfo(HOST); + let hostInfo = getHostInfo(validBidRequests); + const ENDPOINT = hostInfo.endpoint; hostDomain = hostInfo.domain; - const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); const BIDEXTRA = utils.deepAccess(validBidRequests, 'params.ext'); @@ -359,7 +375,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { return { method: 'POST', - url: `https://${HOST}/${ENDPOINT}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, + url: `${hostInfo.url}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, data: JSON.stringify(payload), bids: validBidRequests }; @@ -375,23 +391,24 @@ function getOs() { if (ua == null) { return 'unknown'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'ios'; } else if (ua.match(/Android/)) { return 'android'; } else if (ua.match(/Window/)) { return 'windows'; } else { return 'unknown'; } } -function getHostInfo(hostParam) { +function getHostInfo(validBidRequests) { let domainInfo = {}; + let endpoint = utils.deepAccess(validBidRequests, 'params.endpoint'); + let hostParam = utils.deepAccess(validBidRequests, 'params.host'); domainInfo.domain = hostParam.split('/', 1)[0]; - domainInfo.url = hostParam; - let regexNewEndpoints = new RegExp(`^(vz.*|zz.*|testing)\.ssp\.tappx\.com$`, 'i'); - let regexClassicEndpoints = new RegExp(`^[a-z]{3}\.[a-z]{3}\.tappx\.com$`, 'i'); + let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\.pub\.tappx\.com$`, 'i'); + let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\.[a-z]{3}\.tappx\.com$`, 'i'); if (regexNewEndpoints.test(domainInfo.domain)) { - let endpoint = domainInfo.domain.split('.', 1)[0] - if (endpoint.toUpperCase().indexOf('TESTING') === -1) { - domainInfo.endpoint = endpoint - domainInfo.new_endpoint = true; - } + domainInfo.newEndpoint = true; + domainInfo.endpoint = domainInfo.domain.split('.', 1)[0] + domainInfo.url = `https://${hostParam}` } else if (regexClassicEndpoints.test(domainInfo.domain)) { - domainInfo.new_endpoint = false; + domainInfo.newEndpoint = false; + domainInfo.endpoint = endpoint + domainInfo.url = `https://${hostParam}${endpoint}` } return domainInfo; diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index a07bb2d88d1..776b24bb07c 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -9,7 +9,7 @@ Maintainer: prebid@tappx.com Module that connects to :tappx demand sources. Suppots Banner and Instream Video. Please use ```tappx``` as the bidder code. -Ads sizes available: [320,50], [300,250], [320,480], [1024,768], [728,90] +Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1024], [1024,768] # Banner Test Parameters ``` diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 54c4f8c9dca..9fab7d858e1 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -126,11 +126,35 @@ describe('Tappx bid adapter', function () { assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); }); - it('should return false when required params are missing', function () { - let badBidRequest = c_BIDREQUEST; - delete badBidRequest.bids[0].params.tappxkey; - delete badBidRequest.bids[0].params.endpoint; - assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); + it('should return false when params are missing', function () { + let badBidRequestParam = JSON.parse(JSON.stringify(c_BIDREQUEST)); + delete badBidRequestParam.bids[0].params; + assert.isFalse(spec.isBidRequestValid(badBidRequestParam.bids[0])); + }); + + it('should return false when tappxkey is missing', function () { + let badBidRequestTpxkey = JSON.parse(JSON.stringify(c_BIDREQUEST)); ; + delete badBidRequestTpxkey.bids[0].params.tappxkey; + assert.isFalse(spec.isBidRequestValid(badBidRequestTpxkey.bids[0])); + }); + + it('should return false when host is missing', function () { + let badBidRequestHost = JSON.parse(JSON.stringify(c_BIDREQUEST)); ; + delete badBidRequestHost.bids[0].params.host; + assert.isFalse(spec.isBidRequestValid(badBidRequestHost.bids[0])); + }); + + it('should return false when classic endpoint is missing', function () { + let badBidRequestClEp = JSON.parse(JSON.stringify(c_BIDREQUEST)); ; + delete badBidRequestClEp.bids[0].params.endpoint; + assert.isFalse(spec.isBidRequestValid(badBidRequestClEp.bids[0])); + }); + + it('should return true when endpoint is not set for new endpoints', function () { + let badBidRequestNwEp = JSON.parse(JSON.stringify(c_BIDREQUEST)); ; + delete badBidRequestNwEp.bids[0].params.endpoint; + badBidRequestNwEp.bids[0].params.host = 'zztesting.ssp.tappx.com/rtb/v2/'; + assert.isTrue(spec.isBidRequestValid(badBidRequestNwEp.bids[0])); }); it('should return false for not instream requests', function () { From a5b50837afb87b7076752f621970c3d39e7d6e21 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Wed, 19 May 2021 17:45:51 +0700 Subject: [PATCH 0969/1476] Qwarry Bid Adapter: add sizes (#6787) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests * Qwarry bid adapter: remove gdpr field from request * qwarry bid adapter: add sizes * qwarry bid adapter: add sizes Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev --- modules/qwarryBidAdapter.js | 7 ++++++- test/spec/modules/qwarryBidAdapter_spec.js | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index d19ad0b4fe4..7ed5e5c984c 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -20,7 +20,8 @@ export const spec = { bids.push({ bidId: bidRequest.bidId, zoneToken: bidRequest.params.zoneToken, - pos: bidRequest.params.pos + pos: bidRequest.params.pos, + sizes: prepareSizes(bidRequest.sizes) }) }) @@ -90,4 +91,8 @@ export const spec = { } } +function prepareSizes(sizes) { + return sizes && sizes.map(size => ({ width: size[0], height: size[1] })); +} + registerBidder(spec); diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 5d297d32014..06d4af0756c 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -5,6 +5,7 @@ import { newBidder } from 'src/adapters/bidderFactory.js' const REQUEST = { 'bidId': '456', 'bidder': 'qwarry', + 'sizes': [[100, 200], [300, 400]], 'params': { zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 @@ -85,7 +86,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') - expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 }) + expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, sizes: [{ width: 100, height: 200 }, { width: 300, height: 400 }] }) expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') From c3463221febad83379da612a3088599e4d8e595c Mon Sep 17 00:00:00 2001 From: Rahul Shandilya <67756716+c3p-0@users.noreply.github.com> Date: Wed, 19 May 2021 18:53:34 +0530 Subject: [PATCH 0970/1476] Medianet bid adapter: floor module support (#6713) * Medianet floor module support * removing backslash from wildcard --- modules/medianetBidAdapter.js | 29 ++++++++++++++ test/spec/modules/medianetBidAdapter_spec.js | 41 ++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index a2dc8bdfd03..1a4269a8a1d 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -221,10 +221,39 @@ function slotParams(bidRequest) { } else { params.ext.visibility = SLOT_VISIBILITY.NOT_DETERMINED; } + const floorInfo = getBidFloorByType(bidRequest); + if (floorInfo && floorInfo.length > 0) { + params.bidfloors = floorInfo; + } return params; } +function getBidFloorByType(bidRequest) { + let floorInfo = []; + if (typeof bidRequest.getFloor === 'function') { + [BANNER, VIDEO, NATIVE].forEach(mediaType => { + if (bidRequest.mediaTypes.hasOwnProperty(mediaType)) { + if (mediaType == BANNER) { + bidRequest.mediaTypes.banner.sizes.forEach( + size => { + setFloorInfo(bidRequest, mediaType, size, floorInfo) + } + ) + } else { + setFloorInfo(bidRequest, mediaType, '*', floorInfo) + } + } + }); + } + return floorInfo; +} +function setFloorInfo(bidRequest, mediaType, size, floorInfo) { + let floor = bidRequest.getFloor({currency: 'USD', mediaType: mediaType, size: size}); + if (size.length > 1) floor.size = size; + floor.mediaType = mediaType; + floorInfo.push(floor); +} function getMinSize(sizes) { return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min); } diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 1eeb167601e..adb9663ba7c 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -1446,4 +1446,45 @@ describe('Media.net bid adapter', function () { let bids = spec.interpretResponse(SERVER_VIDEO_OUTSTREAM_RESPONSE_VALID_BID, []); expect(bids[0].context).to.equal('outstream'); }); + describe('buildRequests floor tests', function () { + let floor; + let getFloor = function(req) { + return floor[req.mediaType]; + }; + beforeEach(function () { + floor = { + 'banner': { + 'currency': 'USD', + 'floor': 1 + } + }; + $$PREBID_GLOBAL$$.medianetGlobals = {}; + + let documentStub = sandbox.stub(document, 'getElementById'); + let boundingRect = { + top: 50, + left: 50, + bottom: 100, + right: 100 + }; + documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ + getBoundingClientRect: () => boundingRect + }); + documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + getBoundingClientRect: () => boundingRect + }); + let windowSizeStub = sandbox.stub(spec, 'getWindowSize'); + windowSizeStub.returns({ + w: 1000, + h: 1000 + }); + VALID_BID_REQUEST[0].getFloor = getFloor; + }); + + it('should build valid payload with floor', function () { + let requestObj = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); + requestObj = JSON.parse(requestObj.data); + expect(requestObj.imp[0].hasOwnProperty('bidfloors')).to.equal(true); + }); + }); }); From 566c8ae9db52b0d15f266937d182c12065686244 Mon Sep 17 00:00:00 2001 From: Ignat Khaylov Date: Wed, 19 May 2021 17:23:15 +0300 Subject: [PATCH 0971/1476] add adomain support (#6791) Co-authored-by: Ignat Khaylov --- modules/betweenBidAdapter.js | 5 +++- test/spec/modules/betweenBidAdapter_spec.js | 33 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index f6360332368..5a351def958 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -104,7 +104,10 @@ export const spec = { creativeId: serverResponse.body[i].creativeid, currency: serverResponse.body[i].currency || 'RUB', netRevenue: serverResponse.body[i].netRevenue || true, - ad: serverResponse.body[i].ad + ad: serverResponse.body[i].ad, + meta: { + advertiserDomains: serverResponse.body[i].adomain ? serverResponse.body[i].adomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 1b1ccd9efd2..0e772e7be02 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -267,4 +267,37 @@ describe('betweenBidAdapterTests', function () { const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal(''); }); + it('check adomain', function() { + const serverResponse = { + body: [{ + bidid: 'bid1234', + cpm: 1.12, + w: 240, + h: 400, + currency: 'USD', + ad: 'Ad html', + adomain: ['domain1.com', 'domain2.com'] + }] + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.meta.advertiserDomains).to.deep.equal(['domain1.com', 'domain2.com']); + }); + it('check server response without adomain', function() { + const serverResponse = { + body: [{ + bidid: 'bid1234', + cpm: 1.12, + w: 240, + h: 400, + currency: 'USD', + ad: 'Ad html', + }] + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.meta.advertiserDomains).to.deep.equal([]); + }); }); From cb3ae12f5a8e79386afaea94277b5bb278532fff Mon Sep 17 00:00:00 2001 From: John Salis Date: Wed, 19 May 2021 12:04:41 -0400 Subject: [PATCH 0972/1476] Beachfront Bid Adapter: add floors module support (#6752) * add price floors support to beachfront adapter * revert doc changes to create separate pull request Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 23 ++++++++---- .../spec/modules/beachfrontBidAdapter_spec.js | 36 +++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 7466b3d6a68..da5f385b0da 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,9 +6,10 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.15'; +const ADAPTER_VERSION = '1.16'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; +const CURRENCY = 'USD'; export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id='; export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display'; @@ -79,7 +80,7 @@ export const spec = { creativeId: response.crid || response.cmpId, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, - currency: 'USD', + currency: CURRENCY, netRevenue: true, ttl: 300 }; @@ -111,7 +112,7 @@ export const spec = { width: bid.w, height: bid.h, mediaType: BANNER, - currency: 'USD', + currency: CURRENCY, netRevenue: true, ttl: 300 }; @@ -251,6 +252,16 @@ function getPlayerBidParam(bid, key, defaultValue) { return param === undefined ? defaultValue : param; } +function getBannerBidFloor(bid) { + let floorInfo = utils.isFn(bid.getFloor) ? bid.getFloor({ currency: CURRENCY, mediaType: 'banner', size: '*' }) : {}; + return floorInfo.floor || getBannerBidParam(bid, 'bidfloor'); +} + +function getVideoBidFloor(bid) { + let floorInfo = utils.isFn(bid.getFloor) ? bid.getFloor({ currency: CURRENCY, mediaType: 'video', size: '*' }) : {}; + return floorInfo.floor || getVideoBidParam(bid, 'bidfloor'); +} + function isVideoBidValid(bid) { return isVideoBid(bid) && getVideoBidParam(bid, 'appId') && getVideoBidParam(bid, 'bidfloor'); } @@ -316,7 +327,7 @@ function createVideoRequestData(bid, bidderRequest) { let firstSize = getFirstSize(sizes); let video = getVideoTargetingParams(bid); let appId = getVideoBidParam(bid, 'appId'); - let bidfloor = getVideoBidParam(bid, 'bidfloor'); + let bidfloor = getVideoBidFloor(bid); let tagid = getVideoBidParam(bid, 'tagid'); let topLocation = getTopWindowLocation(bidderRequest); let eids = getEids(bid); @@ -358,7 +369,7 @@ function createVideoRequestData(bid, bidderRequest) { user: { ext: {} }, - cur: ['USD'] + cur: [CURRENCY] }; if (bidderRequest && bidderRequest.uspConsent) { @@ -394,7 +405,7 @@ function createBannerRequestData(bids, bidderRequest) { return { slot: bid.adUnitCode, id: getBannerBidParam(bid, 'appId'), - bidfloor: getBannerBidParam(bid, 'bidfloor'), + bidfloor: getBannerBidFloor(bid), tagid: getBannerBidParam(bid, 'tagid'), sizes: getBannerSizes(bid) }; diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 605ccc464cb..fc74ec8a2aa 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -171,6 +171,24 @@ describe('BeachfrontAdapter', function () { expect(data.cur).to.deep.equal(['USD']); }); + it('must read from the floors module if available', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.getFloor = () => ({ currency: 'USD', floor: 1.16 }); + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].bidfloor).to.equal(1.16); + }); + + it('must use the bid floor param if no value is returned from the floors module', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + bidRequest.getFloor = () => ({}); + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); + }); + it('must parse bid size from a nested array', function () { const width = 640; const height = 480; @@ -403,6 +421,24 @@ describe('BeachfrontAdapter', function () { expect(data.ua).to.equal(navigator.userAgent); }); + it('must read from the floors module if available', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.getFloor = () => ({ currency: 'USD', floor: 1.16 }); + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.slots[0].bidfloor).to.equal(1.16); + }); + + it('must use the bid floor param if no value is returned from the floors module', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + bidRequest.getFloor = () => ({}); + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.slots[0].bidfloor).to.equal(bidRequest.params.bidfloor); + }); + it('must parse bid size from a nested array', function () { const width = 300; const height = 250; From d662340c08b529dcdef71ca28068ffa376418eb1 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Wed, 19 May 2021 22:43:24 +0600 Subject: [PATCH 0973/1476] Zeta Ssp Bid Adapter: merge fpd.device and params.device (#6786) --- modules/zetaSspBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index e267942862b..450608a82f4 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { cur: [DEFAULT_CUR], imp: [impData], site: params.site ? params.site : {}, - device: fpd.device ? fpd.device : {}, + device: {...fpd.device, ...params.device}, user: params.user ? params.user : {}, app: params.app ? params.app : {}, ext: { From 03ad46db7e4b02e8835c53a4bb2a01a8290f1606 Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Wed, 19 May 2021 13:05:32 -0400 Subject: [PATCH 0974/1476] pubGENIUS bid adapter: read more video params from mediaTypes.video (#6793) --- modules/pubgeniusBidAdapter.js | 17 ++++++++++++++++- modules/pubgeniusBidAdapter.md | 12 ++++++------ test/spec/modules/pubgeniusBidAdapter_spec.js | 3 +++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 2df2d25f627..89dea545434 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -149,7 +149,22 @@ export const spec = { function buildVideoParams(videoMediaType, videoParams) { videoMediaType = videoMediaType || {}; - const params = pick(videoMediaType, ['api', 'mimes', 'protocols', 'playbackmethod']); + const params = pick(videoMediaType, [ + 'mimes', + 'minduration', + 'maxduration', + 'protocols', + 'startdelay', + 'placement', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity', + ]); switch (videoMediaType.context) { case 'instream': diff --git a/modules/pubgeniusBidAdapter.md b/modules/pubgeniusBidAdapter.md index 66e0c382285..2a10b421de2 100644 --- a/modules/pubgeniusBidAdapter.md +++ b/modules/pubgeniusBidAdapter.md @@ -65,16 +65,16 @@ var adUnits = [ adUnitId: '1001', test: true, - // other video parameters as in OpenRTB v2.5 spec + // Other video parameters can be put here as in OpenRTB v2.5 spec. + // This overrides the same parameters in mediaTypes.video. video: { - skip: 1 - - // the following overrides mediaTypes.video of the ad unit placement: 1, + + // w and h overrides mediaTypes.video.playerSize. w: 640, h: 360, - mimes: ['video/mp4'], - protocols: [3], + + skip: 1 } } } diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 57b83fced06..6568f7aa782 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -370,6 +370,8 @@ describe('pubGENIUS adapter', () => { protocols: [2, 3], api: [1, 2], playbackmethod: [3, 4], + maxduration: 10, + linearity: 1, }, }; bidRequest.params.video = { @@ -394,6 +396,7 @@ describe('pubGENIUS adapter', () => { skipafter: 1, playbackmethod: [3, 4], api: [1, 2], + linearity: 1, }; expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); From 3f4922f72fc572e2da70c25fdb8551d18339b81c Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 19 May 2021 15:56:13 -0400 Subject: [PATCH 0975/1476] Remove camel case for adserver.adslot value in RP Analytics Adapter (#6795) --- modules/rubiconAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 3237facb2e7..5a2e02b8f89 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -664,7 +664,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }, 'gam', () => { if (utils.deepAccess(bid, 'ortb2Imp.ext.data.adserver.name') === 'gam') { - return {adSlot: bid.ortb2Imp.ext.data.adserver.adSlot} + return {adSlot: bid.ortb2Imp.ext.data.adserver.adslot} } }, 'pbAdSlot', () => utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'), From f0af380141b95fee70a9e87b2aa483e75a9d5f6a Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 19 May 2021 13:12:32 -0700 Subject: [PATCH 0976/1476] Prebid 4.40.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 05f68f4a009..ecf293403b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.40.0-pre", + "version": "4.40.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 507c437f9ae74ef8c6bfcd8f1171dbeafac2c2e4 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 19 May 2021 13:48:34 -0700 Subject: [PATCH 0977/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ecf293403b7..f7176162f8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.40.0", + "version": "4.41.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a031f8d39496414e211dd40d019a4adb05d59031 Mon Sep 17 00:00:00 2001 From: mjaworskiccx <50406214+mjaworskiccx@users.noreply.github.com> Date: Thu, 20 May 2021 10:14:33 +0200 Subject: [PATCH 0978/1476] CCX Bid Adapter: add support for mediatypes video parameters (#6736) * adomain support * adomain support * adomain support * adomain support * adomain support * video params * docs changes --- modules/ccxBidAdapter.js | 12 ++--- modules/ccxBidAdapter.md | 58 +++---------------------- test/spec/modules/ccxBidAdapter_spec.js | 54 +++++++++++++++++++++++ 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 37b6fdc3e98..2160e539040 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -94,12 +94,12 @@ function _buildBid (bid) { } } - placement.video.protocols = utils.deepAccess(bid, 'params.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS - placement.video.mimes = utils.deepAccess(bid, 'params.video.mimes') || SUPPORTED_VIDEO_MIMES - placement.video.playbackmethod = utils.deepAccess(bid, 'params.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS - placement.video.skip = utils.deepAccess(bid, 'params.video.skip') || 0 - if (placement.video.skip === 1 && utils.deepAccess(bid, 'params.video.skipafter')) { - placement.video.skipafter = utils.deepAccess(bid, 'params.video.skipafter') + placement.video.protocols = utils.deepAccess(bid, 'mediaTypes.video.protocols') || utils.deepAccess(bid, 'params.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS + placement.video.mimes = utils.deepAccess(bid, 'mediaTypes.video.mimes') || utils.deepAccess(bid, 'params.video.mimes') || SUPPORTED_VIDEO_MIMES + placement.video.playbackmethod = utils.deepAccess(bid, 'mediaTypes.video.playbackmethod') || utils.deepAccess(bid, 'params.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS + placement.video.skip = utils.deepAccess(bid, 'mediaTypes.video.skip') || utils.deepAccess(bid, 'params.video.skip') || 0 + if (placement.video.skip === 1 && (utils.deepAccess(bid, 'mediaTypes.video.skipafter') || utils.deepAccess(bid, 'params.video.skipafter'))) { + placement.video.skipafter = utils.deepAccess(bid, 'mediaTypes.video.skipafter') || utils.deepAccess(bid, 'params.video.skipafter') } } diff --git a/modules/ccxBidAdapter.md b/modules/ccxBidAdapter.md index 740457dd099..7d86507bccb 100644 --- a/modules/ccxBidAdapter.md +++ b/modules/ccxBidAdapter.md @@ -32,67 +32,21 @@ Module that connects to Clickonometrics's demand sources mediaTypes: { video: { playerSize: [1920, 1080] - + protocols: [2, 3, 5, 6], //default + mimes: ["video/mp4", "video/x-flv"], //default + playbackmethod: [1, 2, 3, 4], //default + skip: 1, //default 0 + skipafter: 5 //delete this key if skip = 0 } }, bids: [ { bidder: "ccx", params: { - placementId: 3287742, - //following options are not required, default values will be used. Uncomment if you want to use custom values - /*video: { - //check OpenRTB documentation for following options description - protocols: [2, 3, 5, 6], //default - mimes: ["video/mp4", "video/x-flv"], //default - playbackmethod: [1, 2, 3, 4], //default - skip: 1, //default 0 - skipafter: 5 //delete this key if skip = 0 - }*/ + placementId: 3287742 } } ] } ]; - -# Pre 1.0 Support - - var adUnits = [ - { - code: 'test-banner', - mediaType: 'banner', - sizes: [300, 250], - bids: [ - { - bidder: "ccx", - params: { - placementId: 3286844 - } - } - ] - }, - { - code: 'test-video', - mediaType: 'video', - sizes: [1920, 1080] - bids: [ - { - bidder: "ccx", - params: { - placementId: 3287742, - //following options are not required, default values will be used. Uncomment if you want to use custom values - /*video: { - //check OpenRTB documentation for following options description - protocols: [2, 3, 5, 6], //default - mimes: ["video/mp4", "video/x-flv"], //default - playbackmethod: [1, 2, 3, 4], //default - skip: 1, //default 0 - skipafter: 5 //delete this key if skip = 0 - }*/ - } - } - ] - } - - ]; \ No newline at end of file diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js index ef86b391e39..d346a14d38a 100644 --- a/test/spec/modules/ccxBidAdapter_spec.js +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -434,4 +434,58 @@ describe('ccxAdapter', function () { expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.be.empty; }); }); + describe('mediaTypesVideoParams', function () { + it('Valid video mediaTypes', function () { + let bids = [ + { + adUnitCode: 'video', + auctionId: '0b9de793-8eda-481e-a548-c187d58b28d9', + bidId: '3u94t90ut39tt3t', + bidder: 'ccx', + bidderRequestId: '23ur20r239r2r', + mediaTypes: { + video: { + playerSize: [[640, 480]], + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 1, + skipafter: 5 + } + }, + params: { + placementId: 608 + }, + sizes: [[640, 480]], + transactionId: 'aefddd38-cfa0-48ab-8bdd-325de4bab5f9' + } + ]; + + let imps = [ + { + video: { + w: 640, + h: 480, + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 1, + skipafter: 5 + }, + id: '3u94t90ut39tt3t', + secure: 1, + ext: { + pid: 608 + } + } + ]; + + let bidsClone = utils.deepClone(bids); + + let response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + let data = JSON.parse(response.data); + + expect(data.imp).to.deep.have.same.members(imps); + }); + }); }); From a2401ae1aa6b1158cfc376765962ccc2e3e4e046 Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Thu, 20 May 2021 05:39:08 -0700 Subject: [PATCH 0979/1476] Index Exchange Bid Adapter: adds support for floc (#6758) --- modules/ixBidAdapter.js | 92 +++++++++++---------- test/spec/modules/ixBidAdapter_spec.js | 106 ++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 41 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 64a3237e02a..3be919453a3 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -23,6 +23,33 @@ const PRICE_TO_DOLLAR_FACTOR = { const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; +// determines which eids we send and the rtiPartner field in ext +const SOURCE_RTI_MAPPING = { + 'liveramp.com': 'idl', + 'netid.de': 'NETID', + 'neustar.biz': 'fabrickId', + 'zeotap.com': 'zeotapIdPlus', + 'uidapi.com': 'UID2', + 'adserver.org': 'TDID' +}; + +const PROVIDERS = [ + 'britepoolid', + 'id5id', + 'lipbid', + 'haloId', + 'criteoId', + 'lotamePanoramaId', + 'merkleId', + 'parrableId', + 'connectid', + 'sharedid', + 'tapadId', + 'quantcastId', + 'pubcid', + 'TDID', + 'flocId' +] /** * Transform valid bid request config object to banner impression object that will be sent to ad server. @@ -285,35 +312,37 @@ function getBidRequest(id, impressions) { * From the userIdAsEids array, filter for the ones our adserver can use, and modify them * for our purposes, e.g. add rtiPartner * @param {array} allEids userIdAsEids passed in by prebid + * @param {object} flocId flocId passed in by prebid * @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter * identity info from IX Library) */ -function getEidInfo(allEids) { - // determines which eids we send and the rtiPartner field in ext - var sourceRTIMapping = { - 'liveramp.com': 'idl', - 'netid.de': 'NETID', - 'neustar.biz': 'fabrickId', - 'zeotap.com': 'zeotapIdPlus', - 'uidapi.com': 'UID2' - }; - var toSend = []; - var seenSources = {}; +function getEidInfo(allEids, flocData) { + let toSend = []; + let seenSources = {}; if (utils.isArray(allEids)) { - for (var i = 0; i < allEids.length; i++) { - if (sourceRTIMapping[allEids[i].source] && utils.deepAccess(allEids[i], 'uids.0')) { - seenSources[allEids[i].source] = 1; - allEids[i].uids[0].ext = { - rtiPartner: sourceRTIMapping[allEids[i].source] + for (const eid of allEids) { + if (SOURCE_RTI_MAPPING[eid.source] && utils.deepAccess(eid, 'uids.0')) { + seenSources[eid.source] = true; + eid.uids[0].ext = { + rtiPartner: SOURCE_RTI_MAPPING[eid.source] }; - delete allEids[i].uids[0].atype; - toSend.push(allEids[i]); + delete eid.uids[0].atype; + toSend.push(eid); } } } - return { toSend: toSend, seenSources: seenSources }; -} + const isValidFlocId = flocData && flocData.id && flocData.version; + if (isValidFlocId) { + const flocEid = { + 'source': 'chrome.com', + 'uids': [{ 'id': flocData.id, 'ext': { 'rtiPartner': 'flocId', 'ver': flocData.version } }] + }; + toSend.push(flocEid); + seenSources['chrome.com'] = true; + } + return { toSend, seenSources }; +} /** * Builds a request object to be sent to the ad server based on bid requests. * @@ -327,10 +356,9 @@ function getEidInfo(allEids) { function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Always use secure HTTPS protocol. let baseUrl = SECURE_BID_URL; - // Get ids from Prebid User ID Modules - var eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids')); - var userEids = eidInfo.toSend; + let eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids'), utils.deepAccess(validBidRequests, '0.userId.flocId')); + let userEids = eidInfo.toSend; // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded // and if the data for the partner exist @@ -570,23 +598,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { function _getUserIds(bidRequest) { const userIds = bidRequest.userId || {}; - const PROVIDERS = [ - 'britepoolid', - 'id5id', - 'lipbid', - 'haloId', - 'criteoId', - 'lotamePanoramaId', - 'merkleId', - 'parrableId', - 'connectid', - 'sharedid', - 'tapadId', - 'quantcastId', - 'pubcid' - ] - - return PROVIDERS.filter(provider => utils.deepAccess(userIds, provider)) + return PROVIDERS.filter(provider => userIds[provider]); } /** diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index e0d45913fd9..50920965b60 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -462,9 +462,23 @@ describe('IndexexchangeAdapter', function () { ]; const DEFAULT_USERID_BID_DATA = { - lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4' + lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4', + flocId: {id: '1234', version: 'chrome.1.2'} }; + const DEFAULT_FLOC_USERID_PAYLOAD = [ + { + source: 'chrome.com', + uids: [{ + id: DEFAULT_USERID_BID_DATA.flocId.id, + ext: { + rtiPartner: 'flocId', + ver: DEFAULT_USERID_BID_DATA.flocId.version + } + }] + } + ]; + describe('inherited functions', function () { it('should exists and is a function', function () { const adapter = newBidder(spec); @@ -903,6 +917,96 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); }); + it('IX adapter reads floc id from prebid userId and adds it to eids when there is not other eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(1); + expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc id from prebid userId and appends it to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(6); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads empty floc obj from prebid userId it, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc obj from prebid userId it version is missing, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {'id': 'abcd'}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc obj from prebid userId it ID is missing, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {'version': 'chrome.a.b.c'}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc id with empty id from prebid userId and it does not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {flocID: {id: '', ver: 'chrome.1.2.3'}}; + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () { validIdentityResponse = { AdserverOrgIp: { From 1adc2e70ef6df386ee6abee95c1615bb652f9038 Mon Sep 17 00:00:00 2001 From: rcheptanariu <35690143+rcheptanariu@users.noreply.github.com> Date: Thu, 20 May 2021 15:53:59 +0300 Subject: [PATCH 0980/1476] InvibesBidAdapter - multiposition support & support for multiple id modules (#6506) --- modules/invibesBidAdapter.js | 119 ++++++++++---------- test/spec/modules/invibesBidAdapter_spec.js | 41 +++++-- 2 files changed, 95 insertions(+), 65 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index d4d26b1e017..7d2942eea55 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -8,10 +8,10 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 5, + PREBID_VERSION: 6, METHOD: 'GET', INVIBES_VENDOR_ID: 436, - USERID_PROVIDERS: ['pubcid', 'pubProvidedId'] + USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'] }; const storage = getStorageManager(CONSTANTS.INVIBES_VENDOR_ID); @@ -168,12 +168,6 @@ function handleResponse(responseObj, bidRequests) { responseObj = responseObj.body || responseObj; responseObj = responseObj.videoAdContentResult || responseObj; - let bidModel = responseObj.BidModel; - if (typeof bidModel !== 'object') { - utils.logInfo('Invibes Adapter - Bidding is not configured'); - return []; - } - if (typeof invibes.bidResponse === 'object') { utils.logInfo('Invibes Adapter - Bid response already received. Invibes only responds to one bid request per user visit'); return []; @@ -181,51 +175,69 @@ function handleResponse(responseObj, bidRequests) { invibes.bidResponse = responseObj; - let ads = responseObj.Ads; + const bidResponses = []; + for (let i = 0; i < bidRequests.length; i++) { + let bidRequest = bidRequests[i]; - if (!Array.isArray(ads) || ads.length < 1) { - if (responseObj.AdReason != null) { - utils.logInfo('Invibes Adapter - ' + responseObj.AdReason); + let requestPlacement = null; + if (responseObj.AdPlacements != null) { + for (let j = 0; j < responseObj.AdPlacements.length; j++) { + let bidModel = responseObj.AdPlacements[j].BidModel; + if (bidModel != null && bidModel.PlacementId == bidRequest.params.placementId) { + requestPlacement = responseObj.AdPlacements[j]; + break; + } + } + } else { + let bidModel = responseObj.BidModel; + if (bidModel != null && bidModel.PlacementId == bidRequest.params.placementId) { + requestPlacement = responseObj; + } } - utils.logInfo('Invibes Adapter - No ads available'); - return []; + let bid = createBid(bidRequest, requestPlacement); + if (bid !== null) { + bidResponses.push(bid); + } } - let ad = ads[0]; + return bidResponses; +} - if (bidModel.PlacementId == null) { - utils.logInfo('Invibes Adapter - No Placement Id in response'); - return []; +function createBid(bidRequest, requestPlacement) { + if (requestPlacement === null || requestPlacement.BidModel === null) { + utils.logInfo('Invibes Adapter - Placement not configured for bidding ' + bidRequest.params.placementId); + return null; } - const bidResponses = []; - for (let i = 0; i < bidRequests.length; i++) { - let bidRequest = bidRequests[i]; - - if (bidModel.PlacementId == bidRequest.params.placementId) { - let size = getBiggerSize(bidRequest.sizes); - - bidResponses.push({ - requestId: bidRequest.bidId, - cpm: ad.BidPrice, - width: bidModel.Width || size[0], - height: bidModel.Height || size[1], - creativeId: ad.VideoExposedId, - currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, - netRevenue: true, - ttl: CONSTANTS.TIME_TO_LIVE, - ad: renderCreative(bidModel) - }); - - const now = Date.now(); - ivLogger.info('Bid auction started at ' + bidModel.AuctionStartTime + ' . Invibes registered the bid at ' + now + ' ; bid request took a total of ' + (now - bidModel.AuctionStartTime) + ' ms.'); - } else { - utils.logInfo('Invibes Adapter - Incorrect Placement Id: ' + bidRequest.params.placementId); + let bidModel = requestPlacement.BidModel; + let ads = requestPlacement.Ads; + if (!Array.isArray(ads) || ads.length < 1) { + if (requestPlacement.AdReason != null) { + utils.logInfo('Invibes Adapter - No ads ' + requestPlacement.AdReason); } + + utils.logInfo('Invibes Adapter - No ads available'); + return null; } - return bidResponses; + let ad = ads[0]; + let size = getBiggerSize(bidRequest.sizes); + + const now = Date.now(); + utils.logInfo('Bid auction started at ' + bidModel.AuctionStartTime + ' . Invibes registered the bid at ' + now + ' ; bid request took a total of ' + (now - bidModel.AuctionStartTime) + ' ms.'); + + return { + requestId: bidRequest.bidId, + cpm: ad.BidPrice, + width: bidModel.Width || size[0], + height: bidModel.Height || size[1], + creativeId: ad.VideoExposedId, + currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, + netRevenue: true, + ttl: CONSTANTS.TIME_TO_LIVE, + ad: renderCreative(bidModel) + }; } function generateRandomId() { @@ -357,17 +369,6 @@ function getCappedCampaignsAsString() { .join(','); } -const noop = function () { -}; - -function initLogger() { - if (storage.hasLocalStorage() && localStorage.InvibesDEBUG) { - return window.console; - } - - return {info: noop, error: noop, log: noop, warn: noop, debug: noop}; -} - function buildSyncUrl() { let syncUrl = _customUserSync || CONSTANTS.SYNC_ENDPOINT; syncUrl += '?visitId=' + invibes.visitId; @@ -392,7 +393,7 @@ function readGdprConsent(gdprConsent) { if (!gdprConsent.vendorData.gdprApplies || gdprConsent.vendorData.hasGlobalConsent) { var index; - for (index = 0; index < invibes.purposes; ++index) { + for (index = 0; index < invibes.purposes.length; ++index) { invibes.purposes[index] = true; } @@ -448,7 +449,13 @@ function tryCopyValueToArray(value, target, length) { } if (value.hasOwnProperty(prop)) { - target[i] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + let parsedProp = parseInt(prop); + if (isNaN(parsedProp)) { + target[i] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + } else { + target[parsedProp - 1] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + } + i++; } } @@ -515,8 +522,6 @@ function getVendorLegitimateInterest(vendorData) { return {}; } -const ivLogger = initLogger(); - /// Local domain cookie management ===================== invibes.Uid = { generate: function () { diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index ee3624b5b16..6a59bf98dad 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -736,6 +736,30 @@ describe('invibesBidAdapter:', function () { ` }]; + let multiResponse = { + AdPlacements: [{ + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }], + BidModel: { + BidVersion: 1, + PlacementId: '12345', + AuctionStartTime: Date.now(), + CreativeHtml: '' + } + }] + }; + + let invalidResponse = { + AdPlacements: [{ + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }] + }] + }; + context('when the response is not valid', function () { it('handles response with no bids requested', function () { let emptyResult = spec.interpretResponse({body: response}); @@ -781,24 +805,25 @@ describe('invibesBidAdapter:', function () { let emptyResult = spec.interpretResponse({BidModel: {}, Ads: [{BidPrice: 1}]}, {bidRequests}); expect(emptyResult).to.be.empty; }); + + it('handles response when bid model is missing', function () { + let emptyResult = spec.interpretResponse(invalidResponse); + expect(emptyResult).to.be.empty; + }); }); - context('when the response is valid', function () { - it('responds with a valid bid', function () { - // top.window.invibes.setCookie('a', 'b', 370); - // top.window.invibes.setCookie('c', 'd', 0); - let result = spec.interpretResponse({body: response}, {bidRequests}); + context('when the multiresponse is valid', function () { + it('responds with a valid multiresponse bid', function () { + let result = spec.interpretResponse({body: multiResponse}, {bidRequests}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('responds with a valid bid and uses logger', function () { - localStorage.InvibesDEBUG = true; + it('responds with a valid singleresponse bid', function () { let result = spec.interpretResponse({body: response}, {bidRequests}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('does not make multiple bids', function () { - localStorage.InvibesDEBUG = false; let result = spec.interpretResponse({body: response}, {bidRequests}); let secondResult = spec.interpretResponse({body: response}, {bidRequests}); expect(secondResult).to.be.empty; From 8388dd3e00c4112c6a5d21cc0930fc1c57c491a7 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 20 May 2021 14:34:36 -0400 Subject: [PATCH 0981/1476] appnexus bid adapter: add support for flocid (#6801) --- modules/appnexusBidAdapter.js | 1 + test/spec/modules/appnexusBidAdapter_spec.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index d28bf391aa5..df686f15885 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -244,6 +244,7 @@ export const spec = { if (bidRequests[0].userId) { let eids = []; + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.flocId.id`), 'chrome.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.criteoId`), 'criteo.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.netId`), 'netid.de', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.idl_env`), 'liveramp.com', null); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c875cba12bc..2c8703e648d 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -875,7 +875,11 @@ describe('AppNexusAdapter', function () { tdid: 'sample-userid', criteoId: 'sample-criteo-userid', netId: 'sample-netId-userid', - idl_env: 'sample-idl-userid' + idl_env: 'sample-idl-userid', + flocId: { + id: 'sample-flocid-value', + version: 'chrome.1.0' + } } }); @@ -892,6 +896,11 @@ describe('AppNexusAdapter', function () { id: 'sample-criteo-userid', }); + expect(payload.eids).to.deep.include({ + source: 'chrome.com', + id: 'sample-flocid-value' + }); + expect(payload.eids).to.deep.include({ source: 'netid.de', id: 'sample-netId-userid', From 2e2b4258a5cf12e2d744b7d1f344081db2aeb9f7 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 20 May 2021 15:04:21 -0400 Subject: [PATCH 0982/1476] appnexus bid adapter: add support for uid2 (#6802) --- modules/appnexusBidAdapter.js | 1 + test/spec/modules/appnexusBidAdapter_spec.js | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index df686f15885..404a8c04d14 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -249,6 +249,7 @@ export const spec = { addUserId(eids, utils.deepAccess(bidRequests[0], `userId.netId`), 'netid.de', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.idl_env`), 'liveramp.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.tdid`), 'adserver.org', 'TDID'); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.uid2.id`), 'uidapi.com', 'UID2'); if (eids.length) { payload.eids = eids; diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 2c8703e648d..4baecdd1ba8 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -873,6 +873,7 @@ describe('AppNexusAdapter', function () { const bidRequest = Object.assign({}, bidRequests[0], { userId: { tdid: 'sample-userid', + uid2: { id: 'sample-uid2-value' }, criteoId: 'sample-criteo-userid', netId: 'sample-netId-userid', idl_env: 'sample-idl-userid', @@ -909,7 +910,13 @@ describe('AppNexusAdapter', function () { expect(payload.eids).to.deep.include({ source: 'liveramp.com', id: 'sample-idl-userid' - }) + }); + + expect(payload.eids).to.deep.include({ + source: 'uidapi.com', + id: 'sample-uid2-value', + rti_partner: 'UID2' + }); }); it('should populate iab_support object at the root level if omid support is detected', function () { From 889ff70bdd69f5058a903d124c8506e1f3658bb8 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 20 May 2021 15:18:59 -0400 Subject: [PATCH 0983/1476] Delete xhbBidAdapter.js --- modules/xhbBidAdapter.js | 457 --------------------------------------- 1 file changed, 457 deletions(-) delete mode 100644 modules/xhbBidAdapter.js diff --git a/modules/xhbBidAdapter.js b/modules/xhbBidAdapter.js deleted file mode 100644 index 9363eb97ddc..00000000000 --- a/modules/xhbBidAdapter.js +++ /dev/null @@ -1,457 +0,0 @@ -import { Renderer } from '../src/Renderer.js'; -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import find from 'core-js-pure/features/array/find.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const BIDDER_CODE = 'xhb'; -const URL = 'https://ib.adnxs.com/ut/v3/prebid'; -const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', - 'startdelay', 'skippable', 'playback_method', 'frameworks']; -const USER_PARAMS = ['age', 'external_uid', 'segments', 'gender', 'dnt', 'language']; -const NATIVE_MAPPING = { - body: 'description', - cta: 'ctatext', - image: { - serverName: 'main_image', - requiredParams: { required: true }, - minimumParams: { sizes: [{}] }, - }, - icon: { - serverName: 'icon', - requiredParams: { required: true }, - minimumParams: { sizes: [{}] }, - }, - sponsoredBy: 'sponsored_by', -}; -const SOURCE = 'pbjs'; - -export const spec = { - code: BIDDER_CODE, - aliases: [], - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - /** - * 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.placementId || (bid.params.member && bid.params.invCode)); - }, - - /** - * 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(bidRequests, bidderRequest) { - const tags = bidRequests.map(bidToTag); - const userObjBid = find(bidRequests, hasUserInfo); - let userObj; - if (userObjBid) { - userObj = {}; - Object.keys(userObjBid.params.user) - .filter(param => includes(USER_PARAMS, param)) - .forEach(param => userObj[param] = userObjBid.params.user[param]); - } - - const memberIdBid = find(bidRequests, hasMemberId); - const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; - - const payload = { - tags: [...tags], - user: userObj, - sdk: { - source: SOURCE, - version: '$prebid.version$' - } - }; - if (member > 0) { - payload.member_id = member; - } - - if (bidderRequest && bidderRequest.gdprConsent) { - // note - objects for impbus use underscore instead of camelCase - payload.gdpr_consent = { - consent_string: bidderRequest.gdprConsent.consentString, - consent_required: bidderRequest.gdprConsent.gdprApplies - }; - } - - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: URL, - data: payloadString, - bidderRequest - }; - }, - - /** - * 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, {bidderRequest}) { - serverResponse = serverResponse.body; - const bids = []; - if (!serverResponse || serverResponse.error) { - let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; - if (serverResponse && serverResponse.error) { errorMessage += `: ${serverResponse.error}`; } - utils.logError(errorMessage); - return bids; - } - - if (serverResponse.tags) { - serverResponse.tags.forEach(serverBid => { - const rtbBid = getRtbBid(serverBid); - if (rtbBid) { - if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) { - const bid = newBid(serverBid, rtbBid, bidderRequest); - bid.mediaType = parseMediaType(rtbBid); - bids.push(bid); - } - } - }); - } - return bids; - }, - - getUserSyncs: function(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://acdn.adnxs.com/dmp/async_usersync.html' - }]; - } - } -}; - -function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { - const renderer = Renderer.install({ - id: rtbBid.renderer_id, - url: rtbBid.renderer_url, - config: rendererOptions, - loaded: false, - }); - - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - - renderer.setEventHandlers({ - impression: () => utils.logMessage('xhb outstream video impression event'), - loaded: () => utils.logMessage('xhb outstream video loaded event'), - ended: () => { - utils.logMessage('xhb outstream renderer video event'); - document.querySelector(`#${adUnitCode}`).style.display = 'none'; - } - }); - return renderer; -} - -/* Turn keywords parameter into ut-compatible format */ -function getKeywords(keywords) { - let arrs = []; - - utils._each(keywords, (v, k) => { - if (utils.isArray(v)) { - let values = []; - utils._each(v, (val) => { - val = utils.getValueString('keywords.' + k, val); - if (val) { values.push(val); } - }); - v = values; - } else { - v = utils.getValueString('keywords.' + k, v); - if (utils.isStr(v)) { - v = [v]; - } else { - return; - } // unsuported types - don't send a key - } - arrs.push({key: k, value: v}); - }); - - return arrs; -} - -/** - * Unpack the Server's Bid into a Prebid-compatible one. - * @param serverBid - * @param rtbBid - * @param bidderRequest - * @return Bid - */ -function newBid(serverBid, rtbBid, bidderRequest) { - const bid = { - requestId: serverBid.uuid, - cpm: 0.00, - creativeId: rtbBid.creative_id, - dealId: 99999999, - currency: 'USD', - netRevenue: true, - ttl: 300, - appnexus: { - buyerMemberId: rtbBid.buyer_member_id - } - }; - - if (rtbBid.rtb.video) { - Object.assign(bid, { - width: rtbBid.rtb.video.player_width, - height: rtbBid.rtb.video.player_height, - vastUrl: rtbBid.rtb.video.asset_url, - vastImpUrl: rtbBid.notify_url, - ttl: 3600 - }); - // This supports Outstream Video - if (rtbBid.renderer_url) { - const rendererOptions = utils.deepAccess( - bidderRequest.bids[0], - 'renderer.options' - ); - - Object.assign(bid, { - adResponse: serverBid, - renderer: newRenderer(bid.adUnitCode, rtbBid, rendererOptions) - }); - bid.adResponse.ad = bid.adResponse.ads[0]; - bid.adResponse.ad.video = bid.adResponse.ad.rtb.video; - } - } else if (rtbBid.rtb[NATIVE]) { - const nativeAd = rtbBid.rtb[NATIVE]; - bid[NATIVE] = { - title: nativeAd.title, - body: nativeAd.desc, - cta: nativeAd.ctatext, - sponsoredBy: nativeAd.sponsored, - clickUrl: nativeAd.link.url, - clickTrackers: nativeAd.link.click_trackers, - impressionTrackers: nativeAd.impression_trackers, - javascriptTrackers: nativeAd.javascript_trackers, - }; - if (nativeAd.main_img) { - bid['native'].image = { - url: nativeAd.main_img.url, - height: nativeAd.main_img.height, - width: nativeAd.main_img.width, - }; - } - if (nativeAd.icon) { - bid['native'].icon = { - url: nativeAd.icon.url, - height: nativeAd.icon.height, - width: nativeAd.icon.width, - }; - } - } else { - Object.assign(bid, { - width: rtbBid.rtb.banner.width, - height: rtbBid.rtb.banner.height, - ad: rtbBid.rtb.banner.content - }); - try { - const url = rtbBid.rtb.trackers[0].impression_urls[0]; - const tracker = utils.createTrackPixelHtml(url); - bid.ad += tracker; - } catch (error) { - utils.logError('Error appending tracking pixel', error); - } - } - - return bid; -} - -function bidToTag(bid) { - const tag = {}; - tag.sizes = transformSizes(bid.sizes); - tag.primary_size = tag.sizes[0]; - tag.ad_types = []; - tag.uuid = bid.bidId; - if (bid.params.placementId) { - tag.id = parseInt(bid.params.placementId, 10); - } else { - tag.code = bid.params.invCode; - } - tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false; - tag.prebid = true; - tag.disable_psa = true; - if (bid.params.reserve) { - tag.reserve = bid.params.reserve; - } - if (bid.params.position) { - tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; - } - if (bid.params.trafficSourceCode) { - tag.traffic_source_code = bid.params.trafficSourceCode; - } - if (bid.params.privateSizes) { - tag.private_sizes = transformSizes(bid.params.privateSizes); - } - if (bid.params.supplyType) { - tag.supply_type = bid.params.supplyType; - } - if (bid.params.pubClick) { - tag.pubclick = bid.params.pubClick; - } - if (bid.params.extInvCode) { - tag.ext_inv_code = bid.params.extInvCode; - } - if (bid.params.externalImpId) { - tag.external_imp_id = bid.params.externalImpId; - } - if (!utils.isEmpty(bid.params.keywords)) { - tag.keywords = getKeywords(bid.params.keywords); - } - - if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) { - tag.ad_types.push(NATIVE); - - if (bid.nativeParams) { - const nativeRequest = buildNativeRequest(bid.nativeParams); - tag[NATIVE] = {layouts: [nativeRequest]}; - } - } - - const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`); - const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - - if (bid.mediaType === VIDEO || videoMediaType) { - tag.ad_types.push(VIDEO); - } - - // instream gets vastUrl, outstream gets vastXml - if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { - tag.require_asset_url = true; - } - - if (bid.params.video) { - tag.video = {}; - // place any valid video params on the tag - Object.keys(bid.params.video) - .filter(param => includes(VIDEO_TARGETING, param)) - .forEach(param => tag.video[param] = bid.params.video[param]); - } - - if ( - (utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) || - (bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER])) - ) { - tag.ad_types.push(BANNER); - } - - return tag; -} - -/* Turn bid request sizes into ut-compatible format */ -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; -} - -function hasUserInfo(bid) { - return !!bid.params.user; -} - -function hasMemberId(bid) { - return !!parseInt(bid.params.member, 10); -} - -function getRtbBid(tag) { - return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb); -} - -function buildNativeRequest(params) { - const request = {}; - - // map standard prebid native asset identifier to /ut parameters - // e.g., tag specifies `body` but /ut only knows `description`. - // mapping may be in form {tag: ''} or - // {tag: {serverName: '', requiredParams: {...}}} - Object.keys(params).forEach(key => { - // check if one of the forms is used, otherwise - // a mapping wasn't specified so pass the key straight through - const requestKey = - (NATIVE_MAPPING[key] && NATIVE_MAPPING[key].serverName) || - NATIVE_MAPPING[key] || - key; - - // required params are always passed on request - const requiredParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].requiredParams; - request[requestKey] = Object.assign({}, requiredParams, params[key]); - - // minimum params are passed if no non-required params given on adunit - const minimumParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].minimumParams; - - if (requiredParams && minimumParams) { - // subtract required keys from adunit keys - const adunitKeys = Object.keys(params[key]); - const requiredKeys = Object.keys(requiredParams); - const remaining = adunitKeys.filter(key => !includes(requiredKeys, key)); - - // if none are left over, the minimum params needs to be sent - if (remaining.length === 0) { - request[requestKey] = Object.assign({}, request[requestKey], minimumParams); - } - } - }); - - return request; -} - -function outstreamRender(bid) { - // push to render queue because ANOutstreamVideo may not be loaded yet - bid.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - tagId: bid.adResponse.tag_id, - sizes: [bid.getSize().split('x')], - targetId: bid.adUnitCode, // target div id to render video - uuid: bid.adResponse.uuid, - adResponse: bid.adResponse, - rendererOptions: bid.renderer.getConfig() - }, handleOutstreamRendererEvents.bind(null, bid)); - }); -} - -function handleOutstreamRendererEvents(bid, id, eventName) { - bid.renderer.handleVideoEvent({ id, eventName }); -} - -function parseMediaType(rtbBid) { - const adType = rtbBid.ad_type; - if (adType === VIDEO) { - return VIDEO; - } else if (adType === NATIVE) { - return NATIVE; - } else { - return BANNER; - } -} - -registerBidder(spec); From bb02ee1dea06ed8ecc6f779ebcd541092b06e39c Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 20 May 2021 16:02:31 -0400 Subject: [PATCH 0984/1476] Restore xhb (#6803) --- modules/xhbBidAdapter.js | 457 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 457 insertions(+) create mode 100644 modules/xhbBidAdapter.js diff --git a/modules/xhbBidAdapter.js b/modules/xhbBidAdapter.js new file mode 100644 index 00000000000..9363eb97ddc --- /dev/null +++ b/modules/xhbBidAdapter.js @@ -0,0 +1,457 @@ +import { Renderer } from '../src/Renderer.js'; +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const BIDDER_CODE = 'xhb'; +const URL = 'https://ib.adnxs.com/ut/v3/prebid'; +const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', + 'startdelay', 'skippable', 'playback_method', 'frameworks']; +const USER_PARAMS = ['age', 'external_uid', 'segments', 'gender', 'dnt', 'language']; +const NATIVE_MAPPING = { + body: 'description', + cta: 'ctatext', + image: { + serverName: 'main_image', + requiredParams: { required: true }, + minimumParams: { sizes: [{}] }, + }, + icon: { + serverName: 'icon', + requiredParams: { required: true }, + minimumParams: { sizes: [{}] }, + }, + sponsoredBy: 'sponsored_by', +}; +const SOURCE = 'pbjs'; + +export const spec = { + code: BIDDER_CODE, + aliases: [], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + /** + * 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.placementId || (bid.params.member && bid.params.invCode)); + }, + + /** + * 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(bidRequests, bidderRequest) { + const tags = bidRequests.map(bidToTag); + const userObjBid = find(bidRequests, hasUserInfo); + let userObj; + if (userObjBid) { + userObj = {}; + Object.keys(userObjBid.params.user) + .filter(param => includes(USER_PARAMS, param)) + .forEach(param => userObj[param] = userObjBid.params.user[param]); + } + + const memberIdBid = find(bidRequests, hasMemberId); + const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; + + const payload = { + tags: [...tags], + user: userObj, + sdk: { + source: SOURCE, + version: '$prebid.version$' + } + }; + if (member > 0) { + payload.member_id = member; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + // note - objects for impbus use underscore instead of camelCase + payload.gdpr_consent = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + } + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: URL, + data: payloadString, + bidderRequest + }; + }, + + /** + * 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, {bidderRequest}) { + serverResponse = serverResponse.body; + const bids = []; + if (!serverResponse || serverResponse.error) { + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; + if (serverResponse && serverResponse.error) { errorMessage += `: ${serverResponse.error}`; } + utils.logError(errorMessage); + return bids; + } + + if (serverResponse.tags) { + serverResponse.tags.forEach(serverBid => { + const rtbBid = getRtbBid(serverBid); + if (rtbBid) { + if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) { + const bid = newBid(serverBid, rtbBid, bidderRequest); + bid.mediaType = parseMediaType(rtbBid); + bids.push(bid); + } + } + }); + } + return bids; + }, + + getUserSyncs: function(syncOptions) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: 'https://acdn.adnxs.com/dmp/async_usersync.html' + }]; + } + } +}; + +function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { + const renderer = Renderer.install({ + id: rtbBid.renderer_id, + url: rtbBid.renderer_url, + config: rendererOptions, + loaded: false, + }); + + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + renderer.setEventHandlers({ + impression: () => utils.logMessage('xhb outstream video impression event'), + loaded: () => utils.logMessage('xhb outstream video loaded event'), + ended: () => { + utils.logMessage('xhb outstream renderer video event'); + document.querySelector(`#${adUnitCode}`).style.display = 'none'; + } + }); + return renderer; +} + +/* Turn keywords parameter into ut-compatible format */ +function getKeywords(keywords) { + let arrs = []; + + utils._each(keywords, (v, k) => { + if (utils.isArray(v)) { + let values = []; + utils._each(v, (val) => { + val = utils.getValueString('keywords.' + k, val); + if (val) { values.push(val); } + }); + v = values; + } else { + v = utils.getValueString('keywords.' + k, v); + if (utils.isStr(v)) { + v = [v]; + } else { + return; + } // unsuported types - don't send a key + } + arrs.push({key: k, value: v}); + }); + + return arrs; +} + +/** + * Unpack the Server's Bid into a Prebid-compatible one. + * @param serverBid + * @param rtbBid + * @param bidderRequest + * @return Bid + */ +function newBid(serverBid, rtbBid, bidderRequest) { + const bid = { + requestId: serverBid.uuid, + cpm: 0.00, + creativeId: rtbBid.creative_id, + dealId: 99999999, + currency: 'USD', + netRevenue: true, + ttl: 300, + appnexus: { + buyerMemberId: rtbBid.buyer_member_id + } + }; + + if (rtbBid.rtb.video) { + Object.assign(bid, { + width: rtbBid.rtb.video.player_width, + height: rtbBid.rtb.video.player_height, + vastUrl: rtbBid.rtb.video.asset_url, + vastImpUrl: rtbBid.notify_url, + ttl: 3600 + }); + // This supports Outstream Video + if (rtbBid.renderer_url) { + const rendererOptions = utils.deepAccess( + bidderRequest.bids[0], + 'renderer.options' + ); + + Object.assign(bid, { + adResponse: serverBid, + renderer: newRenderer(bid.adUnitCode, rtbBid, rendererOptions) + }); + bid.adResponse.ad = bid.adResponse.ads[0]; + bid.adResponse.ad.video = bid.adResponse.ad.rtb.video; + } + } else if (rtbBid.rtb[NATIVE]) { + const nativeAd = rtbBid.rtb[NATIVE]; + bid[NATIVE] = { + title: nativeAd.title, + body: nativeAd.desc, + cta: nativeAd.ctatext, + sponsoredBy: nativeAd.sponsored, + clickUrl: nativeAd.link.url, + clickTrackers: nativeAd.link.click_trackers, + impressionTrackers: nativeAd.impression_trackers, + javascriptTrackers: nativeAd.javascript_trackers, + }; + if (nativeAd.main_img) { + bid['native'].image = { + url: nativeAd.main_img.url, + height: nativeAd.main_img.height, + width: nativeAd.main_img.width, + }; + } + if (nativeAd.icon) { + bid['native'].icon = { + url: nativeAd.icon.url, + height: nativeAd.icon.height, + width: nativeAd.icon.width, + }; + } + } else { + Object.assign(bid, { + width: rtbBid.rtb.banner.width, + height: rtbBid.rtb.banner.height, + ad: rtbBid.rtb.banner.content + }); + try { + const url = rtbBid.rtb.trackers[0].impression_urls[0]; + const tracker = utils.createTrackPixelHtml(url); + bid.ad += tracker; + } catch (error) { + utils.logError('Error appending tracking pixel', error); + } + } + + return bid; +} + +function bidToTag(bid) { + const tag = {}; + tag.sizes = transformSizes(bid.sizes); + tag.primary_size = tag.sizes[0]; + tag.ad_types = []; + tag.uuid = bid.bidId; + if (bid.params.placementId) { + tag.id = parseInt(bid.params.placementId, 10); + } else { + tag.code = bid.params.invCode; + } + tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; + tag.use_pmt_rule = bid.params.usePaymentRule || false; + tag.prebid = true; + tag.disable_psa = true; + if (bid.params.reserve) { + tag.reserve = bid.params.reserve; + } + if (bid.params.position) { + tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; + } + if (bid.params.trafficSourceCode) { + tag.traffic_source_code = bid.params.trafficSourceCode; + } + if (bid.params.privateSizes) { + tag.private_sizes = transformSizes(bid.params.privateSizes); + } + if (bid.params.supplyType) { + tag.supply_type = bid.params.supplyType; + } + if (bid.params.pubClick) { + tag.pubclick = bid.params.pubClick; + } + if (bid.params.extInvCode) { + tag.ext_inv_code = bid.params.extInvCode; + } + if (bid.params.externalImpId) { + tag.external_imp_id = bid.params.externalImpId; + } + if (!utils.isEmpty(bid.params.keywords)) { + tag.keywords = getKeywords(bid.params.keywords); + } + + if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) { + tag.ad_types.push(NATIVE); + + if (bid.nativeParams) { + const nativeRequest = buildNativeRequest(bid.nativeParams); + tag[NATIVE] = {layouts: [nativeRequest]}; + } + } + + const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`); + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + + if (bid.mediaType === VIDEO || videoMediaType) { + tag.ad_types.push(VIDEO); + } + + // instream gets vastUrl, outstream gets vastXml + if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { + tag.require_asset_url = true; + } + + if (bid.params.video) { + tag.video = {}; + // place any valid video params on the tag + Object.keys(bid.params.video) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => tag.video[param] = bid.params.video[param]); + } + + if ( + (utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) || + (bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER])) + ) { + tag.ad_types.push(BANNER); + } + + return tag; +} + +/* Turn bid request sizes into ut-compatible format */ +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; +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +function hasMemberId(bid) { + return !!parseInt(bid.params.member, 10); +} + +function getRtbBid(tag) { + return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb); +} + +function buildNativeRequest(params) { + const request = {}; + + // map standard prebid native asset identifier to /ut parameters + // e.g., tag specifies `body` but /ut only knows `description`. + // mapping may be in form {tag: ''} or + // {tag: {serverName: '', requiredParams: {...}}} + Object.keys(params).forEach(key => { + // check if one of the forms is used, otherwise + // a mapping wasn't specified so pass the key straight through + const requestKey = + (NATIVE_MAPPING[key] && NATIVE_MAPPING[key].serverName) || + NATIVE_MAPPING[key] || + key; + + // required params are always passed on request + const requiredParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].requiredParams; + request[requestKey] = Object.assign({}, requiredParams, params[key]); + + // minimum params are passed if no non-required params given on adunit + const minimumParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].minimumParams; + + if (requiredParams && minimumParams) { + // subtract required keys from adunit keys + const adunitKeys = Object.keys(params[key]); + const requiredKeys = Object.keys(requiredParams); + const remaining = adunitKeys.filter(key => !includes(requiredKeys, key)); + + // if none are left over, the minimum params needs to be sent + if (remaining.length === 0) { + request[requestKey] = Object.assign({}, request[requestKey], minimumParams); + } + } + }); + + return request; +} + +function outstreamRender(bid) { + // push to render queue because ANOutstreamVideo may not be loaded yet + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + tagId: bid.adResponse.tag_id, + sizes: [bid.getSize().split('x')], + targetId: bid.adUnitCode, // target div id to render video + uuid: bid.adResponse.uuid, + adResponse: bid.adResponse, + rendererOptions: bid.renderer.getConfig() + }, handleOutstreamRendererEvents.bind(null, bid)); + }); +} + +function handleOutstreamRendererEvents(bid, id, eventName) { + bid.renderer.handleVideoEvent({ id, eventName }); +} + +function parseMediaType(rtbBid) { + const adType = rtbBid.ad_type; + if (adType === VIDEO) { + return VIDEO; + } else if (adType === NATIVE) { + return NATIVE; + } else { + return BANNER; + } +} + +registerBidder(spec); From 2feed6832c9e675a973f37628f021ae81272b488 Mon Sep 17 00:00:00 2001 From: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Date: Fri, 21 May 2021 02:45:11 -0700 Subject: [PATCH 0985/1476] EMX Digital Bid Adapter: floor module and advertiserDomain support (#6805) --- modules/emx_digitalBidAdapter.js | 28 +++++++++- .../modules/emx_digitalBidAdapter_spec.js | 52 ++++++++++++++++--- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 41a5af6d703..bcdcd7393f7 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -223,7 +223,7 @@ export const spec = { utils._each(validBidRequests, function (bid) { let tagid = utils.getBidIdParameter('tagid', bid.params); - let bidfloor = parseFloat(utils.getBidIdParameter('bidfloor', bid.params)) || 0; + let bidfloor = parseFloat(getBidFloor(bid)) || 0; let isVideo = !!bid.mediaTypes.video; let data = { id: bid.bidId, @@ -287,6 +287,14 @@ export const spec = { bidResponse = emxAdapter.formatVideoResponse(bidResponse, Object.assign({}, emxBid), bidRequest); } bidResponse.mediaType = (isVideo ? VIDEO : BANNER); + + // support for adomain in prebid 5.0 + if (emxBid.adomain && emxBid.adomain.length) { + bidResponse.meta = { + advertiserDomains: emxBid.adomain + }; + } + emxBidResponses.push(bidResponse); }); } @@ -312,4 +320,22 @@ export const spec = { return syncs; } }; + +// support floors module in prebid 5.0 +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return parseFloat(utils.getBidIdParameter('bidfloor', bid.params)); + } + + let floor = bid.getFloor({ + currency: DEFAULT_CUR, + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 855ffa0a0b7..0f82122b9c1 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -253,12 +253,38 @@ describe('emx_digital Adapter', function () { expect(queryParams[1]).to.match(new RegExp('^ts=\d*', 'g')); }); - it('builds with bid floor', function () { - const bidRequestWithBidFloor = utils.deepClone(bidderRequest.bids); - bidRequestWithBidFloor[0].params.bidfloor = 1; - const requestWithFloor = spec.buildRequests(bidRequestWithBidFloor, bidderRequest); + it('builds bidfloor value from bid param when getFloor function does not exist', function () { + const bidRequestWithFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithFloor[0].params.bidfloor = 1; + const requestWithFloor = spec.buildRequests(bidRequestWithFloor, bidderRequest); const data = JSON.parse(requestWithFloor.data); - expect(data.imp[0].bidfloor).to.equal(bidRequestWithBidFloor[0].params.bidfloor); + expect(data.imp[0].bidfloor).to.equal(bidRequestWithFloor[0].params.bidfloor); + }); + + it('builds bidfloor value from getFloor function when it exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithGetFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithGetFloor[0].getFloor = () => floorResponse; + const requestWithGetFloor = spec.buildRequests(bidRequestWithGetFloor, bidderRequest); + const data = JSON.parse(requestWithGetFloor.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); + + it('builds bidfloor value from getFloor when both floor and getFloor function exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithBothFloors = utils.deepClone(bidderRequest.bids); + bidRequestWithBothFloors[0].params.bidfloor = 1; + bidRequestWithBothFloors[0].getFloor = () => floorResponse; + const requestWithBothFloors = spec.buildRequests(bidRequestWithBothFloors, bidderRequest); + const data = JSON.parse(requestWithBothFloors.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); + + it('empty bidfloor value when floor and getFloor is not defined', function () { + const bidRequestWithoutFloor = utils.deepClone(bidderRequest.bids); + const requestWithoutFloor = spec.buildRequests(bidRequestWithoutFloor, bidderRequest); + const data = JSON.parse(requestWithoutFloor.data); + expect(data.imp[0].bidfloor).to.not.exist; }); it('builds request properly', function () { @@ -470,7 +496,8 @@ describe('emx_digital Adapter', function () { 'id': '987654321cba', 'price': 0.5, 'ttl': 300, - 'w': 300 + 'w': 300, + 'adomain': ['example.com'] }], 'seat': '1356' }, { @@ -498,7 +525,10 @@ describe('emx_digital Adapter', function () { 'netRevneue': true, 'mediaType': 'banner', 'ad': '', - 'ttl': 300 + 'ttl': 300, + 'meta': { + 'advertiserDomains': ['example.com'] + } }, { 'requestId': '12819a18-56e1-4256-b836-b69a10202668', 'cpm': 0.7, @@ -630,6 +660,14 @@ describe('emx_digital Adapter', function () { body: badAdmServerResponse })); }); + + it('returns valid advertiser domain', function () { + const bidResponse = utils.deepClone(serverResponse); + let result = spec.interpretResponse({body: bidResponse}); + expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); + // case where adomains are not in request + expect(result[1].meta).to.not.exist; + }); }); describe('getUserSyncs', function () { From 6637abe6caa5750a3918caa1cedc344a423f315a Mon Sep 17 00:00:00 2001 From: lksharma Date: Fri, 21 May 2021 08:26:07 -0400 Subject: [PATCH 0986/1476] Index Exchange Bid Adapter: coppa support, dealid & ttl field updates (#6782) * set bidderRequestId to be a string in r.id * set coppa value in r.reqs.coppa * added support for seat[].bid[].exp ttl value * read dealid from openrtb first, then fallback to ext * use utils.isInteger Co-authored-by: Kajan Umakanthan Co-authored-by: punkiller --- modules/ixBidAdapter.js | 20 ++-- test/spec/modules/ixBidAdapter_spec.js | 124 +++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 3be919453a3..6970eefeac2 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -2,7 +2,6 @@ import * as utils from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; -import isInteger from 'core-js-pure/features/number/is-integer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ix'; @@ -206,6 +205,8 @@ function _applyFloor(bid, imp, mediaType) { */ function parseBid(rawBid, currency, bidRequest) { const bid = {}; + const isValidExpiry = !!((utils.deepAccess(rawBid, 'exp') && utils.isInteger(rawBid.exp))); + const dealID = utils.deepAccess(rawBid, 'dealid') || utils.deepAccess(rawBid, 'ext.dealid'); if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) { bid.cpm = rawBid.price / PRICE_TO_DOLLAR_FACTOR[currency]; @@ -215,7 +216,10 @@ function parseBid(rawBid, currency, bidRequest) { bid.requestId = rawBid.impid; - bid.dealId = utils.deepAccess(rawBid, 'ext.dealid'); + if (dealID) { + bid.dealId = dealID; + } + bid.netRevenue = NET_REVENUE; bid.currency = currency; bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; @@ -226,13 +230,13 @@ function parseBid(rawBid, currency, bidRequest) { bid.width = bidRequest.video.w; bid.height = bidRequest.video.h; bid.mediaType = VIDEO; - bid.ttl = VIDEO_TIME_TO_LIVE; + bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else { bid.ad = rawBid.adm; bid.width = rawBid.w; bid.height = rawBid.h; bid.mediaType = BANNER; - bid.ttl = BANNER_TIME_TO_LIVE; + bid.ttl = isValidExpiry ? rawBid.exp : BANNER_TIME_TO_LIVE; } bid.meta = {}; @@ -253,7 +257,7 @@ function parseBid(rawBid, currency, bidRequest) { * @return {boolean} True if this is a valid size format, and false otherwise. */ function isValidSize(size) { - return Array.isArray(size) && size.length === 2 && isInteger(size[0]) && isInteger(size[1]); + return Array.isArray(size) && size.length === 2 && utils.isInteger(size[0]) && utils.isInteger(size[1]); } /** @@ -385,7 +389,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const r = {}; // Since bidderRequestId are the same for different bid request, just use the first one. - r.id = validBidRequests[0].bidderRequestId; + r.id = validBidRequests[0].bidderRequestId.toString(); r.site = {}; r.ext = {}; @@ -452,6 +456,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } } + if (config.getConfig('coppa')) { + utils.deepSetValue(r, 'regs.coppa', 1); + } + const payload = {}; // Parse additional runtime configs. diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 50920965b60..c026dfd3efc 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1221,7 +1221,7 @@ describe('IndexexchangeAdapter', function () { }); describe('buildRequests', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + let request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const requestUrl = request.url; const requestMethod = request.method; const query = request.data; @@ -1254,6 +1254,7 @@ describe('IndexexchangeAdapter', function () { it('payload should have correct format and value', function () { const payload = JSON.parse(query.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); + expect(payload.id).to.be.a('string'); expect(payload.site).to.exist; expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); @@ -1265,6 +1266,18 @@ describe('IndexexchangeAdapter', function () { expect(payload.imp).to.have.lengthOf(2); }); + it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { + const bidWithIntId = utils.deepClone(DEFAULT_BANNER_VALID_BID); + bidWithIntId[0].bidderRequestId = 123456; + + request = spec.buildRequests(bidWithIntId, DEFAULT_OPTION)[0]; + + const payload = JSON.parse(request.data.r); + expect(bidWithIntId[0].bidderRequestId).to.be.a('number'); + expect(payload.id).to.equal(bidWithIntId[0].bidderRequestId.toString()); + expect(payload.id).to.be.a('string'); + }); + it('payload should not include schain when not provided', function () { const payload = JSON.parse(queryWithoutSchain.r); expect(payload.source).to.not.exist; // source object currently only written for schain @@ -1963,7 +1976,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -1989,7 +2001,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -2016,7 +2027,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -2043,7 +2053,6 @@ describe('IndexexchangeAdapter', function () { currency: 'JPY', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -2056,9 +2065,38 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); - it('should set dealId correctly', function () { + it('should prioritize bid[].dealid over bid[].ext.dealid ', function () { + const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + bidResponse.seatbid[0].bid[0].ext.dealid = 'ext-deal'; + bidResponse.seatbid[0].bid[0].dealid = 'outter-deal'; + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + netRevenue: true, + dealId: 'outter-deal', + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] + } + } + ]; + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(result[0].dealId).to.equal(expectedParse[0].dealId); + }); + + it('should not set bid[].dealid if dealid is not present', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); - bidResponse.seatbid[0].bid[0].ext.dealid = 'deal'; const expectedParse = [ { requestId: '1a2b3c4d', @@ -2071,7 +2109,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: 'deal', meta: { networkId: 50, brandId: 303325, @@ -2084,6 +2121,34 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); + it('should use set bid[].ext.dealid if bid[].dealid is not present', function () { + const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + bidResponse.seatbid[0].bid[0].ext.dealid = 'ext-deal'; + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + dealId: 'ext-deal', + netRevenue: true, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] + } + } + ]; + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + expect(result[0].dealId).to.deep.equal(expectedParse[0].dealId); + }); + it('should get correct bid response for video ad', function () { const expectedParse = [ { @@ -2096,7 +2161,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 3600, netRevenue: true, - dealId: undefined, vastUrl: 'www.abcd.com/vast', meta: { networkId: 51, @@ -2142,6 +2206,26 @@ describe('IndexexchangeAdapter', function () { expect(requestWithoutreferInfo.site.page).to.equal(options.refererInfo.referer); expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); + + it('should set bid[].ttl to seatbid[].bid[].exp value from response', function () { + const BANNER_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + const VIDEO_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_VIDEO_BID_RESPONSE); + VIDEO_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 200; + BANNER_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 100; + const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(bannerResult[0].ttl).to.equal(100); + expect(videoResult[0].ttl).to.equal(200); + }); + + it('should default bid[].ttl if seat[].bid[].exp is not in the resposne', function () { + const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(bannerResult[0].ttl).to.equal(300); + expect(videoResult[0].ttl).to.equal(3600); + }); }); describe('bidrequest consent', function () { @@ -2248,5 +2332,27 @@ describe('IndexexchangeAdapter', function () { expect(utils.deepAccess(requestWithConsent, 'user.ext.consented_providers_settings')).to.not.exist; expect(utils.deepAccess(requestWithConsent, 'user.ext.consent')).to.not.exist; }); + + it('should set coppa to 1 in config when enabled', () => { + config.setConfig({ coppa: true }) + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.equal(1); + }); + it('should not set coppa in config when disabled', () => { + config.setConfig({ coppa: false }) + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.be.undefined; + }); + it('should not set coppa when not specified in config', () => { + config.resetConfig(); + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.be.undefined; + }); }); }); From 0d845fed2c1b1a46da21a8adf034ff3c19d2ae26 Mon Sep 17 00:00:00 2001 From: Steven Francolla Date: Fri, 21 May 2021 13:27:32 -0400 Subject: [PATCH 0987/1476] Update permutiveRtdProvider.md (#6810) Updated to reflect current audience connector support. --- modules/permutiveRtdProvider.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/permutiveRtdProvider.md b/modules/permutiveRtdProvider.md index 3738c6e8be7..39f9a2aaaa5 100644 --- a/modules/permutiveRtdProvider.md +++ b/modules/permutiveRtdProvider.md @@ -20,7 +20,7 @@ pbjs.setConfig({ name: 'permutive', waitForIt: true, // should be true if there's an `auctionDelay` params: { - acBidders: ['appnexus', 'rubicon', 'ozone'] + acBidders: ['appnexus'] } }] }, @@ -31,16 +31,16 @@ pbjs.setConfig({ ## Supported Bidders The below bidders are currently support by the Permutive RTD module. Please reach out to your Permutive Account Manager to request support for any additional bidders. -| Bidder | ID | First-party segments | Audience Connector | +| Bidder | ID | Custom First-Party Segments | Audience Connector ("acBidders") | | ----------- | ---------- | -------------------- | ------------------ | | Xandr | `appnexus` | Yes | Yes | -| Magnite | `rubicon` | Yes | Yes | +| Magnite | `rubicon` | Yes | No | | Ozone | `ozone` | No | Yes | | TrustX | `trustx` | No | Yes | * **First-party segments:** When enabling the respective Activation for a segment in Permutive, this module will automatically attach that segment 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 Permutive. Permutive segments will be sent in the `permutive` key-value. -* **Audience Connector:** You'll need to define which bidder should receive Audience Connector segments. You need to include the `ID` of any bidder in the `acBidders` array. Audience Connector segments will be sent in the `p_standard` key-value. +* **Audience Connector:** You'll need to define which bidder should receive Audience Connector segments. You need to include the `ID` of any bidder in the `acBidders` array. Audience Connector segments will be sent in the `p_standard` key-value. The segments produced by Audience Connector are not supported for PMPs at this time. ## Parameters @@ -66,7 +66,7 @@ pbjs.setConfig({ name: 'permutive', waitForIt: true, params: { - acBidders: ['appnexus', 'rubicon'], + acBidders: ['appnexus'], maxSegs: 450, overwrites: { rubicon: function (bid, data, acEnabled, utils, defaultFn) { From 95fd60692eae073173e896babd5267a23bf2b63f Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Fri, 21 May 2021 10:44:03 -0700 Subject: [PATCH 0988/1476] Conversant adapter - add support for the floor module (#6798) --- modules/conversantBidAdapter.js | 26 ++++++- .../spec/modules/conversantBidAdapter_spec.js | 73 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 806f276fb72..5af399365bd 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -62,7 +62,7 @@ export const spec = { let bidurl = URL; const conversantImps = validBidRequests.map(function(bid) { - const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); + const bidfloor = getBidFloor(bid); siteId = utils.getBidIdParameter('site_id', bid.params) || siteId; pubcidName = utils.getBidIdParameter('pubcid_name', bid.params) || pubcidName; @@ -378,4 +378,28 @@ function readStoredValue(key) { return storedValue; } +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + let floor = utils.getBidIdParameter('bidfloor', bid.params); + + if (!floor && utils.isFn(bid.getFloor)) { + const floorObj = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floorObj) && !isNaN(floorObj.floor) && floorObj.currency === 'USD') { + floor = floorObj.floor; + } + } + + return floor +} + registerBidder(spec); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 1c24fb7694a..96a7f419341 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -588,4 +588,77 @@ describe('Conversant adapter tests', function() { expect(payload).to.have.deep.nested.property('user.ext.fpc', 'fghijk'); }); }); + + describe('price floor module', function() { + let bidRequest; + beforeEach(function() { + bidRequest = [utils.deepClone(bidRequests[0])]; + delete bidRequest[0].params.bidfloor; + }); + + it('obtain floor from getFloor', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 3.21 + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 3.21); + }); + + it('obtain floor from params', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 3.21 + }; + }; + bidRequest[0].params.bidfloor = 0.6; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0.6); + }); + + it('unsupported currency', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'EUR', + floor: 1.23 + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('bad floor value', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 'test' + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('empty floor object', function() { + bidRequest[0].getFloor = () => { + return {}; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('undefined floor result', function() { + bidRequest[0].getFloor = () => {}; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + }); }); From bda27a428c0b98d7bb51d9d1928b1ecf2aad9e43 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 21 May 2021 15:19:01 -0400 Subject: [PATCH 0989/1476] JW Player RTD Module - dev doc improvements (#6811) --- modules/jwplayerRtdProvider.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index 0723e8cbb6c..7fb1bb13d74 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -2,7 +2,7 @@ The purpose of this Real Time Data Provider is to allow publishers to target aga having to integrate with the Player Bidding product. This prebid module makes JW Player's video ad targeting information accessible to Bid Adapters. -#Usage for Publishers: +# Usage for Publishers: Compile the JW Player RTD Provider into your Prebid build: @@ -30,7 +30,7 @@ Lastly, include the content's media ID and/or the player's ID in the matching Ad ```javascript const adUnit = { code: '/19968336/prebid_native_example_1', - ... + ..., ortb2Imp: { ext: { data: { @@ -51,10 +51,12 @@ pbjs.que.push(function() { }); }); ``` +**Note**: The player ID is the ID of the HTML div element used when instantiating the player. +You can retrieve this ID by calling `player.id`, where player is the JW Player instance variable. **Note**: You may also include `jwTargeting` information in the prebid config's `ortb2.site.ext.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. -##Prefetching +## Prefetching In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var and set `waitForIt` to `true`: ```javascript @@ -76,7 +78,7 @@ realTimeData = { }; ``` -#Usage for Bid Adapters: +# Usage for Bid Adapters: Implement the `buildRequests` function. When it is called, the `bidRequests` param will be an array of bids. Each bid for which targeting information was found will conform to the following object structure: @@ -118,6 +120,6 @@ To view an example: **Note:** the mediaIds in the example are placeholder values; replace them with your existing IDs. -#Maintainer info +# Maintainer info Maintained by JW Player. For any questions, comments or feedback please contact Karim Mourra, karim@jwplayer.com From 849359e0dace95327dfad144e1112cb9c733f839 Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Sat, 22 May 2021 04:52:54 -0700 Subject: [PATCH 0990/1476] update OpenX adapter to support mediaTypes.video and gpid (#6813) --- modules/openxBidAdapter.js | 56 +++++++++--- modules/openxBidAdapter.md | 4 +- test/spec/modules/openxBidAdapter_spec.js | 101 +++++++++++++++++----- 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 8a455f6fa25..a398a20a5c5 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -2,8 +2,12 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import includes from 'core-js-pure/features/array/includes.js' const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const VIDEO_TARGETING = ['startdelay', 'mimes', 'minduration', 'maxduration', + 'startdelay', 'skippable', 'playbackmethod', 'api', 'protocols', 'boxingallowed', + 'linearity', 'delivery', 'protocol', 'placement', 'minbitrate', 'maxbitrate']; const BIDDER_CODE = 'openx'; const BIDDER_CONFIG = 'hb_pb'; const BIDDER_VERSION = '3.0.3'; @@ -326,7 +330,12 @@ function buildOXBannerRequest(bids, bidderRequest) { let auids = utils._map(bids, bid => bid.params.unit); queryParams.aus = utils._map(bids, bid => utils.parseSizesInput(bid.mediaTypes.banner.sizes).join(',')).join('|'); - queryParams.divIds = utils._map(bids, bid => encodeURIComponent(bid.adUnitCode)).join(','); + queryParams.divids = utils._map(bids, bid => encodeURIComponent(bid.adUnitCode)).join(','); + // gpid + queryParams.aucs = utils._map(bids, function (bid) { + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + return encodeURIComponent(gpid || '') + }).join(','); if (auids.some(auid => auid)) { queryParams.auid = auids.join(','); @@ -382,6 +391,7 @@ function buildOXVideoRequest(bid, bidderRequest) { } function generateVideoParameters(bid, bidderRequest) { + const videoMediaType = utils.deepAccess(bid, `mediaTypes.video`); let queryParams = buildCommonQueryParamsFromBids([bid], bidderRequest); let oxVideoConfig = utils.deepAccess(bid, 'params.video') || {}; let context = utils.deepAccess(bid, 'mediaTypes.video.context'); @@ -401,16 +411,35 @@ function generateVideoParameters(bid, bidderRequest) { height = parseInt(playerSize[1], 10); } - Object.keys(oxVideoConfig).forEach(function (key) { - if (key === 'openrtb') { - oxVideoConfig[key].w = width || oxVideoConfig[key].w; - oxVideoConfig[key].v = height || oxVideoConfig[key].v; - queryParams[key] = JSON.stringify(oxVideoConfig[key]); - } else if (!(key in queryParams) && key !== 'url') { - // only allow video-related attributes - queryParams[key] = oxVideoConfig[key]; - } - }); + let openRtbParams = {w: width, h: height}; + + // legacy openrtb params could be in video, openrtb, or video.openrtb + let legacyParams = bid.params.video || bid.params.openrtb || {}; + if (legacyParams.openrtb) { + legacyParams = legacyParams.openrtb; + } + // support for video object or full openrtb object + if (utils.isArray(legacyParams.imp)) { + legacyParams = legacyParams.imp[0].video; + } + Object.keys(legacyParams) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => openRtbParams[param] = legacyParams[param]); + + // 5.0 openrtb video params + Object.keys(videoMediaType) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => openRtbParams[param] = videoMediaType[param]); + + let openRtbReq = { + imp: [ + { + video: openRtbParams + } + ] + } + + queryParams['openrtb'] = JSON.stringify(openRtbReq); queryParams.auid = bid.params.unit; // override prebid config with openx config if available @@ -429,6 +458,11 @@ function generateVideoParameters(bid, bidderRequest) { queryParams.vtest = 1; } + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + queryParams.aucs = encodeURIComponent(gpid) + } + // each video bid makes a separate request enrichQueryWithFloors(queryParams, VIDEO, [bid]); diff --git a/modules/openxBidAdapter.md b/modules/openxBidAdapter.md index 965b8ee1948..68e41a93b18 100644 --- a/modules/openxBidAdapter.md +++ b/modules/openxBidAdapter.md @@ -98,7 +98,7 @@ pbjs.setConfig({ ``` # Additional Details -[Banner Ads](https://docs.openx.com/Content/developers/containers/prebid-adapter.html) +[Banner Ads](https://docs.openx.com/publishers/prebid-adapter-web/) -[Video Ads](https://docs.openx.com/Content/developers/containers/prebid-video-adapter.html) +[Video Ads](https://docs.openx.com/publishers/prebid-adapter-video/) diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 7391c8826bc..2a380277e55 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -341,7 +341,8 @@ describe('OpenxAdapter', function () { }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', - 'auctionId': 'test-auction-1' + 'auctionId': 'test-auction-1', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } } }, { 'bidder': 'openx', 'params': { @@ -356,7 +357,8 @@ describe('OpenxAdapter', function () { }, 'bidId': 'test-bid-id-2', 'bidderRequestId': 'test-bid-request-2', - 'auctionId': 'test-auction-2' + 'auctionId': 'test-auction-2', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-1' } } } }]; const bidRequestsWithPlatform = [{ @@ -450,7 +452,12 @@ describe('OpenxAdapter', function () { it('should send the adunit codes', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.divIds).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); + expect(request[0].data.divids).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); + }); + + it('should send the gpids', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + expect(request[0].data.aucs).to.equal(`${encodeURIComponent('/12345/my-gpt-tag-0')},${encodeURIComponent('/12345/my-gpt-tag-1')}`); }); it('should send ad unit ids when any are defined', function () { @@ -1238,7 +1245,8 @@ describe('OpenxAdapter', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } } }]; const mockBidderRequest = {refererInfo: {}}; @@ -1254,6 +1262,7 @@ describe('OpenxAdapter', function () { expect(dataParams.auid).to.equal('12345678'); expect(dataParams.vht).to.equal(480); expect(dataParams.vwd).to.equal(640); + expect(dataParams.aucs).to.equal(encodeURIComponent('/12345/my-gpt-tag-0')); }); it('shouldn\'t have the test parameter', function () { @@ -1313,45 +1322,97 @@ describe('OpenxAdapter', function () { expect(request[0].data.ju).to.not.equal(myUrl); }); - describe('when using the openRtb param', function () { - it('should covert the param to a JSON string', function () { - let myOpenRTBObject = {}; + describe('when using the openrtb video params', function () { + it('should parse legacy params.video.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); - expect(request[0].data.openrtb).to.equal(JSON.stringify(myOpenRTBObject)); + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); - it("should use the bidRequest's playerSize when it is available", function () { - const width = 200; - const height = 100; - const myOpenRTBObject = {v: height, w: width}; + it('should parse legacy params.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.openrtb = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy params.video', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.video = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy params.video as full openrtb', function () { + let myOpenRTBObject = {imp: [{video: {mimes: ['application/javascript']}}]}; + videoBidRequest.params.video = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy video.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.video = { + openrtb: myOpenRTBObject + }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should omit filtered values for legacy', function () { + let myOpenRTBObject = {mimes: ['application/javascript'], dont: 'use'}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse mediatypes.video', function () { + videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] + videoBidRequest.mediaTypes.video.minduration = 15 const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); + expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); + expect(openRtbRequestParams.imp[0].video.minduration).to.equal(15); + }); - expect(openRtbRequestParams.w).to.not.equal(width); - expect(openRtbRequestParams.v).to.not.equal(height); + it('should filter mediatypes.video', function () { + videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] + videoBidRequest.mediaTypes.video.minnothing = 15 + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + const openRtbRequestParams = JSON.parse(request[0].data.openrtb); + expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); + expect(openRtbRequestParams.imp[0].video.minnothing).to.equal(undefined); }); - it('should use the the openRTB\'s sizing when the bidRequest\'s playerSize is not available', function () { + it("should use the bidRequest's playerSize", function () { const width = 200; const height = 100; const myOpenRTBObject = {v: height, w: width}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; - videoBidRequest.mediaTypes.video.playerSize = undefined; - const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); - expect(openRtbRequestParams.w).to.equal(width); - expect(openRtbRequestParams.v).to.equal(height); + expect(openRtbRequestParams.imp[0].video.w).to.equal(640); + expect(openRtbRequestParams.imp[0].video.h).to.equal(480); }); }); }); @@ -1465,7 +1526,7 @@ describe('OpenxAdapter', function () { const request = spec.buildRequests([multiformatBid], mockBidderRequest); const dataParams = request[0].data; - expect(dataParams.divIds).to.have.string(multiformatBid.adUnitCode); + expect(dataParams.divids).to.have.string(multiformatBid.adUnitCode); }); }); From a0732135ad39c3f00c90c75a5af7364e44c7dc77 Mon Sep 17 00:00:00 2001 From: pratik-synacor <64602199+pratik-synacor@users.noreply.github.com> Date: Mon, 24 May 2021 13:19:20 +0530 Subject: [PATCH 0991/1476] SynacorMedia Bid Adapter: Add userID support (#6790) * SynacorMedia Bid Adapter: Add userID support * SynacorMedia Bid Adapter: User ID Support - Remove an unncessary conditional and use indexOf vs includes for IE11 support --- modules/synacormediaBidAdapter.js | 24 +++++++ .../modules/synacormediaBidAdapter_spec.js | 72 ++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index aaff637c790..7694f5d838e 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -14,6 +14,12 @@ const BLOCKED_AD_SIZES = [ '1x1', '1x2' ]; +const SUPPORTED_USER_ID_SOURCES = [ + 'liveramp.com', // Liveramp IdentityLink + 'nextroll.com', // NextRoll XID + 'verizonmedia.com', // Verizon Media ConnectID + 'pubcid.org' // PubCommon ID +]; export const spec = { code: 'synacormedia', supportedMediaTypes: [ BANNER, VIDEO ], @@ -91,6 +97,14 @@ export const spec = { deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + // User ID + if (validBidReqs[0] && validBidReqs[0].userIdAsEids && Array.isArray(validBidReqs[0].userIdAsEids)) { + const eids = this.processEids(validBidReqs[0].userIdAsEids); + if (eids.length) { + deepSetValue(openRtbBidRequest, 'user.ext.eids', eids); + } + } + if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', @@ -104,6 +118,16 @@ export const spec = { } }, + processEids: function(userIdAsEids) { + const eids = []; + userIdAsEids.forEach(function(eid) { + if (SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) > -1) { + eids.push(eid); + } + }); + return eids; + }, + buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) { let format = []; let imps = []; diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 688f1c2c090..5f3633ec311 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -191,6 +191,62 @@ describe('synacormediaBidAdapter ', function () { uspConsent: '1YYY' }; + let validBidRequestWithUserIds = { + bidId: '9876abcd', + sizes: [[300, 250], [300, 600]], + params: { + seatId: 'prebid', + tagId: '1234', + bidfloor: '0.50' + }, + userIdAsEids: [ + { + source: 'pubcid.org', + uids: [{ + id: 'cid0032l2344jskdsl3', + atype: 1 + }] + }, + { + source: 'liveramp.com', + uids: [{ + id: 'lrv39010k42dl', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + }, + { + source: 'neustar.biz', + uids: [{ + id: 'neustar809-044-23njhwer3', + atype: 1 + }] + } + ] + }; + + let expectedEids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'cid0032l2344jskdsl3', + atype: 1 + }] + }, + { + source: 'liveramp.com', + uids: [{ + id: 'lrv39010k42dl', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + } + ]; + let expectedDataImp1 = { banner: { format: [ @@ -631,7 +687,21 @@ describe('synacormediaBidAdapter ', function () { expect(req.data.id).to.equal('xyz123'); expect(req.data.regs.ext.us_privacy).to.equal('1YYY'); expect(req.data.imp).to.eql([expectedDataImp1]); - }) + }); + it('should contain user object when user ids are present in the bidder request', function () { + let req = spec.buildRequests([validBidRequestWithUserIds], bidderRequest); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.user).be.an('object'); + expect(req.data.user).to.have.property('ext'); + expect(req.data.user.ext).to.have.property('eids'); + expect(req.data.user.ext.eids).to.eql(expectedEids); + expect(req.data.imp).to.eql([expectedDataImp1]); + }); }); describe('Bid Requests with placementId should be backward compatible ', function () { From 8335d6701a0aa08f027ef0f69f205392abee3454 Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Mon, 24 May 2021 13:25:49 +0200 Subject: [PATCH 0992/1476] VIS.X: change tracking win & pending url's (#6815) --- modules/visxBidAdapter.js | 16 ++++-- test/spec/modules/visxBidAdapter_spec.js | 66 +++++++++++++++++++++--- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 63f4121724e..12004cc35af 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -10,8 +10,6 @@ const ENDPOINT_URL = BASE_URL + '/hb'; const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; const ADAPTER_SYNC_URL = BASE_URL + '/push_sync'; -const TRACK_WIN_URL = BASE_URL + '/track/win'; -const TRACK_PENDING_URL = BASE_URL + '/track/pending'; const TRACK_TIMEOUT_URL = BASE_URL + '/track/bid_timeout'; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', @@ -191,11 +189,15 @@ export const spec = { }, onSetTargeting: function(bid) { // Call '/track/pending' with the corresponding bid.requestId - utils.triggerPixel(TRACK_PENDING_URL + '?requestId=' + bid.requestId); + if (bid.ext && bid.ext.events && bid.ext.events.pending) { + utils.triggerPixel(bid.ext.events.pending); + } }, onBidWon: function(bid) { // Call '/track/win' with the corresponding bid.requestId - utils.triggerPixel(TRACK_WIN_URL + '?requestId=' + bid.requestId); + if (bid.ext && bid.ext.events && bid.ext.events.win) { + utils.triggerPixel(bid.ext.events.win); + } }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data @@ -240,9 +242,13 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithout currency: reqCurrency, netRevenue: true, ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, }; + if (serverBid.ext && serverBid.ext.prebid) { + bidResponse.ext = serverBid.ext.prebid; + } + if (!_isVideoBid(bid)) { bidResponse.ad = serverBid.adm; } else { diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 7a85d40c790..fa902480cd7 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -460,7 +460,7 @@ describe('VisxAdapter', function () { ]; const response = Object.assign({}, responses[0]); - Object.assign(response.bid[0], {'cur': 'PLN'}); + response.bid = [Object.assign({}, response.bid[0], {'cur': 'PLN'})]; const result = spec.interpretResponse({'body': {'seatbid': [response]}}, request); expect(result).to.deep.equal(expectedResponse); getConfigStub.restore(); @@ -756,6 +756,56 @@ describe('VisxAdapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); + + it('should get right ext data in bid response', function () { + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const pendingUrl = 'https://t.visx.net/track/pending/123123123'; + const winUrl = 'https://t.visx.net/track/win/53245341'; + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + 'ext': { + 'events': { + 'pending': pendingUrl, + 'win': winUrl + } + } + } + ]; + const serverResponse = Object.assign({}, responses[0]); + serverResponse.bid = [Object.assign({}, {ext: { + prebid: { + events: { + 'pending': pendingUrl, + 'win': winUrl + } + } + }}, serverResponse.bid[0])]; + const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); describe('check trackers', function () { beforeEach(function () { @@ -767,15 +817,17 @@ describe('VisxAdapter', function () { }); it('onSetTargeting', function () { - const requestId = '111'; - spec.onSetTargeting({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/pending?requestId=' + requestId)).to.equal(true); + const trackUrl = 'https://t.visx.net/track/pending/123123123'; + const bid = { ext: { events: { pending: trackUrl } } }; + spec.onSetTargeting(bid); + expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); it('onBidWon', function () { - const requestId = '111'; - spec.onBidWon({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/win?requestId=' + requestId)).to.equal(true); + const trackUrl = 'https://t.visx.net/track/win/123123123'; + const bid = { ext: { events: { win: trackUrl } } }; + spec.onBidWon(bid); + expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); it('onTimeout', function () { From 21c70b2657847eb67f7084e4394c57aacce9588b Mon Sep 17 00:00:00 2001 From: Jonathan Nadarajah <50102657+jogury@users.noreply.github.com> Date: Mon, 24 May 2021 21:36:35 +0200 Subject: [PATCH 0993/1476] Ogury Bid Adapter: add new adapter (#6729) --- modules/oguryBidAdapter.js | 135 ++++++++++ modules/oguryBidAdapter.md | 35 +++ test/spec/modules/oguryBidAdapter_spec.js | 312 ++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 modules/oguryBidAdapter.js create mode 100644 modules/oguryBidAdapter.md create mode 100644 test/spec/modules/oguryBidAdapter_spec.js diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js new file mode 100644 index 00000000000..0b4982bc8dc --- /dev/null +++ b/modules/oguryBidAdapter.js @@ -0,0 +1,135 @@ +'use strict'; + +import { BANNER } from '../src/mediaTypes.js'; +import { getAdUnitSizes, logWarn, isFn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'ogury'; +const DEFAULT_TIMEOUT = 1000; +const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; + +function isBidRequestValid(bid) { + const adUnitSizes = getAdUnitSizes(bid); + + const isValidSizes = Boolean(adUnitSizes) && adUnitSizes.length > 0; + const isValidAdUnitId = !!bid.params.adUnitId; + const isValidAssetKey = !!bid.params.assetKey; + + return (isValidSizes && isValidAdUnitId && isValidAssetKey); +} + +function buildRequests(validBidRequests, bidderRequest) { + const openRtbBidRequestBanner = { + id: bidderRequest.auctionId, + tmax: DEFAULT_TIMEOUT, + at: 2, + regs: { + ext: { + gdpr: 1 + }, + }, + site: { + domain: location.hostname + }, + user: { + ext: { + consent: '' + } + }, + imp: [] + }; + + if (bidderRequest.hasOwnProperty('gdprConsent') && + bidderRequest.gdprConsent.hasOwnProperty('gdprApplies')) { + openRtbBidRequestBanner.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + } + + if (bidderRequest.hasOwnProperty('gdprConsent') && + bidderRequest.gdprConsent.hasOwnProperty('consentString') && + bidderRequest.gdprConsent.consentString.length > 0) { + openRtbBidRequestBanner.user.ext.consent = bidderRequest.gdprConsent.consentString + } + + validBidRequests.forEach((bidRequest) => { + const sizes = getAdUnitSizes(bidRequest) + .map(size => ({ w: size[0], h: size[1] })); + + if (bidRequest.hasOwnProperty('mediaTypes') && + bidRequest.mediaTypes.hasOwnProperty('banner')) { + openRtbBidRequestBanner.site.id = bidRequest.params.assetKey; + + openRtbBidRequestBanner.imp.push({ + id: bidRequest.bidId, + tagid: bidRequest.params.adUnitId, + bidfloor: getFloor(bidRequest), + banner: { + format: sizes + } + }); + } + }); + + return { + method: 'POST', + url: BID_HOST, + data: openRtbBidRequestBanner + }; +} + +function interpretResponse(openRtbBidResponse) { + if (!openRtbBidResponse || + !openRtbBidResponse.body || + typeof openRtbBidResponse.body != 'object' || + Object.keys(openRtbBidResponse.body).length === 0) { + logWarn('no response or body is malformed'); + return []; + } + + const bidResponses = []; + + openRtbBidResponse.body.seatbid.forEach((seatbid) => { + seatbid.bid.forEach((bid) => { + let bidResponse = { + requestId: bid.impid, + cpm: bid.price, + currency: 'USD', + width: bid.w, + height: bid.h, + creativeId: bid.id, + netRevenue: true, + ttl: 60, + meta: { + advertiserDomains: bid.adomain + } + }; + + bidResponse.ad = bid.adm; + + bidResponses.push(bidResponse); + }); + }); + return bidResponses; +} + +function getFloor(bid) { + if (!isFn(bid.getFloor)) { + return 0; + } + let floorResult = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: '*' + }); + return floorResult.currency === 'USD' ? floorResult.floor : 0; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + getFloor +} + +registerBidder(spec); diff --git a/modules/oguryBidAdapter.md b/modules/oguryBidAdapter.md new file mode 100644 index 00000000000..00896762dc4 --- /dev/null +++ b/modules/oguryBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +``` +Module Name: Ogury Bidder Adapter +Module Type: Bidder Adapter +Maintainer: web.inventory@ogury.co +``` + +# Description + +Module that connects to Ogury's SSP solution +Ogury bid adapter supports Banner media type. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "ogury", + params: { + assetKey: 'OGY-CA41D116484F', + adUnitId: '2c4d61d0-90aa-0139-0cda-0242ac120004' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js new file mode 100644 index 00000000000..d08bf2c8430 --- /dev/null +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -0,0 +1,312 @@ +import { expect } from 'chai'; +import { spec } from 'modules/oguryBidAdapter'; +import { deepClone } from 'src/utils.js'; + +const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; + +describe('OguryBidAdapter', function () { + let bidRequests; + let bidderRequest; + + bidRequests = [ + { + adUnitCode: 'adUnitCode', + auctionId: 'auctionId', + bidId: 'bidId', + bidder: 'ogury', + params: { + assetKey: 'OGY-assetkey', + adUnitId: 'adunitId', + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + getFloor: ({ size, currency, mediaType }) => { + const floorResult = { + currency: 'USD', + floor: 0 + }; + + if (mediaType === 'banner') { + floorResult.floor = 4; + } else { + floorResult.floor = 1000; + } + + return floorResult; + }, + transactionId: 'transactionId' + }, + { + adUnitCode: 'adUnitCode2', + auctionId: 'auctionId', + bidId: 'bidId2', + bidder: 'ogury', + params: { + assetKey: 'OGY-assetkey', + adUnitId: 'adunitId2' + }, + mediaTypes: { + banner: { + sizes: [[600, 500]] + } + }, + transactionId: 'transactionId2' + }, + ]; + + bidderRequest = { + auctionId: bidRequests[0].auctionId, + gdprConsent: {consentString: 'myConsentString', vendorData: {}, gdprApplies: true}, + }; + + describe('isBidRequestValid', function () { + it('should validate correct bid', () => { + let validBid = deepClone(bidRequests[0]); + + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('should not validate incorrect bid', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.sizes; + delete invalidBid.mediaTypes; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should not validate bid if adunit is not present', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.params.adUnitId; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should not validate bid if assetKet is not present', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.params.assetKey; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should validate bid if getFloor is not present', () => { + let invalidBid = deepClone(bidRequests[1]); + delete invalidBid.getFloor; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const defaultTimeout = 1000; + const expectedRequestObject = { + id: bidRequests[0].auctionId, + at: 2, + tmax: defaultTimeout, + imp: [{ + id: bidRequests[0].bidId, + tagid: bidRequests[0].params.adUnitId, + bidfloor: 4, + banner: { + format: [{ + w: 300, + h: 250 + }] + } + }, { + id: bidRequests[1].bidId, + tagid: bidRequests[1].params.adUnitId, + bidfloor: 0, + banner: { + format: [{ + w: 600, + h: 500 + }] + } + }], + regs: { + ext: { + gdpr: 1 + }, + }, + site: { + id: bidRequests[0].params.assetKey, + domain: window.location.hostname, + }, + user: { + ext: { + consent: bidderRequest.gdprConsent.consentString + }, + } + }; + + it('sends bid request to ENDPOINT via POST', function () { + const validBidRequests = deepClone(bidRequests) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.url).to.equal(BID_HOST); + expect(request.method).to.equal('POST'); + }); + + it('bid request object should be conform', function () { + const validBidRequests = deepClone(bidRequests) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestObject); + expect(request.data.regs.ext.gdpr).to.be.a('number'); + }); + + it('should not add gdpr infos if not present', () => { + const bidderRequestWithoutGdpr = { + ...bidderRequest, + gdprConsent: {}, + } + const expectedRequestObjectWithoutGdpr = { + ...expectedRequestObject, + regs: { + ext: { + gdpr: 1 + }, + }, + user: { + ext: { + consent: '' + }, + } + }; + + const validBidRequests = bidRequests + + const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); + expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); + expect(request.data.regs.ext.gdpr).to.be.a('number'); + }); + + it('should handle bidFloor undefined', () => { + const expectedRequestWithUndefinedFloor = { + ...expectedRequestObject + }; + + const validBidRequests = deepClone(bidRequests); + validBidRequests[1] = { + ...validBidRequests[1], + getFloor: undefined + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithUndefinedFloor); + }); + + it('should handle bidFloor when is not function', () => { + const expectedRequestWithNotAFunctionFloor = { + ...expectedRequestObject + }; + + let validBidRequests = deepClone(bidRequests); + validBidRequests[1] = { + ...validBidRequests[1], + getFloor: 'getFloor' + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithNotAFunctionFloor); + }); + + it('should handle bidFloor when currency is not USD', () => { + const expectedRequestWithUnsupportedFloorCurrency = deepClone(expectedRequestObject) + expectedRequestWithUnsupportedFloorCurrency.imp[0].bidfloor = 0; + let validBidRequests = deepClone(bidRequests); + validBidRequests[0] = { + ...validBidRequests[0], + getFloor: ({ size, currency, mediaType }) => { + return { + currency: 'EUR', + floor: 4 + } + } + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithUnsupportedFloorCurrency); + }); + }); + + describe('interpretResponse', function () { + let openRtbBidResponse = { + body: { + id: 'id_of_bid_response', + seatbid: [{ + bid: [{ + id: 'advertId', + impid: 'bidId', + price: 100, + nurl: 'url', + adm: `test creative
cookies
`, + adomain: ['renault.fr'], + w: 300, + h: 250 + }, { + id: 'advertId2', + impid: 'bidId2', + price: 150, + nurl: 'url2', + adm: `test creative
cookies
`, + adomain: ['peugeot.fr'], + w: 600, + h: 500 + }], + }] + } + }; + + it('should correctly interpret bidResponse', () => { + let expectedInterpretedBidResponse = [{ + requestId: openRtbBidResponse.body.seatbid[0].bid[0].impid, + cpm: openRtbBidResponse.body.seatbid[0].bid[0].price, + currency: 'USD', + width: openRtbBidResponse.body.seatbid[0].bid[0].w, + height: openRtbBidResponse.body.seatbid[0].bid[0].h, + ad: openRtbBidResponse.body.seatbid[0].bid[0].adm, + ttl: 60, + creativeId: openRtbBidResponse.body.seatbid[0].bid[0].id, + netRevenue: true, + meta: { + advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain + } + }, { + requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, + cpm: openRtbBidResponse.body.seatbid[0].bid[1].price, + currency: 'USD', + width: openRtbBidResponse.body.seatbid[0].bid[1].w, + height: openRtbBidResponse.body.seatbid[0].bid[1].h, + ad: openRtbBidResponse.body.seatbid[0].bid[1].adm, + ttl: 60, + creativeId: openRtbBidResponse.body.seatbid[0].bid[1].id, + netRevenue: true, + meta: { + advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain + } + }] + + let request = spec.buildRequests(bidRequests, bidderRequest); + let result = spec.interpretResponse(openRtbBidResponse, request); + + expect(result).to.deep.equal(expectedInterpretedBidResponse) + }); + + it('should return empty array if error during parsing', () => { + const wrongOpenRtbBidReponse = 'wrong data' + let request = spec.buildRequests(bidRequests, bidderRequest); + let result = spec.interpretResponse(wrongOpenRtbBidReponse, request); + + expect(result).to.be.instanceof(Array); + expect(result.length).to.equal(0) + }) + }); +}); From 01dc3c6e989b7ebadc8ab41834fbf16f7966776c Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Mon, 24 May 2021 17:34:06 -0400 Subject: [PATCH 0994/1476] ParrableIdSystem: Store third-party cookie support status in first-party cookie (#6741) * Add tpcSupport, read it from xxhr and store it in compound cookie * Parse a cookie boolean number (0, 1) as a boolean value * Improve conditions * Add tests * Tests passing * Read the cookie splitting parrableId and params (tpc, tpcUntil) * Adapt tests * Revert linting in test task * Convert Date.now to seconds on reading cookie * Add tests * Replace Math.trunc with Math.floor * kick off test * unfollowed local fork, kick off test * Increase cookie expire time to 20s Co-authored-by: Victor Co-authored-by: Chris Huie <3444727+ChrisHuie@users.noreply.github.com> --- modules/parrableIdSystem.js | 61 ++++++--- test/spec/modules/parrableIdSystem_spec.js | 149 ++++++++++++++++++++- 2 files changed, 191 insertions(+), 19 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 826dd9a933a..36ac7070ec1 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -34,25 +34,36 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); - // unpack a value of 1 as true - parrableId[pair[0]] = +pair[1] === 1 ? true : pair[1]; + if (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) { // unpack a value of 0 or 1 as boolean + parrableId[pair[0]] = Boolean(+pair[1]); + } else if (!isNaN(pair[1])) { // convert to number if is a number + parrableId[pair[0]] = +pair[1] + } else { + parrableId[pair[0]] = pair[1] + } }); return parrableId; } -function serializeParrableId(parrableId) { +function serializeParrableId(parrableIdAndParams) { let components = []; - if (parrableId.eid) { - components.push('eid:' + parrableId.eid); + if (parrableIdAndParams.eid) { + components.push('eid:' + parrableIdAndParams.eid); } - if (parrableId.ibaOptout) { + if (parrableIdAndParams.ibaOptout) { components.push('ibaOptout:1'); } - if (parrableId.ccpaOptout) { + if (parrableIdAndParams.ccpaOptout) { components.push('ccpaOptout:1'); } + if (parrableIdAndParams.tpcSupport !== undefined) { + const tpcSupportComponent = parrableIdAndParams.tpcSupport === true ? 'tpc:1' : 'tpc:0'; + const tpcUntil = `tpcUntil:${parrableIdAndParams.tpcUntil}`; + components.push(tpcSupportComponent); + components.push(tpcUntil); + } return components.join(','); } @@ -84,14 +95,21 @@ function encodeBase64UrlSafe(base64) { function readCookie() { const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); if (parrableIdStr) { - return deserializeParrableId(decodeURIComponent(parrableIdStr)); + const parsedCookie = deserializeParrableId(decodeURIComponent(parrableIdStr)); + const { tpc, tpcUntil, ...parrableId } = parsedCookie; + let { eid, ibaOptout, ccpaOptout, ...params } = parsedCookie; + + if ((Date.now() / 1000) >= tpcUntil) { + params.tpc = undefined; + } + return { parrableId, params }; } return null; } -function writeCookie(parrableId) { - if (parrableId) { - const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); +function writeCookie(parrableIdAndParams) { + if (parrableIdAndParams) { + const parrableIdStr = encodeURIComponent(serializeParrableId(parrableIdAndParams)); storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); } } @@ -175,10 +193,14 @@ function shouldFilterImpression(configParams, parrableId) { return isBlocked() || !isAllowed(); } +function epochFromTtl(ttl) { + return Math.floor((Date.now() / 1000) + ttl); +} + function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; - let parrableId = readCookie(); + let { parrableId, params } = readCookie() || {}; if (!parrableId) { parrableId = readLegacyCookies(); migrateLegacyCookies(parrableId); @@ -188,12 +210,13 @@ function fetchId(configParams, gdprConsentData) { return null; } - const eid = (parrableId) ? parrableId.eid : null; + const eid = parrableId ? parrableId.eid : null; const refererInfo = getRefererInfo(); + const tpcSupport = params ? params.tpc : null const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; - const partners = configParams.partners || configParams.partner + const partners = configParams.partners || configParams.partner; const trackers = typeof partners === 'string' ? partners.split(',') : partners; @@ -203,7 +226,8 @@ function fetchId(configParams, gdprConsentData) { trackers, url: refererInfo.referer, prebidVersion: '$prebid.version$', - isIframe: utils.inIframe() + isIframe: utils.inIframe(), + tpcSupport }; const searchParams = { @@ -229,6 +253,7 @@ function fetchId(configParams, gdprConsentData) { const callbacks = { success: response => { let newParrableId = parrableId ? utils.deepClone(parrableId) : {}; + let newParams = {}; if (response) { try { let responseObj = JSON.parse(response); @@ -242,12 +267,16 @@ function fetchId(configParams, gdprConsentData) { if (responseObj.ibaOptout === true) { newParrableId.ibaOptout = true; } + if (responseObj.tpcSupport !== undefined) { + newParams.tpcSupport = responseObj.tpcSupport; + newParams.tpcUntil = epochFromTtl(responseObj.tpcSupportTtl); + } } } catch (error) { utils.logError(error); cb(); } - writeCookie(newParrableId); + writeCookie({ ...newParrableId, ...newParams }); cb(newParrableId); } else { utils.logError('parrableId: ID fetch returned an empty result'); diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 3f517a66c68..c18574aec7b 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -12,6 +12,7 @@ import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const EXPIRE_COOKIE_TIME = 20000; const P_COOKIE_NAME = '_parrable_id'; const P_COOKIE_EID = '01.1563917337.test-eid'; const P_XHR_EID = '01.1588030911.test-new-eid' @@ -21,6 +22,7 @@ const P_CONFIG_MOCK = { partners: 'parrable_test_partner_123,parrable_test_partner_456' } }; +const RESPONSE_HEADERS = { 'Content-Type': 'application/json' }; function getConfigMock() { return { @@ -57,6 +59,11 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { str += ',ccpaOptout:1'; } + if (parrableId.tpc !== undefined) { + const tpcSupportComponent = parrableId.tpc === true ? 'tpc:1' : 'tpc:0'; + str += `,${tpcSupportComponent}`; + str += `,tpcUntil:${parrableId.tpcUntil}`; + } return str; } @@ -65,7 +72,7 @@ function writeParrableCookie(parrableId) { storage.setCookie( P_COOKIE_NAME, cookieValue, - (new Date(Date.now() + 5000).toUTCString()), + (new Date(Date.now() + EXPIRE_COOKIE_TIME).toUTCString()), 'lax' ); } @@ -125,7 +132,6 @@ describe('Parrable ID System', function() { { 'Content-Type': 'text/plain' }, JSON.stringify({ eid: P_XHR_EID }) ); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({ eid: P_XHR_EID }); @@ -242,6 +248,143 @@ describe('Parrable ID System', function() { }) }); }); + + describe('third party cookie support status', function () { + let logErrorStub; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + callbackSpy.resetHistory(); + removeParrableCookie(); + }); + + afterEach(function() { + logErrorStub.restore(); + }); + + describe('when getting tpcSupport from XHR response', function () { + let request; + let dateNowStub; + const dateNowMock = Date.now(); + const tpcSupportTtl = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + after(() => { + dateNowStub.restore(); + }); + + it('should set tpcSupport: true and tpcUntil in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: true, tpcSupportTtl }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:1,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + + it('should set tpcSupport: false and tpcUntil in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + + it('should not set tpcSupport in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID) + ); + }); + }); + + describe('when getting tpcSupport from cookie', function () { + let request; + let dateNowStub; + const dateNowMock = Date.now(); + const tpcSupportTtl = dateNowMock; + const tpcUntilExpired = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + after(() => { + dateNowStub.restore(); + }); + + it('should send tpcSupport in the XHR', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpc: true, + tpcUntil: (dateNowMock / 1000) + 1 + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(true); + }); + + it('should unset tpcSupport from cookie when tpcUntil reached', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpcSupport: true, + tpcUntil: tpcUntilExpired + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(undefined); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + }); + }); }); describe('parrableIdSystem.decode()', function() { @@ -529,7 +672,7 @@ describe('Parrable ID System', function() { }); }); - describe('partners parsing', () => { + describe('partners parsing', function () { let callbackSpy = sinon.spy(); const partnersTestCase = [ From 49f5134c555571e1c3db4cf1b19d8706f533978a Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Tue, 25 May 2021 11:46:04 +0200 Subject: [PATCH 0995/1476] update ID5 docs for PD string links (#6824) --- modules/id5IdSystem.md | 4 ++-- modules/userId/userId.md | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 6a662361492..8ffe29e091f 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -1,6 +1,6 @@ # ID5 Universal ID -The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://wiki.id5.io/x/BIAZ). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. +The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://support.id5.io/portal/en/kb/articles/prebid-js-user-id-module). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. ## ID5 Universal ID Registration @@ -48,7 +48,7 @@ pbjs.setConfig({ | name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | | params | Required | Object | Details for the ID5 Universal ID. | | | params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | -| params.pd | Optional | String | Publisher-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/x/BIAZ) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| 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` | diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 5d78a447572..5cf4a6c2128 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -36,7 +36,7 @@ pbjs.setConfig({ name: "id5Id", params: { partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: "some-pd-string" // See https://wiki.id5.io/x/BIAZ for details + pd: "some-pd-string" // See https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5 for details }, storage: { type: "html5", // ID5 requires html5 @@ -107,7 +107,7 @@ pbjs.setConfig({ type: "cookie", name: "merkleId", expires: 30 - } + } },{ name: 'uid2' } @@ -126,7 +126,7 @@ pbjs.setConfig({ },{ name: 'flocId', params: { - token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" + token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" } }], syncDelay: 5000, @@ -192,7 +192,7 @@ pbjs.setConfig({ name: 'id5Id', params: { partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: 'some-pd-string' // See https://wiki.id5.io/x/BIAZ for details + pd: 'some-pd-string' // See https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5 for details }, storage: { type: 'html5', @@ -224,7 +224,7 @@ pbjs.setConfig({ type: "html5", name: "merkleId", expires: 30 - } + } }, { name: 'admixerId', params: { From 5a337021215b609faf19180fb5143c53293d8e36 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 25 May 2021 12:11:17 +0200 Subject: [PATCH 0996/1476] Adagio Bid Adapter: decrease adapter weight (#6699) --- allowedModules.js | 1 - modules/adagioBidAdapter.js | 79 ++++++---------------- package-lock.json | 5 -- package.json | 1 - test/spec/modules/adagioBidAdapter_spec.js | 2 + 5 files changed, 24 insertions(+), 64 deletions(-) diff --git a/allowedModules.js b/allowedModules.js index d8e8b69f593..2d23b35c501 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -12,7 +12,6 @@ module.exports = { 'modules': [ ...sharedWhiteList, 'criteo-direct-rsa-validate', - 'jsencrypt', 'crypto-js', 'live-connect' // Maintained by LiveIntent : https://github.com/liveintent-berlin/live-connect/ ], diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 66653567dab..aed1a1682dc 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -3,33 +3,27 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { loadExternalScript } from '../src/adloader.js'; -import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; -import sha256 from 'crypto-js/sha256.js'; +import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { createEidsArray } from './userId/eids.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; - -export const BIDDER_CODE = 'adagio'; -export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.10.0'; -export const FEATURES_VERSION = '1'; +const BIDDER_CODE = 'adagio'; +const LOG_PREFIX = 'Adagio:'; +export const VERSION = '2.11.0'; +const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; -export const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; -export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; -export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; -export const GVLID = 617; +const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; +const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; +const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; +const GVLID = 617; export const storage = getStorageManager(GVLID, 'adagio'); export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; -export const MAX_SESS_DURATION = 30 * 60 * 1000; -export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 -jFSzIbGJJyg3cKqvtE6A0iaz9PkIdJIvSSSNrmJv+lRGKPEyRA/VnzJIieL39Ngl -t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj -pV6EP3MTLosuUEpLaQIDAQAB ------END PUBLIC KEY-----`; +const MAX_SESS_DURATION = 30 * 60 * 1000; +const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp'; +const ADAGIO_PUBKEY_E = 65537; // This provide a whitelist and a basic validation // of OpenRTB 2.5 options used by the Adagio SSP. @@ -81,10 +75,7 @@ export function adagioScriptFromLocalStorageCb(ls) { const hash = r[2]; const content = r[3]; - var jsEncrypt = new JSEncrypt(); - jsEncrypt.setPublicKey(ADAGIO_PUBKEY); - - if (jsEncrypt.verify(content, hash, sha256)) { + if (verify(content, hash, ADAGIO_PUBKEY, ADAGIO_PUBKEY_E)) { utils.logInfo(`${LOG_PREFIX} start script.`); Function(ls)(); // eslint-disable-line no-new-func } else { @@ -524,19 +515,8 @@ function autoDetectAdUnitElementId(adUnitCode) { function autoDetectEnvironment() { const device = _features.getDevice(); - let environment; - switch (device) { - case 2: - environment = 'desktop'; - break; - case 4: - environment = 'mobile'; - break; - case 5: - environment = 'tablet'; - break; - }; - return environment; + const map = { 2: 'desktop', 4: 'mobile', 5: 'tablet' }; + return map[device] || 'unknown'; }; function supportIObs() { @@ -653,25 +633,12 @@ function _getGdprConsent(bidderRequest) { allowAuctionWithoutConsent } = bidderRequest.gdprConsent; - const consent = {}; - - if (apiVersion !== undefined) { - consent.apiVersion = apiVersion; - } - - if (consentString !== undefined) { - consent.consentString = consentString; - } - - if (gdprApplies !== undefined) { - consent.consentRequired = (gdprApplies) ? 1 : 0; - } - - if (allowAuctionWithoutConsent !== undefined) { - consent.allowAuctionWithoutConsent = allowAuctionWithoutConsent ? 1 : 0; - } - - return consent; + return utils.cleanObj({ + apiVersion, + consentString, + consentRequired: gdprApplies ? 1 : 0, + allowAuctionWithoutConsent: allowAuctionWithoutConsent ? 1 : 0 + }); } function _getCoppa() { @@ -685,9 +652,7 @@ function _getUspConsent(bidderRequest) { } function _getSchain(bidRequest) { - if (utils.deepAccess(bidRequest, 'schain')) { - return bidRequest.schain; - } + return utils.deepAccess(bidRequest, 'schain'); } function _getEids(bidRequest) { diff --git a/package-lock.json b/package-lock.json index 3c4e59cdba7..a6a58a530de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15084,11 +15084,6 @@ } } }, - "jsencrypt": { - "version": "3.0.0-rc.1", - "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.0.0-rc.1.tgz", - "integrity": "sha512-gcvGaqerlUJy1Kq6tNgPYteVEoWNemu+9hBe2CdsCIz4rVcwjoTQ72iD1W76/PRMlnkzG0yVh7nwOOMOOUfKmg==" - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", diff --git a/package.json b/package.json index f7176162f8e..66f1976d4ce 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,6 @@ "dset": "2.0.1", "express": "^4.15.4", "fun-hooks": "^0.9.9", - "jsencrypt": "^3.0.0-rc.1", "just-clone": "^1.0.2", "live-connect-js": "2.0.0" } diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 6ee42e47950..6e0b82cbab8 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -579,6 +579,7 @@ describe('Adagio bid adapter', () => { const expected = { consentString, + allowAuctionWithoutConsent: 0, consentRequired: 1, apiVersion: 2 }; @@ -615,6 +616,7 @@ describe('Adagio bid adapter', () => { const expected = { consentString, consentRequired: 0, + allowAuctionWithoutConsent: 0, apiVersion: 2 }; From 585c7107645ee9f79befb9d17d2cf8c6000b0d00 Mon Sep 17 00:00:00 2001 From: pm-shashank-jain <40654031+pm-shashank-jain@users.noreply.github.com> Date: Tue, 25 May 2021 16:11:29 +0530 Subject: [PATCH 0997/1476] PubMatic Adapter : Support For Video Params from AdUnit MediaTypes (#6773) --- modules/pubmaticBidAdapter.js | 128 +++--- test/spec/modules/pubmaticBidAdapter_spec.js | 415 +++++++++++++++++-- 2 files changed, 450 insertions(+), 93 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index ff934204b43..79356415ddf 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -48,7 +48,8 @@ const VIDEO_CUSTOM_PARAMS = { 'linearity': DATA_TYPES.NUMBER, 'placement': DATA_TYPES.NUMBER, 'minbitrate': DATA_TYPES.NUMBER, - 'maxbitrate': DATA_TYPES.NUMBER + 'maxbitrate': DATA_TYPES.NUMBER, + 'skip': DATA_TYPES.NUMBER } const NATIVE_ASSETS = { @@ -167,6 +168,12 @@ const BB_RENDERER = { } }; +const MEDIATYPE = [ + BANNER, + VIDEO, + NATIVE +] + let publisherId = 0; let isInvalidNativeRequest = false; let NATIVE_ASSET_ID_TO_KEY_MAP = {}; @@ -531,7 +538,7 @@ function _createBannerRequest(bid) { } function _createVideoRequest(bid) { - var videoData = bid.params.video; + var videoData = utils.mergeDeep(utils.deepAccess(bid.mediaTypes, 'video'), bid.params.video); var videoObj; if (videoData !== UNDEFINED) { @@ -549,11 +556,6 @@ function _createVideoRequest(bid) { videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0], 10); videoObj.h = parseInt(bid.mediaTypes.video.playerSize[1], 10); } - if (bid.params.video.hasOwnProperty('skippable')) { - videoObj.ext = { - 'video_skippable': bid.params.video.skippable ? 1 : 0 - }; - } } else { videoObj = UNDEFINED; utils.logWarn(LOG_WARN_PREFIX + 'Error: Video config params missing for adunit: ' + bid.params.adUnit + ' with mediaType set as video. Ignoring video impression in the adunit.'); @@ -581,6 +583,28 @@ function _addPMPDealsInImpression(impObj, bid) { } } +function _addDealCustomTargetings(imp, bid) { + var dctr = ''; + var dctrLen; + if (bid.params.dctr) { + dctr = bid.params.dctr; + if (utils.isStr(dctr) && dctr.length > 0) { + var arr = dctr.split('|'); + dctr = ''; + arr.forEach(val => { + dctr += (val.length > 0) ? (val.trim() + '|') : ''; + }); + dctrLen = dctr.length; + if (dctr.substring(dctrLen, dctrLen - 1) === '|') { + dctr = dctr.substring(0, dctrLen - 1); + } + imp.ext['key_val'] = dctr.trim() + } else { + utils.logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); + } + } +} + function _createImpressionObject(bid, conf) { var impObj = {}; var bannerObj; @@ -602,7 +626,7 @@ function _createImpressionObject(bid, conf) { }; _addPMPDealsInImpression(impObj, bid); - + _addDealCustomTargetings(impObj, bid); if (bid.hasOwnProperty('mediaTypes')) { for (mediaTypes in bid.mediaTypes) { switch (mediaTypes) { @@ -779,22 +803,28 @@ function _handleEids(payload, validBidRequests) { } } -function _checkMediaType(adm, newBid) { +function _checkMediaType(bid, newBid) { // Create a regex here to check the strings - var admStr = ''; - var videoRegex = new RegExp(/VAST\s+version/); - if (adm.indexOf('span class="PubAPIAd"') >= 0) { - newBid.mediaType = BANNER; - } else if (videoRegex.test(adm)) { - newBid.mediaType = VIDEO; + if (bid.ext && bid.ext['BidType'] != undefined) { + newBid.mediaType = MEDIATYPE[bid.ext.BidType]; } else { - try { - admStr = JSON.parse(adm.replace(/\\/g, '')); - if (admStr && admStr.native) { - newBid.mediaType = NATIVE; + utils.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/); + if (adm.indexOf('span class="PubAPIAd"') >= 0) { + newBid.mediaType = BANNER; + } else if (videoRegex.test(adm)) { + newBid.mediaType = VIDEO; + } else { + try { + admStr = JSON.parse(adm.replace(/\\/g, '')); + if (admStr && admStr.native) { + newBid.mediaType = NATIVE; + } + } catch (e) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native reponse for ad response: ' + adm); } - } catch (e) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native reponse for ad response: ' + adm); } } } @@ -884,38 +914,6 @@ function _blockedIabCategoriesValidation(payload, blockedIabCategories) { } } -function _handleDealCustomTargetings(payload, dctrArr, validBidRequests) { - var dctr = ''; - var dctrLen; - // set dctr value in site.ext, if present in validBidRequests[0], else ignore - if (dctrArr.length > 0) { - if (validBidRequests[0].params.hasOwnProperty('dctr')) { - dctr = validBidRequests[0].params.dctr; - if (utils.isStr(dctr) && dctr.length > 0) { - var arr = dctr.split('|'); - dctr = ''; - arr.forEach(val => { - dctr += (val.length > 0) ? (val.trim() + '|') : ''; - }); - dctrLen = dctr.length; - if (dctr.substring(dctrLen, dctrLen - 1) === '|') { - dctr = dctr.substring(0, dctrLen - 1); - } - payload.site.ext = { - key_val: dctr.trim() - } - } else { - utils.logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); - } - if (dctrArr.length > 1) { - utils.logWarn(LOG_WARN_PREFIX + 'dctr value found in more than 1 adunits. Value from 1st adunit will be picked. Ignoring values from subsequent adunits'); - } - } else { - utils.logWarn(LOG_WARN_PREFIX + 'dctr value not found in 1st adunit, ignoring values from subsequent adunits'); - } - } -} - function _assignRenderer(newBid, request) { let bidParams, context, adUnitCode; if (request.bidderRequest && request.bidderRequest.bids) { @@ -950,22 +948,17 @@ export const spec = { return false; } // video ad validation - if (bid.params.hasOwnProperty('video')) { - if (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0) { + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (!bid.mediaTypes.video.mimes || (bid.params.video && (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0))) { utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); return false; } - if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { - if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); - return false; - } - if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { - utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); - return false; - } - } else { - utils.logError(`${LOG_WARN_PREFIX}: mediaTypes or mediaTypes.video is not specified. Rejecting bid: `, bid); + if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { + utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); + return false; + } + if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { + utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); return false; } } @@ -1090,7 +1083,6 @@ export const spec = { utils.deepSetValue(payload, 'regs.coppa', 1); } - _handleDealCustomTargetings(payload, dctrArr, validBidRequests); _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); _handleFlocId(payload, validBidRequests); @@ -1164,7 +1156,7 @@ export const spec = { if (parsedRequest.imp && parsedRequest.imp.length > 0) { parsedRequest.imp.forEach(req => { if (bid.impid === req.id) { - _checkMediaType(bid.adm, newBid); + _checkMediaType(bid, newBid); switch (newBid.mediaType) { case BANNER: break; diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index e8948fdc2d3..ae0b351c367 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -791,6 +791,151 @@ describe('PubMatic adapter', function () { isValid = spec.isBidRequestValid(validBid); expect(isValid).to.equal(true); }); + + it('should check for context if video is present', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }) + + it('should return false if context is not present in video', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'w': 640, + 'h': 480, + 'protocols': [1, 2, 5], + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }) + + it('should check for mimes if video is present', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }) + + it('should return false if mimes is not present in video', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }) }); describe('Request formation', function () { @@ -858,8 +1003,6 @@ describe('PubMatic adapter', function () { expect(data.site.domain).to.be.a('string'); // domain should be set expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude @@ -879,6 +1022,7 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); @@ -967,7 +1111,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.site).to.not.exist; sandbox.restore(); }); @@ -994,7 +1137,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.app.content).to.deep.equal(content); expect(data.site).to.not.exist; sandbox.restore(); @@ -1026,7 +1168,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.app.content).to.deep.equal(appContent); expect(data.site).to.not.exist; sandbox.restore(); @@ -1043,8 +1184,6 @@ describe('PubMatic adapter', function () { expect(data.site.domain).to.be.a('string'); // domain should be set expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude @@ -1063,6 +1202,7 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.w).to.equal(728); // width expect(data.imp[0].banner.h).to.equal(90); // height expect(data.imp[0].banner.format).to.deep.equal([{w: 160, h: 600}]); + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); }); @@ -2167,7 +2307,6 @@ describe('PubMatic adapter', function () { let data = JSON.parse(request.data); expect(data.imp[0].video).to.exist; expect(data.imp[0].tagid).to.equal('Div1'); - expect(data.imp[0].video.ext['video_skippable']).to.equal(videoBidRequests[0].params.video.skippable ? 1 : 0); expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].params.video['mimes'][0]); expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].params.video['mimes'][1]); @@ -2237,7 +2376,6 @@ describe('PubMatic adapter', function () { // video imp object check expect(data.imp[1].video).to.exist; expect(data.imp[1].tagid).to.equal('Div1'); - expect(data.imp[1].video.ext['video_skippable']).to.equal(multipleMediaRequests[1].params.video.skippable ? 1 : 0); expect(data.imp[1]['video']['mimes']).to.exist.and.to.be.an('array'); expect(data.imp[1]['video']['mimes'][0]).to.equal(multipleMediaRequests[1].params.video['mimes'][0]); expect(data.imp[1]['video']['mimes'][1]).to.equal(multipleMediaRequests[1].params.video['mimes'][1]); @@ -2556,6 +2694,93 @@ describe('PubMatic adapter', function () { expect(data.video).to.exist; expect(data.native).to.not.exist; }); + + it('should build video impression if video params are present in adunit.mediaTypes instead of bid.params', function() { + let videoReq = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'adc36682-887c-41e9-9848-8b72c08332c0', + 'sizes': [ + [640, 480] + ], + 'bidId': '21b59b1353ba82', + 'bidderRequestId': '1a08245305e6dd', + 'auctionId': 'bad3a743-7491-4d19-9a96-b0a69dd24a67', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + let request = spec.buildRequests(videoReq, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.video).to.exist; + }); + + it('should build video impression with overwriting video params present in adunit.mediaTypes with bid.params', function() { + let videoReq = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'adc36682-887c-41e9-9848-8b72c08332c0', + 'sizes': [ + [640, 480] + ], + 'bidId': '21b59b1353ba82', + 'bidderRequestId': '1a08245305e6dd', + 'auctionId': 'bad3a743-7491-4d19-9a96-b0a69dd24a67', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + let request = spec.buildRequests(videoReq, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.video.linearity).to.equal(1); + }); }); it('Request params dctr check', function () { @@ -2565,17 +2790,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', - kadfloor: '1.2', - pmzoneid: 'aabc, ddef', - kadpageurl: 'www.publisher.com', - yob: '1986', - gender: 'M', - lat: '12.3', - lon: '23.7', - wiid: '1234567890', - profId: '100', - verId: '200', - currency: 'AUD', dctr: 'key1=val1|key2=val2,!val3' }, placementCode: '/19968336/header-bid-tag-1', @@ -2621,11 +2835,11 @@ describe('PubMatic adapter', function () { dctr is found in adunit[0] */ - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(multipleBidRequests[0].params.dctr); + expect(data.imp[0].ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(multipleBidRequests[0].params.dctr); /* case 2 - - dctr not present in adunit[0] + dctr not present in adunit[0] but present in adunit[1] */ delete multipleBidRequests[0].params.dctr; request = spec.buildRequests(multipleBidRequests, { @@ -2633,7 +2847,9 @@ describe('PubMatic adapter', function () { }); data = JSON.parse(request.data); - expect(data.site.ext).to.not.exist; + expect(data.imp[0].ext).to.exist.and.to.deep.equal({}); + expect(data.imp[1].ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.imp[1].ext.key_val).to.exist.and.to.equal(multipleBidRequests[1].params.dctr); /* case 3 - dctr is present in adunit[0], but is not a string value @@ -2644,7 +2860,7 @@ describe('PubMatic adapter', function () { }); data = JSON.parse(request.data); - expect(data.site.ext).to.not.exist; + expect(data.imp[0].ext).to.exist.and.to.deep.equal({}); }); it('Request params deals check', function () { @@ -3028,6 +3244,155 @@ describe('PubMatic adapter', function () { let response = spec.interpretResponse(bidResponses, request); expect(response[0].renderer).to.not.exist; }); + + it('should assign mediaType by reading bid.ext.mediaType', function() { + let newvideoRequests = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5670', + 'video': { + 'mimes': ['video/mp4'], + 'skippable': true, + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }]; + let newvideoBidResponses = { + 'body': { + 'id': '1621441141473', + 'cur': 'USD', + 'customdata': 'openrtb1', + 'ext': { + 'buyid': 'myBuyId' + }, + 'seatbid': [{ + 'bid': [{ + 'id': '2c95df014cfe97', + 'impid': '2c95df014cfe97', + 'price': 4.2, + 'cid': 'test1', + 'crid': 'test2', + 'adm': "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1", + 'w': 0, + 'h': 0, + 'dealId': 'ASEA-MS-KLY-TTD-DESKTOP-ID-VID-6S-030420', + 'ext': { + 'BidType': 1 + } + }], + 'ext': { + 'buyid': 'myBuyId' + } + }] + }, + 'headers': {} + } + let newrequest = spec.buildRequests(newvideoRequests, { + auctionId: 'new-auction-id' + }); + let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); + expect(newresponse[0].mediaType).to.equal('video') + }) + + it('should assign mediaType even if bid.ext.mediaType does not exists', function() { + let newvideoRequests = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5670', + 'video': { + 'mimes': ['video/mp4'], + 'skippable': true, + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }]; + let newvideoBidResponses = { + 'body': { + 'id': '1621441141473', + 'cur': 'USD', + 'customdata': 'openrtb1', + 'ext': { + 'buyid': 'myBuyId' + }, + 'seatbid': [{ + 'bid': [{ + 'id': '2c95df014cfe97', + 'impid': '2c95df014cfe97', + 'price': 4.2, + 'cid': 'test1', + 'crid': 'test2', + 'adm': "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1", + 'w': 0, + 'h': 0, + 'dealId': 'ASEA-MS-KLY-TTD-DESKTOP-ID-VID-6S-030420' + }], + 'ext': { + 'buyid': 'myBuyId' + } + }] + }, + 'headers': {} + } + let newrequest = spec.buildRequests(newvideoRequests, { + auctionId: 'new-auction-id' + }); + let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); + expect(newresponse[0].mediaType).to.equal('video') + }) }); describe('getUserSyncs', function() { From 180ddafbb8ebf30f00055e871f615243a2f4deb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bendeg=C3=BAz=20=C3=81cs?= <30595431+acsbendi@users.noreply.github.com> Date: Tue, 25 May 2021 22:38:28 +0200 Subject: [PATCH 0998/1476] Fill currency parameters for Kobler adapter. (#6825) --- modules/koblerBidAdapter.js | 11 ++++++-- test/spec/modules/koblerBidAdapter_spec.js | 31 +++++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js index cc5b374af95..59d1639a329 100644 --- a/modules/koblerBidAdapter.js +++ b/modules/koblerBidAdapter.js @@ -27,11 +27,14 @@ export const buildRequests = function (validBidRequests, bidderRequest) { }; export const interpretResponse = function (serverResponse) { + const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; const res = serverResponse.body; const bids = [] if (res) { res.seatbid.forEach(sb => { sb.bid.forEach(b => { + const adWithCorrectCurrency = b.adm + .replace(/\${AUCTION_PRICE_CURRENCY}/g, adServerPriceCurrency); bids.push({ requestId: b.impid, cpm: b.price, @@ -42,7 +45,7 @@ export const interpretResponse = function (serverResponse) { dealId: b.dealid, netRevenue: true, ttl: TIME_TO_LIVE_IN_SECONDS, - ad: b.adm, + ad: adWithCorrectCurrency, nurl: b.nurl, meta: { advertiserDomains: b.adomain @@ -56,10 +59,14 @@ export const interpretResponse = function (serverResponse) { export const onBidWon = function (bid) { const cpm = bid.cpm || 0; + const cpmCurrency = bid.currency || SUPPORTED_CURRENCY; const adServerPrice = utils.deepAccess(bid, 'adserverTargeting.hb_pb', 0); + const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; if (utils.isStr(bid.nurl) && bid.nurl !== '') { const winNotificationUrl = utils.replaceAuctionPrice(bid.nurl, cpm) - .replace(/\${AD_SERVER_PRICE}/g, adServerPrice); + .replace(/\${AUCTION_PRICE_CURRENCY}/g, cpmCurrency) + .replace(/\${AD_SERVER_PRICE}/g, adServerPrice) + .replace(/\${AD_SERVER_PRICE_CURRENCY}/g, adServerPriceCurrency); utils.triggerPixel(winNotificationUrl); } }; diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js index 725c9ece118..76c2c287989 100644 --- a/test/spec/modules/koblerBidAdapter_spec.js +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -513,6 +513,11 @@ describe('KoblerAdapter', function () { }); it('should generate bids from OpenRTB response', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'NOK' + } + }); const responseWithTwoBids = { body: { seatbid: [ @@ -521,12 +526,12 @@ describe('KoblerAdapter', function () { { impid: '6194ddef-89a4-404f-9efd-6b718fc23308', price: 7.981, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', crid: 'edea9b03-3a57-41aa-9c00-abd673e22006', dealid: '', w: 320, h: 250, - adm: '', + adm: '', adomain: [ 'https://kobler.no' ] @@ -534,12 +539,12 @@ describe('KoblerAdapter', function () { { impid: '2ec0b40f-d3ca-4ba5-8ce3-48290565690f', price: 6.71234, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', crid: 'fa2d5af7-2678-4204-9023-44c526160742', dealid: '2783483223432342', w: 580, h: 400, - adm: '', + adm: '', adomain: [ 'https://bid.kobler.no' ] @@ -563,8 +568,8 @@ describe('KoblerAdapter', function () { dealId: '', netRevenue: true, ttl: 600, - ad: '', - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + ad: '', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', meta: { advertiserDomains: [ 'https://kobler.no' @@ -581,8 +586,8 @@ describe('KoblerAdapter', function () { dealId: '2783483223432342', netRevenue: true, ttl: 600, - ad: '', - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + ad: '', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', meta: { advertiserDomains: [ 'https://bid.kobler.no' @@ -617,9 +622,15 @@ describe('KoblerAdapter', function () { }); it('Should trigger pixel with replaced nurl if nurl is not empty', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'NOK' + } + }); spec.onBidWon({ cpm: 8.341, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + currency: 'NOK', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', adserverTargeting: { hb_pb: 8 } @@ -627,7 +638,7 @@ describe('KoblerAdapter', function () { expect(utils.triggerPixel.callCount).to.be.equal(1); expect(utils.triggerPixel.firstCall.args[0]).to.be.equal( - 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&asp=8' + 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&sp_cur=NOK&asp=8&asp_cur=NOK' ); }); }); From 6580bf446ab0196fa48f34de3969510bb2dae698 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Tue, 25 May 2021 23:57:11 +0300 Subject: [PATCH 0999/1476] Adf Bid Adapter: banner and video media type support added (#6726) --- modules/adfBidAdapter.js | 130 +++++++++-- modules/adfBidAdapter.md | 47 ++-- test/spec/modules/adfBidAdapter_spec.js | 289 +++++++++++++++++++++--- 3 files changed, 399 insertions(+), 67 deletions(-) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 8b3550e6108..5893891eba6 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -5,10 +5,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { - NATIVE + NATIVE, BANNER, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; + +const { getConfig } = config; const BIDDER_CODE = 'adf'; const GVLID = 50; @@ -45,28 +48,58 @@ const NATIVE_PARAMS = { name: 'data' } }; +const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; export const spec = { code: BIDDER_CODE, aliases: BIDDER_ALIAS, gvlid: GVLID, - supportedMediaTypes: [ NATIVE ], + supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], isBidRequestValid: bid => !!bid.params.mid, buildRequests: (validBidRequests, bidderRequest) => { - const page = bidderRequest.refererInfo.referer; + let app, site; + + const commonFpd = getConfig('ortb2') || {}; + let { user } = commonFpd; + + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {}; + if (commonFpd.app) { + utils.mergeDeep(app, commonFpd.app); + } + } else { + site = getConfig('site') || {}; + if (commonFpd.site) { + utils.mergeDeep(site, commonFpd.site); + } + + if (!site.page) { + site.page = bidderRequest.refererInfo.referer; + } + } + + const device = getConfig('device') || {}; + device.w = device.w || window.innerWidth; + device.h = device.h || window.innerHeight; + device.ua = device.ua || navigator.userAgent; + const adxDomain = setOnAny(validBidRequests, 'params.adxDomain') || 'adx.adform.net'; - const ua = navigator.userAgent; + const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = validBidRequests[0].transactionId; // ??? check with ssp + const tid = validBidRequests[0].transactionId; const test = setOnAny(validBidRequests, 'params.test'); - const publisher = setOnAny(validBidRequests, 'params.publisher'); - const siteId = setOnAny(validBidRequests, 'params.siteId'); - const currency = config.getConfig('currency.adServerCurrency'); + const currency = getConfig('currency.adServerCurrency'); const cur = currency && [ currency ]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; + + const imp = { + id: id + 1, + tagid: bid.params.mid + }; + const assets = utils._map(bid.nativeParams, (bidParams, key) => { const props = NATIVE_PARAMS[key]; const asset = { @@ -102,21 +135,51 @@ export const spec = { } }).filter(Boolean); - return { - id: id + 1, - tagid: bid.params.mid, - native: { + if (assets.length) { + imp.native = { request: { assets } - } - }; + }; + + bid.mediaType = NATIVE; + return imp; + } + + const bannerParams = utils.deepAccess(bid, 'mediaTypes.banner'); + + if (bannerParams && bannerParams.sizes) { + const sizes = utils.parseSizesInput(bannerParams.sizes); + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + imp.banner = { + format + }; + bid.mediaType = BANNER; + + return imp; + } + + const videoParams = utils.deepAccess(bid, 'mediaTypes.video'); + if (videoParams) { + imp.video = videoParams; + bid.mediaType = VIDEO; + + return imp; + } }); const request = { id: bidderRequest.auctionId, - site: { id: siteId, page, publisher }, - device: { ua }, + site, + app, + user, + device, source: { tid, fd: 1 }, ext: { pt }, cur, @@ -128,8 +191,8 @@ export const spec = { request.test = 1; } if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { - request.user = { ext: { consent: bidderRequest.gdprConsent.consentString } }; - request.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies & 1 } }; + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); } if (bidderRequest.uspConsent) { @@ -164,16 +227,35 @@ export const spec = { return bids.map((bid, id) => { const bidResponse = bidResponses[id]; if (bidResponse) { - return { + const result = { requestId: bid.bidId, cpm: bidResponse.price, creativeId: bidResponse.crid, ttl: 360, netRevenue: bid.netRevenue === 'net', currency: cur, - mediaType: NATIVE, - native: parseNative(bidResponse) + mediaType: bid.mediaType, + width: bidResponse.w, + height: bidResponse.h, + dealId: bidResponse.dealid, + meta: { + mediaType: bid.mediaType, + advertiserDomains: bidResponse.adomain + } }; + + if (bidResponse.native) { + result.native = parseNative(bidResponse); + } else { + result[ bid.mediaType === VIDEO ? 'vastXml' : 'ad' ] = bidResponse.adm; + } + + if (!bid.renderer && bid.mediaType === VIDEO && utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + result.renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); + result.renderer.setRender(renderer); + } + + return result; } }).filter(Boolean); } @@ -212,3 +294,9 @@ function setOnAny(collection, key) { function flatten(arr) { return [].concat(...arr); } + +function renderer(bid) { + bid.renderer.push(() => { + window.Adform.renderOutstream(bid); + }); +} diff --git a/modules/adfBidAdapter.md b/modules/adfBidAdapter.md index 190aa91ea57..10264e6f486 100644 --- a/modules/adfBidAdapter.md +++ b/modules/adfBidAdapter.md @@ -7,15 +7,12 @@ Maintainer: Scope.FL.Scripts@adform.com # Description Module that connects to Adform demand sources to fetch bids. -Only native format is supported. Using OpenRTB standard. Previous adapter name - adformOpenRTB. +Banner, video and native formats are supported. Using OpenRTB standard. Previous adapter name - adformOpenRTB. # Test Parameters ``` - var adUnits = [ + var adUnits = [{ code: '/19968336/prebid_native_example_1', - sizes: [ - [360, 360] - ], mediaTypes: { native: { image: { @@ -44,16 +41,36 @@ Only native format is supported. Using OpenRTB standard. Previous adapter name - bids: [{ bidder: 'adf', params: { - mid: 606169, // required - adxDomain: 'adx.adform.net', // optional - siteId: '23455', // optional - priceType: 'gross' // optional, default is 'net' - publisher: { // optional block - id: "2706", - name: "Publishers Name", - domain: "publisher.com" - } + mid: 606169, // required + adxDomain: 'adx.adform.net' // optional + } + }] + }, { + code: '/19968336/prebid_banner_example_1', + mediaTypes: { + banner: { + sizes: [[ 300, 250 ]] + } + } + bids: [{ + bidder: 'adf', + params: { + mid: 1038466 + } + }] + }, { + code: '/19968336/prebid_video_example_1', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + bids: [{ + bidder: 'adf', + params: { + mid: 822732 } }] - ]; + }]; ``` diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 2c141d31bf8..ae92af32a1e 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -35,11 +35,13 @@ describe('Adf adapter', function () { }); describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); it('should send request with correct structure', function () { let validBidRequests = [{ bidId: 'bidId', params: { - siteId: 'siteId', adxDomain: '10.8.57.207' } }]; @@ -53,7 +55,7 @@ describe('Adf adapter', function () { describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -63,7 +65,7 @@ describe('Adf adapter', function () { }); it('should send gdpr as number', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -72,7 +74,7 @@ describe('Adf adapter', function () { }); it('should send CCPA Consent data to adform', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { uspConsent: '1YA-', refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -118,7 +120,7 @@ describe('Adf adapter', function () { it('should add test and is_debug to request, if test is set in parameters', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', test: 1 } + params: { test: 1 } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); @@ -151,26 +153,66 @@ describe('Adf adapter', function () { }); it('should send info about device', function () { + config.setConfig({ + device: { w: 100, h: 100 } + }); let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' } + params: { mid: '1000' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); + }); + + it('should send app info', function () { + config.setConfig({ + app: { id: 'appid' }, + ortb2: { app: { name: 'appname' } } + }); + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.app.id, 'appid'); + assert.equal(request.app.name, 'appname'); + assert.equal(request.site, undefined); }); + it('should send info about the site', function () { + config.setConfig({ + site: { + id: '123123', + publisher: { + domain: 'publisher.domain.com' + } + }, + ortb2: { + site: { + publisher: { + name: 'publisher\'s name' + } + } + } + }); let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', publisher: {id: '123123', domain: 'publisher.domain.com', name: 'publisher\'s name'} } + params: { mid: '1000' } }]; let refererInfo = { referer: 'page' }; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); assert.deepEqual(request.site, { page: refererInfo.referer, - publisher: validBidRequests[0].params.publisher, - id: validBidRequests[0].params.siteId + publisher: { + domain: 'publisher.domain.com', + name: 'publisher\'s name' + }, + id: '123123' }); }); @@ -213,7 +255,7 @@ describe('Adf adapter', function () { it('should send correct priceType value', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', priceType: 'net' } + params: { priceType: 'net' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); @@ -237,13 +279,16 @@ describe('Adf adapter', function () { it('should add incrementing values of id', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }, { bidId: 'bidId2', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }, { bidId: 'bidId3', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }]; let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; @@ -253,21 +298,106 @@ describe('Adf adapter', function () { }); it('should add mid', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', mid: 1000 } }, - { bidId: 'bidId2', params: { siteId: 'siteId', mid: 1001 } }, - { bidId: 'bidId3', params: { siteId: 'siteId', mid: 1002 } }]; + let validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }, + { bidId: 'bidId2', params: {mid: 1001}, mediaTypes: {video: {}} }, + { bidId: 'bidId3', params: {mid: 1002}, mediaTypes: {video: {}} }]; let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; for (let i = 0; i < 3; i++) { assert.equal(imps[i].tagid, validBidRequests[i].params.mid); } }); + describe('multiple media types', function () { + it('should use single media type for bidding', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + video: {} + } + }, { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaTypes: { + video: {}, + native: {} + } + }, { + bidId: 'bidId2', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 } + }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + native: {} + } + }]; + let [ banner, video, native ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + + assert.ok(banner.banner); + assert.equal(banner.video, undefined); + assert.equal(banner.native, undefined); + assert.ok(video.video); + assert.equal(video.banner, undefined); + assert.equal(video.native, undefined); + assert.ok(native.native); + assert.equal(native.video, undefined); + assert.equal(native.banner, undefined); + }); + }); + + describe('banner', function () { + it('should convert sizes to openrtb format', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + } + } + }]; + let { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0]; + assert.deepEqual(banner, { + format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] + }); + }); + }); + + describe('video', function () { + it('should pass video mediatype config', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + } + } + }]; + let { video } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0]; + assert.deepEqual(video, { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + }); + }); + }); + describe('native', function () { describe('assets', function () { it('should set correct asset id', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -283,7 +413,7 @@ describe('Adf adapter', function () { it('should add required key if it is necessary', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -303,7 +433,7 @@ describe('Adf adapter', function () { it('should map img and data assets', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: true, sizes: [150, 50] }, @@ -330,7 +460,7 @@ describe('Adf adapter', function () { it('should flatten sizes and utilise first pair', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { sizes: [[200, 300], [100, 200]] @@ -348,7 +478,7 @@ describe('Adf adapter', function () { it('should utilise aspect_ratios', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [{ @@ -380,7 +510,7 @@ describe('Adf adapter', function () { it('should not throw error if aspect_ratios config is not defined', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [] @@ -398,7 +528,7 @@ describe('Adf adapter', function () { it('should expect any dimensions if min_width not passed', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [{ @@ -441,7 +571,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -450,7 +580,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId2', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -482,7 +612,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -491,7 +621,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId2', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -500,7 +630,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId3', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -509,7 +639,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId4', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -543,7 +673,9 @@ describe('Adf adapter', function () { assets: [], link: { url: 'link' }, imptrackers: ['imptrackers url1', 'imptrackers url2'] - } + }, + dealid: 'deal-id', + adomain: [ 'demo.com' ] } ] }], @@ -555,7 +687,8 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, + mediaType: 'native', nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -574,6 +707,9 @@ describe('Adf adapter', function () { assert.deepEqual(bids[0].netRevenue, false); assert.deepEqual(bids[0].currency, serverResponse.body.cur); assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); + assert.deepEqual(bids[0].dealId, 'deal-id'); }); it('should set correct native params', function () { const bid = [ @@ -678,5 +814,96 @@ describe('Adf adapter', function () { const result = spec.interpretResponse(serverResponse, bidRequest)[0]; assert.ok(!result); }); + + describe('banner', function () { + it('should set ad content on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'banner' + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].ad, ''); + }); + }); + + describe('video', function () { + it('should set vastXml on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'video' + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].vastXml, ''); + }); + + it('should add renderer for outstream bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }, { impid: '2', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'video', + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + { + bidId: 'bidId2', + params: { mid: 1000 }, + mediaType: 'video', + mediaTypes: { + video: { + constext: 'instream' + } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.ok(bids[0].renderer); + assert.equal(bids[1].renderer, undefined); + }); + }); }); }); From cc68ad49c6a41a4af964c2235ce34105fcea2603 Mon Sep 17 00:00:00 2001 From: Mike Groh Date: Tue, 25 May 2021 21:11:06 -0400 Subject: [PATCH 1000/1476] Trion bid adapter: support for meta advertiserDomains (#6829) * Adding files associated with the trion adapter update to the newest prebid version(1.0). * Updating pull request with safer checks for user sync and general clean up/consistency of tests. * removing a call to bidder code for pull request review. also removing the test that requires it * there were some changes to the bid factory after our initial release that we didn't account for. Changing adapter to account for response body and required params. * Revert "there were some changes to the bid factory after our initial release that we didn't account for. Changing adapter to account for response body and required params." This reverts commit 324d15785fb61c92db9c0a37f1001f47721e3a25. * there were some changes to the bid factory after our initial release that we didn't account for. Changing adapter to account for response body and required params. * adding safety checks to Trion adapter * Sending up to trion endpoint if there is bot traffic or the browser tab is not visible. * sending the wrong param in the test. * Trion test cleanup. returning document and window states to their original form after tests. * Trion test cleanup. using before and after to alter window and document objects in tests. * re-adding trion adapter to prebid project to stop using deprecated function for page url for 3.0 release * minor formatting change * accept size array from media types banner over the sizes array from pubs. * updating trion bid adapter to be us privacy and gdpr compliant * encoding consent strings for test * Trion adapter update. Changing request params to trion including removing support for optional 're' param, cleaning up existing params to send 1/0 instead of null when non is present and adding prebid version. * adding support for meta advertiserDomains in trion bid adapter for future use Co-authored-by: Mike Groh --- modules/trionBidAdapter.js | 3 +++ test/spec/modules/trionBidAdapter_spec.js | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 37d54e60cd2..e9e030ce33f 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -53,6 +53,9 @@ export const spec = { bid.creativeId = result.creativeId; bid.currency = result.currency; bid.netRevenue = result.netRevenue; + if (result.adomain) { + bid.meta = {advertiserDomains: result.adomain} + } bidResponses.push(bid); } } diff --git a/test/spec/modules/trionBidAdapter_spec.js b/test/spec/modules/trionBidAdapter_spec.js index ae329b4a028..0fc03caa563 100644 --- a/test/spec/modules/trionBidAdapter_spec.js +++ b/test/spec/modules/trionBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import * as utils from 'src/utils.js'; import {spec, acceptPostMessage, getStorageData, setStorageData} from 'modules/trionBidAdapter.js'; +import {deepClone} from 'src/utils.js'; const CONSTANTS = require('src/constants.json'); const adloader = require('src/adloader'); @@ -316,6 +317,14 @@ describe('Trion adapter tests', function () { expect(response[0].cpm).to.equal(bidCpm); TRION_BID_RESPONSE.result.cpm = 100; }); + + it('advertiserDomains is included when sent by server', function () { + TRION_BID_RESPONSE.result.adomain = ['test_adomain']; + let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); + expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); + delete TRION_BID_RESPONSE.result.adomain; + }); }); describe('getUserSyncs', function () { From c48e71e7f1d363c5486ece6ba7696c8589f0e4ff Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 26 May 2021 02:28:31 -0700 Subject: [PATCH 1001/1476] Release Drafter: update to minor as the default (#6827) --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 8984252f4c3..a3246cffd6d 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -22,7 +22,7 @@ version-resolver: patch: labels: - 'patch' - default: patch + default: minor template: | ## In This Release $CHANGES From 61aadfb1c5410213a3b9971c4f31a3a0b8d9e00a Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Wed, 26 May 2021 10:09:24 -0400 Subject: [PATCH 1002/1476] Sonobi bid adapter: add support for gpid and set advertiserDomains (#6826) * added gpid to sonobi bidder adapter. It tries to get the gpt ad unit code * added advertiser domain to bid meta --- modules/sonobiBidAdapter.js | 24 ++++++++-- test/spec/modules/sonobiBidAdapter_spec.js | 55 +++++++++++++++++++--- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index e5bd76eba9f..eb6009079ac 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -1,5 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, deepClone } from '../src/utils.js'; +import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, deepClone, getGptSlotInfoForAdUnitCode } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; @@ -60,11 +60,11 @@ export const spec = { if (/^[\/]?[\d]+[[\/].+[\/]?]?$/.test(slotIdentifier)) { slotIdentifier = slotIdentifier.charAt(0) === '/' ? slotIdentifier : '/' + slotIdentifier; return { - [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}${_validateFloor(bid)}` + [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}${_validateFloor(bid)}${_validateGPID(bid)}` } } else if (/^[0-9a-fA-F]{20}$/.test(slotIdentifier) && slotIdentifier.length === 20) { return { - [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}${_validateFloor(bid)}` + [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}${_validateFloor(bid)}${_validateGPID(bid)}` } } else { logError(`The ad unit code or Sonobi Placement id for slot ${bid.bidId} is invalid`); @@ -199,6 +199,10 @@ export const spec = { width = 1, height = 1 ] = bid.sbi_size.split('x'); + let aDomains = []; + if (bid.sbi_adomain) { + aDomains = [bid.sbi_adomain] + } const bids = { requestId: bidId, cpm: Number(bid.sbi_mouse), @@ -209,7 +213,10 @@ export const spec = { creativeId: bid.sbi_crid || bid.sbi_aid, aid: bid.sbi_aid, netRevenue: true, - currency: 'USD' + currency: 'USD', + meta: { + advertiserDomains: aDomains + } }; if (bid.sbi_dozer) { @@ -302,6 +309,15 @@ function _validateFloor (bid) { return ''; } +function _validateGPID(bid) { + const gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot') || deepAccess(getGptSlotInfoForAdUnitCode(bid.adUnitCode), 'gptSlot') || bid.params.ad_unit; + + if (gpid) { + return `|gpid=${gpid}` + } + return '' +} + const _creative = (mediaType, referer) => (sbiDc, sbiAid) => { if (mediaType === 'video' || mediaType === 'outstream') { return _videoCreative(sbiDc, sbiAid, referer) diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 0314ffb71c1..79bfa86b216 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec, _getPlatform } from 'modules/sonobiBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import {userSync} from '../../../src/userSync.js'; import { config } from 'src/config.js'; +import * as utils from '../../../src/utils.js'; describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) @@ -239,9 +240,12 @@ describe('SonobiBidAdapter', function () { describe('.buildRequests', function () { beforeEach(function() { sinon.stub(userSync, 'canBidderRegisterSync'); + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') + .onFirstCall().returns({gptSlot: '/123123/gpt_publisher/adunit-code-3', divId: 'adunit-code-3-div-id'}) }); afterEach(function() { userSync.canBidderRegisterSync.restore(); + utils.getGptSlotInfoForAdUnitCode.restore(); }); let bidRequest = [{ 'schain': { @@ -271,6 +275,24 @@ describe('SonobiBidAdapter', function () { 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'bidId': '30b31c1838de1f', + ortb2Imp: { + ext: { + data: { + pbadslot: '/123123/gpt_publisher/adunit-code-1' + } + } + } + }, + { + 'bidder': 'sonobi', + 'params': { + 'placement_id': '1a2b3c4d5e6f1a2b3c4e', + 'sizes': [[300, 250], [300, 600]], + 'referrer': 'overrides_top_window_location' + }, + 'adUnitCode': 'adunit-code-3', + 'sizes': [[120, 600], [300, 600], [160, 600]], + 'bidId': '30b31c1838de1d', }, { 'bidder': 'sonobi', @@ -285,8 +307,9 @@ describe('SonobiBidAdapter', function () { }]; let keyMakerData = { - '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25', - '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600', + '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25|gpid=/123123/gpt_publisher/adunit-code-1', + '30b31c1838de1d': '1a2b3c4d5e6f1a2b3c4e|300x250,300x600|gpid=/123123/gpt_publisher/adunit-code-3', + '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600|gpid=/7780971/sparks_prebid_LB', }; let bidderRequests = { @@ -629,6 +652,7 @@ describe('SonobiBidAdapter', function () { 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b039', 'sbi_mouse': 1.07, + 'sbi_adomain': 'sonobi.com' }, '30b31c1838de1e': { 'sbi_size': '300x250', @@ -636,7 +660,9 @@ describe('SonobiBidAdapter', function () { 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.25, 'sbi_dozer': 'dozerkey', - 'sbi_ct': 'video' + 'sbi_ct': 'video', + 'sbi_adomain': 'sonobi.com' + }, '/7780971/sparks_prebid_LB_OUTSTREAM|30b31c1838de1g': { 'sbi_size': '300x600', @@ -644,6 +670,8 @@ describe('SonobiBidAdapter', function () { 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.07, + 'sbi_adomain': 'sonobi.com' + }, '/7780971/sparks_prebid_LB|30b31c1838de1g': {}, '30b31c1838de1zzzz': { @@ -654,6 +682,7 @@ describe('SonobiBidAdapter', function () { sbi_mouse: 1.25, sbi_size: 'preroll', 'sbi_crid': 'somecrid', + 'sbi_adomain': 'sonobi.com' } @@ -680,7 +709,10 @@ describe('SonobiBidAdapter', function () { 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'aid': '30292e432662bd5f86d90774b944b039' + 'aid': '30292e432662bd5f86d90774b944b039', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1e', @@ -694,7 +726,10 @@ describe('SonobiBidAdapter', function () { 'currency': 'USD', 'dealId': 'dozerkey', 'aid': '30292e432662bd5f86d90774b944b038', - 'mediaType': 'video' + 'mediaType': 'video', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1g', @@ -706,7 +741,10 @@ describe('SonobiBidAdapter', function () { 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'aid': '30292e432662bd5f86d90774b944b038' + 'aid': '30292e432662bd5f86d90774b944b038', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1zzzz', @@ -721,7 +759,10 @@ describe('SonobiBidAdapter', function () { 'dealId': 'dozerkey', 'aid': 'force_1550072228_da1c5d030cb49150c5db8a2136175755', 'mediaType': 'video', - renderer: () => {} + renderer: () => {}, + meta: { + advertiserDomains: ['sonobi.com'] + } }, ]; From 0895e4a2d48977855ea7ae872cfa79c91094ed11 Mon Sep 17 00:00:00 2001 From: Seba Perez Date: Wed, 26 May 2021 12:38:56 -0300 Subject: [PATCH 1003/1476] Eplanning Bid Adapter: verify getUserIds exists and is a function; add adomain support (#6832) --- modules/eplanningBidAdapter.js | 14 +++-- test/spec/modules/eplanningBidAdapter_spec.js | 53 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 98a0e290575..dd96353dea3 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -82,9 +82,12 @@ export const spec = { if (bidderRequest && bidderRequest.uspConsent) { params.ccpa = bidderRequest.uspConsent; } - const userIds = (getGlobal()).getUserIds(); - for (var id in userIds) { - params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); + + if ((getGlobal()).getUserIds && typeof (getGlobal()).getUserIds === 'function') { + const userIds = (getGlobal()).getUserIds(); + for (var id in userIds) { + params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); + } } } @@ -114,6 +117,11 @@ export const spec = { netRevenue: NET_REVENUE, currency: DOLLARS, }; + if (ad.adom) { + bidResponse.meta = { + advertiserDomains: ad.adom + }; + } bidResponses.push(bidResponse); }); } diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 1d3a8344170..6aa191f29a5 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -26,6 +26,7 @@ describe('E-Planning Adapter', function () { const I_ID = '7854abc56248f873'; const CRID = '1234567890'; const TEST_ISV = 'leles.e-planning.net'; + const ADOMAIN = 'adomain.com'; const validBid = { 'bidder': 'eplanning', 'bidId': BID_ID, @@ -237,6 +238,39 @@ describe('E-Planning Adapter', function () { ] } }; + const responseWithAdomain = { + body: { + 'sI': { + 'k': '12345' + }, + 'sec': { + 'k': 'ROS' + }, + 'sp': [{ + 'k': CLEAN_ADUNIT_CODE, + 'a': [{ + 'adm': ADM, + 'id': '7854abc56248f874', + 'i': I_ID, + 'fi': '7854abc56248f872', + 'ip': '45621afd87462104', + 'w': W, + 'h': H, + 'crid': CRID, + 'pr': CPM, + 'adom': ADOMAIN + }], + }], + 'cs': [ + 'http://a-sync-url.com/', + { + 'u': 'http://another-sync-url.com/test.php?&partner=123456&endpoint=us-east', + 'ifr': true + } + ] + } + }; + const responseWithNoSpace = { body: { 'sI': { @@ -520,6 +554,25 @@ describe('E-Planning Adapter', function () { }; expect(bidResponse).to.deep.equal(expectedResponse); }); + + it('should pass advertiserDomains when present', function () { + const bidResponse = spec.interpretResponse(responseWithAdomain, { adUnitToBidId: { [CLEAN_ADUNIT_CODE]: BID_ID } })[0]; + const expectedResponse = { + requestId: BID_ID, + cpm: CPM, + width: W, + height: H, + ad: ADM, + ttl: 120, + creativeId: CRID, + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ADOMAIN + } + }; + expect(bidResponse).to.deep.equal(expectedResponse); + }); }); describe('getUserSyncs', function () { From e97db76c9288c82f849b9456b017ab010843b1a4 Mon Sep 17 00:00:00 2001 From: mediaconsortium-develop <76139568+mediaconsortium-develop@users.noreply.github.com> Date: Thu, 27 May 2021 02:12:09 +0900 Subject: [PATCH 1004/1476] Dgkeyword Rtd Provider: add new real-time data submodule (#6410) * add dgkeywordRtdProvider * change access url * we change not to access real url. and one test case delete because of the changing. * update test code. update api access using ajax() * update test code * set keywords using setBidderConfig in ortb2. * change test code for circle CI Co-authored-by: Patrick McCann --- modules/.submodules.json | 1 + modules/dgkeywordRtdProvider.js | 132 +++++++ modules/dgkeywordRtdProvider.md | 35 ++ .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ++++++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 modules/dgkeywordRtdProvider.js create mode 100644 modules/dgkeywordRtdProvider.md create mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 0a10044a78e..74ebc021a6d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js new file mode 100644 index 00000000000..52fa9fd70c4 --- /dev/null +++ b/modules/dgkeywordRtdProvider.js @@ -0,0 +1,132 @@ +/** + * This module adds dgkeyword provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will get keywords from 1plux profile api. + * This module can work only with AppNexusBidAdapter. + * @module modules/dgkeywordProvider + * @requires module:modules/realTimeData + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +/** + * get keywords from api server. and set keywords. + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} moduleConfig + * @param {Object} userConsent + */ +export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { + const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; + const PROFILE_TIMEOUT_MS = 1000; + 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; + let isFinish = false; + utils.logMessage('[dgkeyword sub module]', adUnits, timeout); + let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); + if (setKeywordTargetBidders.length <= 0) { + utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); + callback(); + } else { + utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); + utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); + ajax(url, { + success: function(response) { + const res = JSON.parse(response); + if (!isFinish) { + utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); + if (res) { + let keywords = {}; + if (res['s'] != null && res['s'].length > 0) { + keywords['opeaud'] = res['s']; + } + if (res['t'] != null && res['t'].length > 0) { + keywords['opectx'] = res['t']; + } + if (Object.keys(keywords).length > 0) { + const targetBidKeys = {} + for (let bid of setKeywordTargetBidders) { + // set keywords to params + bid.params.keywords = keywords; + if (!targetBidKeys[bid.bidder]) { + targetBidKeys[bid.bidder] = true; + } + } + + // set keywrods to ortb2 + let addOrtb2 = {}; + utils.deepSetValue(addOrtb2, 'site.keywords', keywords); + utils.deepSetValue(addOrtb2, 'user.keywords', keywords); + const ortb2 = {ortb2: addOrtb2}; + getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); + } + } + isFinish = true; + } + callback(); + }, + error: function(errorStatus) { + // error occur + utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); + callback(); + } + }, null, { + withCredentials: true, + contentType: 'application/json', + }); + setTimeout(function () { + if (!isFinish) { + // profile api timeout + utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); + isFinish = true; + } + callback(); + }, timeout); + } +} + +/** + * get all bidder which hava {dgkeyword: true} in params + * @param {Object} adUnits + */ +export function getTargetBidderOfDgKeywords(adUnits) { + let setKeywordTargetBidders = []; + for (let adUnit of adUnits) { + for (let bid of adUnit.bids) { + if (bid.params && bid.params['dgkeyword'] === true) { + delete bid.params['dgkeyword']; + setKeywordTargetBidders.push(bid); + } + } + } + return setKeywordTargetBidders; +} + +/** @type {RtdSubmodule} */ +export const dgkeywordSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'dgkeyword', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getBidRequestData: getDgKeywordsAndSet, + init: init, +}; + +function init(moduleConfig) { + return true; +} + +function registerSubModule() { + submodule('realTimeData', dgkeywordSubmodule); +} +registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md new file mode 100644 index 00000000000..7cf532ea650 --- /dev/null +++ b/modules/dgkeywordRtdProvider.md @@ -0,0 +1,35 @@ +## Overview + +Module Name: Digital Garage Keyword Module +Module Type: Rtd Provider +Maintainer: mediaconsortium-develop@bi.garage.co.jp +Remarks: This module can work only with AppNexusBidAdapter. +## Integration + +1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: + +``` +gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var DGKEYWORD_TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: DGKEYWORD_TIMEOUT, + dataProviders: [{ + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DGKEYWORD_TIMEOUT + } + }] + } +}); +``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js new file mode 100644 index 00000000000..0fac92e5118 --- /dev/null +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -0,0 +1,326 @@ +import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; +import { cloneDeep } from 'lodash'; +import { server } from 'test/mocks/xhr.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { config } from 'src/config.js'; + +const DG_GET_KEYWORDS_TIMEOUT = 1950; +const DEF_CONFIG = { + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DG_GET_KEYWORDS_TIMEOUT, + }, +}; +const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; +const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; +const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; +const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; + +describe('Digital Garage Keyword Module', function () { + it('should init and return always true', function () { + expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); + }); + + describe('dgkeyword target test', function () { + it('should have no target', function () { + const adUnits_no_target = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: false, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') + .that.is.empty; + }); + it('should have targets', function () { + const adUnits_targets = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + dgkeyword: true, + }, + }, + ], + }, + ]; + const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); + expect(targets[0].bidder).to.be.equal('dg2'); + expect(targets[0].params.placementId).to.be.equal(99999998); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].bidder).to.be.equal('dg'); + expect(targets[1].params.placementId).to.be.equal(99999996); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + }); + }); + + describe('get profile.', function () { + const AD_UNITS = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + it('should get profiles error(404).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond(404); + }); + it('should get profiles timeout.', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + moduleConfig.params.timeout = 10; + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + setTimeout(() => { + const request = server.requests[0]; + if (request) { + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + } + }, 1000) + }); + it('should get profiles ok(200).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet(pdjs, () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); + + done(); + }, moduleConfig, null); + const request = server.requests[0]; + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + }); + }); +}); From c78238e8d1c0b15c611829d35c38c1b3ab47bf6e Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 26 May 2021 12:04:27 -0700 Subject: [PATCH 1005/1476] Revert "Dgkeyword Rtd Provider: add new real-time data submodule (#6410)" (#6837) This reverts commit e97db76c9288c82f849b9456b017ab010843b1a4. Co-authored-by: Olivier --- modules/.submodules.json | 1 - modules/dgkeywordRtdProvider.js | 132 ------- modules/dgkeywordRtdProvider.md | 35 -- .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ------------------ 4 files changed, 494 deletions(-) delete mode 100644 modules/dgkeywordRtdProvider.js delete mode 100644 modules/dgkeywordRtdProvider.md delete mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 74ebc021a6d..0a10044a78e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,7 +36,6 @@ ], "rtdModule": [ "browsiRtdProvider", - "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js deleted file mode 100644 index 52fa9fd70c4..00000000000 --- a/modules/dgkeywordRtdProvider.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * This module adds dgkeyword provider to the real time data module - * The {@link module:modules/realTimeData} module is required - * The module will get keywords from 1plux profile api. - * This module can work only with AppNexusBidAdapter. - * @module modules/dgkeywordProvider - * @requires module:modules/realTimeData - */ - -import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getGlobal } from '../src/prebidGlobal.js'; - -/** - * get keywords from api server. and set keywords. - * @param {Object} reqBidsConfigObj - * @param {function} callback - * @param {Object} moduleConfig - * @param {Object} userConsent - */ -export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { - const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; - const PROFILE_TIMEOUT_MS = 1000; - 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; - let isFinish = false; - utils.logMessage('[dgkeyword sub module]', adUnits, timeout); - let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); - if (setKeywordTargetBidders.length <= 0) { - utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); - callback(); - } else { - utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); - utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); - ajax(url, { - success: function(response) { - const res = JSON.parse(response); - if (!isFinish) { - utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); - if (res) { - let keywords = {}; - if (res['s'] != null && res['s'].length > 0) { - keywords['opeaud'] = res['s']; - } - if (res['t'] != null && res['t'].length > 0) { - keywords['opectx'] = res['t']; - } - if (Object.keys(keywords).length > 0) { - const targetBidKeys = {} - for (let bid of setKeywordTargetBidders) { - // set keywords to params - bid.params.keywords = keywords; - if (!targetBidKeys[bid.bidder]) { - targetBidKeys[bid.bidder] = true; - } - } - - // set keywrods to ortb2 - let addOrtb2 = {}; - utils.deepSetValue(addOrtb2, 'site.keywords', keywords); - utils.deepSetValue(addOrtb2, 'user.keywords', keywords); - const ortb2 = {ortb2: addOrtb2}; - getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); - } - } - isFinish = true; - } - callback(); - }, - error: function(errorStatus) { - // error occur - utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); - callback(); - } - }, null, { - withCredentials: true, - contentType: 'application/json', - }); - setTimeout(function () { - if (!isFinish) { - // profile api timeout - utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); - isFinish = true; - } - callback(); - }, timeout); - } -} - -/** - * get all bidder which hava {dgkeyword: true} in params - * @param {Object} adUnits - */ -export function getTargetBidderOfDgKeywords(adUnits) { - let setKeywordTargetBidders = []; - for (let adUnit of adUnits) { - for (let bid of adUnit.bids) { - if (bid.params && bid.params['dgkeyword'] === true) { - delete bid.params['dgkeyword']; - setKeywordTargetBidders.push(bid); - } - } - } - return setKeywordTargetBidders; -} - -/** @type {RtdSubmodule} */ -export const dgkeywordSubmodule = { - /** - * used to link submodule with realTimeData - * @type {string} - */ - name: 'dgkeyword', - /** - * get data and send back to realTimeData module - * @function - * @param {string[]} adUnitsCodes - */ - getBidRequestData: getDgKeywordsAndSet, - init: init, -}; - -function init(moduleConfig) { - return true; -} - -function registerSubModule() { - submodule('realTimeData', dgkeywordSubmodule); -} -registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md deleted file mode 100644 index 7cf532ea650..00000000000 --- a/modules/dgkeywordRtdProvider.md +++ /dev/null @@ -1,35 +0,0 @@ -## Overview - -Module Name: Digital Garage Keyword Module -Module Type: Rtd Provider -Maintainer: mediaconsortium-develop@bi.garage.co.jp -Remarks: This module can work only with AppNexusBidAdapter. -## Integration - -1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: - -``` -gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." -``` - -2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. - -## Configuration - -This module is configured as part of the `realTimeData.dataProviders` - -```javascript -var DGKEYWORD_TIMEOUT = 1000; -pbjs.setConfig({ - realTimeData: { - auctionDelay: DGKEYWORD_TIMEOUT, - dataProviders: [{ - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DGKEYWORD_TIMEOUT - } - }] - } -}); -``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js deleted file mode 100644 index 0fac92e5118..00000000000 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ /dev/null @@ -1,326 +0,0 @@ -import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; -import { cloneDeep } from 'lodash'; -import { server } from 'test/mocks/xhr.js'; -import { getGlobal } from 'src/prebidGlobal.js'; -import { config } from 'src/config.js'; - -const DG_GET_KEYWORDS_TIMEOUT = 1950; -const DEF_CONFIG = { - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DG_GET_KEYWORDS_TIMEOUT, - }, -}; -const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; -const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; -const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; -const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; - -describe('Digital Garage Keyword Module', function () { - it('should init and return always true', function () { - expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); - }); - - describe('dgkeyword target test', function () { - it('should have no target', function () { - const adUnits_no_target = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: false, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') - .that.is.empty; - }); - it('should have targets', function () { - const adUnits_targets = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - dgkeyword: true, - }, - }, - ], - }, - ]; - const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); - expect(targets[0].bidder).to.be.equal('dg2'); - expect(targets[0].params.placementId).to.be.equal(99999998); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].bidder).to.be.equal('dg'); - expect(targets[1].params.placementId).to.be.equal(99999996); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - }); - }); - - describe('get profile.', function () { - const AD_UNITS = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - it('should get profiles error(404).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - const request = server.requests[0]; - request.respond(404); - }); - it('should get profiles timeout.', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - moduleConfig.params.timeout = 10; - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - setTimeout(() => { - const request = server.requests[0]; - if (request) { - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - } - }, 1000) - }); - it('should get profiles ok(200).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet(pdjs, () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); - - done(); - }, moduleConfig, null); - const request = server.requests[0]; - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - }); - }); -}); From 48f73e7b9ffb40b2e06db5e348e495cb3271bb5a Mon Sep 17 00:00:00 2001 From: jsfledd Date: Wed, 26 May 2021 12:29:22 -0700 Subject: [PATCH 1006/1476] NativoBidAdapter - Added new QS param to the bid request endpoint (#6838) * 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 --- modules/nativoBidAdapter.js | 15 +++++++++++++++ test/spec/modules/nativoBidAdapter_spec.js | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index d396bd4d495..fc0925bf2ca 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -54,9 +54,24 @@ export const spec = { if (!pageUrl) pageUrl = bidderRequest.refererInfo.referer + // Build adUnit data + const adUnitData = { + adUnits: validBidRequests.map((adUnit) => { + return { + adUnitCode: adUnit.adUnitCode, + mediaTypes: adUnit.mediaTypes, + } + }), + } + + // Build QS Params let params = [ { key: 'ntv_ptd', value: placementIds.toString() }, { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + { + key: 'ntv_ppc', + value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 + }, { key: 'ntv_url', value: encodeURIComponent(pageUrl), diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index e1132bf1b74..6f489a65d3c 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -65,8 +65,10 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.be.a('string') expect(request.url).to.include('?') - expect(request.url).to.include('ntv_url') expect(request.url).to.include('ntv_ptd') + expect(request.url).to.include('ntv_pb_rid') + expect(request.url).to.include('ntv_ppc') + expect(request.url).to.include('ntv_url') }) }) }) From 36d0f724a1b45ab321d57c7eb5741b591e6d327e Mon Sep 17 00:00:00 2001 From: hamper Date: Wed, 26 May 2021 23:00:08 +0300 Subject: [PATCH 1007/1476] Vuukle bid adapter: support for meta advertiserDomains (#6839) * add vuukle adapter * add readme * doc: add email * Vuukle bid adapter: support for meta advertiserDomains --- modules/vuukleBidAdapter.js | 6 +++++- test/spec/modules/vuukleBidAdapter_spec.js | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js index e9770b5e62e..7a83bf4e331 100644 --- a/modules/vuukleBidAdapter.js +++ b/modules/vuukleBidAdapter.js @@ -55,8 +55,12 @@ export const spec = { currency: res.currency || 'USD', netRevenue: true, ttl: TIME_TO_LIVE, - ad: res.ad + ad: res.ad, + meta: { + advertiserDomains: Array.isArray(res.adomain) ? res.adomain : [] + } }; + return [bidResponse]; }, } diff --git a/test/spec/modules/vuukleBidAdapter_spec.js b/test/spec/modules/vuukleBidAdapter_spec.js index fdca4085c8e..17353a40b85 100644 --- a/test/spec/modules/vuukleBidAdapter_spec.js +++ b/test/spec/modules/vuukleBidAdapter_spec.js @@ -41,7 +41,8 @@ describe('vuukleBidAdapterTests', function() { 'width': 300, 'height': 250, 'creative_id': '12345', - 'ad': 'test ad' + 'ad': 'test ad', + 'adomain': ['example.com'] } }; @@ -55,5 +56,6 @@ describe('vuukleBidAdapterTests', function() { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.creativeId).to.equal('12345'); + expect(bid.meta.advertiserDomains).to.deep.equal(['example.com']); }); }); From 8cf4ad9227257b37cd95b8ea2d598198e1ecb679 Mon Sep 17 00:00:00 2001 From: John Salis Date: Wed, 26 May 2021 18:44:39 -0400 Subject: [PATCH 1008/1476] add support for advertiser domains and other bid meta (#6842) Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 6 +- .../spec/modules/beachfrontBidAdapter_spec.js | 73 ++++++++++++++----- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index da5f385b0da..43df7eef9ae 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -71,13 +71,15 @@ export const spec = { let firstSize = getFirstSize(sizes); let context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); let responseType = getVideoBidParam(bidRequest, 'responseType') || 'both'; + let responseMeta = Object.assign({ mediaType: VIDEO, advertiserDomains: [] }, response.meta); let bidResponse = { requestId: bidRequest.bidId, bidderCode: spec.code, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, - creativeId: response.crid || response.cmpId, + creativeId: response.crid, + meta: responseMeta, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, currency: CURRENCY, @@ -103,6 +105,7 @@ export const spec = { .filter(bid => bid.adm) .map((bid) => { let request = find(bidRequest, req => req.adUnitCode === bid.slot); + let responseMeta = Object.assign({ mediaType: BANNER, advertiserDomains: [] }, bid.meta); return { requestId: request.bidId, bidderCode: spec.code, @@ -111,6 +114,7 @@ export const spec = { cpm: bid.price, width: bid.w, height: bid.h, + meta: responseMeta, mediaType: BANNER, currency: CURRENCY, netRevenue: true, diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index fc74ec8a2aa..555f2c21262 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -673,6 +673,7 @@ describe('BeachfrontAdapter', function () { width: width, height: height, renderer: null, + meta: { mediaType: 'video', advertiserDomains: [] }, mediaType: 'video', currency: 'USD', netRevenue: true, @@ -680,26 +681,6 @@ describe('BeachfrontAdapter', function () { }); }); - it('should default to the legacy "cmpId" value for the creative ID', () => { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { - video: { - playerSize: [ width, height ] - } - }; - const serverResponse = { - bidPrice: 5.00, - url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - expect(bidResponse).to.deep.contain({ - creativeId: serverResponse.cmpId - }); - }); - it('should return only vast url if the response type is "nurl"', () => { const width = 640; const height = 480; @@ -761,6 +742,31 @@ describe('BeachfrontAdapter', function () { }); expect(bidResponse.renderer.render).to.be.a('function'); }); + + it('should return meta data for the bid response', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.meta).to.deep.equal({ + mediaType: 'video', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); describe('for banner bids', function () { @@ -815,6 +821,7 @@ describe('BeachfrontAdapter', function () { cpm: serverResponse[ i ].price, width: serverResponse[ i ].w, height: serverResponse[ i ].h, + meta: { mediaType: 'banner', advertiserDomains: [] }, mediaType: 'banner', currency: 'USD', netRevenue: true, @@ -822,6 +829,32 @@ describe('BeachfrontAdapter', function () { }); } }); + + it('should return meta data for the bid response', () => { + bidRequests[0].mediaTypes = { + banner: { + sizes: [[ 300, 250 ], [ 728, 90 ]] + } + }; + const serverResponse = [{ + slot: bidRequests[0].adUnitCode, + adm: '
', + crid: 'crid_1', + price: 3.02, + w: 728, + h: 90, + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }]; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest: bidRequests }); + expect(bidResponse[0].meta).to.deep.equal({ + mediaType: 'banner', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); }); From b72e584a024446f1502a74b8c788006ac110f333 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 26 May 2021 16:26:24 -0700 Subject: [PATCH 1009/1476] Prebid Core: Check for stale rendering (#6707) * Check for stale rendering * Check for stale rendering - code review changes --- src/auctionManager.js | 6 + src/config.js | 7 +- src/constants.json | 3 +- src/prebid.js | 118 +++++---- src/secureCreatives.js | 14 +- test/spec/config_spec.js | 19 +- test/spec/unit/pbjs_api_spec.js | 121 +++++++++ test/spec/unit/secureCreatives_spec.js | 331 ++++++++++++++++++++++++- 8 files changed, 558 insertions(+), 61 deletions(-) diff --git a/src/auctionManager.js b/src/auctionManager.js index 1aca1277aa8..bbafd7426d5 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -15,6 +15,8 @@ * @property {function(): Object} createAuction - creates auction instance and stores it for future reference * @property {function(): Object} findBidByAdId - find bid received by adId. This function will be called by $$PREBID_GLOBAL$$.renderAd * @property {function(): Object} getStandardBidderAdServerTargeting - returns standard bidder targeting for all the adapters. Refer http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.bidderSettings for more details + * @property {function(Object): void} addWinningBid - add a winning bid to an auction based on auctionId + * @property {function(): void} clearAllAuctions - clear all auctions for testing */ import { uniques, flatten, logWarn } from './utils.js'; @@ -113,6 +115,10 @@ export function newAuctionManager() { return _auctions.length && _auctions[_auctions.length - 1].getAuctionId() }; + auctionManager.clearAllAuctions = function() { + _auctions.length = 0; + } + function _addAuction(auction) { _auctions.push(auction); } diff --git a/src/config.js b/src/config.js index 7cb1a81955d..00cf1efd854 100644 --- a/src/config.js +++ b/src/config.js @@ -265,7 +265,7 @@ export function newConfig() { } for (let k of Object.keys(val)) { - if (k !== 'secondaryBidders') { + if (k !== 'secondaryBidders' && k !== 'suppressStaleRender') { utils.logWarn(`Auction Options given an incorrect param: ${k}`) return false } @@ -277,6 +277,11 @@ export function newConfig() { utils.logWarn(`Auction Options ${k} must be only string`); return false } + } else if (k === 'suppressStaleRender') { + if (!utils.isBoolean(val[k])) { + utils.logWarn(`Auction Options ${k} must be of type boolean`); + return false; + } } } return true; diff --git a/src/constants.json b/src/constants.json index e6b9687f911..c43e88cf75f 100644 --- a/src/constants.json +++ b/src/constants.json @@ -39,7 +39,8 @@ "AD_RENDER_FAILED": "adRenderFailed", "TCF2_ENFORCEMENT": "tcf2Enforcement", "AUCTION_DEBUG": "auctionDebug", - "BID_VIEWABLE": "bidViewable" + "BID_VIEWABLE": "bidViewable", + "STALE_RENDER": "staleRender" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", diff --git a/src/prebid.js b/src/prebid.js index 2211e8ec2b5..f58c97ec581 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events.js'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, STALE_RENDER } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -390,63 +390,75 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { try { // lookup ad by ad Id const bid = auctionManager.findBidByAdId(id); + if (bid) { - // replace macros according to openRTB with price paid = bid.cpm - bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); - bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); - - // replacing clickthrough if submitted - if (options && options.clickThrough) { - const { clickThrough } = options; - bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); - bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + let shouldRender = true; + if (bid && bid.status === CONSTANTS.BID_STATUS.RENDERED) { + utils.logWarn(`Ad id ${bid.adId} has been rendered before`); + events.emit(STALE_RENDER, bid); + if (utils.deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + shouldRender = false; + } } - // save winning bids - auctionManager.addWinningBid(bid); - - // emit 'bid won' event here - events.emit(BID_WON, bid); - - const { height, width, ad, mediaType, adUrl, renderer } = bid; - - const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); - utils.insertElement(creativeComment, doc, 'body'); - - if (isRendererRequired(renderer)) { - executeRenderer(renderer, bid); - } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { - const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; - emitAdRenderFail({ reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id }); - } else if (ad) { - // will check if browser is firefox and below version 67, if so execute special doc.open() - // for details see: https://github.com/prebid/Prebid.js/pull/3524 - // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) - if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { - const firefoxVerRegx = /firefox\/([\d\.]+)/; - let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group - if (firefoxVer && parseInt(firefoxVer, 10) < 67) { - doc.open('text/html', 'replace'); + if (shouldRender) { + // replace macros according to openRTB with price paid = bid.cpm + bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); + bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); + + // replacing clickthrough if submitted + if (options && options.clickThrough) { + const {clickThrough} = options; + bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); + bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + } + + // save winning bids + auctionManager.addWinningBid(bid); + + // emit 'bid won' event here + events.emit(BID_WON, bid); + + const {height, width, ad, mediaType, adUrl, renderer} = bid; + + const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + utils.insertElement(creativeComment, doc, 'body'); + + if (isRendererRequired(renderer)) { + executeRenderer(renderer, bid); + } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { + const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; + emitAdRenderFail({reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id}); + } else if (ad) { + // will check if browser is firefox and below version 67, if so execute special doc.open() + // for details see: https://github.com/prebid/Prebid.js/pull/3524 + // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) + if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { + const firefoxVerRegx = /firefox\/([\d\.]+)/; + let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group + if (firefoxVer && parseInt(firefoxVer, 10) < 67) { + doc.open('text/html', 'replace'); + } } + doc.write(ad); + doc.close(); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else if (adUrl) { + const iframe = utils.createInvisibleIframe(); + iframe.height = height; + iframe.width = width; + iframe.style.display = 'inline'; + iframe.style.overflow = 'hidden'; + iframe.src = adUrl; + + utils.insertElement(iframe, doc, 'body'); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else { + const message = `Error trying to write ad. No ad for bid response id: ${id}`; + emitAdRenderFail({reason: NO_AD, message, bid, id}); } - doc.write(ad); - doc.close(); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else if (adUrl) { - const iframe = utils.createInvisibleIframe(); - iframe.height = height; - iframe.width = width; - iframe.style.display = 'inline'; - iframe.style.overflow = 'hidden'; - iframe.src = adUrl; - - utils.insertElement(iframe, doc, 'body'); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else { - const message = `Error trying to write ad. No ad for bid response id: ${id}`; - emitAdRenderFail({ reason: NO_AD, message, bid, id }); } } else { const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; diff --git a/src/secureCreatives.js b/src/secureCreatives.js index def1a9abdbb..a172ec62630 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,19 +6,21 @@ import events from './events.js'; import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js'; import constants from './constants.json'; -import { logWarn, replaceAuctionPrice } from './utils.js'; +import { logWarn, replaceAuctionPrice, deepAccess } from './utils.js'; import { auctionManager } from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import includes from 'core-js-pure/features/array/includes.js'; +import { config } from './config.js'; const BID_WON = constants.EVENTS.BID_WON; +const STALE_RENDER = constants.EVENTS.STALE_RENDER; export function listenMessagesFromCreative() { window.addEventListener('message', receiveMessage, false); } -function receiveMessage(ev) { +export function receiveMessage(ev) { var key = ev.message ? 'message' : 'data'; var data = {}; try { @@ -33,6 +35,14 @@ function receiveMessage(ev) { }); if (adObject && data.message === 'Prebid Request') { + if (adObject.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adObject.adId} has been rendered before`); + events.emit(STALE_RENDER, adObject); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + _sendAdToCreative(adObject, ev); // save winning bids diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 8161d9f1827..9492db6e849 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -233,7 +233,7 @@ describe('config API', function () { expect(logWarnSpy.called).to.equal(false); }); - it('sets auctionOptions', function () { + it('sets auctionOptions secondaryBidders', function () { const auctionOptionsConfig = { 'secondaryBidders': ['rubicon', 'appnexus'] } @@ -241,6 +241,14 @@ describe('config API', function () { expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); }); + it('sets auctionOptions suppressStaleRender', function () { + const auctionOptionsConfig = { + 'suppressStaleRender': true + } + setConfig({ auctionOptions: auctionOptionsConfig }); + expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); + }); + it('should log warning for the wrong value passed to auctionOptions', function () { setConfig({ auctionOptions: '' }); expect(logWarnSpy.calledOnce).to.equal(true); @@ -257,6 +265,15 @@ describe('config API', function () { assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); + it('should log warning for invalid auctionOptions suppress stale render', function () { + setConfig({ auctionOptions: { + 'suppressStaleRender': 'test', + }}); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options suppressStaleRender must be of type boolean'; + assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); + }); + it('should log warning for invalid properties to auctionOptions', function () { setConfig({ auctionOptions: { 'testing': true diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 805eae9e3bc..0bd3380f737 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -199,6 +199,10 @@ describe('Unit: Prebid Module', function () { configObj.setConfig({ useBidCache: false }); }); + after(function() { + auctionManager.clearAllAuctions(); + }); + describe('getAdserverTargetingForAdUnitCodeStr', function () { beforeEach(function () { resetAuction(); @@ -1062,6 +1066,8 @@ describe('Unit: Prebid Module', function () { var adResponse = {}; var spyLogError = null; var spyLogMessage = null; + var spyLogWarn = null; + var spyAddWinningBid; var inIframe = true; var triggerPixelStub; @@ -1100,6 +1106,8 @@ describe('Unit: Prebid Module', function () { spyLogError = sinon.spy(utils, 'logError'); spyLogMessage = sinon.spy(utils, 'logMessage'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); inIframe = true; sinon.stub(utils, 'inIframe').callsFake(() => inIframe); @@ -1110,8 +1118,10 @@ describe('Unit: Prebid Module', function () { auction.getBidsReceived = getBidResponses; utils.logError.restore(); utils.logMessage.restore(); + utils.logWarn.restore(); utils.inIframe.restore(); triggerPixelStub.restore(); + spyAddWinningBid.restore(); }); it('should require doc and id params', function () { @@ -1218,6 +1228,117 @@ describe('Unit: Prebid Module', function () { sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, burl); }); + + it('should call addWinningBid', function () { + pushBidResponseToAuction({ + ad: "" + }); + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + var message = 'Calling renderAd with adId :' + bidId; + sinon.assert.calledWith(spyLogMessage, message); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + }); + + it('should warn stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning but still added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + }); + + it('should stop stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + // Setting suppressStaleRender to true explicitly + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning and do not proceed further + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.notCalled(spyAddWinningBid); + + sinon.assert.notCalled(onWonEvent); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + configObj.setConfig({'auctionOptions': {}}); + }); }); describe('requestBids', function () { diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 566154f0003..eca00e8c8fa 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,8 +1,18 @@ import { - _sendAdToCreative -} from '../../../src/secureCreatives.js'; -import { expect } from 'chai'; + _sendAdToCreative, receiveMessage +} 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'; +import * as auctionModule from 'src/auction.js'; +import * as native from 'src/native.js'; +import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; +import events from 'src/events.js'; +import { config as configObj } from 'src/config.js'; + +import { expect } from 'chai'; + +var CONSTANTS = require('src/constants.json'); describe('secureCreatives', () => { describe('_sendAdToCreative', () => { @@ -42,4 +52,319 @@ describe('secureCreatives', () => { window.apntag = oldapntag; }); }); + + describe('receiveMessage', function() { + const bidId = 1; + const warning = `Ad id ${bidId} has been rendered before`; + let auction; + let adResponse = {}; + let spyAddWinningBid; + let spyLogWarn; + let stubFireNativeTrackers; + let stubGetAllAssetsMessage; + let stubEmit; + + function pushBidResponseToAuction(obj) { + adResponse = Object.assign({ + auctionId: 1, + adId: bidId, + width: 300, + height: 250, + renderer: null + }, obj); + auction.getBidsReceived = function() { + let bidsReceived = getBidResponses(); + bidsReceived.push(adResponse); + return bidsReceived; + } + auction.getAuctionId = () => 1; + } + + function resetAuction() { + $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: false }); + auction.getBidRequests = getBidRequests; + auction.getBidsReceived = getBidResponses; + auction.getAdUnits = getAdUnits; + auction.getAuctionStatus = function() { return auctionModule.AUCTION_COMPLETED } + } + + function resetHistories(...others) { + [ + spyAddWinningBid, + spyLogWarn, + stubFireNativeTrackers, + stubGetAllAssetsMessage, + stubEmit + ].forEach(s => s.resetHistory()); + + if (others && others.length > 0) { others.forEach(s => s.resetHistory()); } + } + + before(function() { + const adUnits = getAdUnits(); + const adUnitCodes = getAdUnits().map(unit => unit.code); + const bidsBackHandler = function() {}; + const timeout = 2000; + auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout: timeout}); + resetAuction(); + }); + + after(function() { + auctionManager.clearAllAuctions(); + }); + + beforeEach(function() { + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + stubFireNativeTrackers = sinon.stub(native, 'fireNativeTrackers'); + stubGetAllAssetsMessage = sinon.stub(native, 'getAllAssetsMessage'); + stubEmit = sinon.stub(events, 'emit'); + }); + + afterEach(function() { + spyAddWinningBid.restore(); + spyLogWarn.restore(); + stubFireNativeTrackers.restore(); + stubGetAllAssetsMessage.restore(); + stubEmit.restore(); + resetAuction(); + }); + + describe('Prebid Request', function() { + it('should render', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + }); + + it('should allow stale rendering without config', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + }); + + it('should stop stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.notCalled(spyAddWinningBid); + sinon.assert.notCalled(adResponse.renderer.render); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + + describe('Prebid Native', function() { + it('Prebid native should render', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + 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.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + 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 = { + 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.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + 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.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + }); + + it('Prebid native should allow stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + 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.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + 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.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + }); }); From 9013e5564c5d773adc4f6ad2a51220c9b29307e3 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Thu, 27 May 2021 11:30:38 +0200 Subject: [PATCH 1010/1476] tappx Bid Adapter: fix wrong regex and gdpr bug (#6834) * tappxBidAdapter: hotfix gdpr url param * tappxBidAdapter: update version * tappxBidAdapter: update regex errors --- modules/tappxBidAdapter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 566795a204b..48e0abc5c7f 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10514'; +const TAPPX_BIDDER_VERSION = '0.1.10526'; const TYPE_CNN = 'prebidjs'; const VIDEO_SUPPORT = ['instream']; @@ -79,7 +79,7 @@ export const spec = { // GDPR & CCPA if (gdprConsent) { - url += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + url += '&gdpr_optin=' + (gdprConsent.gdprApplies ? 1 : 0); url += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); } if (uspConsent) { @@ -120,7 +120,7 @@ function validBasic(bid) { } let classicEndpoint = true - if ((new RegExp(`^(vz.*|zz.*)\.*$`, 'i')).test(bid.params.host)) { + if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { classicEndpoint = false } @@ -398,8 +398,8 @@ function getHostInfo(validBidRequests) { domainInfo.domain = hostParam.split('/', 1)[0]; - let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\.pub\.tappx\.com$`, 'i'); - let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\.[a-z]{3}\.tappx\.com$`, 'i'); + 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 (regexNewEndpoints.test(domainInfo.domain)) { domainInfo.newEndpoint = true; From 3cbdf86cb1d729c71f70c13221bfb22dc2c78ac0 Mon Sep 17 00:00:00 2001 From: guiann Date: Thu, 27 May 2021 12:21:34 +0200 Subject: [PATCH 1011/1476] Adyoulike bid adapter - improvements on Native case (#6831) * add required clickurl in every native adrequest * allows the native response to be given as is to prebid if possible * add unit tests on new Native case --- modules/adyoulikeBidAdapter.js | 101 ++++++++-------- test/spec/modules/adyoulikeBidAdapter_spec.js | 113 ++++++++++++------ 2 files changed, 130 insertions(+), 84 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 00c7c02dc72..44d8e2e3016 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -78,6 +78,10 @@ export const spec = { if (nativeReq.type === 'image') { nativeReq = Object.assign({}, NATIVE_IMAGE, nativeReq); } + // click url is always mandatory even if not specified by publisher + nativeReq.clickUrl = { + required: true + }; accumulator[bidReq.bidId].Native = nativeReq; } if (mediatype === VIDEO) { @@ -329,6 +333,9 @@ function getVideoAd(response) { } function getNativeAssets(response, nativeConfig) { + if (typeof response.Native === 'object') { + return response.Native; + } const native = {}; var adJson = {}; @@ -362,59 +369,55 @@ function getNativeAssets(response, nativeConfig) { } Object.keys(nativeConfig).map(function(key, index) { - if (typeof response.Native === 'object') { - native[key] = response.Native[key]; - } else { - switch (key) { - case 'title': - native[key] = textsJson.TITLE; - break; - case 'body': - native[key] = textsJson.DESCRIPTION; - break; - case 'cta': - native[key] = textsJson.CALLTOACTION; - break; - case 'sponsoredBy': - native[key] = adJson.Content.Preview.Sponsor.Name; - break; - case 'image': - // main image requested size - const imgSize = nativeConfig.image.sizes || []; - if (!imgSize.length) { - imgSize[0] = response.Width || 300; - imgSize[1] = response.Height || 250; + switch (key) { + case 'title': + native[key] = textsJson.TITLE; + break; + case 'body': + native[key] = textsJson.DESCRIPTION; + break; + case 'cta': + native[key] = textsJson.CALLTOACTION; + break; + case 'sponsoredBy': + native[key] = adJson.Content.Preview.Sponsor.Name; + break; + case 'image': + // main image requested size + const imgSize = nativeConfig.image.sizes || []; + if (!imgSize.length) { + imgSize[0] = response.Width || 300; + imgSize[1] = response.Height || 250; + } + + native[key] = { + url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), + width: imgSize[0], + height: imgSize[1] + }; + break; + case 'icon': + if (adJson.HasSponsorImage) { + // icon requested size + const iconSize = nativeConfig.icon.sizes || []; + if (!iconSize.length) { + iconSize[0] = 50; + iconSize[1] = 50; } native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), - width: imgSize[0], - height: imgSize[1] + url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), + width: iconSize[0], + height: iconSize[1] }; - break; - case 'icon': - if (adJson.HasSponsorImage) { - // icon requested size - const iconSize = nativeConfig.icon.sizes || []; - if (!iconSize.length) { - iconSize[0] = 50; - iconSize[1] = 50; - } - - native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), - width: iconSize[0], - height: iconSize[1] - }; - } - break; - case 'privacyIcon': - native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); - break; - case 'privacyLink': - native[key] = adJson.Content.Preview.Credit.Url; - break; - } + } + break; + case 'privacyIcon': + native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); + break; + case 'privacyLink': + native[key] = adJson.Content.Preview.Credit.Url; + break; } }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index c432ad1b32d..abf8793865c 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -300,6 +300,74 @@ describe('Adyoulike Adapter', function () { 'Height': 600, } ]; + + const responseWithSingleNative = [{ + 'BidID': 'bid_id_0', + 'Placement': 'placement_0', + 'Native': { + 'body': 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + 'cta': 'Click here to learn more', + 'clickUrl': 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + 'image': { + 'height': 600, + 'url': 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + 'width': 300 + }, + 'privacyIcon': 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + 'privacyLink': 'https://blobs.omnitagjs.com/adchoice/', + 'sponsoredBy': 'QA Team', + 'title': 'Adserver Traffic Redirect Internal', + 'impressionTrackers': [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + ], + 'javascriptTrackers': [ + 'https://testJsIMP.com/fake.js' + ], + 'clickTrackers': [ + 'https://testPixelCLICK.com/fake' + ] + }, + 'Price': 0.5, + 'Height': 600, + }]; + + const nativeResult = [{ + cpm: 0.5, + creativeId: undefined, + currency: 'USD', + netRevenue: true, + requestId: 'bid_id_0', + ttl: 3600, + mediaType: 'native', + native: { + body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + clickTrackers: [ + 'https://testPixelCLICK.com/fake' + ], + clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + cta: 'Click here to learn more', + image: { + height: 600, + url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + width: 300, + }, + impressionTrackers: [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' + ], + javascriptTrackers: [ + 'https://testJsIMP.com/fake.js' + ], + privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + privacyLink: 'https://blobs.omnitagjs.com/adchoice/', + sponsoredBy: 'QA Team', + title: 'Adserver Traffic Redirect Internal', + } + }]; + const responseWithMultiplePlacements = [ { 'BidID': 'bid_id_0', @@ -569,46 +637,21 @@ describe('Adyoulike Adapter', function () { }); it('receive reponse with Native ad', function () { - serverResponse.body = responseWithSinglePlacement; + serverResponse.body = responseWithSingleNative; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); - expect(result).to.deep.equal([{ - cpm: 0.5, - creativeId: undefined, - currency: 'USD', - netRevenue: true, - requestId: 'bid_id_0', - ttl: 3600, - mediaType: 'native', - native: { - body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', - clickTrackers: [ - 'https://testPixelCLICK.com/fake' - ], - clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', - cta: 'Click here to learn more', - image: { - height: 600, - url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', - width: 300, - }, - impressionTrackers: [ - 'https://testPixelIMP.com/fake', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' - ], - javascriptTrackers: [ - 'https://testJsIMP.com/fake.js' - ], - privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', - privacyLink: 'https://blobs.omnitagjs.com/adchoice/', - sponsoredBy: 'QA Team', - title: 'Adserver Traffic Redirect Internal', - } + expect(result).to.deep.equal(nativeResult); + }); + + it('receive reponse with Native from ad markup', function () { + serverResponse.body = responseWithSinglePlacement; + let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + + expect(result.length).to.equal(1); - }]); + expect(result).to.deep.equal(nativeResult); }); }); }); From 438b0e9c8086ebbb06c291c4574136db710ce138 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Thu, 27 May 2021 16:23:55 +0600 Subject: [PATCH 1012/1476] Zeta Ssp Bid Adapter: Improve user sync logic (#6835) --- modules/zetaSspBidAdapter.js | 46 ++++++++++++++------- test/spec/modules/zetaSspBidAdapter_spec.js | 30 ++++++++++++-- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 450608a82f4..258478b0886 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -5,7 +5,8 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'zeta_global_ssp'; const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; -const USER_SYNC_URL = 'https://ssp.disqus.com/match'; +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'; const TTL = 200; const NET_REV = true; @@ -58,7 +59,7 @@ export const spec = { app: params.app ? params.app : {}, ext: { tags: params.tags ? params.tags : {}, - sid: params.sid ? params.sid : {} + sid: params.sid ? params.sid : undefined } }; @@ -123,23 +124,38 @@ export const spec = { }, /** - * 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. - * @param gdprConsent The GDPR consent parameters - * @param uspConsent The USP consent parameters - * @return {UserSync[]} The user syncs which should be dropped. + * Register User Sync. */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + let syncurl = ''; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + // coppa compliance + if (config.getConfig('coppa') === true) { + syncurl += '&coppa=1'; + } + if (syncOptions.iframeEnabled) { - syncs.push({ + return [{ type: 'iframe', - url: USER_SYNC_URL - }); + url: USER_SYNC_URL_IFRAME + syncurl + }]; + } else { + return [{ + type: 'image', + url: USER_SYNC_URL_IMAGE + syncurl + }]; } - return syncs; } } diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index bdfc64c3234..2766632f707 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,6 @@ -import { spec } from '../../../modules/zetaSspBidAdapter.js' +import {spec} from '../../../modules/zetaSspBidAdapter.js' -describe('Zeta Ssp Bid Adapter', function() { +describe('Zeta Ssp Bid Adapter', function () { const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -26,7 +26,7 @@ describe('Zeta Ssp Bid Adapter', function() { } }]; - it('Test the bid validation function', function() { + it('Test the bid validation function', function () { const validBid = spec.isBidRequestValid(bannerRequest[0]); const invalidBid = spec.isBidRequestValid(null); @@ -82,4 +82,28 @@ describe('Zeta Ssp Bid Adapter', function() { expect(bid.requestId).to.equal(receivedBid.impid); expect(bid.meta.advertiserDomains).to.equal(receivedBid.adomain); }); + + it('Different cases for user syncs', function () { + 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 sync1 = spec.getUserSyncs({iframeEnabled: true})[0]; + expect(sync1.type).to.equal('iframe'); + expect(sync1.url).to.include(USER_SYNC_URL_IFRAME); + + const sync2 = spec.getUserSyncs({iframeEnabled: false})[0]; + expect(sync2.type).to.equal('image'); + expect(sync2.url).to.include(USER_SYNC_URL_IMAGE); + + const sync3 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true})[0]; + expect(sync3.type).to.equal('iframe'); + expect(sync3.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync3.url).to.include('&gdpr='); + + const sync4 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true}, 'test')[0]; + expect(sync4.type).to.equal('iframe'); + expect(sync4.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync4.url).to.include('&gdpr='); + expect(sync4.url).to.include('&us_privacy='); + }); }); From 77ab4ecf1bfe072ef48193372214de571338bcf3 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Thu, 27 May 2021 13:03:23 +0200 Subject: [PATCH 1013/1476] Mediasquare Bid Adapter: support advertiserDomains (#6843) * Mediasquare Bid Adapter: support advertiserDomains * Update mediasquareBidAdapter.js * add unit tests * fix unit test * fix string single quote --- modules/mediasquareBidAdapter.js | 3 +++ test/spec/modules/mediasquareBidAdapter_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 84783eb0991..a586157bf20 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -101,6 +101,9 @@ export const spec = { mediasquare: { 'bidder': value['bidder'], 'code': value['code'] + }, + meta: { + 'advertiserDomains': value['adomain'] } }; if ('native' in value) { diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 796be3420e8..20e5588a99e 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -76,6 +76,7 @@ describe('MediaSquare bid adapter tests', function () { 'bidder': 'msqClassic', 'code': 'test/publishername_atf_desktop_rg_pave', 'bid_id': 'aaaa1234', + 'adomain': ['test.com'], }], }}; @@ -135,6 +136,9 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.mediasquare).to.exist; expect(bid.mediasquare.bidder).to.equal('msqClassic'); expect(bid.mediasquare.code).to.equal([DEFAULT_PARAMS[0].params.owner, DEFAULT_PARAMS[0].params.code].join('/')); + expect(bid.meta).to.exist; + expect(bid.meta.advertiserDomains).to.exist; + expect(bid.meta.advertiserDomains).to.have.lengthOf(1); }); it('Verifies bidder code', function () { From 7baf4b08cc79c5cdeab04bd6e0efb34762e17bea Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Thu, 27 May 2021 18:40:41 +0700 Subject: [PATCH 1014/1476] Apacdex Bid Adapter: add support for meta.advertiserDomains, add and update sample adunit, validate dealId field from server response, add support Price Floors Module (#6711) --- modules/apacdexBidAdapter.js | 44 +++++++++++-- modules/apacdexBidAdapter.md | 56 ++++++++++++++-- test/spec/modules/apacdexBidAdapter_spec.js | 73 +++++++++++++++++---- 3 files changed, 153 insertions(+), 20 deletions(-) diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 62ae3f54125..c0431ddf923 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -55,13 +55,13 @@ export const spec = { let eids; let geo; let test; + let bids = []; - var bids = JSON.parse(JSON.stringify(validBidRequests)) - bidderConfig = CONFIG[bids[0].bidder]; + bidderConfig = CONFIG[validBidRequests[0].bidder]; test = config.getConfig('debug'); - bids.forEach(bidReq => { + validBidRequests.forEach(bidReq => { siteId = siteId || bidReq.params.siteId; if (bidReq.schain) { @@ -95,6 +95,13 @@ export const spec = { } bySlotTargetKey[bidReq.adUnitCode] = targetKey; bidReq.targetKey = targetKey; + + let bidFloor = getBidFloor(bidReq); + if (bidFloor) { + bidReq.bidFloor = bidFloor; + } + + bids.push(JSON.parse(JSON.stringify(bidReq))); }); const payload = {}; @@ -169,23 +176,30 @@ export const spec = { const bidResponses = []; serverBids.forEach(bid => { + const dealId = bid.dealId || ''; const bidResponse = { requestId: bid.requestId, cpm: bid.cpm, width: bid.width, height: bid.height, creativeId: bid.creativeId, - dealId: bid.dealId, currency: bid.currency, netRevenue: bid.netRevenue, ttl: bid.ttl, mediaType: bid.mediaType }; + if (dealId.length > 0) { + bidResponse.dealId = dealId; + } if (bid.vastXml) { bidResponse.vastXml = utils.replaceAuctionPrice(bid.vastXml, bid.cpm); } else { bidResponse.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); } + bidResponse.meta = {}; + if (bid.meta && bid.meta.advertiserDomains && utils.isArray(bid.meta.advertiserDomains)) { + bidResponse.meta.advertiserDomains = bid.meta.advertiserDomains; + } bidResponses.push(bidResponse); }); return bidResponses; @@ -336,4 +350,26 @@ export function validateGeoObject(geo) { return true; } +/** + * Get bid floor from Price Floors Module + * + * @param {Object} bid + * @returns {float||null} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.floorPrice) ? bid.params.floorPrice : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/apacdexBidAdapter.md b/modules/apacdexBidAdapter.md index b88190cda94..fa8c727d709 100644 --- a/modules/apacdexBidAdapter.md +++ b/modules/apacdexBidAdapter.md @@ -11,7 +11,7 @@ Maintainer: ken@apacdex.com Connects to APAC Digital Exchange for bids. Apacdex bid adapter supports Banner and Video (Instream and Outstream) ads. -# Test Parameters +# Sample Banner Ad Unit ``` var adUnits = [ { @@ -26,6 +26,7 @@ var adUnits = [ bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] @@ -33,7 +34,7 @@ var adUnits = [ ]; ``` -# Video Test Parameters +# Sample Video Ad Unit: Instream ``` var videoAdUnit = { code: 'test-div', @@ -41,7 +42,17 @@ var videoAdUnit = { mediaTypes: { video: { playerSize: [[640, 480]], - context: 'instream' + context: "instream" + api: [2], + placement: 1, + skip: 1, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] }, }, bids: [ @@ -49,8 +60,45 @@ var videoAdUnit = { bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] }; -``` \ No newline at end of file +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs + +# Sample Video Ad Unit: Outstream +``` +var videoAdUnit = { + code: 'test-div', + sizes: [[410, 231]], + mediaTypes: { + video: { + playerSize: [[410, 231]], + context: "outstream" + api: [2], + placement: 5, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] + }, + }, + bids: [ + { + bidder: 'apacdex', + params: { + siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared + } + } + ] +}; +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs \ No newline at end of file diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index 3a71833bc3e..5f6a935c453 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec, validateGeoObject, getDomain } from '../../../modules/apacdexBidA import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) @@ -200,7 +201,7 @@ describe('ApacdexBidAdapter', function () { 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', - 'geo': {'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60} + 'geo': { 'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60 } }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -336,12 +337,50 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); - describe('debug test', function() { - beforeEach(function() { - config.setConfig({debug: true}); + it('should attach bidFloor param when either bid param floorPrice or getFloor function exists', function () { + let getFloorResponse = { currency: 'USD', floor: 3 }; + let singleBidRequest, request, payload = null; + + // 1 -> floorPrice not defined, getFloor not defined > empty + singleBidRequest = deepClone(bidRequest[0]); + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data; + expect(payload.bids[0].bidFloor).to.not.exist; + + // 2 -> floorPrice is defined, getFloor not defined > floorPrice is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(0.5); + + // 3 -> floorPrice is defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + + // 4 -> floorPrice not defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + }); + describe('debug test', function () { + beforeEach(function () { + config.setConfig({ debug: true }); }); - afterEach(function() { - config.setConfig({debug: false}); + afterEach(function () { + config.setConfig({ debug: false }); }); it('should return a properly formatted request with pbjs_debug is true', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); @@ -514,7 +553,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '30024615be22ef66a', @@ -527,7 +569,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '1854b40107d6745c', @@ -540,7 +585,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'video' + 'mediaType': 'video', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } } ], 'pixel': [{ @@ -610,6 +658,7 @@ describe('ApacdexBidAdapter', function () { if (resp.mediaType === 'banner') { expect(resp.ad.indexOf('Apacdex AD')).to.be.greaterThan(0); } + expect(resp.meta.advertiserDomains).to.deep.equal(['https://example.com']); }); }); }); @@ -693,17 +742,17 @@ describe('ApacdexBidAdapter', function () { describe('getDomain', function () { it('should return valid domain from publisherDomain config', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: pageUrl}); + config.setConfig({ publisherDomain: pageUrl }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return valid domain from pageUrl argument', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return undefined if pageUrl and publisherDomain not config', () => { let pageUrl; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal(pageUrl); }); }); From a5e7d08950f33301372942a64333522474285895 Mon Sep 17 00:00:00 2001 From: rcheptanariu <35690143+rcheptanariu@users.noreply.github.com> Date: Thu, 27 May 2021 16:07:36 +0300 Subject: [PATCH 1015/1476] Invibes Bid Adapter - support for meta taxonomy (#6849) --- modules/invibesBidAdapter.js | 19 ++++++++++++-- test/spec/modules/invibesBidAdapter_spec.js | 29 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 7d2942eea55..18011359a6d 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -11,7 +11,8 @@ const CONSTANTS = { PREBID_VERSION: 6, METHOD: 'GET', INVIBES_VENDOR_ID: 436, - USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'] + USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], + META_TAXONOMY: ['networkId', 'networkName', 'agencyId', 'agencyName', 'advertiserId', 'advertiserName', 'advertiserDomains', 'brandId', 'brandName', 'primaryCatId', 'secondaryCatIds', 'mediaType'] }; const storage = getStorageManager(CONSTANTS.INVIBES_VENDOR_ID); @@ -236,10 +237,24 @@ function createBid(bidRequest, requestPlacement) { currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, netRevenue: true, ttl: CONSTANTS.TIME_TO_LIVE, - ad: renderCreative(bidModel) + ad: renderCreative(bidModel), + meta: addMeta(bidModel.Meta) }; } +function addMeta(bidModelMeta) { + var meta = {}; + if (bidModelMeta != null) { + for (let i = 0; i < CONSTANTS.META_TAXONOMY.length; i++) { + if (bidModelMeta.hasOwnProperty(CONSTANTS.META_TAXONOMY[i])) { + meta[CONSTANTS.META_TAXONOMY[i]] = bidModelMeta[CONSTANTS.META_TAXONOMY[i]]; + } + } + } + + return meta; +} + function generateRandomId() { return (Math.round(Math.random() * 1e12)).toString(36).substring(0, 10); } diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 6a59bf98dad..ea3e1d6611b 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -733,7 +733,8 @@ describe('invibesBidAdapter:', function () { - ` + `, + meta: {} }]; let multiResponse = { @@ -760,6 +761,23 @@ describe('invibesBidAdapter:', function () { }] }; + let responseWithMeta = { + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }], + BidModel: { + BidVersion: 1, + PlacementId: '12345', + AuctionStartTime: Date.now(), + CreativeHtml: '', + Meta: { + advertiserDomains: ['theadvertiser.com', 'theadvertiser_2.com'], + advertiserName: 'theadvertiser' + } + } + }; + context('when the response is not valid', function () { it('handles response with no bids requested', function () { let emptyResult = spec.interpretResponse({body: response}); @@ -829,6 +847,15 @@ describe('invibesBidAdapter:', function () { expect(secondResult).to.be.empty; }); }); + + context('when the response has meta', function () { + it('responds with a valid bid, with the meta info', function () { + let result = spec.interpretResponse({body: responseWithMeta}, {bidRequests}); + expect(result[0].meta.advertiserName).to.equal('theadvertiser'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser.com'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser_2.com'); + }); + }); }); describe('getUserSyncs', function () { From 399292da7f9c7c11531be6cc05efe72b8f7a823f Mon Sep 17 00:00:00 2001 From: nyakove <43004249+nyakove@users.noreply.github.com> Date: Thu, 27 May 2021 16:57:07 +0300 Subject: [PATCH 1016/1476] adWMG Adapter: add 'adomain' support (#6852) * Support floorCPM parameter, fix some minor bugs * fix space-in-parens circleci error * example fix * clean usersync URL * spaces * spaces * add new unit tests, compatibility with IE11 * remove logInfo * Check for floorCPM value * Check params before sending * New endpoints * code format * new endpoint for cookie sync * update tests * Add meta.advertiserDomains data support * Add meta.advertiserDomains data support Co-authored-by: Mikhail Dykun --- modules/adWMGBidAdapter.js | 4 ++++ test/spec/modules/adWMGBidAdapter_spec.js | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 689e7d02124..a3d78a69d91 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -116,6 +116,10 @@ export const spec = { netRevenue: response.netRevenue, ttl: response.ttl, ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: 'banner' + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/adWMGBidAdapter_spec.js b/test/spec/modules/adWMGBidAdapter_spec.js index 8b927ace84c..db536ca14e2 100644 --- a/test/spec/modules/adWMGBidAdapter_spec.js +++ b/test/spec/modules/adWMGBidAdapter_spec.js @@ -209,7 +209,8 @@ describe('adWMGBidAdapter', function () { 'ttl': 300, 'creativeId': 'creative-id', 'netRevenue': true, - 'currency': 'USD' + 'currency': 'USD', + 'adomain': ['testdomain.com'] } }; }); @@ -219,7 +220,7 @@ describe('adWMGBidAdapter', function () { expect(responses).to.be.an('array').that.is.not.empty; let response = responses[0]; - expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'meta', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency'); expect(response.requestId).to.equal('request-id'); expect(response.cpm).to.equal(100); @@ -230,6 +231,8 @@ describe('adWMGBidAdapter', function () { expect(response.creativeId).to.equal('creative-id'); expect(response.netRevenue).to.be.true; expect(response.currency).to.equal('USD'); + expect(response.meta.advertiserDomains[0]).to.equal('testdomain.com'); + expect(response.meta.mediaType).to.equal('banner'); }); it('should return an empty array when serverResponse is empty', () => { From 0523a1daa9cf66a8a4df513e8a07b1d94428c8a3 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Thu, 27 May 2021 16:01:34 +0200 Subject: [PATCH 1017/1476] Added meta.advertiserDomains to bidResponse (#6853) --- modules/gjirafaBidAdapter.js | 5 ++++- test/spec/modules/gjirafaBidAdapter_spec.js | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 77589cd9071..df33369d6ad 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index f0fb01f4398..96bf319dfd2 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('gjirafaAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('gjirafaAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('gjirafaAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(728); + expect(result[0].height).to.equal(90); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); From f02c1fe37c6935dc2b3d9cef292eb8633ffb4679 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Thu, 27 May 2021 16:21:26 +0200 Subject: [PATCH 1018/1476] Added meta.advertiserDomains to bidResponse (#6854) --- modules/malltvBidAdapter.js | 5 ++++- test/spec/modules/malltvBidAdapter_spec.js | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 7deffe6c07a..4e600135e0b 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js index ffe08ad1a5e..c31e91992f7 100644 --- a/test/spec/modules/malltvBidAdapter_spec.js +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('malltvAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('malltvAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('malltvAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); From d237cbffc88d822a03bd4d23e475aba1960814ac Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 27 May 2021 07:39:30 -0700 Subject: [PATCH 1019/1476] LGTM Fixes: fixes for current LGTM issues (#6851) --- integrationExamples/gpt/idImportLibrary_example.html | 1 + modules/aolBidAdapter.js | 2 +- modules/datablocksBidAdapter.js | 6 +++--- modules/gridBidAdapter.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/integrationExamples/gpt/idImportLibrary_example.html b/integrationExamples/gpt/idImportLibrary_example.html index a3ef3f168c0..f35b571e8f9 100644 --- a/integrationExamples/gpt/idImportLibrary_example.html +++ b/integrationExamples/gpt/idImportLibrary_example.html @@ -47,6 +47,7 @@ params: { pid: '14', // Set your real identityLink placement ID here // notUse3P: true // true/false - If you do not want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will be fired. By default this propertt is undefined and 3p request will be fired.}, + }, storage: { type: 'html5', name: 'idl_env', diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 14b529f4973..03e4ac9021a 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -366,7 +366,7 @@ export const spec = { let tagName = item.match(tagNameRegExp)[0]; let url = item.match(srcRegExp)[2]; - if (tagName && tagName) { + if (tagName && url) { pixelsItems.push({ type: tagName === SYNC_TYPES.IMAGE.TAG ? SYNC_TYPES.IMAGE.TYPE : SYNC_TYPES.IFRAME.TYPE, url: url diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index b00a3eae659..47d2eb2652f 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -68,7 +68,7 @@ export const spec = { id: bidRequest.bidId, tagid: bidRequest.adUnitCode, secure: window.location.protocol == 'https:' - } + }; if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { let sizes = bidRequest.mediaTypes.banner.sizes; @@ -76,7 +76,7 @@ export const spec = { imp.banner = { w: sizes[0][0], h: sizes[0][1] - } + }; } else if (sizes.length > 1) { imp.banner = { format: sizes.map(size => ({ w: size[0], h: size[1] })) @@ -181,7 +181,7 @@ export const spec = { if (VIDEO_PARAMS.indexOf(k) > -1) { imp.video[k] = bidRequest.params.video[k]; } - }) + }); } } let host = bidRequest.params.host; diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 955aeff7168..dbeba27d836 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -350,7 +350,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { netRevenue: false, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid && serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] }, dealId: serverBid.dealid }; From 54bdd70426fb6ff5c2d4c22b1696a02b7d8ba16e Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Thu, 27 May 2021 17:14:10 +0200 Subject: [PATCH 1020/1476] sspBC Bid Adapter: add bidfloor to imp and other updates to bid adapter (#6820) 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] Update to v4.8 (floorprices, usersync, code cleaning) * [sspbc-adapter] remove bidloor param from md. Remove unused test for old module (sspBCAdapter) Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 84 +++++++++++++++++++------------------- modules/sspBCBidAdapter.md | 1 - 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 391f3a05721..d166a01a1da 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -9,7 +9,7 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.8'; +const BIDDER_VERSION = '4.9'; const W = window; const { navigator } = W; const oneCodeDetection = {}; @@ -102,25 +102,29 @@ const applyClientHints = ortbRequest => { ortbRequest.user = Object.assign(ortbRequest.user, { data }); }; -function applyGdpr(bidderRequest, ortbRequest) { - if (bidderRequest && bidderRequest.gdprConsent) { - consentApiVersion = bidderRequest.gdprConsent.apiVersion; - ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': bidderRequest.gdprConsent.gdprApplies ? 1 : 0 }); - ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': bidderRequest.gdprConsent.consentString }); +/** + * Add GDPR data to oRTB request + * Store conset API version (will be required by user sync) + */ +const applyGdpr = (bidderRequest, ortbRequest) => { + const { gdprConsent } = bidderRequest; + if (gdprConsent) { + const { apiVersion, gdprApplies, consentString } = gdprConsent; + consentApiVersion = apiVersion; + ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': gdprApplies ? 1 : 0 }); + ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': consentString }); } } -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - - if (result) { - return result; - } - } -} +/** + * Get value for first occurence of key within the collection + */ +const setOnAny = (collection, key) => collection.reduce((prev, next) => prev || utils.deepAccess(next, key), false); -function sendNotification(payload) { +/** + * Send payload to notification endpoint + */ +const sendNotification = payload => { ajax(NOTIFY_URL, null, JSON.stringify(payload), { withCredentials: false, method: 'POST', @@ -132,7 +136,7 @@ function sendNotification(payload) { * @param {object} slot Ad Unit Params by Prebid * @returns {object} Banner by OpenRTB 2.5 §3.2.6 */ -function mapBanner(slot) { +const mapBanner = slot => { if (slot.mediaType === 'banner' || utils.deepAccess(slot, 'mediaTypes.banner') || (!slot.mediaType && !slot.mediaTypes)) { @@ -141,8 +145,6 @@ function mapBanner(slot) { h: size[1], })); - // override - tylko 1szy wymiar - // format = format.slice(0, 1); return { format, id: slot.bidId, @@ -150,7 +152,7 @@ function mapBanner(slot) { } } -function mapImpression(slot) { +const mapImpression = slot => { const { adUnitCode, bidId, params } = slot; const { id, siteId } = params || {}; const imp = { @@ -160,16 +162,18 @@ function mapImpression(slot) { tagid: adUnitCode, }; - const bidfloor = (slot.params && slot.params.bidFloor) ? parseFloat(slot.params.bidFloor) : undefined; - - if (bidfloor) { - imp.bidfloor = bidfloor; + // Check floorprices for this imp + if (typeof slot.getFloor === 'function') { + // sspBC adapter accepts only floor per imp - check for maximum value for requested ad sizes + imp.bidfloor = slot.sizes.reduce((prev, next) => { + const currentFloor = slot.getFloor({ mediaType: 'banner', size: next }).floor; + return prev > currentFloor ? prev : currentFloor; + }, 0); } - return imp; } -function renderCreative(site, auctionId, bid, seat, request) { +const renderCreative = (site, auctionId, bid, seat, request) => { let gam; const mcad = { @@ -292,7 +296,7 @@ const spec = { return { method: 'POST', - url: BIDDER_URL + '?cs=' + cookieSupport() + '&bdver=' + BIDDER_VERSION + '&pbver=' + pbver + '&inver=0', + url: `${BIDDER_URL}?cs=${cookieSupport()}&bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, data: JSON.stringify(payload), bidderRequest, }; @@ -315,11 +319,11 @@ const spec = { seat = seatbid.seat; seatbid.bid.forEach(serverBid => { // get data from bid response - const { adomain, crid, impid, exp, ext, price, w, h } = serverBid; + const { adomain, crid = `mcad_${bidderRequest.auctionId}_${site.slot}`, impid, exp = 300, ext, price, w, h } = serverBid; const bidRequest = bidderRequest.bids.filter(b => { - const { bidId, params } = b; - const { id, siteId } = params || {}; + const { bidId, params = {} } = b; + const { id, siteId } = params; const currentBidId = id && siteId ? id : 'bidid-' + bidId; return currentBidId === impid; })[0]; @@ -348,10 +352,10 @@ const spec = { const bid = { requestId: bidId, - creativeId: crid || 'mcad_' + bidderRequest.auctionId + '_' + site.slot, + creativeId: crid, cpm: price, currency: response.cur, - ttl: exp || 300, + ttl: exp, width: w, height: h, bidderCode: BIDDER_CODE, @@ -365,14 +369,7 @@ const spec = { }; if (bid.cpm > 0) { - // check bidFloor (if present in params) - const { bidFloor } = params || {}; - - if (!bidFloor || bid.cpm >= bidFloor) { - bids.push(bid); - } else { - utils.logWarn('Discarding bid due to bidFloor setting', bid.cpm, bidFloor); - } + bids.push(bid); } } else { utils.logWarn('Discarding response - no matching request / site id', serverBid.impid); @@ -384,13 +381,14 @@ const spec = { return bids; }, getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { + if (syncOptions.iframeEnabled && consentApiVersion != 1) { return [{ type: 'iframe', - url: SYNC_URL + '?tcf=' + consentApiVersion, + url: `${SYNC_URL}?tcf=${consentApiVersion}`, }]; + } else { + utils.logWarn('sspBC adapter requires iframe based user sync.'); } - utils.logWarn('sspBC adapter requires iframe based user sync.'); }, onTimeout(timeoutData) { diff --git a/modules/sspBCBidAdapter.md b/modules/sspBCBidAdapter.md index f22e8e6c458..1678f3be594 100644 --- a/modules/sspBCBidAdapter.md +++ b/modules/sspBCBidAdapter.md @@ -20,7 +20,6 @@ Optional parameters: - domain - page - tmax -- bidFloor - test # Test Parameters From bcd1ebe079c40354a33b9496aa1edcce8f0f9681 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 27 May 2021 17:19:37 +0200 Subject: [PATCH 1021/1476] user id module - force calls to getId if there was previously no consent data stored (#6760) --- modules/userId/index.js | 11 ++++++----- test/spec/modules/userId_spec.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 199934f4cdb..3f235d15dd1 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -277,6 +277,7 @@ function makeStoredConsentDataHash(consentData) { storedConsentData.gdprApplies = consentData.gdprApplies; storedConsentData.apiVersion = consentData.apiVersion; } + return utils.cyrb53Hash(JSON.stringify(storedConsentData)); } @@ -306,17 +307,17 @@ function getStoredConsentData() { } /** - * test if the consent object stored locally matches the current consent data. - * if there is nothing in storage, return true and we'll do an actual comparison next time. - * this way, we don't force a refresh for every user when this code rolls out + * test if the consent object stored locally matches the current consent data. if they + * don't match or there is nothing stored locally, it means a refresh of the user id + * submodule is needed * @param storedConsentData * @param consentData * @returns {boolean} */ function storedConsentDataMatchesConsentData(storedConsentData, consentData) { return ( - typeof storedConsentData === 'undefined' || - storedConsentData === null || + typeof storedConsentData !== 'undefined' && + storedConsentData !== null && storedConsentData === makeStoredConsentDataHash(consentData) ); } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 95bba079fa2..420d2ddce91 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -234,9 +234,10 @@ describe('User ID', function () { }); }); }); - // Because the cookie exists already, there should be no setCookie call by default; the only setCookie call is - // to store consent data - expect(coreStorage.setCookie.callCount).to.equal(1); + // Because the consent cookie doesn't exist yet, we'll have two setCookie calls: + // 1) for the consent cookie + // 2) from the getId() call that results in a new call to store the results + expect(coreStorage.setCookie.callCount).to.equal(2); }); it('Extend cookie', function () { @@ -2407,7 +2408,7 @@ describe('User ID', function () { }); // check MockId data was copied to bid expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('123456778'); + expect(bid.userId.mid).to.equal('1234'); // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); @@ -2785,7 +2786,7 @@ describe('User ID', function () { sharedAfterFunction(); }); - it('does not call getId if no stored consent data and refresh is not needed', function () { + it('calls getId if no stored consent data and refresh is not needed', function () { coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); @@ -2796,9 +2797,9 @@ describe('User ID', function () { innerAdUnits = config.adUnits }, {adUnits}); - sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); - sinon.assert.calledOnce(mockExtendId); + sinon.assert.notCalled(mockExtendId); }); it('calls getId if no stored consent data but refresh is needed', function () { From 649ea9917df275906d10936b1d5e6fa1db53c562 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 27 May 2021 20:58:03 +0200 Subject: [PATCH 1022/1476] Prebid 4.41.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66f1976d4ce..d4c6cd24181 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.41.0-pre", + "version": "4.41.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f2befaab4211240897413082724fe6958984af53 Mon Sep 17 00:00:00 2001 From: Steffen Anders Date: Thu, 27 May 2021 22:06:05 +0200 Subject: [PATCH 1023/1476] Optimized AdUp Technology bid adapter (#6800) --- modules/aduptechBidAdapter.js | 374 ++++++--- modules/aduptechBidAdapter.md | 87 ++- test/spec/modules/aduptechBidAdapter_spec.js | 780 +++++++++++-------- 3 files changed, 780 insertions(+), 461 deletions(-) diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index d5b348ee29b..b70f7cf3ce6 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,20 +1,169 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js' +import { config } from '../src/config.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js' import * as utils from '../src/utils.js'; export const BIDDER_CODE = 'aduptech'; -export const PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; -export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + PUBLISHER_PLACEHOLDER + '_bid'; +export const ENDPOINT_URL_PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; +export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + ENDPOINT_URL_PUBLISHER_PLACEHOLDER + '_bid'; export const ENDPOINT_METHOD = 'POST'; +/** + * Internal utitlity functions + */ +export const internal = { + + /** + * Extracts the GDPR information from given bidderRequest + * + * @param {BidderRequest} bidderRequest + * @returns {null|Object.} + */ + extractGdpr: (bidderRequest) => { + if (bidderRequest && bidderRequest.gdprConsent) { + return { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + }; + } + + return null; + }, + + /** + * Extracts the pageUrl from given bidderRequest.refererInfo or gobal "pageUrl" config or from (top) window location + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractPageUrl: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { + return bidderRequest.refererInfo.canonicalUrl; + } + + if (config && config.getConfig('pageUrl')) { + return config.getConfig('pageUrl'); + } + + try { + return utils.getWindowTop().location.href; + } catch (e) { + return utils.getWindowSelf().location.href; + } + }, + + /** + * Extracts the referrer based on given bidderRequest.refererInfo or from (top) document referrer + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractReferrer: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; + } + + try { + return utils.getWindowTop().document.referrer; + } catch (e) { + return utils.getWindowSelf().document.referrer; + } + }, + + /** + * Extracts banner config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractBannerConfig: (bidRequest) => { + const sizes = utils.getAdUnitSizes(bidRequest); + if (Array.isArray(sizes) && sizes.length > 0) { + return { sizes: sizes }; + } + + return null; + }, + + /** + * Extracts native config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractNativeConfig: (bidRequest) => { + if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.native')) { + return bidRequest.mediaTypes.native; + } + + return null; + }, + + /** + * Extracts the bidder params from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractParams: (bidRequest) => { + if (bidRequest && bidRequest.params) { + return bidRequest.params + } + + return null; + }, + + /** + * Group given array of bidRequests by params.publisher + * + * @param {BidRequest[]} bidRequests + * @returns {Object.} + */ + groupBidRequestsByPublisher: (bidRequests) => { + const groupedBidRequests = {}; + + if (!bidRequests || bidRequests.length === 0) { + return groupedBidRequests; + } + + bidRequests.forEach((bidRequest) => { + const publisher = internal.extractParams(bidRequest).publisher; + if (!publisher) { + return; + } + + if (!groupedBidRequests[publisher]) { + groupedBidRequests[publisher] = []; + } + + groupedBidRequests[publisher].push(bidRequest); + }); + + return groupedBidRequests; + }, + + /** + * Build ednpoint url based on given publisher code + * + * @param {string} publisher + * @returns {string} + */ + buildEndpointUrl: (publisher) => { + return ENDPOINT_URL.replace(ENDPOINT_URL_PUBLISHER_PLACEHOLDER, encodeURIComponent(publisher)); + }, +} + +/** + * The bid adapter definition + */ export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], /** * Validate given bid request * - * @param {*} bidRequest + * @param {BidRequest[]} bidRequest * @returns {boolean} */ isBidRequestValid: (bidRequest) => { @@ -22,12 +171,13 @@ export const spec = { return false; } - const sizes = extractSizesFromBidRequest(bidRequest); - if (!sizes || sizes.length === 0) { + // banner or native config has to be set + if (!internal.extractBannerConfig(bidRequest) && !internal.extractNativeConfig(bidRequest)) { return false; } - const params = extractParamsFromBidRequest(bidRequest); + // publisher and placement param has to be set + const params = internal.extractParams(bidRequest); if (!params || !params.publisher || !params.placement) { return false; } @@ -38,152 +188,124 @@ export const spec = { /** * Build real bid requests * - * @param {*} validBidRequests - * @param {*} bidderRequest - * @returns {*[]} + * @param {BidRequest[]} validBidRequests + * @param {BidderRequest} bidderRequest + * @returns {Object[]} */ buildRequests: (validBidRequests, bidderRequest) => { - const bidRequests = []; - const gdpr = extractGdprFromBidderRequest(bidderRequest); + const requests = []; + + // stop here on invalid or empty data + if (!bidderRequest || !validBidRequests || validBidRequests.length === 0) { + return requests; + } + + // collect required data + const auctionId = bidderRequest.auctionId; + const pageUrl = internal.extractPageUrl(bidderRequest); + const referrer = internal.extractReferrer(bidderRequest); + const gdpr = internal.extractGdpr(bidderRequest); - validBidRequests.forEach((bidRequest) => { - bidRequests.push({ - url: ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequest.params.publisher)), + // group bid requests by publisher + const groupedBidRequests = internal.groupBidRequestsByPublisher(validBidRequests); + + // build requests + for (const publisher in groupedBidRequests) { + const request = { + url: internal.buildEndpointUrl(publisher), method: ENDPOINT_METHOD, data: { + auctionId: auctionId, + pageUrl: pageUrl, + referrer: referrer, + imp: [] + } + }; + + // add gdpr data + if (gdpr) { + request.data.gdpr = gdpr; + } + + // handle multiple bids per request + groupedBidRequests[publisher].forEach((bidRequest) => { + const bid = { bidId: bidRequest.bidId, - auctionId: bidRequest.auctionId, transactionId: bidRequest.transactionId, adUnitCode: bidRequest.adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequest), - referrer: extractTopWindowReferrerFromBidRequest(bidRequest), - sizes: extractSizesFromBidRequest(bidRequest), - params: extractParamsFromBidRequest(bidRequest), - gdpr: gdpr + params: internal.extractParams(bidRequest) + }; + + // add banner config + const bannerConfig = internal.extractBannerConfig(bidRequest); + if (bannerConfig) { + bid.banner = bannerConfig; + } + + // add native config + const nativeConfig = internal.extractNativeConfig(bidRequest); + if (nativeConfig) { + bid.native = nativeConfig; } + + request.data.imp.push(bid); }); - }); - return bidRequests; + requests.push(request); + } + + return requests; }, /** * Handle bid response * - * @param {*} response - * @returns {*[]} + * @param {Object} response + * @returns {Object[]} */ interpretResponse: (response) => { const bidResponses = []; - if (!response.body || !response.body.bid || !response.body.creative) { + // stop here on invalid or empty data + if (!response || !utils.deepAccess(response, 'body.bids') || response.body.bids.length === 0) { return bidResponses; } - bidResponses.push({ - requestId: response.body.bid.bidId, - cpm: response.body.bid.price, - netRevenue: response.body.bid.net, - currency: response.body.bid.currency, - ttl: response.body.bid.ttl, - creativeId: response.body.creative.id, - width: response.body.creative.width, - height: response.body.creative.height, - ad: response.body.creative.html - }); - - return bidResponses; - } -}; - -/** - * Extracts the possible ad unit sizes from given bid request - * - * @param {*} bidRequest - * @returns {number[]} - */ -export function extractSizesFromBidRequest(bidRequest) { - // since pbjs 3.0 - if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')) { - return bidRequest.mediaTypes.banner.sizes; - - // for backward compatibility - } else if (bidRequest && bidRequest.sizes) { - return bidRequest.sizes; - - // fallback - } else { - return []; - } -} - -/** - * Extracts the custom params from given bid request - * - * @param {*} bidRequest - * @returns {*} - */ -export function extractParamsFromBidRequest(bidRequest) { - if (bidRequest && bidRequest.params) { - return bidRequest.params - } else { - return null; - } -} - -/** - * Extracts the GDPR information from given bidder request - * - * @param {*} bidderRequest - * @returns {*} - */ -export function extractGdprFromBidderRequest(bidderRequest) { - let gdpr = null; - - if (bidderRequest && bidderRequest.gdprConsent) { - gdpr = { - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - }; - } + // parse multiple bids per response + response.body.bids.forEach((bid) => { + if (!bid || !bid.bid || !bid.creative) { + return; + } - return gdpr; -} + const bidResponse = { + requestId: bid.bid.bidId, + cpm: bid.bid.price, + netRevenue: bid.bid.net, + currency: bid.bid.currency, + ttl: bid.bid.ttl, + creativeId: bid.creative.id, + meta: { + advertiserDomains: bid.creative.advertiserDomains + } + } -/** - * Extracts the page url from given bid request or use the (top) window location as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowUrlFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.canonicalUrl')) { - return bidRequest.refererInfo.canonicalUrl; - } + if (bid.creative.html) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.creative.html; + bidResponse.width = bid.creative.width; + bidResponse.height = bid.creative.height; + } - try { - return window.top.location.href; - } catch (e) { - return window.location.href; - } -} + if (bid.creative.native) { + bidResponse.mediaType = NATIVE; + bidResponse.native = bid.creative.native; + } -/** - * Extracts the referrer from given bid request or use the (top) document referrer as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowReferrerFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.referer')) { - return bidRequest.refererInfo.referer; - } + bidResponses.push(bidResponse); + }); - try { - return window.top.document.referrer; - } catch (e) { - return window.document.referrer; + return bidResponses; } -} +}; registerBidder(spec); diff --git a/modules/aduptechBidAdapter.md b/modules/aduptechBidAdapter.md index 281fe24cf9d..25034cecbe8 100644 --- a/modules/aduptechBidAdapter.md +++ b/modules/aduptechBidAdapter.md @@ -1,35 +1,86 @@ -# Overview -``` -Module Name: AdUp Technology Bid Adapter -Module Type: Bidder Adapter -Maintainers: - - steffen.anders@adup-tech.com - - sebastian.briesemeister@adup-tech.com - - marten.lietz@adup-tech.com -``` +# AdUp Technology Bid Adapter -# Description +## Description Connects to AdUp Technology demand sources to fetch bids. -Please use ```aduptech``` as bidder code. Only banner formats are supported. -The AdUp Technology bidding adapter requires setup and approval before beginning. -For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). +**Note:** The bid adapter requires correct setup and approval, including an existing publisher account. For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). + + +## Overview +- Module Name: AdUp Technology Bid Adapter +- Module Type: Bidder Adapter +- Maintainers: + - [steffen.anders@adup-tech.com](mailto:steffen.anders@adup-tech.com) + - [sebastian.briesemeister@adup-tech.com](mailto:sebastian.briesemeister@adup-tech.com) + - [marten.lietz@adup-tech.com](mailto:marten.lietz@adup-tech.com) +- Bidder code: `aduptech` +- Supported media types: `banner`, `native` + +## Paramters +| Name | Scope | Description | Example | +| :--- | :---- | :---------- | :------ | +| `publisher` | required | Unique publisher identifier | `'prebid'` | +| `placement` | required | Unique placement identifier per publisher | `'1234'` | +| `query` | optional | Semicolon separated list of keywords | `'urlaub;ibiza;mallorca'` | +| `adtest` | optional | Deactivates tracking of impressions and clicks. **Should only be used for testing purposes!** | `true` | + -# Test Parameters +## Examples + +### Banner ```js var adUnits = [ { - code: 'banner', + code: "example1", mediaTypes: { banner: { sizes: [[300, 250], [300, 600]], } }, bids: [{ - bidder: 'aduptech', + bidder: "aduptech", + params: { + publisher: "prebid", + placement: "12345" + } + }] + } +]; +``` + +### Native +```js +var adUnits = [ + { + code: "example2", + mediaTypes: { + native: { + image: { + required: true, + sizes: [150, 150] + }, + title: { + required: true + }, + body: { + required: true + }, + clickUrl: { + required: true + }, + displayUrl: { + required: true + }, + sponsoredBy: { + required: true + } + } + }, + bids: [{ + bidder: "aduptech", params: { - publisher: 'prebid', - placement: '12345' + publisher: "prebid", + placement: "12345" } }] } diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js index 1e39e0cfc8b..362cd3e506a 100644 --- a/test/spec/modules/aduptechBidAdapter_spec.js +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -1,180 +1,280 @@ import { expect } from 'chai'; import { BIDDER_CODE, - PUBLISHER_PLACEHOLDER, - ENDPOINT_URL, ENDPOINT_METHOD, - spec, - extractGdprFromBidderRequest, - extractParamsFromBidRequest, - extractSizesFromBidRequest, - extractTopWindowReferrerFromBidRequest, - extractTopWindowUrlFromBidRequest + internal, + spec } from '../../../modules/aduptechBidAdapter.js'; +import { config } from '../../../src/config.js'; +import * as utils from '../../../src/utils.js'; +import { BANNER, NATIVE } from '../../../src/mediaTypes.js' import { newBidder } from '../../../src/adapters/bidderFactory.js'; describe('AduptechBidAdapter', () => { - describe('extractGdprFromBidderRequest', () => { - it('should handle empty bidder request', () => { - const bidderRequest = null; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + describe('internal', () => { + describe('extractGdpr', () => { + it('should handle empty bidderRequest', () => { + expect(internal.extractGdpr(null)).to.be.null; + expect(internal.extractGdpr({})).to.be.null; + }); - it('should handle missing gdprConsent in bidder request', () => { - const bidderRequest = {}; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + it('should extract bidderRequest.gdprConsent', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: false + } + }; - it('should handle gdprConsent in bidder request', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }); + }); + + it('should handle missing bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString' + } + }; - expect(extractGdprFromBidderRequest(bidderRequest)).to.deep.equal({ - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: true + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); }); - }); - }); - describe('extractParamsFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; - }); + it('should handle invalid bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: 'foobar' + } + }; - it('should handle missing params in bid request', () => { - const bidRequest = {}; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); + }); }); - it('should handle params in bid request', () => { - const bidRequest = { - params: { - foo: '123', - bar: 456 - } - }; - expect(extractParamsFromBidRequest(bidRequest)).to.deep.equal(bidRequest.params); - }); - }); + describe('extractPageUrl', () => { + let origPageUrl; - describe('extractSizesFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + beforeEach(() => { + // remember original pageUrl in config + origPageUrl = config.getConfig('pageUrl'); - it('should handle missing sizes in bid request', () => { - const bidRequest = {}; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + // unset pageUrl in config + config.setConfig({ pageUrl: null }); + }); + + afterEach(() => { + // set original pageUrl to config + config.setConfig({ pageUrl: origPageUrl }); + }); + + it('should handle empty or missing data', () => { + expect(internal.extractPageUrl(null)).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({})).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: {} })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: null } })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: '' } })).to.equal(utils.getWindowTop().location.href); + }); + + it('should use "pageUrl" from config', () => { + config.setConfig({ pageUrl: 'http://page.url' }); - it('should handle sizes in bid request', () => { - const bidRequest = { - mediaTypes: { - banner: { - sizes: [[12, 34], [56, 78]] + expect(internal.extractPageUrl({})).to.equal(config.getConfig('pageUrl')); + }); + + it('should use bidderRequest.refererInfo.canonicalUrl', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); + }; - it('should handle sizes in bid request (backward compatibility)', () => { - const bidRequest = { - sizes: [[12, 34], [56, 78]] - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.sizes); - }); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); - it('should prefer sizes in mediaTypes.banner', () => { - const bidRequest = { - sizes: [[12, 34]], - mediaTypes: { - banner: { - sizes: [[56, 78]] + it('should prefer bidderRequest.refererInfo.canonicalUrl over "pageUrl" from config', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); - }); + }; - describe('extractTopWindowReferrerFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + config.setConfig({ pageUrl: 'http://page.url' }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); }); - it('should use fallback if refererInfo.referer in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + describe('extractReferrer', () => { + it('should handle empty or missing data', () => { + expect(internal.extractReferrer(null)).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({})).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: {} })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: null } })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: '' } })).to.equal(utils.getWindowTop().document.referrer); + }); - it('should use fallback if refererInfo.referer in bid request is empty', () => { - const bidRequest = { - refererInfo: { - referer: '' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + it('hould use bidderRequest.refererInfo.referer', () => { + const bidderRequest = { + refererInfo: { + referer: 'foobar' + } + }; - it('should use refererInfo.referer from bid request ', () => { - const bidRequest = { - refererInfo: { - referer: 'foobar' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.referer); + expect(internal.extractReferrer(bidderRequest)).to.equal(bidderRequest.refererInfo.referer); + }); }); - }); - describe('extractTopWindowUrlFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractParams', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractParams(null)).to.be.null; + expect(internal.extractParams({})).to.be.null; + }); + + it('should extract bidRequest.params', () => { + const bidRequest = { + params: { + foo: '123', + bar: 456 + } + }; + expect(internal.extractParams(bidRequest)).to.deep.equal(bidRequest.params); + }); }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractBannerConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractBannerConfig(null)).to.be.null; + expect(internal.extractBannerConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.banner', () => { + const bidRequest = { + mediaTypes: { + banner: { + sizes: [[12, 34], [56, 78]] + } + } + }; + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner); + }); + + it('should extract bidRequest.sizes (backward compatibility)', () => { + const bidRequest = { + sizes: [[12, 34], [56, 78]] + }; + + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal({sizes: bidRequest.sizes}); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractNativeConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractNativeConfig(null)).to.be.null; + expect(internal.extractNativeConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.native', () => { + const bidRequest = { + mediaTypes: { + native: { + image: { + required: true + }, + title: { + required: true + } + } + } + }; + + expect(internal.extractNativeConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.native); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is empty', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: '' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('groupBidRequestsByPublisher', () => { + it('should handle empty bidRequests', () => { + expect(internal.groupBidRequestsByPublisher(null)).to.deep.equal({}); + expect(internal.groupBidRequestsByPublisher([])).to.deep.equal({}) + }); + + it('should group given bidRequests by params.publisher', () => { + const bidRequests = [ + { + mediaTypes: { + banner: { + sizes: [[100, 100]] + } + }, + params: { + publisher: 'publisher1', + placement: '1111' + } + }, + { + mediaTypes: { + banner: { + sizes: [[200, 200]] + } + }, + params: { + publisher: 'publisher2', + placement: '2222' + } + }, + { + mediaTypes: { + banner: { + sizes: [[300, 300]] + } + }, + params: { + publisher: 'publisher3', + placement: '3333' + } + }, + { + mediaTypes: { + banner: { + sizes: [[400, 400]] + } + }, + params: { + publisher: 'publisher1', + placement: '4444' + } + } + ]; + + expect(internal.groupBidRequestsByPublisher(bidRequests)).to.deep.equal({ + publisher1: [ + bidRequests[0], + bidRequests[3] + ], + publisher2: [ + bidRequests[1], + ], + publisher3: [ + bidRequests[2], + ], + }); + }); }); - it('should use refererInfo.canonicalUrl from bid request ', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: 'foobar' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.canonicalUrl); + describe('buildEndpointUrl', () => { + it('should build endpoint url based on given publisher code', () => { + expect(internal.buildEndpointUrl(1234)).to.be.equal('https://rtb.d.adup-tech.com/prebid/1234_bid'); + expect(internal.buildEndpointUrl('foobar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foobar_bid'); + expect(internal.buildEndpointUrl('foo bar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foo%20bar_bid'); + }); }); }); @@ -185,42 +285,31 @@ describe('AduptechBidAdapter', () => { adapter = newBidder(spec); }); - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); + describe('code', () => { + it('should be correct', () => { + expect(adapter.getSpec().code).to.equal(BIDDER_CODE); }); }); - describe('isBidRequestValid', () => { - it('should return true when necessary information is given', () => { - expect(spec.isBidRequestValid({ - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('supportedMediaTypes', () => { + it('should be correct', () => { + expect(adapter.getSpec().supportedMediaTypes).to.deep.equal([BANNER, NATIVE]); }); + }); - it('should return true when necessary information is given (backward compatibility)', () => { - expect(spec.isBidRequestValid({ - sizes: [[100, 200]], - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('inherited functions', () => { + it('should exist and be a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('should return false on empty bid', () => { + describe('isBidRequestValid', () => { + it('should be false on empty bid', () => { + expect(spec.isBidRequestValid(null)).to.be.false; expect(spec.isBidRequestValid({})).to.be.false; }); - it('should return false on missing sizes', () => { + it('should be false if mediaTypes.banner and mediaTypes.native is missing', () => { expect(spec.isBidRequestValid({ params: { publisher: 'test', @@ -229,63 +318,65 @@ describe('AduptechBidAdapter', () => { })).to.be.false; }); - it('should return false on empty sizes', () => { + it('should be false if params missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { - sizes: [] + sizes: [[100, 200]] } }, - params: { - publisher: 'test', - placement: '1234' - } })).to.be.false; }); - it('should return false on empty sizes (backward compatibility)', () => { + it('should be false if params is invalid', () => { expect(spec.isBidRequestValid({ - sizes: [], - params: { - publisher: 'test', - placement: '1234' - } + mediaTypes: { + banner: { + sizes: [[100, 200]] + } + }, + params: 'bar' })).to.be.false; }); - it('should return false on missing params', () => { + it('should be false if params is empty', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, + params: {} })).to.be.false; }); - it('should return false on invalid params', () => { + it('should be false if params.publisher is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: 'bar' + params: { + placement: '1234' + } })).to.be.false; }); - it('should return false on empty params', () => { + it('should be false if params.placement is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: {} + params: { + publisher: 'test' + } })).to.be.false; }); - it('should return false on missing publisher', () => { + it('should be true if mediaTypes.banner is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { @@ -293,34 +384,62 @@ describe('AduptechBidAdapter', () => { } }, params: { + publisher: 'test', placement: '1234' } - })).to.be.false; + })).to.be.true; }); - it('should return false on missing placement', () => { + it('should be true if mediaTypes.native is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { - banner: { - sizes: [[100, 200]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'test' + publisher: 'test', + placement: '1234' } - })).to.be.false; + })).to.be.true; }); }); describe('buildRequests', () => { - it('should send one bid request per ad unit to the endpoint via POST', () => { - const bidRequests = [ + it('should handle empty validBidRequests', () => { + expect(spec.buildRequests(null)).to.deep.equal([]); + expect(spec.buildRequests([])).to.deep.equal([]); + }); + + it('should build one request per publisher', () => { + const bidderRequest = { + auctionId: 'auctionId123', + refererInfo: { + canonicalUrl: 'http://crazy.canonical.url', + referer: 'http://crazy.referer.url' + }, + gdprConsent: { + consentString: 'consentString123', + gdprApplies: true + } + }; + + const validBidRequests = [ { - bidder: BIDDER_CODE, bidId: 'bidId1', adUnitCode: 'adUnitCode1', transactionId: 'transactionId1', - auctionId: 'auctionId1', mediaTypes: { banner: { sizes: [[100, 200], [300, 400]] @@ -332,170 +451,197 @@ describe('AduptechBidAdapter', () => { } }, { - bidder: BIDDER_CODE, bidId: 'bidId2', adUnitCode: 'adUnitCode2', transactionId: 'transactionId2', - auctionId: 'auctionId2', mediaTypes: { banner: { - sizes: [[500, 600]] + sizes: [[100, 200]] } }, params: { - publisher: 'publisher2', + publisher: 'publisher1', placement: 'placement2' } - } - ]; - - const result = spec.buildRequests(bidRequests); - expect(result.length).to.equal(2); - - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[0].params.publisher)); - expect(result[0].method).to.equal(ENDPOINT_METHOD); - expect(result[0].data).to.deep.equal({ - bidId: bidRequests[0].bidId, - auctionId: bidRequests[0].auctionId, - transactionId: bidRequests[0].transactionId, - adUnitCode: bidRequests[0].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[0]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[0]), - sizes: extractSizesFromBidRequest(bidRequests[0]), - params: extractParamsFromBidRequest(bidRequests[0]), - gdpr: null - }); - - expect(result[1].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[1].params.publisher)); - expect(result[1].method).to.equal(ENDPOINT_METHOD); - expect(result[1].data).to.deep.equal({ - bidId: bidRequests[1].bidId, - auctionId: bidRequests[1].auctionId, - transactionId: bidRequests[1].transactionId, - adUnitCode: bidRequests[1].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[1]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[1]), - sizes: extractSizesFromBidRequest(bidRequests[1]), - params: extractParamsFromBidRequest(bidRequests[1]), - gdpr: null - }); - }); - - it('should pass gdpr informations', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; - - const bidRequests = [ + }, { - bidder: BIDDER_CODE, bidId: 'bidId3', adUnitCode: 'adUnitCode3', transactionId: 'transactionId3', - auctionId: 'auctionId3', mediaTypes: { - banner: { - sizes: [[100, 200], [300, 400]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'publisher3', + publisher: 'publisher2', placement: 'placement3' } } ]; - const result = spec.buildRequests(bidRequests, bidderRequest); - expect(result.length).to.equal(1); - expect(result[0].data.gdpr).to.deep.equal(extractGdprFromBidderRequest(bidderRequest)); - }); - - it('should encode publisher param in endpoint url', () => { - const bidRequests = [ + expect(spec.buildRequests(validBidRequests, bidderRequest)).to.deep.equal([ { - bidder: BIDDER_CODE, - bidId: 'bidId1', - adUnitCode: 'adUnitCode1', - transactionId: 'transactionId1', - auctionId: 'auctionId1', - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'crazy publisher key äÖÜ', - placement: 'placement1' + url: internal.buildEndpointUrl(validBidRequests[0].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[0].bidId, + transactionId: validBidRequests[0].transactionId, + adUnitCode: validBidRequests[0].adUnitCode, + params: validBidRequests[0].params, + banner: validBidRequests[0].mediaTypes.banner + }, + { + bidId: validBidRequests[1].bidId, + transactionId: validBidRequests[1].transactionId, + adUnitCode: validBidRequests[1].adUnitCode, + params: validBidRequests[1].params, + banner: validBidRequests[1].mediaTypes.banner + } + ] } }, - ]; - - const result = spec.buildRequests(bidRequests); - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequests[0].params.publisher))); - }); - - it('should handle empty bidRequests', () => { - expect(spec.buildRequests([])).to.deep.equal([]); - }); - }); - - describe('interpretResponse', () => { - it('should correctly interpret the server response', () => { - const serverResponse = { - body: { - bid: { - bidId: 'bidId1', - price: 0.12, - net: true, - currency: 'EUR', - ttl: 123 - }, - creative: { - id: 'creativeId1', - width: 100, - height: 200, - html: '
Hello World
' - } - } - }; - - const result = spec.interpretResponse(serverResponse); - expect(result).to.deep.equal([ { - requestId: serverResponse.body.bid.bidId, - cpm: serverResponse.body.bid.price, - netRevenue: serverResponse.body.bid.net, - currency: serverResponse.body.bid.currency, - ttl: serverResponse.body.bid.ttl, - creativeId: serverResponse.body.creative.id, - width: serverResponse.body.creative.width, - height: serverResponse.body.creative.height, - ad: serverResponse.body.creative.html + url: internal.buildEndpointUrl(validBidRequests[2].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[2].bidId, + transactionId: validBidRequests[2].transactionId, + adUnitCode: validBidRequests[2].adUnitCode, + params: validBidRequests[2].params, + native: validBidRequests[2].mediaTypes.native + } + ] + } } ]); }); + }); + describe('interpretResponse', () => { it('should handle empty serverResponse', () => { + expect(spec.interpretResponse(null)).to.deep.equal([]); expect(spec.interpretResponse({})).to.deep.equal([]); + expect(spec.interpretResponse({ body: {} })).to.deep.equal([]); + expect(spec.interpretResponse({ body: { bids: [] } })).to.deep.equal([]); }); - it('should handle missing bid', () => { - expect(spec.interpretResponse({ + it('should correctly interpret the server response', () => { + const serverResponse = { body: { - creative: {} + bids: [ + { + bid: { + bidId: 'bidId1', + price: 0.12, + net: true, + currency: 'EUR', + ttl: 123 + }, + creative: { + id: 'creativeId1', + advertiserDomains: ['advertiser1.com', 'advertiser2.org'], + width: 100, + height: 200, + html: '
Hello World
' + } + }, + { + bid: { + bidId: 'bidId2', + price: 0.99, + net: false, + currency: 'USD', + ttl: 465 + }, + creative: { + id: 'creativeId2', + advertiserDomains: ['advertiser3.com'], + native: { + title: 'Ad title', + body: 'Ad description', + displayUrl: 'Ad display url', + clickUrl: 'http://click.url/ad.html', + image: { + url: 'https://image.url/ad.png', + width: 123, + height: 456 + }, + sponsoredBy: 'Ad sponsored by', + impressionTrackers: [ + 'https://impression.tracking.url/1', + 'https://impression.tracking.url/2', + ], + privacyLink: 'https://example.com/privacy', + privacyIcon: 'https://example.com/icon.png' + } + } + }, + null, // should be skipped + {} // should be skipped + ] } - })).to.deep.equal([]); - }); + }; - it('should handle missing creative', () => { - expect(spec.interpretResponse({ - body: { - bid: {} + expect(spec.interpretResponse(serverResponse)).to.deep.equal([ + { + requestId: serverResponse.body.bids[0].bid.bidId, + cpm: serverResponse.body.bids[0].bid.price, + netRevenue: serverResponse.body.bids[0].bid.net, + currency: serverResponse.body.bids[0].bid.currency, + ttl: serverResponse.body.bids[0].bid.ttl, + creativeId: serverResponse.body.bids[0].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[0].creative.advertiserDomains + }, + mediaType: BANNER, + width: serverResponse.body.bids[0].creative.width, + height: serverResponse.body.bids[0].creative.height, + ad: serverResponse.body.bids[0].creative.html + }, + { + requestId: serverResponse.body.bids[1].bid.bidId, + cpm: serverResponse.body.bids[1].bid.price, + netRevenue: serverResponse.body.bids[1].bid.net, + currency: serverResponse.body.bids[1].bid.currency, + ttl: serverResponse.body.bids[1].bid.ttl, + creativeId: serverResponse.body.bids[1].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[1].creative.advertiserDomains + }, + mediaType: NATIVE, + native: serverResponse.body.bids[1].creative.native } - })).to.deep.equal([]); + ]); }); }); }); From c04b0248e90642588c6e98ba86190aa916f2c101 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Fri, 28 May 2021 17:35:50 +0700 Subject: [PATCH 1024/1476] QwarryBidAdapter: added schain (#6864) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests * Qwarry bid adapter: remove gdpr field from request * qwarry bid adapter: add sizes * qwarry bid adapter: add sizes * added schain * added test for schain Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: Ekaterina Legostaeva --- modules/qwarryBidAdapter.js | 3 ++- test/spec/modules/qwarryBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 7ed5e5c984c..a410c2fe163 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -28,7 +28,8 @@ export const spec = { let payload = { requestId: bidderRequest.bidderRequestId, bids, - referer: bidderRequest.refererInfo.referer + referer: bidderRequest.refererInfo.referer, + schain: validBidRequests[0].schain } if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 06d4af0756c..6dbb983ea23 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -9,6 +9,15 @@ const REQUEST = { 'params': { zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 + }, + 'schain': { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'qwarry.com', + sid: '00001', + hp: 1 + }] } } @@ -86,6 +95,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') 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: 'qwarry.com', sid: '00001', hp: 1}]}) expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, sizes: [{ width: 100, height: 200 }, { width: 300, height: 400 }] }) expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) From 9c484c029a9b899f95d7adddcd0fb5adb494c77c Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Fri, 28 May 2021 08:41:35 -0400 Subject: [PATCH 1025/1476] PulsePoint Adapter: Fixing issues related to Prebid 5.0 (#6857) --- modules/pulsepointBidAdapter.js | 26 +++++++- modules/pulsepointBidAdapter.md | 35 +++++----- .../spec/modules/pulsepointBidAdapter_spec.js | 64 ++++++++++++++++++- 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index f74d79a3dc5..adea33fc3b9 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -119,7 +119,8 @@ function bidResponseAvailable(request, response) { adId: id, ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, netRevenue: DEFAULT_NET_REVENUE, - currency: bidResponse.cur || DEFAULT_CURRENCY + currency: bidResponse.cur || DEFAULT_CURRENCY, + meta: { advertiserDomains: idToBidMap[id].adomain || [] } }; if (idToImpMap[id].video) { // for outstream, a renderer is specified @@ -154,7 +155,7 @@ function impression(slot) { 'native': nativeImpression(slot), tagid: slot.params.ct.toString(), video: video(slot), - bidfloor: slot.params.bidfloor, + bidfloor: bidFloor(slot), ext: ext(slot), }; } @@ -192,7 +193,11 @@ function parseSizes(slot) { */ function video(slot) { if (slot.params.video) { - return Object.assign({}, slot.params.video, {battr: slot.params.battr}); + return Object.assign({}, + slot.params.video, // previously supported as bidder param + slot.mediaTypes && slot.mediaTypes.video ? slot.mediaTypes.video : {}, // params on mediaTypes.video + {battr: slot.params.battr} + ); } return null; } @@ -519,4 +524,19 @@ function nativeResponse(imp, bid) { return null; } +function bidFloor(slot) { + let floor = slot.params.bidfloor; + if (utils.isFn(slot.getFloor)) { + const floorData = slot.getFloor({ + mediaType: slot.mediaTypes.banner ? 'banner' : slot.mediaTypes.video ? 'video' : 'Native', + size: '*', + currency: DEFAULT_CURRENCY, + }); + if (floorData && floorData.floor) { + floor = floorData.floor; + } + } + return floor; +} + registerBidder(spec); diff --git a/modules/pulsepointBidAdapter.md b/modules/pulsepointBidAdapter.md index 36d4ef6cce5..7f4b7e6b611 100644 --- a/modules/pulsepointBidAdapter.md +++ b/modules/pulsepointBidAdapter.md @@ -45,23 +45,21 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'outstream' + context: 'outstream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + mimes: ["video/mp4", "video/ogg", "video/webm"], + pos: 3 } }, bids: [{ bidder: 'pulsepoint', params: { cp: 512379, - ct: 505642, - video: { - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - linearity: 1, - mimes: ["video/mp4", "video/ogg", "video/webm"], - pos: 3 - } + ct: 505642 } }], renderer: { @@ -74,7 +72,12 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'instream' + context: 'instream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + protocols: [2,3,5] } }, bids: [{ @@ -82,14 +85,6 @@ Please use ```pulsepoint``` as the bidder code. params: { cp: 512379, ct: 694973, - video: { - battr: [1,3], - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - protocols: [2,3,5] - } } }] }]; diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index c3830d5cb46..6630cb0907c 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -254,7 +254,7 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ttl).to.equal(20); }); - it('Verify ttl/currency applied to bid', function () { + it('Verify ttl/currency/adomain applied to bid', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); const ortbRequest = request.data; const ortbResponse = { @@ -264,7 +264,8 @@ describe('PulsePoint Adapter Tests', function () { price: 1.25, adm: 'This is an Ad#1', crid: 'Creative#123', - exp: 50 + exp: 50, + adomain: ['advertiser.com'] }, { impid: ortbRequest.imp[1].id, price: 1.25, @@ -282,11 +283,15 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ad).to.equal('This is an Ad#1'); expect(bid.ttl).to.equal(50); expect(bid.currency).to.equal('GBP'); + expect(bid.meta).to.not.be.null; + expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); const secondBid = bids[1]; expect(secondBid.cpm).to.equal(1.25); expect(secondBid.ad).to.equal('This is an Ad#2'); expect(secondBid.ttl).to.equal(20); expect(secondBid.currency).to.equal('GBP'); + expect(secondBid.meta).to.not.be.null; + expect(secondBid.meta.advertiserDomains).to.eql([]); }); it('Verify full passback', function () { @@ -778,4 +783,59 @@ describe('PulsePoint Adapter Tests', function () { const secondBid = bids[1]; expect(secondBid.vastXml).to.equal(''); }); + it('Verify bid floor', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].params.bidfloor = 1.05; + let request = spec.buildRequests(bidRequests, bidderRequest); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.05); + expect(ortbRequest.imp[1].bidfloor).to.be.undefined; + let floorArg = null; + // publisher uses the floor module + bidRequests[0].getFloor = (arg) => { + floorArg = arg; + return { floor: 1.25 }; + }; + bidRequests[1].getFloor = () => { + return { floor: 2.05 }; + }; + request = spec.buildRequests(bidRequests, bidderRequest); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.25); + expect(ortbRequest.imp[1].bidfloor).to.equal(2.05); + expect(floorArg).to.not.be.null; + expect(floorArg.mediaType).to.equal('banner'); + expect(floorArg.currency).to.equal('USD'); + expect(floorArg.size).to.equal('*'); + }); + it('Verify Video params on mediaTypes.video', function () { + const bidRequests = deepClone(videoSlotConfig); + bidRequests[0].mediaTypes = { + video: { + w: 600, + h: 400, + minduration: 15, + maxduration: 20, + startdelay: 10, + skip: 0, + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(600); + expect(ortbRequest.imp[0].video.h).to.equal(400); + expect(ortbRequest.imp[0].video.minduration).to.equal(15); + expect(ortbRequest.imp[0].video.maxduration).to.equal(20); + expect(ortbRequest.imp[0].video.startdelay).to.equal(10); + expect(ortbRequest.imp[0].video.skip).to.equal(0); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); }); From 5b855a614842f6bb52ef676aaf6e326a1cd0a41d Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Fri, 28 May 2021 06:14:08 -0700 Subject: [PATCH 1026/1476] Bugfix: Parrable Cookie Length in Testing (#6869) --- test/spec/modules/parrableIdSystem_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index c18574aec7b..256be7340b7 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -12,7 +12,7 @@ import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const EXPIRE_COOKIE_TIME = 20000; +const EXPIRE_COOKIE_TIME = 864000000; const P_COOKIE_NAME = '_parrable_id'; const P_COOKIE_EID = '01.1563917337.test-eid'; const P_XHR_EID = '01.1588030911.test-new-eid' From 7ce7b09d43625499675b8511982e99fab9164502 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Fri, 28 May 2021 17:03:11 +0200 Subject: [PATCH 1027/1476] Add meta.advertiserDomains support (#6870) Co-authored-by: francesco --- modules/onetagBidAdapter.js | 3 ++- test/spec/modules/onetagBidAdapter_spec.js | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 16b8096646f..0974f9db4cf 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -101,7 +101,8 @@ function interpretResponse(serverResponse, bidderRequest) { netRevenue: bid.netRevenue || false, mediaType: bid.mediaType, meta: { - mediaType: bid.mediaType + mediaType: bid.mediaType, + advertiserDomains: bid.adomain }, ttl: bid.ttl || 300 }; diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index c1462c3814d..5a1f30a0de8 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -240,6 +240,7 @@ describe('onetag', function () { expect(dataItem.creativeId).to.be.a('string'); expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); + expect(dataItem.meta.advertiserDomains).to.be.an('array'); } }); it('Returns an empty array if response is not valid', function () { @@ -323,6 +324,7 @@ function getBannerVideoResponse() { currency: 'USD', requestId: 'banner', mediaType: BANNER, + adomain: [] }, { cpm: 13, @@ -334,7 +336,8 @@ function getBannerVideoResponse() { requestId: 'videoInstream', vastUrl: 'https://videoinstream.org', videoCacheKey: 'key', - mediaType: VIDEO + mediaType: VIDEO, + adomain: ['test_domain'] }, { cpm: 13, @@ -347,7 +350,8 @@ function getBannerVideoResponse() { requestId: 'videoOutstream', ad: '', rendererUrl: 'https://testRenderer', - mediaType: VIDEO + mediaType: VIDEO, + adomain: [] } ] } From c73e59f20360c7ccc17835ea2be23c23026008ec Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Fri, 28 May 2021 08:04:00 -0700 Subject: [PATCH 1028/1476] adding GAM ad unit code to impression ext object (#6861) Co-authored-by: punkiller --- modules/ixBidAdapter.js | 9 +++++++++ test/spec/modules/ixBidAdapter_spec.js | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 6970eefeac2..4837c9c061f 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -112,6 +112,11 @@ function bidToVideoImp(bid) { return imp; } +/** + * Converts an incoming PBJS bid to an IX Impression + * @param {object} bid PBJS bid object + * @returns {object} IX impression object + */ function bidToImp(bid) { const imp = {}; @@ -127,6 +132,10 @@ function bidToImp(bid) { imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } + const dfpAdUnitCode = utils.deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); + if (dfpAdUnitCode) { + imp.ext.dfp_ad_unit_code = dfpAdUnitCode; + } return imp; } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index c026dfd3efc..48c8e48e6af 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1251,6 +1251,32 @@ describe('IndexexchangeAdapter', function () { expect(query.nf).not.to.exist; }); + it('should send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot exists', function () { + const AD_UNIT_CODE = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + expect(dfp_ad_unit_code).to.equal(AD_UNIT_CODE); + }); + + it('should not send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot does not exists', function () { + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + + expect(dfp_ad_unit_code).to.not.exist; + }); + it('payload should have correct format and value', function () { const payload = JSON.parse(query.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); From 6f81c1617aaa44ec68670887d8bd0fb032f720b1 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Fri, 28 May 2021 21:59:12 +0600 Subject: [PATCH 1029/1476] Zeta Ssp Bid Adapter: support for eids (#6819) --- modules/zetaSspBidAdapter.js | 9 +++++- test/spec/modules/zetaSspBidAdapter_spec.js | 34 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 258478b0886..c956df55a1f 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -43,7 +43,7 @@ export const spec = { const secure = 1; // treat all requests as secure const request = validBidRequests[0]; const params = request.params; - let impData = { + const impData = { id: request.bidId, secure: secure, banner: buildBanner(request) @@ -84,6 +84,7 @@ export const spec = { } }; } + provideEids(request, payload); return { method: 'POST', url: ENDPOINT_URL, @@ -172,4 +173,10 @@ function buildBanner(request) { }; } +function provideEids(request, payload) { + if (Array.isArray(request.userIdAsEids) && request.userIdAsEids.length > 0) { + utils.deepSetValue(payload, 'user.ext.eids', request.userIdAsEids); + } +} + registerBidder(spec); diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 2766632f707..4602e2d2b77 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,29 @@ import {spec} from '../../../modules/zetaSspBidAdapter.js' -describe('Zeta Ssp Bid Adapter', function () { +describe('Zeta Ssp Bid Adapter', function() { + const eids = [ + { + 'source': 'example.com', + 'uids': [ + { + 'id': 'someId1', + 'atype': 1 + }, + { + 'id': 'someId2', + 'atype': 1 + }, + { + 'id': 'someId3', + 'atype': 2 + } + ], + 'ext': { + 'foo': 'bar' + } + } + ]; + const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -23,7 +46,8 @@ describe('Zeta Ssp Bid Adapter', function () { sid: 'publisherId' }, test: 1 - } + }, + userIdAsEids: eids }]; it('Test the bid validation function', function () { @@ -34,6 +58,12 @@ describe('Zeta Ssp Bid Adapter', function () { expect(invalidBid).to.be.false; }); + it('Test provide eids', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.user.ext.eids).to.eql(eids); + }); + it('Test the request processing function', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); expect(request).to.not.be.empty; From 983d8a9d7aa315b5a4ca01ca5a744375e9b26d14 Mon Sep 17 00:00:00 2001 From: Andrzej Michnowski Date: Fri, 28 May 2021 18:38:11 +0200 Subject: [PATCH 1030/1476] Adocean Bid Adapter: add meta.advertiserDomains to bid response (#6872) * AdOcean adapter - support for multiple sizes * AdOcean adapter - tests - use normal functions instead of arrow ones for consistency * AdOcean adapter - support for multiple sizes - changed way of sending dimensions * AdOcean adapter - change separator between sizes group * AdOcean adapter - small fix in buildRequest * AdOcean adapter - support advertiserDomains * AdOcean adapter - add advertiserDomains default value Co-authored-by: Marcin Muras Co-authored-by: Andrzej Michnowski --- modules/adoceanBidAdapter.js | 5 ++++- test/spec/modules/adoceanBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index b75d1972f5a..7ce9c7ae8f2 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -80,7 +80,10 @@ function interpretResponse(placementResponse, bidRequest, bids) { width: parseInt(placementResponse.width, 10), netRevenue: false, ttl: parseInt(placementResponse.ttl), - creativeId: placementResponse.crid + creativeId: placementResponse.crid, + meta: { + advertiserDomains: placementResponse.adomain || [] + } }; bids.push(bid); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 2b4b7d711e1..43316cd7483 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -150,7 +150,8 @@ describe('AdoceanAdapter', function () { 'width': '300', 'height': '250', 'crid': '0af345b42983cc4bc0', - 'ttl': '300' + 'ttl': '300', + 'adomain': ['adocean.pl'] } ], 'headers': { @@ -186,7 +187,10 @@ describe('AdoceanAdapter', function () { 'ad': '', 'creativeId': '0af345b42983cc4bc0', 'ttl': 300, - 'netRevenue': false + 'netRevenue': false, + 'meta': { + 'advertiserDomains': ['adocean.pl'] + } } ]; @@ -197,6 +201,8 @@ describe('AdoceanAdapter', function () { resultKeys.forEach(function(k) { if (k === 'ad') { expect(result[0][k]).to.match(/$/); + } else if (k === 'meta') { + expect(result[0][k]).to.deep.equal(expectedResponse[0][k]); } else { expect(result[0][k]).to.equal(expectedResponse[0][k]); } From 00b1afac40647fd61d608032d37eaf8ee3160936 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Fri, 28 May 2021 11:58:29 -0700 Subject: [PATCH 1031/1476] No Bid Adapter: added bidResponse.meta and advertiserDomains (#6862) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. * Added support for the "meta" attribute in bid response. * Removed IDE files. Co-authored-by: Reda Guermas --- modules/nobidBidAdapter.js | 5 ++- test/spec/modules/nobidBidAdapter_spec.js | 53 ++++++++++++++++++++--- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 051202cab97..3aabd8f0635 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.9'; +window.nobidVersion = '1.3.0'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -298,6 +298,9 @@ function nobidInterpretResponse(response, bidRequest) { if (bid.videoCacheKey) { bidResponse.videoCacheKey = bid.videoCacheKey; } + if (bid.meta) { + bidResponse.meta = bid.meta; + } bidResponses.push(bidResponse); } diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 8dc15fbc89e..c44b0ce3fc2 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -257,12 +257,12 @@ describe('Nobid Adapter', function () { 'uids': [ { 'id': 'ID5_ID', - 'atype': 1, - 'ext': { - 'linkType': 0 - } + 'atype': 1 } - ] + ], + 'ext': { + 'linkType': 0 + } }, { 'source': 'adserver.org', @@ -284,7 +284,7 @@ describe('Nobid Adapter', function () { refererInfo: {referer: REFERER} } - it('should get user ids from eids', function () { + it('should criteo eid', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.exist.and.to.equal(2); @@ -598,6 +598,47 @@ describe('Nobid Adapter', function () { }); }); + describe('interpretResponseWithMeta', function () { + const CREATIVE_ID_300x250 = 'CREATIVE-100'; + const ADUNIT_300x250 = 'ADUNIT-1'; + const ADMARKUP_300x250 = 'ADMARKUP-300x250'; + const PRICE_300x250 = 0.51; + const REQUEST_ID = '3db3773286ee59'; + const DEAL_ID = 'deal123'; + const ADOMAINS = ['adomain1', 'adomain2']; + let response = { + country: 'US', + ip: '68.83.15.75', + device: 'COMPUTER', + site: 2, + bids: [ + {id: 1, + bdrid: 101, + divid: ADUNIT_300x250, + dealid: DEAL_ID, + creativeid: CREATIVE_ID_300x250, + size: {'w': 300, 'h': 250}, + adm: ADMARKUP_300x250, + price: '' + PRICE_300x250, + meta: { + advertiserDomains: ADOMAINS + } + } + ] + }; + + it('should meta.advertiserDomains be respected', function () { + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result[0].meta.advertiserDomains).to.equal(ADOMAINS); + }); + }); + describe('buildRequestsWithSupplyChain', function () { const SITE_ID = 2; let bidRequests = [ From 2bd62fa41d91460ec3734ecc8a07f7a8fb4c6847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Sat, 29 May 2021 14:26:00 +0300 Subject: [PATCH 1032/1476] add meta.advertiserDomains and meta.mediaType to bid response (#6875) --- modules/cointrafficBidAdapter.js | 6 ++++- .../modules/cointrafficBidAdapter_spec.js | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 43f5cc01ccf..a6241980e06 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -85,7 +85,11 @@ export const spec = { height: response.height, creativeId: response.creativeId, ttl: response.ttl, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: response.mediaType + } }; bidResponses.push(bidResponse); diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index a2ce4cedea0..3755ddc4c4a 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -142,7 +142,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -155,7 +157,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); @@ -186,7 +194,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -199,7 +209,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; const getConfigStub = sinon.stub(config, 'getConfig').returns('USD'); From bf610f6074137a16fa6e1b6131bf9bac1b3f4943 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 31 May 2021 09:17:49 +0200 Subject: [PATCH 1033/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4c6cd24181..6df9993806c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.41.0", + "version": "4.42.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e17f231032c31bd5fe1529c1e33e1398ecdf57aa Mon Sep 17 00:00:00 2001 From: arasaki-yuki <35788388+arasaki-yuki@users.noreply.github.com> Date: Mon, 31 May 2021 23:11:59 +0900 Subject: [PATCH 1034/1476] GMOSSP BidAdapter: add adomain support (#6877) --- modules/gmosspBidAdapter.js | 4 ++++ test/spec/modules/gmosspBidAdapter_spec.js | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 5eac4069b21..bf57e63d53c 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -94,6 +94,10 @@ export const spec = { ttl: res.ttl || 300 }; + if (res.adomains) { + utils.deepSetValue(bid, 'meta.advertiserDomains', Array.isArray(res.adomains) ? res.adomains : [res.adomains]); + } + return [bid]; }, diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index 52bb8460caa..d29e27554c2 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -106,6 +106,9 @@ describe('GmosspAdapter', function () { price: 20, w: 300, h: 250, + adomains: [ + 'test.com' + ], ad: '
', creativeId: '985ec572b32be309.76973017', cur: 'JPY', @@ -126,6 +129,11 @@ describe('GmosspAdapter', function () { currency: 'JPY', width: 300, height: 250, + meta: { + advertiserDomains: [ + 'test.com' + ] + }, ad: '
', creativeId: '985ec572b32be309.76973017', netRevenue: true, From 262e27c0ba0469ee415d75c2ffa54e81513eb15a Mon Sep 17 00:00:00 2001 From: Junus Date: Mon, 31 May 2021 20:13:58 +0600 Subject: [PATCH 1035/1476] A4G Bid Adapter: add meta adomain (#6878) * A4G adapter: add meta.advertiserDomains * added another test --- modules/a4gBidAdapter.js | 5 ++++- test/spec/modules/a4gBidAdapter_spec.js | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index 1961dba1f10..01c59616dc0 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -77,7 +77,10 @@ export const spec = { currency: A4G_CURRENCY, netRevenue: true, ttl: A4G_TTL, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length > 0 ? response.adomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index cb05fa62ab6..a9ead9fd021 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -161,7 +161,11 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } + } ]; const result = spec.interpretResponse(bidResponse, bidRequest); @@ -181,7 +185,10 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } } ]; const result = spec.interpretResponse(bidResponseWithoutCrid, bidRequest); @@ -201,7 +208,8 @@ describe('a4gAdapterTests', function () { 'currency', 'netRevenue', 'ttl', - 'ad' + 'ad', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -215,5 +223,13 @@ describe('a4gAdapterTests', function () { expect(result[0].requestId).to.not.equal(result[0].adId); }) + + it('advertiserDomains is included when sent by server', function () { + bidResponse.body[0].adomain = ['test_adomain']; + let response = spec.interpretResponse(bidResponse, bidRequest); + expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); + expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); + delete bidResponse.body[0].adomain; + }); }); }); From 8183e8500ee37c8445ae68949326741a4b3eb233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Mon, 31 May 2021 16:17:38 +0200 Subject: [PATCH 1036/1476] add pbjs version to bid request (#6879) --- modules/outbrainBidAdapter.js | 8 ++++++++ test/spec/modules/outbrainBidAdapter_spec.js | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index c0af09c2b50..052122e95f1 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -79,6 +79,14 @@ export const spec = { imp: imps, bcat: bcat, badv: badv, + ext: { + prebid: { + channel: { + name: 'pbjs', + version: '$prebid.version$' + } + } + } }; if (test) { diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index 4bdd657f419..a5f23240a7c 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -204,7 +204,14 @@ describe('Outbrain Adapter', function () { request: JSON.stringify(expectedNativeAssets) } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') @@ -244,7 +251,14 @@ describe('Outbrain Adapter', function () { ] } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') From 10fc2a4be963b63917eb5ccd04ebf96b80a0df74 Mon Sep 17 00:00:00 2001 From: PWyrembak Date: Mon, 31 May 2021 18:56:40 +0300 Subject: [PATCH 1037/1476] TrustX Bid Adapter: meta.advertiserDomains support (#6891) * 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 * TrustX Bid Adapter: added us_privacy parameter in bid request * TrustX Bid Adapter: fix us_privacy parameter in bid request * Fix alias error for TrustX Bid Adapter * TrustX Bid Adapter: added new request format * TrustX Bid adapter: fix new format endpoint * TrustX Bid Adapter: update md file to support useNewFormat parameter * TrustX Bid Adapter: added additional sync url * TrustX Bid Adapter: added check for enabled syncs number + added gdpr data to sync urls * TrustX Bid Adapter: added support of meta.advertiserDomains --- modules/trustxBidAdapter.js | 5 ++- test/spec/modules/trustxBidAdapter_spec.js | 44 +++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 9f8de30a0d4..247a1f6d048 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -179,7 +179,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst) { currency: 'USD', netRevenue: newFormat ? false : priceType !== 'gross', ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, }; if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index c0155f87ab6..f53116f60a4 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -661,7 +661,7 @@ describe('TrustXAdapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300, 'adomain': ['somedomain.com']}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, @@ -699,6 +699,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, } ]; @@ -756,6 +759,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, }, { 'requestId': '4dff80cc4ee346', @@ -769,6 +775,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '5703af74d0472a', @@ -782,6 +791,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -909,6 +921,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '4e111f1b66e4', @@ -922,6 +937,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '26d6f897b516', @@ -935,6 +953,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '326bde7fbf69', @@ -948,6 +969,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1009,6 +1033,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '57b2ebe70e16', @@ -1022,6 +1049,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1082,6 +1112,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1161,6 +1194,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1178,6 +1214,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1195,6 +1234,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' From 6be24d483e415dee4bdfd145803d2cb4510273dc Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 31 May 2021 19:17:47 +0300 Subject: [PATCH 1038/1476] Adkernel: denakop alias (#6886) --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index e4b04a27d42..b18b2333bac 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -68,7 +68,8 @@ export const spec = { {code: 'bcm'}, {code: 'engageadx'}, {code: 'converge_digital', gvlid: 248}, - {code: 'adomega'} + {code: 'adomega'}, + {code: 'denakop'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 59b0fc9c033a4c97fc01e6859a0d6c2a9d69e77e Mon Sep 17 00:00:00 2001 From: mediaconsortium-develop <76139568+mediaconsortium-develop@users.noreply.github.com> Date: Tue, 1 Jun 2021 01:58:48 +0900 Subject: [PATCH 1039/1476] Digital Garage RTD module: new rtd submodule #6410 (#6847) * re-open pull request reference #6410 * delete Overview --- modules/.submodules.json | 1 + modules/dgkeywordRtdProvider.js | 132 +++++++ modules/dgkeywordRtdProvider.md | 29 ++ .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ++++++++++++++++++ 4 files changed, 488 insertions(+) create mode 100644 modules/dgkeywordRtdProvider.js create mode 100644 modules/dgkeywordRtdProvider.md create mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 0a10044a78e..74ebc021a6d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js new file mode 100644 index 00000000000..52fa9fd70c4 --- /dev/null +++ b/modules/dgkeywordRtdProvider.js @@ -0,0 +1,132 @@ +/** + * This module adds dgkeyword provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will get keywords from 1plux profile api. + * This module can work only with AppNexusBidAdapter. + * @module modules/dgkeywordProvider + * @requires module:modules/realTimeData + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +/** + * get keywords from api server. and set keywords. + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} moduleConfig + * @param {Object} userConsent + */ +export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { + const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; + const PROFILE_TIMEOUT_MS = 1000; + 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; + let isFinish = false; + utils.logMessage('[dgkeyword sub module]', adUnits, timeout); + let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); + if (setKeywordTargetBidders.length <= 0) { + utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); + callback(); + } else { + utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); + utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); + ajax(url, { + success: function(response) { + const res = JSON.parse(response); + if (!isFinish) { + utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); + if (res) { + let keywords = {}; + if (res['s'] != null && res['s'].length > 0) { + keywords['opeaud'] = res['s']; + } + if (res['t'] != null && res['t'].length > 0) { + keywords['opectx'] = res['t']; + } + if (Object.keys(keywords).length > 0) { + const targetBidKeys = {} + for (let bid of setKeywordTargetBidders) { + // set keywords to params + bid.params.keywords = keywords; + if (!targetBidKeys[bid.bidder]) { + targetBidKeys[bid.bidder] = true; + } + } + + // set keywrods to ortb2 + let addOrtb2 = {}; + utils.deepSetValue(addOrtb2, 'site.keywords', keywords); + utils.deepSetValue(addOrtb2, 'user.keywords', keywords); + const ortb2 = {ortb2: addOrtb2}; + getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); + } + } + isFinish = true; + } + callback(); + }, + error: function(errorStatus) { + // error occur + utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); + callback(); + } + }, null, { + withCredentials: true, + contentType: 'application/json', + }); + setTimeout(function () { + if (!isFinish) { + // profile api timeout + utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); + isFinish = true; + } + callback(); + }, timeout); + } +} + +/** + * get all bidder which hava {dgkeyword: true} in params + * @param {Object} adUnits + */ +export function getTargetBidderOfDgKeywords(adUnits) { + let setKeywordTargetBidders = []; + for (let adUnit of adUnits) { + for (let bid of adUnit.bids) { + if (bid.params && bid.params['dgkeyword'] === true) { + delete bid.params['dgkeyword']; + setKeywordTargetBidders.push(bid); + } + } + } + return setKeywordTargetBidders; +} + +/** @type {RtdSubmodule} */ +export const dgkeywordSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'dgkeyword', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getBidRequestData: getDgKeywordsAndSet, + init: init, +}; + +function init(moduleConfig) { + return true; +} + +function registerSubModule() { + submodule('realTimeData', dgkeywordSubmodule); +} +registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md new file mode 100644 index 00000000000..314475b45a8 --- /dev/null +++ b/modules/dgkeywordRtdProvider.md @@ -0,0 +1,29 @@ + ## Integration + +1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: + +``` +gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var DGKEYWORD_TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: DGKEYWORD_TIMEOUT, + dataProviders: [{ + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DGKEYWORD_TIMEOUT + } + }] + } +}); +``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js new file mode 100644 index 00000000000..0fac92e5118 --- /dev/null +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -0,0 +1,326 @@ +import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; +import { cloneDeep } from 'lodash'; +import { server } from 'test/mocks/xhr.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { config } from 'src/config.js'; + +const DG_GET_KEYWORDS_TIMEOUT = 1950; +const DEF_CONFIG = { + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DG_GET_KEYWORDS_TIMEOUT, + }, +}; +const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; +const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; +const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; +const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; + +describe('Digital Garage Keyword Module', function () { + it('should init and return always true', function () { + expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); + }); + + describe('dgkeyword target test', function () { + it('should have no target', function () { + const adUnits_no_target = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: false, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') + .that.is.empty; + }); + it('should have targets', function () { + const adUnits_targets = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + dgkeyword: true, + }, + }, + ], + }, + ]; + const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); + expect(targets[0].bidder).to.be.equal('dg2'); + expect(targets[0].params.placementId).to.be.equal(99999998); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].bidder).to.be.equal('dg'); + expect(targets[1].params.placementId).to.be.equal(99999996); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + }); + }); + + describe('get profile.', function () { + const AD_UNITS = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + it('should get profiles error(404).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond(404); + }); + it('should get profiles timeout.', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + moduleConfig.params.timeout = 10; + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + setTimeout(() => { + const request = server.requests[0]; + if (request) { + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + } + }, 1000) + }); + it('should get profiles ok(200).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet(pdjs, () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); + + done(); + }, moduleConfig, null); + const request = server.requests[0]; + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + }); + }); +}); From b5f15cf6a4e9e9f12d4b1a58a6a14635e9495755 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 31 May 2021 12:26:04 -0700 Subject: [PATCH 1040/1476] Revert "Digital Garage RTD module: new rtd submodule #6410 (#6847)" (#6895) This reverts commit 59b0fc9c033a4c97fc01e6859a0d6c2a9d69e77e. --- modules/.submodules.json | 1 - modules/dgkeywordRtdProvider.js | 132 ------- modules/dgkeywordRtdProvider.md | 29 -- .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ------------------ 4 files changed, 488 deletions(-) delete mode 100644 modules/dgkeywordRtdProvider.js delete mode 100644 modules/dgkeywordRtdProvider.md delete mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 74ebc021a6d..0a10044a78e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,7 +36,6 @@ ], "rtdModule": [ "browsiRtdProvider", - "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js deleted file mode 100644 index 52fa9fd70c4..00000000000 --- a/modules/dgkeywordRtdProvider.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * This module adds dgkeyword provider to the real time data module - * The {@link module:modules/realTimeData} module is required - * The module will get keywords from 1plux profile api. - * This module can work only with AppNexusBidAdapter. - * @module modules/dgkeywordProvider - * @requires module:modules/realTimeData - */ - -import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getGlobal } from '../src/prebidGlobal.js'; - -/** - * get keywords from api server. and set keywords. - * @param {Object} reqBidsConfigObj - * @param {function} callback - * @param {Object} moduleConfig - * @param {Object} userConsent - */ -export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { - const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; - const PROFILE_TIMEOUT_MS = 1000; - 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; - let isFinish = false; - utils.logMessage('[dgkeyword sub module]', adUnits, timeout); - let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); - if (setKeywordTargetBidders.length <= 0) { - utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); - callback(); - } else { - utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); - utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); - ajax(url, { - success: function(response) { - const res = JSON.parse(response); - if (!isFinish) { - utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); - if (res) { - let keywords = {}; - if (res['s'] != null && res['s'].length > 0) { - keywords['opeaud'] = res['s']; - } - if (res['t'] != null && res['t'].length > 0) { - keywords['opectx'] = res['t']; - } - if (Object.keys(keywords).length > 0) { - const targetBidKeys = {} - for (let bid of setKeywordTargetBidders) { - // set keywords to params - bid.params.keywords = keywords; - if (!targetBidKeys[bid.bidder]) { - targetBidKeys[bid.bidder] = true; - } - } - - // set keywrods to ortb2 - let addOrtb2 = {}; - utils.deepSetValue(addOrtb2, 'site.keywords', keywords); - utils.deepSetValue(addOrtb2, 'user.keywords', keywords); - const ortb2 = {ortb2: addOrtb2}; - getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); - } - } - isFinish = true; - } - callback(); - }, - error: function(errorStatus) { - // error occur - utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); - callback(); - } - }, null, { - withCredentials: true, - contentType: 'application/json', - }); - setTimeout(function () { - if (!isFinish) { - // profile api timeout - utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); - isFinish = true; - } - callback(); - }, timeout); - } -} - -/** - * get all bidder which hava {dgkeyword: true} in params - * @param {Object} adUnits - */ -export function getTargetBidderOfDgKeywords(adUnits) { - let setKeywordTargetBidders = []; - for (let adUnit of adUnits) { - for (let bid of adUnit.bids) { - if (bid.params && bid.params['dgkeyword'] === true) { - delete bid.params['dgkeyword']; - setKeywordTargetBidders.push(bid); - } - } - } - return setKeywordTargetBidders; -} - -/** @type {RtdSubmodule} */ -export const dgkeywordSubmodule = { - /** - * used to link submodule with realTimeData - * @type {string} - */ - name: 'dgkeyword', - /** - * get data and send back to realTimeData module - * @function - * @param {string[]} adUnitsCodes - */ - getBidRequestData: getDgKeywordsAndSet, - init: init, -}; - -function init(moduleConfig) { - return true; -} - -function registerSubModule() { - submodule('realTimeData', dgkeywordSubmodule); -} -registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md deleted file mode 100644 index 314475b45a8..00000000000 --- a/modules/dgkeywordRtdProvider.md +++ /dev/null @@ -1,29 +0,0 @@ - ## Integration - -1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: - -``` -gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." -``` - -2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. - -## Configuration - -This module is configured as part of the `realTimeData.dataProviders` - -```javascript -var DGKEYWORD_TIMEOUT = 1000; -pbjs.setConfig({ - realTimeData: { - auctionDelay: DGKEYWORD_TIMEOUT, - dataProviders: [{ - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DGKEYWORD_TIMEOUT - } - }] - } -}); -``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js deleted file mode 100644 index 0fac92e5118..00000000000 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ /dev/null @@ -1,326 +0,0 @@ -import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; -import { cloneDeep } from 'lodash'; -import { server } from 'test/mocks/xhr.js'; -import { getGlobal } from 'src/prebidGlobal.js'; -import { config } from 'src/config.js'; - -const DG_GET_KEYWORDS_TIMEOUT = 1950; -const DEF_CONFIG = { - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DG_GET_KEYWORDS_TIMEOUT, - }, -}; -const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; -const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; -const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; -const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; - -describe('Digital Garage Keyword Module', function () { - it('should init and return always true', function () { - expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); - }); - - describe('dgkeyword target test', function () { - it('should have no target', function () { - const adUnits_no_target = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: false, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') - .that.is.empty; - }); - it('should have targets', function () { - const adUnits_targets = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - dgkeyword: true, - }, - }, - ], - }, - ]; - const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); - expect(targets[0].bidder).to.be.equal('dg2'); - expect(targets[0].params.placementId).to.be.equal(99999998); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].bidder).to.be.equal('dg'); - expect(targets[1].params.placementId).to.be.equal(99999996); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - }); - }); - - describe('get profile.', function () { - const AD_UNITS = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - it('should get profiles error(404).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - const request = server.requests[0]; - request.respond(404); - }); - it('should get profiles timeout.', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - moduleConfig.params.timeout = 10; - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - setTimeout(() => { - const request = server.requests[0]; - if (request) { - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - } - }, 1000) - }); - it('should get profiles ok(200).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet(pdjs, () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); - - done(); - }, moduleConfig, null); - const request = server.requests[0]; - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - }); - }); -}); From 1fe48accb649eb0296c060a647a87b5e260b2b17 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Tue, 1 Jun 2021 02:30:44 +0700 Subject: [PATCH 1041/1476] Qwarry Bid Adapter: meta.advertiserDomains added (#6883) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests * Qwarry bid adapter: remove gdpr field from request * qwarry bid adapter: add sizes * qwarry bid adapter: add sizes * added schain * added test for schain * added supporting of advertiserDomains * qwarry bid adapter: added meta.advertiserDomains Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: Ekaterina Legostaeva --- modules/qwarryBidAdapter.js | 3 +++ test/spec/modules/qwarryBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index a410c2fe163..c9a86f73910 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -75,6 +75,9 @@ export const spec = { bid.vastXml = bid.ad; } + bid.meta = {}; + bid.meta.advertiserDomains = bid.adomain || []; + bids.push(bid); }) diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 6dbb983ea23..560206681ee 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -33,7 +33,8 @@ const BIDDER_BANNER_RESPONSE = { 'creativeId': 1, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'banner' + 'format': 'banner', + 'adomain': ['test.com'] }] } @@ -49,7 +50,8 @@ const BIDDER_VIDEO_RESPONSE = { 'creativeId': 2, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'video' + 'format': 'video', + 'adomain': ['test.com'] }] } @@ -119,6 +121,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('netRevenue').equal(true) expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('banner') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles video request : should get correct bid response', function () { @@ -136,6 +140,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('video') expect(result[0]).to.have.property('vastXml').equal('vast') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles no bid response : should get empty array', function () { From 9859d199887501c4fccca1ff3b7d036b02466127 Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Tue, 1 Jun 2021 11:43:17 +0200 Subject: [PATCH 1042/1476] Richaudience Bid Adapter: add adomain support (#6880) * RichaudienceBidAdapter Add adomine for Prebid 5.0 * new pull * new pull 2 * New Pull 3 * Add unit test * New push 1 * New push 2 Co-authored-by: sgimenez --- modules/richaudienceBidAdapter.js | 3 ++- test/spec/modules/richaudienceBidAdapter_spec.js | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 5e2a5e1bff5..4b556e83236 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -97,7 +97,8 @@ export const spec = { netRevenue: response.netRevenue, currency: response.currency, ttl: response.ttl, - dealId: response.dealId, + meta: response.adomain, + dealId: response.dealId }; if (response.media_type === 'video') { diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 72410b71fb2..00ae55823b0 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -149,8 +149,8 @@ describe('Richaudience adapter tests', function () { netRevenue: true, currency: 'USD', ttl: 300, - dealId: 'dealId' - + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -165,7 +165,8 @@ describe('Richaudience adapter tests', function () { currency: 'USD', ttl: 300, vastXML: '', - dealId: 'dealId' + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -600,6 +601,7 @@ describe('Richaudience adapter tests', function () { expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(300); expect(bid.dealId).to.equal('dealId'); + expect(bid.meta).to.equal('richaudience.com'); }); it('no banner media response inestream', function () { @@ -628,6 +630,7 @@ describe('Richaudience adapter tests', function () { expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(300); expect(bid.dealId).to.equal('dealId'); + expect(bid.meta).to.equal('richaudience.com'); }); it('no banner media response outstream', function () { From 7d02e2f7d596fcb26186c4bdf185defbf5589e31 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:18:13 +0300 Subject: [PATCH 1043/1476] Hybrid Bid Adapter: add placeholder for advertiserDomains support (#6885) * Add adomains stub. * Hybrid Bid Adapter: add unit test for advertiserDomains. Co-authored-by: Petrov Denis --- modules/hybridBidAdapter.js | 5 ++++- test/spec/modules/hybridBidAdapter_spec.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index e7281086a92..15f8acd824f 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -78,7 +78,10 @@ function buildBid(bidData) { creativeId: bidData.bidId, currency: bidData.currency, netRevenue: true, - ttl: TTL + ttl: TTL, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === PLACEMENT_TYPE_VIDEO) { diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index d1899ef3d81..ffbc27293fb 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -255,7 +255,8 @@ describe('Hybrid.ai Adapter', function() { currency: 'USD', content: 'html', width: 100, - height: 100 + height: 100, + advertiserDomains: ['hybrid.ai'] } ] } @@ -269,6 +270,7 @@ describe('Hybrid.ai Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['hybrid.ai']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { From d7d5f62f9e3f14d0bf6aaed31c6f5c549fb30faa Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:27:07 +0300 Subject: [PATCH 1044/1476] Vox Bid Adapter: Add placeholder to pass advertiser domains (#6884) * Add stub to pass adomains. * Vox Bid Adapter: add unit test for advertiserDomains. Co-authored-by: Petrov Denis --- modules/voxBidAdapter.js | 5 ++++- test/spec/modules/voxBidAdapter_spec.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 73df9bb8b9b..ba469c3c7ed 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -70,7 +70,10 @@ function buildBid(bidData) { netRevenue: true, mediaType: BANNER, ttl: TTL, - content: bidData.content + content: bidData.content, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === 'video') { diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index c6221cba9e5..6906c7dbba4 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -243,7 +243,8 @@ describe('VOX Adapter', function() { content: 'html', width: 100, height: 100 - } + }, + advertiserDomains: ['voxexchange.io'] } ] } @@ -257,6 +258,7 @@ describe('VOX Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['voxexchange.io']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { From 5c9e149ecc83ccbda53673c0a1e4cf680766e56d Mon Sep 17 00:00:00 2001 From: rtuschkany <35923908+rtuschkany@users.noreply.github.com> Date: Tue, 1 Jun 2021 14:20:06 +0200 Subject: [PATCH 1045/1476] ConnectAd Bid-Adapter: Add adomain support (#6859) * ConnectAd bid adapter: Add adomain support Add adomain support * ConnectAd Bid-Adapter: Add adomain support fix * ConnectAd Bid-Adapter: Add adomain support * ConnectAd Bid-Adapter: Add adomain support test update --- modules/connectadBidAdapter.js | 1 + test/spec/modules/connectadBidAdapter_spec.js | 39 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 4fa2a56a004..111b6ac10e8 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -124,6 +124,7 @@ export const spec = { bid.width = decision.width; bid.height = decision.height; bid.dealid = decision.dealid || null; + bid.meta = { advertiserDomains: decision && decision.adomain ? decision.adomain : [] }; bid.ad = retrieveAd(decision); bid.currency = 'USD'; bid.creativeId = decision.adId; diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index dbac3c0dc7c..657bc432d06 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -303,7 +303,43 @@ describe('ConnectAd Adapter', function () { }); describe('bid responses', function () { - it('should return complete bid response', function () { + it('should return complete bid response with adomain', function () { + const ADOMAINS = ['connectad.io']; + + let serverResponse = { + body: { + decisions: { + '2f95c00074b931': { + adId: '0', + adomain: ['connectad.io'], + contents: [ + { + body: '<<<---- Creative --->>>' + } + ], + height: '250', + width: '300', + pricing: { + clearPrice: 11.899999999999999 + } + } + } + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(11.899999999999999); + expect(bids[0].width).to.equal('300'); + expect(bids[0].height).to.equal('250'); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); + }); + + it('should return complete bid response with empty adomain', function () { + const ADOMAINS = []; + let serverResponse = { body: { decisions: { @@ -331,6 +367,7 @@ describe('ConnectAd Adapter', function () { expect(bids[0].width).to.equal('300'); expect(bids[0].height).to.equal('250'); expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); }); it('should return empty bid response', function () { From 4827f16e224813c338523b2f8e323e57abf28e27 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 1 Jun 2021 15:26:25 +0300 Subject: [PATCH 1046/1476] Aniview Bid Adapter: added meta.advertiserDomains to bidResponse and extended cookie sync logic (#6858) * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Fix Consent parameters * Update aniviewBidAdapter.js V3 support * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Update refererInfo * Update aniviewBidAdapter.js Fix tabs and spaces * Update aniviewBidAdapter.js fixes * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Add ccpa support * Update aniviewBidAdapter.js Typo * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js * Fix size and sample Fixed sizes from playerSize Updated md sample * Fix tabs * Fix sizes * Recheck * Add tgt parameter * Update sample * Add support for cookie sync + tests * Add support for cookie sync + tests * Add support for cookie sync + tests * Support aliases Support aliases * Update Update * Fix lint Fix lint * Update spec Update spec * Aniview Bid Adapter: Added the new alias * Aniview Bid Adapter: Added the new configs for the renderer * Aniview Bid Adapter: Added unit tests for the renderer * Aniview Bid Adapter: Have added gvlid * Aniview Bid Adapter: added meta.advertiserDomains to bidResponse and extended cookie sync logic Co-authored-by: Itay Nave Co-authored-by: Itay Nave <38345760+itaynave@users.noreply.github.com> --- modules/aniviewBidAdapter.js | 9 ++++++- test/spec/modules/aniviewBidAdapter_spec.js | 27 ++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 6b83c40897e..b37d105da1a 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -201,6 +201,10 @@ function interpretResponse(serverResponse, bidRequest) { bidResponse.vastUrl = window.URL.createObjectURL(blob); bidResponse.vastXml = xmlStr; bidResponse.mediaType = VIDEO; + bidResponse.meta = { + advertiserDomains: [] + }; + if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = newRenderer(bidRequest); } bidResponses.push(bidResponse); @@ -230,7 +234,10 @@ function getSyncData(xml, options) { if (data && data.trackers && data.trackers.length) { data = data.trackers; for (var j = 0; j < data.length; j++) { - if (typeof data[j] === 'object' && typeof data[j].url === 'string' && data[j].e === 'inventory') { + if (typeof data[j] === 'object' && + typeof data[j].url === 'string' && + (data[j].e === 'inventory' || data[j].e === 'sync') + ) { if (data[j].t == 1 && options.pixelEnabled) { ret.push({url: data[j].url, type: 'image'}); } else { diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js index 2e1fdb56201..b6d63fc2a8e 100644 --- a/test/spec/modules/aniviewBidAdapter_spec.js +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -160,6 +160,7 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(bidResponse.currency).to.equal('USD'); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.meta.advertiserDomains).to.be.an('array').that.is.empty; }); it('safely handles XML parsing failure from invalid bid response', function () { @@ -207,12 +208,16 @@ describe('ANIVIEW Bid Adapter Test', function () { }); describe('getUserSyncs', function () { - it('Check get sync pixels from response', function () { - let pixelUrl = 'https://sync.pixel.url/sync'; + let pixelUrl = 'https://sync.pixel.url/sync'; + function createBidResponse (pixelEvent, pixelType) { + let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; + return 'FORDFORD00:00:15'; + } + + it('Check get iframe sync pixels from response on inventory', function () { let pixelEvent = 'inventory'; let pixelType = '3'; - let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; - let bidResponse = 'FORDFORD00:00:15'; + let bidResponse = createBidResponse(pixelEvent, pixelType); let serverResponse = [ {body: bidResponse} ]; @@ -222,5 +227,19 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(pixel.url).to.equal(pixelUrl); expect(pixel.type).to.equal('iframe'); }); + + it('Check get image sync pixels from response on sync', function () { + let pixelEvent = 'sync'; + let pixelType = '1'; + let bidResponse = createBidResponse(pixelEvent, pixelType); + let serverResponse = [ + {body: bidResponse} + ]; + let syncPixels = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); + expect(syncPixels.length).to.equal(1); + let pixel = syncPixels[0]; + expect(pixel.url).to.equal(pixelUrl); + expect(pixel.type).to.equal('image'); + }); }); }); From d7a1418092fc087ecc8bd9679c257560ed338e4f Mon Sep 17 00:00:00 2001 From: hendrikiseke1979 <53309111+hendrikiseke1979@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:02:02 +0200 Subject: [PATCH 1047/1476] OR Bid Adapter: add support for advertiserDomains and floors module (#6890) * orbidder adapter: add withCredentials:true header to BidRequest and onBidWon Requests * add blank in order to trigger build again * remove blank to trigger build ... again * adding extra line to trigger build ... again * add prebid version to request * add unit test for version parameter * add version parameter to win requests * fix comment * trigger rebuild * trigger rebuild * remove onBidWon callback from adapter * fix retrieving orbidder endpoint url from local storage * fix unit tests * use getBidFloor function for prebidv5 compatibility * fill meta.advertiserDomains from serverResponse.adomain for prebidv5 compatibility * drop eslint-disable * switch misleading expect and actual in unit test * cr: rename adomain to advertiserDomains * trigger build pipeline * trigger build pipeline Co-authored-by: Volk, Rainer Co-authored-by: RainerVolk4014 <53347752+RainerVolk4014@users.noreply.github.com> Co-authored-by: siggi-otto <57615762+siggi-otto@users.noreply.github.com> Co-authored-by: Hendrik Iseke <39734979+hiseke@users.noreply.github.com> Co-authored-by: Hendrik Iseke Co-authored-by: rvolk <> Co-authored-by: Arne Schulz --- modules/orbidderBidAdapter.js | 31 ++++++++++++++ test/spec/modules/orbidderBidAdapter_spec.js | 43 +++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index e01746af487..4a7d686a7bc 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,5 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; +import * as utils from '../src/utils.js'; const storageManager = getStorageManager(); @@ -32,6 +33,8 @@ export const spec = { referer = bidderRequest.refererInfo.referer || ''; } + bidRequest.params.bidfloor = getBidFloor(bidRequest); + const ret = { url: `${hostname}/bid`, method: 'POST', @@ -70,6 +73,13 @@ export const spec = { } bidResponse[requiredKey] = bid[requiredKey]; } + + if (Array.isArray(bid.advertiserDomains)) { + bidResponse.meta = { + advertiserDomains: bid.advertiserDomains + } + } + bidResponses.push(bidResponse); }); } @@ -77,4 +87,25 @@ export const spec = { }, }; +/** + * Get bid floor from Price Floors Module + * @param {Object} bid + * @returns {float||undefined} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidfloor; + } + + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { + return floor.floor; + } + return undefined; +} + registerBidder(spec); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index df551311c0b..42cc25ae04f 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -104,7 +104,7 @@ describe('orbidderBidAdapter', () => { expect(request.data.pageUrl).to.equal('https://localhost:9876/'); // expect(request.data.referrer).to.equal(''); Object.keys(defaultBidRequest).forEach((key) => { - expect(defaultBidRequest[key]).to.equal(request.data[key]); + expect(request.data[key]).to.deep.equal(defaultBidRequest[key]); }); }); @@ -189,6 +189,47 @@ describe('orbidderBidAdapter', () => { }); }); + it('should get correct bid response with advertiserDomains', () => { + const serverResponse = [ + { + 'width': 300, + 'height': 250, + 'creativeId': '29681110', + 'ad': '', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'advertiserDomains': ['cm.tavira.pt'] + } + ]; + + const expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 0.5, + 'creativeId': '29681110', + 'width': 300, + 'height': 250, + 'ttl': 60, + 'currency': 'EUR', + 'ad': '', + 'netRevenue': true, + 'meta': { + 'advertiserDomains': ['cm.tavira.pt'] + } + } + ]; + + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(expectedResponse.length); + Object.keys(expectedResponse[0]).forEach((key) => { + expect(result[0][key]).to.deep.equal(expectedResponse[0][key]); + }); + }); + it('handles broken server response', () => { const serverResponse = [ { From d779cdc9a6a8baf43b559d740429d5b2da77f992 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 1 Jun 2021 16:11:28 +0300 Subject: [PATCH 1048/1476] AdkernelAdn: meta fields support (#6899) --- modules/adkernelAdnBidAdapter.js | 30 +++++++++++++++++-- .../modules/adkernelAdnBidAdapter_spec.js | 19 ++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 0de750c773f..dc56ed6abbb 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -103,13 +103,17 @@ function buildBid(tag) { requestId: tag.impid, bidderCode: spec.code, cpm: tag.bid, - width: tag.w, - height: tag.h, creativeId: tag.crid, currency: 'USD', ttl: 720, netRevenue: true }; + if (tag.w) { + bid.width = tag.w; + } + if (tag.h) { + bid.height = tag.h; + } if (tag.tag) { bid.ad = tag.tag; bid.mediaType = BANNER; @@ -117,9 +121,31 @@ function buildBid(tag) { bid.vastUrl = tag.vast_url; bid.mediaType = VIDEO; } + fillBidMeta(bid, tag); return bid; } +function fillBidMeta(bid, tag) { + if (utils.isStr(tag.agencyName)) { + utils.deepSetValue(bid, 'meta.agencyName', tag.agencyName); + } + if (utils.isNumber(tag.advertiserId)) { + utils.deepSetValue(bid, 'meta.advertiserId', tag.advertiserId); + } + if (utils.isStr(tag.advertiserName)) { + utils.deepSetValue(bid, 'meta.advertiserName', tag.advertiserName); + } + if (utils.isArray(tag.advertiserDomains)) { + utils.deepSetValue(bid, 'meta.advertiserDomains', tag.advertiserDomains); + } + if (utils.isStr(tag.primaryCatId)) { + utils.deepSetValue(bid, 'meta.primaryCatId', tag.primaryCatId); + } + if (utils.isArray(tag.secondaryCatIds)) { + utils.deepSetValue(bid, 'meta.secondaryCatIds', tag.secondaryCatIds); + } +} + function getBidFloor(bid, mediaType, sizes) { var floor; var size = sizes.length === 1 ? sizes[0] : '*'; diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index d3c9a2cf816..c4ad134711a 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -107,7 +107,12 @@ describe('AdkernelAdn adapter', function () { bid: 5.0, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + primaryCatId: 'IAB1-1', }, { id: 'ad-unit-2', impid: '31d798477126c4', @@ -115,7 +120,12 @@ describe('AdkernelAdn adapter', function () { bid: 2.5, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + secondaryCatIds: ['IAB1-4', 'IAB8-16', 'IAB25-5'] }, { id: 'video_wrapper', impid: '57d602ad1c9545', @@ -375,6 +385,11 @@ describe('AdkernelAdn adapter', function () { expect(resp).to.have.property('mediaType', 'banner'); expect(resp).to.have.property('ad'); expect(resp.ad).to.have.string(''); + expect(resp.meta.advertiserId).to.be.eql(777); + expect(resp.meta.advertiserName).to.be.eql('advertiser'); + expect(resp.meta.agencyName).to.be.eql('agency'); + expect(resp.meta.advertiserDomains).to.be.eql(['example.com']); + expect(resp.meta.primaryCatId).to.be.eql('IAB1-1'); }); it('should return fully-initialized video bid-response', function () { From 1bc1427cb62cd3057eeb1e621107f70e148cad51 Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 1 Jun 2021 18:44:12 +0530 Subject: [PATCH 1049/1476] BrightMountainMedia Bid Adapter: add floors module support (#6833) * Update BrightMountainMedia cookie sync URL * Bright Mountain Media: Update bidder code * Bright Mountain Media: Add brightmountainmedia as alias * Bright Mountain Media: Update Bid Endpoint * BrightMountainMedia Bid Adapter: add floors module support * BrightMountainMedia Bid Adapter: support advertiserDomains --- modules/brightMountainMediaBidAdapter.js | 36 +++++++++++++- .../brightMountainMediaBidAdapter_spec.js | 49 ++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 5539004bdcd..531238c7400 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -58,6 +58,7 @@ export const spec = { let placement = { placementId: bid.params.placement_id, bidId: bid.bidId, + floor: {}, }; if (bid.mediaTypes.hasOwnProperty(BANNER)) { @@ -87,6 +88,23 @@ export const spec = { placement['playbackmethod'] = bid.mediaTypes.video.playbackmethod; } } + + if (typeof bid.getFloor === 'function') { + let floorInfo = {}; + + for (let size of placement['sizes']) { + floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: placement['traffic'], + size: size, + }); + + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') { + placement.floor[`${size[0]}x${size[1]}`] = parseFloat(floorInfo.floor); + } + } + } + if (bid.schain) { placement.schain = bid.schain; } @@ -105,10 +123,26 @@ export const spec = { if (response && Array.isArray(response) && response.length > 0) { for (let i = 0; i < response.length; i++) { if (response[i].cpm > 0) { + const tempResponse = { + requestId: response[i].requestId, + width: response[i].width, + height: response[i].height, + cpm: response[i].cpm, + mediaType: response[i].mediaType, + creativeId: response[i].creativeId, + currency: response[i].currency, + netRevenue: response[i].netRevenue, + ttl: response[i].ttl, + ad: response[i].ad, + meta: { + advertiserDomains: response[i].adomain && response[i].adomain.length ? response[i].adomain : [], + } + }; + if (response[i].mediaType && response[i].mediaType === 'video') { response[i].vastXml = response[i].ad; } - bidResponse.push(response[i]); + bidResponse.push(tempResponse); } } } diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 6c7ef816f4f..17f23c5acd3 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -103,6 +103,48 @@ describe('brightMountainMediaBidAdapter_spec', function () { let serverRequest = spec.buildRequests([bidBanner], bidderRequest); testServerRequestBody(serverRequest); + it('sends bidfloor param if present', function () { + bidBanner.getFloor = function () { + return { + currency: 'USD', + floor: 0.5, + } + }; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].floor['300x250']).to.equal(0.5); + }); + + it('sends gdpr info if exists', function () { + const gdprConsent = { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true + }; + + bidderRequest['gdprConsent'] = gdprConsent; + const request = spec.buildRequests([bidBanner], bidderRequest); + + expect(request.data.gdpr_require).to.exist.and.to.be.a('number'); + expect(request.data.gdpr_consent).to.exist.and.to.be.a('string'); + }); + + it('sends schain info if exists', function () { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }; + bidBanner.schain = schain; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].schain).to.be.an('object'); + }); + bidderRequest['bids'] = [bidVideo]; serverRequest = spec.buildRequests([bidVideo], bidderRequest); testServerRequestBody(serverRequest); @@ -129,6 +171,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); expect(dataItem.mediaType).to.be.a('string'); + expect(dataItem.meta.advertiserDomains[0]).to.be.a('string'); } }); } @@ -145,7 +188,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; @@ -160,7 +204,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; let serverResponses = spec.interpretResponse(resObjectBanner); From 7647b97f7a07412fc49f4cca5572efa741db8462 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 1 Jun 2021 15:18:09 +0200 Subject: [PATCH 1050/1476] AdagioBidAdapter: support priceFloors module (#6867) --- modules/adagioBidAdapter.js | 57 ++++++++++++++++- test/spec/modules/adagioBidAdapter_spec.js | 71 ++++++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index aed1a1682dc..5598dc5d224 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -24,6 +24,8 @@ export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; const MAX_SESS_DURATION = 30 * 60 * 1000; const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp'; const ADAGIO_PUBKEY_E = 65537; +const CURRENCY = 'USD'; +const DEFAULT_FLOOR = 0.1; // This provide a whitelist and a basic validation // of OpenRTB 2.5 options used by the Adagio SSP. @@ -819,6 +821,48 @@ function _parseNativeBidResponse(bid) { bid.native = native } +function _getFloors(bidRequest) { + if (!utils.isFn(bidRequest.getFloor)) { + return false; + } + + const floors = []; + + const getAndPush = (mediaType, size) => { + const info = bidRequest.getFloor({ + currency: CURRENCY, + mediaType, + size: [] + }); + + floors.push(utils.cleanObj({ + mt: mediaType, + s: utils.isArray(size) ? `${size[0]}x${size[1]}` : undefined, + f: (!isNaN(info.floor) && info.currency === CURRENCY) ? info.floor : DEFAULT_FLOOR + })); + } + + Object.keys(bidRequest.mediaTypes).forEach(mediaType => { + if (SUPPORTED_MEDIA_TYPES.indexOf(mediaType) !== -1) { + const sizeProp = mediaType === VIDEO ? 'playerSize' : 'sizes'; + + if (bidRequest.mediaTypes[mediaType][sizeProp] && bidRequest.mediaTypes[mediaType][sizeProp].length) { + if (utils.isArray(bidRequest.mediaTypes[mediaType][sizeProp][0])) { + bidRequest.mediaTypes[mediaType][sizeProp].forEach(size => { + getAndPush(mediaType, [size[0], size[1]]); + }); + } else { + getAndPush(mediaType, [bidRequest.mediaTypes[mediaType][sizeProp][0], bidRequest.mediaTypes[mediaType][sizeProp][1]]); + } + } else { + getAndPush(mediaType, '*'); + } + } + }); + + return floors; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -914,6 +958,9 @@ export const spec = { const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); + // Handle priceFloors module + bidRequest.floors = _getFloors(bidRequest); + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { _buildVideoBidRequest(bidRequest); } @@ -923,10 +970,14 @@ export const spec = { // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - adUnit.params.organizationId = adUnit.params.organizationId.toString(); + const adUnitCopy = utils.deepClone(adUnit); + adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString(); + + // remove useless props + delete adUnitCopy.floorData; - groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []; - groupedAdUnits[adUnit.params.organizationId].push(adUnit); + groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; + groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); return groupedAdUnits; }, {}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 6e0b82cbab8..4b66a96be16 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -740,6 +740,77 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.user.eids).to.be.empty; }); }); + + describe('with priceFloors module', function() { + it('should get and set floor by mediatype and sizes', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + playerSize: [600, 480] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + + // delete the computed `sizes` prop as we are based on mediaTypes only. + delete bid01.sizes + + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(3); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'banner', s: '300x250'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'banner', s: '300x600'}); + expect(requests[0].data.adUnits[0].floors[2]).to.deep.equal({f: 1, mt: 'video', s: '600x480'}); + }); + + it('should get and set floor by mediatype if no size provided (ex native, video)', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + }, + native: { + body: { required: true } + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(2); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'video'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'native'}); + }); + + it('should get and set floor with default value if no floors found', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: NaN, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(1); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 0.1, mt: 'video'}); + }); + }); }); describe('interpretResponse()', function() { From 17e446deb5e22efa3898b24e5f37d829af4377cc Mon Sep 17 00:00:00 2001 From: etargetse <40423120+etargetse@users.noreply.github.com> Date: Tue, 1 Jun 2021 16:00:59 +0200 Subject: [PATCH 1051/1476] eTarget Bid Adapter: add "getMetaData" function to adapter, support for advertiserDomains (#6901) --- modules/etargetBidAdapter.js | 39 +++++++++++++++++++-- test/spec/modules/etargetBidAdapter_spec.js | 5 +++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 42c991a17a4..8a1b25cec70 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -1,5 +1,5 @@ -'use strict'; - +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -57,10 +57,40 @@ export const spec = { data: bidderRequest, bids: validBidRequests, netRevenue: netRevenue, + metaData: getMetaData(), bidder: 'etarget', gdpr: gdprObject }; + function getMetaData() { + var mts = {}; + var hmetas = document.getElementsByTagName('meta'); + var wnames = ['title', 'og:title', 'description', 'og:description', 'og:url', 'base', 'keywords']; + try { + for (var k in hmetas) { + if (typeof hmetas[k] == 'object') { + var mname = hmetas[k].name || hmetas[k].getAttribute('property'); + var mcont = hmetas[k].content; + if (!!mname && mname != 'null' && !!mcont) { + if (wnames.indexOf(mname) >= 0) { + if (!mts[mname]) { + mts[mname] = []; + } + mts[mname].push(mcont); + } + } + } + } + mts['title'] = [(document.getElementsByTagName('title')[0] || []).innerHTML]; + mts['base'] = [(document.getElementsByTagName('base')[0] || {}).href]; + mts['referer'] = [document.location.href]; + mts['ortb2'] = (config.getConfig('ortb2') || {}); + } catch (e) { + mts.error = e; + } + return mts; + } + function formRequestUrl(reqData) { var key; var url = []; @@ -107,9 +137,14 @@ export const spec = { bidObject.gdpr = bidRequest.gdpr.gdpr; bidObject.gdpr_consent = bidRequest.gdpr.gdpr_consent; } + + if (bid.adomain) { + utils.deepSetValue(bidObject, 'meta.advertiserDomains', Array.isArray(bid.adomain) ? bid.adomain : [bid.adomain]); + } bidRespones.push(bidObject); } } + return bidRespones; function verifySize(adItem, validSizes) { diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index 2dbf6cd68c5..e2310aee1fb 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -26,6 +26,11 @@ describe('etarget adapter', function () { assert.lengthOf(parsedUrl.items, 7); }); + it('should be an object', function () { + let request = spec.buildRequests(bids); + assert.isNotNull(request.metaData); + }); + it('should handle global request parameters', function () { let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); assert.equal(parsedUrl.path, 'https://sk.search.etargetnet.com/hb'); From 296b926b2c0aa88407716e7bf8793d24f3090d1a Mon Sep 17 00:00:00 2001 From: jackhsiehucf <77815341+jackhsiehucf@users.noreply.github.com> Date: Tue, 1 Jun 2021 23:15:39 +0800 Subject: [PATCH 1052/1476] ucfunnel Bid Adapter: add support Price Floors Module (#6806) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel fix error message in debug mode * ucfunnel adapter add bidfloor parameter * ucfunnel adapter support CCPA * ucfunnel adapter native support clicktrackers * ucfunnel adapter change cookie sync setting * ucfunnel adapter update request parameter * Update ucfunnelBidAdapter * ucfunnel adapter add currency in ad response * ucfunnel adapter support uid2 * ucfunnel Bid Adapter: add support for FLoC and Verizon Media ConnectID * ucfunnel Bid Adapter: add support Price Floors Module Co-authored-by: root Co-authored-by: Ryan Chou Co-authored-by: ucfunnel Co-authored-by: jack.hsieh --- modules/ucfunnelBidAdapter.js | 38 ++++++++++++++++++-- test/spec/modules/ucfunnelBidAdapter_spec.js | 38 ++++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 734aba97789..685ffdb42b7 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -8,6 +8,7 @@ const COOKIE_NAME = 'ucf_uid'; const VER = 'ADGENT_PREBID-2018011501'; const BIDDER_CODE = 'ucfunnel'; const GVLID = 607; +const CURRENCY = 'USD'; const VIDEO_CONTEXT = { INSTREAM: 0, OUSTREAM: 2 @@ -210,12 +211,41 @@ function getSupplyChain(schain) { return supplyChain; } +function getMediaType(mediaTypes) { + if (mediaTypes != null && mediaTypes.banner) { + return 'banner'; + } else if (mediaTypes != null && mediaTypes.video) { + return 'video'; + } else if (mediaTypes != null && mediaTypes.native) { + return 'native' + } + return 'banner'; +} + +function getFloor(bid, size, mediaTypes) { + if (bid.params.bidfloor) { + return bid.params.bidfloor; + } + if (typeof bid.getFloor === 'function') { + var bidFloor = bid.getFloor({ + currency: CURRENCY, + mediaType: getMediaType(mediaTypes), + size: (size) ? [ size[0], size[1] ] : '*', + }); + if (bidFloor.currency === CURRENCY) { + return bidFloor.floor; + } + } + return undefined; +} + function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const language = navigator.language; const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; const userIdTdid = (bid.userId && bid.userId.tdid) ? bid.userId.tdid : ''; const supplyChain = getSupplyChain(bid.schain); + const bidFloor = getFloor(bid, size, bid.mediaTypes); // general bid data let bidData = { ver: VER, @@ -225,9 +255,13 @@ function getRequestData(bid, bidderRequest) { dnt: dnt, adid: bid.params.adid, tdid: userIdTdid, - schain: supplyChain, - fp: bid.params.bidfloor + schain: supplyChain }; + + if (bidFloor) { + bidData.fp = bidFloor; + } + addUserId(bidData, bid.userId); try { bidData.host = window.top.location.hostname; diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index bee420f40d4..5899554244b 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -25,8 +25,7 @@ const userId = { const validBannerBidReq = { bidder: BIDDER_CODE, params: { - adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D', - bidfloor: 1.0 + adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, sizes: [[300, 250]], bidId: '263be71e91dd9d', @@ -175,6 +174,41 @@ describe('ucfunnel Adapter', function () { expect(data.w).to.equal(width); expect(data.h).to.equal(height); }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.02); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); }); describe('interpretResponse', function () { From 667e2683533c546f08b0809b8dd84de9b81d0c3e Mon Sep 17 00:00:00 2001 From: Alexandru Date: Tue, 1 Jun 2021 19:30:11 +0300 Subject: [PATCH 1053/1476] Brightcom Bid Adapter: handle meta.advertiserDomains (#6905) --- modules/brightcomBidAdapter.js | 5 ++++- test/spec/modules/brightcomBidAdapter_spec.js | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 2aad211b186..7d6d77356f1 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -122,7 +122,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(brightcomBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: brightcomBid && brightcomBid.adomain ? brightcomBid.adomain : [] + } }); }); } diff --git a/test/spec/modules/brightcomBidAdapter_spec.js b/test/spec/modules/brightcomBidAdapter_spec.js index a89391d681e..b7d52c9f7d5 100644 --- a/test/spec/modules/brightcomBidAdapter_spec.js +++ b/test/spec/modules/brightcomBidAdapter_spec.js @@ -247,7 +247,8 @@ describe('brightcomBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -265,7 +266,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -283,7 +287,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); From 4fe2be1d85b398be2f22912a769beb4a9931d6be Mon Sep 17 00:00:00 2001 From: Alexandru Date: Tue, 1 Jun 2021 19:59:36 +0300 Subject: [PATCH 1054/1476] Onomagic Bid Adapter: handle meta.advertiserDomains (#6906) * Onomagic Bid Adapter: handle meta.advertiserDomains * Onomagic Bid Adapter: fix lint issues --- modules/onomagicBidAdapter.js | 5 ++++- test/spec/modules/onomagicBidAdapter_spec.js | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index 55fca29fbf3..eba62cfe1d4 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -117,7 +117,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(onomagicBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: onomagicBid && onomagicBid.adomain ? onomagicBid.adomain : [] + } }); }); } diff --git a/test/spec/modules/onomagicBidAdapter_spec.js b/test/spec/modules/onomagicBidAdapter_spec.js index 7c71c3e5764..6ddc0edd477 100644 --- a/test/spec/modules/onomagicBidAdapter_spec.js +++ b/test/spec/modules/onomagicBidAdapter_spec.js @@ -222,7 +222,8 @@ describe('onomagicBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -240,7 +241,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -258,7 +262,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); From 5cca29ed0a744000fa652ed9e91bc1306f0ed965 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Tue, 1 Jun 2021 10:03:57 -0700 Subject: [PATCH 1055/1476] add support for advertiser domains (#6908) --- modules/underdogmediaBidAdapter.js | 3 +++ test/spec/modules/underdogmediaBidAdapter_spec.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 8368077a627..6268774bc12 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -120,6 +120,9 @@ export const spec = { currency: 'USD', netRevenue: false, ttl: mid.ttl || 60, + meta: { + advertiserDomains: mid.advertiser_domains || [] + } }; if (bidResponse.cpm <= 0) { diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index aeb9f56c851..70d09513f27 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -276,6 +276,7 @@ describe('UnderdogMedia adapter', function () { mids: [ { ad_code_html: 'ad_code_html', + advertiser_domains: ['domain1'], cpm: 2.5, height: '600', mid: '32634', @@ -300,6 +301,7 @@ describe('UnderdogMedia adapter', function () { expect(bids).to.be.lengthOf(2); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['domain1']) expect(bids[0].bidderCode).to.equal('underdogmedia'); expect(bids[0].cpm).to.equal(2.5); expect(bids[0].width).to.equal('160'); From f3302af630ec83260d57ecab02ab32c9215607da Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:50:16 +0200 Subject: [PATCH 1056/1476] smartx Bid Adapter: Add support for Floors Module (#6902) * 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 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 42 +++++--- test/spec/modules/smartxBidAdapter_spec.js | 108 +++++++++++++++++++-- 2 files changed, 133 insertions(+), 17 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index b2c48b58f82..46e2055c8f0 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -46,14 +46,6 @@ export const spec = { utils.logError(BIDDER_CODE + ': siteId is not present in bidder params'); return false; } - if (!utils.getBidIdParameter('bidfloor', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloor is not present in bidder params'); - return false; - } - if (!utils.getBidIdParameter('bidfloorcur', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloorcur is not present in bidder params'); - return false; - } if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { if (!utils.getBidIdParameter('outstream_options', bid.params)) { utils.logError(BIDDER_CODE + ': outstream_options parameter is not defined'); @@ -85,8 +77,8 @@ export const spec = { const smartxRequests = bidRequests.map(function (bid) { const tagId = utils.getBidIdParameter('tagId', bid.params); const publisherId = utils.getBidIdParameter('publisherId', bid.params); - const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); - const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params); + const bidfloor = getBidFloor(bid) || 0; + const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; const siteId = utils.getBidIdParameter('siteId', bid.params); const domain = utils.getBidIdParameter('domain', bid.params); const cat = utils.getBidIdParameter('cat', bid.params); @@ -161,7 +153,7 @@ export const spec = { const at = utils.getBidIdParameter('at', bid.params) || 2; - const cur = utils.getBidIdParameter('cur', bid.params) || ['EUR']; + const cur = utils.getBidIdParameter('cur', bid.params) || 'EUR'; const requestPayload = { id: utils.generateUUID(), @@ -226,6 +218,8 @@ export const spec = { } } + // Todo: USER ID MODULE + requestPayload.user = { ext: userExt, data: targetingarr @@ -412,4 +406,30 @@ function outstreamRender(bid) { } } } + +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + let floor = utils.getBidIdParameter('bidfloor', bid.params); + let floorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; + + if (!floor && utils.isFn(bid.getFloor)) { + const floorObj = bid.getFloor({ + currency: floorcur, + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floorObj) && !isNaN(floorObj.floor) && floorObj.currency === floorcur) { + floor = floorObj.floor; + } + } + + return floor; +} + registerBidder(spec); diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 3c871c6f88b..d0659865afa 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -108,14 +108,13 @@ describe('The smartx adapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should fail without bidfloor', function () { + it('should succeed with floor Module set', function () { delete bid.params.bidfloor; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should fail without bidfloorcur', function () { delete bid.params.bidfloorcur; - expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.floors = { + currency: 'EUR' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should fail with context outstream but no options set for outstream', function () { @@ -514,4 +513,101 @@ describe('The smartx adapter', function () { window.document.getElementById.restore(); }); }); + + describe('price floor module', function () { + var bid, + bidRequestObj; + + beforeEach(function () { + bid = getValidBidObject(); + bidRequestObj = { + refererInfo: { + referer: 'prebid.js' + } + }; + delete bid.params.bidfloor; + }); + + it('obtain floor from getFloor', function () { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 3.21); + }); + + it('obtain floor from params', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + bid.params.bidfloor = 0.64; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0.64); + }); + + it('check currency USD', function() { + bid.getFloor = () => { + return { + currency: 'USD', + floor: 1.23 + }; + }; + bid.params.bidfloorcur = 'USD' + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'USD'); + expect(payload.data.imp).to.have.property('bidfloor', 1.23); + }); + + it('check defaut currency EUR', function() { + delete bid.params.bidfloorcur; + + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 4.56 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'EUR'); + expect(payload.data.imp).to.have.property('bidfloor', 4.56); + }); + + it('bad floor value', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 'bad' + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('empty floor object', function() { + bid.getFloor = () => { + return {}; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('undefined floor result', function() { + bid.getFloor = () => {}; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + }); }) From e414946e31eb6a822ef69edbb27395d0b058d669 Mon Sep 17 00:00:00 2001 From: ym-dlabuzov <81709888+ym-dlabuzov@users.noreply.github.com> Date: Tue, 1 Jun 2021 21:28:06 +0300 Subject: [PATCH 1057/1476] Yieldmo Bid Adapter: read video parameters from the ad unit (#6873) --- modules/yieldmoBidAdapter.js | 90 +++++++++++++-------- modules/yieldmoBidAdapter.md | 5 ++ test/spec/modules/yieldmoBidAdapter_spec.js | 71 +++++++++++++++- 3 files changed, 130 insertions(+), 36 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 36f93f60c9e..fa1ab3a90b3 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -12,8 +12,8 @@ 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 OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; -const OPENRTB_VIDEO_BIDPARAMS = ['placement', 'startdelay', 'skipafter', - 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos']; +const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', + 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const LOCAL_WINDOW = utils.getWindowTop(); const DEFAULT_PLAYBACK_METHOD = 2; @@ -335,7 +335,6 @@ function openRtbRequest(bidRequests, bidderRequest) { * @return Object OpenRTB's 'imp' (impression) object */ function openRtbImpression(bidRequest) { - const videoReq = utils.deepAccess(bidRequest, 'mediaTypes.video'); const size = extractPlayerSize(bidRequest); const imp = { id: bidRequest.bidId, @@ -347,23 +346,27 @@ function openRtbImpression(bidRequest) { video: { w: size[0], h: size[1], - mimes: videoReq.mimes, linearity: 1 } }; + const mediaTypesParams = utils.deepAccess(bidRequest, 'mediaTypes.video'); + Object.keys(mediaTypesParams) + .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) + .forEach(param => imp.video[param] = mediaTypesParams[param]); + const videoParams = utils.deepAccess(bidRequest, 'params.video'); Object.keys(videoParams) .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) .forEach(param => imp.video[param] = videoParams[param]); - if (videoParams.skippable) imp.video.skip = 1; - if (videoParams.placement !== 1) { - imp.video = { - ...imp.video, - startdelay: DEFAULT_START_DELAY, - playbackmethod: [ DEFAULT_PLAYBACK_METHOD ] - } + if (imp.video.skippable) { + imp.video.skip = 1; + delete imp.video.skippable; + } + if (imp.video.placement !== 1) { + imp.video.startdelay = DEFAULT_START_DELAY; + imp.video.playbackmethod = [ DEFAULT_PLAYBACK_METHOD ]; } return imp; } @@ -476,51 +479,68 @@ function validateVideoParams(bid) { const isDefined = val => typeof val !== 'undefined'; const validate = (fieldPath, validateCb, errorCb, errorCbParam) => { - const value = utils.deepAccess(bid, fieldPath); - if (!validateCb(value)) { - errorCb(fieldPath, value, errorCbParam); + if (fieldPath.indexOf('video') === 0) { + const valueFieldPath = 'params.' + fieldPath; + const mediaFieldPath = 'mediaTypes.' + fieldPath; + const valueParams = utils.deepAccess(bid, valueFieldPath); + const mediaTypesParams = utils.deepAccess(bid, mediaFieldPath); + const hasValidValueParams = validateCb(valueParams); + const hasValidMediaTypesParams = validateCb(mediaTypesParams); + + if (hasValidValueParams) return valueParams; + else if (hasValidMediaTypesParams) return hasValidMediaTypesParams; + else { + if (!hasValidValueParams) errorCb(valueFieldPath, valueParams, errorCbParam); + else if (!hasValidMediaTypesParams) errorCb(mediaFieldPath, mediaTypesParams, errorCbParam); + } + return valueParams || mediaTypesParams; + } else { + const value = utils.deepAccess(bid, fieldPath); + if (!validateCb(value)) { + errorCb(fieldPath, value, errorCbParam); + } + return value; } - return value; } try { + validate('video.context', val => !utils.isEmpty(val), paramRequired); + validate('params.placementId', val => !utils.isEmpty(val), paramRequired); - validate('mediaTypes.video.playerSize', val => utils.isArrayOfNums(val, 2) || + validate('video.playerSize', val => utils.isArrayOfNums(val, 2) || (utils.isArray(val) && val.every(v => utils.isArrayOfNums(v, 2))), paramInvalid, 'array of 2 integers, ex: [640,480] or [[640,480]]'); - validate('mediaTypes.video.mimes', val => isDefined(val), paramRequired); - validate('mediaTypes.video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, + validate('video.mimes', val => isDefined(val), paramRequired); + validate('video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, 'array of strings, ex: ["video/mp4"]'); - validate('params.video', val => !utils.isEmpty(val), paramRequired); - - const placement = validate('params.video.placement', val => isDefined(val), paramRequired); - validate('params.video.placement', val => val >= 1 && val <= 5, paramInvalid); + const placement = validate('video.placement', val => isDefined(val), paramRequired); + validate('video.placement', val => val >= 1 && val <= 5, paramInvalid); if (placement === 1) { - validate('params.video.startdelay', val => isDefined(val), + validate('video.startdelay', val => isDefined(val), (field, v) => paramRequired(field, v, 'placement == 1')); - validate('params.video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); + validate('video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); } - validate('params.video.protocols', val => isDefined(val), paramRequired); - validate('params.video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.protocols', val => isDefined(val), paramRequired); + validate('video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.api', val => isDefined(val), paramRequired); - validate('params.video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.api', val => isDefined(val), paramRequired); + validate('video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, + validate('video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, 'array of integers, ex: [2,6]'); - validate('params.video.maxduration', val => isDefined(val), paramRequired); - validate('params.video.maxduration', val => utils.isInteger(val), paramInvalid); - validate('params.video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); - validate('params.video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.maxduration', val => isDefined(val), paramRequired); + validate('video.maxduration', val => utils.isInteger(val), paramInvalid); + validate('video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); + validate('video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); validate('params.badv', val => !isDefined(val) || utils.isArray(val), paramInvalid, 'array of strings, ex: ["ford.com","pepsi.com"]'); validate('params.bcat', val => !isDefined(val) || utils.isArray(val), paramInvalid, diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 54be295a1a1..040fbbec486 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -96,3 +96,8 @@ var videoAdUnit = [{ }] }]; ``` + +Please also note, that we support the following OpenRTB params: +'mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', +'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable'. +They can be specified in `mediaTypes.video` or in `bids[].params.video`. diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 3b317f88dc6..77542480c6c 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -331,13 +331,82 @@ describe('YieldmoAdapter', function () { }); describe('Instream video:', function () { + let videoBid; + const buildVideoBidAndGetVideoParam = () => build([videoBid])[0].data.imp[0].video; + + beforeEach(() => { + videoBid = mockVideoBid(); + }); + it('should attempt to send video bid requests to the endpoint via POST', function () { - const requests = build([mockVideoBid()]); + const requests = build([videoBid]); expect(requests.length).to.equal(1); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); }); + it('should add mediaTypes.video prop to the imp.video prop', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should override mediaTypes.video prop if params.video prop is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 50; + utils.deepAccess(videoBid, 'params.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should add mediaTypes.video.mimes prop to the imp.video', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = ['video/mp4']; + expect(buildVideoBidAndGetVideoParam().minduration).to.deep.equal(['video/mp4']); + }); + + it('should override mediaTypes.video.mimes prop if params.video.mimes is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['mimes'] = ['video/mp4']; + utils.deepAccess(videoBid, 'params.video')['mimes'] = ['video/mkv']; + expect(buildVideoBidAndGetVideoParam().mimes).to.deep.equal(['video/mkv']); + }); + + describe('video.skip state check', () => { + it('should not set video.skip if neither *.video.skip nor *.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + + it('should set video.skip=1 if mediaTypes.video.skip is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if params.video.skip is present', function () { + utils.deepAccess(videoBid, 'params.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should not set video.skip if params.video.skippable is false', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + }); + it('should process floors module if available', function () { const requests = build([ mockVideoBid({...mockGetFloor(3.99)}), From 6e203ea6924b196cdcb93aff82416c48dd0978d6 Mon Sep 17 00:00:00 2001 From: hnkhandev <57697206+hnkhandev@users.noreply.github.com> Date: Tue, 1 Jun 2021 12:30:31 -0600 Subject: [PATCH 1058/1476] Accept outstream renderers defined in mediatype for PBS (#6896) --- modules/prebidServerBidAdapter/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index ec5d05f0fe0..4f13bab05eb 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -594,7 +594,7 @@ const OPEN_RTB_PROTOCOL = { } if (!utils.isEmpty(videoParams)) { - if (videoParams.context === 'outstream' && !adUnit.renderer) { + if (videoParams.context === 'outstream' && (!videoParams.renderer || !adUnit.renderer)) { // Don't push oustream w/o renderer to request object. utils.logError('Outstream bid without renderer cannot be sent to Prebid Server.'); } else { From 1c3247c6beae0119bfabc43a7cee4a61629374b6 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 1 Jun 2021 13:58:39 -0700 Subject: [PATCH 1059/1476] Sharethrough Bid Adapter: Use getFloor module for Prebid 5.0 compliance (#6874) * Fix Prebid 5.0 compliance: leverage floors module * Upgrade adapter version --- modules/sharethroughBidAdapter.js | 21 ++++++++++++++++--- .../modules/sharethroughBidAdapter_spec.js | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 68ccde0da46..59cf2a3a035 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -const VERSION = '3.3.2'; +const VERSION = '3.4.0'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -57,8 +57,9 @@ export const sharethroughAdapterSpec = { query.schain = JSON.stringify(bidRequest.schain); } - if (bidRequest.bidfloor) { - query.bidfloor = parseFloat(bidRequest.bidfloor); + const floor = getFloor(bidRequest); + if (floor) { + query.bidfloor = floor; } if (bidRequest.params.badv) { @@ -292,4 +293,18 @@ function getProtocol() { return document.location.protocol; } +function getFloor(bid) { + if (utils.isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })) + }); + if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') { + return parseFloat(floorInfo.floor); + } + } + return null; +} + registerBidder(sharethroughAdapterSpec); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 5c8e01536dd..b8d91249ec3 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -436,7 +436,7 @@ describe('sharethrough adapter spec', function() { it('should include the bidfloor parameter if it is present in the bid request', function() { const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest['bidfloor'] = 0.50; + bidRequest['getFloor'] = () => ({ currency: 'USD', floor: 0.5 }); const builtBidRequest = spec.buildRequests([bidRequest])[0]; expect(builtBidRequest.data.bidfloor).to.eq(0.5); }); From a259700fc1e152d67672a95170605506fa241939 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 2 Jun 2021 13:21:09 +0300 Subject: [PATCH 1060/1476] Onomagic Bid Adapter: use getFloor function (#6907) * Onomagic Bid Adapter: use getFloor function * Onomagic Bid Adapter: fix issues --- modules/onomagicBidAdapter.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index eba62cfe1d4..548c0170c05 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -246,4 +246,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); From 481b94f8c27d535d7b9105a69e76d01bad49a64b Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Wed, 2 Jun 2021 19:11:28 +0800 Subject: [PATCH 1061/1476] Jixie Bid Adapter: add support for advertiserDomains (#6898) * 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 --- modules/jixieBidAdapter.js | 10 ++++++++++ test/spec/modules/jixieBidAdapter_spec.js | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 7c6e0027482..db6c9032e65 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -240,6 +240,16 @@ export const spec = { let rendererScript = (oneBid.osparams.script ? oneBid.osparams.script : JX_OUTSTREAM_RENDERER_URL); bnd.renderer = createRenderer_(oneBid, rendererScript, jxOutstreamRender_); } + // a note on advertiserDomains: our adserver is not responding in + // openRTB-type json. so there is no need to copy from 'adomain' over + // to meta: advertiserDomains + // However, we will just make sure the property is there. + if (!bnd.meta) { + bnd.meta = {}; + } + if (!bnd.meta.advertiserDomains) { + bnd.meta.advertiserDomains = []; + } bidResponses.push(bnd); }); if (response.body.setids) { diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 842f9e0ed30..ae58da30f64 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -281,7 +281,8 @@ describe('jixie Adapter', function () { }, 'vastUrl': 'https://ad.jixie.io/v1/video?creativeid=522' }, - // display ad returned here: + // display ad returned here: This one there is advertiserDomains + // in the response . Will be checked in the unit tests below { 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', 'jxBidId': '600c9ae6fda1acb-028d5dee-2c83-44e3-bed1-b75002475cdf', @@ -411,6 +412,9 @@ describe('jixie Adapter', function () { expect(result[0].ttl).to.equal(300) expect(result[0].vastUrl).to.include('https://ad.jixie.io/v1/video?creativeid=') expect(result[0].trackingUrlBase).to.include('sync') + // We will always make sure the meta->advertiserDomains property is there + // If no info it is an empty array. + expect(result[0].meta.advertiserDomains.length).to.equal(0) // display ad expect(result[1].requestId).to.equal('600c9ae6fda1acb') @@ -422,6 +426,7 @@ describe('jixie Adapter', function () { expect(result[1].netRevenue).to.equal(true) expect(result[1].ttl).to.equal(300) expect(result[1].ad).to.include('jxoutstream') + expect(result[1].meta.advertiserDomains.length).to.equal(3) expect(result[1].trackingUrlBase).to.include('sync') // should pick up about using alternative outstream renderer @@ -436,6 +441,7 @@ describe('jixie Adapter', function () { expect(result[2].vastXml).to.include('') expect(result[2].trackingUrlBase).to.include('sync'); expect(result[2].renderer.id).to.equal('demoslot4-div') + expect(result[2].meta.advertiserDomains.length).to.equal(0) expect(result[2].renderer.url).to.equal(JX_OTHER_OUTSTREAM_RENDERER_URL); // should know to use default outstream renderer @@ -450,6 +456,7 @@ describe('jixie Adapter', function () { expect(result[3].vastXml).to.include('') expect(result[3].trackingUrlBase).to.include('sync'); expect(result[3].renderer.id).to.equal('demoslot2-div') + expect(result[3].meta.advertiserDomains.length).to.equal(0) expect(result[3].renderer.url).to.equal(JX_OUTSTREAM_RENDERER_URL) setLocalStorageSpy.restore(); From fbc44aa6c658f3be93f5dbb94e0f549061873902 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Wed, 2 Jun 2021 20:20:56 +0900 Subject: [PATCH 1062/1476] relaido Bid Adapter: Add meta OBJ to BidResponse (#6914) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * add response meda OBJ update version and test code Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- modules/relaidoBidAdapter.js | 6 +++++- test/spec/modules/relaidoBidAdapter_spec.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 92709b7c047..f69f8c5c6e2 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.3'; +const ADAPTER_VERSION = '1.0.4'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -112,6 +112,10 @@ function interpretResponse(serverResponse, bidRequest) { ttl: body.ttl || DEFAULT_TTL, netRevenue: true, mediaType: mediaType, + meta: { + advertiserDomains: body.adomain || [], + mediaType: VIDEO + } }; if (mediaType === VIDEO) { bidResponse.vastXml = body.vast; diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 91aa6b05e6e..c2082eb1e91 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { getStorageManager } from '../../../src/storageManager.js'; const UUID_KEY = 'relaido_uuid'; @@ -59,7 +60,8 @@ describe('RelaidoAdapter', function () { uuid: relaido_uuid, vast: '', playerUrl: 'https://relaido/player.js', - syncUrl: 'https://relaido/sync.html' + syncUrl: 'https://relaido/sync.html', + adomain: ['relaido.co.jp', 'www.cmertv.co.jp'] } }; serverRequest = { @@ -276,6 +278,8 @@ describe('RelaidoAdapter', function () { expect(response.currency).to.equal(serverResponse.body.currency); expect(response.creativeId).to.equal(serverResponse.body.creativeId); expect(response.vastXml).to.equal(serverResponse.body.vast); + expect(response.meta.advertiserDomains).to.equal(serverResponse.body.adomain); + expect(response.meta.mediaType).to.equal(VIDEO); expect(response.ad).to.be.undefined; }); From f957aa779be7fbea758932b43644a3d3a95d41c8 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 2 Jun 2021 04:28:08 -0700 Subject: [PATCH 1063/1476] AdYouLike Bidder: Handle advertiser domains (#6916) * AdYouLike Bidder: Handle advertiser domains * fix linting * update tests * fix linting * fix error --- modules/adyoulikeBidAdapter.js | 3 +- test/spec/modules/adyoulikeBidAdapter_spec.js | 34 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 44d8e2e3016..74ce62950f8 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -449,7 +449,8 @@ function createBid(response, bidRequests) { creativeId: response.CreativeID, cpm: response.Price, netRevenue: true, - currency: CURRENCY + currency: CURRENCY, + meta: response.Meta || { advertiserDomains: [] } }; if (request && request.Native) { diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index abf8793865c..e6e95ea5423 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -290,6 +290,19 @@ describe('Adyoulike Adapter', function () { 'Placement': 'placement_0' } ]; + + const testMetaObject = { + 'networkId': 123, + 'advertiserId': '3', + 'advertiserName': 'foobar', + 'advertiserDomains': ['foobar.com'], + 'brandId': '345', + 'brandName': 'Foo', + 'primaryCatId': '34', + 'secondaryCatIds': ['IAB-222', 'IAB-333'], + 'mediaType': 'banner' + }; + const admSample = "\u003cscript id=\"ayl-prebid-a11a121205932e75e622af275681965d\"\u003e\n(function(){\n\twindow.isPrebid = true\n\tvar prebidResults = /*PREBID*/{\"OnEvents\":{\"CLICK\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelCLICK.com/fake\"}],\"IMPRESSION\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelIMP.com/fake\"},{\"Kind\":\"JAVASCRIPT_URL\",\"Url\":\"https://testJsIMP.com/fake.js\"}]},\"Disabled\":false,\"Attempt\":\"a11a121205932e75e622af275681965d\",\"ApiPrefix\":\"https://fo-api.omnitagjs.com/fo-api\",\"TrackingPrefix\":\"https://tracking.omnitagjs.com/tracking\",\"DynamicPrefix\":\"https://tag-dyn.omnitagjs.com/fo-dyn\",\"StaticPrefix\":\"https://fo-static.omnitagjs.com/fo-static\",\"BlobPrefix\":\"https://fo-api.omnitagjs.com/fo-api/blobs\",\"SspPrefix\":\"https://fo-ssp.omnitagjs.com/fo-ssp\",\"VisitorPrefix\":\"https://visitor.omnitagjs.com/visitor\",\"Trusted\":true,\"Placement\":\"e622af275681965d3095808561a1e510\",\"PlacementAccess\":\"ALL\",\"Site\":\"6e2df7a92203c3c7a25561ed63f25a27\",\"Lang\":\"EN\",\"SiteLogo\":null,\"HasSponsorImage\":false,\"ResizeIframe\":true,\"IntegrationConfig\":{\"Kind\":\"WIDGET\",\"Widget\":{\"ExtraStyleSheet\":\"\",\"Placeholders\":{\"Body\":{\"Color\":{\"R\":77,\"G\":21,\"B\":82,\"A\":100},\"BackgroundColor\":{\"R\":255,\"G\":255,\"B\":255,\"A\":100},\"FontFamily\":\"Lato\",\"Width\":\"100%\",\"Align\":\"\",\"BoxShadow\":true},\"CallToAction\":{\"Color\":{\"R\":26,\"G\":157,\"B\":212,\"A\":100}},\"Description\":{\"Length\":130},\"Image\":{\"Width\":600,\"Height\":600,\"Lowres\":false,\"Raw\":false},\"Size\":{\"Height\":\"250px\",\"Width\":\"300px\"},\"Sponsor\":{\"Color\":{\"R\":35,\"G\":35,\"B\":35,\"A\":100},\"Label\":true,\"WithoutLogo\":false},\"Title\":{\"Color\":{\"R\":219,\"G\":181,\"B\":255,\"A\":100}}},\"Selector\":{\"Kind\":\"CSS\",\"Css\":\"#ayl-prebid-a11a121205932e75e622af275681965d\"},\"Insertion\":\"AFTER\",\"ClickFormat\":true,\"Creative20\":true,\"WidgetKind\":\"CREATIVE_TEMPLATE_4\"}},\"Legal\":\"Sponsored\",\"ForcedCampaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"ForcedTrack\":\"\",\"ForcedCreative\":\"\",\"ForcedSource\":\"\",\"DisplayMode\":\"DEFAULT\",\"Campaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"CampaignAccess\":\"ALL\",\"CampaignKind\":\"AD_TRAFFIC\",\"DataSource\":\"LOCAL\",\"DataSourceUrl\":\"\",\"DataSourceOnEventsIsolated\":false,\"DataSourceWithoutCookie\":false,\"Content\":{\"Preview\":{\"Thumbnail\":{\"Image\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://tag-dyn.omnitagjs.com/fo-dyn/native/preview/image?key=fd4362d35bb174d6f1c80d4bb5643c22\\u0026kind=INTERNAL\\u0026ztop=0.000000\\u0026zleft=0.000000\\u0026zwidth=0.333333\\u0026zheight=1.000000\\u0026width=[width]\\u0026height=[height]\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Text\":{\"CALLTOACTION\":\"Click here to learn more\",\"DESCRIPTION\":\"Considérant l'extrémité conjoncturelle, il serait bon d'anticiper toutes les voies de bon sens.\",\"SPONSOR\":\"Tested by\",\"TITLE\":\"Adserver Traffic Redirect Internal\"},\"Sponsor\":{\"Name\":\"QA Team\"},\"Credit\":{\"Logo\":{\"Resource\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Url\":\"https://blobs.omnitagjs.com/adchoice/\"}},\"Landing\":{\"Url\":\"https://www.w3.org/People/mimasa/test/xhtml/entities/entities-11.xhtml#lat1\",\"LegacyTracking\":false},\"ViewButtons\":{\"Close\":{\"Skip\":6000}},\"InternalContentFields\":{\"AnimatedImage\":false}},\"AdDomain\":\"adyoulike.com\",\"Opener\":\"REDIRECT\",\"PerformUITriggers\":[\"CLICK\"],\"RedirectionTarget\":\"TAB\"}/*PREBID*/;\n\tvar insertAds = function insertAds() {\insertAds();\n\t}\n})();\n\u003c/script\u003e"; const responseWithSinglePlacement = [ { @@ -298,6 +311,7 @@ describe('Adyoulike Adapter', function () { 'Ad': admSample, 'Price': 0.5, 'Height': 600, + 'Meta': testMetaObject } ]; @@ -365,7 +379,8 @@ describe('Adyoulike Adapter', function () { privacyLink: 'https://blobs.omnitagjs.com/adchoice/', sponsoredBy: 'QA Team', title: 'Adserver Traffic Redirect Internal', - } + }, + meta: testMetaObject }]; const responseWithMultiplePlacements = [ @@ -617,6 +632,7 @@ describe('Adyoulike Adapter', function () { expect(result[0].ad).to.equal(admSample); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(600); + expect(result[0].meta).to.deep.equal(testMetaObject); }); it('receive reponse with multiple placement', function () { @@ -636,8 +652,8 @@ describe('Adyoulike Adapter', function () { expect(result[1].height).to.equal(250); }); - it('receive reponse with Native ad', function () { - serverResponse.body = responseWithSingleNative; + it('receive reponse with Native from ad markup', function () { + serverResponse.body = responseWithSinglePlacement; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); @@ -645,13 +661,19 @@ describe('Adyoulike Adapter', function () { expect(result).to.deep.equal(nativeResult); }); - it('receive reponse with Native from ad markup', function () { - serverResponse.body = responseWithSinglePlacement; + it('receive reponse with Native ad', function () { + serverResponse.body = responseWithSingleNative; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); - expect(result).to.deep.equal(nativeResult); + const noMeta = [...nativeResult]; + const metaBackup = noMeta[0].meta; + + // this test should return default meta object + noMeta[0].meta = { advertiserDomains: [] }; + + expect(result).to.deep.equal(noMeta); }); }); }); From 7e67b4e3b8b89dae7c04bd5cd68041bfd41fa031 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 2 Jun 2021 16:19:17 +0300 Subject: [PATCH 1064/1476] Brightcom Bid Adapter: use getFloor function (#6918) --- modules/brightcomBidAdapter.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 7d6d77356f1..8299a2331cb 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -251,4 +251,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); From b105b753472e0b59690ad13289b7169983d9b605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Wed, 2 Jun 2021 15:39:24 +0200 Subject: [PATCH 1065/1476] Sublime Bid Adapter : Add support for meta.advertiserDomains (#6920) * Add advertiserDomains stub * Use utils from require * Update version * Replace let to const and fix version --- modules/sublimeBidAdapter.js | 8 ++- test/spec/modules/sublimeBidAdapter_spec.js | 77 +++++++++++++-------- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 41fdb72e76e..854f58df8fe 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.7.1'; +const SUBLIME_VERSION = '0.7.2'; /** * Identify the current device type @@ -207,6 +207,12 @@ function interpretResponse(serverResponse, bidRequest) { sspname: response.sspname || null }; + // We don't support advertiserDomains atm + if (response.advertiserDomains) { + // Creating a stub for Prebid.js 5.0 compliance + bidResponse.meta = Object.assign({}, bidResponse.meta, { advertiserDomains: [] }); + } + bidResponses.push(bidResponse); } diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index 2e1d65f0533..fab487cb65f 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -47,7 +47,7 @@ describe('Sublime Adapter', function() { }); describe('isBidRequestValid', function() { - let bid = { + const bid = { bidder: 'sublime', params: { zoneId: 24549, @@ -60,14 +60,14 @@ describe('Sublime Adapter', function() { }); it('should return false when required params are not passed', function() { - let bid = Object.assign({}, bid); + const bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); describe('buildRequests', function() { - let bidRequests = [ + const bidRequests = [ { bidder: 'sublime', adUnitCode: 'sublime_code', @@ -90,7 +90,7 @@ describe('Sublime Adapter', function() { } ]; - let bidderRequest = { + const bidderRequest = { gdprConsent: { consentString: 'EOHEIRCOUCOUIEHZIOEIU-TEST', gdprApplies: true @@ -101,7 +101,7 @@ describe('Sublime Adapter', function() { } }; - let request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest); it('should have a post method', function() { expect(request[0].method).to.equal('POST'); @@ -121,7 +121,7 @@ describe('Sublime Adapter', function() { }); describe('buildRequests: default arguments', function() { - let bidRequests = [{ + const bidRequests = [{ bidder: 'sublime', adUnitCode: 'sublime_code', bidId: 'abc1234', @@ -132,7 +132,7 @@ describe('Sublime Adapter', function() { } }]; - let request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); it('should have an url that match the default endpoint', function() { expect(request[0].url).to.equal('https://pbjs.sskzlabs.com/bid'); @@ -140,7 +140,7 @@ describe('Sublime Adapter', function() { }); describe('interpretResponse', function() { - let serverResponse = { + const serverResponse = { 'request_id': '3db3773286ee59', 'sspname': 'foo', 'cpm': 0.5, @@ -155,7 +155,7 @@ describe('Sublime Adapter', function() { } }; - let expectedResponse = [ + const expectedResponse = [ { requestId: '', cpm: 0.5, @@ -167,23 +167,23 @@ describe('Sublime Adapter', function() { sspname: 'foo', netRevenue: true, ttl: 600, - pbav: '0.7.1', + pbav: '0.7.2', ad: '', }, ]; - let result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({body: serverResponse}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('should get correct default size for 1x1', function() { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'cpm': 0.5, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -197,9 +197,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({body: serverResponse}, bidRequest); + const result = spec.interpretResponse({body: serverResponse}, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0.5, width: 1, @@ -210,7 +210,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', sspname: 'sublime' }; @@ -218,24 +218,24 @@ describe('Sublime Adapter', function() { }); it('should return bid empty response', function () { - let serverResponse = ''; - let bidRequest = {}; + const serverResponse = ''; + const bidRequest = {}; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); }); it('should return bid with default value in response', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -249,9 +249,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0, width: 1, @@ -263,20 +263,20 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', }; expect(result[0]).to.deep.equal(expectedResponse); }); it('should return empty bid response because of timeout', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'timeout': true, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -290,9 +290,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); @@ -300,11 +300,28 @@ describe('Sublime Adapter', function() { spec.onTimeout(result); }); }); + + it('should add advertiserDomains', function() { + const responseWithAdvertiserDomains = utils.deepClone(serverResponse); + responseWithAdvertiserDomains.advertiserDomains = ['a_sublime_adomain']; + + const bidRequest = { + bidder: 'sublime', + params: { + zoneId: 456, + } + }; + + const result = spec.interpretResponse({ body: responseWithAdvertiserDomains }, bidRequest); + + expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); + expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + }); }); describe('onBidWon', function() { let sandbox; - let bid = { foo: 'bar' }; + const bid = { foo: 'bar' }; beforeEach(function () { sandbox = sinon.sandbox.create(); From db50c5b49bb7bfdf68f2a36ae7295fac60ff9f37 Mon Sep 17 00:00:00 2001 From: Margaret Liu Date: Wed, 2 Jun 2021 09:27:32 -0500 Subject: [PATCH 1066/1476] LockerDome Bid Adapter: support for meta.advertiserDomains (#6921) * Add lockerdome adapter advertiserDomains support stub for Prebid 5.0 * Add lockerdome adapter advertiserDomains support stub for Prebid 5.0 * fix linting error Co-authored-by: Chris Huie --- modules/lockerdomeBidAdapter.js | 5 ++++- test/spec/modules/lockerdomeBidAdapter_spec.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 80c40b39f9a..4e30519c6d3 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -66,7 +66,10 @@ export const spec = { currency: bid.currency, netRevenue: bid.netRevenue, ad: bid.ad, - ttl: bid.ttl + ttl: bid.ttl, + meta: { + advertiserDomains: bid.adomain && Array.isArray(bid.adomain) ? bid.adomain : [] + } }; }); }, diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index 42e3f52e533..9e3d7981300 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -180,7 +180,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }, { requestId: '4510f2834773ce', @@ -191,7 +192,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }] } }; @@ -217,6 +219,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.bids[0].netRevenue); expect(interpretedResponse[0].ad).to.equal(serverResponse.body.bids[0].ad); expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.bids[0].ttl); + expect(interpretedResponse[0]).to.have.property('meta'); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[0].adomain); expect(interpretedResponse[1].requestId).to.equal(serverResponse.body.bids[1].requestId); expect(interpretedResponse[1].cpm).to.equal(serverResponse.body.bids[1].cpm); @@ -227,6 +232,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[1].netRevenue).to.equal(serverResponse.body.bids[1].netRevenue); expect(interpretedResponse[1].ad).to.equal(serverResponse.body.bids[1].ad); expect(interpretedResponse[1].ttl).to.equal(serverResponse.body.bids[1].ttl); + expect(interpretedResponse[1]).to.have.property('meta'); + expect(interpretedResponse[1].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[1].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[1].adomain); }); }); }); From b271db1f72258847a27f9e043b7c47e9e8c98991 Mon Sep 17 00:00:00 2001 From: Gena Date: Wed, 2 Jun 2021 17:51:08 +0300 Subject: [PATCH 1067/1476] Update Adtelligent, Adtarget, ViewDeos adapters to support adomain (#6917) * Update Adtelligent, Adtarget, ViewDeos adapters to support adomain * fix * fix tests --- modules/adtargetBidAdapter.js | 5 ++++- modules/adtelligentBidAdapter.js | 5 ++++- modules/viewdeosDXBidAdapter.js | 5 ++++- test/spec/modules/adtargetBidAdapter_spec.js | 13 ++++++++++--- test/spec/modules/adtelligentBidAdapter_spec.js | 17 ++++++++++++----- test/spec/modules/viewdeosDXBidAdapter_spec.js | 16 +++++++++++----- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index b22addec54f..1779ba94371 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -173,7 +173,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 9ca4b95dfd2..d21931a6dcb 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -248,7 +248,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js index 22c16ab5b34..212759642f5 100644 --- a/modules/viewdeosDXBidAdapter.js +++ b/modules/viewdeosDXBidAdapter.js @@ -190,7 +190,10 @@ function createBid(bidResponse, mediaType, bidderParams) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 3600 + ttl: 3600, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === DISPLAY) { diff --git a/test/spec/modules/adtargetBidAdapter_spec.js b/test/spec/modules/adtargetBidAdapter_spec.js index 5a867e7dd52..d1221d24022 100644 --- a/test/spec/modules/adtargetBidAdapter_spec.js +++ b/test/spec/modules/adtargetBidAdapter_spec.js @@ -45,7 +45,8 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 + 'cpm': 0.9, + 'adomain': ['a.com'] }] }; const SERVER_DISPLAY_RESPONSE = { @@ -107,7 +108,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -120,7 +124,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('adtargetBidAdapter', () => { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index e6916997133..4cfb367efb3 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -80,9 +80,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; const SERVER_DISPLAY_RESPONSE = { @@ -163,7 +163,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -177,7 +180,11 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } + }]; describe('adtelligentBidAdapter', () => { diff --git a/test/spec/modules/viewdeosDXBidAdapter_spec.js b/test/spec/modules/viewdeosDXBidAdapter_spec.js index f9bee1b0efe..31df9244ada 100644 --- a/test/spec/modules/viewdeosDXBidAdapter_spec.js +++ b/test/spec/modules/viewdeosDXBidAdapter_spec.js @@ -39,9 +39,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; @@ -123,7 +123,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -136,7 +139,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('viewdeosDXBidAdapter', function () { From 42093f41109a9e7e5505243c9cd77d4a2dc932f3 Mon Sep 17 00:00:00 2001 From: Giudici-a <34242194+Giudici-a@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:07:44 +0200 Subject: [PATCH 1068/1476] Adot Bid Adapter: add the advertising domains support (#6876) --- modules/adotBidAdapter.js | 54 +++++- modules/adotBidAdapter.md | 44 +++-- test/spec/modules/adotBidAdapter_spec.js | 225 +++++++++++++---------- 3 files changed, 191 insertions(+), 132 deletions(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 502ef05b0d5..ddd9531eb43 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -74,7 +74,7 @@ function isBanner(mediaTypes) { } function isVideo(mediaTypes) { - return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.video); + return isPlainObject(mediaTypes) && 'video' in mediaTypes; } function validateBanner(banner) { @@ -104,13 +104,16 @@ function validateMediaSizes(mediaSize) { function validateParameters(parameters, adUnit) { if (isVideo(adUnit.mediaTypes)) { if (!isPlainObject(parameters)) return false; + if (!isPlainObject(adUnit.mediaTypes.video)) return false; if (!validateVideoParameters(parameters.video, adUnit)) return false; } return true; } -function validateVideoParameters(video, adUnit) { +function validateVideoParameters(videoParams, adUnit) { + const video = adUnit.mediaTypes.video; + if (!video) return false; if (!isArray(video.mimes)) return false; @@ -124,9 +127,9 @@ function validateVideoParameters(video, adUnit) { if (video.protocols.length === 0) return false; if (!video.protocols.every(isNumber)) return false; - if (isInstream(adUnit.mediaTypes.video)) { - if (!video.instreamContext) return false; - if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(video.instreamContext) === -1) return false; + if (isInstream(video)) { + if (!videoParams.instreamContext) return false; + if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(videoParams.instreamContext) === -1) return false; } return true; @@ -203,7 +206,7 @@ function generateImpressionsFromAdUnit(acc, adUnit) { if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params)); if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp, ext}); - if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data, params), pmp, ext}); + if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data), pmp, ext}); }, []); return acc.concat(imps); @@ -226,25 +229,52 @@ function generateBannerFromAdUnit(impId, data, params) { function generateVideoFromAdUnit(data, params) { const {playerSize} = data; + const video = data + const hasPlayerSize = isArray(playerSize) && playerSize.length > 0; - const {position, video = {}} = params; const {minDuration, maxDuration, protocols} = video; const size = {width: hasPlayerSize ? playerSize[0][0] : null, height: hasPlayerSize ? playerSize[0][1] : null}; const duration = {min: isNumber(minDuration) ? minDuration : null, max: isNumber(maxDuration) ? maxDuration : null}; + const startdelay = computeStartDelay(data, params); return { mimes: SUPPORTED_VIDEO_MIMES, + skip: video.skippable || 0, w: size.width, h: size.height, - startdelay: computeStartDelay(data, params), + startdelay: startdelay, + linearity: video.linearity || null, minduration: duration.min, maxduration: duration.max, protocols, - pos: position || 0 + api: getApi(protocols), + format: hasPlayerSize ? playerSize.map(s => { + return {w: s[0], h: s[1]}; + }) : null, + pos: video.position || 0 }; } +function getApi(protocols) { + let defaultValue = [2]; + let listProtocols = [ + {key: 'VPAID_1_0', value: 1}, + {key: 'VPAID_2_0', value: 2}, + {key: 'MRAID_1', value: 3}, + {key: 'ORMMA', value: 4}, + {key: 'MRAID_2', value: 5}, + {key: 'MRAID_3', value: 6}, + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1; + }).map(p => p.value) + } else { + return defaultValue; + } +} + function isInstream(video) { return isPlainObject(video) && (video.context === 'instream'); } @@ -263,7 +293,7 @@ function computeStartDelay(data, params) { return null; } -function generateNativeFromAdUnit(data, params) { +function generateNativeFromAdUnit(data) { const {type} = data; const presetFormatter = type && NATIVE_PRESET_FORMATTERS[data.type]; const nativeFields = presetFormatter ? presetFormatter(data) : data; @@ -500,6 +530,10 @@ function generateAdFromBid(bid, bidResponse, serverRequest) { mediaType: bid.ext.adot.media_type, }; + if (bid.adomain) { + base.meta = { advertiserDomains: bid.adomain }; + } + if (isBidANative(bid)) return {...base, native: formatNativeData(bid)}; const size = getSizeFromBid(bid, impressionData); diff --git a/modules/adotBidAdapter.md b/modules/adotBidAdapter.md index 98a4a1a5970..894a592ec18 100644 --- a/modules/adotBidAdapter.md +++ b/modules/adotBidAdapter.md @@ -47,23 +47,22 @@ const adUnit = { context: 'outstream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { - video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3] - } + video: {} } }] } @@ -82,23 +81,22 @@ const adUnit = { context: 'instream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3], - // Instream video context. Must be either 'pre-roll', 'mid-roll' or 'post-roll'. instreamContext: 'pre-roll' } } diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index d580cd763a4..40605b17b20 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -25,19 +25,16 @@ describe('Adot Adapter', function () { bidder: 'adot', bidderRequestId: 'bid_request_id', bidId: 'bid_id', - params: { + params: {}, + mediaTypes: { video: { + context: 'outstream', + playerSize: [[300, 250]], mimes: ['video/mp4'], minDuration: 5, maxDuration: 30, protocols: [2, 3] } - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[300, 250]] - } } }, @@ -48,17 +45,17 @@ describe('Adot Adapter', function () { bidId: 'bid_id', params: { video: { - instreamContext: 'pre-roll', - mimes: ['video/mp4'], - minDuration: 5, - maxDuration: 30, - protocols: [2, 3] + instreamContext: 'pre-roll' } }, mediaTypes: { video: { context: 'instream', - playerSize: [[300, 250]] + playerSize: [[300, 250]], + mimes: ['video/mp4'], + minDuration: 5, + maxDuration: 30, + protocols: [2, 3] } } }, @@ -557,6 +554,7 @@ describe('Adot Adapter', function () { price: 1.5, h: 350, w: 300, + adomain: ['adot'], ext: { adot: { media_type: 'banner' @@ -568,6 +566,7 @@ describe('Adot Adapter', function () { crid: 'creative_id_2', adm: 'creative_data_2_${AUCTION_PRICE}', nurl: 'win_notice_url_2_${AUCTION_PRICE}', + adomain: ['adot'], price: 2.5, h: 400, w: 350, @@ -913,14 +912,14 @@ describe('Adot Adapter', function () { it('should return true when given an ad unit without minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); it('should return true when given an ad unit without maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); @@ -941,84 +940,84 @@ describe('Adot Adapter', function () { it('should return false when given an ad unit without video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = undefined; + adUnit.mediaTypes.video = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with invalid video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = 'bad_bidder_parameters'; + adUnit.mediaTypes.video = 'bad_bidder_parameters'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = undefined; + adUnit.mediaTypes.video.mimes = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = 'bad_mime_types'; + adUnit.mediaTypes.video.mimes = 'bad_mime_types'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = []; + adUnit.mediaTypes.video.mimes = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = [200]; + adUnit.mediaTypes.video.mimes = [200]; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = 'bad_min_duration'; + adUnit.mediaTypes.video.minDuration = 'bad_min_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = 'bad_max_duration'; + adUnit.mediaTypes.video.maxDuration = 'bad_max_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = undefined; + adUnit.mediaTypes.video.protocols = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = 'bad_protocols'; + adUnit.mediaTypes.video.protocols = 'bad_protocols'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = []; + adUnit.mediaTypes.video.protocols = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = ['bad_protocols_value']; + adUnit.mediaTypes.video.protocols = ['bad_protocols_value']; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); @@ -1452,13 +1451,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid pre-roll instream ad unit', function () { @@ -1476,13 +1475,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(0); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid mid-roll instream ad unit', function () { @@ -1500,13 +1499,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-1); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid post-roll instream ad unit', function () { @@ -1524,13 +1523,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-2); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without player size', function () { @@ -1548,13 +1547,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with an empty player size', function () { @@ -1572,13 +1571,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with multiple player sizes', function () { @@ -1596,18 +1595,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without minimum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; const adUnits = [adUnit]; @@ -1620,18 +1619,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); expect(serverRequests[0].data.imp[0].video.minduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without maximum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; const adUnits = [adUnit]; @@ -1644,13 +1643,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); expect(serverRequests[0].data.imp[0].video.maxduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with two impressions when given two valid ad units with different impression identifiers', function () { @@ -1671,31 +1670,31 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[0].data.imp[1]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[1].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[1].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[1].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[1].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[1].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return a server request with one overridden impression when given two valid ad units with identical identifiers', function () { const adUnit_1 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_1.params.video.minDuration = 10; + adUnit_1.mediaTypes.video.minDuration = 10; const adUnit_2 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_2.params.video.minDuration = 15; + adUnit_2.mediaTypes.video.minDuration = 15; const adUnits = [adUnit_1, adUnit_2]; @@ -1708,13 +1707,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return two server requests with one impression when given two valid ad units with different bid request identifiers', function () { @@ -1735,25 +1734,25 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[1].data).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); expect(serverRequests[1].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[1].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[1].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[1].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[1].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); }); @@ -2121,6 +2120,48 @@ describe('Adot Adapter', function () { expect(ads[1].renderer).to.equal(null); }); + it('should return two ads when given a valid server response with two bids that contains adomain', function () { + const serverRequest = examples.serverRequest_banner_twoImps; + + const serverResponse = examples.serverResponse_banner_twoBids; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); + const adm2WithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[1].adm, serverResponse.body.seatbid[0].bid[1].price); + + expect(ads).to.be.an('array').and.to.have.length(2); + + expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); + expect(ads[0].adUrl).to.equal(null); + expect(ads[0].vastXml).to.equal(null); + expect(ads[0].vastUrl).to.equal(null); + expect(ads[0].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].adomain[0]) + expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[0].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[0].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[0].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(ads[0].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[0].renderer).to.equal(null); + expect(ads[1].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[1].bidId); + expect(ads[1].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].adomain[0]) + expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(adm2WithAuctionPriceReplaced); + expect(ads[1].adUrl).to.equal(null); + expect(ads[1].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].crid); + expect(ads[1].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].price); + expect(ads[1].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[1].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[1].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[1].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].h); + expect(ads[1].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].w); + expect(ads[1].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[1].renderer).to.equal(null); + }); + it('should return no ad when not given a server response', function () { const ads = spec.interpretResponse(null); @@ -3084,20 +3125,6 @@ describe('Adot Adapter', function () { expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].renderer).to.be.an('object'); }); - - it('should append a command to the ad rendering queue when executing the renderer', function (done) { - const serverRequest = examples.serverRequest_video_outstream; - const serverResponse = examples.serverResponse_video_outstream; - - const [ad] = spec.interpretResponse(serverResponse, serverRequest); - - this.spyAdRenderingQueue(ad); - - executeAdRenderer(ad, () => { - expect(ad.renderer.push.calledOnce).to.equal(true); - expect(ad.renderer.push.firstCall.args[0]).to.exist.and.to.be.a('function'); - }, done); - }); }); }); From b04d2129d4de0752577fcdf061c1363870cb6db0 Mon Sep 17 00:00:00 2001 From: Jonas Gresens <5290643+jgresens@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:26:56 +0200 Subject: [PATCH 1069/1476] Smaato bid adapter: Rework multi imp support (#6814) * Smaato bid adapter: Refactor, clean and add tests * Smaato bid adapter: Create individual request to Smaatos server per valid bid request --- modules/smaatoBidAdapter.js | 181 ++-- test/spec/modules/smaatoBidAdapter_spec.js | 968 +++++++++++---------- 2 files changed, 604 insertions(+), 545 deletions(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index fbb4c14e5f8..719d78892ce 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -5,75 +5,22 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const CLIENT = 'prebid_js_$prebid.version$_1.1' - -/** -* Transform BidRequest to OpenRTB-formatted BidRequest Object -* @param {Array} validBidRequests -* @param {any} bidderRequest -* @returns {string} -*/ -const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { - /** - * Turn incoming prebid sizes into openRtb format mapping. - * @param {*} sizes in format [[10, 10], [20, 20]] - * @returns array of openRtb format mappings [{w: 10, h: 10}, {w: 20, h: 20}] - */ - const parseSizes = (sizes) => { - return sizes.map((size) => { - return {w: size[0], h: size[1]}; - }) - } - - const imp = validBidRequests.map(br => { - const bannerMediaType = utils.deepAccess(br, 'mediaTypes.banner'); - const videoMediaType = utils.deepAccess(br, 'mediaTypes.video'); - let result = { - id: br.bidId, - tagid: utils.deepAccess(br, 'params.adspaceId') - } - - if (bannerMediaType) { - const sizes = parseSizes(utils.getAdUnitSizes(br)); - result.banner = { - w: sizes[0].w, - h: sizes[0].h, - format: sizes - } - } - - if (videoMediaType) { - result.video = { - mimes: videoMediaType.mimes, - minduration: videoMediaType.minduration, - startdelay: videoMediaType.startdelay, - linearity: videoMediaType.linearity, - w: videoMediaType.playerSize[0][0], - h: videoMediaType.playerSize[0][1], - maxduration: videoMediaType.maxduration, - skip: videoMediaType.skip, - protocols: videoMediaType.protocols, - ext: { - rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 - }, - skipmin: videoMediaType.skipmin, - api: videoMediaType.api - } - } - - return result; - }); +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.2' +const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const request = { id: bidderRequest.auctionId, at: 1, - imp, + imp: [{ + id: bidRequest.bidId, + tagid: utils.deepAccess(bidRequest, 'params.adspaceId') + }], cur: ['USD'], tmax: bidderRequest.timeout, site: { id: window.location.hostname, publisher: { - id: utils.deepAccess(validBidRequests[0], 'params.publisherId') + id: utils.deepAccess(bidRequest, 'params.publisherId') }, domain: window.location.hostname, page: window.location.href, @@ -94,12 +41,40 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { ext: {} }, ext: { - client: CLIENT + client: SMAATO_CLIENT } }; - let ortb2 = config.getConfig('ortb2') || {}; + if (utils.deepAccess(bidRequest, 'mediaTypes.banner')) { + const sizes = utils.getAdUnitSizes(bidRequest).map((size) => ({w: size[0], h: size[1]})); + request.imp[0].banner = { + w: sizes[0].w, + h: sizes[0].h, + format: sizes + } + } + const videoMediaType = utils.deepAccess(bidRequest, 'mediaTypes.video'); + if (videoMediaType) { + request.imp[0].video = { + mimes: videoMediaType.mimes, + minduration: videoMediaType.minduration, + startdelay: videoMediaType.startdelay, + linearity: videoMediaType.linearity, + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + maxduration: videoMediaType.maxduration, + skip: videoMediaType.skip, + protocols: videoMediaType.protocols, + ext: { + rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 + }, + skipmin: videoMediaType.skipmin, + api: videoMediaType.api + } + } + + let ortb2 = config.getConfig('ortb2') || {}; Object.assign(request.user, ortb2.user); Object.assign(request.site, ortb2.site); @@ -112,20 +87,19 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (utils.deepAccess(validBidRequests[0], 'params.app')) { - const geo = utils.deepAccess(validBidRequests[0], 'params.app.geo'); + if (utils.deepAccess(bidRequest, 'params.app')) { + const geo = utils.deepAccess(bidRequest, 'params.app.geo'); utils.deepSetValue(request, 'device.geo', geo); - const ifa = utils.deepAccess(validBidRequests[0], 'params.app.ifa') + const ifa = utils.deepAccess(bidRequest, 'params.app.ifa') utils.deepSetValue(request, 'device.ifa', ifa); } - const eids = utils.deepAccess(validBidRequests[0], 'userIdAsEids'); + const eids = utils.deepAccess(bidRequest, 'userIdAsEids'); if (eids && eids.length) { utils.deepSetValue(request, 'user.ext.eids', eids); } - utils.logInfo('[SMAATO] OpenRTB Request:', request); - return JSON.stringify(request); + return request } export const spec = { @@ -133,41 +107,41 @@ export const spec = { 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. - */ + * 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: (bid) => { return typeof bid.params === 'object' && - typeof bid.params.publisherId === 'string' && - typeof bid.params.adspaceId === 'string'; + typeof bid.params.publisherId === 'string' && + typeof bid.params.adspaceId === 'string'; }, - /** - * 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: (validBidRequests, bidderRequest) => { - utils.logInfo('[SMAATO] Client version:', CLIENT); - return { - method: 'POST', - url: validBidRequests[0].params.endpoint || SMAATO_ENDPOINT, - data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), - options: { - withCredentials: true, - crossOrigin: true, - } - }; + utils.logInfo('[SMAATO] Client version:', SMAATO_CLIENT); + + return validBidRequests.map((validBidRequest) => { + const openRtbBidRequest = buildOpenRtbBidRequest(validBidRequest, bidderRequest); + utils.logInfo('[SMAATO] OpenRTB Request:', openRtbBidRequest); + + return { + method: 'POST', + url: validBidRequest.params.endpoint || SMAATO_ENDPOINT, + data: JSON.stringify(openRtbBidRequest), + options: { + withCredentials: true, + crossOrigin: true, + } + }; + }); }, /** - * 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. - */ + * 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: (serverResponse, bidRequest) => { // response is empty (HTTP 204) if (utils.isEmpty(serverResponse.body)) { @@ -235,15 +209,14 @@ export const spec = { }, /** - * 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. - */ + * 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: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - const syncs = [] - return syncs; + return []; } } registerBidder(spec); diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 1bc77fc9572..0abc8463d28 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,200 +1,36 @@ import { spec } from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; -import {createEidsArray} from 'modules/userId/eids.js'; - -const imageAd = { - image: { - img: { - url: 'https://prebid/static/ad.jpg', - w: 320, - h: 50, - ctaurl: 'https://prebid/track/ctaurl' - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; - -const richmediaAd = { - richmedia: { - mediadata: { - content: '

RICHMEDIA CONTENT

', - w: 800, - h: 600 - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; +import { config } from 'src/config.js'; +import { createEidsArray } from 'modules/userId/eids.js'; const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const site = { - keywords: 'power tools,drills' -}; - -const user = { - keywords: 'a,b', - gender: 'M', - yob: 1984 -}; - -const openRtbBidResponse = (adType) => { - let adm = ''; - - switch (adType) { - case ADTYPE_IMG: - adm = JSON.stringify(imageAd); - break; - case ADTYPE_RICHMEDIA: - adm = JSON.stringify(richmediaAd); - break; - case ADTYPE_VIDEO: - adm = ''; - break; - default: - throw Error('Invalid AdType'); - } - - let resp = { - body: { - bidid: '04db8629-179d-4bcd-acce-e54722969006', - cur: 'USD', - ext: {}, - id: '5ebea288-f13a-4754-be6d-4ade66c68877', - seatbid: [ - { - bid: [ - { - 'adm': adm, - 'adomain': [ - 'smaato.com' - ], - 'bidderName': 'smaato', - 'cid': 'CM6523', - 'crid': 'CR69381', - 'dealid': '12345', - 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', - 'impid': '226416e6e6bf41', - 'iurl': 'https://prebid/iurl', - 'nurl': 'https://prebid/nurl', - 'price': 0.01, - 'w': 350, - 'h': 50 - } - ], - seat: 'CM6523' - } - ], - }, - headers: { - get: function (header) { - if (header === 'X-SMT-ADTYPE') { - return adType; - } - } - } - }; - return resp; -}; - const request = { method: 'POST', url: 'https://prebid.ad.smaato.net/oapi/prebid', data: '' }; -const interpretedBidsImg = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '
\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsRichmedia = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '

RICHMEDIA CONTENT

\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsVideo = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - vastXml: '', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'video' - } - } -]; +const REFERRER = 'http://example.com/page.html' +const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' const defaultBidderRequest = { gdprConsent: { - consentString: 'HFIDUYFIUYIUYWIPOI87392DSU', + consentString: CONSENT_STRING, gdprApplies: true }, uspConsent: 'uspConsentString', refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, }, timeout: 1200 }; const minimalBidderRequest = { refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, } }; @@ -221,77 +57,6 @@ const singleBannerBidRequest = { bidderWinsCount: 0 }; -const singleVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - -const combinedBannerAndVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - }, - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - const inAppBidRequest = { bidder: 'smaato', params: { @@ -322,36 +87,10 @@ const inAppBidRequest = { bidderWinsCount: 0 }; -const userIdBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - userId: { - criteoId: '123456', - tdid: '89145' - }, - userIdAsEids: createEidsArray({ - criteoId: '123456', - tdid: '89145' - }) -}; +const extractPayloadOfFirstAndOnlyRequest = (reqs) => { + expect(reqs).to.have.length(1); + return JSON.parse(reqs[0].data); +} describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { @@ -368,222 +107,557 @@ describe('smaatoBidAdapterTest', () => { }); describe('buildRequests', () => { - beforeEach(() => { - this.req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - this.sandbox = sinon.sandbox.create(); - }); + describe('common', () => { + it('auction type is 1 (first price auction)', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - afterEach(() => { - this.sandbox.restore(); - }); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.at).to.be.equal(1); + }) - it('can override endpoint', () => { - const overridenEndpoint = 'https://prebid/bidder'; - let bidRequest = utils.deepClone(singleBannerBidRequest); - utils.deepSetValue(bidRequest, 'params.endpoint', overridenEndpoint); - const actualEndpoint = spec.buildRequests([bidRequest], defaultBidderRequest).url; - expect(actualEndpoint).to.equal(overridenEndpoint); - }); + it('currency is US dollar', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends correct imps', () => { - expect(this.req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.cur).to.be.deep.equal(['USD']); + }) + + it('can override endpoint', () => { + const overridenEndpoint = 'https://prebid/bidder'; + const updatedBidRequest = utils.deepClone(singleBannerBidRequest); + utils.deepSetValue(updatedBidRequest, 'params.endpoint', overridenEndpoint); + + const reqs = spec.buildRequests([updatedBidRequest], defaultBidderRequest); + + expect(reqs).to.have.length(1); + expect(reqs[0].url).to.equal(overridenEndpoint); + }); + + it('sends correct imp', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + tagid: 'adspaceId' + } + ]); + }); + + it('sends correct site', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.id).to.exist.and.to.be.a('string'); + expect(req.site.domain).to.exist.and.to.be.a('string'); + expect(req.site.page).to.exist.and.to.be.a('string'); + expect(req.site.ref).to.equal(REFERRER); + expect(req.site.publisher.id).to.equal('publisherId'); + }) + + it('sends gdpr applies if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.equal(1); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + }); + + it('sends no gdpr applies if no gdpr exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.not.exist; + expect(req.user.ext.consent).to.not.exist; + }); + + it('sends us_privacy if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.equal('uspConsentString'); + }); + + it('sends tmax', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.tmax).to.equal(1200); + }); + + it('sends no us_privacy if no us_privacy exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.not.exist; + }); + + it('sends first party data', () => { + this.sandbox = sinon.sandbox.create() + this.sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + ortb2: { + site: { + keywords: 'power tools,drills' + }, + user: { + keywords: 'a,b', + gender: 'M', + yob: 1984 } - ] - }, - tagid: 'adspaceId' - } - ]) - }); + } + }; + return utils.deepAccess(config, key); + }); + + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.gender).to.equal('M'); + expect(req.user.yob).to.equal(1984); + expect(req.user.keywords).to.eql('a,b'); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + expect(req.site.keywords).to.eql('power tools,drills'); + expect(req.site.publisher.id).to.equal('publisherId'); + this.sandbox.restore(); + }); - it('sends correct site', () => { - expect(this.req.site.id).to.exist.and.to.be.a('string'); - expect(this.req.site.domain).to.exist.and.to.be.a('string'); - expect(this.req.site.page).to.exist.and.to.be.a('string'); - expect(this.req.site.ref).to.equal('http://example.com/page.html'); - expect(this.req.site.publisher.id).to.equal('publisherId'); - }) + it('has no user ids', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends gdpr applies if exists', () => { - expect(this.req.regs.ext.gdpr).to.equal(1); - expect(this.req.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.not.exist; + }); }); - it('sends no gdpr applies if no gdpr exists', () => { - let req_without_gdpr = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_gdpr.regs.ext.gdpr).to.not.exist; - expect(req_without_gdpr.user.ext.consent).to.not.exist; - }); + describe('multiple requests', () => { + it('build individual server request for each bid request', () => { + const bidRequest1 = utils.deepClone(singleBannerBidRequest); + const bidRequest1BidId = '1111'; + utils.deepSetValue(bidRequest1, 'bidId', bidRequest1BidId); + const bidRequest2 = utils.deepClone(singleBannerBidRequest); + const bidRequest2BidId = '2222'; + utils.deepSetValue(bidRequest2, 'bidId', bidRequest2BidId); - it('sends usp if exists', () => { - expect(this.req.regs.ext.us_privacy).to.equal('uspConsentString'); - }); + const reqs = spec.buildRequests([bidRequest1, bidRequest2], defaultBidderRequest); - it('sends tmax', () => { - expect(this.req.tmax).to.equal(1200); + expect(reqs).to.have.length(2); + expect(JSON.parse(reqs[0].data).imp[0].id).to.be.equal(bidRequest1BidId); + expect(JSON.parse(reqs[1].data).imp[0].id).to.be.equal(bidRequest2BidId); + }); }); - it('sends no usp if no usp exists', () => { - let req_without_usp = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_usp.regs.ext.us_privacy).to.not.exist; - }); + describe('buildRequests for video imps', () => { + it('sends correct video imps', () => { + const singleVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; - it('sends fp data', () => { - this.sandbox.stub(config, 'getConfig').callsFake(key => { - const config = { - ortb2: { - site, - user + const reqs = spec.buildRequests([singleVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' } + ]); + }); + + it('allows combined banner and video imp in single bid request', () => { + const combinedBannerAndVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + }, + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 }; - return utils.deepAccess(config, key); + + const reqs = spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' + } + ]); }); - let bidRequest = utils.deepClone(singleBannerBidRequest); - let req_fpd = JSON.parse(spec.buildRequests([bidRequest], defaultBidderRequest).data); - expect(req_fpd.user.gender).to.equal('M'); - expect(req_fpd.user.yob).to.equal(1984); - expect(req_fpd.user.keywords).to.eql('a,b'); - expect(req_fpd.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); - expect(req_fpd.site.keywords).to.eql('power tools,drills'); - expect(req_fpd.site.publisher.id).to.equal('publisherId'); }); - it('has no user ids', () => { - expect(this.req.user.ext.eids).to.not.exist; + describe('in-app requests', () => { + it('add geo and ifa info to device object', () => { + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('when geo is missing, then add only ifa to device object', () => { + const inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); + delete inAppBidRequestWithoutGeo.params.app.geo + + const reqs = spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('add no specific device info if param does not exist', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.not.exist; + }); }); - }); - describe('buildRequests for video imps', () => { - it('sends correct video imps', () => { - let req = JSON.parse(spec.buildRequests([singleVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + describe('user ids in requests', () => { + it('user ids are added to user.ext.eids', () => { + const userIdBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows combines banner and video imp in single bid request', () => { - let req = JSON.parse(spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 - } - ] + mediaTypes: { + banner: { + sizes: [[300, 50]] + } }, - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + userId: { + criteoId: '123456', + tdid: '89145' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows two imps in the same bid request', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest, singleBannerBidRequest], defaultBidderRequest).data); - expect(req.imp).to.have.length(2); - }); - }); + userIdAsEids: createEidsArray({ + criteoId: '123456', + tdid: '89145' + }) + }; - describe('in-app requests', () => { - it('add geo and ifa info to device object', () => { - let req = JSON.parse(spec.buildRequests([inAppBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add only ifa to device object', () => { - let inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); - delete inAppBidRequestWithoutGeo.params.app.geo - let req = JSON.parse(spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest).data); + const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add no specific device info if param does not exist', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.not.exist; + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.exist; + expect(req.user.ext.eids).to.have.length(2); + }); }); }); - describe('user ids in requests', () => { - it('user ids are added to user.ext.eids', () => { - let req = JSON.parse(spec.buildRequests([userIdBidRequest], defaultBidderRequest).data); - expect(req.user.ext.eids).to.exist; - expect(req.user.ext.eids).to.have.length(2); + describe('interpretResponse', () => { + const buildOpenRtbBidResponse = (adType) => { + let adm = ''; + + switch (adType) { + case ADTYPE_IMG: + adm = JSON.stringify({ + image: { + img: { + url: 'https://prebid/static/ad.jpg', + w: 320, + h: 50, + ctaurl: 'https://prebid/track/ctaurl' + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_RICHMEDIA: + adm = JSON.stringify({ + richmedia: { + mediadata: { + content: '

RICHMEDIA CONTENT

', + w: 800, + h: 600 + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_VIDEO: + adm = ''; + break; + default: + throw Error('Invalid AdType'); + } + + return { + body: { + bidid: '04db8629-179d-4bcd-acce-e54722969006', + cur: 'USD', + ext: {}, + id: '5ebea288-f13a-4754-be6d-4ade66c68877', + seatbid: [ + { + bid: [ + { + 'adm': adm, + 'adomain': [ + 'smaato.com' + ], + 'bidderName': 'smaato', + 'cid': 'CM6523', + 'crid': 'CR69381', + 'dealid': '12345', + 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', + 'impid': '226416e6e6bf41', + 'iurl': 'https://prebid/iurl', + 'nurl': 'https://prebid/nurl', + 'price': 0.01, + 'w': 350, + 'h': 50 + } + ], + seat: 'CM6523' + } + ], + }, + headers: { + get: function (header) { + if (header === 'X-SMT-ADTYPE') { + return adType; + } + } + } + }; + }; + + it('returns empty array on no bid responses', () => { + const response_with_empty_body = {body: {}} + + const bids = spec.interpretResponse(response_with_empty_body, request); + + expect(bids).to.be.empty }); - }); - describe('interpretResponse', () => { it('single image reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); - assert.deepStrictEqual(bids, interpretedBidsImg); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single richmedia reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_RICHMEDIA), request); - assert.deepStrictEqual(bids, interpretedBidsRichmedia); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '

RICHMEDIA CONTENT

', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single video reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_VIDEO), request); - assert.deepStrictEqual(bids, interpretedBidsVideo); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } + } + ]); }); + it('ignores bid response with invalid ad type', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return undefined; } } + const bids = spec.interpretResponse(resp, request); + expect(bids).to.be.empty }); + it('uses correct TTL when expire header exists', () => { const clock = sinon.useFakeTimers(); clock.tick(2000); - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return ADTYPE_IMG; @@ -592,15 +666,27 @@ describe('smaatoBidAdapterTest', () => { return 2000 + (400 * 1000); } } + const bids = spec.interpretResponse(resp, request); + expect(bids[0].ttl).to.equal(400); + clock.restore(); }); + it('uses net revenue flag send from server', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.body.seatbid[0].bid[0].ext = {net: false}; + const bids = spec.interpretResponse(resp, request); + expect(bids[0].netRevenue).to.equal(false); }) }); + + describe('getUserSyncs', () => { + it('returns no pixels', () => { + expect(spec.getUserSyncs()).to.be.empty + }) + }) }); From 948cb1d04c8e8b740ee1013068b4f32d503fb53e Mon Sep 17 00:00:00 2001 From: mediaconsortium-develop <76139568+mediaconsortium-develop@users.noreply.github.com> Date: Thu, 3 Jun 2021 00:33:24 +0900 Subject: [PATCH 1070/1476] dgkeyword RTD Provider: add new real time data module (#6912) * re-open pull request reference #6410 * delete Overview * for test , comment out. * for test, comment out. * for test. comment out setConfig. * execute resetConfig aftet test. * execute resetConfig after test. * set emppty bidderConfig after test * fix setBidderConfig and test code * fix test code. * fix * for test resetBidderConfig in config * resetBidderConfig afterEach * setBidderConfig is not executed when ci test --- modules/.submodules.json | 1 + modules/dgkeywordRtdProvider.js | 134 +++++++ modules/dgkeywordRtdProvider.md | 29 ++ .../spec/modules/dgkeywordRtdProvider_spec.js | 351 ++++++++++++++++++ 4 files changed, 515 insertions(+) create mode 100644 modules/dgkeywordRtdProvider.js create mode 100644 modules/dgkeywordRtdProvider.md create mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 0a10044a78e..74ebc021a6d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js new file mode 100644 index 00000000000..58cec36a6b9 --- /dev/null +++ b/modules/dgkeywordRtdProvider.js @@ -0,0 +1,134 @@ +/** + * This module adds dgkeyword provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will get keywords from 1plux profile api. + * This module can work only with AppNexusBidAdapter. + * @module modules/dgkeywordProvider + * @requires module:modules/realTimeData + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +/** + * get keywords from api server. and set keywords. + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} moduleConfig + * @param {Object} userConsent + */ +export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { + const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; + const PROFILE_TIMEOUT_MS = 1000; + 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; + let isFinish = false; + utils.logMessage('[dgkeyword sub module]', adUnits, timeout); + let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); + if (setKeywordTargetBidders.length <= 0) { + utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); + callback(); + } else { + utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); + utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); + ajax(url, { + success: function(response) { + const res = JSON.parse(response); + if (!isFinish) { + utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); + if (res) { + let keywords = {}; + if (res['s'] != null && res['s'].length > 0) { + keywords['opeaud'] = res['s']; + } + if (res['t'] != null && res['t'].length > 0) { + keywords['opectx'] = res['t']; + } + if (Object.keys(keywords).length > 0) { + const targetBidKeys = {} + for (let bid of setKeywordTargetBidders) { + // set keywords to params + bid.params.keywords = keywords; + if (!targetBidKeys[bid.bidder]) { + targetBidKeys[bid.bidder] = true; + } + } + + if (!reqBidsConfigObj._ignoreSetOrtb2) { + // set keywrods to ortb2 + let addOrtb2 = {}; + utils.deepSetValue(addOrtb2, 'site.keywords', keywords); + utils.deepSetValue(addOrtb2, 'user.keywords', keywords); + const ortb2 = {ortb2: addOrtb2}; + reqBidsConfigObj.setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); + } + } + } + isFinish = true; + } + callback(); + }, + error: function(errorStatus) { + // error occur + utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); + callback(); + } + }, null, { + withCredentials: true, + contentType: 'application/json', + }); + setTimeout(function () { + if (!isFinish) { + // profile api timeout + utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); + isFinish = true; + } + callback(); + }, timeout); + } +} + +/** + * get all bidder which hava {dgkeyword: true} in params + * @param {Object} adUnits + */ +export function getTargetBidderOfDgKeywords(adUnits) { + let setKeywordTargetBidders = []; + for (let adUnit of adUnits) { + for (let bid of adUnit.bids) { + if (bid.params && bid.params['dgkeyword'] === true) { + delete bid.params['dgkeyword']; + setKeywordTargetBidders.push(bid); + } + } + } + return setKeywordTargetBidders; +} + +/** @type {RtdSubmodule} */ +export const dgkeywordSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'dgkeyword', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getBidRequestData: getDgKeywordsAndSet, + init: init, +}; + +function init(moduleConfig) { + return true; +} + +function registerSubModule() { + submodule('realTimeData', dgkeywordSubmodule); +} +registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md new file mode 100644 index 00000000000..314475b45a8 --- /dev/null +++ b/modules/dgkeywordRtdProvider.md @@ -0,0 +1,29 @@ + ## Integration + +1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: + +``` +gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var DGKEYWORD_TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: DGKEYWORD_TIMEOUT, + dataProviders: [{ + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DGKEYWORD_TIMEOUT + } + }] + } +}); +``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js new file mode 100644 index 00000000000..a145f429557 --- /dev/null +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -0,0 +1,351 @@ +import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; +import { cloneDeep } from 'lodash'; +import { server } from 'test/mocks/xhr.js'; +import { config } from 'src/config.js'; + +const DG_GET_KEYWORDS_TIMEOUT = 1950; +const IGNORE_SET_ORTB2 = true; +const DEF_CONFIG = { + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DG_GET_KEYWORDS_TIMEOUT, + }, +}; +const DUMMY_RESPONSE_HEADER = { 'Content-Type': 'application/json' }; +const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; +const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; +const SUCCESS_ORTB2 = { + ortb2: { + site: { keywords: SUCCESS_RESULT }, + user: { keywords: SUCCESS_RESULT }, + }, +}; + +describe('Digital Garage Keyword Module', function () { + it('should init and return always true', function () { + expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); + }); + + describe('dgkeyword target test', function () { + it('should have no target', function () { + const adUnits_no_target = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: false, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') + .that.is.empty; + }); + it('should have targets', function () { + const adUnits_targets = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + dgkeyword: true, + }, + }, + ], + }, + ]; + const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); + expect(targets[0].bidder).to.be.equal('dg2'); + expect(targets[0].params.placementId).to.be.equal(99999998); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].bidder).to.be.equal('dg'); + expect(targets[1].params.placementId).to.be.equal(99999996); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + }); + }); + + describe('get profile.', function () { + const AD_UNITS = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + it('should get profiles error(404).', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(pbjs.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond(404); + }); + it('should get profiles timeout.', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + moduleConfig.params.timeout = 10; + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(pbjs.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + setTimeout(() => { + const request = server.requests[0]; + if (request) { + request.respond( + 200, + DUMMY_RESPONSE_HEADER, + JSON.stringify(DUMMY_RESPONSE) + ); + } + }, 1000); + }); + it('should get profiles ok(200).', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + if (IGNORE_SET_ORTB2) { + pbjs._ignoreSetOrtb2 = true; + } + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + if (!IGNORE_SET_ORTB2) { + expect(pbjs.getBidderConfig()).to.be.deep.equal({ + dg2: SUCCESS_ORTB2, + dg: SUCCESS_ORTB2, + }); + } + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond( + 200, + DUMMY_RESPONSE_HEADER, + JSON.stringify(DUMMY_RESPONSE) + ); + }); + }); +}); From 0f267d801ca35b5f9dde797e344a301e6eaf02ee Mon Sep 17 00:00:00 2001 From: Rahul Shandilya <67756716+c3p-0@users.noreply.github.com> Date: Wed, 2 Jun 2021 21:54:28 +0530 Subject: [PATCH 1071/1476] Medianet bid adapter: MD update for video parameters. (#6922) --- modules/medianetBidAdapter.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/modules/medianetBidAdapter.md b/modules/medianetBidAdapter.md index b465d678fc9..fbe967122e9 100644 --- a/modules/medianetBidAdapter.md +++ b/modules/medianetBidAdapter.md @@ -79,17 +79,16 @@ var videoAdUnit = { mediaTypes: { video: { context: "instream", - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4'], + placement: 1 } }, bids: [{ bidder: 'medianet', params: { cid: '8CUX0H51C', - video: { - mimes: ['video/mp4'], - placement: 1 - }, + crid: '776755783', // Site member is to be used only for testing site: { page: 'http://smoketesting.net/prebidtest/', @@ -111,8 +110,10 @@ var videoAdUnit = { code: 'video1', mediaTypes: { video: { - context: "outstream", - playerSize: [640, 480] + context: "outstream", + playerSize: [640, 480], + mimes: ['video/mp4'], + placement: 1 } }, /** @@ -128,10 +129,7 @@ var videoAdUnit = { bidder: 'medianet', params: { cid: '8CUX0H51C', - video: { - mimes: ['video/mp4'], - placement: 1 - }, + crid: '776755783', // Site member is to be used only for testing site: { page: 'http://smoketesting.net/prebidtest/', From 5698959c5d0de2a527ea55986102329967105e3e Mon Sep 17 00:00:00 2001 From: Yohan Boutin Date: Wed, 2 Jun 2021 18:25:09 +0200 Subject: [PATCH 1072/1476] Seedtag Bid Adapter: read video params from mediaTypes, and allow override from bidder params, support passing adomain (#6888) * read video params from mediaTypes, and allow override from from bidder * implement meta.advertiserDomain * fix unit tests * allow outstream video --- modules/seedtagBidAdapter.js | 51 ++++++++++++++------- modules/seedtagBidAdapter.md | 21 +++++++-- test/spec/modules/seedtagBidAdapter_spec.js | 29 ++++++++++-- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index c25d833a71c..f4e99a2e2a0 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -46,7 +46,7 @@ function mapMediaType(seedtagMediaType) { } function hasVideoMediaType(bid) { - return !!bid.mediaTypes && !!bid.mediaTypes.video + return (!!bid.mediaTypes && !!bid.mediaTypes.video) || (!!bid.params && !!bid.params.video) } function hasMandatoryParams(params) { @@ -58,13 +58,12 @@ function hasMandatoryParams(params) { ); } -function hasMandatoryVideoParams(mediaTypes) { - const isVideoInStream = - !!mediaTypes.video && mediaTypes.video.context === 'instream'; - const isPlayerSize = - !!utils.deepAccess(mediaTypes, 'video.playerSize') && - utils.isArray(utils.deepAccess(mediaTypes, 'video.playerSize')); - return isVideoInStream && isPlayerSize; +function hasMandatoryVideoParams(bid) { + const videoParams = getVideoParams(bid) + + return hasVideoMediaType(bid) && !!videoParams.playerSize && + utils.isArray(videoParams.playerSize) && + videoParams.playerSize.length > 0; } function buildBidRequests(validBidRequests) { @@ -91,18 +90,33 @@ function buildBidRequests(validBidRequests) { } if (hasVideoMediaType(validBidRequest)) { - bidRequest.videoParams = params.video || {}; - bidRequest.videoParams.w = - validBidRequest.mediaTypes.video.playerSize[0][0]; - bidRequest.videoParams.h = - validBidRequest.mediaTypes.video.playerSize[0][1]; + bidRequest.videoParams = getVideoParams(validBidRequest) } return bidRequest; }) } -function buildBid(seedtagBid) { +/** + * return video param (global or overrided per bidder) + */ +function getVideoParams(validBidRequest) { + const videoParams = validBidRequest.mediaTypes.video || {}; + if (videoParams.playerSize) { + videoParams.w = videoParams.playerSize[0][0]; + 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 +} + +function buildBidResponse(seedtagBid) { const mediaType = mapMediaType(seedtagBid.mediaType); const bid = { requestId: seedtagBid.bidId, @@ -114,7 +128,10 @@ function buildBid(seedtagBid) { netRevenue: true, mediaType: mediaType, ttl: seedtagBid.ttl, - nurl: seedtagBid.nurl + nurl: seedtagBid.nurl, + meta: { + advertiserDomains: seedtagBid && seedtagBid.adomain && seedtagBid.adomain.length > 0 ? seedtagBid.adomain : [] + } }; if (mediaType === VIDEO) { @@ -152,7 +169,7 @@ export const spec = { */ isBidRequestValid(bid) { return hasVideoMediaType(bid) - ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid.mediaTypes) + ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid) : hasMandatoryParams(bid.params); }, @@ -197,7 +214,7 @@ export const spec = { const serverBody = serverResponse.body; if (serverBody && serverBody.bids && utils.isArray(serverBody.bids)) { return utils._map(serverBody.bids, function(bid) { - return buildBid(bid); + return buildBidResponse(bid); }); } else { return []; diff --git a/modules/seedtagBidAdapter.md b/modules/seedtagBidAdapter.md index 627ff8333ad..f4249fb2e89 100644 --- a/modules/seedtagBidAdapter.md +++ b/modules/seedtagBidAdapter.md @@ -46,7 +46,19 @@ var adUnits = [{ mediaTypes: { video: { context: 'instream', // required - playerSize: [600, 300] // required + playerSize: [600, 300], // required + 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: [ @@ -57,9 +69,10 @@ var adUnits = [{ adUnitId: '0000', // required placement: 'video', // required adPosition: 0, // optional - // Video object as specified in OpenRTB 2.5 - video: { - mimes: ['video/mp4'], // recommended + video: { // optional + context: 'instream', // optional + playerSize: [600, 300], // optional + mimes: ['video/mp4'], // optional minduration: 5, // optional maxduration: 60, // optional boxingallowed: 1, // optional diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index c82c8300d2e..cb04ada59c7 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('Seedtag Adapter', function() { }) }) describe('when video slot has all mandatory params', function() { - it('should return true, when video mediatype object are correct.', function() { + it('should return true, when video context is instream', function () { const slotConfig = getSlotConfigs( { video: { @@ -72,6 +72,24 @@ describe('Seedtag Adapter', function() { const isBidRequestValid = spec.isBidRequestValid(slotConfig) expect(isBidRequestValid).to.equal(true) }) + + it('should return true, when video context is outstream', function () { + const slotConfig = getSlotConfigs( + { + video: { + context: 'outstream', + playerSize: [[600, 200]] + } + }, + { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'video' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) }) }) describe('returns false', function() { @@ -137,7 +155,7 @@ describe('Seedtag Adapter', function() { ) expect(isBidRequestValid).to.equal(false) }) - it('is not instream ', function() { + it('is outstream ', function () { const isBidRequestValid = spec.isBidRequestValid( createVideoSlotConfig({ video: { @@ -146,7 +164,7 @@ describe('Seedtag Adapter', function() { } }) ) - expect(isBidRequestValid).to.equal(false) + expect(isBidRequestValid).to.equal(true) }) describe('order does not matter', function() { it('when video is not the first slot', function() { @@ -326,7 +344,8 @@ describe('Seedtag Adapter', function() { height: 90, mediaType: 'display', ttl: 360, - nurl: 'testurl.com/nurl' + nurl: 'testurl.com/nurl', + adomain: ['advertiserdomain.com'] } ], cookieSync: { url: '' } @@ -342,6 +361,7 @@ describe('Seedtag Adapter', function() { expect(bids[0].netRevenue).to.equal(true) expect(bids[0].ad).to.equal('content') expect(bids[0].nurl).to.equal('testurl.com/nurl') + expect(bids[0].meta.advertiserDomains).to.deep.equal(['advertiserdomain.com']) }) }) describe('the bid is a video', function() { @@ -374,6 +394,7 @@ describe('Seedtag Adapter', function() { expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) expect(bids[0].vastXml).to.equal('content') + expect(bids[0].meta.advertiserDomains).to.deep.equal([]) }) }) }) From 0cd655d28af6790457f652fa2e06ca3f2be2f6d8 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 2 Jun 2021 14:08:17 -0400 Subject: [PATCH 1073/1476] Prebid 4.42.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6df9993806c..4515885807c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.42.0-pre", + "version": "4.42.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b099bcc777a0243ce8b4d8c10ffe5a87b53ccecb Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 2 Jun 2021 14:32:46 -0400 Subject: [PATCH 1074/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4515885807c..368b49919cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.42.0", + "version": "4.43.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From befd64e68a1cba9fd1b0a4094eee5e75c4365d7e Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Wed, 2 Jun 2021 14:44:19 -0700 Subject: [PATCH 1075/1476] PubMatic: For Video: bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array (#6926) * bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array --- modules/pubmaticBidAdapter.js | 25 +++- test/spec/modules/pubmaticBidAdapter_spec.js | 132 +++++++++---------- 2 files changed, 84 insertions(+), 73 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 79356415ddf..b78b1fc96ba 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -929,7 +929,16 @@ function _assignRenderer(newBid, request) { newBid.renderer = BB_RENDERER.newRenderer(newBid.rendererCode, adUnitCode); } } -}; +} + +function isNonEmptyArray(test) { + if (utils.isArray(test) === true) { + if (test.length > 0) { + return true; + } + } + return false; +} export const spec = { code: BIDDER_CODE, @@ -949,15 +958,23 @@ export const spec = { } // video ad validation if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { - if (!bid.mediaTypes.video.mimes || (bid.params.video && (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0))) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); + // bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array + let mediaTypesVideoMimes = utils.deepAccess(bid.mediaTypes, 'video.mimes'); + let paramsVideoMimes = utils.deepAccess(bid, 'params.video.mimes'); + if (isNonEmptyArray(mediaTypesVideoMimes) === false && isNonEmptyArray(paramsVideoMimes) === false) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); return false; } + if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); return false; } - if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { + + if (bid.mediaTypes[VIDEO].context === 'outstream' && + !utils.isStr(bid.params.outstreamAU) && + !bid.hasOwnProperty('renderer') && + !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); return false; } diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index ae0b351c367..80ce93858d5 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -864,78 +864,72 @@ describe('PubMatic adapter', function () { expect(isValid).to.equal(false); }) - it('should check for mimes if video is present', function() { + it('bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array', function() { let bid = { - 'bidder': 'pubmatic', - 'params': { - 'adSlot': 'SLOT_NHB1@728x90', - 'publisherId': '5890' - }, - 'mediaTypes': { - 'video': { - 'playerSize': [ - [640, 480] - ], - 'protocols': [1, 2, 5], - 'context': 'instream', - 'mimes': ['video/flv'], - 'skippable': false, - 'skip': 1, - 'linearity': 2 - } - }, - 'adUnitCode': 'video1', - 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', - 'sizes': [ - [640, 480] - ], - 'bidId': '2c95df014cfe97', - 'bidderRequestId': '1fe59391566442', - 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + 'video': {} }, - isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(true); - }) - - it('should return false if mimes is not present in video', function() { - let bid = { - 'bidder': 'pubmatic', - 'params': { - 'adSlot': 'SLOT_NHB1@728x90', - 'publisherId': '5890' - }, - 'mediaTypes': { - 'video': { - 'playerSize': [ - [640, 480] - ], - 'protocols': [1, 2, 5], - 'context': 'instream', - 'skippable': false, - 'skip': 1, - 'linearity': 2 - } - }, - 'adUnitCode': 'video1', - 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', - 'sizes': [ - [640, 480] - ], - 'bidId': '2c95df014cfe97', - 'bidderRequestId': '1fe59391566442', - 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } }, - isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }) + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }; + + delete bid.params.video.mimes; // Undefined + bid.mediaTypes.video.mimes = 'string'; // NOT array + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.params.video.mimes; // Undefined + delete bid.mediaTypes.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.params.video.mimes; // Undefined + bid.mediaTypes.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + bid.params.video = {mimes: 'string'}; // NOT array + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + delete bid.params.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + bid.params.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // Undefined + bid.params.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // Undefined + delete bid.params.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); describe('Request formation', function () { From 25106daa67962e735e23b00f2efe565fadbef9bd Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 3 Jun 2021 01:16:24 +0300 Subject: [PATCH 1076/1476] GridNMBidAdapter: Use absent in video params data from mediaTypes (#6817) --- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 37 ++++++++-- test/spec/modules/gridBidAdapter_spec.js | 20 ++--- test/spec/modules/gridNMBidAdapter_spec.js | 85 +++++++++++++++++++++- 4 files changed, 125 insertions(+), 19 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index dbeba27d836..d4aceaea162 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -347,7 +347,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { height: serverBid.h, creativeId: serverBid.auid, // bid.bidId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, meta: { advertiserDomains: serverBid.adomain ? serverBid.adomain : [] diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index af1e9f84f43..4ab8464b115 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -24,6 +24,8 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; +const VIDEO_KEYS = ['mimes', 'protocols', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype']; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ VIDEO ], @@ -39,11 +41,12 @@ export const spec = { !bid.params.secid || !utils.isStr(bid.params.secid) || !bid.params.pubid || !utils.isStr(bid.params.pubid); + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const { protocols = video.protocols, mimes = video.mimes } = utils.deepAccess(bid, 'params.video') || {}; if (!invalid) { - invalid = !bid.params.video || !bid.params.video.protocols || !bid.params.video.mimes; + invalid = !protocols || !mimes; } if (!invalid) { - const {protocols, mimes} = bid.params.video; invalid = !utils.isArray(mimes) || !mimes.length || mimes.filter((it) => !(it && utils.isStr(it))).length; if (!invalid) { invalid = !utils.isArray(protocols) || !protocols.length || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; @@ -63,7 +66,7 @@ export const spec = { const requests = []; bids.forEach(bid => { - const {params, bidderRequestId, sizes} = bid; + const { params, bidderRequestId, sizes } = bid; const payload = { sizes: utils.parseSizesInput(sizes).join(','), r: bidderRequestId, @@ -91,11 +94,32 @@ export const spec = { } } + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const paramsVideo = Object.assign({}, params.video); + VIDEO_KEYS.forEach((key) => { + if (!(key in paramsVideo) && key in video) { + paramsVideo[key] = video[key]; + } + }); + + if (!paramsVideo.size && video.playerSize && video.playerSize.length === 2) { + paramsVideo.size = video.playerSize.join('x'); + } + + if (!('mind' in paramsVideo) && 'minduration' in video) { + paramsVideo.mind = video.minduration; + } + if (!('maxd' in paramsVideo) && 'maxduration' in video) { + paramsVideo.maxd = video.maxduration; + } + + const paramsToSend = Object.assign({}, params, {video: paramsVideo}); + requests.push({ method: 'POST', url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), bid: bid, - data: params // content + data: paramsToSend // content }); }); @@ -139,11 +163,14 @@ export const spec = { height: serverBid.h, creativeId: serverBid.auid || bid.bidderRequestId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, dealId: serverBid.dealid, vastXml: serverBid.adm, mediaType: VIDEO, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, adResponse: { content: serverBid.adm } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e161d82fdd6..4f5f62f2cb8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -555,7 +555,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -615,7 +615,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -631,7 +631,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -647,7 +647,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -709,7 +709,7 @@ describe('TheMediaGrid Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -728,7 +728,7 @@ describe('TheMediaGrid Adapter', function () { 'height': undefined, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -862,7 +862,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -878,7 +878,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -894,7 +894,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -910,7 +910,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 4
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 2aec9713000..6d3a607c0c5 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -132,6 +132,43 @@ describe('TheMediaGridNM Adapter', function () { expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); }); + + it('should return true when required params is absent, but available in mediaTypes', function () { + const paramsList = [ + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + } + } + ]; + + const mediaTypes = { + video: { + mimes: ['video/mp4', 'video/x-ms-wmv'], + playerSize: [200, 300], + protocols: [1, 2, 3, 4, 5, 6] + } + }; + + paramsList.forEach((params) => { + const validBid = Object.assign({}, bid); + delete validBid.params; + validBid.params = params; + validBid.mediaTypes = mediaTypes; + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); }); describe('buildRequests', function () { @@ -198,6 +235,39 @@ describe('TheMediaGridNM Adapter', function () { }); }); + it('should attach valid params from mediaTypes', function () { + const mediaTypes = { + video: { + skipafter: 10, + minduration: 10, + maxduration: 100, + protocols: [1, 3, 4], + playerSize: [300, 250] + } + }; + const bidRequest = Object.assign({ mediaTypes }, bidRequests[0]); + const req = spec.buildRequests([bidRequest], bidderRequest)[0]; + const expectedVideo = { + 'skipafter': 10, + 'mind': 10, + 'maxd': 100, + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6], + 'size': '300x250' + }; + const expectedParams = Object.assign({}, bidRequest.params); + expectedParams.video = Object.assign(expectedParams.video, expectedVideo); + + expect(req.url).to.be.an('string'); + const payload = parseRequestUrl(req.url); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(req.data).to.deep.equal(expectedParams); + }); + it('if gdprConsent is present payload must have gdpr params', function () { const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); expect(request.url).to.be.an('string'); @@ -235,7 +305,7 @@ describe('TheMediaGridNM Adapter', function () { describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '2'}, - {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300, adomain: ['my_domain.ru']}], 'seat': '2'}, {'bid': [{'price': 0, 'h': 250, 'w': 300}], 'seat': '2'}, {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, undefined, @@ -302,9 +372,12 @@ describe('TheMediaGridNM Adapter', function () { 'height': 250, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': [] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -318,9 +391,12 @@ describe('TheMediaGridNM Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': ['my_domain.ru'] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -352,6 +428,9 @@ describe('TheMediaGridNM Adapter', function () { 'bidId': '2bc598e42b6a', 'bidderRequestId': '39d74f5b71464', 'auctionId': '1cbd2feafe5e8b', + 'meta': { + 'advertiserDomains': [] + }, 'mediaTypes': { 'video': { 'context': 'instream' From 589bc3f8bc60295210c757cf3e60e1f9d83c5050 Mon Sep 17 00:00:00 2001 From: Rigel Cheng Date: Thu, 3 Jun 2021 06:19:08 +0800 Subject: [PATCH 1077/1476] BridgewellBidAdapter: modify to include user ids in the bid request object (#6845) --- modules/bridgewellBidAdapter.js | 17 +++++++--- .../spec/modules/bridgewellBidAdapter_spec.js | 33 ++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 58c4e907328..2034a59242e 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -4,8 +4,8 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'bridgewell'; -const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb=' + Math.random(); -const BIDDER_VERSION = '0.0.3'; +const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb='; +const BIDDER_VERSION = '1.1.0'; export const spec = { code: BIDDER_CODE, @@ -37,7 +37,12 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const adUnits = []; + var bidderUrl = REQUEST_ENDPOINT + Math.random(); + var userIds; + utils._each(validBidRequests, function (bid) { + userIds = bid.userId; + if (bid.params.cid) { adUnits.push({ cid: bid.params.cid, @@ -47,7 +52,8 @@ export const spec = { banner: { sizes: bid.sizes } - } + }, + userIds: userIds || {} }); } else { adUnits.push({ @@ -58,7 +64,8 @@ export const spec = { banner: { sizes: bid.sizes } - } + }, + userIds: userIds || {} }); } }); @@ -70,7 +77,7 @@ export const spec = { return { method: 'POST', - url: REQUEST_ENDPOINT, + url: bidderUrl, data: { version: { prebid: '$prebid.version$', diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 0860404355e..8da82c71820 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -2,6 +2,14 @@ import { expect } from 'chai'; import { spec } from 'modules/bridgewellBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +const userId = { + 'criteoId': 'vYlICF9oREZlTHBGRVdrJTJCUUJnc3U2ckNVaXhrV1JWVUZVSUxzZmJlcnJZR0ZxbVhFRnU5bDAlMkJaUWwxWTlNcmdEeHFrJTJGajBWVlV4T3lFQ0FyRVcxNyUyQlIxa0lLSlFhcWJpTm9PSkdPVkx0JTJCbzlQRTQlM0Q', + 'pubcid': '074864cb-3705-430e-9ff7-48ccf3c21b94', + 'sharedid': {'id': '01F61MX53D786DSB2WYD38ZVM7', 'third': '01F61MX53D786DSB2WYD38ZVM7'}, + 'uid2': {'id': 'eb33b0cb-8d35-1234-b9c0-1a31d4064777'}, + 'flocId': {'id': '12345', 'version': 'chrome.1.1'}, +} + describe('bridgewellBidAdapter', function () { const adapter = newBidder(spec); @@ -87,6 +95,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId, }, { 'bidder': 'bridgewell', @@ -126,6 +135,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId, } ]; @@ -142,10 +152,13 @@ describe('bridgewellBidAdapter', function () { expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { - expect(payload.adUnits[i]).to.have.property('ChannelID').that.is.a('string'); - expect(payload.adUnits[i]).to.not.have.property('cid'); - expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); - expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); + let u = payload.adUnits[i]; + expect(u).to.have.property('ChannelID').that.is.a('string'); + expect(u).to.not.have.property('cid'); + expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); + expect(u).to.have.property('userIds'); + expect(u.userIds).to.deep.equal(userId); } }); @@ -170,6 +183,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId }, ]; @@ -180,10 +194,13 @@ describe('bridgewellBidAdapter', function () { expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { - expect(payload.adUnits[i]).to.have.property('cid').that.is.a('number'); - expect(payload.adUnits[i]).to.not.have.property('ChannelID'); - expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); - expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); + let u = payload.adUnits[i]; + expect(u).to.have.property('cid').that.is.a('number'); + expect(u).to.not.have.property('ChannelID'); + expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); + expect(u).to.have.property('userIds'); + expect(u.userIds).to.deep.equal(userId); } }); From 65975d3afa0f89c1f9fd7ccc2e77fc3251c2a333 Mon Sep 17 00:00:00 2001 From: Klaas-Jan Boon Date: Thu, 3 Jun 2021 12:19:16 +0200 Subject: [PATCH 1078/1476] Blue Billywig Adapter: add advertiserDomains support (#6927) * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Update bluebillywigBidAdapter test parameters to match renderer to rendererCode rename * Blue Billywig - Also pass through site config with OpenRTB request * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Code quality update, always hit user syncs, improved video params * Add support for meta.advertiserDomains to BB Adapter, addresses issue #6466 * kick off circleci tests Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie --- modules/bluebillywigBidAdapter.js | 5 +++-- package-lock.json | 2 +- test/spec/modules/bluebillywigBidAdapter_spec.js | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js index cac993b0f8c..3d4eeb058b3 100644 --- a/modules/bluebillywigBidAdapter.js +++ b/modules/bluebillywigBidAdapter.js @@ -8,7 +8,7 @@ import { createEidsArray } from './userId/eids.js'; const DEV_MODE = window.location.search.match(/bbpbs_debug=true/); -// Blue Billywig Constants +// Blue Billywig Constants const BB_CONSTANTS = { BIDDER_CODE: 'bluebillywig', AUCTION_URL: '$$URL_STARTpbs.bluebillywig.com/openrtb2/auction?pub=$$PUBLICATION', @@ -122,7 +122,8 @@ const BB_HELPERS = { if (!bidObject.vastUrl && bid.nurl && !bid.adm) { // ad markup is on win notice url, and adm is ommited according to OpenRTB 2.5 bidObject.vastUrl = bid.nurl; } - + bidObject.meta = bid.meta || {}; + if (bid.adomain) { bidObject.meta.advertiserDomains = bid.adomain; } return bidObject; }, }; diff --git a/package-lock.json b/package-lock.json index a6a58a530de..d59e11b038e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.39.0-pre", + "version": "4.43.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js index 3aee6c69438..c831ddf6ddf 100644 --- a/test/spec/modules/bluebillywigBidAdapter_spec.js +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -760,6 +760,10 @@ describe('BlueBillywigAdapter', () => { expect(bid.currency).to.equal(serverResponse.body.cur); expect(bid.ttl).to.equal(BB_CONSTANTS.DEFAULT_TTL); + expect(bid).to.have.property('meta'); + expect(bid.meta).to.have.property('advertiserDomains'); + expect(bid.meta.advertiserDomains[0]).to.equal('bluebillywig.com'); + expect(bid.publicationName).to.equal(validBidderRequest.bids[0].params.publicationName); expect(bid.rendererCode).to.equal(validBidderRequest.bids[0].params.rendererCode); expect(bid.accountId).to.equal(validBidderRequest.bids[0].params.accountId); From 25a495cec4847c824376619efa9ef556c229620c Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Thu, 3 Jun 2021 13:32:35 +0300 Subject: [PATCH 1079/1476] oneVideo Bid Adapter: adomain & mediaTypes.video Support (SAPR-16874) (#6925) --- modules/oneVideoBidAdapter.js | 113 +++++++++---- modules/oneVideoBidAdapter.md | 162 ++++++++++++------- test/spec/modules/oneVideoBidAdapter_spec.js | 106 +++++++++--- 3 files changed, 262 insertions(+), 119 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index e57c9d12ff7..dfedbd156a9 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.1.0', + VERSION: '3.1.1', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -17,26 +17,52 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { + // Bidder code validation if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { return false; } - // Video validations - if (typeof bid.params.video === 'undefined' || typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight == 'undefined' || typeof bid.params.video.mimes == 'undefined') { - return false; + // E2E test skip validations + if (bid.params && bid.params.video && bid.params.video.e2etest) { + return true; } + // MediaTypes Video / Banner validation + if (typeof bid.mediaTypes.video === 'undefined' && typeof bid.mediaTypes.banner === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.video OR mediaTypes.banner not declared'); + return false; + }; - // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support if (bid.mediaTypes.video) { - if (bid.mediaTypes.video.context === 'outstream' && bid.params.video.display === 1) { + // Player size validation + if (typeof bid.mediaTypes.video.playerSize === 'undefined') { + if (bid.params.video && (typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight === 'undefined')) { + utils.logError('Failed validation: Player size not declared in either mediaTypes.playerSize OR bid.params.video.plauerWidth & bid.params.video.playerHeight.'); + return false; + }; + }; + // Mimes validation + if (typeof bid.mediaTypes.video.mimes === 'undefined') { + if (!bid.params.video || typeof bid.params.video.mimes === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.mimes OR params.video.mimes not declared'); + return false; + }; + }; + // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support + if (bid.mediaTypes.video.context === 'outstream' && bid.params.video && bid.params.video.display === 1) { + utils.logError('Failed validation: Dynamic Ad Placement cannot be used with context Outstream (params.video.display=1)'); return false; - } - } else if (bid.mediaTypes.banner && !bid.params.video.display) { + }; + }; + + // DAP Validation + if (bid.mediaTypes.banner && bid.params.video && !bid.params.video.display) { + utils.logError('Failed validation: If you are trying to use Dynamic Ad Placement you must pass params.video.display=1'); return false; - } + }; - // Pub Id validation + // Publisher Id (Exchange) validation if (typeof bid.params.pubId === 'undefined') { + utils.logError('Failed validation: Adapter cannot send requests without bid.params.pubId'); return false; } @@ -49,7 +75,7 @@ export const spec = { * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(bids, bidRequest) { + buildRequests: function (bids, bidRequest) { let consentData = bidRequest ? bidRequest.gdprConsent : null; return bids.map(bid => { @@ -100,7 +126,10 @@ export const spec = { currency: response.cur, ttl: (bidRequest.params.video.ttl > 0 && bidRequest.params.video.ttl <= 3600) ? bidRequest.params.video.ttl : 300, netRevenue: true, - adUnitCode: bidRequest.adUnitCode + adUnitCode: bidRequest.adUnitCode, + meta: { + advertiserDomains: bid.adomain + } }; bidResponse.mediaType = (bidRequest.mediaTypes.banner) ? 'banner' : 'video' @@ -194,40 +223,54 @@ function getRequestData(bid, consentData, bidRequest) { if (bid.params.video.display == undefined || bid.params.video.display != 1) { bidData.imp[0].video = { - mimes: bid.params.video.mimes, - w: bid.params.video.playerWidth, - h: bid.params.video.playerHeight, - pos: bid.params.video.position, + linearity: 1 + }; + if (bid.params.video.playerWidth && bid.params.video.playerHeight) { + bidData.imp[0].video.w = bid.params.video.playerWidth; + bidData.imp[0].video.h = bid.params.video.playerHeight; + } else { + const playerSize = getSize(bid.mediaTypes.video.playerSize); + bidData.imp[0].video.w = playerSize.width; + bidData.imp[0].video.h = playerSize.height; + }; + if (bid.params.video.mimes) { + bidData.imp[0].video.mimes = bid.params.video.mimes; + } else { + bidData.imp[0].video.mimes = bid.mediaTypes.video.mimes; }; - if (bid.params.video.maxbitrate) { - bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate + if (bid.mediaTypes.video.maxbitrate || bid.params.video.maxbitrate) { + bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate || bid.mediaTypes.video.maxbitrate; } - if (bid.params.video.maxduration) { - bidData.imp[0].video.maxduration = bid.params.video.maxduration + if (bid.mediaTypes.video.maxduration || bid.params.video.maxduration) { + bidData.imp[0].video.maxduration = bid.params.video.maxduration || bid.mediaTypes.video.maxduration; } - if (bid.params.video.minduration) { - bidData.imp[0].video.minduration = bid.params.video.minduration + if (bid.mediaTypes.video.minduration || bid.params.video.minduration) { + bidData.imp[0].video.minduration = bid.params.video.minduration || bid.mediaTypes.video.minduration; } - if (bid.params.video.api) { - bidData.imp[0].video.api = bid.params.video.api + if (bid.mediaTypes.video.api || bid.params.video.api) { + bidData.imp[0].video.api = bid.params.video.api || bid.mediaTypes.video.api; } - if (bid.params.video.delivery) { - bidData.imp[0].video.delivery = bid.params.video.delivery + if (bid.mediaTypes.video.delivery || bid.params.video.delivery) { + bidData.imp[0].video.delivery = bid.params.video.delivery || bid.mediaTypes.video.delivery; } - if (bid.params.video.position) { - bidData.imp[0].video.pos = bid.params.video.position + if (bid.mediaTypes.video.position || bid.params.video.position) { + bidData.imp[0].video.pos = bid.params.video.position || bid.mediaTypes.video.position; } - if (bid.params.video.playbackmethod) { - bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod + if (bid.mediaTypes.video.playbackmethod || bid.params.video.playbackmethod) { + bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod || bid.mediaTypes.video.playbackmethod; } - if (bid.params.video.placement) { - bidData.imp[0].video.placement = bid.params.video.placement + if (bid.mediaTypes.video.placement || bid.params.video.placement) { + bidData.imp[0].video.placement = bid.params.video.placement || bid.mediaTypes.video.placement; } if (bid.params.video.rewarded) { bidData.imp[0].ext.rewarded = bid.params.video.rewarded } - bidData.imp[0].video.linearity = 1; - bidData.imp[0].video.protocols = bid.params.video.protocols || [2, 5]; + if (bid.mediaTypes.video.linearity || bid.params.video.linearity) { + bidData.imp[0].video.linearity = bid.params.video.linearity || bid.mediaTypes.video.linearity || 1; + } + if (bid.mediaTypes.video.protocols || bid.params.video.protocols) { + bidData.imp[0].video.protocols = bid.params.video.protocols || bid.mediaTypes.video.protocols || [2, 5]; + } } else if (bid.params.video.display == 1) { getFloorRequestObject.mediaType = 'banner'; bidData.imp[0].banner = { @@ -307,7 +350,7 @@ function getRequestData(bid, consentData, bidRequest) { } } if (bid.params.video.e2etest) { - utils.logMessage('+++ oneVideoBidAdapter: E2E test mode enabled. \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); + utils.logMessage('E2E test mode enabled: \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); bidData.imp[0].bidfloor = null; bidData.imp[0].video.w = 300; bidData.imp[0].video.h = 250; diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index a42433e80b5..c1762ac0cd3 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -3,12 +3,18 @@ **Module Name**: One Video Bidder Adapter **Module Type**: Bidder Adapter **Maintainer**: deepthi.neeladri.sravana@verizonmedia.com + adam.browning@verizonmedia.com # Description Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to fetch bids. - +# Prebid.js V5.0 Support +The oneVideo adapter now reads `mediaTypes.video` for mandatory parameters such as `playerSize` & `mimes`. +Note: You can use the `bid.params.video` object to specify explicit overrides for whatever is declared in `mediaTypes.video`. +Important: You must pass `bid.params.video = {}` as bare minimum for the adapter to work. # Integration Examples: -## Instream Video adUnit example & parameters + +## Instream Video adUnit using mediaTypes.video +*Note:* By default, the adapter will read the mandatory parameters from mediaTypes.video. *Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ @@ -17,7 +23,18 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], } }, bids: [ @@ -25,21 +42,10 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1,5], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, - minduration: 10, - maxduration: 30, ttl: 300, custom: { key1: "value1", @@ -59,16 +65,16 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to } ] ``` -## Outstream Video adUnit example & parameters -*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +## Instream Video adUnit using params.video overrides +*Note:* If the mandatory parameters are not specified in mediaTypes.video the adapter will read check to see if overrides are set in params.video. Decalring values using params.video will always override the settings in mediaTypes.video. +*Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ { code: 'video1', mediaTypes: { video: { - context: 'outstream', - playerSize: [480, 640] + context: 'instream', } }, bids: [ @@ -76,21 +82,74 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, + playerWidth: 640, + playerHeight: 480, mimes: ['video/mp4', 'application/javascript'], protocols: [2,5], api: [2], position: 1, delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, playbackmethod: [1,5], + protocols: [2,5], + api: [2], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, + ttl: 300, + custom: { + key1: "value1", + key2: 123345 + } + }, + bidfloor: 0.5, + site: { + id: 1, + page: 'https://verizonmedia.com', + referrer: 'https://verizonmedia.com' + }, + pubId: 'HBExchange' + } + } + ] + } + ] +``` +## Outstream Video adUnit example & parameters +*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +``` + var adUnits = [ + { + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], minduration: 10, maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], + + } + }, + bids: [ + { + bidder: 'oneVideo', + params: { + video: { + sid: YOUR_VSSP_ORG_ID, + hp: 1, + rewarded: 1, ttl: 250 }, bidfloor: 0.5, @@ -116,7 +175,8 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: "instream", - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -124,11 +184,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - position: 1, - display: 1 + ttl: 250 }, bidfloor: 0.5, site: { @@ -190,6 +246,7 @@ var adUnits = [ video: { context: "instream", playerSize: [480, 640] + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -197,12 +254,8 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 300, - playerHeight: 250, - mimes: ['video/mp4', 'application/javascript'], e2etest: true - }, - pubId: 'YOUR_PUBLISHER_ID' + } } } ] @@ -226,7 +279,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -234,12 +290,7 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - sid: + sid: 123456 }, bidfloor: 0.5, site: { @@ -279,7 +330,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -287,18 +341,14 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], + ttl: 250 }, bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', referrer: 'https://verizonmedia.com' - }, + }, pubId: 'HBExchange' } } @@ -315,18 +365,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300, content: { id: "1234", @@ -373,18 +421,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300 }, bidfloor: 0.5, diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 3f5304dce0a..5289203fd5b 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -59,11 +59,77 @@ describe('OneVideoBidAdapter', function () { }); describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed', function () { + it('should return false when mediaTypes video OR banner not declared', function () { + bidRequest.mediaTypes = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return true (skip validations) when e2etest = true', function () { + bidRequest.params.video = { + e2etest: true + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mediaTypes.video has all mandatory params', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + } + bidRequest.params.video = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when params.video has all override params instead of mediaTypes.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream' + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'] + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when playerWidth & playerHeight are passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + mimes: ['video/mp4', 'application/javascript'] + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mimes is passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSizes: [640, 480] + }; + bidRequest.video = { + mimes: ['video/mp4', 'application/javascript'] + }; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "video" param is missing', function () { + it('should return false when both mediaTypes.video and params.video Objects are missing', function () { + bidRequest.mediaTypes = {}; + bidRequest.params = { + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when both mediaTypes.video and params.video are missing mimes and player size', function () { + bidRequest.mediaTypes = { + video: { + context: 'instream' + } + }; bidRequest.params = { pubId: 'brxd' }; @@ -76,34 +142,16 @@ describe('OneVideoBidAdapter', function () { playerWidth: 480, playerHeight: 640, mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 } }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); + it('should return true when the "pubId" param exists', function () { - bidRequest.params = { + bidRequest.mediaTypes = { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 + playerSizes: [640, 480], + mimes: ['video/mp4', 'application/javascript'] }, pubId: 'brxd' }; @@ -216,7 +264,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.1.0'; + const VERSION = '3.1.1'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -640,7 +688,10 @@ describe('OneVideoBidAdapter', function () { adid: 123, crid: 2, price: 6.01, - adm: '' + adm: '', + adomain: [ + 'verizonmedia.com' + ], }] }], cur: 'USD' @@ -664,6 +715,9 @@ describe('OneVideoBidAdapter', function () { netRevenue: true, adUnitCode: bidRequest.adUnitCode, renderer: (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined, + meta: { + advertiserDomains: ['verizonmedia.com'] + } }; expect(bidResponse).to.deep.equal(o); }); From 70317f9a34493d07b7a4e889b1539cdd84230ed7 Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Thu, 3 Jun 2021 16:45:29 +0200 Subject: [PATCH 1080/1476] adnuntias Bid Adapter: Added GDPR support and segment passing (#6796) * Master merge issues * Adnuntius Bid Adapter: Added tests for gdpr and segments * Moved segments to read from ortb2 instead of a custom value. * Changed bidder to read segments from ortb2. * fixing lgtm alert --- modules/adnuntiusBidAdapter.js | 39 +++++++++- test/spec/modules/adnuntiusBidAdapter_spec.js | 75 +++++++++++++++++++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 8752e37a96f..5e10219188d 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -1,30 +1,61 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://delivery.adnuntius.com/i?tzo='; +const GVLID = 855; + +const checkSegment = function (segment) { + if (utils.isStr(segment)) return segment; + if (segment.id) return segment.id +} + +const getSegmentsFromOrtb = function (ortb2) { + const userData = utils.deepAccess(ortb2, 'user.data'); + let segments = []; + if (userData) { + userData.forEach(userdat => { + if (userdat.segment) { + segments.push(...userdat.segment.filter(checkSegment).map(checkSegment)); + } + }); + } + return segments +} export const spec = { code: BIDDER_CODE, - + gvlid: GVLID, + supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return !!(bid.bidId || (bid.params.member && bid.params.invCode)); }, - buildRequests: function (validBidRequests) { + buildRequests: function (validBidRequests, bidderRequest) { const networks = {}; const bidRequests = {}; const requests = []; + const ortb2 = config.getConfig('ortb2'); + const segments = getSegmentsFromOrtb(ortb2); const tzo = new Date().getTimezoneOffset(); + const gdprApplies = utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); + const consentString = utils.deepAccess(bidderRequest, 'gdprConsent.consentString'); + const reqConsent = (gdprApplies !== undefined) ? '&consentString=' + consentString : ''; + const reqSegments = (segments.length > 0) ? '&segments=' + segments.join(',') : ''; for (var i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] const network = bid.params.network || 'network'; + const targeting = bid.params.targeting || {}; + bidRequests[network] = bidRequests[network] || []; bidRequests[network].push(bid); networks[network] = networks[network] || {}; networks[network].adUnits = networks[network].adUnits || []; - networks[network].adUnits.push({ ...bid.params.targeting, auId: bid.params.auId, targetId: bid.bidId }); + networks[network].adUnits.push({ ...targeting, auId: bid.params.auId, targetId: bid.bidId }); } const networkKeys = Object.keys(networks) @@ -32,7 +63,7 @@ export const spec = { const network = networkKeys[j]; requests.push({ method: 'POST', - url: ENDPOINT_URL + tzo + '&format=json', + url: ENDPOINT_URL + tzo + '&format=json' + reqSegments + reqConsent, data: JSON.stringify(networks[network]), bid: bidRequests[network] }); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 62073fc6aaa..9c9bf0e9914 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -2,10 +2,17 @@ import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' import { spec } from 'modules/adnuntiusBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; describe('adnuntiusBidAdapter', function () { + afterEach(function () { + config.resetConfig(); + }); const tzo = new Date().getTimezoneOffset(); const ENDPOINT_URL = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; + // const ENDPOINT_URL_SEGMENTS_ = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; + const ENDPOINT_URL_SEGMENTS = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&segments=segment1,segment2,segment3`; + const ENDPOINT_URL_CONSENT = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&consentString=consentString`; const adapter = newBidder(spec); const bidRequests = [ @@ -116,6 +123,74 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('data'); expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}]}'); }); + + it('should pass segments if available in config', function () { + config.setBidderConfig({ + bidders: ['adnuntius', 'other'], + config: { + ortb2: { + user: { + data: [{ + name: 'adnuntius', + segment: [{ id: 'segment1' }, { id: 'segment2' }] + }, + { + name: 'other', + segment: ['segment3'] + }], + } + } + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); + }); + + it('should skip segments in config if not either id or array of strings', function () { + config.setBidderConfig({ + bidders: ['adnuntius', 'other'], + config: { + ortb2: { + user: { + data: [{ + name: 'adnuntius', + segment: [{ id: 'segment1' }, { id: 'segment2' }, { id: 'segment3' }] + }, + { + name: 'other', + segment: [{ + notright: 'segment4' + }] + }], + } + } + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); + }); + }); + + describe('user privacy', function () { + it('should send GDPR Consent data if gdprApplies', function () { + let request = spec.buildRequests(bidRequests, { 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' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + }); }); describe('interpretResponse', function () { From 3529b59174ebf06db1c0701869003e30fdbb98ef Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 3 Jun 2021 11:07:25 -0400 Subject: [PATCH 1081/1476] Prebid 4.42.1 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 368b49919cd..ea75c79a0b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.43.0-pre", + "version": "4.42.1", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 707aeac1446bc2b8c46e37fce333ea7a0e750d12 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 3 Jun 2021 11:23:06 -0400 Subject: [PATCH 1082/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ea75c79a0b5..368b49919cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.42.1", + "version": "4.43.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 18a3e07dbe7068bd1fab1161318a52573f9440d3 Mon Sep 17 00:00:00 2001 From: Sleiman Jneidi Date: Thu, 3 Jun 2021 16:55:25 +0100 Subject: [PATCH 1083/1476] Quantcast Bid Adapter: add advertiserDomains to meta (#6929) * qc adds advertisers domain to meta * fix type in spec --- modules/quantcastBidAdapter.js | 7 ++++++- test/spec/modules/quantcastBidAdapter_spec.js | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index e9541edb534..408f05d33cb 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -248,7 +248,7 @@ export const spec = { } const bidResponsesList = response.bids.map(bid => { - const { ad, cpm, width, height, creativeId, currency, videoUrl, dealId } = bid; + const { ad, cpm, width, height, creativeId, currency, videoUrl, dealId, meta } = bid; const result = { requestId: response.requestId, @@ -270,6 +270,11 @@ export const spec = { if (dealId !== undefined && dealId) { result['dealId'] = dealId; } + result.meta = {}; + + if (meta !== undefined && meta.advertiserDomains && utils.isArray(meta.advertiserDomains)) { + result.meta.advertiserDomains = meta.advertiserDomains; + } return result; }); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 5b4e7963e60..584f4dcbaf4 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -658,7 +658,10 @@ describe('Quantcast adapter', function () { '
Quantcast
', creativeId: 1001, width: 300, - height: 250 + height: 250, + meta: { + advertiserDomains: ['dailymail.com'] + } } ] }; @@ -718,7 +721,10 @@ describe('Quantcast adapter', function () { ttl: QUANTCAST_TTL, creativeId: 1001, netRevenue: QUANTCAST_NET_REVENUE, - currency: 'USD' + currency: 'USD', + meta: { + advertiserDomains: ['dailymail.com'] + } }; const interpretedResponse = qcSpec.interpretResponse(response); @@ -738,7 +744,10 @@ describe('Quantcast adapter', function () { creativeId: 1001, netRevenue: QUANTCAST_NET_REVENUE, currency: 'USD', - dealId: 'test-dealid' + dealId: 'test-dealid', + meta: { + advertiserDomains: ['dailymail.com'] + } }; const interpretedResponse = qcSpec.interpretResponse(response); @@ -757,7 +766,8 @@ describe('Quantcast adapter', function () { creativeId: undefined, ad: undefined, netRevenue: QUANTCAST_NET_REVENUE, - currency: 'USD' + currency: 'USD', + meta: {} }; const interpretedResponse = qcSpec.interpretResponse(videoResponse); From 08c2994ef664ecc5c4a9d8a6bdcf7706d830ae90 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Fri, 4 Jun 2021 03:05:03 +0600 Subject: [PATCH 1084/1476] Bugfix: fix overriding user object (#6934) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 9 ++++----- test/spec/modules/zetaSspBidAdapter_spec.js | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index c956df55a1f..530a2efce1e 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -76,13 +76,12 @@ export const spec = { gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0 } }; - } - if (request.gdprConsent && request.gdprConsent.gdprApplies) { - payload.user = { - ext: { + if (request.gdprConsent.gdprApplies && request.gdprConsent.consentString) { + payload.user.ext = { + ...payload.user.ext, consent: request.gdprConsent.consentString } - }; + } } provideEids(request, payload); return { diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 4602e2d2b77..bace0e86d9a 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -35,14 +35,18 @@ describe('Zeta Ssp Bid Adapter', function() { refererInfo: { referer: 'zetaglobal.com' }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, params: { - placement: 12345, + placement: 111, user: { - uid: 12345, - buyeruid: 12345 + uid: 222, + buyeruid: 333 }, tags: { - someTag: 123, + someTag: 444, sid: 'publisherId' }, test: 1 @@ -136,4 +140,12 @@ describe('Zeta Ssp Bid Adapter', function() { expect(sync4.url).to.include('&gdpr='); expect(sync4.url).to.include('&us_privacy='); }); + + it('Test do not override user object', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.user.uid).to.eql(222); + expect(payload.user.buyeruid).to.eql(333); + expect(payload.user.ext.consent).to.eql('consentString'); + }); }); From 896addd975181a655286d99314aea96a7b43149d Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Fri, 4 Jun 2021 10:40:07 +0200 Subject: [PATCH 1085/1476] VIS.X adapter: fix for multiformat ad units & added fields to bidResponse.meta (#6936) * VIS.X: fallback to banner if video configuration isn't supported * VIS.X: unused prop * VIS.X: pass advertiserDomains & mediaType to bidResponse.meta --- modules/visxBidAdapter.js | 63 +++++++++--- test/spec/modules/visxBidAdapter_spec.js | 117 ++++++++++++++++++++--- 2 files changed, 155 insertions(+), 25 deletions(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 12004cc35af..f2076dfe180 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -33,8 +33,11 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { if (_isVideoBid(bid)) { - if (!_isValidVideoBid(bid)) { - return false; + if (!_isValidVideoBid(bid, true)) { + // in case if video bid configuration invalid will try to send bid request for banner + if (!_isBannerBid(bid)) { + return false; + } } } return !!bid.params.uid; @@ -243,6 +246,10 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithout netRevenue: true, ttl: TIME_TO_LIVE, dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.advertiserDomains ? serverBid.advertiserDomains : [], + mediaType: serverBid.mediaType + }, }; if (serverBid.ext && serverBid.ext.prebid) { @@ -289,23 +296,35 @@ function _isVideoBid(bid) { return bid.mediaType === VIDEO || deepAccess(bid, 'mediaTypes.video'); } -function _isValidVideoBid(bid) { +function _isBannerBid(bid) { + return bid.mediaType === BANNER || deepAccess(bid, 'mediaTypes.banner'); +} + +function _isValidVideoBid(bid, logErrors = false) { let result = true; const videoMediaType = deepAccess(bid, 'mediaTypes.video'); if (videoMediaType.context !== VIDEO_INSTREAM) { - logError(LOG_ERROR_MESS.onlyVideoInstream) + if (logErrors) { + logError(LOG_ERROR_MESS.onlyVideoInstream); + } result = false; } if (!(videoMediaType.playerSize && parseSizesInput(deepAccess(videoMediaType, 'playerSize', [])))) { - logError(LOG_ERROR_MESS.videoMissing + 'playerSize'); + if (logErrors) { + logError(LOG_ERROR_MESS.videoMissing + 'playerSize'); + } result = false; } if (!videoMediaType.mimes) { - logError(LOG_ERROR_MESS.videoMissing + 'mimes'); + if (logErrors) { + logError(LOG_ERROR_MESS.videoMissing + 'mimes'); + } result = false; } if (!videoMediaType.protocols) { - logError(LOG_ERROR_MESS.videoMissing + 'protocols'); + if (logErrors) { + logError(LOG_ERROR_MESS.videoMissing + 'protocols'); + } result = false; } return result; @@ -323,13 +342,29 @@ function _initVideoTypes(bids) { if (bids && bids.length) { bids.forEach(function (bid) { const mediaTypes = deepAccess(bid, 'mediaTypes.video', {}); - _playerSize.push(parseSizesInput(deepAccess(mediaTypes, 'playerSize', [])).join('|')); - _protocols.push(deepAccess(mediaTypes, 'protocols', []).join('|')); - _api.push(deepAccess(mediaTypes, 'api', []).join('|')); - _mimes.push(deepAccess(mediaTypes, 'mimes', []).join('|')); - _minduration.push(deepAccess(mediaTypes, 'minduration', null)); - _maxduration.push(deepAccess(mediaTypes, 'maxduration', null)); - _skip.push(deepAccess(mediaTypes, 'skip', null)); + let bidPlayerSize = ''; + let bidProtocols = ''; + let bidApi = ''; + let bidMimes = ''; + let bidMinduration = null; + let bidMaxduration = null; + let bidSkip = null; + if (_isVideoBid(bid) && _isValidVideoBid(bid)) { + bidPlayerSize = parseSizesInput(deepAccess(mediaTypes, 'playerSize', [])).join('|'); + bidProtocols = deepAccess(mediaTypes, 'protocols', []).join('|'); + bidApi = deepAccess(mediaTypes, 'api', []).join('|'); + bidMimes = deepAccess(mediaTypes, 'mimes', []).join('|'); + bidMinduration = deepAccess(mediaTypes, 'minduration', null); + bidMaxduration = deepAccess(mediaTypes, 'maxduration', null); + bidSkip = deepAccess(mediaTypes, 'skip', null); + } + _playerSize.push(bidPlayerSize); + _protocols.push(bidProtocols); + _api.push(bidApi); + _mimes.push(bidMimes); + _minduration.push(bidMinduration); + _maxduration.push(bidMaxduration); + _skip.push(bidSkip); }); } _playerSize = _playerSize.join(','); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index fa902480cd7..62049e212f7 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -302,11 +302,50 @@ describe('VisxAdapter', function () { }); }); + describe('buildRequests (multiple media types w/ unsupported video+outstream)', function () { + const bidderRequest = { + refererInfo: { + referer: 'https://example.com' + } + }; + const referrer = bidderRequest.refererInfo.referer; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903538' + }, + 'adUnitCode': 'misconfigured-video', + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'playerSize': [400, 300] + } + }, + 'bidId': '39aff3a7169a6a', + 'bidderRequestId': '22edffe2733bf6', + 'auctionId': '1d1a030790a476', + } + ]; + + it('should send requst for banner bid', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '903538'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(payload).to.not.have.property('playerSize'); + }); + }); + describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, undefined, @@ -341,6 +380,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['some_domain.com'], + 'mediaType': 'banner', + }, } ]; @@ -397,6 +440,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['some_domain.com'], + 'mediaType': 'banner', + }, }, { 'requestId': '4dff80cc4ee346', @@ -409,6 +456,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, }, { 'requestId': '5703af74d0472a', @@ -421,6 +472,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, } ]; @@ -456,6 +511,10 @@ describe('VisxAdapter', function () { 'currency': 'PLN', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['some_domain.com'], + 'mediaType': 'banner', + }, } ]; @@ -509,11 +568,11 @@ describe('VisxAdapter', function () { it('complicated case', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -585,6 +644,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['some_domain.com'], + 'mediaType': 'banner', + }, }, { 'requestId': '4e111f1b66e4', @@ -597,6 +660,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, }, { 'requestId': '26d6f897b516', @@ -609,6 +676,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, }, { 'requestId': '326bde7fbf69', @@ -621,6 +692,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, }, { 'requestId': '1751cd90161', @@ -633,6 +708,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, } ]; @@ -642,8 +721,8 @@ describe('VisxAdapter', function () { it('dublicate uids and sizes in one slot', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -693,6 +772,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, }, { 'requestId': '57b2ebe70e16', @@ -705,6 +788,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'banner', + }, } ]; @@ -714,7 +801,7 @@ describe('VisxAdapter', function () { it('handles video bid', function () { const fullResponse = [ - {'bid': [{'price': 0.5, 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR', 'mediaType': 'video'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -751,6 +838,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [], + 'mediaType': 'video', + }, } ]; const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); @@ -786,6 +877,10 @@ describe('VisxAdapter', function () { 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['some_domain.com'], + 'mediaType': 'banner', + }, 'ext': { 'events': { 'pending': pendingUrl, From 6e5702cb5d1ec50923ddfb779e6e15166223ee10 Mon Sep 17 00:00:00 2001 From: Daniel Liebner Date: Fri, 4 Jun 2021 05:11:30 -0400 Subject: [PATCH 1086/1476] Bid Glass adapter: Add support for meta.advertiserDomains (#6938) * Added bidglass adapter + test * PR Review Updates: - Added formal params to getUserSyncs function definition - getUserSyncs now always returns an array - Improved unit test coverage * PR Review Updates: - Removed unused methods: getUserSyncs, onTimeout, onBidWon, onSetTargeting - Removed getUserSyncs unit test - Removed "dead code" - Removed some unnecessary comments - Fixed usage of parseInt * Bid Glass Bid Adapter: pass options in bid request * Merge externally set targeting params * Updates to address gulp errors * Get `bidglass` reference from window * Add support for meta.advertiserDomains --- modules/bidglassBidAdapter.js | 37 +++++++++++++++-------- test/spec/modules/bidglassAdapter_spec.js | 10 ++++-- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 44f5cdf4384..b77ca474e13 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -125,20 +125,31 @@ export const spec = { interpretResponse: function(serverResponse) { const bidResponses = []; - utils._each(serverResponse.body.bidResponses, function(bid) { - bidResponses.push({ - requestId: bid.requestId, - cpm: parseFloat(bid.cpm), - width: parseInt(bid.width, 10), - height: parseInt(bid.height, 10), - creativeId: bid.creativeId, - dealId: bid.dealId || null, - currency: bid.currency || 'USD', - mediaType: bid.mediaType || 'banner', + utils._each(serverResponse.body.bidResponses, function(serverBid) { + const bidResponse = { + requestId: serverBid.requestId, + cpm: parseFloat(serverBid.cpm), + width: parseInt(serverBid.width, 10), + height: parseInt(serverBid.height, 10), + creativeId: serverBid.creativeId, + dealId: serverBid.dealId || null, + currency: serverBid.currency || 'USD', + mediaType: serverBid.mediaType || 'banner', netRevenue: true, - ttl: bid.ttl || 10, - ad: bid.ad - }); + ttl: serverBid.ttl || 10, + ad: serverBid.ad, + meta: {} + }; + + if (serverBid.meta) { + let meta = serverBid.meta; + + if (meta.advertiserDomains && meta.advertiserDomains.length) { + bidResponse.meta.advertiserDomains = meta.advertiserDomains; + } + } + + bidResponses.push(bidResponse); }); return bidResponses; diff --git a/test/spec/modules/bidglassAdapter_spec.js b/test/spec/modules/bidglassAdapter_spec.js index d153430103d..7b007f7cc1f 100644 --- a/test/spec/modules/bidglassAdapter_spec.js +++ b/test/spec/modules/bidglassAdapter_spec.js @@ -65,7 +65,10 @@ describe('Bid Glass Adapter', function () { 'creativeId': '-1', 'width': '300', 'height': '250', - 'requestId': '30b31c1838de1e' + 'requestId': '30b31c1838de1e', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }] } }; @@ -83,7 +86,10 @@ describe('Bid Glass Adapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 10, - 'ad': '' + 'ad': '', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }]; let result = spec.interpretResponse(response); From 10a2760ebec167980c78a80f192ff0c38075534b Mon Sep 17 00:00:00 2001 From: Estavillo Date: Fri, 4 Jun 2021 03:47:01 -0700 Subject: [PATCH 1087/1476] GUMGUM Bid Adapter: Add GPID for TTD (#6841) * add gpid * add jira reference * add unit test Co-authored-by: Estavillo Co-authored-by: Lisa Benmore --- modules/gumgumBidAdapter.js | 10 +++++++++- test/spec/modules/gumgumBidAdapter_spec.js | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 4786fd04b15..a49cd1593d9 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -247,11 +247,18 @@ function buildRequests (validBidRequests, bidderRequest) { params = {}, schain, transactionId, - userId = {} + userId = {}, + ortb2Imp } = bidRequest; const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest); let sizes = [1, 1]; let data = {}; + let gpid = ''; + + // ADJS-1024 + if (utils.deepAccess(ortb2Imp, 'ext.data.adserver.name')) { + gpid = ortb2Imp.ext.data.adserver.adslot + } if (mediaTypes.banner) { sizes = mediaTypes.banner.sizes; @@ -318,6 +325,7 @@ function buildRequests (validBidRequests, bidderRequest) { sizes, url: BID_ENDPOINT, method: 'GET', + gpid: gpid, data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId), _getTradeDeskIDParam(userId)) }) }); diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index a7b18a16173..62988b75fd1 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -189,6 +189,13 @@ describe('gumgumAdapter', function () { expect(bidRequest.data).to.not.have.property('irisid'); }); + it('should set the global placement id (gpid)', function () { + const req = { ...bidRequests[0], ortb2Imp: { ext: { data: { adserver: { name: 'test', adslot: 123456 } } } } } + const bidRequest = spec.buildRequests([req])[0]; + expect(bidRequest).to.have.property('gpid'); + expect(bidRequest.gpid).to.equal(123456); + }); + describe('product id', function () { it('should set the correct pi param if native param is found', function () { const request = { ...bidRequests[0], params: { ...zoneParam, native: 2 } }; From 40f7f2be2f6b77412c060ea2a621e0f30dde266e Mon Sep 17 00:00:00 2001 From: Aleksa Trajkovic Date: Fri, 4 Jun 2021 14:00:25 +0200 Subject: [PATCH 1088/1476] PBS Bid Adapter: seatbid.bid.ext.prebid.meta support (#6939) --- modules/prebidServerBidAdapter/index.js | 5 +++-- test/spec/modules/prebidServerBidAdapter_spec.js | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 4f13bab05eb..8f443a28024 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -987,8 +987,9 @@ const OPEN_RTB_PROTOCOL = { bidObject.creativeId = bid.crid; if (bid.burl) { bidObject.burl = bid.burl; } bidObject.currency = (response.cur) ? response.cur : DEFAULT_S2S_CURRENCY; - bidObject.meta = bidObject.meta || {}; - if (bid.ext && bid.ext.dchain) { bidObject.meta.dchain = utils.deepClone(bid.ext.dchain); } + bidObject.meta = {}; + let extPrebidMeta = utils.deepAccess(bid, 'ext.prebid.meta'); + if (extPrebidMeta && utils.isPlainObject(extPrebidMeta)) { bidObject.meta = utils.deepClone(extPrebidMeta); } if (bid.adomain) { bidObject.meta.advertiserDomains = bid.adomain; } // the OpenRTB location for "TTL" as understood by Prebid.js is "exp" (expiration). diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 6833daf49cf..1b9ae7ad184 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -271,9 +271,11 @@ const RESPONSE_OPENRTB = { 'type': 'banner', 'event': { 'win': 'http://wurl.org?id=333' + }, + 'meta': { + 'dchain': { 'ver': '1.0', 'complete': 0, 'nodes': [ { 'asi': 'magnite.com', 'bsid': '123456789', } ] } } }, - 'dchain': { 'ver': '1.0', 'complete': 0, 'nodes': [ { 'asi': 'magnite.com', 'bsid': '123456789', } ] }, 'bidder': { 'appnexus': { 'brand_id': 1, From 287f07c45cf4d01c1cd3e76b9d1850324af719ca Mon Sep 17 00:00:00 2001 From: nwlosinski Date: Fri, 4 Jun 2021 14:51:28 +0200 Subject: [PATCH 1089/1476] add meta.advertiserDomains to bid response (#6942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Norbert Włosiński --- modules/justpremiumBidAdapter.js | 5 ++++- test/spec/modules/justpremiumBidAdapter_spec.js | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/justpremiumBidAdapter.js b/modules/justpremiumBidAdapter.js index 1f84feee7d1..b0d7722fa06 100644 --- a/modules/justpremiumBidAdapter.js +++ b/modules/justpremiumBidAdapter.js @@ -90,7 +90,10 @@ export const spec = { netRevenue: true, currency: bid.currency || 'USD', ttl: bid.ttl || spec.time, - format: bid.format + format: bid.format, + meta: { + advertiserDomains: bid.adomain && bid.adomain.length > 0 ? bid.adomain : [] + } } bidResponses.push(bidResponse) } diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index 7cc154254ff..cb3648cba44 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -103,7 +103,8 @@ describe('justpremium adapter', function () { 'width': 970, 'price': 0.52, 'format': 'lb', - 'adm': 'creative code' + 'adm': 'creative code', + 'adomain': ['justpremium.com'] }] }, 'pass': { @@ -123,7 +124,10 @@ describe('justpremium adapter', function () { netRevenue: true, currency: 'USD', ttl: 60000, - format: 'lb' + format: 'lb', + meta: { + advertiserDomains: ['justpremium.com'] + }, } ] @@ -140,6 +144,7 @@ describe('justpremium adapter', function () { expect(result[0].creativeId).to.equal(3213123) expect(result[0].netRevenue).to.equal(true) expect(result[0].format).to.equal('lb') + expect(result[0].meta.advertiserDomains[0]).to.equal('justpremium.com') }) it('Verify wrong server response', function () { From 3aeab296d95a517810d045db16fcf0a8f3a6e061 Mon Sep 17 00:00:00 2001 From: Marco Cosentino <807030+cosenmarco@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:33:41 +0200 Subject: [PATCH 1090/1476] ID5 analytics adapter: initial release (#6871) * Adding Markdown for the new id5 analytics module * #24 Adding a first untested implementation * #24 Adding some unit tess and refactoring * #24 Adding cleanup transformations, improvements and tests * #24 Improving on specs and implementation of cleanup * #24 Adding standard tracking of bidWon and cleanup of native creative * #24 More cleanup rules * #24 Using real URL instad of mock * #24 Typo * #24 Code review improvements Co-authored-by: Marco Cosentino Co-authored-by: Scott Menzer --- modules/id5AnalyticsAdapter.js | 304 ++++++++++++ modules/id5AnalyticsAdapter.md | 42 ++ test/spec/modules/id5AnalyticsAdapter_spec.js | 452 ++++++++++++++++++ 3 files changed, 798 insertions(+) create mode 100644 modules/id5AnalyticsAdapter.js create mode 100644 modules/id5AnalyticsAdapter.md create mode 100644 test/spec/modules/id5AnalyticsAdapter_spec.js diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js new file mode 100644 index 00000000000..abf23d2925d --- /dev/null +++ b/modules/id5AnalyticsAdapter.js @@ -0,0 +1,304 @@ +import buildAdapter from '../src/AnalyticsAdapter.js'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager.js'; +import { ajax } from '../src/ajax.js'; +import { logInfo, logError } from '../src/utils.js'; +import events from '../src/events.js'; + +const { + EVENTS: { + AUCTION_END, + TCF2_ENFORCEMENT, + BID_WON, + BID_VIEWABLE, + AD_RENDER_FAILED + } +} = CONSTANTS + +const GVLID = 131; + +const STANDARD_EVENTS_TO_TRACK = [ + AUCTION_END, + TCF2_ENFORCEMENT, + BID_WON, +]; + +// These events cause the buffered events to be sent over +const FLUSH_EVENTS = [ + TCF2_ENFORCEMENT, + AUCTION_END, + BID_WON, + BID_VIEWABLE, + AD_RENDER_FAILED +]; + +const CONFIG_URL_PREFIX = 'https://api.id5-sync.com/analytics' +const TZ = new Date().getTimezoneOffset(); +const PBJS_VERSION = $$PREBID_GLOBAL$$.version; +const ID5_REDACTED = '__ID5_REDACTED__'; +const isArray = Array.isArray; + +let id5Analytics = Object.assign(buildAdapter({analyticsType: 'endpoint'}), { + // Keeps an array of events for each auction + eventBuffer: {}, + + eventsToTrack: STANDARD_EVENTS_TO_TRACK, + + track: (event) => { + const _this = id5Analytics; + + if (!event || !event.args) { + return; + } + + try { + const auctionId = event.args.auctionId; + _this.eventBuffer[auctionId] = _this.eventBuffer[auctionId] || []; + + // Collect events and send them in a batch when the auction ends + const que = _this.eventBuffer[auctionId]; + que.push(_this.makeEvent(event.eventType, event.args)); + + if (FLUSH_EVENTS.indexOf(event.eventType) >= 0) { + // Auction ended. Send the batch of collected events + _this.sendEvents(que); + + // From now on just send events to server side as they come + que.push = (pushedEvent) => _this.sendEvents([pushedEvent]); + } + } catch (error) { + logError('id5Analytics: ERROR', error); + _this.sendErrorEvent(error); + } + }, + + sendEvents: (eventsToSend) => { + const _this = id5Analytics; + // By giving some content this will be automatically a POST + eventsToSend.forEach((event) => + ajax(_this.options.ingestUrl, null, JSON.stringify(event))); + }, + + makeEvent: (event, payload) => { + const _this = id5Analytics; + const filteredPayload = deepTransformingClone(payload, + transformFnFromCleanupRules(event)); + return { + source: 'pbjs', + event, + payload: filteredPayload, + partnerId: _this.options.partnerId, + meta: { + sampling: _this.options.id5Sampling, + pbjs: PBJS_VERSION, + tz: TZ, + } + }; + }, + + sendErrorEvent: (error) => { + const _this = id5Analytics; + _this.sendEvents([ + _this.makeEvent('analyticsError', { + message: error.message, + stack: error.stack, + }) + ]); + }, + + random: () => Math.random(), +}); + +const ENABLE_FUNCTION = (config) => { + const _this = id5Analytics; + _this.options = (config && config.options) || {}; + + const partnerId = _this.options.partnerId; + if (typeof partnerId !== 'number') { + logError('id5Analytics: partnerId in config.options must be a number representing the id5 partner ID'); + return; + } + + ajax(`${CONFIG_URL_PREFIX}/${partnerId}/pbjs`, (result) => { + logInfo('id5Analytics: Received from configuration endpoint', result); + + const configFromServer = JSON.parse(result); + + const sampling = _this.options.id5Sampling = + typeof configFromServer.sampling === 'number' ? configFromServer.sampling : 0; + + if (typeof configFromServer.ingestUrl !== 'string') { + logError('id5Analytics: cannot find ingestUrl in config endpoint response; no analytics will be available'); + return; + } + _this.options.ingestUrl = configFromServer.ingestUrl; + + // 3-way fallback for which events to track: server > config > standard + _this.eventsToTrack = configFromServer.eventsToTrack || _this.options.eventsToTrack || STANDARD_EVENTS_TO_TRACK; + _this.eventsToTrack = isArray(_this.eventsToTrack) ? _this.eventsToTrack : STANDARD_EVENTS_TO_TRACK; + + logInfo('id5Analytics: Configuration is', _this.options); + 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!') + + // Clean start + _this.eventBuffer = {}; + + // Replay all events until now + if (!config.disablePastEventsProcessing) { + events.getEvents().forEach((event) => { + if (event && _this.eventsToTrack.indexOf(event.eventType) >= 0) { + _this.track(event); + } + }); + } + + // Merge in additional cleanup rules + if (configFromServer.additionalCleanupRules) { + const newRules = configFromServer.additionalCleanupRules; + _this.eventsToTrack.forEach((key) => { + // Some protective checks in case we mess up server side + if ( + isArray(newRules[key]) && + newRules[key].every((eventRules) => + isArray(eventRules.match) && + (eventRules.apply in TRANSFORM_FUNCTIONS)) + ) { + logInfo('id5Analytics: merging additional cleanup rules for event ' + key); + CLEANUP_RULES[key].push(...newRules[key]); + } + }); + } + + // Register to the events of interest + _this.handlers = {}; + _this.eventsToTrack.forEach((eventType) => { + const handler = _this.handlers[eventType] = (args) => + _this.track({ eventType, args }); + events.on(eventType, handler); + }); + } + }); + + // Make only one init possible within a lifecycle + _this.enableAnalytics = () => {}; +}; + +id5Analytics.enableAnalytics = ENABLE_FUNCTION; +id5Analytics.disableAnalytics = () => { + const _this = id5Analytics; + // Un-register to the events of interest + _this.eventsToTrack.forEach((eventType) => { + if (_this.handlers && _this.handlers[eventType]) { + events.off(eventType, _this.handlers[eventType]); + } + }); + + // Make re-init possible. Work around the fact that past events cannot be forgotten + _this.enableAnalytics = (config) => { + config.disablePastEventsProcessing = true; + ENABLE_FUNCTION(config); + }; +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: id5Analytics, + code: 'id5Analytics', + gvlid: GVLID +}); + +export default id5Analytics; + +function redact(obj, key) { + obj[key] = ID5_REDACTED; +} + +function erase(obj, key) { + delete obj[key]; +} + +// The transform function matches against a path and applies +// required transformation if match is found. +function deepTransformingClone(obj, transform, currentPath = []) { + const result = isArray(obj) ? [] : {}; + const recursable = typeof obj === 'object' && obj !== null; + if (recursable) { + const keys = Object.keys(obj); + if (keys.length > 0) { + keys.forEach((key) => { + const newPath = currentPath.concat(key); + result[key] = deepTransformingClone(obj[key], transform, newPath); + transform(newPath, result, key); + }); + return result; + } + } + return obj; +} + +// Every set of rules is an object where "match" is an array and +// "apply" is the function to apply in case of match. The function to apply +// takes (obj, prop) and transforms property "prop" in object "obj". +// The "match" is an array of path parts. Each part is either a string or an array. +// In case of array, it represents alternatives which all would match. +// Special path part '*' matches any subproperty +const CLEANUP_RULES = {}; +CLEANUP_RULES[AUCTION_END] = [{ + match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', ['userId', 'crumbs'], '*'], + apply: 'redact' +}, { + match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', 'userIdAsEids', '*', 'uids', '*', ['id', 'ext']], + apply: 'redact' +}, { + match: ['bidderRequests', '*', 'gdprConsent', 'vendorData'], + apply: 'erase' +}, { + match: ['bidsReceived', '*', ['ad', 'native']], + apply: 'erase' +}, { + match: ['noBids', '*', ['userId', 'crumbs'], '*'], + apply: 'redact' +}, { + match: ['noBids', '*', 'userIdAsEids', '*', 'uids', '*', ['id', 'ext']], + apply: 'redact' +}]; + +CLEANUP_RULES[BID_WON] = [{ + match: [['ad', 'native']], + apply: 'erase' +}]; + +const TRANSFORM_FUNCTIONS = { + 'redact': redact, + 'erase': erase, +}; + +// Builds a rule function depending on the event type +function transformFnFromCleanupRules(eventType) { + const rules = CLEANUP_RULES[eventType] || []; + return (path, obj, key) => { + for (let i = 0; i < rules.length; i++) { + let match = true; + const ruleMatcher = rules[i].match; + const transformation = rules[i].apply; + if (ruleMatcher.length !== path.length) { + continue; + } + for (let fragment = 0; fragment < ruleMatcher.length && match; fragment++) { + const choices = makeSureArray(ruleMatcher[fragment]); + match = !choices.every((choice) => choice !== '*' && path[fragment] !== choice); + } + if (match) { + const transformfn = TRANSFORM_FUNCTIONS[transformation]; + transformfn(obj, key); + break; + } + } + }; +} + +function makeSureArray(object) { + return isArray(object) ? object : [object]; +} diff --git a/modules/id5AnalyticsAdapter.md b/modules/id5AnalyticsAdapter.md new file mode 100644 index 00000000000..e12784b70f6 --- /dev/null +++ b/modules/id5AnalyticsAdapter.md @@ -0,0 +1,42 @@ +# Overview +Module Name: ID5 Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: [id5.io](https://id5.io) + +# ID5 Universal ID + +The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://support.id5.io/portal/en/kb/articles/prebid-js-user-id-module). + +# ID5 Analytics Registration + +The ID5 Analytics Adapter is free to use during our Beta period, but requires a simple registration with ID5. Please visit [id5.io/universal-id](https://id5.io/universal-id) to sign up and request your ID5 Partner Number to get started. If you're already using the ID5 Universal ID, you may use your existing Partner Number with the analytics adapter. + +The ID5 privacy policy is at [https://www.id5.io/platform-privacy-policy](https://www.id5.io/platform-privacy-policy). + +## ID5 Analytics Configuration + +First, make sure to add the ID5 Analytics submodule to your Prebid.js package with: + +``` +gulp build --modules=...,id5AnalyticsAdapter +``` + +The following configuration parameters are available: + +```javascript +pbjs.enableAnalytics({ + provider: 'id5Analytics', + options: { + partnerId: 1234, // change to the Partner Number you received from ID5 + eventsToTrack: ['auctionEnd','bidWon'] + } +}); +``` + +| Parameter | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| provider | Required | String | The name of this module: `id5Analytics` | `id5Analytics` | +| options.partnerId | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `1234` | +| options.eventsToTrack | Optional | Array of strings | Overrides the set of tracked events | `['auctionEnd','bidWon']` | diff --git a/test/spec/modules/id5AnalyticsAdapter_spec.js b/test/spec/modules/id5AnalyticsAdapter_spec.js new file mode 100644 index 00000000000..4a1e708ed7d --- /dev/null +++ b/test/spec/modules/id5AnalyticsAdapter_spec.js @@ -0,0 +1,452 @@ +import adapterManager from '../../../src/adapterManager.js'; +import id5AnalyticsAdapter from '../../../modules/id5AnalyticsAdapter.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import events from '../../../src/events.js'; +import constants from '../../../src/constants.json'; +import { generateUUID } from '../../../src/utils.js'; + +const CONFIG_URL = 'https://api.id5-sync.com/analytics/12349/pbjs'; +const INGEST_URL = 'https://test.me/ingest'; + +describe('ID5 analytics adapter', () => { + let server; + let config; + + beforeEach(() => { + server = sinon.createFakeServer(); + config = { + options: { + partnerId: 12349, + } + }; + }); + + afterEach(() => { + server.restore(); + }); + + it('registers itself with the adapter manager', () => { + const adapter = adapterManager.getAnalyticsAdapter('id5Analytics'); + expect(adapter).to.exist; + expect(adapter.gvlid).to.be.a('number'); + expect(adapter.adapter).to.equal(id5AnalyticsAdapter); + }); + + it('tolerates undefined or empty config', () => { + id5AnalyticsAdapter.enableAnalytics(undefined); + id5AnalyticsAdapter.enableAnalytics({}); + }); + + it('calls configuration endpoint', () => { + server.respondWith('GET', CONFIG_URL, [200, + { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + `{ "sampling": 0, "ingestUrl": "${INGEST_URL}" }` + ]); + id5AnalyticsAdapter.enableAnalytics(config); + server.respond(); + + expect(server.requests).to.have.length(1); + + id5AnalyticsAdapter.disableAnalytics(); + }); + + it('does not call configuration endpoint when partner id is missing', () => { + id5AnalyticsAdapter.enableAnalytics({}); + server.respond(); + + expect(server.requests).to.have.length(0); + + id5AnalyticsAdapter.disableAnalytics(); + }); + + describe('after configuration', () => { + let auction; + + beforeEach(() => { + server.respondWith('GET', CONFIG_URL, [200, + { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + `{ "sampling": 1, "ingestUrl": "${INGEST_URL}" }` + ]); + + server.respondWith('POST', INGEST_URL, [200, + { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + '' + ]); + + auction = { + auctionId: generateUUID(), + adUnits: [{ + 'code': 'user-728', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600], [728, 90]] + } + }, + adUnitCodes: ['user-728'] + }], + }; + }); + + afterEach(() => { + id5AnalyticsAdapter.disableAnalytics(); + }); + + it('sends auction end events to the backend', () => { + id5AnalyticsAdapter.enableAnalytics(config); + server.respond(); + events.emit(constants.EVENTS.AUCTION_END, auction); + server.respond(); + + // Why 3? 1: config, 2: tcfEnforcement, 3: auctionEnd + // tcfEnforcement? yes, gdprEnforcement module emits in reaction to auctionEnd + expect(server.requests).to.have.length(3); + + const body1 = JSON.parse(server.requests[1].requestBody); + expect(body1.source).to.equal('pbjs'); + expect(body1.event).to.equal('tcf2Enforcement'); + expect(body1.partnerId).to.equal(12349); + expect(body1.meta).to.be.a('object'); + expect(body1.meta.pbjs).to.equal($$PREBID_GLOBAL$$.version); + expect(body1.meta.sampling).to.equal(1); + expect(body1.meta.tz).to.be.a('number'); + + const body2 = JSON.parse(server.requests[2].requestBody); + expect(body2.source).to.equal('pbjs'); + expect(body2.event).to.equal('auctionEnd'); + expect(body2.partnerId).to.equal(12349); + expect(body2.meta).to.be.a('object'); + expect(body2.meta.pbjs).to.equal($$PREBID_GLOBAL$$.version); + expect(body2.meta.sampling).to.equal(1); + expect(body2.meta.tz).to.be.a('number'); + expect(body2.payload).to.eql(auction); + }); + + it('filters unwanted IDs from the events it sends', () => { + auction.adUnits[0].bids = [{ + 'bidder': 'appnexus', + 'params': { + 'placementId': '16618951' + }, + 'userId': { + 'criteoId': '_h_y_19IMUhMZG1TOTRReHFNc29TekJ3TzQ3elhnRU81ayUyQjhiRkdJJTJGaTFXJTJCdDRnVmN4S0FETUhQbXdmQWg0M3g1NWtGbGolMkZXalclMkJvWjJDOXFDSk1HU3ZKaVElM0QlM0Q', + 'id5id': { + 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', + 'ext': { 'linkType': 1 } + }, + 'tdid': '888a6042-8f99-483b-aa26-23c44bc9166b' + }, + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': '_h_y_19IMUhMZG1TOTRReHFNc29TekJ3TzQ3elhnRU81ayUyQjhiRkdJJTJGaTFXJTJCdDRnVmN4S0FETUhQbXdmQWg0M3g1NWtGbGolMkZXalclMkJvWjJDOXFDSk1HU3ZKaVElM0QlM0Q', + 'atype': 1 + }] + }, { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', + 'atype': 1, + 'ext': { 'linkType': 1 } + }] + }] + }]; + + auction.bidderRequests = [{ + 'bidderCode': 'appnexus', + 'auctionId': 'e8d15df4-d89c-44c9-8b36-812f75cbf227', + 'bidderRequestId': '1451a3c759c60359', + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': '16824712' + }, + 'userId': { + 'id5id': { + 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*CmuuahP8jbPJGRCUDdT2VZ8wz0eJM8O8mNlKktlEjuYAABFEjc2c9faqDencf2hR', + 'ext': { + 'linkType': 1 + } + }, + 'sharedid': { + 'id': '01F6J4T72MRFYVWTN65WFA0H7N', + 'third': '01F6J4T72MRFYVWTN65WFA0H7N' + }, + 'tdid': '0e45f56b-ad09-4c91-b090-8bd03e0d0754' + }, + 'userIdAsEids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*CmuuahP8jbPJGRCUDdT2VZ8wz0eJM8O8mNlKktlEjuYAABFEjc2c9faqDencf2hR', + 'atype': 1, + 'ext': { + 'linkType': 1 + } + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01F6J4T72MRFYVWTN65WFA0H7N', + 'atype': 1, + 'ext': { + 'third': '01F6J4T72MRFYVWTN65WFA0H7N' + } + } + ] + }, + { + 'source': 'adserver.org', + 'uids': [ + { + 'id': '0e45f56b-ad09-4c91-b090-8bd03e0d0754', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + } + ], + 'ortb2Imp': { + 'ext': { + 'data': { + 'adserver': { + 'name': 'gam', + 'adslot': '/6783/Kiwi/portail' + }, + 'pbadslot': '/6783/Kiwi/portail' + } + } + }, + 'adUnitCode': 'btf_leaderboard', + 'transactionId': '3ce8216e-7898-4a22-86ba-01519b62bfce', + 'sizes': [ + [ + 728, + 90 + ] + ], + 'bidId': '146661c05209a56e', + 'bidderRequestId': '1451a3c759c60359', + 'auctionId': 'e8d15df4-d89c-44c9-8b36-812f75cbf227', + 'src': 'client', + 'bidRequestsCount': 2, + 'bidderRequestsCount': 2, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1621959214757, + 'timeout': 2000, + 'refererInfo': { + 'referer': 'https://www.blog.com/?pbjs_debug=true', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'https://www.blog.com/?pbjs_debug=true' + ], + 'canonicalUrl': null + }, + 'gdprConsent': { + 'consentString': 'CPGw1WAPGw1WAAHABBENBbCsAP_AAH_AAAAAH3tf_X__b3_j-_59__t0eY1f9_7_v-0zjhfdt-8N2f_X_L8X42M7vF36pq4KuR4Eu3LBIQdlHOHcTUmw6okVrTPsbk2Mr7NKJ7PEmnMbe2dYGH9_n93TuZKY7__8___z__-v_v____f_r-3_3__59X---_e_V399zLv9__3__9gfaASYal8AF2JY4Mk0aVQogQhWEh0AoAKKAYWiawgZXBTsrgI9QQMAEJqAjAiBBiCjFgEAAgEASERASAHggEQBEAgABACpAQgAI2AQWAFgYBAAKAaFiBFAEIEhBkcFRymBARItFBPZWAJRd7GmEIZRYAUCj-iowEShBAsDISFg4AAA.f_gAD_gAAAAA', + 'vendorData': { + 'cmpId': 7, + 'cmpVersion': 1, + 'gdprApplies': true, + 'tcfPolicyVersion': 2, + 'eventStatus': 'useractioncomplete', + 'cmpStatus': 'loaded', + 'listenerId': 47, + 'tcString': 'CPGw1WAPGw1WAAHABBENBbCsAP_AAH_AAAAAH3tf_X__b3_j-_59__t0eY1f9_7_v-0zjhfdt-8N2f_X_L8X42M7vF36pq4KuR4Eu3LBIQdlHOHcTUmw6okVrTPsbk2Mr7NKJ7PEmnMbe2dYGH9_n93TuZKY7__8___z__-v_v____f_r-3_3__59X---_e_V399zLv9__3__9gfaASYal8AF2JY4Mk0aVQogQhWEh0AoAKKAYWiawgZXBTsrgI9QQMAEJqAjAiBBiCjFgEAAgEASERASAHggEQBEAgABACpAQgAI2AQWAFgYBAAKAaFiBFAEIEhBkcFRymBARItFBPZWAJRd7GmEIZRYAUCj-iowEShBAsDISFg4AAA.f_gAD_gAAAAA', + }, + 'gdprApplies': true, + 'addtlConsent': '1~7.12.35.62.66.70.89.93.108.122.144.149.153.162.167.184.196.221.241.253.259.272.311.317.323.326.338.348.350.415.440.448.449.482.486.491.494.495.540.571.574.585.587.588.590.725.733.780.817.839.864.867.932.938.981.986.1031.1033.1051.1092.1097.1126.1127.1170.1171.1186.1201.1204.1205.1211.1215.1230.1232.1236.1248.1276.1290.1301.1313.1344.1364.1365.1415.1419.1428.1449.1451.1509.1558.1564.1570.1577.1591.1651.1669.1712.1716.1720.1721.1725.1733.1753.1765.1799.1810.1834.1842.1870.1878.1889.1896.1911.1922.1929.2012.2072.2078.2079.2109.2177.2202.2253.2290.2299.2316.2357.2373.2526.2531.2571.2572.2575.2628.2663.2677.2776.2778.2779.2985.3033.3052.3154', + 'apiVersion': 2 + }, + 'start': 1621959214763 + }]; + + auction.bidsReceived = [{ + 'bidderCode': 'appnexus', + 'width': 728, + 'height': 90, + 'statusMessage': 'Bid available', + 'adId': '99e7838aa7f1c4f', + 'requestId': '21e0b32208ee9a', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.020601, + 'creativeId': 209272535, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': 'user-728', + 'appnexus': { + 'buyerMemberId': 11563 + }, + 'meta': { + 'advertiserId': 4388779 + }, + 'ad': 'stuff i am not interested in', + 'originalCpm': 0.020601, + 'originalCurrency': 'USD', + 'auctionId': 'c7694dbb-a583-4a73-a933-b16f1f821ba4', + // Make sure cleanup is resilient + 'someNullObject': null, + 'someUndefinedProperty': undefined + }]; + + id5AnalyticsAdapter.enableAnalytics(config); + server.respond(); + events.emit(constants.EVENTS.AUCTION_END, auction); + server.respond(); + + expect(server.requests).to.have.length(3); + + const body = JSON.parse(server.requests[2].requestBody); + expect(body.event).to.equal('auctionEnd'); + expect(body.payload.adUnits[0].bids[0].userId).to.eql({ + 'criteoId': '__ID5_REDACTED__', + 'id5id': '__ID5_REDACTED__', + 'tdid': '__ID5_REDACTED__' + }); + expect(body.payload.bidderRequests[0].bids[0].userId).to.eql({ + 'sharedid': '__ID5_REDACTED__', + 'id5id': '__ID5_REDACTED__', + 'tdid': '__ID5_REDACTED__' + }); + body.payload.adUnits[0].bids[0].userIdAsEids.forEach((userId) => { + expect(userId.uids[0].id).to.equal('__ID5_REDACTED__'); + if (userId.uids[0].ext) { + expect(userId.uids[0].ext).to.equal('__ID5_REDACTED__'); + } + }); + body.payload.bidderRequests[0].bids[0].userIdAsEids.forEach((userId) => { + expect(userId.uids[0].id).to.equal('__ID5_REDACTED__'); + if (userId.uids[0].ext) { + expect(userId.uids[0].ext).to.equal('__ID5_REDACTED__'); + } + }); + expect(body.payload.bidsReceived[0].ad).to.equal(undefined); + expect(body.payload.bidsReceived[0].requestId).to.equal('21e0b32208ee9a'); + }); + + it('can override events to collect if configured to do so', () => { + server.respondWith('GET', CONFIG_URL, [200, + { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + `{ "sampling": 1, "ingestUrl": "${INGEST_URL}", "eventsToTrack": ["tcf2Enforcement"] }` + ]); + id5AnalyticsAdapter.enableAnalytics(config); + server.respond(); + events.emit(constants.EVENTS.AUCTION_END, auction); + server.respond(); + + expect(server.requests).to.have.length(2); + const body1 = JSON.parse(server.requests[1].requestBody); + expect(body1.event).to.equal('tcf2Enforcement'); + }); + + it('can extend cleanup rules from server side', () => { + auction.bidsReceived = [{ + 'bidderCode': 'appnexus', + 'width': 728, + 'height': 90, + 'statusMessage': 'Bid available', + 'adId': '99e7838aa7f1c4f', + 'requestId': '21e0b32208ee9a', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.020601, + 'creativeId': 209272535, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'adUnitCode': 'user-728', + 'appnexus': { + 'buyerMemberId': 11563 + }, + 'meta': { + 'advertiserId': 4388779 + }, + 'ad': 'stuff i am not interested in', + 'originalCpm': 0.020601, + 'originalCurrency': 'USD', + 'auctionId': 'c7694dbb-a583-4a73-a933-b16f1f821ba4' + }, { + 'bidderCode': 'ix', + 'width': 728, + 'height': 90, + 'statusMessage': 'Bid available', + 'adId': '228f725de4a9ff09', + 'requestId': '225a42b4a8ec7287', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.06, + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': '8838044', + 'ad': 'lots of HTML code', + 'ttl': 300, + 'meta': { + 'networkId': 85, + 'brandId': 822, + 'brandName': 'Microsoft Brands', + 'advertiserDomains': [ + 'microsoftstore.com' + ] + }, + 'originalCpm': 0.06, + 'originalCurrency': 'USD', + 'auctionId': 'fe28ce44-61bb-4ed8-be3c-3e801dfddcb9', + 'responseTimestamp': 1621954632648, + 'requestTimestamp': 1621954632498, + 'bidder': 'ix', + 'adUnitCode': 'sticky_footer', + 'timeToRespond': 150, + 'pbLg': '0.00', + 'pbCg': '0.06', + 'size': '728x90', + 'adserverTargeting': { + 'hb_bidder': 'ix', + } + }]; + server.respondWith('GET', CONFIG_URL, [200, + { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*' + }, + `{ "sampling": 1, "ingestUrl": "${INGEST_URL}", "additionalCleanupRules": {"auctionEnd": [{"match":["bidsReceived", "*", "requestId"],"apply":"erase"}]} }` + ]); + id5AnalyticsAdapter.enableAnalytics(config); + server.respond(); + events.emit(constants.EVENTS.AUCTION_END, auction); + server.respond(); + + expect(server.requests).to.have.length(3); + const body = JSON.parse(server.requests[2].requestBody); + expect(body.event).to.equal('auctionEnd'); + expect(body.payload.bidsReceived[0].requestId).to.equal(undefined); + expect(body.payload.bidsReceived[1].requestId).to.equal(undefined); + expect(body.payload.bidsReceived[0].bidderCode).to.equal('appnexus'); + expect(body.payload.bidsReceived[1].bidderCode).to.equal('ix'); + }); + }); +}); From bc2f9648a4cb4e8e2cea660ed97aa5b482fb818e Mon Sep 17 00:00:00 2001 From: Vladyslav Laktionov Date: Fri, 4 Jun 2021 18:14:37 +0300 Subject: [PATCH 1091/1476] BidsCube Bid Adapter: add new adapter (#6730) --- modules/bidscubeBidAdapter.js | 90 ++++++++ modules/bidscubeBidAdapter.md | 30 +++ test/spec/modules/bidscubeBidAdapter_spec.js | 226 +++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 modules/bidscubeBidAdapter.js create mode 100644 modules/bidscubeBidAdapter.md create mode 100644 test/spec/modules/bidscubeBidAdapter_spec.js diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js new file mode 100644 index 00000000000..d3f27a5ac6d --- /dev/null +++ b/modules/bidscubeBidAdapter.js @@ -0,0 +1,90 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' +import * as utils from '../src/utils.js' + +const BIDDER_CODE = 'bidscube' +const URL = 'https://supply.bidscube.com/?c=o&m=multi' +const URL_SYNC = 'https://supply.bidscube.com/?c=o&m=cookie' + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: function (opts) { + return Boolean(opts.bidId && opts.params && !isNaN(parseInt(opts.params.placementId))) + }, + + buildRequests: function (validBidRequests) { + validBidRequests = validBidRequests || [] + let winTop = window + try { + window.top.location.toString() + winTop = window.top + } catch (e) { utils.logMessage(e) } + + const location = utils.getWindowLocation() + const placements = [] + + for (let i = 0; i < validBidRequests.length; i++) { + const p = validBidRequests[i] + + placements.push({ + placementId: p.params.placementId, + bidId: p.bidId, + traffic: p.params.traffic || BANNER, + allParams: JSON.stringify(p) + }) + } + + return { + method: 'POST', + url: URL, + data: { + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language : '', + secure: +(location.protocol === 'https:'), + host: location.hostname, + page: location.pathname, + placements: placements + } + } + }, + + interpretResponse: function (opts) { + const body = opts.body + const response = [] + + for (let i = 0; i < body.length; i++) { + const item = body[i] + if (isBidResponseValid(item)) { + response.push(item) + } + } + + return response + }, + + getUserSyncs: function (syncOptions, serverResponses) { + return [{ type: 'image', url: URL_SYNC }] + } +} + +registerBidder(spec) + +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) + case NATIVE: + return Boolean(bid.title && bid.image && bid.impressionTrackers) + default: + return false + } +} diff --git a/modules/bidscubeBidAdapter.md b/modules/bidscubeBidAdapter.md new file mode 100644 index 00000000000..5f3972726ec --- /dev/null +++ b/modules/bidscubeBidAdapter.md @@ -0,0 +1,30 @@ +# Overview + +``` +Module Name: BidsCube Bidder Adapter +Module Type: Bidder Adapter +Maintainer: publishers@bidscube.com +``` + +# Description + +Module that connects to BidsCube' demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'bidscube', + params: { + placementId: 0, + traffic: 'banner' + } + }] + }]; +``` diff --git a/test/spec/modules/bidscubeBidAdapter_spec.js b/test/spec/modules/bidscubeBidAdapter_spec.js new file mode 100644 index 00000000000..26cf19fc310 --- /dev/null +++ b/test/spec/modules/bidscubeBidAdapter_spec.js @@ -0,0 +1,226 @@ +import { expect } from 'chai' +import { spec } from '../../../modules/bidscubeBidAdapter.js' +import { deepStrictEqual, notEqual, ok, strictEqual } from 'assert' + +describe('BidsCubeAdapter', () => { + const bid = { + bidId: '9ec5b177515ee2e5', + bidder: 'bidscube', + params: { + placementId: 0, + traffic: 'banner', + allParams: '{}' + } + } + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + strictEqual(true, spec.isBidRequestValid(bid)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.placementId + strictEqual(false, spec.isBidRequestValid(b)) + }) + }) + + describe('buildRequests', () => { + const serverRequest = spec.buildRequests([bid]) + + it('Creates a ServerRequest object with method, URL and data', () => { + ok(serverRequest) + ok(serverRequest.method) + ok(serverRequest.url) + ok(serverRequest.data) + }) + + it('Returns POST method', () => { + strictEqual('POST', serverRequest.method) + }) + + it('Returns valid URL', () => { + strictEqual('https://supply.bidscube.com/?c=o&m=multi', serverRequest.url) + }) + + it('Returns valid data if array of bids is valid', () => { + const { data } = serverRequest + strictEqual('object', typeof data) + deepStrictEqual(['deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) + strictEqual('number', typeof data.deviceWidth) + strictEqual('number', typeof data.deviceHeight) + strictEqual('string', typeof data.language) + strictEqual('string', typeof data.host) + strictEqual('string', typeof data.page) + notEqual(-1, [0, 1].indexOf(data.secure)) + + const placement = data.placements[0] + deepStrictEqual(['placementId', 'bidId', 'traffic', 'allParams'], Object.keys(placement)) + strictEqual(0, placement.placementId) + strictEqual('9ec5b177515ee2e5', placement.bidId) + strictEqual('banner', placement.traffic) + strictEqual('{"bidId":"9ec5b177515ee2e5","bidder":"bidscube","params":{"placementId":0,"traffic":"banner","allParams":"{}"}}', placement.allParams) + }) + + it('Returns empty data if no valid requests are passed', () => { + const { placements } = spec.buildRequests([]).data + + expect(spec.buildRequests([]).data.placements).to.be.an('array') + strictEqual(0, placements.length) + }) + }) + + describe('interpretResponse', () => { + const validData = [ + { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['test.com'] + } + }] + }, + { + body: [{ + vastUrl: 'bidscube.com', + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['test.com'] + } + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'bidscube.com', + title: 'Test', + image: 'bidscube.com', + creativeId: '2', + impressionTrackers: ['bidscube.com'], + ttl: 120, + cpm: 0.4, + requestId: '9ec5b177515ee2e5', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['test.com'] + } + }] + } + ] + + for (const obj of validData) { + const { mediaType } = obj.body[0] + + it(`Should interpret ${mediaType} response`, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(1, response.length) + + const copy = { ...obj.body[0] } + deepStrictEqual(copy, response[0]) + }) + } + + for (const obj of validData) { + it(`Should interpret response has meta.advertiserDomains`, () => { + const response = spec.interpretResponse(obj) + + expect(response[0]['meta']['advertiserDomains']).to.be.an('array') + expect(response[0]['meta']['advertiserDomains'][0]).to.be.an('string') + }) + } + + const invalidData = [ + { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'bidscube.com', + title: 'Test', + impressionTrackers: ['bidscube.com'], + ttl: 120, + requestId: '9ec5b177515ee2e5', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of invalidData) { + const { mediaType } = obj.body[0] + + it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + } + + it('Should return an empty array if invalid response is passed', () => { + const response = spec.interpretResponse({ + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + }) + + describe('getUserSyncs', () => { + it('Returns valid URL and type', () => { + const expectedResult = [{ type: 'image', url: 'https://supply.bidscube.com/?c=o&m=cookie' }] + deepStrictEqual(expectedResult, spec.getUserSyncs()) + }) + }) +}) From 07237265359182c5a2a1807a86df047121e95387 Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:04:43 +0200 Subject: [PATCH 1092/1476] Improve Digital: add support for adomain and floors module (#6943) --- modules/improvedigitalBidAdapter.js | 32 +++++++++++++++++-- .../modules/improvedigitalBidAdapter_spec.js | 16 ++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index dc0911ff5da..e73df68b625 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -11,7 +11,7 @@ const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js const VIDEO_TARGETING = ['skip', 'skipmin', 'skipafter']; export const spec = { - version: '7.3.0', + version: '7.4.0', code: BIDDER_CODE, gvlid: 253, aliases: ['id'], @@ -158,6 +158,12 @@ export const spec = { bid.height = 1; } + if (bidObject.adomain) { + bid.meta = { + advertiserDomains: bidObject.adomain + }; + } + bids.push(bid); }); return bids; @@ -218,6 +224,21 @@ function getVideoTargetingParams(bid) { return result; } +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return null; + } + const floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + function outstreamRender(bid) { bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ @@ -264,8 +285,6 @@ function getNormalizedBidRequest(bid) { const bidId = utils.getBidIdParameter('bidId', bid); const transactionId = utils.getBidIdParameter('transactionId', bid); const currency = config.getConfig('currency.adServerCurrency'); - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); - const bidFloorCur = utils.getBidIdParameter('bidFloorCur', bid.params); let normalizedBidRequest = {}; if (isInstreamVideo(bid)) { @@ -309,6 +328,13 @@ function getNormalizedBidRequest(bid) { if (currency) { normalizedBidRequest.currency = currency; } + // Floor + let bidFloor = getBidFloor(bid); + let bidFloorCur = null; + if (!bidFloor) { + bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + bidFloorCur = utils.getBidIdParameter('bidFloorCur', bid.params); + } if (bidFloor) { normalizedBidRequest.bidFloor = bidFloor; normalizedBidRequest.bidFloorCur = bidFloorCur ? bidFloorCur.toUpperCase() : 'USD'; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index f34a75ef8f3..095e50f0c66 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -263,6 +263,14 @@ describe('Improve Digital Adapter Tests', function () { params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); expect(params.bid_request.imp[0].bidfloor).to.equal(0.05); expect(params.bid_request.imp[0].bidfloorcur).to.equal('EUR'); + + // getFloor defined -> use it over bidFloor + let getFloorResponse = { currency: 'USD', floor: 3 }; + bidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([bidRequest])[0]; + params = JSON.parse(decodeURIComponent(request.data.substring(PARAM_PREFIX.length))); + expect(params.bid_request.imp[0].bidfloor).to.equal(3); + expect(params.bid_request.imp[0].bidfloorcur).to.equal('USD'); }); it('should add GDPR consent string', function () { @@ -953,6 +961,14 @@ describe('Improve Digital Adapter Tests', function () { expect(bids[0].netRevenue).to.equal(true); }); + it('should set advertiserDomains', function () { + const adomain = ['domain.com']; + const response = JSON.parse(JSON.stringify(serverResponse)); + response.body.bid[0].adomain = adomain; + const bids = spec.interpretResponse(response, {bidderRequest}); + expect(bids[0].meta.advertiserDomains).to.equal(adomain); + }); + // Native ads it('should return a well-formed native ad bid', function () { let bids = spec.interpretResponse(serverResponseNative, {bidderRequest}); From afd54f7538f5ad6c90d74e4e24105baed458d8a8 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Fri, 4 Jun 2021 23:09:24 +0600 Subject: [PATCH 1093/1476] Zeta Ssp Bid Adapter: provide domain in site object (#6928) * Provide domain value in site object * Support IE11 * Just minor refactor Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 20 ++++++++++++++++---- test/spec/modules/zetaSspBidAdapter_spec.js | 9 ++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 530a2efce1e..ecf3c2835d4 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -62,9 +62,10 @@ export const spec = { sid: params.sid ? params.sid : undefined } }; - + const rInfo = bidderRequest.refererInfo; payload.device.ua = navigator.userAgent; - payload.site.page = bidderRequest.refererInfo.referer; + payload.site.page = (rInfo && rInfo.referer) ? rInfo.referer.trim() : window.location.href; + payload.site.domain = getDomainFromURL(payload.site.page); payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; if (params.test) { @@ -115,8 +116,9 @@ export const spec = { netRevenue: NET_REV, }; if (zetaBid.adomain && zetaBid.adomain.length) { - bid.meta = {}; - bid.meta.advertiserDomains = zetaBid.adomain; + bid.meta = { + advertiserDomains: zetaBid.adomain + }; } bidResponse.push(bid); } @@ -178,4 +180,14 @@ function provideEids(request, payload) { } } +function getDomainFromURL(url) { + let anchor = document.createElement('a'); + anchor.href = url; + let hostname = anchor.hostname; + if (hostname.indexOf('www.') === 0) { + return hostname.substring(4); + } + return hostname; +} + registerBidder(spec); diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index bace0e86d9a..2313fae3705 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -33,7 +33,7 @@ describe('Zeta Ssp Bid Adapter', function() { } }, refererInfo: { - referer: 'zetaglobal.com' + referer: 'http://www.zetaglobal.com/page?param=value' }, gdprConsent: { gdprApplies: 1, @@ -68,6 +68,13 @@ describe('Zeta Ssp Bid Adapter', function() { expect(payload.user.ext.eids).to.eql(eids); }); + it('Test page and domain in site', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.site.page).to.eql('http://www.zetaglobal.com/page?param=value'); + expect(payload.site.domain).to.eql('zetaglobal.com'); + }); + it('Test the request processing function', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); expect(request).to.not.be.empty; From fbe94af1207143d91e4af30f7076297afad610e9 Mon Sep 17 00:00:00 2001 From: Filip Stamenkovic Date: Fri, 4 Jun 2021 19:11:42 +0200 Subject: [PATCH 1094/1476] PBS Bid Adapter: Fix duplicate imp.id (#6933) * pbs adapter in case of twin codes, add suffix to imp.id * add fix for the native assets cache * fix for impressionId generation --- modules/prebidServerBidAdapter/index.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8f443a28024..ad5cb70cc23 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -492,12 +492,24 @@ const OPEN_RTB_PROTOCOL = { const firstBidRequest = bidRequests[0]; // transform ad unit into array of OpenRTB impression objects + let impIds = new Set(); adUnits.forEach(adUnit => { + // 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', + // first imp will have id 'test' and second imp will have id 'test-2' + let impressionId = adUnit.code; + let i = 1; + while (impIds.has(impressionId)) { + i++; + impressionId = `${adUnit.code}-${i}`; + } + impIds.add(impressionId); + const nativeParams = processNativeAdUnitParams(utils.deepAccess(adUnit, 'mediaTypes.native')); let nativeAssets; if (nativeParams) { try { - nativeAssets = nativeAssetCache[adUnit.code] = Object.keys(nativeParams).reduce((assets, type) => { + nativeAssets = nativeAssetCache[impressionId] = Object.keys(nativeParams).reduce((assets, type) => { let params = nativeParams[type]; function newAsset(obj) { @@ -563,10 +575,9 @@ const OPEN_RTB_PROTOCOL = { const bannerParams = utils.deepAccess(adUnit, 'mediaTypes.banner'); adUnit.bids.forEach(bid => { - // OpenRTB response contains the adunit code and bidder name. These are + // OpenRTB response contains imp.id and bidder name. These are // combined to create a unique key for each bid since an id isn't returned - bidIdMap[`${adUnit.code}${bid.bidder}`] = bid.bid_id; - + bidIdMap[`${impressionId}${bid.bidder}`] = bid.bid_id; // check for and store valid aliases to add to the request if (adapterManager.aliasRegistry[bid.bidder]) { const bidder = adapterManager.bidderRegistry[bid.bidder]; @@ -647,7 +658,7 @@ const OPEN_RTB_PROTOCOL = { return acc; }, {...utils.deepAccess(adUnit, 'ortb2Imp.ext')}); - const imp = { id: adUnit.code, ext, secure: s2sConfig.secure }; + const imp = { id: impressionId, ext, secure: s2sConfig.secure }; const ortb2 = {...utils.deepAccess(adUnit, 'ortb2Imp.ext.data')}; Object.keys(ortb2).forEach(prop => { @@ -941,7 +952,7 @@ const OPEN_RTB_PROTOCOL = { } if (utils.isPlainObject(adm) && Array.isArray(adm.assets)) { - let origAssets = nativeAssetCache[bidRequest.adUnitCode]; + let origAssets = nativeAssetCache[bid.impid]; bidObject.native = utils.cleanObj(adm.assets.reduce((native, asset) => { let origAsset = origAssets[asset.id]; if (utils.isPlainObject(asset.img)) { @@ -997,7 +1008,7 @@ const OPEN_RTB_PROTOCOL = { bidObject.ttl = (bid.exp) ? bid.exp : configTtl; bidObject.netRevenue = (bid.netRevenue) ? bid.netRevenue : DEFAULT_S2S_NETREVENUE; - bids.push({ adUnit: bid.impid, bid: bidObject }); + bids.push({ adUnit: bidRequest.adUnitCode, bid: bidObject }); }); }); } From ab3450f448e6fd184ebac2a01779d4c7fa7d4271 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 4 Jun 2021 13:44:57 -0400 Subject: [PATCH 1095/1476] Update beachfrontBidAdapter.js (#6949) --- modules/beachfrontBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 43df7eef9ae..1bff77f29ce 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -78,7 +78,7 @@ export const spec = { cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, - creativeId: response.crid, + creativeId: response.crid || response.cmpId, meta: responseMeta, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, From d026a10c558f98d8b05faef818e233ec10e084ef Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 4 Jun 2021 14:47:54 -0400 Subject: [PATCH 1096/1476] Inskin, Consumable, FreewheelSSP Bid Adapters: Placeholder for advertiserDomains (#6950) * Update consumableBidAdapter.js * Update inskinBidAdapter.js * Update freewheel-sspBidAdapter.js * Update consumableBidAdapter_spec.js * Update freewheel-sspBidAdapter_spec.js * Update inskinBidAdapter_spec.js * Update consumableBidAdapter.js * Update inskinBidAdapter.js * Update freewheel-sspBidAdapter.js * Update consumableBidAdapter.js * Update freewheel-sspBidAdapter_spec.js --- modules/consumableBidAdapter.js | 1 + modules/freewheel-sspBidAdapter.js | 1 + modules/inskinBidAdapter.js | 1 + test/spec/modules/consumableBidAdapter_spec.js | 1 + test/spec/modules/freewheel-sspBidAdapter_spec.js | 8 ++++---- test/spec/modules/inskinBidAdapter_spec.js | 1 + 6 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 8eb56f7d0c2..92e2192b925 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -122,6 +122,7 @@ export const spec = { bid.currency = 'USD'; bid.creativeId = decision.adId; bid.ttl = 30; + bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] } bid.netRevenue = true; bid.referrer = bidRequest.bidderRequest.refererInfo.referer; diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index 1c9cd75f76f..fa2f7b4cd6b 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -420,6 +420,7 @@ export const spec = { currency: princingData.currency, netRevenue: true, ttl: 360, + meta: { advertiserDomains: princingData.adomain && utils.isArray(princingData.adomain) ? princingData.adomain : [] }, dealId: dealId, campaignId: campaignId, bannerId: bannerId diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js index 29040205818..5173f1dca63 100644 --- a/modules/inskinBidAdapter.js +++ b/modules/inskinBidAdapter.js @@ -169,6 +169,7 @@ export const spec = { bid.currency = 'USD'; bid.creativeId = decision.adId; bid.ttl = 360; + bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] } bid.netRevenue = true; bidResponses.push(bid); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 44076194885..f0b02913f96 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -299,6 +299,7 @@ describe('Consumable BidAdapter', function () { expect(b).to.have.property('currency', 'USD'); expect(b).to.have.property('creativeId'); expect(b).to.have.property('ttl', 30); + expect(b.meta).to.have.property('advertiserDomains'); expect(b).to.have.property('netRevenue', true); expect(b).to.have.property('referrer'); }); diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js index e416bd1c26b..a5b4bd2a03f 100644 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -368,7 +368,7 @@ describe('freewheelSSP BidAdapter Test', () => { ]; let result = spec.interpretResponse(response, request[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains).to.deep.equal([]); expect(result[0].dealId).to.equal('NRJ-PRO-00008'); expect(result[0].campaignId).to.equal('SMF-WOW-55555'); expect(result[0].bannerId).to.equal('12345'); @@ -395,7 +395,7 @@ describe('freewheelSSP BidAdapter Test', () => { ]; let result = spec.interpretResponse(response, request[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains).to.deep.equal([]); expect(result[0].dealId).to.equal('NRJ-PRO-00008'); expect(result[0].campaignId).to.equal('SMF-WOW-55555'); expect(result[0].bannerId).to.equal('12345'); @@ -522,7 +522,7 @@ describe('freewheelSSP BidAdapter Test', () => { ]; let result = spec.interpretResponse(response, request[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains).to.deep.equal([]); expect(result[0].dealId).to.equal('NRJ-PRO-00008'); expect(result[0].campaignId).to.equal('SMF-WOW-55555'); expect(result[0].bannerId).to.equal('12345'); @@ -551,7 +551,7 @@ describe('freewheelSSP BidAdapter Test', () => { ]; let result = spec.interpretResponse(response, request[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains).to.deep.equal([]); expect(result[0].dealId).to.equal('NRJ-PRO-00008'); expect(result[0].campaignId).to.equal('SMF-WOW-55555'); expect(result[0].bannerId).to.equal('12345'); diff --git a/test/spec/modules/inskinBidAdapter_spec.js b/test/spec/modules/inskinBidAdapter_spec.js index cedaff2a0cf..151cbce7692 100644 --- a/test/spec/modules/inskinBidAdapter_spec.js +++ b/test/spec/modules/inskinBidAdapter_spec.js @@ -328,6 +328,7 @@ describe('InSkin BidAdapter', function () { expect(b).to.have.property('currency', 'USD'); expect(b).to.have.property('creativeId'); expect(b).to.have.property('ttl', 360); + expect(b.meta).to.have.property('advertiserDomains'); expect(b).to.have.property('netRevenue', true); }); }); From 9c8706e26a9db7aa46baf134921a6203ff2352e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20DEYM=C3=88S?= <47388595+MaxSmileWanted@users.noreply.github.com> Date: Sat, 5 Jun 2021 12:05:16 +0200 Subject: [PATCH 1097/1476] Fixing issues related to Prebid 5.0 (#6953) --- modules/smilewantedBidAdapter.js | 39 ++++++++++++++++++- .../modules/smilewantedBidAdapter_spec.js | 6 +-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index fb05298a230..fad1aad194c 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -29,7 +29,6 @@ export const spec = { var payload = { zoneId: bid.params.zoneId, currencyCode: config.getConfig('currency.adServerCurrency') || 'EUR', - bidfloor: bid.params.bidfloor || 0.0, tagId: bid.adUnitCode, sizes: bid.sizes.map(size => ({ w: size[0], @@ -41,6 +40,15 @@ export const spec = { prebidVersion: '$prebid.version$' }; + const floor = getBidFloor(bid); + if (floor) { + payload.bidfloor = floor; + } + + if (bid.params.bidfloor) { + payload.bidfloor = bid.params.bidfloor; + } + if (bidderRequest && bidderRequest.refererInfo) { payload.pageDomain = bidderRequest.refererInfo.referer || ''; } @@ -70,6 +78,7 @@ export const spec = { try { if (response) { + const dealId = response.dealId || ''; const bidResponse = { requestId: JSON.parse(bidRequest.data).bidId, cpm: response.cpm, @@ -93,6 +102,14 @@ export const spec = { bidResponse['renderer'] = newRenderer(JSON.parse(bidRequest.data), response); } + if (dealId.length > 0) { + bidResponse.dealId = dealId; + } + + bidResponse.meta = {}; + if (response.meta && response.meta.advertiserDomains && utils.isArray(response.meta.advertiserDomains)) { + bidResponse.meta.advertiserDomains = response.meta.advertiserDomains; + } bidResponses.push(bidResponse); } } catch (error) { @@ -172,4 +189,24 @@ function outstreamRender(bid) { }); } +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + if (utils.isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })) + }); + if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') { + return parseFloat(floorInfo.floor); + } + } + return null; +} + registerBidder(spec); diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index d0d8b65a42f..da18a08ee9b 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -14,8 +14,7 @@ const DISPLAY_REQUEST = [{ ], bidder: 'smilewanted', params: { - zoneId: 1, - bidfloor: 2.50 + zoneId: 1 }, requestId: 'request_abcd1234', transactionId: 'trans_abcd1234' @@ -115,7 +114,6 @@ describe('smilewantedBidAdapterTests', function () { const requestDisplayContent = JSON.parse(requestDisplay[0].data); expect(requestDisplayContent).to.have.property('zoneId').and.to.equal(1); expect(requestDisplayContent).to.have.property('currencyCode').and.to.equal('EUR'); - expect(requestDisplayContent).to.have.property('bidfloor').and.to.equal(2.50); expect(requestDisplayContent).to.have.property('sizes'); expect(requestDisplayContent.sizes[0]).to.have.property('w').and.to.equal(300); expect(requestDisplayContent.sizes[0]).to.have.property('h').and.to.equal(250); @@ -129,7 +127,6 @@ describe('smilewantedBidAdapterTests', function () { const requestVideoInstreamContent = JSON.parse(requestVideoInstream[0].data); expect(requestVideoInstreamContent).to.have.property('zoneId').and.to.equal(2); expect(requestVideoInstreamContent).to.have.property('currencyCode').and.to.equal('EUR'); - expect(requestVideoInstreamContent).to.have.property('bidfloor').and.to.equal(2.50); expect(requestVideoInstreamContent).to.have.property('sizes'); expect(requestVideoInstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoInstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); @@ -141,7 +138,6 @@ describe('smilewantedBidAdapterTests', function () { const requestVideoOutstreamContent = JSON.parse(requestVideoOutstream[0].data); expect(requestVideoOutstreamContent).to.have.property('zoneId').and.to.equal(3); expect(requestVideoOutstreamContent).to.have.property('currencyCode').and.to.equal('EUR'); - expect(requestVideoOutstreamContent).to.have.property('bidfloor').and.to.equal(2.50); expect(requestVideoOutstreamContent).to.have.property('sizes'); expect(requestVideoOutstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoOutstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); From ea8cec461a234fe0bbc3a1f61b857dc62c32472b Mon Sep 17 00:00:00 2001 From: Eugeniy Orlov Date: Mon, 7 Jun 2021 16:52:02 +0300 Subject: [PATCH 1098/1476] Buzzoola Bid Adapter: add advertiser domain and native media type support (#6956) * add native and advertiser adomain support * fix tests * change context for testing video adunit * bidrequestid == requestid * fix outstream tests (#6956) * rollback context change (#6956) Co-authored-by: rpopov --- modules/buzzoolaBidAdapter.js | 6 +- modules/buzzoolaBidAdapter.md | 42 +++++- test/spec/modules/buzzoolaBidAdapter_spec.js | 148 ++++++++++++++++++- 3 files changed, 183 insertions(+), 13 deletions(-) diff --git a/modules/buzzoolaBidAdapter.js b/modules/buzzoolaBidAdapter.js index f87607657c3..db4863f7503 100644 --- a/modules/buzzoolaBidAdapter.js +++ b/modules/buzzoolaBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {OUTSTREAM} from '../src/video.js'; @@ -11,7 +11,7 @@ const RENDERER_SRC = 'https://tube.buzzoola.com/new/build/buzzlibrary.js'; export const spec = { code: BIDDER_CODE, aliases: ['buzzoolaAdapter'], - supportedMediaTypes: [BANNER, VIDEO], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Determines whether or not the given bid request is valid. @@ -21,7 +21,7 @@ export const spec = { */ isBidRequestValid: function (bid) { let types = bid.mediaTypes; - return !!(bid && bid.mediaTypes && (types.banner || types.video) && bid.params && bid.params.placementId); + return !!(bid && bid.mediaTypes && (types.banner || types.video || types.native) && bid.params && bid.params.placementId); }, /** diff --git a/modules/buzzoolaBidAdapter.md b/modules/buzzoolaBidAdapter.md index aec3eda6c58..87f5262af4f 100644 --- a/modules/buzzoolaBidAdapter.md +++ b/modules/buzzoolaBidAdapter.md @@ -26,7 +26,7 @@ var adUnits = [ bids: [{ bidder: 'buzzoola', params: { - placementId: 417846 + placementId: 417845 } }] }, @@ -45,7 +45,7 @@ var adUnits = [ bids: [{ bidder: 'buzzoola', params: { - placementId: 417845 + placementId: 417846 } }] }, @@ -67,6 +67,44 @@ var adUnits = [ placementId: 417845 } }] + }, + // Native adUnit + { + code: '/21737252144/prebid_test_native', + mediaTypes: { + native: { + image: { + required: true, + sizes: [640, 134] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + } + } + }, + bids: [{ + bidder: 'buzzoola', + params: { + placementId: 417845 + } + }] } ]; ``` diff --git a/test/spec/modules/buzzoolaBidAdapter_spec.js b/test/spec/modules/buzzoolaBidAdapter_spec.js index 8a04999219d..312441c4202 100644 --- a/test/spec/modules/buzzoolaBidAdapter_spec.js +++ b/test/spec/modules/buzzoolaBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/buzzoolaBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; +import '../../../src/prebid.js'; import {executeRenderer, Renderer} from '../../../src/Renderer.js'; import {deepClone} from '../../../src/utils.js'; @@ -54,7 +55,12 @@ const BANNER_RESPONSE = [{ 'netRevenue': true, 'ttl': 10800, 'ad': '
', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': [ + 'buzzoola.com' + ] + } }]; const REQUIRED_BANNER_FIELDS = [ @@ -67,7 +73,8 @@ const REQUIRED_BANNER_FIELDS = [ 'creativeId', 'netRevenue', 'currency', - 'mediaType' + 'mediaType', + 'meta' ]; const VIDEO_BID = { @@ -92,17 +99,22 @@ const VIDEO_BID_REQUEST = { const VIDEO_RESPONSE = [{ 'requestId': '325a54271dc40a', - 'cpm': 4.6158956756756755, + 'cpm': 5.528554074074074, 'width': 640, - 'height': 380, + 'height': 480, 'creativeId': '11774', 'dealId': '', 'currency': 'RUB', 'netRevenue': true, 'ttl': 10800, 'ad': '{"crs":[{"advertiser_id":165,"title":"qa//PrebidJStestVideoURL","description":"qa//PrebidJStest","duration":0,"ya_id":"55038886","raw_content":"{\\"main_content\\": \\"https://tube.buzzoola.com/xstatic/o42/mcaug/2.mp4\\"}","content":{"main_content":"https://tube.buzzoola.com/xstatic/o42/mcaug/2.mp4"},"content_type":"video_url","sponsor_link":"","sponsor_name":"","overlay":"","overlay_start_after":0,"overlay_close_after":0,"action_button_title":"","tracking_url":{},"iframe_domains":[],"soc_share_url":"https://tube.buzzoola.com/share.html","player_show_skip_button_before_play":false,"player_show_skip_button_seconds":5,"player_show_title":true,"player_data_attributes":{"expandable":"default","overroll":"default"},"click_event_view":"default","share_panel_position":"left","auto_play":true,"logo_url":{},"share_buttons":["vkontakte","facebook","twitter","moimir","odnoklassniki","embed"],"player_show_panels":false,"thumbnail":"","tracking_js":{},"click_event_url":"https://exchange.buzzoola.com/event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/click/0/","vpaid_js_url":"https://tube.buzzoola.com/new/js/lib/vpaid_js_proxy.js","skip_clickthru":false,"landing_link_text":"","sound_enabled_by_default":false,"landing_link_position":"right","displayed_price":"","js_wrapper_url":"","enable_moat":false,"branding_template":"","event_url":"https://exchange.buzzoola.com/event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/","resend_event_url":"https://exchange.buzzoola.com/resend_event/f9382ceb-49c2-4683-50d8-5c516c53cd69/14795a96-6261-49dc-7241-207333ab1490/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm/","creative_hash":"m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpIERGosa1adogXgqjDml4Pm","custom_html":"","custom_js":"","height":0,"width":0,"campaign_id":5758,"line_item_id":17319,"creative_id":11774,"extra":{"imp_id":"14795a96-6261-49dc-7241-207333ab1490","rtime":"2019-08-27 13:58:36"},"subcontent":"vast","auction_settings":{"price":"4.6158956756756755","currency":"RUB","event_name":"player_seen","time_slice":0},"hash_to_embed":"kbDH64c7yFYkSu0KCwSkoUD2bNHAnUTHBERqLGtWnaIF4Kow5peD5g","need_ad":false}],"tracking_urls":{"ctor":["https://www.tns-counter.ru/V13a****buzzola_com/ru/CP1251/tmsec=buzzola_total/1322650417245790778","https://www.tns-counter.ru/V13a****buzzoola_kz/ru/UTF-8/tmsec=buzzoola_video/5395765100939533275","https://buzzoolaru.solution.weborama.fr/fcgi-bin/dispatch.fcgi?a.A=ev&a.si=3071&a.te=37&a.aap=1&a.agi=862&a.evn=PrebidJS.test&g.ra=4581478478720298652","https://x01.aidata.io/0.gif?pid=BUZZOOLA&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://top-fwz1.mail.ru/counter?id=3026769","https://www.tns-counter.ru/V13a****buzzola_com/ru/UTF-8/tmsec=buzzola_inread/542059452789128996","https://dm.hybrid.ai/match?id=111&vid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://px.adhigh.net/p/cm/buzzoola?u=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://ssp1.rtb.beeline.ru/userbind?src=buz&ssp_user_id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sync.upravel.com/image?source=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://relap.io/api/partners/bzcs.gif?uid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://x.bidswitch.net/sync?ssp=sspicyads","https://inv-nets.admixer.net/adxcm.aspx?ssp=3C5173FC-CA30-4692-9116-009C19CB1BF9&rurl=%2F%2Fexchange.buzzoola.com%2Fcookiesync%2Fdsp%2Fadmixer-video%2F%24%24visitor_cookie%24%24","https://sync.datamind.ru/cookie/accepter?source=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://dmp.vihub.ru/match?sysid=buz&redir=no&uid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://ad.adriver.ru/cgi-bin/rle.cgi?sid=1&ad=608223&bt=21&pid=2551979&bid=6150299&bn=6150299&rnd=1279444531737367663","https://reichelcormier.bid/point/?method=match&type=ssp&key=4677290772f9000878093d69c199bfba&id=3509&extUid=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sync.republer.com/match?src=buzzoola&id=dbdb5b13-e719-4987-7f6a-a882322bbfce","https://sm.rtb.mts.ru/p?id=dbdb5b13-e719-4987-7f6a-a882322bbfce&ssp=buzzoola","https://cm.mgid.com/m?cdsp=371151&adu=https%3A%2F%2Fexchange.buzzoola.com%2Fcookiesync%2Fdsp%2Fmarketgid-native%2F%7Bmuidn%7D","https://dmp.gotechnology.io/dmp/syncsspdmp?sspid=122258"]},"tracking_js":{"ctor":["https://buzzoola.fraudscore.mobi/dooJ9sheeeDaZ3fe.js?s=268671&l=417845"]},"placement":{"placement_id":417845,"unit_type":"inread","unit_settings":{"align":"left","autoplay_enable_sound":false,"creatives_amount":1,"debug_mode":false,"expandable":"never","sound_control":"default","target":"","width":"100%"},"unit_settings_list":["width","sound_control","debug_mode","target","creatives_amount","expandable","container_height","align","height"]},"uuid":"dbdb5b13-e719-4987-7f6a-a882322bbfce","auction_id":"f9382ceb-49c2-4683-50d8-5c516c53cd69","env":"prod"}', - 'vastXml': '\n00:00:30', - 'mediaType': 'video' + 'vastUrl': 'https://exchange.buzzoola.com/prebid/adm/6cfa2ee1-f001-4fab-5582-a62eaee46205/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75R6wziBhL0JAERGosa1adogXgqjDml4Pm/?auction_id=92702ce1-2328-4c7a-57aa-41c738e8bb75', + 'mediaType': 'video', + 'meta': { + 'advertiserDomains': [ + 'buzzoola.com' + ] + } }]; const RENDERER_DATA = { @@ -121,8 +133,107 @@ const REQUIRED_VIDEO_FIELDS = [ 'creativeId', 'netRevenue', 'currency', - 'vastXml', - 'mediaType' + 'vastUrl', + 'mediaType', + 'meta' +]; + +const NATIVE_BID = { + 'bidder': 'buzzoola', + 'params': {'placementId': 417845}, + 'mediaTypes': { + 'native': { + 'image': { + 'required': true, + 'sizes': [640, 134] + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'privacyLink': { + 'required': false + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [50, 50] + } + } + }, + 'bidId': '22a42cd3522c6f' +}; + +const NATIVE_BID_REQUEST = { + bidderCode: 'buzzoola', + bids: [NATIVE_BID] +}; + +const NATIVE_RESPONSE = [{ + 'requestId': '22a42cd3522c6f', + 'cpm': 6.553015238095238, + 'width': 600, + 'height': 300, + 'creativeId': '17970', + 'dealId': '', + 'currency': 'RUB', + 'netRevenue': true, + 'ttl': 10800, + 'ad': 'https://tube.buzzoola.com/xstatic/o42/stoloto/6', + 'mediaType': 'native', + 'native': { + 'body': 'В 1388-м тираже «Русского лото» джекпот', + 'clickTrackers': [ + 'https://exchange.buzzoola.com/event/6cee890f-1878-4a37-46b3-0107b6c590ae/a1aedc5b-50f2-4a7c-6d24-e235bb1f87ed/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpJwP8cy-zNH8GX-_nWFkILh/click/' + ], + 'clickUrl': 'https://ad.doubleclick.net/ddm/trackclk/N250204.3446512BUZZOOLA/B25801892.303578321;dc_trk_aid=496248119;dc_trk_cid=151207455;dc_lat=;dc_rdid=;tag_for_child_directed_treatment=;tfua=;ltd=?https://stoloto.onelink.me/mEJM?pid=Buzzoola_mb4&c=rl_10_05_2021&is_retargeting=true&af_ios_url=https%3A%2F%2Fapps.apple.com%2Fru%2Fapp%2F%25D1%2581%25D1%2582%25D0%25BE%25D0%25BB%25D0%25BE%25D1%2582%25D0%25BE-%25D1%2583-%25D0%25BD%25D0%25B0%25D1%2581-%25D0%25B2%25D1%258B%25D0%25B8%25D0%25B3%25D1%2580%25D1%258B%25D0%25B2%25D0%25B0%25D1%258E%25D1%2582%2Fid579961527&af_android_url=https%3A%2F%2Fgalaxystore.samsung.com%2Fdetail%2Fru.stoloto.mobile&af_dp=stolotoone%3A%2F%2Fgames&af_web_dp=https%3A%2F%2Fwww.stoloto.ru%2Fruslotto%2Fgame%3Flastdraw%3Fad%3Dbuzzoola_app_dx_rl_10_05_2021%26utm_source%3Dbuzzoola_app_dx%26utm_medium%3Dcpm%26utm_campaign%3Drl_10_05_2021%26utm_content%3Dbuzzoola_app_dx_mob_native_ios_mb4%26utm_term%3D__6ple2-9znjyg_', + 'icon': { + 'height': '100', + 'url': 'https://tube.buzzoola.com/xstatic/o42/stoloto/logo3.png', + 'width': '100' + }, + 'image': { + 'height': '450', + 'url': 'https://tube.buzzoola.com/xstatic/o42/stoloto/6/16x9.png', + 'width': '800' + }, + 'impressionTrackers': [ + 'https://exchange.buzzoola.com/event/6cee890f-1878-4a37-46b3-0107b6c590ae/a1aedc5b-50f2-4a7c-6d24-e235bb1f87ed/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpJwP8cy-zNH8GX-_nWFkILh/ctor/', + 'https://exchange.buzzoola.com/event/6cee890f-1878-4a37-46b3-0107b6c590ae/a1aedc5b-50f2-4a7c-6d24-e235bb1f87ed/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpJwP8cy-zNH8GX-_nWFkILh/impression/?price=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}', + 'https://exchange.buzzoola.com/event/6cee890f-1878-4a37-46b3-0107b6c590ae/a1aedc5b-50f2-4a7c-6d24-e235bb1f87ed/m7JVQI9Y7J35_gEDugNO2bIiP2qTqPKfuLrqqh_LoJu0tD6PoLEglMXUBzVpSg75c-unsaijXpJwP8cy-zNH8GX-_nWFkILh/player_seen/', + 'https://cr.frontend.weborama.fr/cr?key=mailru&url=https%3A%2F%2Fad.mail.ru%2Fcm.gif%3Fp%3D68%26id%3D%7BWEBO_CID%7D' + ], + 'sponsoredBy': 'Buzzoola', + 'title': 'Test PrebidJS Native' + }, + 'meta': { + 'advertiserDomains': [ + 'buzzoola.com' + ] + } +}]; + +const REQUIRED_NATIVE_FIELDS = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'ad', + 'native', + 'ttl', + 'creativeId', + 'netRevenue', + 'currency', + 'mediaType', + 'meta' ]; describe('buzzoolaBidAdapter', () => { @@ -149,18 +260,22 @@ describe('buzzoolaBidAdapter', () => { describe('buildRequests', () => { let videoBidRequests = [VIDEO_BID]; let bannerBidRequests = [BANNER_BID]; + let nativeBidRequests = [NATIVE_BID]; const bannerRequest = spec.buildRequests(bannerBidRequests, BANNER_BID_REQUEST); + const nativeRequest = spec.buildRequests(nativeBidRequests, NATIVE_BID_REQUEST); const videoRequest = spec.buildRequests(videoBidRequests, VIDEO_BID_REQUEST); it('sends bid request to ENDPOINT via POST', () => { expect(videoRequest.method).to.equal('POST'); expect(bannerRequest.method).to.equal('POST'); + expect(nativeRequest.method).to.equal('POST'); }); it('sends bid request to correct ENDPOINT', () => { expect(videoRequest.url).to.equal(ENDPOINT); expect(bannerRequest.url).to.equal(ENDPOINT); + expect(nativeRequest.url).to.equal(ENDPOINT); }); it('sends correct video bid parameters', () => { @@ -170,6 +285,10 @@ describe('buzzoolaBidAdapter', () => { it('sends correct banner bid parameters', () => { expect(bannerRequest.data).to.deep.equal(BANNER_BID_REQUEST); }); + + it('sends correct native bid parameters', () => { + expect(nativeRequest.data).to.deep.equal(NATIVE_BID_REQUEST); + }); }); describe('interpretResponse', () => { @@ -201,6 +320,10 @@ describe('buzzoolaBidAdapter', () => { nobidServerResponseCheck(BANNER_BID_REQUEST); }); + it('handles native nobid responses', () => { + nobidServerResponseCheck(NATIVE_BID_REQUEST); + }); + it('handles video empty responses', () => { nobidServerResponseCheck(VIDEO_BID_REQUEST, emptyResponse); }); @@ -209,6 +332,10 @@ describe('buzzoolaBidAdapter', () => { nobidServerResponseCheck(BANNER_BID_REQUEST, emptyResponse); }); + it('handles native empty responses', () => { + nobidServerResponseCheck(NATIVE_BID_REQUEST, emptyResponse); + }); + it('should get correct video bid response', () => { bidServerResponseCheck(VIDEO_RESPONSE, VIDEO_BID_REQUEST, REQUIRED_VIDEO_FIELDS); }); @@ -216,6 +343,10 @@ describe('buzzoolaBidAdapter', () => { it('should get correct banner bid response', () => { bidServerResponseCheck(BANNER_RESPONSE, BANNER_BID_REQUEST, REQUIRED_BANNER_FIELDS); }); + + it('should get correct native bid response', () => { + bidServerResponseCheck(NATIVE_RESPONSE, NATIVE_BID_REQUEST, REQUIRED_NATIVE_FIELDS); + }); }); describe('outstream renderer', () => { @@ -268,6 +399,7 @@ describe('buzzoolaBidAdapter', () => { }; const spy = sinon.spy(window.Buzzoola.Core, 'install'); executeRenderer(renderer, result); + renderer.callback(); expect(spy.called).to.be.true; const spyCall = spy.getCall(0); From 20e0ef807cab2581e5d8ccf9f477e35b3cc3e5e5 Mon Sep 17 00:00:00 2001 From: lowendavid <66423906+lowendavid@users.noreply.github.com> Date: Mon, 7 Jun 2021 15:59:45 +0200 Subject: [PATCH 1099/1476] Smart Ad Server: add advertiser domain support and read more mediaTypes.video params (#6944) * SmartAdServer-Version5.0Compliance adomain added * Updating prebid to 5.0 now lets test. * Improving the switch case * Fixing tests. * Fixing startdelay default val and remove comment * Removed wrong comment and protocol to protocols * Making sure the value we math.Max is valid * checked protocol valid & default val added * Default protocol is null * easier to read code * added assignement of protocols = * Added tests and fixed adapter thanks to test * added outstream test * removing minor extra character --- modules/smartadserverBidAdapter.js | 28 +- .../modules/smartadserverBidAdapter_spec.js | 248 ++++++++++++++++++ 2 files changed, 273 insertions(+), 3 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index bb9364c72c3..54c58084dff 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -86,15 +86,36 @@ export const spec = { h: size[1] })); } else if (videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream')) { + // use IAB ORTB values if the corresponding values weren't already set by bid.params.video + // Assign a default protocol, the highest value possible means we are retrocompatible with all older values. + var protocol = null; + if (bid.params.video && bid.params.video.protocol) { + protocol = bid.params.video.protocol; + } else if (Array.isArray(videoMediaType.protocols)) { + protocol = Math.max.apply(Math, videoMediaType.protocols); + } + + // Default value for all exotic cases set to bid.params.video.startDelay midroll hence 2. + var startDelay = 2; + if (bid.params.video && bid.params.video.startDelay) { + startDelay = bid.params.video.startDelay + } else if (videoMediaType.startdelay == 0) { + startDelay = 1; + } else if (videoMediaType.startdelay == -1) { + startDelay = 2; + } else if (videoMediaType.startdelay == -2) { + startDelay = 3; + } + // Specific attributes for instream. let playerSize = videoMediaType.playerSize[0]; payload.isVideo = videoMediaType.context === 'instream'; payload.mediaType = VIDEO; payload.videoData = { - videoProtocol: bid.params.video.protocol, + videoProtocol: protocol, playerWidth: playerSize[0], playerHeight: playerSize[1], - adBreak: bid.params.video.startDelay || 1 + adBreak: startDelay }; } else { return {}; @@ -148,7 +169,8 @@ export const spec = { currency: response.currency, netRevenue: response.isNetCpm, ttl: response.ttl, - dspPixels: response.dspPixels + dspPixels: response.dspPixels, + meta: { advertiserDomains: response.adomain ? response.adomain : [] } }; if (bidRequest.mediaType === VIDEO) { diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 749de43b9af..f9206740535 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -539,6 +539,130 @@ describe('Smart bid adapter tests', function () { expect(request[0]).to.be.empty; expect(request[1]).to.not.be.empty; }); + + describe('Instream videoData meta & params tests', function () { + it('Verify videoData assigns values from meta', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]], // It seems prebid.js transforms the player size array into an array of array... + protocols: [8, 2], + startdelay: 0 + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(8); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(1); + }); + + it('Verify videoData default values assigned', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]] // It seems prebid.js transforms the player size array into an array of array... + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); + }); + + it('Verify videoData params override meta values', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]], // It seems prebid.js transforms the player size array into an array of array... + protocols: [8, 2], + startdelay: 0 + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + video: { + protocol: 6, + startDelay: 3 + } + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); + }); + }); }); describe('Outstream video tests', function () { @@ -644,6 +768,130 @@ describe('Smart bid adapter tests', function () { }); }); + describe('Outstream videoData meta & params tests', function () { + it('Verify videoData assigns values from meta', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], // It seems prebid.js transforms the player size array into an array of array... + protocols: [8, 2], + startdelay: 0 + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid-outstream', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(8); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(1); + }); + + it('Verify videoData default values assigned', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]] // It seems prebid.js transforms the player size array into an array of array... + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid-outstream', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); + }); + + it('Verify videoData params override meta values', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'EUR' + } + }); + const request = spec.buildRequests([{ + adUnitCode: 'sas_42', + bidId: 'abcd1234', + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], // It seems prebid.js transforms the player size array into an array of array... + protocols: [8, 2], + startdelay: 0 + } + }, + params: { + siteId: '1234', + pageId: '5678', + formatId: '90', + target: 'test=prebid-outstream', + bidfloor: 0.420, + buId: '7569', + appName: 'Mozilla', + ckId: 42, + video: { + protocol: 6, + startDelay: 3 + } + }, + requestId: 'efgh5678', + transactionId: 'zsfgzzg' + }]); + + 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'); + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); + }); + }); + describe('External ids tests', function () { it('Verify external ids in request and ids found', function () { config.setConfig({ From d1ec22edf6c7172031503f618701c92c4a5b95d4 Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Mon, 7 Jun 2021 17:27:35 +0200 Subject: [PATCH 1100/1476] Pass parameter from Livewrapped wrapper (#6954) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter * Collect info on ad units receiving any valid bid * Support for ID5 Pass metadata from adapter * Typo in test + eids on wrong level * Fix for Prebid 3.0 * Fix get referer * http -> https in tests * Native support * Read sizes from mediatype.banner * Revert accidental commit * Support native data collection + minor refactorings * Set analytics endpoint * Support for app parameters * Fix issue where adunits with bids were not counted on reload * Send debug info from adapter to external debugger * SChain support * Send GDPR data in analytics request * video support Video support * Report back floor via analytic * Send auction id and adunit/bidder connection id * Criteo id support * Updated example * livewrapped Analytics Adapter info file * Livewrapped gvlid * Pass parameter from wrapper --- modules/livewrappedAnalyticsAdapter.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index a872a709ec9..29b57e3dd97 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -58,7 +58,8 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE lwFloor: lwFloor, floorData: bidRequest.floorData, auc: bidRequest.auc, - buc: bidRequest.buc + buc: bidRequest.buc, + lw: bidRequest.lw } utils.logInfo(bidRequest); @@ -83,6 +84,7 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE cache.auctions[args.auctionId].bidAdUnits[bidResponse.adUnit] = { sent: 0, + lw: bidResponse.lw, timeStamp: cache.auctions[args.auctionId].timeStamp }; } @@ -184,7 +186,8 @@ function getSentRequests() { floor: bid.lwFloor, auctionId: auctionIdPos, auc: bid.auc, - buc: bid.buc + buc: bid.buc, + lw: bid.lw }); } }); @@ -220,7 +223,8 @@ function getResponses(gdpr, auctionIds) { floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, auctionId: auctionIdPos, auc: bid.auc, - buc: bid.buc + buc: bid.buc, + lw: bid.lw }); } }); @@ -255,7 +259,8 @@ function getWins(gdpr, auctionIds) { floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, auctionId: auctionIdPos, auc: bid.auc, - buc: bid.buc + buc: bid.buc, + lw: bid.lw }); } }); @@ -312,7 +317,8 @@ function getTimeouts(auctionIds) { timeStamp: auction.timeStamp, auctionId: auctionIdPos, auc: bid.auc, - buc: bid.buc + buc: bid.buc, + lw: bid.lw }); } }); @@ -333,7 +339,8 @@ function getbidAdUnits() { bidAdUnits.push({ adUnit: adUnit, - timeStamp: bidAdUnit.timeStamp + timeStamp: bidAdUnit.timeStamp, + lw: bidAdUnit.lw }); } }); From fb723008f39726a9be0bb4f33b406497a48f6b5e Mon Sep 17 00:00:00 2001 From: Yohan Boutin Date: Mon, 7 Jun 2021 17:49:29 +0200 Subject: [PATCH 1101/1476] Seedtag : add requestCount to bid request (#6958) * add requestCount per adunit to the bidRequest * use internal bidderRequestsCount * fix unit test --- modules/seedtagBidAdapter.js | 52 ++++++++++----------- test/spec/modules/seedtagBidAdapter_spec.js | 2 + 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index f4e99a2e2a0..0acf1a8ff97 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -66,35 +66,34 @@ function hasMandatoryVideoParams(bid) { videoParams.playerSize.length > 0; } -function buildBidRequests(validBidRequests) { - return utils._map(validBidRequests, function(validBidRequest) { - const params = validBidRequest.params; - const mediaTypes = utils._map( - Object.keys(validBidRequest.mediaTypes), - function(pbjsType) { - return mediaTypesMap[pbjsType]; - } - ); +function buildBidRequest(validBidRequest) { + const params = validBidRequest.params; + const mediaTypes = utils._map( + Object.keys(validBidRequest.mediaTypes), + function (pbjsType) { + return mediaTypesMap[pbjsType]; + } + ); - const bidRequest = { - id: validBidRequest.bidId, - transactionId: validBidRequest.transactionId, - sizes: validBidRequest.sizes, - supplyTypes: mediaTypes, - adUnitId: params.adUnitId, - placement: params.placement, - }; + const bidRequest = { + id: validBidRequest.bidId, + transactionId: validBidRequest.transactionId, + sizes: validBidRequest.sizes, + supplyTypes: mediaTypes, + adUnitId: params.adUnitId, + placement: params.placement, + requestCount: validBidRequest.bidderRequestsCount || 1 // FIXME : in unit test the parameter bidderRequestsCount is undefined + }; - if (params.adPosition) { - bidRequest.adPosition = params.adPosition; - } + if (params.adPosition) { + bidRequest.adPosition = params.adPosition; + } - if (hasVideoMediaType(validBidRequest)) { - bidRequest.videoParams = getVideoParams(validBidRequest) - } + if (hasVideoMediaType(validBidRequest)) { + bidRequest.videoParams = getVideoParams(validBidRequest) + } - return bidRequest; - }) + return bidRequest; } /** @@ -160,7 +159,6 @@ export const spec = { code: BIDDER_CODE, aliases: [SEEDTAG_ALIAS], supportedMediaTypes: [BANNER, VIDEO], - /** * Determines whether or not the given bid request is valid. * @@ -187,7 +185,7 @@ export const spec = { timeout: bidderRequest.timeout, version: '$prebid.version$', connectionType: getConnectionType(), - bidRequests: buildBidRequests(validBidRequests) + bidRequests: utils._map(validBidRequests, buildBidRequest) }; if (payload.cmp) { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index cb04ada59c7..95fba601ecb 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -291,6 +291,7 @@ describe('Seedtag Adapter', function() { expect(bannerBid.sizes[0][1]).to.equal(250) expect(bannerBid.sizes[1][0]).to.equal(300) expect(bannerBid.sizes[1][1]).to.equal(600) + expect(bannerBid.requestCount).to.equal(1) }) it('should request an InStream Video', function() { const videoBid = bidRequests[1] @@ -307,6 +308,7 @@ describe('Seedtag Adapter', function() { expect(videoBid.sizes[0][1]).to.equal(250) expect(videoBid.sizes[1][0]).to.equal(300) expect(videoBid.sizes[1][1]).to.equal(600) + expect(videoBid.requestCount).to.equal(1) }) }) }) From 9ed06b45dc4cc86c3165a6a42368d5d379aee7f8 Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Mon, 7 Jun 2021 21:01:47 +0400 Subject: [PATCH 1102/1476] Dspx Bid Adapter: new features and support of adomain in meta (#6931) * DSPX prebid adapter: add userId(uid2, netId) support * Dspx Bid Adapter: set priority for banner type * Add meta.advertiserDomains support * Add meta.advertiserDomains support * Add extra tests * Add extra tests Co-authored-by: Alexander --- modules/dspxBidAdapter.js | 90 ++++++++++++++++++++---- test/spec/modules/dspxBidAdapter_spec.js | 35 ++++++--- 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index dd49a744225..447ba77be68 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -19,10 +19,6 @@ export const spec = { return validBidRequests.map(bidRequest => { const params = bidRequest.params; - const videoData = utils.deepAccess(bidRequest, 'mediaTypes.video') || {}; - const sizes = utils.parseSizesInput(videoData.playerSize || bidRequest.sizes)[0]; - const [width, height] = sizes.split('x'); - const placementId = params.placement; const rnd = Math.floor(Math.random() * 99999999999); const referrer = bidderRequest.refererInfo.referer; @@ -32,26 +28,28 @@ export const spec = { let endpoint = isDev ? ENDPOINT_URL_DEV : ENDPOINT_URL; let payload = {}; - if (isVideoRequest(bidRequest)) { - let vastFormat = params.vastFormat || DEFAULT_VAST_FORMAT; + if (isBannerRequest(bidRequest)) { + let size = getBannerSizes(bidRequest)[0]; payload = { - _f: vastFormat, + _f: 'html', alternative: 'prebid_js', inventory_item_id: placementId, - srw: width, - srh: height, + srw: size.width, + srh: size.height, idt: 100, rnd: rnd, ref: referrer, bid_id: bidId, }; } else { + let size = getVideoSizes(bidRequest)[0]; + let vastFormat = params.vastFormat || DEFAULT_VAST_FORMAT; payload = { - _f: 'html', + _f: vastFormat, alternative: 'prebid_js', inventory_item_id: placementId, - srw: width, - srh: height, + srw: size.width, + srh: size.height, idt: 100, rnd: rnd, ref: referrer, @@ -86,6 +84,14 @@ export const spec = { if (isDev) { payload.prebidDevMode = 1; } + + if (bidRequest.userId && bidRequest.userId.netId) { + payload.did_netid = bidRequest.userId.netId; + } + if (bidRequest.userId && bidRequest.userId.uid2) { + payload.did_uid2 = bidRequest.userId.uid2; + } + return { method: 'GET', url: endpoint, @@ -112,7 +118,10 @@ export const spec = { currency: currency, netRevenue: netRevenue, type: response.type, - ttl: config.getConfig('_bidderTimeout') + ttl: config.getConfig('_bidderTimeout'), + meta: { + advertiserDomains: response.adomain || [] + } }; if (response.vastXml) { bidResponse.vastXml = response.vastXml; @@ -120,6 +129,7 @@ export const spec = { } else { bidResponse.ad = response.adTag; } + bidResponses.push(bidResponse); } return bidResponses; @@ -178,6 +188,16 @@ function objectToQueryString(obj, prefix) { return str.join('&'); } +/** + * Check if it's a banner bid request + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {boolean} True if it's a banner bid + */ +function isBannerRequest(bid) { + return bid.mediaType === 'banner' || !!utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoRequest(bid); +} + /** * Check if it's a video bid request * @@ -188,4 +208,48 @@ function isVideoRequest(bid) { return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); } +/** + * Get video sizes + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {object} True if it's a video bid + */ +function getVideoSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); +} + +/** + * Get banner sizes + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {object} True if it's a video bid + */ +function getBannerSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); +} + +/** + * Parse size + * @param sizes + * @returns {width: number, h: height} + */ +function parseSize(size) { + let sizeObj = {} + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + return sizeObj; +} + +/** + * Parse sizes + * @param sizes + * @returns {{width: number , height: number }[]} + */ +function parseSizes(sizes) { + if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) + return sizes.map(size => parseSize(size)); + } + return [parseSize(sizes)]; // or a single one ? (ie. [728,90]) +} + registerBidder(spec); diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js index 87752f7747a..a4abf46e90f 100644 --- a/test/spec/modules/dspxBidAdapter_spec.js +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -61,7 +61,11 @@ describe('dspxAdapter', function () { ], 'bidId': '30b31c1838de1e1', 'bidderRequestId': '22edbae2733bf61', - 'auctionId': '1d1a030790a475' + 'auctionId': '1d1a030790a475', + 'userId': { + 'netId': '123', + 'uid2': '456' + } }, { 'bidder': 'dspx', @@ -102,9 +106,18 @@ describe('dspxAdapter', function () { 'placement': '101', 'devMode': true }, - 'sizes': [ - [300, 250] - ], + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + }, + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'bidId': '30b31c1838de1e4', 'bidderRequestId': '22edbae2733bf67', 'auctionId': '1d1a030790a478' @@ -145,7 +158,7 @@ describe('dspxAdapter', function () { expect(request1.method).to.equal('GET'); expect(request1.url).to.equal(ENDPOINT_URL); let data = request1.data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); - expect(data).to.equal('_f=html&alternative=prebid_js&inventory_item_id=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bprivate_auction%5D=0&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop'); + expect(data).to.equal('_f=html&alternative=prebid_js&inventory_item_id=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bprivate_auction%5D=0&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop&did_netid=123&did_uid2=456'); }); var request2 = spec.buildRequests([bidRequests[1]], bidderRequest)[0]; @@ -199,7 +212,8 @@ describe('dspxAdapter', function () { 'currency': 'EUR', 'ttl': 60, 'netRevenue': true, - 'zone': '6682' + 'zone': '6682', + 'adomain': ['bdomain'] } }; let serverVideoResponse = { @@ -229,7 +243,8 @@ describe('dspxAdapter', function () { netRevenue: true, ttl: 300, type: 'sspHTML', - ad: '' + ad: '', + meta: {advertiserDomains: ['bdomain']} }, { requestId: '23beaa6af6cdde', cpm: 0.5, @@ -242,7 +257,8 @@ describe('dspxAdapter', function () { ttl: 300, type: 'vast2', vastXml: '{"reason":7001,"status":"accepted"}', - mediaType: 'video' + mediaType: 'video', + meta: {advertiserDomains: []} }]; it('should get the correct bid response by display ad', function () { @@ -255,6 +271,8 @@ describe('dspxAdapter', function () { }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains.length).to.equal(1); + expect(result[0].meta.advertiserDomains[0]).to.equal(expectedResponse[0].meta.advertiserDomains[0]); }); it('should get the correct dspx video bid response by display ad', function () { @@ -273,6 +291,7 @@ describe('dspxAdapter', function () { }]; let result = spec.interpretResponse(serverVideoResponse, bidRequest[0]); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[1])); + expect(result[0].meta.advertiserDomains.length).to.equal(0); }); it('handles empty bid response', function () { From 4ff92fabf1472d2f61cc19fdc0f7286794d54abe Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 7 Jun 2021 14:20:40 -0400 Subject: [PATCH 1103/1476] Mediaforce Bid Adapter: support for adomain (#6961) * Update mediaforceBidAdapter.js * Update mediaforceBidAdapter_spec.js * Update mediaforceBidAdapter.js * Update mediaforceBidAdapter.js --- modules/mediaforceBidAdapter.js | 3 +++ test/spec/modules/mediaforceBidAdapter_spec.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index e03423f36ac..bd7e7f74f1a 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -219,6 +219,9 @@ export const spec = { currency: cur, netRevenue: true, ttl: serverBid.ttl || 300, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, burl: serverBid.burl, }; if (serverBid.dealid) { diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index 24326afe5c0..be9b528aedd 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -391,6 +391,7 @@ describe('mediaforce bid adapter', function () { mediaType: BANNER, requestId: bid.impid, ttl: 300, + meta: { advertiserDomains: [] }, width: bid.w, }])); }); @@ -477,6 +478,7 @@ describe('mediaforce bid adapter', function () { mediaType: NATIVE, requestId: bid.impid, ttl: 300, + meta: { advertiserDomains: [] }, }])); }); }); @@ -560,6 +562,7 @@ describe('mediaforce bid adapter', function () { mediaType: NATIVE, requestId: bid.impid, ttl: 300, + meta: { advertiserDomains: [] }, }])); }); }); From 8cac6d7b7dc202ea48a1de4cf03a5d84e689d6b1 Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Mon, 7 Jun 2021 21:57:17 +0300 Subject: [PATCH 1104/1476] Nextmillinnium adapter improvement (#6957) * change structure of request and response * fixes * remove logInfo * update doc --- modules/nextMillenniumBidAdapter.js | 72 ++++++++----------- modules/nextMillenniumBidAdapter.md | 4 +- .../modules/nextMillenniumBidAdapter_spec.js | 44 ++++++------ 3 files changed, 56 insertions(+), 64 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index af1f0562ba4..86da82753b6 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -3,8 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'nextMillennium'; -const HOST = 'https://brainlyads.com'; -const CURRENCY = 'USD'; +const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TIME_TO_LIVE = 360; export const spec = { @@ -13,7 +12,7 @@ export const spec = { isBidRequestValid: function(bid) { return !!( - bid.params.placement_id && utils.isNumber(bid.params.placement_id) + bid.params.placement_id && utils.isStr(bid.params.placement_id) ); }, @@ -23,13 +22,19 @@ export const spec = { utils._each(validBidRequests, function(bid) { requests.push({ method: 'POST', - url: HOST + '/hb/s2s', + url: ENDPOINT, options: { contentType: 'application/json', withCredentials: true }, data: JSON.stringify({ - placement_id: utils.getBidIdParameter('placement_id', bid.params) + 'ext': { + 'prebid': { + 'storedrequest': { + 'id': utils.getBidIdParameter('placement_id', bid.params) + } + } + } }), bidId: bid.bidId }); @@ -39,47 +44,32 @@ export const spec = { }, interpretResponse: function(serverResponse, bidRequest) { - try { - const bidResponse = serverResponse.body; - const bidResponses = []; + const response = serverResponse.body; + const bidResponses = []; - if (Number(bidResponse.cpm) > 0) { - bidResponses.push({ - requestId: bidRequest.bidId, - cpm: bidResponse.cpm, - width: bidResponse.width, - height: bidResponse.height, - creativeId: bidResponse.creativeId, - currency: CURRENCY, - netRevenue: false, - ttl: TIME_TO_LIVE, - ad: bidResponse.ad + try { + utils._each(response.seatbid, (resp) => { + utils._each(resp.bid, (bid) => { + bidResponses.push({ + requestId: bidRequest.bidId, + cpm: bid.price, + width: bid.w, + height: bid.h, + creativeId: bid.adid, + currency: response.cur, + netRevenue: false, + ttl: TIME_TO_LIVE, + meta: { + advertiserDomains: bid.adomain || [] + }, + ad: bid.adm + }); }); - } - - return bidResponses; + }) } catch (err) { utils.logError(err); - return []; - } - }, - - getUserSyncs: function(syncOptions) { - const syncs = [] - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: HOST + '/hb/s2s/matching' - }); - } - - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: HOST + '/hb/s2s/matching' - }); } - return syncs; + return bidResponses; } }; registerBidder(spec); diff --git a/modules/nextMillenniumBidAdapter.md b/modules/nextMillenniumBidAdapter.md index c583969b4af..048fe907ac7 100644 --- a/modules/nextMillenniumBidAdapter.md +++ b/modules/nextMillenniumBidAdapter.md @@ -2,7 +2,7 @@ ``` Module Name: NextMillennium Bid Adapter Module Type: Bidder Adapter -Maintainer: mikhail.ivanchenko@iageengineering.net +Maintainer: mihail.ivanchenko@nextmillennium.io ``` # Description @@ -21,7 +21,7 @@ Currently module supports only banner mediaType. bids: [{ bidder: 'nextMillennium', params: { - placement_id: -1 + placement_id: '-1' } }] }]; diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index f4d929b439c..42032eb03ea 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import * as utils from '../../../src/utils.js'; import { spec } from 'modules/nextMillenniumBidAdapter.js'; describe('nextMillenniumBidAdapterTests', function() { @@ -8,20 +9,19 @@ describe('nextMillenniumBidAdapterTests', function() { bidId: 'transaction_1234', bidder: 'nextMillennium', params: { - placement_id: 12345 + placement_id: '12345' }, sizes: [[300, 250]] } ] }; - let request = []; it('validate_pub_params', function() { expect( spec.isBidRequestValid({ bidder: 'nextMillennium', params: { - placement_id: 12345 + placement_id: '12345' } }) ).to.equal(true); @@ -32,7 +32,7 @@ describe('nextMillenniumBidAdapterTests', function() { { bidId: 'bid1234', bidder: 'nextMillennium', - params: { placement_id: -1 }, + params: { placement_id: '-1' }, sizes: [[300, 250]] } ]; @@ -40,34 +40,36 @@ describe('nextMillenniumBidAdapterTests', function() { expect(request[0].bidId).to.equal('bid1234'); }); - it('validate_getUserSyncs_function', function() { - expect(spec.getUserSyncs({ iframeEnabled: true })).to.have.lengthOf(1); - expect(spec.getUserSyncs({ iframeEnabled: false })).to.have.lengthOf(0); - - let pixel = spec.getUserSyncs({ iframeEnabled: true }); - expect(pixel[0].type).to.equal('iframe'); - expect(pixel[0].url).to.equal('https://brainlyads.com/hb/s2s/matching'); - }); - it('validate_response_params', function() { let serverResponse = { body: { - cpm: 1.7, - width: 300, - height: 250, - creativeId: 'p35t0enob6twbt9mofjc8e', - ad: 'Hello! It\'s a test ad!' + 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.bids[0]); expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - expect(bid.creativeId).to.equal('p35t0enob6twbt9mofjc8e'); + expect(bid.creativeId).to.equal('96846035'); expect(bid.ad).to.equal('Hello! It\'s a test ad!'); - expect(bid.cpm).to.equal(1.7); + expect(bid.cpm).to.equal(0.5); expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); From f028348458352f060c5809c26098c82d170223bb Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 7 Jun 2021 14:42:29 -0700 Subject: [PATCH 1105/1476] PubMatic bid adapter: Change user-sync end-point (#6963) * added support for pubcommon, digitrust, id5id * added support for IdentityLink * changed the source for id5 * added unit test cases * changed source param for identityLink * chnaging end-point --- modules/pubmaticBidAdapter.js | 2 +- test/spec/modules/pubmaticBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index b78b1fc96ba..df22f8713cd 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -7,7 +7,7 @@ import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'pubmatic'; const LOG_WARN_PREFIX = 'PubMatic: '; const ENDPOINT = 'https://hbopenbid.pubmatic.com/translator?source=prebid-client'; -const USER_SYNC_URL_IFRAME = 'https://ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p='; +const USER_SYNC_URL_IFRAME = 'https://ads.pubmatic.com/AdServer/js/user_sync.html?kdntuid=1&p='; const USER_SYNC_URL_IMAGE = 'https://image8.pubmatic.com/AdServer/ImgSync?p='; const DEFAULT_CURRENCY = 'USD'; const AUCTION_TYPE = 1; diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 80ce93858d5..d22acabd4cf 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3390,7 +3390,7 @@ describe('PubMatic adapter', function () { }); describe('getUserSyncs', function() { - const syncurl_iframe = 'https://ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p=5670'; + const syncurl_iframe = 'https://ads.pubmatic.com/AdServer/js/user_sync.html?kdntuid=1&p=5670'; const syncurl_image = 'https://image8.pubmatic.com/AdServer/ImgSync?p=5670'; let sandbox; beforeEach(function () { From 3ea9b0a3a2695a6a5acf9fb8c14b53afb1a547ed Mon Sep 17 00:00:00 2001 From: Michael Callari Date: Mon, 7 Jun 2021 19:15:49 -0400 Subject: [PATCH 1106/1476] Optimera RTD Module: initial release (#6962) --- .../gpt/optimeraRtdProvider_example.html | 152 ++++++++++++++ modules/.submodules.json | 1 + modules/optimeraRtdProvider.js | 188 ++++++++++++++++++ modules/optimeraRtdProvider.md | 44 ++++ test/spec/modules/optimeraRtdProvider_spec.js | 84 ++++++++ 5 files changed, 469 insertions(+) create mode 100644 integrationExamples/gpt/optimeraRtdProvider_example.html create mode 100644 modules/optimeraRtdProvider.js create mode 100644 modules/optimeraRtdProvider.md create mode 100644 test/spec/modules/optimeraRtdProvider_spec.js diff --git a/integrationExamples/gpt/optimeraRtdProvider_example.html b/integrationExamples/gpt/optimeraRtdProvider_example.html new file mode 100644 index 00000000000..109a4c2b366 --- /dev/null +++ b/integrationExamples/gpt/optimeraRtdProvider_example.html @@ -0,0 +1,152 @@ + + + + + + + + + Optimera RTD Example + + + + + + + + + + +

Basic Prebid.js Example

+
Div-1
+
+ +
+ +
+ +
Div-0
+
+ +
+ + + + \ No newline at end of file diff --git a/modules/.submodules.json b/modules/.submodules.json index 74ebc021a6d..2b2e6457531 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -40,6 +40,7 @@ "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", + "optimeraRtdProvider", "permutiveRtdProvider", "reconciliationRtdProvider", "sirdataRtdProvider" diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js new file mode 100644 index 00000000000..fadbd099368 --- /dev/null +++ b/modules/optimeraRtdProvider.js @@ -0,0 +1,188 @@ +/** + * This module adds optimera provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * + * The module will fetch targeting values from the Optimera server + * and apply the tageting to each ad request. These values are created + * from the Optimera Mesaurement script which is installed on the + * Publisher's site. + * + * @module modules/optimeraRtdProvider + * @requires module:modules/realTimeData + */ + +/** + * @typedef {Object} ModuleParams + * @property {string} clientID + * @property {string} optimeraKeyName + * @property {string} device + */ + +import * as utils from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { ajaxBuilder } from '../src/ajax.js'; + +/** @type {ModuleParams} */ +let _moduleParams = {}; + +/** + * Default Optimera Key Name + * This can default to hb_deal_optimera for publishers + * who used the previous Optimera Bidder Adapter. + * @type {string} */ +export let optimeraKeyName = 'hb_deal_optimera'; + +/** + * Optimera Score File Base URL. + * This is the base URL for the data endpoint request to fetch + * the targeting values. + * @type {string} + */ +export const scoresBaseURL = 'https://dyv1bugovvq1g.cloudfront.net/'; + +/** + * Optimera Score File URL. + * @type {string} + */ +export let scoresURL; + +/** + * Optimera Client ID. + * @type {string} + */ +export let clientID; + +/** + * Optional device parameter. + * @type {string} + */ +export let device = 'default'; + +/** + * Targeting object for all ad positions. + * @type {string} + */ +export let optimeraTargeting = {}; + +/** + * Make the request for the Score File. + */ +export function scoreFileRequest() { + utils.logInfo('Fetch Optimera score file.'); + const ajax = ajaxBuilder(); + ajax(scoresURL, + { + success: (res, req) => { + if (req.status === 200) { + try { + setScores(res); + } catch (err) { + utils.logError('Unable to parse Optimera Score File.', err); + } + } else if (req.status === 403) { + utils.logError('Unable to fetch the Optimera Score File - 403'); + } + }, + error: () => { + utils.logError('Unable to fetch the Optimera Score File.'); + } + }); +} + +/** + * Apply the Optimera targeting to the ad slots. + */ +export function returnTargetingData(adUnits, config) { + const targeting = {}; + try { + adUnits.forEach(function(adUnit) { + if (optimeraTargeting[adUnit]) { + targeting[adUnit] = {}; + targeting[adUnit][optimeraKeyName] = optimeraTargeting[adUnit]; + } + }); + } catch (err) { + utils.logError('error', err); + } + utils.logInfo('Apply Optimera targeting'); + return targeting; +} + +/** + * Initialize the Module. + */ +export function init(moduleConfig) { + _moduleParams = moduleConfig.params; + if (_moduleParams && _moduleParams.clientID) { + clientID = _moduleParams.clientID; + if (_moduleParams.optimeraKeyName) { + optimeraKeyName = (_moduleParams.optimeraKeyName); + } + if (_moduleParams.device) { + device = _moduleParams.device; + } + setScoresURL(); + scoreFileRequest(); + return true; + } else { + if (!_moduleParams.clientID) { + utils.logError('Optimera clientID is missing in the Optimera RTD configuration.'); + } + return false; + } +} + +/** + * Set the score file url. + * This fully-formed URL for the data endpoint request to fetch + * the targeting values. This is not a js library, rather JSON + * which has the targeting values for the page. + */ +export function setScoresURL() { + const optimeraHost = window.location.host; + const optimeraPathName = window.location.pathname; + scoresURL = `${scoresBaseURL}${clientID}/${optimeraHost}${optimeraPathName}.js`; +} + +/** + * Set the scores for the divice if given. + * @param {*} result + * @returns {string} JSON string of Optimera Scores. + */ +export function setScores(result) { + let scores = {}; + try { + scores = JSON.parse(result); + if (device !== 'default' && scores.device[device]) { + scores = scores.device[device]; + } + } catch (e) { + utils.logError('Optimera score file could not be parsed.'); + } + optimeraTargeting = scores; +} + +/** @type {RtdSubmodule} */ +export const optimeraSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'optimeraRTD', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getTargetingData: returnTargetingData, + init, +}; + +/** + * Register the Sub Module. + */ +function registerSubModule() { + submodule('realTimeData', optimeraSubmodule); +} + +registerSubModule(); diff --git a/modules/optimeraRtdProvider.md b/modules/optimeraRtdProvider.md new file mode 100644 index 00000000000..610dec537e0 --- /dev/null +++ b/modules/optimeraRtdProvider.md @@ -0,0 +1,44 @@ +# Overview +``` +Module Name: Optimera Real Time Date Module +Module Type: RTD Module +Maintainer: mcallari@optimera.nyc +``` + +# Description + +Optimera Real Time Data Module. Provides targeting for ad requests from data collected by the Optimera Measurement script on your site. Please contact [Optimera](http://optimera.nyc/) for information. This is a port of the Optimera Bidder Adapter. + +# Configurations + +Compile the Optimera RTD Provider into your Prebid build: + +`gulp build --modules=optimeraRtdProvider` + +Configuration example for using RTD module with `optimera` provider +```javascript + pbjs.setConfig({ + realTimeData: { + dataProviders: [ + { + name: 'optimeraRTD', + waitForIt: true, + params: { + clientID: '9999', + optimeraKeyName: 'optimera', + device: 'de' + } + } + ] + } +``` + +#Params + +Contact Optimera to get assistance with the params. + +| param name | type |Scope | Description | +| :------------ | :------------ | :------- | :------- | +| clientID | string | required | Optimera Client ID | +| optimeraKeyName | string | optional | GAM key name for Optimera. If migrating from the Optimera bidder adapter this will default to hb_deal_optimera and can be ommitted from the configuration. | +| device | string | optional | Device type code for mobile, tablet, or desktop. Either mo, tb, de | diff --git a/test/spec/modules/optimeraRtdProvider_spec.js b/test/spec/modules/optimeraRtdProvider_spec.js new file mode 100644 index 00000000000..1d2c0d99a0a --- /dev/null +++ b/test/spec/modules/optimeraRtdProvider_spec.js @@ -0,0 +1,84 @@ +import * as optimeraRTD from '../../../modules/optimeraRtdProvider.js'; +let utils = require('src/utils.js'); + +describe('Optimera RTD sub module', () => { + it('should init, return true, and set the params', () => { + const conf = { + dataProviders: [{ + name: 'optimeraRTD', + params: { + clientID: '9999', + optimeraKeyName: 'optimera', + device: 'de' + } + }] + }; + expect(optimeraRTD.init(conf.dataProviders[0])).to.equal(true); + expect(optimeraRTD.clientID).to.equal('9999'); + expect(optimeraRTD.optimeraKeyName).to.equal('optimera'); + expect(optimeraRTD.device).to.equal('de'); + }); +}); + +describe('Optimera RTD score file url is properly set', () => { + it('Proerly set the score file url', () => { + optimeraRTD.setScores(); + expect(optimeraRTD.scoresURL).to.equal('https://dyv1bugovvq1g.cloudfront.net/9999/localhost:9876/context.html.js'); + }); +}); + +describe('Optimera RTD score file properly sets targeting values', () => { + const scores = { + 'div-0': ['A1', 'A2'], + 'div-1': ['A3', 'A4'], + 'device': { + 'de': { + 'div-0': ['A5', 'A6'], + 'div-1': ['A7', 'A8'], + }, + 'mo': { + 'div-0': ['A9', 'B0'], + 'div-1': ['B1', 'B2'], + } + } + }; + it('Properly set the score file url', () => { + optimeraRTD.setScores(JSON.stringify(scores)); + expect(optimeraRTD.optimeraTargeting['div-0']).to.include.ordered.members(['A5', 'A6']); + expect(optimeraRTD.optimeraTargeting['div-1']).to.include.ordered.members(['A7', 'A8']); + }); +}); + +describe('Optimera RTD targeting object is properly formed', () => { + const adDivs = ['div-0', 'div-1']; + it('applyTargeting properly created the targeting object', () => { + const targeting = optimeraRTD.returnTargetingData(adDivs); + expect(targeting).to.deep.include({'div-0': {'optimera': ['A5', 'A6']}}); + expect(targeting).to.deep.include({'div-1': {'optimera': ['A7', 'A8']}}); + }); +}); + +describe('Optimera RTD error logging', () => { + let utilsLogErrorStub; + + beforeEach(function () { + utilsLogErrorStub = sinon.stub(utils, 'logError'); + }); + afterEach(function () { + utilsLogErrorStub.restore(); + }); + + it('ommitting clientID should log an error', () => { + const conf = { + dataProviders: [{ + name: 'optimeraRTD', + params: { + optimeraKeyName: 'optimera', + device: 'de' + } + }] + }; + optimeraRTD.init(conf.dataProviders[0]); + expect(utils.logError.called).to.equal(true); + }); +}); From 34f3197e826258d60f12f6dd4d6e7be06d6958ca Mon Sep 17 00:00:00 2001 From: iprom-adserver <79305981+iprom-adserver@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:50:58 +0200 Subject: [PATCH 1107/1476] Iprom adapter: add advertiserDomains (#6965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * iPROM adapter upload - adapter * Add advertiserDomains support Co-authored-by: Gašper Žagar --- modules/ipromBidAdapter.js | 13 ++++++++++--- test/spec/modules/ipromBidAdapter_spec.js | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/ipromBidAdapter.js b/modules/ipromBidAdapter.js index e328cd1ec5d..fed8ca2ebb0 100644 --- a/modules/ipromBidAdapter.js +++ b/modules/ipromBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'iprom'; const ENDPOINT_URL = 'https://core.iprom.net/programmatic'; -const VERSION = 'v1.0.0'; +const VERSION = 'v1.0.1'; const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_NETREVENUE = true; const DEFAULT_TTL = 360; @@ -52,7 +52,7 @@ export const spec = { const bidResponses = []; bids.forEach(bid => { - bidResponses.push({ + const b = { ad: bid.ad, requestId: bid.requestId, cpm: bid.cpm, @@ -62,7 +62,14 @@ export const spec = { currency: bid.currency || DEFAULT_CURRENCY, netRevenue: bid.netRevenue || DEFAULT_NETREVENUE, ttl: bid.ttl || DEFAULT_TTL, - }); + meta: {}, + }; + + if (bid.aDomains && bid.aDomains.length) { + b.meta.advertiserDomains = bid.aDomains; + } + + bidResponses.push(b); }); return bidResponses; diff --git a/test/spec/modules/ipromBidAdapter_spec.js b/test/spec/modules/ipromBidAdapter_spec.js index 535cc332b21..a3310a33cc2 100644 --- a/test/spec/modules/ipromBidAdapter_spec.js +++ b/test/spec/modules/ipromBidAdapter_spec.js @@ -164,7 +164,8 @@ describe('iPROM Adapter', function () { width: '300', height: '250', creativeId: 1234, - ad: 'Iprom Header bidding example' + ad: 'Iprom Header bidding example', + aDomains: ['https://example.com'], } ]}; @@ -177,6 +178,7 @@ describe('iPROM Adapter', function () { expect(bids[0].width).to.equal('300'); expect(bids[0].height).to.equal('250'); expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['https://example.com']); }); it('should return empty bid response', function () { From fe51453d2b9ae985bab83623d5483e85d4f5fd5f Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 8 Jun 2021 16:11:20 +0300 Subject: [PATCH 1108/1476] Adkernel Bid Adapter: eids support (#6964) --- modules/adkernelBidAdapter.js | 11 ++++++++++ test/spec/modules/adkernelBidAdapter_spec.js | 22 +++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index b18b2333bac..09f16a82221 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -404,6 +404,10 @@ function buildRtbRequest(imps, bidderRequest, schain) { if (schain) { utils.deepSetValue(req, 'source.ext.schain', schain); } + let eids = getExtendedUserIds(bidderRequest); + if (eids) { + utils.deepSetValue(req, 'user.ext.eids', eids); + } return req; } @@ -435,6 +439,13 @@ function createSite(refInfo) { return site; } +function getExtendedUserIds(bidderRequest) { + let eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); + if (utils.isArray(eids)) { + return eids; + } +} + /** * Format creative with optional nurl call * @param bid rtb Bid object diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index b9d647238bf..ab1c5501bd9 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -28,7 +28,15 @@ describe('Adkernel adapter', function () { banner: { sizes: [[728, 90]] } - } + }, + userIdAsEids: [ + { + source: 'crwdcntrl.net', + uids: [ + {atype: 1, id: '97d09fbba28542b7acbb6317c9534945a702b74c5993c352f332cfe83f40cdd9'} + ] + } + ] }, bid3_host2 = { bidder: 'adkernel', params: {zoneId: 1, host: 'rtb-private.adkernel.com'}, @@ -251,6 +259,7 @@ describe('Adkernel adapter', function () { function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST, dnt = true) { let dntmock = sandbox.stub(utils, 'getDNT').callsFake(() => dnt); + bidderRequest.bids = bidRequests; let pbRequests = spec.buildRequests(bidRequests, bidderRequest); dntmock.restore(); let rtbRequests = pbRequests.map(r => JSON.parse(r.data)); @@ -381,6 +390,17 @@ describe('Adkernel adapter', function () { let [_, bidRequests] = buildRequest([bid]); expect(bidRequests[0].imp[0]).to.have.property('bidfloor', 0.145); }); + + it('should forward user ids if available', function() { + let bid = Object.assign({}, bid2_zone2); + let [_, bidRequests] = buildRequest([bid]); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('ext'); + expect(bidRequests[0].user.ext).to.have.property('eids'); + expect(bidRequests[0].user.ext.eids).to.be.an('array').that.is.not.empty; + expect(bidRequests[0].user.ext.eids[0]).to.have.property('source'); + expect(bidRequests[0].user.ext.eids[0]).to.have.property('uids'); + }); }); describe('video request building', function () { From ee90abe6909505677565e071477dc1fcd6cc3c2e Mon Sep 17 00:00:00 2001 From: Nick Duitz <42961155+nduitz@users.noreply.github.com> Date: Tue, 8 Jun 2021 16:31:14 +0200 Subject: [PATCH 1109/1476] Welect Bid Adapter: support advertiser domain (#6966) * be explicit about what our server returns * support advertiserDomains in response --- modules/welectBidAdapter.js | 16 +++++++++++++++- test/spec/modules/welectBidAdapter_spec.js | 12 +++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js index 467b628c944..c90a3d29498 100644 --- a/modules/welectBidAdapter.js +++ b/modules/welectBidAdapter.js @@ -84,7 +84,21 @@ export const spec = { } const bidResponses = []; - const bidResponse = responseBody.bidResponse; + const bidResponse = { + requestId: responseBody.bidResponse.requestId, + cpm: responseBody.bidResponse.cpm, + width: responseBody.bidResponse.width, + height: responseBody.bidResponse.height, + creativeId: responseBody.bidResponse.creativeId, + currency: responseBody.bidResponse.currency, + netRevenue: responseBody.bidResponse.netRevenue, + ttl: responseBody.bidResponse.ttl, + ad: responseBody.bidResponse.ad, + vastUrl: responseBody.bidResponse.vastUrl, + meta: { + advertiserDomains: responseBody.bidResponse.meta.advertiserDomains + } + }; bidResponses.push(bidResponse); return bidResponses; }, diff --git a/test/spec/modules/welectBidAdapter_spec.js b/test/spec/modules/welectBidAdapter_spec.js index 571f68038ac..2f2af35eaec 100644 --- a/test/spec/modules/welectBidAdapter_spec.js +++ b/test/spec/modules/welectBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec as adapter} from 'modules/welectBidAdapter.js'; +import { expect } from 'chai'; +import { spec as adapter } from 'modules/welectBidAdapter.js'; describe('WelectAdapter', function () { describe('Check methods existance', function () { @@ -147,6 +147,9 @@ describe('WelectAdapter', function () { ad: { video: 'some vast url' }, + meta: { + advertiserDomains: [], + }, cpm: 17, creativeId: 'svmpreview', currency: 'EUR', @@ -183,6 +186,9 @@ describe('WelectAdapter', function () { ad: { video: 'some vast url' }, + meta: { + advertiserDomains: [] + }, cpm: 17, creativeId: 'svmpreview', currency: 'EUR', @@ -198,7 +204,7 @@ describe('WelectAdapter', function () { expect(adapter.interpretResponse(unavailableResponse, bid)).to.deep.equal([]); }); - it('if response reflects availability, should equal result', function() { + it('if response reflects availability, should equal result', function () { expect(adapter.interpretResponse(availableResponse, bid)).to.deep.equal([result]) }) }); From 11c3b498ac67712e06168f3424534114e528e4a1 Mon Sep 17 00:00:00 2001 From: mwehr-zeta <70167335+mwehr-zeta@users.noreply.github.com> Date: Tue, 8 Jun 2021 10:34:46 -0400 Subject: [PATCH 1110/1476] Zeta Bid Adapter: bugfix for the possibility to overwrite data with gdpr consent (#6967) * bugfix * update Prebid definer id * use compatible syntax * polishing * polishing --- modules/zetaBidAdapter.js | 27 ++++++++++++------------ modules/zetaBidAdapter.md | 2 +- test/spec/modules/zetaBidAdapter_spec.js | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index ee5c854df97..a6cbc831604 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -2,8 +2,9 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; const BIDDER_CODE = 'zeta_global'; +const PREBID_DEFINER_ID = '44253' const ENDPOINT_URL = 'https://prebid.rfihub.com/prebid'; -const USER_SYNC_URL = 'https://p.rfihub.com/cm?pub=42770&in=1'; +const USER_SYNC_URL = 'https://p.rfihub.com/cm?in=1&pub='; const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; @@ -87,10 +88,12 @@ export const spec = { badv: params.badv, bapp: params.bapp, source: params.source ? params.source : {}, + regs: params.regs ? params.regs : {}, ext: params.ext ? params.ext : {} }; payload.device.ua = navigator.userAgent; + payload.device.ip = navigator.ip; payload.site.page = bidderRequest.refererInfo.referer; payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; payload.ext.definerId = params.definerId; @@ -99,20 +102,18 @@ export const spec = { payload.test = params.test; } if (request.gdprConsent) { - payload.regs = { - ext: { - gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0 - } - }; + payload.regs.ext = Object.assign( + payload.regs.ext, + {gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0} + ); } if (request.gdprConsent && request.gdprConsent.gdprApplies) { - payload.user = { - ext: { - consent: request.gdprConsent.consentString - } - }; + payload.user.ext = Object.assign( + payload.user.ext, + {consent: request.gdprConsent.consentString} + ); } - const postUrl = params.definerId !== '0' ? ENDPOINT_URL.concat('/', params.definerId) : ENDPOINT_URL; + const postUrl = params.definerId !== PREBID_DEFINER_ID ? ENDPOINT_URL.concat('/', params.definerId) : ENDPOINT_URL; return { method: 'POST', url: postUrl, @@ -162,7 +163,7 @@ export const spec = { if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: USER_SYNC_URL + url: USER_SYNC_URL.concat(PREBID_DEFINER_ID) }); } return syncs; diff --git a/modules/zetaBidAdapter.md b/modules/zetaBidAdapter.md index 89a9767d29a..e0f7271a4f1 100644 --- a/modules/zetaBidAdapter.md +++ b/modules/zetaBidAdapter.md @@ -35,7 +35,7 @@ Module that connects to Zeta's demand sources country: 'USA' } }, - definerId: '0', + definerId: '44253', test: 1 } } diff --git a/test/spec/modules/zetaBidAdapter_spec.js b/test/spec/modules/zetaBidAdapter_spec.js index ccdd5f43cb0..25350725dee 100644 --- a/test/spec/modules/zetaBidAdapter_spec.js +++ b/test/spec/modules/zetaBidAdapter_spec.js @@ -24,7 +24,7 @@ describe('Zeta Bid Adapter', function() { country: 'USA' } }, - definerId: '0', + definerId: '44253', test: 1 } }]; From 6c07b74381bce0a3f7e9cf4c70a003698ffdcc95 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:14:47 +0200 Subject: [PATCH 1111/1476] tappx Bid Adapter: update tests, log warinings, and fix consent (#6968) * tappxBidAdapter - Update logWarns messages * tappxBidAdapter - update _getHostInfo tests * tappxBidAdapter - Update version and add pbjs version in requests * tappxBidAdapter - Fix: Add the consent in the correct parameter * tappxBidAdapter - Make mimes param mandatory for video requests * tappxBidAdapter - Add more info to video requests * tappxBidAdapter - replace double quotes with single quotes Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 117 +++++++++++++++++----- modules/tappxBidAdapter.md | 16 ++- test/spec/modules/tappxBidAdapter_spec.js | 64 +++++++++++- 3 files changed, 168 insertions(+), 29 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 48e0abc5c7f..5cdd2ba376a 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,10 +8,35 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10526'; +const TAPPX_BIDDER_VERSION = '0.1.10607'; const TYPE_CNN = 'prebidjs'; +const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream']; +const DATA_TYPES = { + 'NUMBER': 'number', + 'STRING': 'string', + 'BOOLEAN': 'boolean', + 'ARRAY': 'array', + 'OBJECT': 'object' +}; +const VIDEO_CUSTOM_PARAMS = { + 'minduration': DATA_TYPES.NUMBER, + 'maxduration': DATA_TYPES.NUMBER, + 'startdelay': DATA_TYPES.NUMBER, + 'playbackmethod': DATA_TYPES.ARRAY, + 'api': DATA_TYPES.ARRAY, + 'protocols': DATA_TYPES.ARRAY, + 'w': DATA_TYPES.NUMBER, + 'h': DATA_TYPES.NUMBER, + 'battr': DATA_TYPES.ARRAY, + 'linearity': DATA_TYPES.NUMBER, + 'placement': DATA_TYPES.NUMBER, + 'minbitrate': DATA_TYPES.NUMBER, + 'maxbitrate': DATA_TYPES.NUMBER, + 'skip': DATA_TYPES.NUMBER +} + var hostDomain; export const spec = { @@ -53,7 +78,7 @@ export const spec = { interpretResponse: function(serverResponse, originalRequest) { const responseBody = serverResponse.body; if (!serverResponse.body) { - utils.logWarn('[TAPPX]: Empty response body HTTP 204, no bids'); + utils.logWarn(LOG_PREFIX, 'Empty response body HTTP 204, no bids'); return []; } @@ -105,17 +130,17 @@ export const spec = { function validBasic(bid) { if (bid.params == null) { - utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters.`); + utils.logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters.'); return false; } if (bid.params.tappxkey == null) { - utils.logWarn(`[TAPPX]: Please review the mandatory Tappxkey parameter.`); + utils.logWarn(LOG_PREFIX, 'Please review the mandatory Tappxkey parameter.'); return false; } if (bid.params.host == null) { - utils.logWarn(`[TAPPX]: Please review the mandatory Host parameter.`); + utils.logWarn(LOG_PREFIX, 'Please review the mandatory Host parameter.'); return false; } @@ -125,7 +150,7 @@ function validBasic(bid) { } if (classicEndpoint && bid.params.endpoint == null) { - utils.logWarn(`[TAPPX]: Please review the mandatory endpoint Tappx parameters.`); + utils.logWarn(LOG_PREFIX, 'Please review the mandatory endpoint Tappx parameters.'); return false; } @@ -138,7 +163,11 @@ function validMediaType(bid) { // Video validations if (typeof video != 'undefined') { if (VIDEO_SUPPORT.indexOf(video.context) === -1) { - utils.logWarn(`[TAPPX]: Please review the mandatory Tappx parameters for Video. Only "instream" is suported.`); + utils.logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Only "instream" is suported.'); + return false; + } + if (typeof video.mimes == 'undefined') { + utils.logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Mimes param is mandatory.'); return false; } } @@ -191,7 +220,7 @@ function interpretBid(serverBid, request) { * @return response ad */ function buildOneRequest(validBidRequests, bidderRequest) { - let hostInfo = getHostInfo(validBidRequests); + let hostInfo = _getHostInfo(validBidRequests); const ENDPOINT = hostInfo.endpoint; hostDomain = hostInfo.domain; @@ -263,10 +292,21 @@ function buildOneRequest(validBidRequests, bidderRequest) { if (videoMediaType) { let video = {}; - w = videoMediaType.playerSize[0][0]; - h = videoMediaType.playerSize[0][1]; - video.w = w; - video.h = h; + + let videoParams = utils.deepAccess(validBidRequests, 'params.video'); + for (var key in VIDEO_CUSTOM_PARAMS) { + if (videoParams.hasOwnProperty(key)) { + video[key] = _checkParamDataType(key, videoParams[key], VIDEO_CUSTOM_PARAMS[key]); + } + } + + if ((video.w === undefined || video.w == null || video.w <= 0) || + (video.h === undefined || video.h == null || video.h <= 0)) { + w = videoMediaType.playerSize[0][0]; + h = videoMediaType.playerSize[0][1]; + video.w = w; + video.h = h; + } video.mimes = videoMediaType.mimes; @@ -288,10 +328,10 @@ function buildOneRequest(validBidRequests, bidderRequest) { if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { imp.bidfloor = floor.floor; } else { - utils.logWarn('[TAPPX]: ', 'Currency not valid. Use only USD with Tappx.'); + utils.logWarn(LOG_PREFIX, 'Currency not valid. Use only USD with Tappx.'); } } catch (e) { - utils.logWarn('[TAPPX]: ', e); + utils.logWarn(LOG_PREFIX, e); imp.bidfloor = utils.deepAccess(validBidRequests, 'params.bidfloor'); // Be sure that we have an imp.bidfloor } } @@ -333,11 +373,20 @@ function buildOneRequest(validBidRequests, bidderRequest) { // < Params // > GDPR + + // Universal ID + const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); + payload.user = { + ext: { + eids: eidsArr + } + }; + let regs = {}; regs.gdpr = 0; if (!(bidderRequest.gdprConsent == null)) { if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { regs.gdpr = bidderRequest.gdprConsent.gdprApplies; } - if (regs.gdpr) { regs.consent = bidderRequest.gdprConsent.consentString; } + if (regs.gdpr) { payload.user.ext.consent = bidderRequest.gdprConsent.consentString; } } // CCPA @@ -350,14 +399,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { if (config.getConfig('coppa') === true) { regs.coppa = config.getConfig('coppa') === true ? 1 : 0; } - - // Universal ID - const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); - payload.user = { - ext: { - eids: eidsArr - } - }; // < GDPR // > Payload @@ -373,9 +414,11 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.regs = regs; // < Payload + let pbjsv = ($$PREBID_GLOBAL$$.version != null) ? encodeURIComponent($$PREBID_GLOBAL$$.version) : -1; + return { method: 'POST', - url: `${hostInfo.url}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, + url: `${hostInfo.url}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}&pbjsv=${pbjsv}`, data: JSON.stringify(payload), bids: validBidRequests }; @@ -391,7 +434,7 @@ function getOs() { if (ua == null) { return 'unknown'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'ios'; } else if (ua.match(/Android/)) { return 'android'; } else if (ua.match(/Window/)) { return 'windows'; } else { return 'unknown'; } } -function getHostInfo(validBidRequests) { +export function _getHostInfo(validBidRequests) { let domainInfo = {}; let endpoint = utils.deepAccess(validBidRequests, 'params.endpoint'); let hostParam = utils.deepAccess(validBidRequests, 'params.host'); @@ -414,4 +457,28 @@ function getHostInfo(validBidRequests) { return domainInfo; } +export function _checkParamDataType(key, value, datatype) { + var errMsg = 'Ignoring param key: ' + key + ', expects ' + datatype + ', found ' + typeof value; + var functionToExecute; + switch (datatype) { + case DATA_TYPES.BOOLEAN: + functionToExecute = utils.isBoolean; + break; + case DATA_TYPES.NUMBER: + functionToExecute = utils.isNumber; + break; + case DATA_TYPES.STRING: + functionToExecute = utils.isStr; + break; + case DATA_TYPES.ARRAY: + functionToExecute = utils.isArray; + break; + } + if (functionToExecute(value)) { + return value; + } + utils.logWarn(LOG_PREFIX, errMsg); + return undefined; +} + registerBidder(spec); diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index 776b24bb07c..ca9b2b97035 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -62,7 +62,21 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 tappxkey: "pub-1234-desktop-1234", endpoint: "VZ12TESTCTV", bidfloor: 0.005, - test: true + test: true, + video: { + skippable: true, // optional + minduration: 5, // optional + maxduration: 30, // optional + startdelay: 5, // optional + playbackmethod: [1,3], // optional + api: [ 1, 2 ], // optional + protocols: [ 2, 3 ], // optional + battr: [ 13, 14 ], // optional + linearity: 1, // optional + placement: 2, // optional + minbitrate: 10, // optional + maxbitrate: 10 // optional + } } }] } diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 9fab7d858e1..c4e0329de17 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -1,5 +1,6 @@ import { assert } from 'chai'; -import { spec } from 'modules/tappxBidAdapter'; +import { spec } from 'modules/tappxBidAdapter.js'; +import { _checkParamDataType, _getHostInfo } from '../../../modules/tappxBidAdapter.js'; const c_BIDREQUEST = { data: { @@ -115,7 +116,7 @@ const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; const c_BIDDERREQUEST_B = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; -const c_BIDDERREQUEST_V = {'method': 'POST', 'url': 'https://testing.ssp.tappx.com/rtb/v2//VZ12TESTCTV?type_cnn=prebidjs&v=0.1.10329', 'data': '{"site":{"name":"localhost","bundle":"localhost","domain":"localhost"},"user":{"ext":{}},"id":"e807363f-3095-43a8-a4a6-f44196cb7318","test":1,"at":1,"tmax":1000,"bidder":"tappx","imp":[{"video":{"w":320,"h":250,"mimes":["video/mp4","application/javascript"]},"id":"28f49c71b13f2f","tagid":"localhost_typeAdBanVid_windows","secure":1,"bidfloor":0.005,"ext":{"bidder":{"tappxkey":"pub-1234-desktop-1234","endpoint":"VZ12TESTCTV","host":"testing.ssp.tappx.com/rtb/v2/"}}}],"device":{"os":"windows","ip":"peer","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36","h":864,"w":1536,"dnt":0,"language":"en","make":"Google Inc."},"params":{"host":"tappx.com","bidfloor":0.005},"regs":{"gdpr":0,"ext":{}}}', 'bids': {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com/rtb/v2/', 'tappxkey': 'pub-1234-desktop-1234', 'endpoint': 'VZ12TESTCTV', 'bidfloor': 0.005, 'test': true}, 'crumbs': {'pubcid': 'dccfe922-3823-4676-b7b2-e5ed8743154e'}, 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video-ad-div'}}}, 'renderer': {'options': {'text': 'Tappx Outstream Video'}}, 'mediaTypes': {'video': {'context': 'instream', 'mimes': ['video/mp4', 'application/javascript'], 'playerSize': [[320, 250]]}}, 'adUnitCode': 'video-ad-div', 'transactionId': 'ed41c805-d14c-49c3-954d-26b98b2aa2c2', 'sizes': [[320, 250]], 'bidId': '28f49c71b13f2f', 'bidderRequestId': '1401710496dc7', 'auctionId': 'e807363f-3095-43a8-a4a6-f44196cb7318', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}} +const c_BIDDERREQUEST_V = {'method': 'POST', 'url': 'https://testing.ssp.tappx.com/rtb/v2//VZ12TESTCTV?type_cnn=prebidjs&v=0.1.10329', 'data': '{"site":{"name":"localhost","bundle":"localhost","domain":"localhost"},"user":{"ext":{}},"id":"0fecfa84-c541-49f8-8c45-76b90fddc30e","test":1,"at":1,"tmax":1000,"bidder":"tappx","imp":[{"video":{"mimes":["video/mp4","application/javascript"],"minduration":3,"maxduration":30,"startdelay":5,"playbackmethod":[1,3],"api":[1,2],"protocols":[2,3],"battr":[13,14],"linearity":1,"placement":2,"minbitrate":10,"maxbitrate":10,"w":320,"h":250},"id":"2398241a5a860b","tagid":"localhost_typeAdBanVid_windows","secure":1,"bidfloor":0.005,"ext":{"bidder":{"tappxkey":"pub-1234-desktop-1234","endpoint":"vz34906po","host":"https://vz34906po.pub.tappx.com/rtb/","bidfloor":0.005}}}],"device":{"os":"windows","ip":"peer","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36","h":864,"w":1536,"dnt":0,"language":"en","make":"Google Inc."},"params":{"host":"tappx.com","bidfloor":0.005},"regs":{"gdpr":0,"ext":{}}}', 'bids': {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com/rtb/v2/', 'tappxkey': 'pub-1234-desktop-1234', 'endpoint': 'VZ12TESTCTV', 'bidfloor': 0.005, 'test': true}, 'crumbs': {'pubcid': 'dccfe922-3823-4676-b7b2-e5ed8743154e'}, 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video-ad-div'}}}, 'renderer': {'options': {'text': 'Tappx Outstream Video'}}, 'mediaTypes': {'video': {'mimes': ['video/mp4', 'application/javascript'], 'minduration': 3, 'maxduration': 30, 'startdelay': 5, 'playbackmethod': [1, 3], 'api': [1, 2], 'protocols': [2, 3], 'battr': [13, 14], 'linearity': 1, 'placement': 2, 'minbitrate': 10, 'maxbitrate': 10, 'w': 320, 'h': 250}}, 'adUnitCode': 'video-ad-div', 'transactionId': 'ed41c805-d14c-49c3-954d-26b98b2aa2c2', 'sizes': [[320, 250]], 'bidId': '28f49c71b13f2f', 'bidderRequestId': '1401710496dc7', 'auctionId': 'e807363f-3095-43a8-a4a6-f44196cb7318', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}} describe('Tappx bid adapter', function () { /** @@ -157,6 +158,15 @@ describe('Tappx bid adapter', function () { assert.isTrue(spec.isBidRequestValid(badBidRequestNwEp.bids[0])); }); + it('should return false mimes param is missing', function () { + let badBidRequest_mimes = c_BIDDERREQUEST_V; + delete badBidRequest_mimes.bids.mediaTypes.video; + badBidRequest_mimes.bids.mediaTypes.video = {}; + badBidRequest_mimes.bids.mediaTypes.video.context = 'instream'; + badBidRequest_mimes.bids.mediaTypes.video.playerSize = [320, 250]; + assert.isFalse(spec.isBidRequestValid(badBidRequest_mimes.bids), badBidRequest_mimes); + }); + it('should return false for not instream requests', function () { let badBidRequest_v = c_BIDDERREQUEST_V; delete badBidRequest_v.bids.mediaTypes.banner; @@ -186,7 +196,7 @@ describe('Tappx bid adapter', function () { const payload = JSON.parse(request[0].data); expect(payload.regs.gdpr).to.exist.and.to.be.true; - expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); + expect(payload.user.ext.consent).to.exist.and.to.equal(c_CONSENTSTRING); expect(payload.regs.ext.us_privacy).to.exist; }); @@ -356,4 +366,52 @@ describe('Tappx bid adapter', function () { expect(payload.imp[0].bidfloor).to.equal(1.23); }); }) + + describe('_getHostInfo tests', function() { + const HOST_VALIDBIDREQ = {}; + HOST_VALIDBIDREQ.bidder = 'tappx'; + HOST_VALIDBIDREQ.params = {}; + HOST_VALIDBIDREQ.params.endpoint = 'ZZ1234PBJS'; + + it('Test testing endpoints', function() { + let testHostValidRequest = HOST_VALIDBIDREQ; + testHostValidRequest.params.host = 'testing.xxx.tappx.com\/rtb\/v2\/'; + let testHostObject = _getHostInfo(testHostValidRequest); + assert.isObject(testHostObject); + expect(testHostObject.newEndpoint).to.be.false; + expect(testHostObject.endpoint).to.be.equal(testHostValidRequest.params.endpoint); + }) + it('Test classic endpoints', function() { + let classicHostValidRequest = HOST_VALIDBIDREQ; + classicHostValidRequest.params.host = 'xxx.xxx.tappx.com\/rtb\/v2\/'; + let classicHostObject = _getHostInfo(classicHostValidRequest); + assert.isObject(classicHostObject); + expect(classicHostObject.newEndpoint).to.be.false; + expect(classicHostObject.endpoint).to.be.equal(classicHostValidRequest.params.endpoint); + }) + it('Test new endpoints', function() { + let newHostValidRequest = HOST_VALIDBIDREQ; + newHostValidRequest.params.host = 'zz1111xxx.xxx.tappx.com\/rtb\/v2\/'; + let newHostObject = _getHostInfo(newHostValidRequest); + assert.isObject(newHostObject); + expect(newHostObject.newEndpoint).to.be.true; + expect(newHostObject.endpoint).to.be.equal('zz1111xxx'); + }) + }) + + describe('_checkParamDataType tests', function() { + it('should return the expected datatypes', function () { + assert.isString(_checkParamDataType('Right string', 'test', 'string')); + assert.isBoolean(_checkParamDataType('Right bool', true, 'boolean')); + assert.isNumber(_checkParamDataType('Right number', 10, 'number')); + assert.isArray(_checkParamDataType('Right array', [10, 11], 'array')); + }); + + it('should return undefined var for wrong datatypes', function () { + expect(_checkParamDataType('Wrong string', 10, 'string')).to.be.undefined; + expect(_checkParamDataType('Wrong bool', 10, 'boolean')).to.be.undefined; + expect(_checkParamDataType('Wrong number', 'one', 'number')).to.be.undefined; + expect(_checkParamDataType('Wrong array', false, 'array')).to.be.undefined; + }); + }) }); From c6ea12a1ffb4a8024ce6a60ba00d7a24f6a2877f Mon Sep 17 00:00:00 2001 From: Sleiman Jneidi Date: Tue, 8 Jun 2021 16:27:48 +0100 Subject: [PATCH 1112/1476] Quantcast Bid Adapter: uses video mediatypes to read OpenRTB parameters (#6932) --- modules/quantcastBidAdapter.js | 50 +++++------ test/spec/modules/quantcastBidAdapter_spec.js | 83 +++++++++++++++++-- 2 files changed, 100 insertions(+), 33 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 408f05d33cb..ecb5ad3176e 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -24,33 +24,33 @@ export const QUANTCAST_FPA = '__qca'; export const storage = getStorageManager(QUANTCAST_VENDOR_ID, BIDDER_CODE); function makeVideoImp(bid) { - const video = {}; - if (bid.params.video) { - video['mimes'] = bid.params.video.mimes; - video['minduration'] = bid.params.video.minduration; - video['maxduration'] = bid.params.video.maxduration; - video['protocols'] = bid.params.video.protocols; - video['startdelay'] = bid.params.video.startdelay; - video['linearity'] = bid.params.video.linearity; - video['battr'] = bid.params.video.battr; - video['maxbitrate'] = bid.params.video.maxbitrate; - video['playbackmethod'] = bid.params.video.playbackmethod; - video['delivery'] = bid.params.video.delivery; - video['placement'] = bid.params.video.placement; - video['api'] = bid.params.video.api; - } - if (bid.mediaTypes.video.mimes) { - video['mimes'] = bid.mediaTypes.video.mimes; + const videoInMediaType = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const videoInParams = utils.deepAccess(bid, 'params.video') || {}; + const video = Object.assign({}, videoInParams, videoInMediaType); + + if (video.playerSize) { + video.w = video.playerSize[0]; + video.h = video.playerSize[1]; } - if (utils.isArray(bid.mediaTypes.video.playerSize[0])) { - video['w'] = bid.mediaTypes.video.playerSize[0][0]; - video['h'] = bid.mediaTypes.video.playerSize[0][1]; - } else { - video['w'] = bid.mediaTypes.video.playerSize[0]; - video['h'] = bid.mediaTypes.video.playerSize[1]; + const videoCopy = { + mimes: video.mimes, + minduration: video.minduration, + maxduration: video.maxduration, + protocols: video.protocols, + startdelay: video.startdelay, + linearity: video.linearity, + battr: video.battr, + maxbitrate: video.maxbitrate, + playbackmethod: video.playbackmethod, + delivery: video.delivery, + placement: video.placement, + api: video.api, + w: video.w, + h: video.h } + return { - video: video, + video: videoCopy, placementCode: bid.placementCode, bidFloor: bid.params.bidFloor || DEFAULT_BID_FLOOR }; @@ -270,9 +270,9 @@ export const spec = { if (dealId !== undefined && dealId) { result['dealId'] = dealId; } - result.meta = {}; if (meta !== undefined && meta.advertiserDomains && utils.isArray(meta.advertiserDomains)) { + result.meta = {}; result.meta.advertiserDomains = meta.advertiserDomains; } diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 584f4dcbaf4..5e0d129581c 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -47,16 +47,15 @@ describe('Quantcast adapter', function () { storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); }); - function setupVideoBidRequest(videoParams) { + function setupVideoBidRequest(videoParams, mediaTypesParams) { bidRequest.params = { publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast // Video object as specified in OpenRTB 2.5 video: videoParams }; - bidRequest['mediaTypes'] = { - video: { - context: 'instream', - playerSize: [600, 300] + if (mediaTypesParams) { + bidRequest['mediaTypes'] = { + video: mediaTypesParams } } }; @@ -175,6 +174,69 @@ describe('Quantcast adapter', function () { delivery: [1], // optional placement: 1, // optional api: [2, 3] // optional + }, { + context: 'instream', + playerSize: [600, 300] + }); + + const requests = qcSpec.buildRequests([bidRequest], bidderRequest); + const expectedVideoBidRequest = { + publisherId: QUANTCAST_TEST_PUBLISHER, + requestId: '2f7b179d443f14', + imp: [ + { + video: { + mimes: ['video/mp4'], + minduration: 3, + maxduration: 5, + protocols: [3], + startdelay: 1, + linearity: 1, + battr: [1, 2], + maxbitrate: 10, + playbackmethod: [1], + delivery: [1], + placement: 1, + api: [2, 3], + w: 600, + h: 300 + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + } + ], + site: { + page: 'http://example.com/hello.html', + referrer: 'http://example.com/hello.html', + domain: 'example.com' + }, + bidId: '2f7b179d443f14', + gdprSignal: 0, + uspSignal: 0, + coppa: 0, + prebidJsVersion: '$prebid.version$', + fpa: '' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); + }); + + it('sends video bid requests containing all the required parameters from mediaTypes', function() { + setupVideoBidRequest(null, { + mimes: ['video/mp4'], // required + minduration: 3, // optional + maxduration: 5, // optional + protocols: [3], // optional + startdelay: 1, // optional + linearity: 1, // optinal + battr: [1, 2], // optional + maxbitrate: 10, // optional + playbackmethod: [1], // optional + delivery: [1], // optional + placement: 1, // optional + api: [2, 3], // optional + context: 'instream', + playerSize: [600, 300] }); const requests = qcSpec.buildRequests([bidRequest], bidderRequest); @@ -222,6 +284,9 @@ describe('Quantcast adapter', function () { it('overrides video parameters with parameters from adunit', function() { setupVideoBidRequest({ mimes: ['video/mp4'] + }, { + context: 'instream', + playerSize: [600, 300] }); bidRequest.mediaTypes.video.mimes = ['video/webm']; @@ -257,7 +322,10 @@ describe('Quantcast adapter', function () { }); it('sends video bid request when no video parameters are given', function () { - setupVideoBidRequest(null); + setupVideoBidRequest(null, { + context: 'instream', + playerSize: [600, 300] + }); const requests = qcSpec.buildRequests([bidRequest], bidderRequest); const expectedVideoBidRequest = { @@ -766,8 +834,7 @@ describe('Quantcast adapter', function () { creativeId: undefined, ad: undefined, netRevenue: QUANTCAST_NET_REVENUE, - currency: 'USD', - meta: {} + currency: 'USD' }; const interpretedResponse = qcSpec.interpretResponse(videoResponse); From b62d7945d25b8fdca124c2a1ac879560b4a8e598 Mon Sep 17 00:00:00 2001 From: Paris Holley Date: Tue, 8 Jun 2021 11:47:32 -0400 Subject: [PATCH 1113/1476] support for #6650 and fixed tests (#6969) --- modules/mantisBidAdapter.js | 5 +++- test/spec/modules/mantisBidAdapter_spec.js | 31 +++++++++++++++------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/modules/mantisBidAdapter.js b/modules/mantisBidAdapter.js index 960fbe27c73..61b7c31c8e4 100644 --- a/modules/mantisBidAdapter.js +++ b/modules/mantisBidAdapter.js @@ -1,7 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); +export const storage = getStorageManager(); function inIframe() { try { @@ -247,6 +247,9 @@ export const spec = { width: ad.width, height: ad.height, ad: ad.html, + meta: { + advertiserDomains: ad.domains || [] + }, ttl: ad.ttl || serverResponse.body.ttl || 86400, creativeId: ad.view, netRevenue: true, diff --git a/test/spec/modules/mantisBidAdapter_spec.js b/test/spec/modules/mantisBidAdapter_spec.js index d9ab3c69a24..579f41e620d 100644 --- a/test/spec/modules/mantisBidAdapter_spec.js +++ b/test/spec/modules/mantisBidAdapter_spec.js @@ -1,19 +1,18 @@ import {expect} from 'chai'; -import {spec} from 'modules/mantisBidAdapter.js'; +import {spec, storage} from 'modules/mantisBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; import {sfPostMessage, iframePostMessage} from 'modules/mantisBidAdapter'; describe('MantisAdapter', function () { const adapter = newBidder(spec); - let sandbox; + const sandbox = sinon.sandbox.create(); let clock; - beforeEach(function() { - sandbox = sinon.sandbox.create(); + beforeEach(function () { clock = sandbox.useFakeTimers(); }); - afterEach(function() { + afterEach(function () { sandbox.restore(); }); @@ -171,13 +170,12 @@ describe('MantisAdapter', function () { }); it('use storage uuid', function () { - window.localStorage.setItem('mantis:uuid', 'bar'); + sandbox.stub(storage, 'hasLocalStorage').callsFake(() => true); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('mantis:uuid').returns('bar'); const request = spec.buildRequests(bidRequests); expect(request.url).to.include('uuid=bar'); - - window.localStorage.removeItem('mantis:uuid'); }); it('detect amp', function () { @@ -249,6 +247,9 @@ describe('MantisAdapter', function () { ad: '', creativeId: 'view', netRevenue: true, + meta: { + advertiserDomains: [] + }, currency: 'USD' } ]; @@ -268,6 +269,7 @@ describe('MantisAdapter', function () { bid: 'bid', cpm: 1, view: 'view', + domains: ['foobar.com'], width: 300, height: 250, html: '' @@ -286,6 +288,9 @@ describe('MantisAdapter', function () { ad: '', creativeId: 'view', netRevenue: true, + meta: { + advertiserDomains: ['foobar.com'] + }, currency: 'USD' } ]; @@ -305,6 +310,7 @@ describe('MantisAdapter', function () { cpm: 1, view: 'view', width: 300, + domains: ['foobar.com'], height: 250, html: '' } @@ -322,15 +328,22 @@ describe('MantisAdapter', function () { ad: '', creativeId: 'view', netRevenue: true, + meta: { + advertiserDomains: ['foobar.com'] + }, currency: 'USD' } ]; let bidderRequest; + sandbox.stub(storage, 'hasLocalStorage').returns(true); + const spy = sandbox.spy(storage, 'setDataInLocalStorage'); + let result = spec.interpretResponse(response, {bidderRequest}); + + expect(spy.calledWith('mantis:uuid', 'uuid')); expect(result[0]).to.deep.equal(expectedResponse[0]); expect(window.mantis_uuid).to.equal(response.body.uuid); - expect(window.localStorage.getItem('mantis:uuid')).to.equal(response.body.uuid); }); it('no ads returned', function () { From d5750fc15e4a93fe51f390263fbdabdc8a3379df Mon Sep 17 00:00:00 2001 From: yuvalgg <57989766+yuvalgg@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:52:20 +0300 Subject: [PATCH 1114/1476] Intentiq id System: module maintenance (#6745) * Update intentIqIdSystem.js * decode fix * tests fix * Alert fix --- modules/intentIqIdSystem.js | 51 ++++++---------------- test/spec/modules/intentIqIdSystem_spec.js | 28 ++++++------ 2 files changed, 27 insertions(+), 52 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 98732da95d5..e12a765c086 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -17,34 +17,7 @@ export const FIRST_PARTY_KEY = '_iiq_fdata'; export const storage = getStorageManager(undefined, MODULE_NAME); -const NOT_AVAILABLE = 'NA'; - -/** - * Verify the response is valid - Id value or Not Found (ignore not available response) - * @param response - * @param respJson - parsed json response - * @returns {boolean} - */ -function isValidResponse(response, respJson) { - if (!response || response == '' || response === NOT_AVAILABLE) { - // Empty or NA response - return false; - } else if (respJson && (respJson.RESULT === NOT_AVAILABLE || respJson.data == '' || respJson.data === NOT_AVAILABLE)) { - // Response type is json with value NA - return false; - } else { return true; } -} - -/** - * Verify the response json is valid - * @param respJson - parsed json response - * @returns {boolean} - */ -function isValidResponseJson(respJson) { - if (respJson && 'data' in respJson) { - return true; - } else { return false; } -} +const INVALID_ID = 'INVALID_ID'; /** * Generate standard UUID string @@ -130,7 +103,7 @@ export const intentIqIdSubmodule = { * @returns {{intentIqId: {string}}|undefined} */ decode(value) { - return isValidResponse(value, undefined) ? { 'intentIqId': value } : undefined; + return value && value != '' && INVALID_ID != value ? { 'intentIqId': value } : undefined; }, /** * performs action to obtain id and return a value in the callback's response argument @@ -157,23 +130,25 @@ export const intentIqIdSubmodule = { let url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; url += configParams.pcid ? '&pcid=' + encodeURIComponent(configParams.pcid) : ''; url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; - if (firstPartyData) { - url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : ''; - url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : ''; - } + url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : ''; + url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : ''; const resp = function (callback) { const callbacks = { success: response => { let respJson = tryParse(response); - if (isValidResponse(response, respJson) && isValidResponseJson(respJson)) { + // If response is a valid json and should save is true + if (respJson && respJson.ls) { // Store pid field if found in response json - if (firstPartyData && 'pcid' in firstPartyData && 'pid' in respJson) { - firstPartyData = { - 'pcid': firstPartyData.pcid, - 'pid': respJson.pid } + if ('pid' in respJson) { + firstPartyData.pid = respJson.pid; storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); } + + // If should save and data is empty, means we should save as INVALID_ID + if (respJson.data == '') { + respJson.data = INVALID_ID; + } callback(respJson.data); } else { callback(); diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 7e819195b9f..8ea30a6ba92 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -55,10 +55,10 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should ignore NA and invalid responses in decode', function () { + it('should ignore INVALID_ID and invalid responses in decode', function () { // let resp = JSON.stringify({'RESULT': 'NA'}); // expect(intentIqIdSubmodule.decode(resp)).to.equal(undefined); - expect(intentIqIdSubmodule.decode('NA')).to.equal(undefined); + expect(intentIqIdSubmodule.decode('INVALID_ID')).to.equal(undefined); expect(intentIqIdSubmodule.decode('')).to.equal(undefined); expect(intentIqIdSubmodule.decode(undefined)).to.equal(undefined); }); @@ -134,37 +134,37 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should ignore {RESULT: NA} in get id', function () { + it('save result if ls=true', function () { let callBackSpy = sinon.spy(); - let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, - JSON.stringify({RESULT: 'NA'}) + JSON.stringify({pid: 'test_pid', data: 'test_personid', ls: true}) ); expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.args[0][0]).to.be.undefined; + expect(callBackSpy.args[0][0]).to.be.eq('test_personid'); }); - it('should ignore NA in get id', function () { + it('dont save result if ls=false', function () { let callBackSpy = sinon.spy(); - let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; + let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pcid=12&pai=11&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, - 'NA' + JSON.stringify({pid: 'test_pid', data: 'test_personid', ls: false}) ); expect(callBackSpy.calledOnce).to.be.true; expect(callBackSpy.args[0][0]).to.be.undefined; }); - it('should parse result from json response', function () { + it('save result as INVALID_ID on empty data and ls=true ', function () { let callBackSpy = sinon.spy(); let submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; submoduleCallback(callBackSpy); @@ -173,9 +173,9 @@ describe('IntentIQ tests', function () { request.respond( 200, responseHeader, - JSON.stringify({pid: 'test_pid', data: 'test_personid'}) + JSON.stringify({pid: 'test_pid', data: '', ls: true}) ); expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.args[0][0]).to.be.eq('test_personid'); + expect(callBackSpy.args[0][0]).to.be.eq('INVALID_ID'); }); }); From a532e4a30390d92966f1a7af8cd01ffc2a6b5d45 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:23:53 +0200 Subject: [PATCH 1115/1476] oneTag Bid Adapter: edit page fields (#6971) --- modules/onetagBidAdapter.js | 5 ++++ test/spec/modules/onetagBidAdapter_spec.js | 31 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 0974f9db4cf..86f29b0dd73 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -201,6 +201,10 @@ function getPageInfo() { topmostFrame.document.referrer !== '' ? topmostFrame.document.referrer : null, + ancestorOrigin: + window.location.ancestorOrigins && window.location.ancestorOrigins[0] != null + ? window.location.ancestorOrigins[0] + : null, masked: currentFrameNesting, wWidth: topmostFrame.innerWidth, wHeight: topmostFrame.innerHeight, @@ -240,6 +244,7 @@ function requestsToBids(bidRequests) { videoObj['protocols'] = bidRequest.mediaTypes.video.protocols; videoObj['maxDuration'] = bidRequest.mediaTypes.video.maxduration; videoObj['api'] = bidRequest.mediaTypes.video.api; + videoObj['playbackmethod'] = bidRequest.mediaTypes.video.playbackmethod || []; videoObj['type'] = VIDEO; return videoObj; }); diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 5a1f30a0de8..f51e95039ea 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -142,6 +142,9 @@ describe('onetag', function () { 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'); @@ -153,9 +156,33 @@ describe('onetag', function () { for (let i = 0; i < bids.length; i++) { const bid = bids[i]; if (hasTypeVideo(bid)) { - expect(bid).to.have.all.keys('adUnitCode', 'auctionId', 'bidId', 'bidderRequestId', 'pubId', 'transactionId', 'context', 'mimes', 'playerSize', 'protocols', 'maxDuration', 'api', 'type'); + expect(bid).to.have.all.keys( + 'adUnitCode', + 'auctionId', + 'bidId', + 'bidderRequestId', + 'pubId', + 'transactionId', + 'context', + 'mimes', + 'playerSize', + 'protocols', + 'maxDuration', + 'api', + 'playbackmethod', + 'type' + ); } else if (isValid(BANNER, bid)) { - expect(bid).to.have.all.keys('adUnitCode', 'auctionId', 'bidId', 'bidderRequestId', 'pubId', 'transactionId', 'sizes', 'type'); + expect(bid).to.have.all.keys( + 'adUnitCode', + 'auctionId', + 'bidId', + 'bidderRequestId', + 'pubId', + 'transactionId', + 'sizes', + 'type' + ); } expect(bid.bidId).to.be.a('string'); expect(bid.pubId).to.be.a('string'); From d496c6846c573ccb55a562e8678d86587fb27f26 Mon Sep 17 00:00:00 2001 From: afsheenb Date: Tue, 8 Jun 2021 14:21:21 -0400 Subject: [PATCH 1116/1476] ozone 2.6.0 adapter updates (#6946) Co-authored-by: Afsheen Bigdeli --- modules/ozoneBidAdapter.js | 362 ++++++++++++++-------- test/spec/modules/ozoneBidAdapter_spec.js | 284 ++++++++++++++--- 2 files changed, 484 insertions(+), 162 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 8ada9d59ae2..928344be74f 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -11,16 +11,17 @@ const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & 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.5.0'; +const OZONEVERSION = '2.6.0'; export const spec = { gvlid: 524, - aliases: [{ code: 'lmc' }], + aliases: [{code: 'lmc', gvlid: 524}, {code: 'newspassid', gvlid: 524}], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], - cookieSyncBag: {'publisherId': null, 'siteId': null, 'userIdObject': {}}, // variables we want to make available to cookie sync - propertyBag: {'pageId': null, 'buildRequestsStart': 0, 'buildRequestsEnd': 0}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ + cookieSyncBag: {publisherId: null, siteId: null, userIdObject: {}}, // variables we want to make available to cookie sync + propertyBag: {pageId: null, buildRequestsStart: 0, buildRequestsEnd: 0, endpointOverride: null}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ whitelabel_defaults: { 'logId': 'OZONE', 'bidder': 'ozone', @@ -40,19 +41,45 @@ export const spec = { this.propertyBag.whitelabel.logId = bidder.toUpperCase(); this.propertyBag.whitelabel.bidder = bidder; let bidderConfig = config.getConfig(bidder) || {}; + utils.logInfo('got bidderConfig: ', JSON.parse(JSON.stringify(bidderConfig))); if (bidderConfig.kvpPrefix) { this.propertyBag.whitelabel.keyPrefix = bidderConfig.kvpPrefix; } + let arr = this.getGetParametersAsObject(); if (bidderConfig.endpointOverride) { if (bidderConfig.endpointOverride.origin) { + this.propertyBag.endpointOverride = bidderConfig.endpointOverride.origin; this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.origin + AUCTIONURI; this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.origin + OZONECOOKIESYNC; } - if (bidderConfig.endpointOverride.rendererUrl) { + if (arr.hasOwnProperty('renderer')) { + if (arr.renderer.match('%3A%2F%2F')) { + this.propertyBag.whitelabel.rendererUrl = decodeURIComponent(arr['renderer']); + } else { + this.propertyBag.whitelabel.rendererUrl = arr['renderer']; + } + } else if (bidderConfig.endpointOverride.rendererUrl) { this.propertyBag.whitelabel.rendererUrl = bidderConfig.endpointOverride.rendererUrl; } + if (bidderConfig.endpointOverride.cookieSyncUrl) { + this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.cookieSyncUrl; + } + if (bidderConfig.endpointOverride.auctionUrl) { + this.propertyBag.endpointOverride = bidderConfig.endpointOverride.auctionUrl; + this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.auctionUrl; + } } - this.logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); + try { + if (arr.hasOwnProperty('auction') && arr.auction === 'dev') { + utils.logInfo('GET: auction=dev'); + this.propertyBag.whitelabel.auctionUrl = ORIGIN_DEV + AUCTIONURI; + } + if (arr.hasOwnProperty('cookiesync') && arr.cookiesync === 'dev') { + utils.logInfo('GET: cookiesync=dev'); + this.propertyBag.whitelabel.cookieSyncUrl = ORIGIN_DEV + OZONECOOKIESYNC; + } + } catch (e) {} + utils.logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); }, getAuctionUrl() { return this.propertyBag.whitelabel.auctionUrl; @@ -63,27 +90,6 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, - /** - * wrappers for this.logInfo logWarn & logError, to add the proper prefix - */ - logInfo() { - if (!this.propertyBag.whitelabel) { return; } - let args = arguments; - args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; - utils.logInfo.apply(this, args); - }, - logError() { - if (!this.propertyBag.whitelabel) { return; } - let args = arguments; - args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; - utils.logError.apply(this, args); - }, - logWarn() { - if (!this.propertyBag.whitelabel) { return; } - let args = arguments; - args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; - utils.logWarn.apply(this, args); - }, /** * Basic check to see whether required parameters are in the request. * @param bid @@ -91,62 +97,62 @@ export const spec = { */ isBidRequestValid(bid) { this.loadWhitelabelData(bid); - this.logInfo('isBidRequestValid : ', config.getConfig(), bid); + utils.logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code if (!(bid.params.hasOwnProperty('placementId'))) { - this.logError('BID ADAPTER VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + utils.logError('VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!this.isValidPlacementId(bid.params.placementId)) { - this.logError('BID ADAPTER VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); + utils.logError('VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('publisherId'))) { - this.logError('BID ADAPTER VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + utils.logError('VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - this.logError('BID ADAPTER VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); + utils.logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('siteId'))) { - this.logError('BID ADAPTER VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + utils.logError('VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) { - this.logError('BID ADAPTER VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); + utils.logError('VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); return false; } if (bid.params.hasOwnProperty('customParams')) { - this.logError('BID ADAPTER VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); + utils.logError('VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); return false; } if (bid.params.hasOwnProperty('customData')) { if (!Array.isArray(bid.params.customData)) { - this.logError('BID ADAPTER VALIDATION FAILED : customData is not an Array', adUnitCode); + utils.logError('VALIDATION FAILED : customData is not an Array', adUnitCode); return false; } if (bid.params.customData.length < 1) { - this.logError('BID ADAPTER VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); + utils.logError('VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); return false; } if (!(bid.params.customData[0]).hasOwnProperty('targeting')) { - this.logError('BID ADAPTER VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); + utils.logError('VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); return false; } if (typeof bid.params.customData[0]['targeting'] != 'object') { - this.logError('BID ADAPTER VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); + utils.logError('VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); return false; } } if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - this.logError('No video context key/value in bid. Rejecting bid: ', bid); + utils.logError('No video context key/value in bid. Rejecting bid: ', bid); return false; } if (bid.mediaTypes[VIDEO].context !== 'instream' && bid.mediaTypes[VIDEO].context !== 'outstream') { - this.logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); + utils.logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); return false; } } @@ -166,7 +172,7 @@ export const spec = { this.propertyBag.buildRequestsStart = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; - this.logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${OZONEVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); + utils.logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${OZONEVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); // First check - is there any config to block this request? if (this.blockTheRequest()) { return []; @@ -178,26 +184,20 @@ export const spec = { this.cookieSyncBag.publisherId = utils.deepAccess(validBidRequests[0], 'params.publisherId'); htmlParams = validBidRequests[0].params; } - this.logInfo('cookie sync bag', this.cookieSyncBag); + utils.logInfo('cookie sync bag', this.cookieSyncBag); let singleRequest = this.getWhitelabelConfigItem('ozone.singleRequest'); singleRequest = singleRequest !== false; // undefined & true will be true - this.logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); + utils.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'] - if (bidderRequest && bidderRequest.gdprConsent) { - this.logInfo('ADDING GDPR info'); - let apiVersion = bidderRequest.gdprConsent.apiVersion || '1'; - ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; - if (ozoneRequest.regs.ext.gdpr) { - ozoneRequest.user = ozoneRequest.user || {}; - ozoneRequest.user.ext = {'consent': bidderRequest.gdprConsent.consentString}; - } else { - this.logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); - } - } else { - this.logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object was present.'); + // First party data module : look for ortb2 in setconfig & set the User object. NOTE THAT this should happen before we set the consentString + let fpd = config.getConfig('ortb2'); + if (fpd && utils.deepAccess(fpd, 'user')) { + utils.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 @@ -214,18 +214,18 @@ export const spec = { let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { - this.logInfo('no mediaTypes detected - will use the sizes array in the config root'); + utils.logInfo('no mediaTypes detected - will use the sizes array in the config root'); arrBannerSizes = ozoneBidRequest.sizes; } else { - this.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); + utils.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); } } else { if (ozoneBidRequest.mediaTypes.hasOwnProperty(BANNER)) { arrBannerSizes = ozoneBidRequest.mediaTypes[BANNER].sizes; /* Note - if there is a sizes element in the config root it will be pushed into here */ - this.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); + utils.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); } if (ozoneBidRequest.mediaTypes.hasOwnProperty(VIDEO)) { - this.logInfo('openrtb 2.5 compliant video'); + utils.logInfo('openrtb 2.5 compliant video'); // examine all the video attributes in the config, and either put them into obj.video if allowed by IAB2.5 or else in to obj.video.ext if (typeof ozoneBidRequest.mediaTypes[VIDEO] == 'object') { let childConfig = utils.deepAccess(ozoneBidRequest, 'params.video', {}); @@ -234,25 +234,33 @@ export const spec = { } // we need to duplicate some of the video values let wh = getWidthAndHeightFromVideoObject(obj.video); - this.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); + utils.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); if (wh && typeof wh === 'object') { obj.video.w = wh['w']; obj.video.h = wh['h']; if (playerSizeIsNestedArray(obj.video)) { // this should never happen; it was in the original spec for this change though. - this.logInfo('setting obj.video.format to be an array of objects'); + utils.logInfo('setting obj.video.format to be an array of objects'); obj.video.ext.format = [wh]; } else { - this.logInfo('setting obj.video.format to be an object'); + utils.logInfo('setting obj.video.format to be an object'); obj.video.ext.format = wh; } } else { - this.logWarn('cannot set w, h & format values for video; the config is not right'); + utils.logWarn('cannot set w, h & format values for video; the config is not right'); } } // Native integration is not complete yet if (ozoneBidRequest.mediaTypes.hasOwnProperty(NATIVE)) { obj.native = ozoneBidRequest.mediaTypes[NATIVE]; - this.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); + utils.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); + } + // is the publisher specifying floors, and is the floors module enabled? + if (ozoneBidRequest.hasOwnProperty('getFloor')) { + utils.logInfo('This bidRequest object has property: getFloor'); + obj.floor = this.getFloorObjectForAuction(ozoneBidRequest); + utils.logInfo('obj.floor is : ', obj.floor); + } else { + utils.logInfo('This bidRequest object DOES NOT have property: getFloor'); } } if (arrBannerSizes.length > 0) { @@ -268,17 +276,18 @@ export const spec = { } // these 3 MUST exist - we check them in the validation method obj.placementId = placementId; - // build the imp['ext'] object - obj.ext = {'prebid': {'storedrequest': {'id': placementId}}}; + // build the imp['ext'] object - NOTE - Dont obliterate anything that' already in obj.ext + utils.deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); + // obj.ext = {'prebid': {'storedrequest': {'id': placementId}}}; obj.ext[whitelabelBidder] = {}; obj.ext[whitelabelBidder].adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' obj.ext[whitelabelBidder].transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } - this.logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); + utils.logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); if (isTestMode != null) { - this.logInfo('setting isTestMode to ', isTestMode); + utils.logInfo('setting isTestMode to ', isTestMode); if (obj.ext[whitelabelBidder].hasOwnProperty('customData')) { for (let i = 0; i < obj.ext[whitelabelBidder].customData.length; i++) { obj.ext[whitelabelBidder].customData[i]['targeting'][wlOztestmodeKey] = isTestMode; @@ -288,6 +297,19 @@ export const spec = { obj.ext[whitelabelBidder].customData[0].targeting[wlOztestmodeKey] = isTestMode; } } + if (fpd && utils.deepAccess(fpd, 'site')) { + // attach the site fpd into exactly : imp[n].ext.[whitelabel].customData.0.targeting + utils.logInfo('added FPD site object'); + if (utils.deepAccess(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', false)) { + obj.ext[whitelabelBidder].customData[0].targeting = Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); + // let keys = utils.getKeys(fpd.site); + // for (let i = 0; i < keys.length; i++) { + // obj.ext[whitelabelBidder].customData[0].targeting[keys[i]] = fpd.site[keys[i]]; + // } + } else { + utils.deepSetValue(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', fpd.site); + } + } return obj; }); @@ -305,20 +327,27 @@ export const spec = { } extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') - this.logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); + utils.logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { extObj[whitelabelBidder][whitelabelPrefix + '_omp_floor'] = ozOmpFloorDollars; } else if (typeof ozOmpFloorDollars !== 'undefined') { - this.logError(`${whitelabelPrefix}_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. ${whitelabelPrefix}_omp_floor: 1.55. You have it set as a ` + (typeof ozOmpFloorDollars)); + utils.logError(`${whitelabelPrefix}_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. ${whitelabelPrefix}_omp_floor: 1.55. You have it set as a ` + (typeof ozOmpFloorDollars)); } let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = utils.isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; extObj[whitelabelBidder][whitelabelPrefix + '_kvp_rw'] = useOzWhitelistAdserverKeys ? 1 : 0; if (whitelabelBidder != 'ozone') { - this.logInfo('setting aliases object'); + utils.logInfo('setting aliases object'); extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; } + // 20210413 - adding a set of GET params to pass to auction + 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; } + // extObj.ortb2 = config.getConfig('ortb2'); // original test location var userExtEids = this.generateEids(validBidRequests); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { @@ -328,6 +357,26 @@ export const spec = { }; ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] === 'true') ? 1 : 0; + // this should come as late as possible so it overrides any user.ext.consent value + if (bidderRequest && bidderRequest.gdprConsent) { + utils.logInfo('ADDING GDPR info'); + let apiVersion = utils.deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); + ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; + if (utils.deepAccess(ozoneRequest, 'regs.ext.gdpr')) { + utils.deepSetValue(ozoneRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } else { + utils.logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); + } + } else { + utils.logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object'); + } + if (bidderRequest && bidderRequest.uspConsent) { + utils.logInfo('ADDING CCPA info'); + utils.deepSetValue(ozoneRequest, 'user.ext.uspConsent', bidderRequest.uspConsent); + } else { + utils.logInfo('WILL NOT ADD CCPA info; no bidderRequest.uspConsent.'); + } + // this is for 2.2.1 // coppa compliance if (config.getConfig('coppa') === true) { @@ -336,7 +385,7 @@ export const spec = { // return the single request object OR the array: if (singleRequest) { - this.logInfo('buildRequests starting to generate response for a single request'); + utils.logInfo('buildRequests starting to generate response for a single request'); ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? ozoneRequest.imp = tosendtags; @@ -349,14 +398,14 @@ export const spec = { data: JSON.stringify(ozoneRequest), bidderRequest: bidderRequest }; - this.logInfo('buildRequests request data for single = ', ozoneRequest); + utils.logInfo('buildRequests request data for single = ', JSON.parse(JSON.stringify(ozoneRequest))); this.propertyBag.buildRequestsEnd = new Date().getTime(); - this.logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); + utils.logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); return ret; } // not single request - pull apart the tosendtags array & return an array of objects each containing one element in the imp array. let arrRet = tosendtags.map(imp => { - this.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); + utils.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); imp.ext[whitelabelBidder].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object ozoneRequestSingle.id = imp.ext[whitelabelBidder].transactionId; // Unique ID of the bid request, provided by the exchange. @@ -365,7 +414,7 @@ export const spec = { ozoneRequestSingle.ext = extObj; ozoneRequestSingle.source = {'tid': imp.ext[whitelabelBidder].transactionId}; utils.deepSetValue(ozoneRequestSingle, 'user.ext.eids', userExtEids); - this.logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); + utils.logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { method: 'POST', url: this.getAuctionUrl(), @@ -374,9 +423,42 @@ export const spec = { }; }); this.propertyBag.buildRequestsEnd = new Date().getTime(); - this.logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); + utils.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: utils.deepAccess(bidRequestRef, 'mediaTypes.banner.sizes', null), + video: utils.deepAccess(bidRequestRef, 'mediaTypes.video.playerSize', null), + native: utils.deepAccess(bidRequestRef, 'mediaTypes.native.image.sizes', null) + } + utils.logInfo('getFloorObjectForAuction mediaTypesSizes : ', mediaTypesSizes); + let ret = {}; + if (mediaTypesSizes.banner) { + ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner}); + } + if (mediaTypesSizes.video) { + ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video}); + } + if (mediaTypesSizes.native) { + ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native}); + } + utils.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 @@ -392,8 +474,8 @@ export const spec = { let startTime = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; - this.logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); - this.logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); + utils.logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); + utils.logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; // note that serverResponse.id value is the auction_id we might want to use for reporting reasons. if (!serverResponse.hasOwnProperty('seatbid')) { @@ -404,12 +486,15 @@ export const spec = { } let arrAllBids = []; let enhancedAdserverTargeting = this.getWhitelabelConfigItem('ozone.enhancedAdserverTargeting'); - this.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + utils.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); if (typeof enhancedAdserverTargeting == 'undefined') { enhancedAdserverTargeting = true; } - this.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + utils.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + + // 2021-03-05 - comment this out for a build without adding adid to the response 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'; @@ -420,30 +505,32 @@ export const spec = { 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); - this.logInfo(`seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); + utils.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 = ozoneAddStandardProperties(sb.bid[j], defaultWidth, defaultHeight); + // prebid 4.0 compliance + thisBid.meta = {advertiserDomains: thisBid.adomain || []}; let videoContext = null; let isVideo = false; let bidType = utils.deepAccess(thisBid, 'ext.prebid.type'); - this.logInfo(`this bid type is : ${bidType}`, j); + utils.logInfo(`this bid type is : ${bidType}`, j); if (bidType === VIDEO) { isVideo = true; videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - this.logInfo('going to attach a renderer to OUTSTREAM video : ', j); + utils.logInfo('going to attach a renderer to OUTSTREAM video : ', j); thisBid.renderer = newRenderer(thisBid.bidId); } else { - this.logInfo('bid is not an outstream video, will not attach a renderer: ', j); + utils.logInfo('bid is not an outstream video, will not attach a renderer: ', j); } } let adserverTargeting = {}; if (enhancedAdserverTargeting) { let allBidsForThisBidid = ozoneGetAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); // add all the winning & non-winning bids for this bidId: - this.logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); + utils.logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); Object.keys(allBidsForThisBidid).forEach((bidderName, index, ar2) => { - this.logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); + utils.logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); // let bidderName = bidderNameWH.split('_')[0]; adserverTargeting[whitelabelPrefix + '_' + bidderName] = bidderName; adserverTargeting[whitelabelPrefix + '_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); @@ -473,21 +560,27 @@ export const spec = { }); } else { if (useOzWhitelistAdserverKeys) { - this.logWarn(`You have set a whitelist of adserver keys but this will be ignored because ${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`); + utils.logWarn(`You have set a whitelist of adserver keys but this will be ignored because ${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`); } else { - this.logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); + utils.logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); } } // also add in the winning bid, to be sent to dfp let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); + adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; + if (enhancedAdserverTargeting) { adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; + adserverTargeting[whitelabelPrefix + '_pb'] = winningBid.price; + adserverTargeting[whitelabelPrefix + '_pb_r'] = getRoundedBid(winningBid.price, bidType); + adserverTargeting[whitelabelPrefix + '_adId'] = String(winningBid.adId); + adserverTargeting[whitelabelPrefix + '_size'] = `${winningBid.width}x${winningBid.height}`; } if (useOzWhitelistAdserverKeys) { // delete any un-whitelisted keys - this.logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); + utils.logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); Object.keys(adserverTargeting).forEach(function(key) { if (ozWhitelistAdserverKeys.indexOf(key) === -1) { delete adserverTargeting[key]; } }); } thisBid.adserverTargeting = adserverTargeting; @@ -495,7 +588,7 @@ export const spec = { } } let endTime = new Date().getTime(); - this.logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + utils.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; }, /** @@ -539,8 +632,9 @@ export const spec = { return ret; }, // see http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs - getUserSyncs(optionsType, serverResponse, gdprConsent) { - this.logInfo('getUserSyncs optionsType, serverResponse, gdprConsent, cookieSyncBag', optionsType, serverResponse, gdprConsent, this.cookieSyncBag); + // us privacy: https://docs.prebid.org/dev-docs/modules/consentManagementUsp.html + getUserSyncs(optionsType, serverResponse, gdprConsent, usPrivacy) { + utils.logInfo('getUserSyncs optionsType', optionsType, 'serverResponse', serverResponse, 'gdprConsent', gdprConsent, 'usPrivacy', usPrivacy, 'cookieSyncBag', this.cookieSyncBag); if (!serverResponse || serverResponse.length === 0) { return []; } @@ -551,9 +645,13 @@ export const spec = { } arrQueryString.push('gdpr=' + (utils.deepAccess(gdprConsent, 'gdprApplies', false) ? '1' : '0')); arrQueryString.push('gdpr_consent=' + utils.deepAccess(gdprConsent, 'consentString', '')); - var objKeys = Object.getOwnPropertyNames(this.cookieSyncBag.userIdObject); - for (let idx in objKeys) { - let keyname = objKeys[idx]; + arrQueryString.push('usp_consent=' + (usPrivacy || '')); + // var objKeys = Object.getOwnPropertyNames(this.cookieSyncBag.userIdObject); + // for (let idx in objKeys) { + // let keyname = objKeys[idx]; + // arrQueryString.push(keyname + '=' + this.cookieSyncBag.userIdObject[keyname]); + // } + for (let keyname in this.cookieSyncBag.userIdObject) { arrQueryString.push(keyname + '=' + this.cookieSyncBag.userIdObject[keyname]); } arrQueryString.push('publisherId=' + this.cookieSyncBag.publisherId); @@ -565,7 +663,7 @@ export const spec = { if (strQueryString.length > 0) { strQueryString = '?' + strQueryString; } - this.logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); + utils.logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); return [{ type: 'iframe', url: this.getCookieSyncUrl() + strQueryString @@ -600,15 +698,15 @@ 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 - * NOTE that criteortus is deprecated & should be removed asap * @return map */ findAllUserIds(bidRequest) { var ret = {}; - // @todo - what is fabrick called & where to look for it? If it's a simple value then it will automatically be ok - let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus', - 'sharedid', 'lotamePanoramaId', 'fabrickId']; + // @todo - what is Neustar fabrick called & where to look for it? If it's a simple value then it will automatically be ok + // it is not in the table 'Bidder Adapter Implementation' on https://docs.prebid.org/dev-docs/modules/userId.html#prebidjs-adapters + let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; @@ -616,19 +714,37 @@ export const spec = { if (typeof (bidRequest.userId[key]) == 'string') { ret[key] = bidRequest.userId[key]; } else if (typeof (bidRequest.userId[key]) == 'object') { + utils.logError(`WARNING: findAllUserIds had to use first key in user object to get value for bid.userId key: ${key}. Prebid adapter should be updated.`); + // fallback - get the value of the first key in the object; this is NOT desirable behaviour ret[key] = bidRequest.userId[key][Object.keys(bidRequest.userId[key])[0]]; // cannot use Object.values } else { - this.logError(`failed to get string key value for userId : ${key}`); + utils.logError(`failed to get string key value for userId : ${key}`); } } } - var lipbid = utils.deepAccess(bidRequest.userId, 'lipb.lipbid'); + let lipbid = utils.deepAccess(bidRequest.userId, 'lipb.lipbid'); if (lipbid) { ret['lipb'] = {'lipbid': lipbid}; } + let id5id = utils.deepAccess(bidRequest.userId, 'id5id.uid'); + if (id5id) { + ret['id5id'] = id5id; + } + let parrableId = utils.deepAccess(bidRequest.userId, 'parrableId.eid'); + if (parrableId) { + ret['parrableId'] = parrableId; + } + let sharedid = utils.deepAccess(bidRequest.userId, 'sharedid.id'); + if (sharedid) { + ret['sharedid'] = sharedid; + } + let sharedidthird = utils.deepAccess(bidRequest.userId, 'sharedid.third'); + if (sharedidthird) { + ret['sharedidthird'] = sharedidthird; + } } if (!ret.hasOwnProperty('pubcid')) { - var pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); + let pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); if (pubcid) { ret['pubcid'] = pubcid; // if built with old pubCommonId module } @@ -654,10 +770,10 @@ export const spec = { let arr = this.getGetParametersAsObject(); if (arr.hasOwnProperty(whitelabelPrefix + 'storedrequest')) { if (this.isValidPlacementId(arr[whitelabelPrefix + 'storedrequest'])) { - this.logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); + utils.logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); return arr[whitelabelPrefix + 'storedrequest']; } else { - this.logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); + utils.logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); } } return null; @@ -717,7 +833,7 @@ export const spec = { // if there is an ozone.oz_request = false then quit now. let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { - this.logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); + utils.logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; @@ -815,13 +931,13 @@ export const spec = { * @returns seatbid object */ export function injectAdIdsIntoAllBidResponses(seatbid) { - spec.logInfo('injectAdIdsIntoAllBidResponses', seatbid); + utils.logInfo('injectAdIdsIntoAllBidResponses', seatbid); for (let i = 0; i < seatbid.length; i++) { let sb = seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { // modify the bidId per-bid, so each bid has a unique adId within this response, and dfp can select one. // 2020-06 we now need a second level of ID because there might be multiple identical impid's within a seatbid! - sb.bid[j]['adId'] = `${sb.bid[j]['impid']}-${i}-${j}`; + sb.bid[j]['adId'] = `${sb.bid[j]['impid']}-${i}-${spec.propertyBag.whitelabel.keyPrefix}-${j}`; } } return seatbid; @@ -841,7 +957,7 @@ export function checkDeepArray(Arr) { export function defaultSize(thebidObj) { if (!thebidObj) { - spec.logInfo('defaultSize received empty bid obj! going to return fixed default size'); + utils.logInfo('defaultSize received empty bid obj! going to return fixed default size'); return { 'defaultHeight': 250, 'defaultWidth': 300 @@ -919,14 +1035,14 @@ export function getRoundedBid(price, mediaType) { let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); - spec.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); + utils.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); let priceStringsObj = getPriceBucketString( price, theConfigObject, config.getConfig('currency.granularityMultiplier') ); - spec.logInfo('priceStringsObj', priceStringsObj); + utils.logInfo('priceStringsObj', priceStringsObj); // by default, without any custom granularity set, you get granularity name : 'medium' let granularityNamePriceStringsKeyMapping = { 'medium': 'med', @@ -937,7 +1053,7 @@ export function getRoundedBid(price, mediaType) { }; if (granularityNamePriceStringsKeyMapping.hasOwnProperty(theConfigKey)) { let priceStringsKey = granularityNamePriceStringsKeyMapping[theConfigKey]; - spec.logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); + utils.logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); return priceStringsObj[priceStringsKey]; } return priceStringsObj['auto']; @@ -1006,15 +1122,15 @@ export function getWidthAndHeightFromVideoObject(objVideo) { return null; } if (playerSize[0] && typeof playerSize[0] === 'object') { - spec.logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); + utils.logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); playerSize = playerSize[0]; if (typeof playerSize[0] !== 'number' && typeof playerSize[0] !== 'string') { - spec.logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); + utils.logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); return null; } } if (playerSize.length !== 2) { - spec.logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); + utils.logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); return null; } return ({'w': playerSize[0], 'h': playerSize[1]}); @@ -1041,17 +1157,17 @@ export function playerSizeIsNestedArray(objVideo) { * @returns {*} */ function getPlayerSizeFromObject(objVideo) { - spec.logInfo('getPlayerSizeFromObject received object', objVideo); + utils.logInfo('getPlayerSizeFromObject received object', objVideo); let playerSize = utils.deepAccess(objVideo, 'playerSize'); if (!playerSize) { playerSize = utils.deepAccess(objVideo, 'ext.playerSize'); } if (!playerSize) { - spec.logError('getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); + utils.logError('getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); return null; } if (typeof playerSize !== 'object') { - spec.logError('getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); + utils.logError('getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); return null; } return playerSize; @@ -1062,7 +1178,7 @@ function getPlayerSizeFromObject(objVideo) { */ function newRenderer(adUnitCode, rendererOptions = {}) { let isLoaded = window.ozoneVideo; - spec.logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); + utils.logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); const renderer = Renderer.install({ url: spec.getRendererUrl(), config: rendererOptions, @@ -1072,12 +1188,12 @@ function newRenderer(adUnitCode, rendererOptions = {}) { try { renderer.setRender(outstreamRender); } catch (err) { - spec.logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); + utils.logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); } return renderer; } function outstreamRender(bid) { - spec.logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); + utils.logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); // push to render queue because ozoneVideo may not be loaded yet bid.renderer.push(() => { window.ozoneVideo.outstreamRender(bid); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index 10b8ce31d28..6a8682c1c30 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -27,6 +27,20 @@ var validBidRequests = [ 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: 'ozone', + 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, @@ -55,8 +69,7 @@ var validBidRequestsMulti = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; -// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus' -// NOTE THAT criteortus is no longer referenced anywhere - should be removed asap +// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId' // see http://prebid.org/dev-docs/modules/userId.html var validBidRequestsWithUserIdData = [ { @@ -73,12 +86,12 @@ var validBidRequestsWithUserIdData = [ userId: { 'pubcid': '12345678', 'tdid': '1111tdid', - 'id5id': 'ID5-someId', - 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'criteoId': '1111criteoId', 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, - 'parrableId': {'eid': '01.5678.parrableid'} + 'parrableId': {'eid': '01.5678.parrableid'}, + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }, userIdAsEids: [ { @@ -107,13 +120,6 @@ var validBidRequestsWithUserIdData = [ 'atype': 1, }] }, - { - 'source': 'criteortus', - 'uids': [{ - 'id': {'ozone': {'userid': 'critId123'}}, - 'atype': 1, - }] - }, { 'source': 'criteoId', 'uids': [{ @@ -330,7 +336,7 @@ var validBidderRequest1OutstreamVideo2020 = { ] }, 'userId': { - 'id5id': 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA', + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' }, 'userIdAsEids': [ @@ -2014,14 +2020,13 @@ describe('ozone Adapter', function () { let bidRequests = validBidRequests; // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { - 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': '2222', + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'idl_env': '3333', - 'lipb': {'lipbid': '4444'}, 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', 'pubcid': '5555', - 'tdid': '6666' + 'tdid': '6666', + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -2035,14 +2040,13 @@ describe('ozone Adapter', function () { let bidRequests = validBidRequests; // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { - 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': '2222', + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'idl_env': '3333', - 'lipb': {'lipbid': '4444'}, 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in - 'tdid': '6666' + 'tdid': '6666', + 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); @@ -2056,11 +2060,9 @@ describe('ozone Adapter', function () { /* 'pubcid': '12345678', 'tdid': '1111tdid', - 'id5id': 'ID5-someId', - 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'criteoId': '1111criteoId', 'idl_env': 'liverampId', - 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {'eid': '01.5678.parrableid'} */ @@ -2074,16 +2076,14 @@ describe('ozone Adapter', function () { 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('criteortus'); // this is deprecated - expect(payload.user.ext.eids[3]['uids'][0]['id']['ozone']['userid']).to.equal('critId123'); - expect(payload.user.ext.eids[4]['source']).to.equal('criteoId'); - expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('1111criteoId'); - expect(payload.user.ext.eids[5]['source']).to.equal('idl_env'); - expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('liverampId'); - expect(payload.user.ext.eids[6]['source']).to.equal('lipb'); - expect(payload.user.ext.eids[6]['uids'][0]['id']['lipbid']).to.equal('lipbidId123'); - expect(payload.user.ext.eids[7]['source']).to.equal('parrableId'); - expect(payload.user.ext.eids[7]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); + 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 () { @@ -2093,6 +2093,21 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); expect(request.method).to.equal('POST'); + const data = JSON.parse(request.data); + expect(data.ext.ozone.origin).to.equal(fakeOrigin); + 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); + expect(request.url).to.equal(fakeurl); + expect(request.method).to.equal('POST'); + const data = JSON.parse(request.data); + expect(data.ext.ozone.origin).to.equal(fakeurl); config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); @@ -2109,6 +2124,28 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); 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 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'); + expect(utils.deepAccess(result[0].adserverTargeting, 'xx_pb')).to.equal(0.5); + expect(utils.deepAccess(result[0].adserverTargeting, 'xx_adId')).to.equal('2899ec066a91ff8-0-xx-0'); + 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 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; @@ -2131,8 +2168,8 @@ describe('ozone Adapter', function () { config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null spec.propertyBag.whitelabel = null; }); - var specMock = utils.deepClone(spec); it('should use oztestmode GET value if set', function() { + var specMock = utils.deepClone(spec); // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; @@ -2142,7 +2179,34 @@ describe('ozone Adapter', function () { 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'); }); + it('should pass through GET params if present: ozf, ozpf, ozrp, ozip', function() { + var specMock = utils.deepClone(spec); + // mock the getGetParametersAsObject function to simulate GET parameters : + specMock.getGetParametersAsObject = function() { + return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; + }; + const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.ozone.ozf).to.equal(1); + expect(data.ext.ozone.ozpf).to.equal(0); + expect(data.ext.ozone.ozrp).to.equal(2); + expect(data.ext.ozone.ozip).to.equal(123); + }); + it('should pass through GET params if present: ozf, ozpf, ozrp, ozip with alternative values', function() { + var specMock = utils.deepClone(spec); + // mock the getGetParametersAsObject function to simulate GET parameters : + specMock.getGetParametersAsObject = function() { + return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; + }; + const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.ozone.ozf).to.equal(0); + expect(data.ext.ozone.ozpf).to.equal(1); + expect(data.ext.ozone).to.not.haveOwnProperty('ozrp'); + expect(data.ext.ozone).to.not.haveOwnProperty('ozip'); + }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { + var specMock = utils.deepClone(spec); // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; @@ -2152,8 +2216,33 @@ describe('ozone Adapter', function () { 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'); }); + it('should use GET values auction=dev & cookiesync=dev if set', function() { + // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: + var specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {}; + }; + let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + 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'); + + // now mock the response from getGetParametersAsObject & do the request again + + specMock = utils.deepClone(spec); + specMock.getGetParametersAsObject = function() { + return {'auction': 'dev', 'cookiesync': 'dev'}; + }; + request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + url = request.url; + expect(url).to.equal('https://test.ozpr.net/openrtb2/auction'); + cookieUrl = specMock.getCookieSyncUrl(); + expect(cookieUrl).to.equal('https://test.ozpr.net/static/load-cookie.html'); + }); it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: + var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': '1122334455'}; // 10 digits are valid }; @@ -2164,6 +2253,7 @@ describe('ozone Adapter', function () { }); it('should NOT use an invalid ozstoredrequest GET value if set to override the placementId values, and set oz_rw to 0', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: + var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': 'BADVAL'}; // 10 digits are valid }; @@ -2230,6 +2320,72 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); }); + it('should handle ortb2 site data', function () { + config.setConfig({'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, validBidderRequest.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 () { + config.setConfig({'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, validBidderRequest.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 () { + config.setConfig({'ortb2': { + 'user': { + 'gender': 'who knows these days' + } + }}); + const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.user.gender).to.equal('who knows these days'); + config.resetConfig(); + }); + it('should not override the user.ext.consent string even if this is set in config ortb2', function () { + config.setConfig({'ortb2': { + 'user': { + 'ext': { + 'consent': 'this is the consent override that shouldnt work', + 'consent2': 'this should be set' + } + } + }}); + const request = spec.buildRequests(validBidRequests, bidderRequestWithFullGdpr.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); @@ -2241,6 +2397,33 @@ describe('ozone Adapter', function () { } expect(payload.imp[0].video.ext).to.include({'context': 'outstream'}); }); + it('should handle standard floor config correctly', function () { + config.setConfig({ + floors: { + enforcement: { + floorDeals: false, + bidAdjustment: true + }, + data: { + currency: 'USD', + schema: { + fields: ['mediaType'] + }, + values: { + 'video': 1.20, + 'banner': 0.8 + } + } + } + }); + 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 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(); + }); }); describe('interpretResponse', function () { @@ -2266,11 +2449,20 @@ describe('ozone Adapter', function () { 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)); + validBR.uspConsent = '1YNY'; + const request = spec.buildRequests(validBidRequests, validBR); + const payload = JSON.parse(request.data); + expect(payload.user.ext.uspConsent).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'}; const request = spec.buildRequests(validBidRequests, validBidderRequestWithGdpr); + 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 () { @@ -2374,7 +2566,7 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); 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-0'); + 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 () { @@ -2421,7 +2613,7 @@ describe('ozone Adapter', function () { const result = spec.interpretResponse(validres, request); expect(result.length).to.equal(1); expect(result[0]['price']).to.equal(0.9); - expect(result[0]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('2899ec066a91ff8-0-1'); + expect(result[0]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('2899ec066a91ff8-0-oz-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)); @@ -2431,7 +2623,7 @@ describe('ozone Adapter', function () { 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']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-2'); + expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-2'); // change the bid values so a different second bid for an impid by the same bidder gets dropped validres = JSON.parse(JSON.stringify(multiResponse1)); validres.body.seatbid[0].bid[1].price = 1.1; @@ -2441,7 +2633,7 @@ describe('ozone Adapter', function () { 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-1'); + expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-1'); }); }); @@ -2464,6 +2656,20 @@ describe('ozone Adapter', function () { expect(result[0].url).to.include('gdpr=1'); expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); }); + it('should append ccpa (usp data)', function() { + // get the cookie bag populated + spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + 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.only('should use "" if no usp is sent to cookieSync', function() { + // get the cookie bag populated + spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + 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 () { From 6dd4c8fc5731a9f4547a826662458c64d8e8ba0d Mon Sep 17 00:00:00 2001 From: punkiller Date: Tue, 8 Jun 2021 11:22:40 -0700 Subject: [PATCH 1117/1476] Index Exchange Bid Adapter: bidder params.size not required; read video parameters from ad unit (#6691) * not relying on ix bidder params.size, reading from ad unit level mediaTypes * video impression should have size * deprecating params.size, reading mediaTypes.video * mediaTypes.video.[w/h] + update the documentation * Updating the documentation examples * addressing Mikes feedback * IE-11 does not support object.entries() * cleaning up the video params check --- integrationExamples/gpt/ixMultiFormat.html | 117 ++++++++++ modules/ixBidAdapter.js | 252 ++++++++++++++------- modules/ixBidAdapter.md | 143 ++++++------ test/spec/modules/ixBidAdapter_spec.js | 117 +++++----- 4 files changed, 419 insertions(+), 210 deletions(-) create mode 100644 integrationExamples/gpt/ixMultiFormat.html diff --git a/integrationExamples/gpt/ixMultiFormat.html b/integrationExamples/gpt/ixMultiFormat.html new file mode 100644 index 00000000000..c4ed5bb9b1e --- /dev/null +++ b/integrationExamples/gpt/ixMultiFormat.html @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + + diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 4837c9c061f..d326cbaf9b1 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -50,18 +50,36 @@ const PROVIDERS = [ 'flocId' ] +const REQUIRED_VIDEO_PARAMS = ['mimes', 'minduration', 'maxduration']; // note: protocol/protocols is also reqd + +const VIDEO_PARAMS_ALLOW_LIST = [ + 'mimes', 'minduration', 'maxduration', 'protocols', 'protocol', + 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', + 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', + 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', + 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext', + 'playerSize', 'w', 'h' +]; + /** * Transform valid bid request config object to banner impression object that will be sent to ad server. * - * @param {object} bid A valid bid request config object. + * @param {object} bid A valid bid request config object * @return {object} A impression object that will be sent to ad server. */ function bidToBannerImp(bid) { const imp = bidToImp(bid); - imp.banner = {}; - imp.banner.w = bid.params.size[0]; - imp.banner.h = bid.params.size[1]; + const impSize = utils.deepAccess(bid, 'params.size'); + if (impSize) { + imp.banner.w = impSize[0]; + imp.banner.h = impSize[1]; + // populate sid with size if not id + if (!(utils.deepAccess(imp, 'ext.sid'))) { + imp.ext.sid = `${impSize[0]}x${impSize[1]}`; + } + } + imp.banner.topframe = utils.inIframe() ? 0 : 1; _applyFloor(bid, imp, BANNER); @@ -78,18 +96,27 @@ function bidToBannerImp(bid) { function bidToVideoImp(bid) { const imp = bidToImp(bid); const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video'); - const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - const videoAdUnitAllowlist = [ - 'mimes', 'minduration', 'maxduration', 'protocols', 'protocol', - 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', - 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', - 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', - 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext' - ]; - - imp.video = utils.deepClone(bid.params.video) - imp.video.w = bid.params.size[0]; - imp.video.h = bid.params.size[1]; + const videoParamRef = utils.deepAccess(bid, 'params.video'); + + if (!checkVideoParams(bid, videoAdUnitRef, videoParamRef)) { + return {}; + } + + imp.video = videoParamRef ? utils.deepClone(bid.params.video) : {}; + + for (const adUnitProperty in videoAdUnitRef) { + if (VIDEO_PARAMS_ALLOW_LIST.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { + imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty]; + } + } + + if (imp.video.minduration > imp.video.maxduration) { + utils.logError('IX Bid Adapter: video minduration [' + imp.video.minduration + + '] cannot be greater than video maxduration [' + imp.video.maxduration + ']'); + return {}; + } + + const context = (videoParamRef && videoParamRef.context) || (videoAdUnitRef && videoAdUnitRef.context); if (context) { if (context === 'instream') { @@ -97,13 +124,22 @@ function bidToVideoImp(bid) { } else if (context === 'outstream') { imp.video.placement = 4; } else { - utils.logWarn(`ix bidder params: video context '${context}' is not supported`); + utils.logWarn(`IX Bid Adapter: video context '${context}' is not supported`); } } - for (const adUnitProperty in videoAdUnitRef) { - if (videoAdUnitAllowlist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { - imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty]; + if (!(imp.video.w && imp.video.h)) { + // Getting impression Size + const impSize = getFirstSize(utils.deepAccess(imp, 'video.playerSize')) || getFirstSize(utils.deepAccess(bid, 'params.size')); + if (impSize) { + imp.video.w = impSize[0]; + imp.video.h = impSize[1]; + if (!(utils.deepAccess(imp, 'ext.sid'))) { + imp.ext.sid = `${impSize[0]}x${impSize[1]}`; + } + } else { + utils.logWarn('IX Bid Adapter: Video size is missing in [mediaTypes.video] missing'); + return {}; } } @@ -128,8 +164,6 @@ function bidToImp(bid) { if (bid.params.hasOwnProperty('id') && (typeof bid.params.id === 'string' || typeof bid.params.id === 'number')) { imp.ext.sid = String(bid.params.id); - } else { - imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } const dfpAdUnitCode = utils.deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); @@ -278,16 +312,68 @@ function isValidSize(size) { * @return {boolean} True if the size object is an element of the size array, and false * otherwise. */ -function includesSize(sizeArray, size) { +function includesSize(sizeArray = [], size) { if (isValidSize(sizeArray)) { return sizeArray[0] === size[0] && sizeArray[1] === size[1]; } - for (let i = 0; i < sizeArray.length; i++) { if (sizeArray[i][0] === size[0] && sizeArray[i][1] === size[1]) { return true; } } + return false; +} + +/** + * Checks if all required video params are present + * @param {object} bid Bid Object + * @param {object} mediaTypeVideoRef Ad unit level mediaTypes object + * @param {object} paramsVideoRef IX bidder params level video object + * @returns bool Are the required video params available + */ +function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { + let reqParamsPresent = true; + + if (!mediaTypeVideoRef) { + utils.logWarn('IX Bid Adapter: mediaTypes.video is the preferred location for video params in ad unit'); + } + + for (let property of REQUIRED_VIDEO_PARAMS) { + const propInMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty(property); + const propInVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty(property); + + if (!propInMediaType && !propInVideoRef) { + utils.logError('IX Bid Adapter: ' + property + ' is not included in either the adunit or params level'); + reqParamsPresent = false; + } + } + + // early return + if (!reqParamsPresent) { + return false; + } + + // check protocols/protocol + const protocolMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocol'); + const protocolsMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocols'); + const protocolVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocol'); + const protocolsVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocols'); + + return protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef; +} + +/** + * Get One size from Size Array + * [[250,350]] -> [250, 350] + * [250, 350] -> [250, 350] + * @param {array} sizes array of sizes + */ +function getFirstSize(sizes = []) { + if (isValidSize(sizes)) { + return sizes; + } else if (isValidSize(sizes[0])) { + return sizes[0]; + } return false; } @@ -295,9 +381,9 @@ function includesSize(sizeArray, size) { /** * Determines whether or not the given bidFloor parameters are valid. * - * @param {*} bidFloor The bidFloor parameter inside bid request config. - * @param {*} bidFloorCur The bidFloorCur parameter inside bid request config. - * @return {boolean} True if this is a valid bidFloor parameters format, and false + * @param {number} bidFloor The bidFloor parameter inside bid request config. + * @param {number} bidFloorCur The bidFloorCur parameter inside bid request config. + * @return {bool} True if this is a valid bidFloor parameters format, and false * otherwise. */ function isValidBidFloorParams(bidFloor, bidFloorCur) { @@ -534,31 +620,31 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { let currMissingImps = []; while (i < transactionIds.length && requests.length < MAX_REQ_LIMIT) { - if (impressions[transactionIds[i]].hasOwnProperty('missingCount')) { - msd = impressions[transactionIds[i]].missingCount; - } + const impObj = impressions[transactionIds[i]]; + msd = utils.deepAccess(impObj, 'missingCount') && utils.deepAccess(impObj, 'ixImps') ? impObj.missingCount : 0; if (BASE_REQ_SIZE < MAX_REQ_SIZE) { - trimImpressions(impressions[transactionIds[i]], MAX_REQ_SIZE - BASE_REQ_SIZE); + trimImpressions(impObj, MAX_REQ_SIZE - BASE_REQ_SIZE); } else { utils.logError('ix bidder: Base request size has exceeded maximum request size.'); } - if (impressions[transactionIds[i]].hasOwnProperty('missingImps')) { - msi = impressions[transactionIds[i]].missingImps.length; - } + msi = utils.deepAccess(impObj, 'missingImps') && utils.deepAccess(impObj, 'ixImps') ? impObj.missingImps.length : 0; - let currImpsSize = new Blob([encodeURIComponent(JSON.stringify(impressions[transactionIds[i]]))]).size; + let currImpsSize = new Blob([encodeURIComponent(JSON.stringify(impObj))]).size; currReqSize += currImpsSize; if (currReqSize < MAX_REQ_SIZE) { - // pushing ix configured sizes first - r.imp.push(...impressions[transactionIds[i]].ixImps); + // since not reading params.size, ixImps can be undefined/empty + if (impObj.ixImps && impObj.ixImps.length > 0) { + // pushing ix configured sizes first + r.imp.push(...impObj.ixImps); + } // update msd msi r.ext.ixdiag.msd += msd; r.ext.ixdiag.msi += msi; - if (impressions[transactionIds[i]].hasOwnProperty('missingImps')) { - currMissingImps.push(...impressions[transactionIds[i]].missingImps); + if (impObj.hasOwnProperty('missingImps') && impObj.missingImps.length > 0) { + currMissingImps.push(...impObj.missingImps); } i++; @@ -699,6 +785,7 @@ function trimImpressions(impressions, maxSize) { currSize = new Blob([encodeURIComponent(JSON.stringify(impressions))]).size; } } + /** * * @param {array} bannerSizeList list of banner sizes @@ -707,14 +794,15 @@ function trimImpressions(impressions, maxSize) { */ function removeFromSizes(bannerSizeList, bannerSize) { + if (!bannerSize) return; + for (let i = 0; i < bannerSizeList.length; i++) { - if (bannerSize[0] == bannerSizeList[i][0] && bannerSize[1] == bannerSizeList[i][1]) { + const size = bannerSizeList[i]; + if (bannerSize[0] === size[0] && bannerSize[1] === size[1]) { bannerSizeList.splice(i, 1); - return true; + break; } } - // size not found - return false; } /** @@ -790,40 +878,26 @@ export const spec = { const hasBidFloor = bid.params.hasOwnProperty('bidFloor'); const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur'); - if (!isValidSize(bid.params.size)) { - utils.logError('ix bidder params: bid size has invalid format.'); - return false; - } - if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) { return false; } - if (bid.hasOwnProperty('mediaTypes') && !(mediaTypeBannerSizes || mediaTypeVideoPlayerSize)) { + if (utils.deepAccess(bid, 'mediaTypes.banner') && !mediaTypeBannerSizes) { return false; } - if (!includesSize(bid.sizes, paramsSize) && !((mediaTypeVideoPlayerSize && includesSize(mediaTypeVideoPlayerSize, paramsSize)) || - (mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) { - utils.logError('ix bidder params: bid size is not included in ad unit sizes or player size.'); - return false; - } - - if (mediaTypeVideoRef && paramsVideoRef) { - const requiredIXParams = ['mimes', 'minduration', 'maxduration', 'protocols']; - let isParamsLevelValid = true; - for (let property of requiredIXParams) { - if (!mediaTypeVideoRef.hasOwnProperty(property) && !paramsVideoRef.hasOwnProperty(property)) { - const isProtocolsValid = (property === 'protocols' && (mediaTypeVideoRef.hasOwnProperty('protocol') || paramsVideoRef.hasOwnProperty('protocol'))); - if (isProtocolsValid) { - continue; - } - utils.logError('ix bidder params: ' + property + ' is not included in either the adunit or params level'); - isParamsLevelValid = false; - } + if (paramsSize) { + // since there is an ix bidder level size, make sure its valid + const ixSize = getFirstSize(paramsSize); + if (!ixSize) { + utils.logError('ix bidder params: size has invalid format.'); + return false; } - - if (!isParamsLevelValid) { + // check if the ix bidder level size, is present in ad unit level + if (!includesSize(bid.sizes, ixSize) && + !(includesSize(mediaTypeVideoPlayerSize, ixSize)) && + !(includesSize(mediaTypeBannerSizes, ixSize))) { + utils.logError('ix bidder params: bid size is not included in ad unit sizes or player size.'); return false; } } @@ -839,7 +913,10 @@ export const spec = { return false; } } - + // For multi format unit + if (!mediaTypeBannerSizes && (mediaTypeVideoRef || paramsVideoRef)) { + return checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef); + } return true; }, @@ -867,30 +944,35 @@ export const spec = { for (let i = 0; i < validBidRequests.length; i++) { validBidRequest = validBidRequests[i]; - - if (validBidRequest.mediaType === VIDEO || utils.deepAccess(validBidRequest, 'mediaTypes.video')) { - if (validBidRequest.mediaType === VIDEO || includesSize(validBidRequest.mediaTypes.video.playerSize, validBidRequest.params.size)) { - if (!videoImps.hasOwnProperty(validBidRequest.transactionId)) { + const videoAdUnitRef = utils.deepAccess(validBidRequest, 'mediaTypes.video'); + const videoParamRef = utils.deepAccess(validBidRequest, 'params.video'); + + // identify video ad unit + if (validBidRequest.mediaType === VIDEO || videoAdUnitRef || videoParamRef) { + if (!videoImps.hasOwnProperty(validBidRequest.transactionId)) { + const imp = bidToVideoImp(validBidRequest); + if (Object.keys(imp).length != 0) { videoImps[validBidRequest.transactionId] = {}; - } - if (!videoImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { videoImps[validBidRequest.transactionId].ixImps = []; + videoImps[validBidRequest.transactionId].ixImps.push(imp); } - videoImps[validBidRequest.transactionId].ixImps.push(bidToVideoImp(validBidRequest)); } } + if (validBidRequest.mediaType === BANNER || - (utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) || + (utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes')) || (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { let imp = bidToBannerImp(validBidRequest); - - if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { - bannerImps[validBidRequest.transactionId] = {}; - } - if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { - bannerImps[validBidRequest.transactionId].ixImps = [] + // Create IX imps from params.size + if (utils.deepAccess(validBidRequest, 'params.size')) { + if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { + bannerImps[validBidRequest.transactionId] = {}; + } + if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { + bannerImps[validBidRequest.transactionId].ixImps = [] + } + bannerImps[validBidRequest.transactionId].ixImps.push(imp); } - bannerImps[validBidRequest.transactionId].ixImps.push(imp); if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { updateMissingSizes(validBidRequest, missingBannerSizes, imp); } diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index c358b19a0a2..59b699bad2d 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -13,30 +13,10 @@ Description This module connects publishers to Index Exchange's (IX) network of demand sources through Prebid.js. This module is GDPR and CCPA compliant. -It is compatible with both the older ad unit format where the `sizes` and -`mediaType` properties are placed at the top-level of the ad unit, and the newer -format where this information is encapsulated within the `mediaTypes` object. We -recommend that you use the newer format when possible as it will be better able +It is compatible with the newer PrebidJS 5.0 ad unit format where the `banner` and/or `video` properties are encapsulated within the `adUnits[].mediaTypes` object. We +recommend that you use this newer format when possible as it will be better able to accommodate new feature additions. -If a mix of properties from both formats are present within an ad unit, the -newer format's properties will take precedence. - -Here are examples of both formats. - -##### Older Format -```javascript -var adUnits = [{ - // ... - - sizes: [ - [300, 250], - [300, 600] - ] - - // ... -}]; -``` ##### Newer Format ```javascript @@ -51,10 +31,7 @@ var adUnits = [{ }, video: { context: 'instream', - playerSize: [ - [300, 250], - [300, 600] - ] + playerSize: [300, 250] } }, // ... @@ -69,30 +46,59 @@ var adUnits = [{ | Video | Fully supported for all IX approved sizes. | Native | Not supported. -# Bid Parameters + + +# Ad unit or Bidder Parameters + +These params can be specified in the ad unit level `adUnits[].mediaTypes`, which will be the preferred way going forward with PBJS 5.0 Each of the IX-specific parameters provided under the `adUnits[].bids[].params` object are detailed here. + ### Banner | Key | Scope | Type | Description | --- | --- | --- | --- -| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` -| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`. Examples: `[300, 250]`, `[300, 600]`, `[728, 90]` +| siteId | Required | String | An IX-specific identifier that is associated with this ad unit. It will be associated to the single size, if the size provided. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` +| sizes | Required | Number[Number[]] | The size / sizes associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].mediaTypes.banner.sizes`. Examples: `[300, 250]`, `[300, 600]`, `[728, 90]` ### Video | Key | Scope | Type | Description | --- | --- | --- | --- -| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` -| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.video.playerSize`. Examples: `[300, 250]`, `[300, 600]` -| video | Required | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required. Properties not defined at this level, will be pulled from the Adunit level. +| siteId | Required | String | An IX-specific identifier that is associated with this ad unit. It will be associated to the single size, if the size is provided. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` +| size | Optional (Deprecated)| Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.video.playerSize`. Examples: `[300, 250]`, `[300, 600]` +| video | Optional | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required. Properties not defined at this level, will be pulled from the Adunit level. +|video.w| Required | Integer | The video player size width in pixels that will be passed to demand partners. +|video.h| Required | Integer | The video player size height in pixels that will be passed to demand partners. +|video.playerSize| Optional* | Integer | The video player size that will be passed to demand partners. * In the absence of `video.w` and `video.h`, this field is required. | video.mimes | Required | String[] | Array list of content MIME types supported. Popular MIME types include, but are not limited to, `"video/x-ms- wmv"` for Windows Media and `"video/x-flv"` for Flash Video. |video.minduration| Required | Integer | Minimum video ad duration in seconds. |video.maxduration| Required | Integer | Maximum video ad duration in seconds. |video.protocol / video.protocols| Required | Integer / Integer[] | Either a single protocol provided as an integer, or protocols provided as a list of integers. `2` - VAST 2.0, `3` - VAST 3.0, `5` - VAST 2.0 Wrapper, `6` - VAST 3.0 Wrapper +## Deprecation warning + +We are deprecating the older format +of having `mediaType` and `sizes` at the ad unit level. + +Here are examples of the format. + +##### Older Deprecated Format +```javascript +var adUnits = [{ + // ... + + sizes: [ + [300, 250], + [300, 600] + ] + + // ... +}]; +``` + Setup Guide =========== @@ -100,10 +106,15 @@ Setup Guide Follow these steps to configure and add the IX module to your Prebid.js integration. +Both video and banner params will be read from the `adUnits[].mediaTypes.video` and `adUnits[].mediaTypes.banner` respectively. + The examples in this guide assume the following starting configuration (you may remove banner or video, if either does not apply). + In regards to video, `context` can either be `'instream'` or `'outstream'`. Note that `outstream` requires additional configuration on the adUnit. + + ```javascript var adUnits = [{ code: 'banner-div-a', @@ -134,21 +145,23 @@ var adUnits = [{ ### 1. Add IX to the appropriate ad units -For each size in an ad unit that IX will be bidding on, add one of the following +For each ad unit that IX will be bidding on, add one of the following bid objects under `adUnits[].bids`: +- size is optional and deprecated ```javascript { bidder: 'ix', params: { siteId: '', - size: [] + size: [] // deprecated } } ``` -Set `params.siteId` and `params.size` in each bid object to the values provided +Set `params.siteId` in the bid object to the values provided by your IX representative. +- `params.size` is not required anymore **Examples** @@ -167,14 +180,7 @@ var adUnits = [{ bids: [{ bidder: 'ix', params: { - siteId: '12345', - size: [300, 250] - } - }, { - bidder: 'ix', - params: { - siteId: '12345', - size: [300, 600] + siteId: '12345' } }] }]; @@ -185,35 +191,30 @@ var adUnits = [{ code: 'video-div-a', mediaTypes: { video: { + // Preferred location for openrtb v2.5 compatible video obj context: 'instream', - playerSize: [ - [300, 250], - [300, 600] - ] + playerSize: [300, 250], + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] } }, bids: [{ bidder: 'ix', params: { - siteId: '12345', - size: [300, 250], - video: { - mimes: [ - 'video/mp4', - 'video/webm' - ], - minduration: 0, - maxduration: 60, - protocols: [6] - } + siteId: '12345' } }, { bidder: 'ix', params: { siteId: '12345', - size: [300, 600], video: { // openrtb v2.5 compatible video obj + // If required, use this to override mediaTypes.video.XX properties } } }] @@ -231,7 +232,14 @@ var adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [[300, 250]] + playerSize: [300, 250], + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] } }, renderer: { @@ -244,15 +252,8 @@ var adUnits = [{ bidder: 'ix', params: { siteId: '12345', - size: [300, 250], video: { - mimes: [ - 'video/mp4', - 'video/webm' - ], - minduration: 0, - maxduration: 60, - protocols: [6] + // If required, use this to override mediaTypes.video.XX properties } } }] @@ -413,11 +414,9 @@ FAQs ### Why do I have to input size in `adUnits[].bids[].params` for IX when the size is already in the ad unit? -There is one important reason why we recommend it: +No, only `siteId` is required. -1. An IX site ID is recommended to map to a single size, whereas an ad unit can have multiple -sizes. To ensure that the right site ID is mapped to the correct size in the ad unit, -we require the size to be explicitly stated or our bidder will auto assign the site ID to sizes that are not assigned. +The `size` parameter is no longer a required field, the `siteId` will now be associated with all the sizes in the ad unit. ### How do I view IX's bid request in the network? diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 48c8e48e6af..cf716fd594b 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -212,11 +212,10 @@ describe('IndexexchangeAdapter', function () { siteId: '123', size: [300, 250] }, - sizes: [[300, 250], [300, 600]], mediaTypes: { video: { context: 'outstream', - playerSize: [[400, 100]] + playerSize: [600, 700] }, banner: { sizes: [[300, 250], [300, 600]] @@ -246,20 +245,19 @@ describe('IndexexchangeAdapter', function () { maxduration: 60, protocols: [1] }, - size: [400, 100] + size: [300, 250] }, - sizes: [[300, 250], [300, 600]], mediaTypes: { video: { context: 'outstream', - playerSize: [[400, 100]] + playerSize: [300, 250] }, banner: { sizes: [[300, 250], [300, 600]] } }, adUnitCode: 'div-gpt-ad-1460505748562-0', - transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', + transactionId: '273f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', bidderRequestId: '11a22b33c44e', auctionId: '1aa2bb3cc4de', @@ -531,10 +529,10 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when size is missing', function () { + it('should return True when size is missing ', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); delete bid.params.size; - expect(spec.isBidRequestValid(bid)).to.equal(false); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return false when size array is wrong length', function () { @@ -553,33 +551,47 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaTypes is not banner or video', function () { + it('should return false when mediaTypes.banner does not have sizes', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.mediaTypes = { - native: { - sizes: [[300, 250]] + banner: { + size: [[300, 250]] } }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaTypes.banner does not have sizes', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + it('should return false when mediaTypes.video.playerSize does not include params.size', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes = { - banner: { - size: [[300, 250]] + video: { + playerSize: [300, 250] } }; + bid.params.size = [100, 200]; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaTypes.video does not have sizes', function () { + it('should return true when mediaTypes.video.playerSize includes params.size', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes = { video: { - size: [[300, 250]] + playerSize: [[300, 250], [200, 300]] } }; + bid.params.size = [[300, 250]]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when bid.params.size is missing', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.params.size; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when minduration is missing', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.params.video.minduration; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -709,7 +721,7 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(cloneValidBid, ALIAS_OPTIONS); const payload = JSON.parse(request[0].data.r); expect(request).to.be.an('array'); - expect(request).to.have.lengthOf(1); + expect(request).to.have.lengthOf.above(0); // should be 1 or more expect(payload.user.eids).to.have.lengthOf(5); expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); }); @@ -1517,7 +1529,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); expect(impression.banner).to.exist; - expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);// undefined - 300 expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); expect(impression.banner.topframe).to.exist; expect(impression.banner.topframe).to.be.oneOf([0, 1]); @@ -1858,25 +1870,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.skippable).to.equal(false); expect(impression.ext).to.exist; expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal(sidValue); - }); - - it('impression should have correct format when mediaType is specified.', function () { - const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); - delete bid.mediaTypes; - bid.mediaType = 'video'; - const requestBidFloor = spec.buildRequests([bid])[0]; - const impression = JSON.parse(requestBidFloor.data.r).imp[0]; - const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; - - expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); - expect(impression.video).to.exist; - expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); - expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); - expect(impression.video.placement).to.not.exist; - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal(sidValue); + expect(impression.ext.sid).to.equal(sidValue); // TODO undefined - 400x600 }); it('should set correct placement if context is outstream', function () { @@ -1891,6 +1885,14 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.placement).to.equal(4); }); + it('should handle unexpected context', function() { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes.video.context = 'VaccineJanssen'; + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; + expect(impression.video.placement).to.be.undefined; + }); + it('should not override video properties if they are already configured at the params video level', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; @@ -1944,22 +1946,31 @@ describe('IndexexchangeAdapter', function () { }); describe('only video bidder params set', function () { - const request = spec.buildRequests(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID); - - const videoImp = JSON.parse(request[0].data.r).imp[0]; - expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(1); - expect(JSON.parse(request[0].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); - expect(videoImp.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); - expect(videoImp.video).to.exist; - expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[0]); - expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[1]); + it('should generate video impression', function () { + const request = spec.buildRequests(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID); + const videoImp = JSON.parse(request[1].data.r).imp[0]; + expect(JSON.parse(request[1].data.r).imp).to.have.lengthOf(1); + expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); + expect(videoImp.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); + expect(videoImp.video).to.exist; + expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[0]); + expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[1]); + }); + it('should get missing sizes count 0 when params.size not used', function () { + const bid = utils.deepClone(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]); + delete bid.params.size; + const request = spec.buildRequests([bid]); + const diagObj = JSON.parse(request[0].data.r).ext.ixdiag; + expect(diagObj.msd).to.equal(0); + expect(diagObj.msi).to.equal(0); + }); }); describe('both banner and video bidder params set', function () { const request = spec.buildRequests([DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0], DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]]); it('should return valid banner and video requests', function () { const bannerImp = JSON.parse(request[0].data.r).imp[0]; - expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(2); + expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(4); expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); expect(bannerImp.id).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].bidId); expect(bannerImp.banner).to.exist; @@ -1971,18 +1982,18 @@ describe('IndexexchangeAdapter', function () { expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); expect(videoImp.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); expect(videoImp.video).to.exist; - expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[0]); - expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[1]); + expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[0]); + expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[1]); }); it('should contain all correct IXdiag properties', function () { const diagObj = JSON.parse(request[0].data.r).ext.ixdiag; expect(diagObj.iu).to.equal(0); expect(diagObj.nu).to.equal(0); - expect(diagObj.ou).to.equal(1); + expect(diagObj.ou).to.equal(2); expect(diagObj.ren).to.equal(false); - expect(diagObj.mfu).to.equal(1); - expect(diagObj.allu).to.equal(1); + expect(diagObj.mfu).to.equal(2); + expect(diagObj.allu).to.equal(2); expect(diagObj.version).to.equal('$prebid.version$'); }); }); From 038a58e6cb397532f725c52ca68f0ce75511b8ff Mon Sep 17 00:00:00 2001 From: Ryan Schweitzer <50628828+r-schweitzer@users.noreply.github.com> Date: Tue, 8 Jun 2021 22:29:15 +0200 Subject: [PATCH 1118/1476] PBJS Core: Add allowSendAllBidsTargetingKeys to targetingControls (#6822) --- src/targeting.js | 8 ++++- test/spec/unit/core/targeting_spec.js | 43 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/targeting.js b/src/targeting.js index 365453e1e8f..edf9521c251 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -597,13 +597,19 @@ export function newTargeting(auctionManager) { const standardKeys = TARGETING_KEYS.concat(NATIVE_TARGETING_KEYS); const adUnitBidLimit = config.getConfig('sendBidsControl.bidLimit'); const bids = getHighestCpmBidsFromBidPool(bidsReceived, getHighestCpm, adUnitBidLimit); + const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys'); + + const allowedSendAllBidTargeting = allowSendAllBidsTargetingKeys + ? allowSendAllBidsTargetingKeys.map((key) => CONSTANTS.TARGETING_KEYS[key]) + : standardKeys; // populate targeting keys for the remaining bids return bids.map(bid => { if (bidShouldBeAddedToTargeting(bid, adUnitCodes)) { return { [bid.adUnitCode]: getTargetingMap(bid, standardKeys.filter( - key => typeof bid.adserverTargeting[key] !== 'undefined') + key => typeof bid.adserverTargeting[key] !== 'undefined' && + allowedSendAllBidTargeting.indexOf(key) !== -1) ) }; } diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 5d43ed48266..c82aac1acf9 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -456,6 +456,49 @@ describe('targeting tests', function () { }); }); + describe('targetingControls.allowSendAllBidsTargetingKeys', function () { + let bid4; + + beforeEach(function() { + bid4 = utils.deepClone(bid1); + bid4.adserverTargeting = { + hb_deal: '4321', + hb_pb: '0.1', + hb_adid: '567891011', + hb_bidder: 'appnexus', + }; + bid4.bidder = bid4.bidderCode = 'appnexus'; + bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false + bid4.dealId = '4321'; + enableSendAllBids = true; + config.setConfig({ + targetingControls: { + allowTargetingKeys: ['BIDDER', 'AD_ID', 'PRICE_BUCKET'], + allowSendAllBidsTargetingKeys: ['PRICE_BUCKET', 'AD_ID'] + } + }); + bidsReceived.push(bid4); + }); + + it('targeting should include custom keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('foobar'); + }); + + it('targeting should only include keys prefixed by allowed default send all bids targeting keys and standard keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder', 'hb_adid', 'hb_pb'); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_adid_rubicon', 'hb_pb_rubicon'); + expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder', 'hb_adid', 'hb_pb', 'hb_adid_appnexus', 'hb_pb_appnexus'); + }); + + it('targeting should not include keys prefixed by disallowed default targeting keys and disallowed send all bid targeting keys', function () { + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + expect(targeting['/123456/header-bid-tag-0']).to.not.have.all.keys(['hb_deal', 'hb_bidder_rubicon', 'hb_bidder_appnexus', 'hb_deal_appnexus', 'hb_deal_rubicon']); + }); + }); + describe('targetingControls.alwaysIncludeDeals', function () { let bid4; From 86e86d76bae0113311883103d772ccf4fe7f1fb5 Mon Sep 17 00:00:00 2001 From: Renato Aguilar <41385245+raguilar-ias@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:42:45 -0500 Subject: [PATCH 1119/1476] Integral Ad Science RTD module: initial release (#6816) * PREP-192 Update Prebid.js PET adapter to operate as a real time data model * PREP-192 add iasRtdProvider to submodules.json * PREP-192 sort iasRtdProvider alphabetically * PREP-192 change getBidRequestData method logic add test cases * PREP-192 add test cases * PREP-192 refactor method * add brandsafety response validation --- modules/.submodules.json | 1 + modules/iasRtdProvider.js | 124 +++++++++++++++++++++++ modules/iasRtdProvider.md | 9 ++ test/spec/modules/iasRtdProvider_spec.js | 74 ++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 modules/iasRtdProvider.js create mode 100644 modules/iasRtdProvider.md create mode 100644 test/spec/modules/iasRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 2b2e6457531..9e8300687db 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -39,6 +39,7 @@ "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", + "iasRtdProvider", "jwplayerRtdProvider", "optimeraRtdProvider", "permutiveRtdProvider", diff --git a/modules/iasRtdProvider.js b/modules/iasRtdProvider.js new file mode 100644 index 00000000000..bbd2529bf86 --- /dev/null +++ b/modules/iasRtdProvider.js @@ -0,0 +1,124 @@ +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import * as utils from '../src/utils.js'; +import { ajaxBuilder } from '../src/ajax.js'; + +/** @type {string} */ +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'ias'; + +/** + * Module init + * @param {Object} provider + * @param {Object} userConsent + * @return {boolean} + */ +export function init(config, userConsent) { + return true; +} + +function stringifySlotSizes(sizes) { + let result = ''; + if (utils.isArray(sizes)) { + result = sizes.reduce((acc, size) => { + acc.push(size.join('.')); + return acc; + }, []); + result = '[' + result.join(',') + ']'; + } + return result; +} + +function stringifySlot(bidRequest) { + const id = bidRequest.code; + const ss = stringifySlotSizes(bidRequest.sizes); + const p = bidRequest.bids[0].params.adUnitPath; + const slot = { id, ss, p }; + const keyValues = utils.getKeys(slot).map(function (key) { + return [key, slot[key]].join(':'); + }); + return '{' + keyValues.join(',') + '}'; +} + +function stringifyWindowSize() { + return [window.innerWidth || -1, window.innerHeight || -1].join('.'); +} + +function stringifyScreenSize() { + return [(window.screen && window.screen.width) || -1, (window.screen && window.screen.height) || -1].join('.'); +} + +function getPageLevelKeywords(response) { + let result = {}; + if (response.brandSafety) { + shallowMerge(result, response.brandSafety); + } + result.fr = response.fr; + result.custom = response.custom; + return result; +} + +function shallowMerge(dest, src) { + utils.getKeys(src).reduce((dest, srcKey) => { + dest[srcKey] = src[srcKey]; + return dest; + }, dest); +} + +function getBidRequestData(reqBidsConfigObj, callback, config) { + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + + let isFinish = false; + + const IAS_HOST = 'https://pixel.adsafeprotected.com/services/pub'; + const { pubId } = config.params; + const anId = pubId; + let queries = []; + queries.push(['anId', anId]); + + queries = queries.concat(adUnits.reduce(function (acc, request) { + acc.push(['slot', stringifySlot(request)]); + return acc; + }, [])); + + queries.push(['wr', stringifyWindowSize()]); + queries.push(['sr', stringifyScreenSize()]); + queries.push(['url', encodeURIComponent(window.location.href)]); + + const queryString = encodeURI(queries.map(qs => qs.join('=')).join('&')); + + const ajax = ajaxBuilder(); + + ajax(`${IAS_HOST}?${queryString}`, { + success: function (response, request) { + if (!isFinish) { + if (request.status === 200) { + const iasResponse = JSON.parse(response); + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const rtd = bid.rtd || {}; + const iasRtd = {}; + iasRtd[SUBMODULE_NAME] = Object.assign({}, rtd[SUBMODULE_NAME], getPageLevelKeywords(iasResponse)); + bid.rtd = Object.assign({}, rtd, iasRtd); + }); + }); + } + isFinish = true; + } + callback(); + }, + error: function () { + utils.logError('failed to retrieve targeting information'); + callback(); + } + }); +} + +/** @type {RtdSubmodule} */ +export const iasSubModule = { + name: SUBMODULE_NAME, + init: init, + getBidRequestData: getBidRequestData +}; + +submodule(MODULE_NAME, iasSubModule); diff --git a/modules/iasRtdProvider.md b/modules/iasRtdProvider.md new file mode 100644 index 00000000000..d8c46ff2697 --- /dev/null +++ b/modules/iasRtdProvider.md @@ -0,0 +1,9 @@ +# Overview + +Module Name: Integral Ad Science(IAS) Rtd Provider +Module Type: Rtd Provider +Maintainer: raguilar@integralads.com + +# Description + +RTD provider for Integral Ad Science(IAS) Contact raguilar@integralads.com for information. diff --git a/test/spec/modules/iasRtdProvider_spec.js b/test/spec/modules/iasRtdProvider_spec.js new file mode 100644 index 00000000000..778a3a81b9b --- /dev/null +++ b/test/spec/modules/iasRtdProvider_spec.js @@ -0,0 +1,74 @@ +import { iasSubModule } from 'modules/iasRtdProvider.js'; +import { expect } from 'chai'; + +describe('iasRtdProvider is a RTD provider that', function () { + it('has the correct module name', function () { + expect(iasSubModule.name).to.equal('ias'); + }); + describe('has a method `init` that', function () { + it('exists', function () { + expect(iasSubModule.init).to.be.a('function'); + }); + it('returns true', function () { + expect(iasSubModule.init()).to.equal(true); + }); + }); + describe('has a method `getBidRequestData` that', function () { + const callback = sinon.spy(); + const config = { + name: 'ias', + waitForIt: true, + params: { + pubId: 1234 + } + }; + it('exists', function () { + expect(iasSubModule.getBidRequestData).to.be.a('function'); + }); + it('verify config params', function () { + expect(config.name).to.not.be.undefined; + expect(config.name).to.equal('ias'); + expect(config.params.pubId).to.not.be.undefined; + expect(config.params).to.have.property('pubId'); + }); + it('invoke method', function () { + iasSubModule.getBidRequestData({ adUnits: adUnits }, callback, config); + expect(adUnits).to.length(2); + expect(callback.calledOnce).to.be.false; + }); + }); +}); + +const adUnits = [ + { + code: 'one-div-id', + mediaTypes: { + banner: { + sizes: [[970, 250], [728, 90], [1000, 90]] + } + }, + sizes: [[970, 250], [728, 90], [1000, 90]], + bids: [ + { + bidder: 'ias', + params: { + pubId: '1234', + adUnitPath: '/a/b/c' + } + }] + }, + { + code: 'two-div-id', + mediaTypes: { + banner: { sizes: [[300, 250], [300, 600]] } + }, + sizes: [[300, 250], [300, 600]], + bids: [ + { + bidder: 'ias', + params: { + pubId: '1234', + adUnitPath: '/d/e/f' + } + }] + }]; From 64586addce529aca3ab9350a6349f58c4f412c0c Mon Sep 17 00:00:00 2001 From: Lisa Benmore Date: Tue, 8 Jun 2021 22:48:43 -0700 Subject: [PATCH 1120/1476] ADTS-132 Use static bid floor if defined if getFloor module is not used by pub (#6974) --- modules/gumgumBidAdapter.js | 8 +++++--- test/spec/modules/gumgumBidAdapter_spec.js | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index a49cd1593d9..fdcbbd8a0f3 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -208,7 +208,7 @@ function _getVidParams (attributes) { * @param {Object} bid * @returns {Number} floor */ -function _getFloor (mediaTypes, staticBidfloor, bid) { +function _getFloor (mediaTypes, staticBidFloor, bid) { const curMediaType = Object.keys(mediaTypes)[0] || 'banner'; const bidFloor = { floor: 0, currency: 'USD' }; @@ -220,9 +220,11 @@ function _getFloor (mediaTypes, staticBidfloor, bid) { floor && (bidFloor.floor = floor); currency && (bidFloor.currency = currency); - if (staticBidfloor && floor && currency === 'USD') { - bidFloor.floor = Math.max(staticBidfloor, parseFloat(floor)); + if (staticBidFloor && floor && currency === 'USD') { + bidFloor.floor = Math.max(staticBidFloor, parseFloat(floor)); } + } else if (staticBidFloor) { + bidFloor.floor = staticBidFloor } return bidFloor; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 62988b75fd1..19d3309e3ee 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -196,6 +196,13 @@ describe('gumgumAdapter', function () { expect(bidRequest.gpid).to.equal(123456); }); + it('should set the bid floor if getFloor module is not present but static bid floor is defined', function () { + const req = { ...bidRequests[0], params: { bidfloor: 42 } } + const bidRequest = spec.buildRequests([req])[0]; + expect(bidRequest.data).to.have.property('fp'); + expect(bidRequest.data.fp).to.equal(42); + }); + describe('product id', function () { it('should set the correct pi param if native param is found', function () { const request = { ...bidRequests[0], params: { ...zoneParam, native: 2 } }; From 99a51e0ad47d18ee7c4660b3c576389947b320fa Mon Sep 17 00:00:00 2001 From: llays Date: Wed, 9 Jun 2021 10:35:14 +0300 Subject: [PATCH 1121/1476] Invamia Bid Adapter: add new bid adapter (#6941) * Added invamia bia adapter * Changed serialization method * Replaced URLSearchParams in tests as it's not supported by Circle CI * Delete package-lock.json * revert changes on package-lock * Revert "Changed serialization method" This reverts commit 4fc0ad31 * Changed serialization method * Cache bid request params in local variable * Implemented multiple bid support * Updated tests Co-authored-by: Andrew Lays --- modules/invamiaBidAdapter.js | 88 ++++++++++ modules/invamiaBidAdapter.md | 30 ++++ test/spec/modules/invamiaBidAdapter_spec.js | 169 ++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 modules/invamiaBidAdapter.js create mode 100644 modules/invamiaBidAdapter.md create mode 100644 test/spec/modules/invamiaBidAdapter_spec.js diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js new file mode 100644 index 00000000000..0cc63d34550 --- /dev/null +++ b/modules/invamiaBidAdapter.js @@ -0,0 +1,88 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'invamia'; +const ENDPOINT_URL = 'https://ad.invamia.com/delivery/impress'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bidRequest The bid request params to validate. + * @return boolean True if this is a valid bid request, and false otherwise. + */ + isBidRequestValid: function(bidRequest) { + return !!bidRequest.params.zoneId; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {Array} validBidRequests an array of bid requests + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests) { + let serverRequests = []; + + validBidRequests.forEach(bidRequest => { + const sizes = bidRequest.mediaTypes.banner.sizes; + + sizes.forEach(([width, height]) => { + bidRequest.params.requestedSizes = [width, height]; + + const payload = { + ctype: 'div', + pzoneid: bidRequest.params.zoneId, + width, + height, + }; + + const payloadString = Object.keys(payload).map(k => k + '=' + encodeURIComponent(payload[k])).join('&'); + + serverRequests.push({ + method: 'GET', + url: ENDPOINT_URL, + data: payloadString, + bidderRequest: bidRequest, + }); + }); + }); + + return serverRequests; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidderRequest A matched bid request for this response. + * @return Array An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, {bidderRequest}) { + const response = serverResponse.body; + const bidResponses = []; + + if (response && response.template && response.template.html) { + const {bidId} = bidderRequest; + const [width, height] = bidderRequest.params.requestedSizes; + + const bidResponse = { + requestId: bidId, + cpm: response.hb.cpm, + creativeId: response.banner.hash, + currency: 'USD', + netRevenue: response.hb.netRevenue, + ttl: 600, + ad: response.template.html, + width, + height, + }; + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, +} + +registerBidder(spec); diff --git a/modules/invamiaBidAdapter.md b/modules/invamiaBidAdapter.md new file mode 100644 index 00000000000..c8e12808123 --- /dev/null +++ b/modules/invamiaBidAdapter.md @@ -0,0 +1,30 @@ +# Overview + +``` +Module Name: Invamia Bidder Adapter +Module Type: Bidder Adapter +Maintainer: contact@invamia.com +``` + +# Description + +Module that connects to Invamia demand sources. + +# Test Parameters + +``` + const adUnits = [{ + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [{ + bidder: 'invamia', + params: { + zoneId: 379783, + }, + }], + }]; +``` diff --git a/test/spec/modules/invamiaBidAdapter_spec.js b/test/spec/modules/invamiaBidAdapter_spec.js new file mode 100644 index 00000000000..ed004b651bd --- /dev/null +++ b/test/spec/modules/invamiaBidAdapter_spec.js @@ -0,0 +1,169 @@ +import {expect} from 'chai'; +import {spec} from 'modules/invamiaBidAdapter.js'; + +describe('invamia bid adapter tests', function () { + describe('bid requests', function () { + it('should accept valid bid', function () { + const validBid = { + bidder: 'invamia', + params: {zoneId: 123}, + }; + + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should reject invalid bid', function () { + const invalidBid = { + bidder: 'invamia', + params: {}, + }; + + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + + it('should correctly build payload string', function () { + const bidRequests = [{ + bidder: 'invamia', + params: {zoneId: 123}, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + }]; + const payload = spec.buildRequests(bidRequests)[0].data; + + expect(payload).to.contain('ctype=div'); + expect(payload).to.contain('pzoneid=123'); + expect(payload).to.contain('width=300'); + expect(payload).to.contain('height=250'); + }); + + it('should support multiple bids', function () { + const bidRequests = [{ + bidder: 'invamia', + params: {zoneId: 123}, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + }, { + bidder: 'invamia', + params: {zoneId: 321}, + mediaTypes: { + banner: { + sizes: [[728, 90]], + }, + }, + bidId: '23acc48ad47af52', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba992', + bidderRequestId: '1c56ad30b9b8ca82', + transactionId: '92489f71-1bf2-49a0-adf9-000cea9347292', + }]; + const payload = spec.buildRequests(bidRequests); + + expect(payload).to.be.lengthOf(2); + }); + + it('should support multiple sizes', function () { + const bidRequests = [{ + bidder: 'invamia', + params: {zoneId: 123}, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + }, + }, + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + }]; + const payload = spec.buildRequests(bidRequests); + + expect(payload).to.be.lengthOf(2); + }); + }); + + describe('bid responses', function () { + it('should return complete bid response', function () { + const serverResponse = { + body: { + banner: { + hash: '1c56ad30b9b8ca8', + }, + hb: { + cpm: 0.5, + netRevenue: false, + }, + template: { + html: '', + }, + }, + }; + const bidderRequest = { + bidId: '23acc48ad47af5', + params: { + requestedSizes: [300, 250], + }, + }; + + const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].requestId).to.equal('23acc48ad47af5'); + expect(bids[0].creativeId).to.equal('1c56ad30b9b8ca8'); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].ttl).to.equal(600); + expect(bids[0].cpm).to.equal(0.5); + expect(bids[0].netRevenue).to.equal(false); + expect(bids[0].currency).to.equal('USD'); + }); + + it('should return empty bid response', function () { + const serverResponse = { + body: {}, + }; + const bidderRequest = { + bidId: '23acc48ad47af5', + params: { + requestedSizes: [300, 250], + }, + }; + + const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response 2', function () { + const serverResponse = { + body: { + template: { + html: '', + } + }, + }; + const bidderRequest = { + bidId: '23acc48ad47af5', + params: { + requestedSizes: [300, 250], + }, + }; + + const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + + expect(bids).to.be.lengthOf(0); + }); + }); +}); From 388ad4ecab7f51cfa6a0ab6899fb3da24dde1f6b Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 9 Jun 2021 03:37:36 -0700 Subject: [PATCH 1122/1476] Revert "ozone 2.6.0 adapter updates (#6946)" (#6976) This reverts commit d496c6846c573ccb55a562e8678d86587fb27f26. --- modules/ozoneBidAdapter.js | 362 ++++++++-------------- test/spec/modules/ozoneBidAdapter_spec.js | 284 +++-------------- 2 files changed, 162 insertions(+), 484 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 928344be74f..8ada9d59ae2 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -11,17 +11,16 @@ const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & 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.6.0'; +const OZONEVERSION = '2.5.0'; export const spec = { gvlid: 524, - aliases: [{code: 'lmc', gvlid: 524}, {code: 'newspassid', gvlid: 524}], + aliases: [{ code: 'lmc' }], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], - cookieSyncBag: {publisherId: null, siteId: null, userIdObject: {}}, // variables we want to make available to cookie sync - propertyBag: {pageId: null, buildRequestsStart: 0, buildRequestsEnd: 0, endpointOverride: null}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ + cookieSyncBag: {'publisherId': null, 'siteId': null, 'userIdObject': {}}, // variables we want to make available to cookie sync + propertyBag: {'pageId': null, 'buildRequestsStart': 0, 'buildRequestsEnd': 0}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ whitelabel_defaults: { 'logId': 'OZONE', 'bidder': 'ozone', @@ -41,45 +40,19 @@ export const spec = { this.propertyBag.whitelabel.logId = bidder.toUpperCase(); this.propertyBag.whitelabel.bidder = bidder; let bidderConfig = config.getConfig(bidder) || {}; - utils.logInfo('got bidderConfig: ', JSON.parse(JSON.stringify(bidderConfig))); if (bidderConfig.kvpPrefix) { this.propertyBag.whitelabel.keyPrefix = bidderConfig.kvpPrefix; } - let arr = this.getGetParametersAsObject(); if (bidderConfig.endpointOverride) { if (bidderConfig.endpointOverride.origin) { - this.propertyBag.endpointOverride = bidderConfig.endpointOverride.origin; 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']); - } else { - this.propertyBag.whitelabel.rendererUrl = arr['renderer']; - } - } else if (bidderConfig.endpointOverride.rendererUrl) { + if (bidderConfig.endpointOverride.rendererUrl) { this.propertyBag.whitelabel.rendererUrl = bidderConfig.endpointOverride.rendererUrl; } - if (bidderConfig.endpointOverride.cookieSyncUrl) { - this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.cookieSyncUrl; - } - if (bidderConfig.endpointOverride.auctionUrl) { - this.propertyBag.endpointOverride = bidderConfig.endpointOverride.auctionUrl; - this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.auctionUrl; - } } - try { - if (arr.hasOwnProperty('auction') && arr.auction === 'dev') { - utils.logInfo('GET: auction=dev'); - this.propertyBag.whitelabel.auctionUrl = ORIGIN_DEV + AUCTIONURI; - } - if (arr.hasOwnProperty('cookiesync') && arr.cookiesync === 'dev') { - utils.logInfo('GET: cookiesync=dev'); - this.propertyBag.whitelabel.cookieSyncUrl = ORIGIN_DEV + OZONECOOKIESYNC; - } - } catch (e) {} - utils.logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); + this.logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); }, getAuctionUrl() { return this.propertyBag.whitelabel.auctionUrl; @@ -90,6 +63,27 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, + /** + * wrappers for this.logInfo logWarn & logError, to add the proper prefix + */ + logInfo() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logInfo.apply(this, args); + }, + logError() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logError.apply(this, args); + }, + logWarn() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logWarn.apply(this, args); + }, /** * Basic check to see whether required parameters are in the request. * @param bid @@ -97,62 +91,62 @@ export const spec = { */ isBidRequestValid(bid) { this.loadWhitelabelData(bid); - utils.logInfo('isBidRequestValid : ', config.getConfig(), bid); + this.logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code if (!(bid.params.hasOwnProperty('placementId'))) { - utils.logError('VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!this.isValidPlacementId(bid.params.placementId)) { - utils.logError('VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('publisherId'))) { - utils.logError('VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - utils.logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('siteId'))) { - utils.logError('VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) { - utils.logError('VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); return false; } if (bid.params.hasOwnProperty('customParams')) { - utils.logError('VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); return false; } if (bid.params.hasOwnProperty('customData')) { if (!Array.isArray(bid.params.customData)) { - utils.logError('VALIDATION FAILED : customData is not an Array', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData is not an Array', adUnitCode); return false; } if (bid.params.customData.length < 1) { - utils.logError('VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); return false; } if (!(bid.params.customData[0]).hasOwnProperty('targeting')) { - utils.logError('VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); return false; } if (typeof bid.params.customData[0]['targeting'] != 'object') { - utils.logError('VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); return false; } } if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - utils.logError('No video context key/value in bid. Rejecting bid: ', bid); + this.logError('No video context key/value in bid. Rejecting bid: ', bid); return false; } if (bid.mediaTypes[VIDEO].context !== 'instream' && bid.mediaTypes[VIDEO].context !== 'outstream') { - utils.logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); + this.logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); return false; } } @@ -172,7 +166,7 @@ export const spec = { this.propertyBag.buildRequestsStart = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; - utils.logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${OZONEVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); + this.logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${OZONEVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); // First check - is there any config to block this request? if (this.blockTheRequest()) { return []; @@ -184,20 +178,26 @@ export const spec = { this.cookieSyncBag.publisherId = utils.deepAccess(validBidRequests[0], 'params.publisherId'); htmlParams = validBidRequests[0].params; } - utils.logInfo('cookie sync bag', this.cookieSyncBag); + this.logInfo('cookie sync bag', this.cookieSyncBag); let singleRequest = this.getWhitelabelConfigItem('ozone.singleRequest'); singleRequest = singleRequest !== false; // undefined & true will be true - utils.logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); + this.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'] - // First party data module : look for ortb2 in setconfig & set the User object. NOTE THAT this should happen before we set the consentString - let fpd = config.getConfig('ortb2'); - if (fpd && utils.deepAccess(fpd, 'user')) { - utils.logInfo('added FPD user object'); - ozoneRequest.user = fpd.user; + if (bidderRequest && bidderRequest.gdprConsent) { + this.logInfo('ADDING GDPR info'); + let apiVersion = bidderRequest.gdprConsent.apiVersion || '1'; + ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; + if (ozoneRequest.regs.ext.gdpr) { + ozoneRequest.user = ozoneRequest.user || {}; + ozoneRequest.user.ext = {'consent': bidderRequest.gdprConsent.consentString}; + } else { + this.logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); + } + } else { + this.logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object was present.'); } - const getParams = this.getGetParametersAsObject(); const wlOztestmodeKey = whitelabelPrefix + 'testmode'; const isTestMode = getParams[wlOztestmodeKey] || null; // this can be any string, it's used for testing ads @@ -214,18 +214,18 @@ export const spec = { let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { - utils.logInfo('no mediaTypes detected - will use the sizes array in the config root'); + this.logInfo('no mediaTypes detected - will use the sizes array in the config root'); arrBannerSizes = ozoneBidRequest.sizes; } else { - utils.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); + this.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); } } else { if (ozoneBidRequest.mediaTypes.hasOwnProperty(BANNER)) { arrBannerSizes = ozoneBidRequest.mediaTypes[BANNER].sizes; /* Note - if there is a sizes element in the config root it will be pushed into here */ - utils.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); + this.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); } if (ozoneBidRequest.mediaTypes.hasOwnProperty(VIDEO)) { - utils.logInfo('openrtb 2.5 compliant video'); + this.logInfo('openrtb 2.5 compliant video'); // examine all the video attributes in the config, and either put them into obj.video if allowed by IAB2.5 or else in to obj.video.ext if (typeof ozoneBidRequest.mediaTypes[VIDEO] == 'object') { let childConfig = utils.deepAccess(ozoneBidRequest, 'params.video', {}); @@ -234,33 +234,25 @@ export const spec = { } // we need to duplicate some of the video values let wh = getWidthAndHeightFromVideoObject(obj.video); - utils.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); + this.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); if (wh && typeof wh === 'object') { obj.video.w = wh['w']; obj.video.h = wh['h']; if (playerSizeIsNestedArray(obj.video)) { // this should never happen; it was in the original spec for this change though. - utils.logInfo('setting obj.video.format to be an array of objects'); + this.logInfo('setting obj.video.format to be an array of objects'); obj.video.ext.format = [wh]; } else { - utils.logInfo('setting obj.video.format to be an object'); + this.logInfo('setting obj.video.format to be an object'); obj.video.ext.format = wh; } } else { - utils.logWarn('cannot set w, h & format values for video; the config is not right'); + this.logWarn('cannot set w, h & format values for video; the config is not right'); } } // Native integration is not complete yet if (ozoneBidRequest.mediaTypes.hasOwnProperty(NATIVE)) { obj.native = ozoneBidRequest.mediaTypes[NATIVE]; - utils.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); - } - // is the publisher specifying floors, and is the floors module enabled? - if (ozoneBidRequest.hasOwnProperty('getFloor')) { - utils.logInfo('This bidRequest object has property: getFloor'); - obj.floor = this.getFloorObjectForAuction(ozoneBidRequest); - utils.logInfo('obj.floor is : ', obj.floor); - } else { - utils.logInfo('This bidRequest object DOES NOT have property: getFloor'); + this.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); } } if (arrBannerSizes.length > 0) { @@ -276,18 +268,17 @@ export const spec = { } // these 3 MUST exist - we check them in the validation method obj.placementId = placementId; - // build the imp['ext'] object - NOTE - Dont obliterate anything that' already in obj.ext - utils.deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); - // obj.ext = {'prebid': {'storedrequest': {'id': placementId}}}; + // build the imp['ext'] object + obj.ext = {'prebid': {'storedrequest': {'id': placementId}}}; obj.ext[whitelabelBidder] = {}; obj.ext[whitelabelBidder].adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' obj.ext[whitelabelBidder].transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } - utils.logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); + this.logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); if (isTestMode != null) { - utils.logInfo('setting isTestMode to ', isTestMode); + this.logInfo('setting isTestMode to ', isTestMode); if (obj.ext[whitelabelBidder].hasOwnProperty('customData')) { for (let i = 0; i < obj.ext[whitelabelBidder].customData.length; i++) { obj.ext[whitelabelBidder].customData[i]['targeting'][wlOztestmodeKey] = isTestMode; @@ -297,19 +288,6 @@ export const spec = { obj.ext[whitelabelBidder].customData[0].targeting[wlOztestmodeKey] = isTestMode; } } - if (fpd && utils.deepAccess(fpd, 'site')) { - // attach the site fpd into exactly : imp[n].ext.[whitelabel].customData.0.targeting - utils.logInfo('added FPD site object'); - if (utils.deepAccess(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', false)) { - obj.ext[whitelabelBidder].customData[0].targeting = Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); - // let keys = utils.getKeys(fpd.site); - // for (let i = 0; i < keys.length; i++) { - // obj.ext[whitelabelBidder].customData[0].targeting[keys[i]] = fpd.site[keys[i]]; - // } - } else { - utils.deepSetValue(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', fpd.site); - } - } return obj; }); @@ -327,27 +305,20 @@ export const spec = { } extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') - utils.logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); + this.logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { extObj[whitelabelBidder][whitelabelPrefix + '_omp_floor'] = ozOmpFloorDollars; } else if (typeof ozOmpFloorDollars !== 'undefined') { - utils.logError(`${whitelabelPrefix}_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. ${whitelabelPrefix}_omp_floor: 1.55. You have it set as a ` + (typeof ozOmpFloorDollars)); + this.logError(`${whitelabelPrefix}_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. ${whitelabelPrefix}_omp_floor: 1.55. You have it set as a ` + (typeof ozOmpFloorDollars)); } let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = utils.isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; extObj[whitelabelBidder][whitelabelPrefix + '_kvp_rw'] = useOzWhitelistAdserverKeys ? 1 : 0; if (whitelabelBidder != 'ozone') { - utils.logInfo('setting aliases object'); + this.logInfo('setting aliases object'); extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; } - // 20210413 - adding a set of GET params to pass to auction - 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; } - // extObj.ortb2 = config.getConfig('ortb2'); // original test location var userExtEids = this.generateEids(validBidRequests); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { @@ -357,26 +328,6 @@ export const spec = { }; ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] === 'true') ? 1 : 0; - // this should come as late as possible so it overrides any user.ext.consent value - if (bidderRequest && bidderRequest.gdprConsent) { - utils.logInfo('ADDING GDPR info'); - let apiVersion = utils.deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); - ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; - if (utils.deepAccess(ozoneRequest, 'regs.ext.gdpr')) { - utils.deepSetValue(ozoneRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - } else { - utils.logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); - } - } else { - utils.logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object'); - } - if (bidderRequest && bidderRequest.uspConsent) { - utils.logInfo('ADDING CCPA info'); - utils.deepSetValue(ozoneRequest, 'user.ext.uspConsent', bidderRequest.uspConsent); - } else { - utils.logInfo('WILL NOT ADD CCPA info; no bidderRequest.uspConsent.'); - } - // this is for 2.2.1 // coppa compliance if (config.getConfig('coppa') === true) { @@ -385,7 +336,7 @@ export const spec = { // return the single request object OR the array: if (singleRequest) { - utils.logInfo('buildRequests starting to generate response for a single request'); + this.logInfo('buildRequests starting to generate response for a single request'); ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? ozoneRequest.imp = tosendtags; @@ -398,14 +349,14 @@ export const spec = { data: JSON.stringify(ozoneRequest), bidderRequest: bidderRequest }; - utils.logInfo('buildRequests request data for single = ', JSON.parse(JSON.stringify(ozoneRequest))); + this.logInfo('buildRequests request data for single = ', ozoneRequest); this.propertyBag.buildRequestsEnd = new Date().getTime(); - utils.logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); + this.logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); return ret; } // not single request - pull apart the tosendtags array & return an array of objects each containing one element in the imp array. let arrRet = tosendtags.map(imp => { - utils.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); + this.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); imp.ext[whitelabelBidder].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object ozoneRequestSingle.id = imp.ext[whitelabelBidder].transactionId; // Unique ID of the bid request, provided by the exchange. @@ -414,7 +365,7 @@ export const spec = { ozoneRequestSingle.ext = extObj; ozoneRequestSingle.source = {'tid': imp.ext[whitelabelBidder].transactionId}; utils.deepSetValue(ozoneRequestSingle, 'user.ext.eids', userExtEids); - utils.logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); + this.logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { method: 'POST', url: this.getAuctionUrl(), @@ -423,42 +374,9 @@ export const spec = { }; }); this.propertyBag.buildRequestsEnd = new Date().getTime(); - utils.logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); + this.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: utils.deepAccess(bidRequestRef, 'mediaTypes.banner.sizes', null), - video: utils.deepAccess(bidRequestRef, 'mediaTypes.video.playerSize', null), - native: utils.deepAccess(bidRequestRef, 'mediaTypes.native.image.sizes', null) - } - utils.logInfo('getFloorObjectForAuction mediaTypesSizes : ', mediaTypesSizes); - let ret = {}; - if (mediaTypesSizes.banner) { - ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner}); - } - if (mediaTypesSizes.video) { - ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video}); - } - if (mediaTypesSizes.native) { - ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native}); - } - utils.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 @@ -474,8 +392,8 @@ export const spec = { let startTime = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; - utils.logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); - utils.logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); + this.logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); + this.logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; // note that serverResponse.id value is the auction_id we might want to use for reporting reasons. if (!serverResponse.hasOwnProperty('seatbid')) { @@ -486,15 +404,12 @@ export const spec = { } let arrAllBids = []; let enhancedAdserverTargeting = this.getWhitelabelConfigItem('ozone.enhancedAdserverTargeting'); - utils.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); + this.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); if (typeof enhancedAdserverTargeting == 'undefined') { enhancedAdserverTargeting = true; } - utils.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); - - // 2021-03-05 - comment this out for a build without adding adid to the response + this.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'; @@ -505,32 +420,30 @@ export const spec = { 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); - utils.logInfo(`seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); + this.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 = ozoneAddStandardProperties(sb.bid[j], defaultWidth, defaultHeight); - // prebid 4.0 compliance - thisBid.meta = {advertiserDomains: thisBid.adomain || []}; let videoContext = null; let isVideo = false; let bidType = utils.deepAccess(thisBid, 'ext.prebid.type'); - utils.logInfo(`this bid type is : ${bidType}`, j); + this.logInfo(`this bid type is : ${bidType}`, j); if (bidType === VIDEO) { isVideo = true; videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - utils.logInfo('going to attach a renderer to OUTSTREAM video : ', j); + this.logInfo('going to attach a renderer to OUTSTREAM video : ', j); thisBid.renderer = newRenderer(thisBid.bidId); } else { - utils.logInfo('bid is not an outstream video, will not attach a renderer: ', j); + this.logInfo('bid is not an outstream video, will not attach a renderer: ', j); } } let adserverTargeting = {}; if (enhancedAdserverTargeting) { let allBidsForThisBidid = ozoneGetAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); // add all the winning & non-winning bids for this bidId: - utils.logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); + this.logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); Object.keys(allBidsForThisBidid).forEach((bidderName, index, ar2) => { - utils.logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); + this.logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); // let bidderName = bidderNameWH.split('_')[0]; adserverTargeting[whitelabelPrefix + '_' + bidderName] = bidderName; adserverTargeting[whitelabelPrefix + '_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); @@ -560,27 +473,21 @@ export const spec = { }); } else { if (useOzWhitelistAdserverKeys) { - utils.logWarn(`You have set a whitelist of adserver keys but this will be ignored because ${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`); + this.logWarn(`You have set a whitelist of adserver keys but this will be ignored because ${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`); } else { - utils.logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); + this.logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); } } // also add in the winning bid, to be sent to dfp let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); - adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; - if (enhancedAdserverTargeting) { adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; - adserverTargeting[whitelabelPrefix + '_pb'] = winningBid.price; - adserverTargeting[whitelabelPrefix + '_pb_r'] = getRoundedBid(winningBid.price, bidType); - adserverTargeting[whitelabelPrefix + '_adId'] = String(winningBid.adId); - adserverTargeting[whitelabelPrefix + '_size'] = `${winningBid.width}x${winningBid.height}`; } if (useOzWhitelistAdserverKeys) { // delete any un-whitelisted keys - utils.logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); + this.logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); Object.keys(adserverTargeting).forEach(function(key) { if (ozWhitelistAdserverKeys.indexOf(key) === -1) { delete adserverTargeting[key]; } }); } thisBid.adserverTargeting = adserverTargeting; @@ -588,7 +495,7 @@ export const spec = { } } let endTime = new Date().getTime(); - utils.logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + this.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; }, /** @@ -632,9 +539,8 @@ export const spec = { return ret; }, // see http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs - // us privacy: https://docs.prebid.org/dev-docs/modules/consentManagementUsp.html - getUserSyncs(optionsType, serverResponse, gdprConsent, usPrivacy) { - utils.logInfo('getUserSyncs optionsType', optionsType, 'serverResponse', serverResponse, 'gdprConsent', gdprConsent, 'usPrivacy', usPrivacy, 'cookieSyncBag', this.cookieSyncBag); + getUserSyncs(optionsType, serverResponse, gdprConsent) { + this.logInfo('getUserSyncs optionsType, serverResponse, gdprConsent, cookieSyncBag', optionsType, serverResponse, gdprConsent, this.cookieSyncBag); if (!serverResponse || serverResponse.length === 0) { return []; } @@ -645,13 +551,9 @@ export const spec = { } arrQueryString.push('gdpr=' + (utils.deepAccess(gdprConsent, 'gdprApplies', false) ? '1' : '0')); arrQueryString.push('gdpr_consent=' + utils.deepAccess(gdprConsent, 'consentString', '')); - arrQueryString.push('usp_consent=' + (usPrivacy || '')); - // var objKeys = Object.getOwnPropertyNames(this.cookieSyncBag.userIdObject); - // for (let idx in objKeys) { - // let keyname = objKeys[idx]; - // arrQueryString.push(keyname + '=' + this.cookieSyncBag.userIdObject[keyname]); - // } - for (let keyname in this.cookieSyncBag.userIdObject) { + var objKeys = Object.getOwnPropertyNames(this.cookieSyncBag.userIdObject); + for (let idx in objKeys) { + let keyname = objKeys[idx]; arrQueryString.push(keyname + '=' + this.cookieSyncBag.userIdObject[keyname]); } arrQueryString.push('publisherId=' + this.cookieSyncBag.publisherId); @@ -663,7 +565,7 @@ export const spec = { if (strQueryString.length > 0) { strQueryString = '?' + strQueryString; } - utils.logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); + this.logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); return [{ type: 'iframe', url: this.getCookieSyncUrl() + strQueryString @@ -698,15 +600,15 @@ 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 + * NOTE that criteortus is deprecated & should be removed asap * @return map */ findAllUserIds(bidRequest) { var ret = {}; - // @todo - what is Neustar fabrick called & where to look for it? If it's a simple value then it will automatically be ok - // it is not in the table 'Bidder Adapter Implementation' on https://docs.prebid.org/dev-docs/modules/userId.html#prebidjs-adapters - let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; + // @todo - what is fabrick called & where to look for it? If it's a simple value then it will automatically be ok + let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus', + 'sharedid', 'lotamePanoramaId', 'fabrickId']; if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; @@ -714,37 +616,19 @@ export const spec = { if (typeof (bidRequest.userId[key]) == 'string') { ret[key] = bidRequest.userId[key]; } else if (typeof (bidRequest.userId[key]) == 'object') { - utils.logError(`WARNING: findAllUserIds had to use first key in user object to get value for bid.userId key: ${key}. Prebid adapter should be updated.`); - // fallback - get the value of the first key in the object; this is NOT desirable behaviour ret[key] = bidRequest.userId[key][Object.keys(bidRequest.userId[key])[0]]; // cannot use Object.values } else { - utils.logError(`failed to get string key value for userId : ${key}`); + this.logError(`failed to get string key value for userId : ${key}`); } } } - let lipbid = utils.deepAccess(bidRequest.userId, 'lipb.lipbid'); + var lipbid = utils.deepAccess(bidRequest.userId, 'lipb.lipbid'); if (lipbid) { ret['lipb'] = {'lipbid': lipbid}; } - let id5id = utils.deepAccess(bidRequest.userId, 'id5id.uid'); - if (id5id) { - ret['id5id'] = id5id; - } - let parrableId = utils.deepAccess(bidRequest.userId, 'parrableId.eid'); - if (parrableId) { - ret['parrableId'] = parrableId; - } - let sharedid = utils.deepAccess(bidRequest.userId, 'sharedid.id'); - if (sharedid) { - ret['sharedid'] = sharedid; - } - let sharedidthird = utils.deepAccess(bidRequest.userId, 'sharedid.third'); - if (sharedidthird) { - ret['sharedidthird'] = sharedidthird; - } } if (!ret.hasOwnProperty('pubcid')) { - let pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); + var pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); if (pubcid) { ret['pubcid'] = pubcid; // if built with old pubCommonId module } @@ -770,10 +654,10 @@ export const spec = { let arr = this.getGetParametersAsObject(); if (arr.hasOwnProperty(whitelabelPrefix + 'storedrequest')) { if (this.isValidPlacementId(arr[whitelabelPrefix + 'storedrequest'])) { - utils.logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); + this.logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); return arr[whitelabelPrefix + 'storedrequest']; } else { - utils.logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); + this.logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); } } return null; @@ -833,7 +717,7 @@ export const spec = { // if there is an ozone.oz_request = false then quit now. let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { - utils.logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); + this.logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; @@ -931,13 +815,13 @@ export const spec = { * @returns seatbid object */ export function injectAdIdsIntoAllBidResponses(seatbid) { - utils.logInfo('injectAdIdsIntoAllBidResponses', seatbid); + spec.logInfo('injectAdIdsIntoAllBidResponses', seatbid); for (let i = 0; i < seatbid.length; i++) { let sb = seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { // modify the bidId per-bid, so each bid has a unique adId within this response, and dfp can select one. // 2020-06 we now need a second level of ID because there might be multiple identical impid's within a seatbid! - sb.bid[j]['adId'] = `${sb.bid[j]['impid']}-${i}-${spec.propertyBag.whitelabel.keyPrefix}-${j}`; + sb.bid[j]['adId'] = `${sb.bid[j]['impid']}-${i}-${j}`; } } return seatbid; @@ -957,7 +841,7 @@ export function checkDeepArray(Arr) { export function defaultSize(thebidObj) { if (!thebidObj) { - utils.logInfo('defaultSize received empty bid obj! going to return fixed default size'); + spec.logInfo('defaultSize received empty bid obj! going to return fixed default size'); return { 'defaultHeight': 250, 'defaultWidth': 300 @@ -1035,14 +919,14 @@ export function getRoundedBid(price, mediaType) { let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); - utils.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); + spec.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); let priceStringsObj = getPriceBucketString( price, theConfigObject, config.getConfig('currency.granularityMultiplier') ); - utils.logInfo('priceStringsObj', priceStringsObj); + spec.logInfo('priceStringsObj', priceStringsObj); // by default, without any custom granularity set, you get granularity name : 'medium' let granularityNamePriceStringsKeyMapping = { 'medium': 'med', @@ -1053,7 +937,7 @@ export function getRoundedBid(price, mediaType) { }; if (granularityNamePriceStringsKeyMapping.hasOwnProperty(theConfigKey)) { let priceStringsKey = granularityNamePriceStringsKeyMapping[theConfigKey]; - utils.logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); + spec.logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); return priceStringsObj[priceStringsKey]; } return priceStringsObj['auto']; @@ -1122,15 +1006,15 @@ export function getWidthAndHeightFromVideoObject(objVideo) { return null; } if (playerSize[0] && typeof playerSize[0] === 'object') { - utils.logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); + spec.logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); playerSize = playerSize[0]; if (typeof playerSize[0] !== 'number' && typeof playerSize[0] !== 'string') { - utils.logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); + spec.logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); return null; } } if (playerSize.length !== 2) { - utils.logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); + spec.logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); return null; } return ({'w': playerSize[0], 'h': playerSize[1]}); @@ -1157,17 +1041,17 @@ export function playerSizeIsNestedArray(objVideo) { * @returns {*} */ function getPlayerSizeFromObject(objVideo) { - utils.logInfo('getPlayerSizeFromObject received object', objVideo); + spec.logInfo('getPlayerSizeFromObject received object', objVideo); let playerSize = utils.deepAccess(objVideo, 'playerSize'); if (!playerSize) { playerSize = utils.deepAccess(objVideo, 'ext.playerSize'); } if (!playerSize) { - utils.logError('getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); + spec.logError('getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); return null; } if (typeof playerSize !== 'object') { - utils.logError('getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); + spec.logError('getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); return null; } return playerSize; @@ -1178,7 +1062,7 @@ function getPlayerSizeFromObject(objVideo) { */ function newRenderer(adUnitCode, rendererOptions = {}) { let isLoaded = window.ozoneVideo; - utils.logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); + spec.logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); const renderer = Renderer.install({ url: spec.getRendererUrl(), config: rendererOptions, @@ -1188,12 +1072,12 @@ function newRenderer(adUnitCode, rendererOptions = {}) { try { renderer.setRender(outstreamRender); } catch (err) { - utils.logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); + spec.logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); } return renderer; } function outstreamRender(bid) { - utils.logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); + spec.logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); // push to render queue because ozoneVideo may not be loaded yet bid.renderer.push(() => { window.ozoneVideo.outstreamRender(bid); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index 6a8682c1c30..10b8ce31d28 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -27,20 +27,6 @@ var validBidRequests = [ 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: 'ozone', - 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, @@ -69,7 +55,8 @@ var validBidRequestsMulti = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; -// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId' +// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus' +// NOTE THAT criteortus is no longer referenced anywhere - should be removed asap // see http://prebid.org/dev-docs/modules/userId.html var validBidRequestsWithUserIdData = [ { @@ -86,12 +73,12 @@ var validBidRequestsWithUserIdData = [ userId: { 'pubcid': '12345678', 'tdid': '1111tdid', - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, 'criteoId': '1111criteoId', 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, - 'parrableId': {'eid': '01.5678.parrableid'}, - 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + 'parrableId': {'eid': '01.5678.parrableid'} }, userIdAsEids: [ { @@ -120,6 +107,13 @@ var validBidRequestsWithUserIdData = [ 'atype': 1, }] }, + { + 'source': 'criteortus', + 'uids': [{ + 'id': {'ozone': {'userid': 'critId123'}}, + 'atype': 1, + }] + }, { 'source': 'criteoId', 'uids': [{ @@ -336,7 +330,7 @@ var validBidderRequest1OutstreamVideo2020 = { ] }, 'userId': { - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'id5id': 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA', 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' }, 'userIdAsEids': [ @@ -2020,13 +2014,14 @@ describe('ozone Adapter', function () { let bidRequests = validBidRequests; // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { + 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'id5id': '2222', 'idl_env': '3333', + 'lipb': {'lipbid': '4444'}, 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', 'pubcid': '5555', - 'tdid': '6666', - 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + 'tdid': '6666' }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -2040,13 +2035,14 @@ describe('ozone Adapter', function () { let bidRequests = validBidRequests; // values from http://prebid.org/dev-docs/modules/userId.html#pubcommon-id bidRequests[0]['userId'] = { + 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'id5id': '2222', 'idl_env': '3333', + 'lipb': {'lipbid': '4444'}, 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in - 'tdid': '6666', - 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + 'tdid': '6666' }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); @@ -2060,9 +2056,11 @@ describe('ozone Adapter', function () { /* 'pubcid': '12345678', 'tdid': '1111tdid', - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, 'criteoId': '1111criteoId', 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {'eid': '01.5678.parrableid'} */ @@ -2076,14 +2074,16 @@ describe('ozone Adapter', function () { 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'); + expect(payload.user.ext.eids[3]['source']).to.equal('criteortus'); // this is deprecated + expect(payload.user.ext.eids[3]['uids'][0]['id']['ozone']['userid']).to.equal('critId123'); + expect(payload.user.ext.eids[4]['source']).to.equal('criteoId'); + expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('1111criteoId'); + expect(payload.user.ext.eids[5]['source']).to.equal('idl_env'); + expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('liverampId'); + expect(payload.user.ext.eids[6]['source']).to.equal('lipb'); + expect(payload.user.ext.eids[6]['uids'][0]['id']['lipbid']).to.equal('lipbidId123'); + expect(payload.user.ext.eids[7]['source']).to.equal('parrableId'); + expect(payload.user.ext.eids[7]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); }); it('replaces the auction url for a config override', function () { @@ -2093,21 +2093,6 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); expect(request.method).to.equal('POST'); - const data = JSON.parse(request.data); - expect(data.ext.ozone.origin).to.equal(fakeOrigin); - 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); - expect(request.url).to.equal(fakeurl); - expect(request.method).to.equal('POST'); - const data = JSON.parse(request.data); - expect(data.ext.ozone.origin).to.equal(fakeurl); config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); @@ -2124,28 +2109,6 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); 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 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'); - expect(utils.deepAccess(result[0].adserverTargeting, 'xx_pb')).to.equal(0.5); - expect(utils.deepAccess(result[0].adserverTargeting, 'xx_adId')).to.equal('2899ec066a91ff8-0-xx-0'); - 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 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; @@ -2168,8 +2131,8 @@ describe('ozone Adapter', function () { config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null spec.propertyBag.whitelabel = null; }); + var specMock = utils.deepClone(spec); it('should use oztestmode GET value if set', function() { - var specMock = utils.deepClone(spec); // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; @@ -2179,34 +2142,7 @@ describe('ozone Adapter', function () { 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'); }); - it('should pass through GET params if present: ozf, ozpf, ozrp, ozip', function() { - var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters : - specMock.getGetParametersAsObject = function() { - return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; - }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const data = JSON.parse(request.data); - expect(data.ext.ozone.ozf).to.equal(1); - expect(data.ext.ozone.ozpf).to.equal(0); - expect(data.ext.ozone.ozrp).to.equal(2); - expect(data.ext.ozone.ozip).to.equal(123); - }); - it('should pass through GET params if present: ozf, ozpf, ozrp, ozip with alternative values', function() { - var specMock = utils.deepClone(spec); - // mock the getGetParametersAsObject function to simulate GET parameters : - specMock.getGetParametersAsObject = function() { - return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; - }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const data = JSON.parse(request.data); - expect(data.ext.ozone.ozf).to.equal(0); - expect(data.ext.ozone.ozpf).to.equal(1); - expect(data.ext.ozone).to.not.haveOwnProperty('ozrp'); - expect(data.ext.ozone).to.not.haveOwnProperty('ozip'); - }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { - var specMock = utils.deepClone(spec); // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; @@ -2216,33 +2152,8 @@ describe('ozone Adapter', function () { 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'); }); - it('should use GET values auction=dev & cookiesync=dev if set', function() { - // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: - var specMock = utils.deepClone(spec); - specMock.getGetParametersAsObject = function() { - return {}; - }; - let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); - 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'); - - // now mock the response from getGetParametersAsObject & do the request again - - specMock = utils.deepClone(spec); - specMock.getGetParametersAsObject = function() { - return {'auction': 'dev', 'cookiesync': 'dev'}; - }; - request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); - url = request.url; - expect(url).to.equal('https://test.ozpr.net/openrtb2/auction'); - cookieUrl = specMock.getCookieSyncUrl(); - expect(cookieUrl).to.equal('https://test.ozpr.net/static/load-cookie.html'); - }); it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: - var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': '1122334455'}; // 10 digits are valid }; @@ -2253,7 +2164,6 @@ describe('ozone Adapter', function () { }); it('should NOT use an invalid ozstoredrequest GET value if set to override the placementId values, and set oz_rw to 0', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: - var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': 'BADVAL'}; // 10 digits are valid }; @@ -2320,72 +2230,6 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); }); - it('should handle ortb2 site data', function () { - config.setConfig({'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, validBidderRequest.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 () { - config.setConfig({'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, validBidderRequest.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 () { - config.setConfig({'ortb2': { - 'user': { - 'gender': 'who knows these days' - } - }}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.user.gender).to.equal('who knows these days'); - config.resetConfig(); - }); - it('should not override the user.ext.consent string even if this is set in config ortb2', function () { - config.setConfig({'ortb2': { - 'user': { - 'ext': { - 'consent': 'this is the consent override that shouldnt work', - 'consent2': 'this should be set' - } - } - }}); - const request = spec.buildRequests(validBidRequests, bidderRequestWithFullGdpr.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); @@ -2397,33 +2241,6 @@ describe('ozone Adapter', function () { } expect(payload.imp[0].video.ext).to.include({'context': 'outstream'}); }); - it('should handle standard floor config correctly', function () { - config.setConfig({ - floors: { - enforcement: { - floorDeals: false, - bidAdjustment: true - }, - data: { - currency: 'USD', - schema: { - fields: ['mediaType'] - }, - values: { - 'video': 1.20, - 'banner': 0.8 - } - } - } - }); - 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 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(); - }); }); describe('interpretResponse', function () { @@ -2449,20 +2266,11 @@ describe('ozone Adapter', function () { 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)); - validBR.uspConsent = '1YNY'; - const request = spec.buildRequests(validBidRequests, validBR); - const payload = JSON.parse(request.data); - expect(payload.user.ext.uspConsent).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'}; const request = spec.buildRequests(validBidRequests, validBidderRequestWithGdpr); - 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 () { @@ -2566,7 +2374,7 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); 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'); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adId')).to.equal('2899ec066a91ff8-0-0'); config.resetConfig(); }); it('should ignore a whitelist if enhancedAdserverTargeting is false', function () { @@ -2613,7 +2421,7 @@ describe('ozone Adapter', function () { const result = spec.interpretResponse(validres, request); expect(result.length).to.equal(1); expect(result[0]['price']).to.equal(0.9); - expect(result[0]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('2899ec066a91ff8-0-oz-1'); + expect(result[0]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('2899ec066a91ff8-0-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)); @@ -2623,7 +2431,7 @@ describe('ozone Adapter', function () { 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']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-2'); + expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-2'); // change the bid values so a different second bid for an impid by the same bidder gets dropped validres = JSON.parse(JSON.stringify(multiResponse1)); validres.body.seatbid[0].bid[1].price = 1.1; @@ -2633,7 +2441,7 @@ describe('ozone Adapter', function () { 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'); + expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-1'); }); }); @@ -2656,20 +2464,6 @@ describe('ozone Adapter', function () { expect(result[0].url).to.include('gdpr=1'); expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); }); - it('should append ccpa (usp data)', function() { - // get the cookie bag populated - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - 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.only('should use "" if no usp is sent to cookieSync', function() { - // get the cookie bag populated - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - 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 () { From f1d5e7174eb229b28b880483b06ab6024423f8f7 Mon Sep 17 00:00:00 2001 From: yieldlift <61510052+yieldlift@users.noreply.github.com> Date: Wed, 9 Jun 2021 17:43:39 +0700 Subject: [PATCH 1123/1476] added meta.adomain (#6977) --- modules/yieldliftBidAdapter.js | 3 +++ test/spec/modules/yieldliftBidAdapter_spec.js | 1 + 2 files changed, 4 insertions(+) diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index 87a598bd335..9398e2c7816 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -96,6 +96,9 @@ export const spec = { creativeId: bid.crid, netRevenue: DEFAULT_NET_REVENUE, currency: DEFAULT_CURRENCY, + meta: { + adomain: bid.adomain + } }) }) } else { diff --git a/test/spec/modules/yieldliftBidAdapter_spec.js b/test/spec/modules/yieldliftBidAdapter_spec.js index 640a0f4e613..86a7b83e2c6 100644 --- a/test/spec/modules/yieldliftBidAdapter_spec.js +++ b/test/spec/modules/yieldliftBidAdapter_spec.js @@ -208,6 +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]).to.have.property('ttl', 30); expect(bids[index]).to.have.property('netRevenue', true); } From 0998fb4dbb092dd52f560753c9a26512905716e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Fern=C3=A1ndez?= Date: Wed, 9 Jun 2021 12:50:02 +0200 Subject: [PATCH 1124/1476] Change support email (#6979) --- modules/axonixBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/axonixBidAdapter.md b/modules/axonixBidAdapter.md index 7a4606d5502..acbaae1d4b0 100644 --- a/modules/axonixBidAdapter.md +++ b/modules/axonixBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name : Axonix Bidder Adapter Module Type : Bidder Adapter -Maintainer : support+prebid@axonix.com +Maintainer : support.axonix@emodoinc.com ``` # Description From 27e45416070f97f30e7f4da8ba8d774222bfed5a Mon Sep 17 00:00:00 2001 From: Mirko Rean <3244291+mirkorean@users.noreply.github.com> Date: Wed, 9 Jun 2021 12:58:59 +0200 Subject: [PATCH 1125/1476] Add meta.advertiserDomains for Prebid 5.0 (#6978) --- modules/yieldlabBidAdapter.js | 5 ++++- test/spec/modules/yieldlabBidAdapter_spec.js | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 9c1c54cfb2b..dc039c1a687 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -114,7 +114,10 @@ export const spec = { netRevenue: false, ttl: BID_RESPONSE_TTL_SEC, referrer: '', - ad: `` + ad: ``, + meta: { + advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a' + } } if (isVideo(bidRequest, adType)) { diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 6dde671578c..3cc5971ce67 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -179,6 +179,7 @@ describe('yieldlabBidAdapter', function () { 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(' - `, - width: request.bidRequest.sizes[0][0], - height: request.bidRequest.sizes[0][1], - creativeId: request.bidRequest.adUnitCode, - netRevenue: true, - currency: 'EUR', - ttl: 60 - }]; - } -}; - -registerBidder(spec); diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js deleted file mode 100644 index b9dbae529ba..00000000000 --- a/modules/adheseBidAdapter.js +++ /dev/null @@ -1,233 +0,0 @@ -'use strict'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'adhese'; -const GVLID = 553; -const USER_SYNC_BASE_URL = 'https://user-sync.adhese.com/iframe/user_sync.html'; - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid: function(bid) { - return !!(bid.params.account && bid.params.location && (bid.params.format || bid.mediaTypes.banner.sizes)); - }, - - buildRequests: function(validBidRequests, bidderRequest) { - if (validBidRequests.length === 0) { - return null; - } - const { gdprConsent, refererInfo } = bidderRequest; - - const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; - const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; - const commonParams = { ...gdprParams, ...refererParams }; - - const slots = validBidRequests.map(bid => ({ - slotname: bidToSlotName(bid), - parameters: cleanTargets(bid.params.data) - })); - - const payload = { - slots: slots, - parameters: commonParams, - vastContentAsUrl: true, - user: { - ext: { - eids: getEids(validBidRequests), - } - } - }; - - const account = getAccount(validBidRequests); - const uri = 'https://ads-' + account + '.adhese.com/json'; - - return { - method: 'POST', - url: uri, - data: JSON.stringify(payload), - bids: validBidRequests, - options: { - contentType: 'application/json' - } - }; - }, - - interpretResponse: function(serverResponse, request) { - const serverAds = serverResponse.body.reduce(function(map, ad) { - map[ad.slotName] = ad; - return map; - }, {}); - - serverResponse.account = getAccount(request.bids); - - return request.bids - .map(bid => ({ - bid: bid, - ad: serverAds[bidToSlotName(bid)] - })) - .filter(item => item.ad) - .map(item => adResponse(item.bid, item.ad)); - }, - - getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { - if (syncOptions.iframeEnabled && serverResponses.length > 0) { - const account = serverResponses[0].account; - if (account) { - let syncurl = USER_SYNC_BASE_URL + '?account=' + account; - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&consentString=' + encodeURIComponent(gdprConsent.consentString || ''); - } - return [{type: 'iframe', url: syncurl}]; - } - } - return []; - } -}; - -function adResponse(bid, ad) { - const price = getPrice(ad); - const adDetails = getAdDetails(ad); - const markup = getAdMarkup(ad); - - const bidResponse = getbaseAdResponse({ - requestId: bid.bidId, - mediaType: ad.extension.mediaType, - cpm: Number(price.amount), - currency: price.currency, - width: Number(ad.width), - height: Number(ad.height), - creativeId: adDetails.creativeId, - dealId: adDetails.dealId, - adhese: { - originData: adDetails.originData, - origin: adDetails.origin, - originInstance: adDetails.originInstance - } - }); - - if (bidResponse.mediaType === VIDEO) { - if (ad.cachedBodyUrl) { - bidResponse.vastUrl = ad.cachedBodyUrl - } else { - bidResponse.vastXml = markup; - } - } else { - const counter = ad.impressionCounter ? "" : ''; - bidResponse.ad = markup + counter; - } - return bidResponse; -} - -function cleanTargets(target) { - const targets = {}; - if (target) { - Object.keys(target).forEach(function (key) { - const val = target[key]; - const dirtyValues = Array.isArray(val) ? val : [val]; - const values = dirtyValues.filter(v => v === 0 || v); - if (values.length > 0) { - if (targets[key]) { - const distinctValues = values.filter(v => targets[key].indexOf(v) < 0); - targets[key].push.apply(targets[key], distinctValues); - } else { - targets[key] = values; - } - } - }); - } - return targets; -} - -function bidToSlotName(bid) { - if (bid.params.format) { - return bid.params.location + '-' + bid.params.format; - } - - var sizes = bid.mediaTypes.banner.sizes; - sizes.sort(); - var format = sizes.map(size => size[0] + 'x' + size[1]).join('_'); - - if (format.length > 0) { - return bid.params.location + '-' + format; - } else { - return bid.params.location; - } -} - -function getAccount(validBidRequests) { - return validBidRequests[0].params.account; -} - -function getEids(validBidRequests) { - if (validBidRequests[0] && validBidRequests[0].userIdAsEids) { - return validBidRequests[0].userIdAsEids; - } -} - -function getbaseAdResponse(response) { - return Object.assign({ netRevenue: true, ttl: 360 }, response); -} - -function isAdheseAd(ad) { - return !ad.origin || ad.origin === 'JERLICIA'; -} - -function getAdMarkup(ad) { - if (!isAdheseAd(ad) || (ad.ext === 'js' && ad.body !== undefined && ad.body !== '' && ad.body.match(/ { - return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placementId)); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language : '', - 'secure': 1, - 'host': location.host, - 'page': location.pathname, - 'placements': placements - }; - request.language.indexOf('-') != -1 && (request.language = request.language.split('-')[0]) - 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++) { - let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER - - placements.push({ - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [], - traffic: traff - }); - if (bid.schain) { - placements.schain = bid.schain; - } - } - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - serverResponse = serverResponse.body; - for (let i = 0; i < serverResponse.length; i++) { - let resItem = serverResponse[i]; - if (isBidResponseValid(resItem)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = URL_SYNC - 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}`; - } - return [{ - type: 'image', - url: syncUrl - }]; - } - -}; - -registerBidder(spec); diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js deleted file mode 100644 index d46c01f7f48..00000000000 --- a/modules/admaticBidAdapter.js +++ /dev/null @@ -1,147 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'admatic'; -const ENDPOINT_URL = 'https://ads4.admatic.com.tr/prebid/v3/bidrequest'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['admatic'], // 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) { - return !!(bid.params.pid && bid.params.wid && bid.params.url); - }, - /** - * 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) { - const payload = { - request: [] - }; - - for (var i = 0; i < validBidRequests.length; i++) { - var validBidRequest = validBidRequests[i]; - payload.auctionId = validBidRequest.auctionId; - payload.bidder = validBidRequest.bidder; - payload.bidderRequestId = validBidRequest.bidderRequestId; - payload.pid = validBidRequest.params.pid; - payload.wid = validBidRequest.params.wid; - payload.url = validBidRequest.params.url; - - var request = { - adUnitCode: validBidRequest.adUnitCode, - bidId: validBidRequest.bidId, - transactionId: validBidRequest.transactionId, - priceType: validBidRequest.params.priceType, - sizes: transformSizes(validBidRequest.sizes) - } - - payload.request.push(request); - } - - const payloadString = JSON.stringify(payload); - - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString, - bidder: 'admatic', - bids: validBidRequests - }; - }, - - /** - * 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 = []; - - if (serverBody) { - if (serverBody.tags && serverBody.tags.length > 0) { - serverBody.tags.forEach(serverBid => { - if (serverBid != null) { - if (serverBid.cpm !== 0) { - const bidResponse = { - requestId: serverBid.bidId, - cpm: serverBid.cpm, - width: serverBid.width, - height: serverBid.height, - creativeId: serverBid.creativeId, - dealId: serverBid.dealId, - currency: serverBid.currency, - netRevenue: serverBid.netRevenue, - ttl: serverBid.ttl, - referrer: serverBid.referrer, - ad: serverBid.ad - }; - - 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) { - const syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: 'https://ads4.admatic.com.tr/prebid/static/usersync/v3/async_usersync.html' - }); - } - - if (syncOptions.pixelEnabled && serverResponses.length > 0) { - syncs.push({ - type: 'image', - url: 'https://ads5.admatic.com.tr/prebid/v3/bidrequest/usersync' - }); - } - return syncs; - } -} - -/* Turn bid request sizes into ut-compatible format */ -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; -} - -registerBidder(spec); diff --git a/modules/admediaBidAdapter.js b/modules/admediaBidAdapter.js deleted file mode 100644 index 0a9e510843c..00000000000 --- a/modules/admediaBidAdapter.js +++ /dev/null @@ -1,71 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'admedia'; -const ENDPOINT_URL = 'https://prebid.admedia.com/bidder/'; - -export const spec = { - code: BIDDER_CODE, - - isBidRequestValid: function (bid) { - return bid.params && !!bid.params.aid; - }, - - buildRequests: function (validBidRequests, bidderRequest) { - let payload = {}; - - if (bidderRequest && bidderRequest.refererInfo) { - payload.referer = encodeURIComponent(bidderRequest.refererInfo.referer); - } - - payload.tags = []; - - utils._each(validBidRequests, function (bid) { - const tag = { - id: bid.bidId, - sizes: bid.sizes, - aid: bid.params.aid - }; - payload.tags.push(tag); - }); - - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString, - }; - }, - - interpretResponse: function (serverResponse, bidRequest) { - const bidResponses = []; - - if (!serverResponse.body.tags) { - return bidResponses; - } - - utils._each(serverResponse.body.tags, function (response) { - if (!response.error && response.cpm > 0) { - const bidResponse = { - requestId: response.id, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.id, - dealId: response.id, - currency: 'USD', - netRevenue: true, - ttl: 120, - // referrer: REFERER, - ad: response.ad - }; - - bidResponses.push(bidResponse); - } - }); - - return bidResponses; - } -}; - -registerBidder(spec); diff --git a/modules/adpartnerBidAdapter.js b/modules/adpartnerBidAdapter.js deleted file mode 100644 index 2180b472120..00000000000 --- a/modules/adpartnerBidAdapter.js +++ /dev/null @@ -1,124 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js' -import {ajax} from '../src/ajax.js'; - -const BIDDER_CODE = 'adpartner'; -export const ENDPOINT_PROTOCOL = 'https'; -export const ENDPOINT_DOMAIN = 'a4p.adpartner.pro'; -export const ENDPOINT_PATH = '/hb/bid'; - -export const spec = { - code: BIDDER_CODE, - - isBidRequestValid: function (bidRequest) { - return !!parseInt(bidRequest.params.unitId); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - let referer = window.location.href; - try { - referer = typeof bidderRequest.refererInfo === 'undefined' - ? window.top.location.href - : bidderRequest.refererInfo.referer; - } catch (e) {} - - let bidRequests = []; - let beaconParams = { - tag: [], - sizes: [], - referer: '' - }; - - validBidRequests.forEach(function(validBidRequest) { - bidRequests.push({ - unitId: parseInt(validBidRequest.params.unitId), - adUnitCode: validBidRequest.adUnitCode, - sizes: validBidRequest.sizes, - bidId: validBidRequest.bidId, - referer: referer - }); - - beaconParams.tag.push(validBidRequest.params.unitId); - beaconParams.sizes.push(spec.joinSizesToString(validBidRequest.sizes)); - beaconParams.referer = encodeURIComponent(referer); - }); - - beaconParams.tag = beaconParams.tag.join(','); - beaconParams.sizes = beaconParams.sizes.join(','); - - let adPartnerRequestUrl = utils.buildUrl({ - protocol: ENDPOINT_PROTOCOL, - hostname: ENDPOINT_DOMAIN, - pathname: ENDPOINT_PATH, - search: beaconParams - }); - - return { - method: 'POST', - url: adPartnerRequestUrl, - data: JSON.stringify(bidRequests) - }; - }, - - joinSizesToString: function(sizes) { - let res = []; - sizes.forEach(function(size) { - res.push(size.join('x')); - }); - - return res.join('|'); - }, - - interpretResponse: function (serverResponse, bidRequest) { - const validBids = JSON.parse(bidRequest.data); - - if (typeof serverResponse.body === 'undefined') { - return []; - } - - return validBids - .map(bid => ({ - bid: bid, - ad: serverResponse.body[bid.adUnitCode] - })) - .filter(item => item.ad) - .map(item => spec.adResponse(item.bid, item.ad)); - }, - - adResponse: function(bid, ad) { - return { - requestId: bid.bidId, - ad: ad.ad, - cpm: ad.cpm, - width: ad.width, - height: ad.height, - ttl: 60, - creativeId: ad.creativeId, - netRevenue: ad.netRevenue, - currency: ad.currency, - winNotification: ad.winNotification - }; - }, - - onBidWon: function(data) { - data.winNotification.forEach(function(unitWon) { - let adPartnerBidWonUrl = utils.buildUrl({ - protocol: ENDPOINT_PROTOCOL, - hostname: ENDPOINT_DOMAIN, - pathname: unitWon.path - }); - - if (unitWon.method === 'POST') { - spec.postRequest(adPartnerBidWonUrl, JSON.stringify(unitWon.data)); - } - }); - - return true; - }, - - postRequest(endpoint, data) { - ajax(endpoint, null, data, {method: 'POST'}); - } -} - -registerBidder(spec); diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js deleted file mode 100644 index 12d0410a821..00000000000 --- a/modules/adprimeBidAdapter.js +++ /dev/null @@ -1,129 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'adprime'; -const AD_URL = 'https://delta.adprime.com/?c=o&m=multi'; -const SYNC_URL = 'https://delta.adprime.com/?c=rtb&m=sync'; - -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); - case NATIVE: - return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - '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++) { - let bid = validBidRequests[i]; - let sizes - let identeties = {} - if (bid.mediaTypes) { - if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - sizes = bid.mediaTypes[BANNER].sizes - } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { - sizes = bid.mediaTypes[VIDEO].playerSize - } - } - if (bid.userId && bid.userId.idl_env) { - identeties.identityLink = bid.userId.idl_env - } - - placements.push({ - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: sizes || [], - wPlayer: sizes ? sizes[0] : 0, - hPlayer: sizes ? sizes[1] : 0, - traffic: bid.params.traffic || BANNER, - schain: bid.schain || {}, - keywords: bid.params.keywords || [], - identeties - }); - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = SYNC_URL - 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}`; - } - return [{ - type: 'image', - url: syncUrl - }]; - } - -}; - -registerBidder(spec); diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index c6298cffde9..b3efa8a303d 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -428,9 +428,6 @@ function bidToTag(bid) { tag.use_pmt_rule = bid.params.usePaymentRule || false tag.prebid = true; tag.disable_psa = true; - if (bid.params.reserve) { - tag.reserve = bid.params.reserve; - } if (bid.params.position) { tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; } diff --git a/modules/adspendBidAdapter.js b/modules/adspendBidAdapter.js deleted file mode 100644 index 9fe70885eeb..00000000000 --- a/modules/adspendBidAdapter.js +++ /dev/null @@ -1,164 +0,0 @@ -import * as utils 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 } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); -const BIDDER_CODE = 'adspend'; -const BID_URL = 'https://rtb.com.ru/headerbidding-bid'; -const SYNC_URL = 'https://rtb.com.ru/headerbidding-sync?uid={UUID}'; -const COOKIE_NAME = 'hb-adspend-id'; -const TTL = 10000; -const RUB = 'RUB'; -const FIRST_PRICE = 1; -const NET_REVENUE = true; - -const winEventURLs = {}; -const placementToBidMap = {}; - -export const spec = { - code: BIDDER_CODE, - aliases: ['as'], - supportedMediaTypes: [BANNER], - - onBidWon: function(winObj) { - const requestId = winObj.requestId; - const cpm = winObj.cpm; - const event = winEventURLs[requestId].replace( - /\$\{AUCTION_PRICE\}/, - cpm - ); - - ajax(event, null); - }, - - isBidRequestValid: function(bid) { - const adServerCur = config.getConfig('currency.adServerCurrency') === RUB; - - return !!(adServerCur && - bid.params && - bid.params.bidfloor && - bid.crumbs.pubcid && - utils.checkCookieSupport() && - storage.cookiesAreEnabled() - ); - }, - - buildRequests: function(bidRequests, bidderRequest) { - const req = bidRequests[Math.floor(Math.random() * bidRequests.length)]; - const bidId = req.bidId; - const at = FIRST_PRICE; - const site = { id: req.crumbs.pubcid, domain: document.domain }; - const device = { ua: navigator.userAgent, ip: '' }; - const user = { id: getUserID() } - const cur = [ RUB ]; - const tmax = bidderRequest.timeout; - - const imp = bidRequests.map(req => { - const params = req.params; - - const tagId = params.tagId; - const id = params.placement; - const banner = { 'format': getFormats(req.sizes) }; - const bidfloor = params.bidfloor !== undefined - ? Number(params.bidfloor) : 1; - const bidfloorcur = RUB; - - placementToBidMap[id] = bidId; - - return { - id, - tagId, - banner, - bidfloor, - bidfloorcur, - secure: 0, - }; - }); - - const payload = { - bidId, - at, - site, - device, - user, - imp, - cur, - tmax, - }; - - return { - method: 'POST', - url: BID_URL, - data: JSON.stringify(payload), - }; - }, - - interpretResponse: function(resp, {bidderRequest}) { - if (resp.body === '') return []; - - const bids = resp.body.seatbid[0].bid.map(bid => { - const cpm = bid.price; - const impid = bid.impid; - const requestId = placementToBidMap[impid]; - const width = bid.w; - const height = bid.h; - const creativeId = bid.adid; - const dealId = bid.dealid; - const currency = resp.body.cur; - const netRevenue = NET_REVENUE; - const ttl = TTL; - const ad = bid.adm; - - return { - cpm, - requestId, - width, - height, - creativeId, - dealId, - currency, - netRevenue, - ttl, - ad, - }; - }); - - return bids; - }, - - getUserSyncs: function(syncOptions, resps) { - let syncs = []; - - resps.forEach(resp => { - if (syncOptions.pixelEnabled && resp.body === '') { - const uuid = getUserID(); - syncs.push({ - type: 'image', - url: SYNC_URL.replace('{UUID}', uuid), - }); - } - }); - - return syncs - } -} - -const getUserID = () => { - const i = storage.getCookie(COOKIE_NAME); - - if (i === null) { - const uuid = utils.generateUUID(); - storage.setCookie(COOKIE_NAME, uuid); - return uuid; - } - return i; -}; - -const getFormats = arr => arr.map((s) => { - return { w: s[0], h: s[1] }; -}); - -registerBidder(spec); diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index b4dc7f7ea89..96f1ceb12f0 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -77,15 +77,6 @@ function _getDomainFromURL(url) { return anchor.hostname; } -function _parseSlotParam(paramName, paramValue) { - switch (paramName) { - case 'reserve': - return parseFloat(paramValue) || UNDEFINED; - default: - return paramValue; - } -} - let platform = (function getPlatform() { var ua = navigator.userAgent; if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1) { @@ -380,7 +371,7 @@ function _createImpressionObject(bid, conf) { impObj = { id: bid.bidId, tagid: String(bid.params.zoneId || undefined), - bidfloor: _parseSlotParam('reserve', bid.params.reserve), + bidfloor: 0, secure: 1, ext: {}, bidfloorcur: ADTRUE_CURRENCY diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js deleted file mode 100755 index 746baa9ae35..00000000000 --- a/modules/advangelistsBidAdapter.js +++ /dev/null @@ -1,378 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js-pure/features/array/find.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const ADAPTER_VERSION = '1.0'; -const BIDDER_CODE = 'advangelists'; - -export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; -export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; -export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; -export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; -export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; - -let pubid = ''; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid(bidRequest) { - if (typeof bidRequest != 'undefined') { - if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } - if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } - return true; - } else { return false; } - }, - - buildRequests(bids, bidderRequest) { - let requests = []; - let videoBids = bids.filter(bid => isVideoBidValid(bid)); - let bannerBids = bids.filter(bid => isBannerBidValid(bid)); - videoBids.forEach(bid => { - pubid = getVideoBidParam(bid, 'pubid'); - requests.push({ - method: 'POST', - url: VIDEO_ENDPOINT + pubid, - data: createVideoRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - - bannerBids.forEach(bid => { - pubid = getBannerBidParam(bid, 'pubid'); - requests.push({ - method: 'POST', - url: BANNER_ENDPOINT + pubid, - data: createBannerRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - return requests; - }, - - interpretResponse(serverResponse, {bidRequest}) { - let response = serverResponse.body; - if (response !== null && utils.isEmpty(response) == false) { - if (isVideoBid(bidRequest)) { - let bidResponse = { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - mediaType: VIDEO, - netRevenue: true - } - - if (response.seatbid[0].bid[0].adm) { - bidResponse.vastXml = response.seatbid[0].bid[0].adm; - bidResponse.adResponse = { - content: response.seatbid[0].bid[0].adm - }; - } else { - bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; - } - - return bidResponse; - } else { - return { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ad: response.seatbid[0].bid[0].adm, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - mediaType: BANNER, - netRevenue: true - } - } - } - } -}; - -function isBannerBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); -} - -function isVideoBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.video'); -} - -function isVideoBidValid(bid) { - return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); -} - -function isBannerBidValid(bid) { - return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); -} - -function getVideoBidParam(bid, key) { - return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function getBannerBidParam(bid, key) { - return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); -} - -function isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); -} - -function getDoNotTrack() { - return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; -} - -function findAndFillParam(o, key, value) { - try { - if (typeof value === 'function') { - o[key] = value(); - } else { - o[key] = value; - } - } catch (ex) {} -} - -function getOsVersion() { - let clientStrings = [ - { s: 'Android', r: /Android/ }, - { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, - { s: 'Mac OS X', r: /Mac OS X/ }, - { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, - { s: 'Linux', r: /(Linux|X11)/ }, - { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, - { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, - { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, - { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, - { s: 'Windows Vista', r: /Windows NT 6.0/ }, - { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, - { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, - { s: 'UNIX', r: /UNIX/ }, - { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } - ]; - let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); - return cs ? cs.s : 'unknown'; -} - -function getFirstSize(sizes) { - return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; -} - -function parseSizes(sizes) { - return utils.parseSizesInput(sizes).map(size => { - let [ width, height ] = size.split('x'); - return { - w: parseInt(width, 10) || undefined, - h: parseInt(height, 10) || undefined - }; - }); -} - -function getVideoSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); -} - -function getBannerSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); -} - -function getTopWindowReferrer() { - try { - return window.top.document.referrer; - } catch (e) { - return ''; - } -} - -function getVideoTargetingParams(bid) { - return Object.keys(Object(bid.params.video)) - .filter(param => includes(VIDEO_TARGETING, param)) - .reduce((obj, param) => { - obj[ param ] = bid.params.video[ param ]; - return obj; - }, {}); -} - -function createVideoRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - let sizes = getVideoSizes(bid); - let firstSize = getFirstSize(sizes); - - let video = getVideoTargetingParams(bid); - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1, - 'os': getOsVersion() - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getVideoBidParam(bid, 'placement'); - - for (let j = 0; j < sizes.length; j++) { - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': 2.0, - 'bidfloorcur': 'USD', - 'secure': secure, - 'video': Object.assign({ - 'id': utils.generateUUID(), - 'pos': 0, - 'w': firstSize.w, - 'h': firstSize.h, - 'mimes': DEFAULT_MIMES - }, video) - - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} - -function getTopWindowLocation(bidderRequest) { - let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; - return utils.parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); -} - -function createBannerRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - let sizes = getBannerSizes(bid); - - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1 - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getBannerBidParam(bid, 'placement'); - for (let j = 0; j < sizes.length; j++) { - let size = sizes[j]; - - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': 2.0, - 'bidfloorcur': 'USD', - 'secure': secure, - 'banner': { - 'id': utils.generateUUID(), - 'pos': 0, - 'w': size['w'], - 'h': size['h'] - } - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} - -registerBidder(spec); diff --git a/modules/advenueBidAdapter.js b/modules/advenueBidAdapter.js deleted file mode 100644 index d7fa614b0a7..00000000000 --- a/modules/advenueBidAdapter.js +++ /dev/null @@ -1,86 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'advenue'; -const URL_MULTI = 'https://ssp.advenuemedia.co.uk/?c=o&m=multi'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - /** - * 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: (bid) => { - return Boolean(bid.bidId && - bid.params && - !isNaN(bid.params.placementId) && - spec.supportedMediaTypes.indexOf(bid.params.traffic) !== -1 - ); - }, - - /** - * 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: (validBidRequests, bidderRequest) => { - let winTop; - try { - winTop = window.top; - winTop.location.toString(); - } catch (e) { - utils.logMessage(e); - winTop = window; - }; - - const location = bidderRequest ? new URL(bidderRequest.refererInfo.referer) : winTop.location; - const placements = []; - const request = { - 'secure': (location.protocol === 'https:') ? 1 : 0, - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'host': location.host, - 'page': location.pathname, - 'placements': placements - }; - - for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i]; - const params = bid.params; - placements.push({ - placementId: params.placementId, - bidId: bid.bidId, - sizes: bid.sizes, - traffic: params.traffic - }); - } - return { - method: 'POST', - url: URL_MULTI, - data: request - }; - }, - - /** - * 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: (serverResponse) => { - try { - serverResponse = serverResponse.body; - } catch (e) { - utils.logMessage(e); - }; - return serverResponse; - }, -}; - -registerBidder(spec); diff --git a/modules/advertlyBidAdapter.js b/modules/advertlyBidAdapter.js deleted file mode 100755 index 973b6dd0742..00000000000 --- a/modules/advertlyBidAdapter.js +++ /dev/null @@ -1,127 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import * as utils from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import { ajax } from '../src/ajax.js'; -import {Renderer} from '../src/Renderer.js'; - -const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; -const BIDDER_CODE = 'advertly'; -const DOMAIN = 'https://api.advertly.com/'; -const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -function isBidRequestValid(bid) { - return (typeof bid.params !== 'undefined' && parseInt(utils.getValue(bid.params, 'publisherId')) > 0); -} - -function buildRequests(validBidRequests) { - return { - method: 'POST', - url: DOMAIN + 'www/admin/plugins/Prebid/getAd.php', - options: { - withCredentials: false, - crossOrigin: true - }, - data: validBidRequests, - }; -} - -function interpretResponse(serverResponse, request) { - const response = serverResponse.body; - const bidResponses = []; - var bidRequestResponses = []; - utils._each(response, function(bidAd) { - let bnd = {}; - Object.assign(bnd, bidAd); - bnd.adResponse = { - content: bidAd.vastXml, - height: bidAd.height, - width: bidAd.width - }; - bnd.ttl = config.getConfig('_bidderTimeout') - bnd.renderer = bidAd.context === 'outstream' ? createRenderer(bidAd, RENDERER_URL) : undefined; - bidResponses.push(bnd); - }); - - bidRequestResponses.push({ - function: 'saveResponses', - request: request, - response: bidResponses - }); - sendResponseToServer(bidRequestResponses); - return bidResponses; -} - -function outstreamRender(bidAd) { - bidAd.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - sizes: [bidAd.width, bidAd.height], - width: bidAd.width, - height: bidAd.height, - targetId: bidAd.adUnitCode, - adResponse: bidAd.adResponse, - rendererOptions: { - showVolume: false, - allowFullscreen: false - } - }); - }); -} - -function createRenderer(bidAd, url) { - const renderer = Renderer.install({ - id: bidAd.adUnitCode, - url: url, - loaded: false, - config: {'player_height': bidAd.height, 'player_width': bidAd.width}, - adUnitCode: bidAd.adUnitCode - }); - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - return renderer; -} - -function onBidWon(bid) { - let wonBids = []; - wonBids.push(bid); - wonBids[0].function = 'onBidWon'; - sendResponseToServer(wonBids); -} - -function onTimeout(details) { - details.unshift({ 'function': 'onTimeout' }); - sendResponseToServer(details); -} - -function sendResponseToServer(data) { - ajax(DOMAIN + 'www/admin/plugins/Prebid/tracking/track.php', null, JSON.stringify(data), { - withCredentials: false, - method: 'POST', - crossOrigin: true - }); -} - -function getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: DOMAIN + 'www/admin/plugins/Prebid/userSync.php' - }]; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, - onBidWon, - onTimeout -}; - -registerBidder(spec); diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js deleted file mode 100644 index ce4196fe249..00000000000 --- a/modules/ajaBidAdapter.js +++ /dev/null @@ -1,194 +0,0 @@ -import { Renderer } from '../src/Renderer.js'; -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'aja'; -const URL = 'https://ad.as.amanad.adtdp.com/v2/prebid'; -const SDK_TYPE = 5; -const AD_TYPE = { - BANNER: 1, - NATIVE: 2, - VIDEO: 3, -}; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [VIDEO, BANNER, NATIVE], - - isBidRequestValid: function(bid) { - return !!(bid.params.asi); - }, - - buildRequests: function(validBidRequests, bidderRequest) { - var bidRequests = []; - for (var i = 0, len = validBidRequests.length; i < len; i++) { - var bid = validBidRequests[i]; - var queryString = ''; - const asi = utils.getBidIdParameter('asi', bid.params); - queryString = utils.tryAppendQueryString(queryString, 'asi', asi); - queryString = utils.tryAppendQueryString(queryString, 'skt', SDK_TYPE); - queryString = utils.tryAppendQueryString(queryString, 'prebid_id', bid.bidId); - queryString = utils.tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); - - if (bidderRequest && bidderRequest.refererInfo) { - queryString = utils.tryAppendQueryString(queryString, 'page_url', bidderRequest.refererInfo.referer); - } - - bidRequests.push({ - method: 'GET', - url: URL, - data: queryString - }); - } - - return bidRequests; - }, - - interpretResponse: function(bidderResponse, request) { - const bidderResponseBody = bidderResponse.body; - - if (!bidderResponseBody.is_ad_return) { - return []; - } - - const ad = bidderResponseBody.ad; - - const bid = { - requestId: ad.prebid_id, - cpm: ad.price, - creativeId: ad.creative_id, - dealId: ad.deal_id, - currency: ad.currency || 'USD', - netRevenue: true, - ttl: 300, // 5 minutes - } - - if (AD_TYPE.VIDEO === ad.ad_type) { - const videoAd = bidderResponseBody.ad.video; - Object.assign(bid, { - vastXml: videoAd.vtag, - width: videoAd.w, - height: videoAd.h, - renderer: newRenderer(bidderResponseBody), - adResponse: bidderResponseBody, - mediaType: VIDEO - }); - } else if (AD_TYPE.BANNER === ad.ad_type) { - const bannerAd = bidderResponseBody.ad.banner; - Object.assign(bid, { - width: bannerAd.w, - height: bannerAd.h, - ad: bannerAd.tag, - mediaType: BANNER - }); - try { - bannerAd.imps.forEach(impTracker => { - const tracker = utils.createTrackPixelHtml(impTracker); - bid.ad += tracker; - }); - } catch (error) { - utils.logError('Error appending tracking pixel', error); - } - } else if (AD_TYPE.NATIVE === ad.ad_type) { - const nativeAds = ad.native.template_and_ads.ads; - - nativeAds.forEach(nativeAd => { - const assets = nativeAd.assets; - - Object.assign(bid, { - mediaType: NATIVE - }); - - bid.native = { - title: assets.title, - body: assets.description, - cta: assets.cta_text, - sponsoredBy: assets.sponsor, - clickUrl: assets.lp_link, - impressionTrackers: nativeAd.imps, - privacyLink: assets.adchoice_url, - }; - - if (assets.img_main !== undefined) { - bid.native.image = { - url: assets.img_main, - width: parseInt(assets.img_main_width, 10), - height: parseInt(assets.img_main_height, 10) - }; - } - - if (assets.img_icon !== undefined) { - bid.native.icon = { - url: assets.img_icon, - width: parseInt(assets.img_icon_width, 10), - height: parseInt(assets.img_icon_height, 10) - }; - } - }); - } - - return [bid]; - }, - - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - if (!serverResponses.length) { - return syncs; - } - - const bidderResponseBody = serverResponses[0].body; - - if (syncOptions.pixelEnabled && bidderResponseBody.syncs) { - bidderResponseBody.syncs.forEach(sync => { - syncs.push({ - type: 'image', - url: sync - }); - }); - } - - if (syncOptions.iframeEnabled && bidderResponseBody.sync_htmls) { - bidderResponseBody.sync_htmls.forEach(sync => { - syncs.push({ - type: 'iframe', - url: sync - }); - }); - } - - return syncs; - }, -} - -function newRenderer(bidderResponse) { - const renderer = Renderer.install({ - id: bidderResponse.ad.prebid_id, - url: bidderResponse.ad.video.purl, - loaded: false, - }); - - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on newRenderer', err); - } - - return renderer; -} - -function outstreamRender(bid) { - bid.renderer.push(() => { - window.aja_vast_player.init({ - vast_tag: bid.adResponse.ad.video.vtag, - ad_unit_code: bid.adUnitCode, // target div id to render video - width: bid.width, - height: bid.height, - progress: bid.adResponse.ad.video.progress, - loop: bid.adResponse.ad.video.loop, - inread: bid.adResponse.ad.video.inread - }); - }); -} - -registerBidder(spec); diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 03e4ac9021a..4ab6f53ebdc 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -86,9 +86,7 @@ function _isOneMobileBidder(bidderCode) { function _isNexageRequestPost(bid) { if (_isOneMobileBidder(bid.bidder) && bid.params.id && bid.params.imp && bid.params.imp[0]) { let imp = bid.params.imp[0]; - return imp.id && imp.tagid && - ((imp.banner && imp.banner.w && imp.banner.h) || - (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration)); + return imp.id && imp.tagid && imp.banner && imp.banner.w && imp.banner.h; } } diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 58aa40f5ce9..72cf0baa3bd 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -14,6 +14,7 @@ const URL = 'https://ib.adnxs.com/ut/v3/prebid'; const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', 'skippable', 'playback_method', 'frameworks', 'context', 'skipoffset']; +const VIDEO_RTB_TARGETING = ['minduration', 'maxduration', 'skip', 'skipafter', 'playbackmethod', 'api']; const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; const APP_DEVICE_PARAMS = ['geo', 'device_id']; // appid is collected separately const DEBUG_PARAMS = ['enabled', 'dongle', 'member_id', 'debug_timeout']; @@ -818,6 +819,51 @@ function bidToTag(bid) { } } + // use IAB ORTB values if the corresponding values weren't already set by bid.params.video + if (videoMediaType) { + tag.video = tag.video || {}; + Object.keys(videoMediaType) + .filter(param => includes(VIDEO_RTB_TARGETING, param)) + .forEach(param => { + switch (param) { + case 'minduration': + case 'maxduration': + if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param]; + break; + case 'skip': + if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1); + break; + case 'skipafter': + if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param]; + break; + case 'playbackmethod': + if (typeof tag.video['playback_method'] !== 'number') { + let type = videoMediaType[param]; + type = (utils.isArray(type)) ? type[0] : type; + + // we only support iab's options 1-4 at this time. + if (type >= 1 && type <= 4) { + tag.video['playback_method'] = type; + } + } + break; + case 'api': + if (!tag['video_frameworks'] && utils.isArray(videoMediaType[param])) { + // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values) + let apiTmp = videoMediaType[param].map(val => { + let v = (val === 4) ? 5 : (val === 5) ? 4 : val; + + if (v >= 1 && v <= 5) { + return v; + } + }).filter(v => v); + tag['video_frameworks'] = apiTmp; + } + break; + } + }); + } + if (bid.renderer) { tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); } diff --git a/modules/appnexusBidAdapter.md b/modules/appnexusBidAdapter.md index d1f61836297..7ac70e67584 100644 --- a/modules/appnexusBidAdapter.md +++ b/modules/appnexusBidAdapter.md @@ -89,7 +89,18 @@ var adUnits = [ mediaTypes: { video: { playerSize: [[300, 250]], - context: 'outstream' + context: 'outstream', + // Certain ORTB 2.5 video values can be read from the mediatypes object; below are examples of supported params. + // To note - appnexus supports additional values for our system that are not part of the ORTB spec. If you want + // to use these values, they will have to be declared in the bids[].params.video object instead using the appnexus syntax. + // Between the corresponding values of the mediaTypes.video and params.video objects, the properties in params.video will + // take precedence if declared; eg in the example below, the `skippable: true` setting will be used instead of the `skip: 0`. + minduration: 1, + maxduration: 60, + skip: 0, // 1 - true, 0 - false + skipafter: 5, + playbackmethod: [2], // note - we only support options 1-4 at this time + api: [1,2,3] // note - option 6 is not supported at this time } }, bids: [ diff --git a/modules/atomxBidAdapter.js b/modules/atomxBidAdapter.js deleted file mode 100644 index e9f15218c4c..00000000000 --- a/modules/atomxBidAdapter.js +++ /dev/null @@ -1,107 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'atomx'; - -function getDomain() { - var domain = ''; - - try { - if ((domain === '') && (window.top == window)) { - domain = window.location.href; - } - - if ((domain === '') && (window.top == window.parent)) { - domain = document.referrer; - } - - if (domain == '') { - var atomxt = 'atomxtest'; - - // It should be impossible to change the window.location.ancestorOrigins. - window.location.ancestorOrigins[0] = atomxt; - if (window.location.ancestorOrigins[0] != atomxt) { - var ancestorOrigins = window.location.ancestorOrigins; - - // If the length is 0 we are a javascript tag running in the main domain. - // But window.top != window or window.location.hostname is empty. - if (ancestorOrigins.length == 0) { - // This browser is so fucked up, just return an empty string. - return ''; - } - - // ancestorOrigins is an array where [0] is our own window.location - // and [length-1] is the top window.location. - domain = ancestorOrigins[ancestorOrigins.length - 1]; - } - } - } catch (unused) { - } - - if (domain === '') { - domain = document.referrer; - } - - if (domain === '') { - domain = window.location.href; - } - - return domain.substr(0, 512); -} - -export const spec = { - code: BIDDER_CODE, - - isBidRequestValid: function(bid) { - return bid.params && (!!bid.params.id); - }, - - buildRequests: function(validBidRequests) { - return validBidRequests.map(bidRequest => { - return { - method: 'GET', - url: 'https://p.ato.mx/placement', - data: { - v: 12, - id: bidRequest.params.id, - size: utils.parseSizesInput(bidRequest.sizes)[0], - prebid: bidRequest.bidId, - b: 0, - h: '7t3y9', - type: 'javascript', - screen: window.screen.width + 'x' + window.screen.height + 'x' + window.screen.colorDepth, - timezone: new Date().getTimezoneOffset(), - domain: getDomain(), - r: document.referrer.substr(0, 512), - }, - }; - }); - }, - - interpretResponse: function (serverResponse, bidRequest) { - const body = serverResponse.body; - const res = { - requestId: body.code, - cpm: body.cpm * 1000, - width: body.width, - height: body.height, - creativeId: body.creative_id, - currency: 'USD', - netRevenue: true, - ttl: 60, - }; - - if (body.adm) { - res.ad = body.adm; - } else { - res.adUrl = body.url; - } - - return [res]; - }, - - getUserSyncs: function(syncOptions, serverResponses) { - return []; - }, -}; -registerBidder(spec); diff --git a/modules/avocetBidAdapter.js b/modules/avocetBidAdapter.js deleted file mode 100644 index 1283bb865d4..00000000000 --- a/modules/avocetBidAdapter.js +++ /dev/null @@ -1,141 +0,0 @@ -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'avct'; -const DEFAULT_BASE_URL = 'https://ads.avct.cloud'; -const DEFAULT_PREBID_PATH = '/prebid'; - -function getPrebidURL() { - let host = config.getConfig('avct.baseUrl'); - if (host && typeof host === 'string') { - return `${host}${getPrebidPath()}`; - } - return `${DEFAULT_BASE_URL}${getPrebidPath()}`; -} - -function getPrebidPath() { - let prebidPath = config.getConfig('avct.prebidPath'); - if (prebidPath && typeof prebidPath === 'string') { - return prebidPath; - } - return DEFAULT_PREBID_PATH; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid with params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function (bid) { - return ( - !!bid.params && - !!bid.params.placement && - typeof bid.params.placement === 'string' && - bid.params.placement.length === 24 - ); - }, - /** - * 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 (bidRequests, bidderRequest) { - // Get currency from config - const currency = config.getConfig('currency.adServerCurrency'); - - // Publisher domain from config - const publisherDomain = config.getConfig('publisherDomain'); - - // First-party data from config - const fpd = config.getLegacyFpd(config.getConfig('ortb2')); - - // GDPR status and TCF consent string - let tcfConsentString; - let gdprApplies = false; - if (bidderRequest.gdprConsent) { - tcfConsentString = bidderRequest.gdprConsent.consentString; - gdprApplies = !!bidderRequest.gdprConsent.gdprApplies; - } - - // US privacy string - let usPrivacyString; - if (bidderRequest.uspConsent) { - usPrivacyString = bidderRequest.uspConsent; - } - - // Supply chain - let schain; - if (bidderRequest.schain) { - schain = bidderRequest.schain; - } - - // ID5 identifier - let id5id; - if (bidRequests[0].userId && bidRequests[0].userId.id5id && bidRequests[0].userId.id5id.uid) { - id5id = bidRequests[0].userId.id5id.uid; - } - - // Build the avocet ext object - const ext = { - currency, - tcfConsentString, - gdprApplies, - usPrivacyString, - schain, - publisherDomain, - fpd, - id5id, - }; - - // Extract properties from bidderRequest - const { - auctionId, - auctionStart, - bidderCode, - bidderRequestId, - refererInfo, - timeout, - } = bidderRequest; - - // Construct payload - const payload = JSON.stringify({ - auctionId, - auctionStart, - bidderCode, - bidderRequestId, - refererInfo, - timeout, - bids: bidRequests, - ext, - }); - - return { - method: 'POST', - url: getPrebidURL(), - data: payload, - }; - }, - interpretResponse: function (serverResponse, bidRequest) { - if ( - !serverResponse || - !serverResponse.body || - typeof serverResponse.body !== 'object' - ) { - return []; - } - if (Array.isArray(serverResponse.body)) { - return serverResponse.body; - } - if (Array.isArray(serverResponse.body.responses)) { - return serverResponse.body.responses; - } - return []; - }, -}; -registerBidder(spec); diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 5a351def958..feb6cae437a 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,5 +1,5 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; +import { getAdUnitSizes, parseSizesInput } from '../src/utils.js'; import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'between'; @@ -37,8 +37,6 @@ export const spec = { tz: getTz(), fl: getFl(), rr: getRr(), - shid: getSharedId(i)('id'), - shid3: getSharedId(i)('third'), s: i.params.s, bidid: i.bidId, transactionid: i.transactionId, @@ -149,15 +147,6 @@ export const spec = { } } -function getSharedId(bid) { - const id = deepAccess(bid, 'userId.sharedid.id'); - const third = deepAccess(bid, 'userId.sharedid.third'); - return function(kind) { - if (kind === 'id') return id || ''; - return third || ''; - } -} - function getRr() { try { var td = top.document; diff --git a/modules/bidfluenceBidAdapter.js b/modules/bidfluenceBidAdapter.js deleted file mode 100644 index f8a1f9ac92f..00000000000 --- a/modules/bidfluenceBidAdapter.js +++ /dev/null @@ -1,131 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); -const BIDDER_CODE = 'bidfluence'; - -function stdTimezoneOffset(t) { - const jan = new Date(t.getFullYear(), 0, 1); - const jul = new Date(t.getFullYear(), 6, 1); - return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); -} -function dst(t) { - return t.getTimezoneOffset() < stdTimezoneOffset(t); -} -function getBdfTz(d) { - let tz = d.getTimezoneOffset(); - if (dst(d)) { - tz += 60; - } - return tz.toString(); -} -function getUTCDate() { - var m = new Date(); - var dateString = m.getUTCFullYear() + '/' + - ('0' + (m.getUTCMonth() + 1)).slice(-2) + '/' + - ('0' + m.getUTCDate()).slice(-2) + ' ' + - ('0' + m.getUTCHours()).slice(-2) + ':' + - ('0' + m.getUTCMinutes()).slice(-2) + ':' + - ('0' + m.getUTCSeconds()).slice(-2); - - return dateString; -} - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function (bid) { - return !!bid.params.placementId || !!bid.params.publisherId; - }, - - buildRequests: function (validBidRequests, bidderRequest) { - const body = document.getElementsByTagName('body')[0]; - const refInfo = bidderRequest.refererInfo; - const gdpr = bidderRequest.gdprConsent; - const vpW = Math.max(window.innerWidth || body.clientWidth || 0) + 2; - const vpH = Math.max(window.innerHeight || body.clientHeight || 0) + 2; - const sr = screen.height > screen.width ? screen.height + 'x' + screen.width + 'x' + screen.colorDepth : screen.width + 'x' + screen.height + 'x' + screen.colorDepth; - - var payload = { - v: '2.0', - azr: true, - ck: storage.cookiesAreEnabled(), - re: refInfo ? refInfo.referer : '', - st: refInfo ? refInfo.stack : [], - tz: getBdfTz(new Date()), - sr: sr, - tm: bidderRequest.timeout, - vp: vpW + 'x' + vpH, - sdt: getUTCDate(), - top: refInfo ? refInfo.reachedTop : false, - gdpr: gdpr ? gdpr.gdprApplies : false, - gdprc: gdpr ? gdpr.consentString : '', - bids: [] - }; - - utils._each(validBidRequests, function (bidRequest) { - var params = bidRequest.params; - var sizes = utils.parseSizesInput(bidRequest.sizes)[0]; - var width = sizes.split('x')[0]; - var height = sizes.split('x')[1]; - - var currentBidPayload = { - bid: bidRequest.bidId, - tid: params.placementId, - pid: params.publisherId, - rp: params.reservePrice || 0, - w: width, - h: height - }; - - payload.bids.push(currentBidPayload); - }); - - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: `https://bdf${payload.bids[0].pid}.bidfluence.com/Prebid`, - data: payloadString, - options: { contentType: 'text/plain' } - }; - }, - - interpretResponse: function (serverResponse, bidRequest) { - const bidResponses = []; - const response = serverResponse.body; - - utils._each(response.Bids, function (currentResponse) { - var cpm = currentResponse.Cpm || 0; - - if (cpm > 0) { - const bidResponse = { - requestId: currentResponse.BidId, - cpm: cpm, - width: currentResponse.Width, - height: currentResponse.Height, - creativeId: currentResponse.CreativeId, - ad: currentResponse.Ad, - currency: 'USD', - netRevenue: true, - ttl: 360 - }; - bidResponses.push(bidResponse); - } - }); - - return bidResponses; - }, - - getUserSyncs: function (serverResponses) { - if (serverResponses.userSyncs) { - const syncs = serverResponses.UserSyncs.map((sync) => { - return { - type: sync.Type === 'ifr' ? 'iframe' : 'image', - url: sync.Url - }; - }); - return syncs; - } - } -}; -registerBidder(spec); diff --git a/modules/bidlabBidAdapter.js b/modules/bidlabBidAdapter.js deleted file mode 100644 index 8f501505a6d..00000000000 --- a/modules/bidlabBidAdapter.js +++ /dev/null @@ -1,112 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'bidlab'; -const AD_URL = 'https://service.bidlab.ai/?c=o&m=multi'; -const URL_SYNC = 'https://service.bidlab.ai/?c=o&m=sync'; -const NO_SYNC = true; - -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); - case NATIVE: - return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - noSync: NO_SYNC, - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - 'host': location.host, - 'page': location.pathname, - 'placements': placements - }; - request.language.indexOf('-') != -1 && (request.language = request.language.split('-')[0]) - 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++) { - let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER - - placements.push({ - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [], - traffic: traff - }); - if (bid.schain) { - placements.schain = bid.schain; - } - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses) => { - if (NO_SYNC) { - return false - } else { - return [{ - type: 'image', - url: URL_SYNC - }]; - } - } - -}; - -registerBidder(spec); diff --git a/modules/bidphysicsBidAdapter.js b/modules/bidphysicsBidAdapter.js deleted file mode 100644 index b6b5690ede5..00000000000 --- a/modules/bidphysicsBidAdapter.js +++ /dev/null @@ -1,134 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; - -const ENDPOINT_URL = 'https://exchange.bidphysics.com/auction'; - -const DEFAULT_BID_TTL = 30; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_NET_REVENUE = true; - -export const spec = { - code: 'bidphysics', - aliases: ['yieldlift'], - supportedMediaTypes: [BANNER], - - isBidRequestValid: function (bid) { - return (!!bid.params.unitId && typeof bid.params.unitId === 'string') || - (!!bid.params.networkId && typeof bid.params.networkId === 'string') || - (!!bid.params.publisherId && typeof bid.params.publisherId === 'string'); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - if (!validBidRequests || !bidderRequest) { - return; - } - const publisherId = validBidRequests[0].params.publisherId; - const networkId = validBidRequests[0].params.networkId; - const impressions = validBidRequests.map(bidRequest => ({ - id: bidRequest.bidId, - banner: { - format: bidRequest.sizes.map(sizeArr => ({ - w: sizeArr[0], - h: sizeArr[1] - })) - }, - ext: { - bidphysics: { - unitId: bidRequest.params.unitId - } - } - })); - - const openrtbRequest = { - id: bidderRequest.auctionId, - imp: impressions, - site: { - domain: window.location.hostname, - page: window.location.href, - ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null - }, - ext: { - bidphysics: { - publisherId: publisherId, - networkId: networkId, - } - } - }; - - // apply gdpr - if (bidderRequest.gdprConsent) { - openrtbRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0}}; - openrtbRequest.user = {ext: {consent: bidderRequest.gdprConsent.consentString}}; - } - - const payloadString = JSON.stringify(openrtbRequest); - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString, - }; - }, - - interpretResponse: function (serverResponse, request) { - const bidResponses = []; - const response = (serverResponse || {}).body; - // response is always one seat (bidphysics) with (optional) bids for each impression - if (response && response.seatbid && response.seatbid.length === 1 && response.seatbid[0].bid && response.seatbid[0].bid.length) { - response.seatbid[0].bid.forEach(bid => { - bidResponses.push({ - requestId: bid.impid, - cpm: bid.price, - width: bid.w, - height: bid.h, - ad: bid.adm, - ttl: DEFAULT_BID_TTL, - creativeId: bid.crid, - netRevenue: DEFAULT_NET_REVENUE, - currency: DEFAULT_CURRENCY, - }) - }) - } else { - utils.logInfo('bidphysics.interpretResponse :: no valid responses to interpret'); - } - return bidResponses; - }, - getUserSyncs: function (syncOptions, serverResponses) { - utils.logInfo('bidphysics.getUserSyncs', 'syncOptions', syncOptions, 'serverResponses', serverResponses); - let syncs = []; - - if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { - return syncs; - } - - serverResponses.forEach(resp => { - const userSync = utils.deepAccess(resp, 'body.ext.usersync'); - if (userSync) { - let syncDetails = []; - Object.keys(userSync).forEach(key => { - const value = userSync[key]; - if (value.syncs && value.syncs.length) { - syncDetails = syncDetails.concat(value.syncs); - } - }); - syncDetails.forEach(syncDetails => { - syncs.push({ - type: syncDetails.type === 'iframe' ? 'iframe' : 'image', - url: syncDetails.url - }); - }); - - if (!syncOptions.iframeEnabled) { - syncs = syncs.filter(s => s.type !== 'iframe') - } - if (!syncOptions.pixelEnabled) { - syncs = syncs.filter(s => s.type !== 'image') - } - } - }); - utils.logInfo('bidphysics.getUserSyncs result=%o', syncs); - return syncs; - }, - -}; -registerBidder(spec); diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js deleted file mode 100644 index 2af9a7afed2..00000000000 --- a/modules/bizzclickBidAdapter.js +++ /dev/null @@ -1,326 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; - -const BIDDER_CODE = 'bizzclick'; -const ACCOUNTID_MACROS = '[account_id]'; -const URL_ENDPOINT = `https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=${ACCOUNTID_MACROS}`; -const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; -const NATIVE_PARAMS = { - title: { - id: 0, - name: 'title' - }, - icon: { - id: 2, - type: 1, - name: 'img' - }, - image: { - id: 3, - type: 3, - name: 'img' - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - }, - body: { - id: 4, - name: 'data', - type: 2 - }, - cta: { - id: 1, - type: 12, - name: 'data' - } -}; -const NATIVE_VERSION = '1.2'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - /** - * 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: (bid) => { - return Boolean(bid.params.accountId) && Boolean(bid.params.placementId) - }, - - /** - * 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: (validBidRequests, bidderRequest) => { - if (validBidRequests && validBidRequests.length === 0) return [] - let accuontId = validBidRequests[0].params.accountId; - const endpointURL = URL_ENDPOINT.replace(ACCOUNTID_MACROS, accuontId); - - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - - let bids = []; - for (let bidRequest of validBidRequests) { - let impObject = prepareImpObject(bidRequest); - let data = { - id: bidRequest.bidId, - test: config.getConfig('debug') ? 1 : 0, - at: 1, - cur: ['USD'], - device: { - w: winTop.screen.width, - h: winTop.screen.height, - dnt: utils.getDNT() ? 1 : 0, - language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '', - }, - site: { - page: location.pathname, - host: location.host - }, - source: { - tid: bidRequest.transactionId - }, - regs: { - coppa: config.getConfig('coppa') === true ? 1 : 0, - ext: {} - }, - user: { - ext: {} - }, - tmax: bidRequest.timeout, - imp: [impObject], - }; - if (bidRequest) { - if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { - utils.deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); - utils.deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); - } - - if (bidRequest.uspConsent !== undefined) { - utils.deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); - } - } - bids.push(data) - } - return { - method: 'POST', - url: endpointURL, - data: bids - }; - }, - - /** - * 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: (serverResponse) => { - if (!serverResponse || !serverResponse.body) return [] - let bizzclickResponse = serverResponse.body; - - let bids = []; - for (let response of bizzclickResponse) { - let mediaType = response.seatbid[0].bid[0].ext && response.seatbid[0].bid[0].ext.mediaType ? response.seatbid[0].bid[0].ext.mediaType : BANNER; - - let bid = { - requestId: response.id, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ttl: response.ttl || 1200, - currency: response.cur || 'USD', - netRevenue: true, - creativeId: response.seatbid[0].bid[0].crid, - dealId: response.seatbid[0].bid[0].dealid, - mediaType: mediaType - }; - - switch (mediaType) { - case VIDEO: - bid.vastXml = response.seatbid[0].bid[0].adm - bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl - break - case NATIVE: - bid.native = parseNative(response.seatbid[0].bid[0].adm) - break - default: - bid.ad = response.seatbid[0].bid[0].adm - } - - bids.push(bid); - } - - return bids; - }, -}; - -/** - * Determine type of request - * - * @param bidRequest - * @param type - * @returns {boolean} - */ -const checkRequestType = (bidRequest, type) => { - return (typeof utils.deepAccess(bidRequest, `mediaTypes.${type}`) !== 'undefined'); -} - -const parseNative = admObject => { - const { assets, link, imptrackers, jstracker } = admObject.native; - const result = { - clickUrl: link.url, - clickTrackers: link.clicktrackers || undefined, - impressionTrackers: imptrackers || undefined, - javascriptTrackers: jstracker ? [ jstracker ] : undefined - }; - assets.forEach(asset => { - const kind = NATIVE_ASSET_IDS[asset.id]; - const content = kind && asset[NATIVE_PARAMS[kind].name]; - if (content) { - result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; - } - }); - - return result; -} - -const prepareImpObject = (bidRequest) => { - let impObject = { - id: bidRequest.transactionId, - secure: 1, - ext: { - placementId: bidRequest.params.placementId - } - }; - if (checkRequestType(bidRequest, BANNER)) { - impObject.banner = addBannerParameters(bidRequest); - } - if (checkRequestType(bidRequest, VIDEO)) { - impObject.video = addVideoParameters(bidRequest); - } - if (checkRequestType(bidRequest, NATIVE)) { - impObject.native = { - ver: NATIVE_VERSION, - request: addNativeParameters(bidRequest) - }; - } - return impObject -}; - -const addNativeParameters = bidRequest => { - let impObject = { - id: bidRequest.transactionId, - ver: NATIVE_VERSION, - }; - - const assets = utils._map(bidRequest.mediaTypes.native, (bidParams, key) => { - const props = NATIVE_PARAMS[key]; - const asset = { - required: bidParams.required & 1, - }; - if (props) { - asset.id = props.id; - let wmin, hmin; - let aRatios = bidParams.aspect_ratios; - - if (aRatios && aRatios[0]) { - aRatios = aRatios[0]; - wmin = aRatios.min_width || 0; - hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; - } - - if (bidParams.sizes) { - const sizes = flatten(bidParams.sizes); - wmin = sizes[0]; - hmin = sizes[1]; - } - - 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; - if (hmin) asset[props.name]['hmin'] = hmin; - - return asset; - } - }).filter(Boolean); - - impObject.assets = assets; - return impObject -} - -const addBannerParameters = (bidRequest) => { - let bannerObject = {}; - const size = parseSizes(bidRequest, 'banner'); - bannerObject.w = size[0]; - bannerObject.h = size[1]; - return bannerObject; -}; - -const parseSizes = (bid, mediaType) => { - let mediaTypes = bid.mediaTypes; - if (mediaType === 'video') { - let size = []; - if (mediaTypes.video && mediaTypes.video.w && mediaTypes.video.h) { - size = [ - mediaTypes.video.w, - mediaTypes.video.h - ]; - } else if (Array.isArray(utils.deepAccess(bid, 'mediaTypes.video.playerSize')) && bid.mediaTypes.video.playerSize.length === 1) { - size = bid.mediaTypes.video.playerSize[0]; - } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) { - size = bid.sizes[0]; - } - return size; - } - let sizes = []; - if (Array.isArray(mediaTypes.banner.sizes)) { - sizes = mediaTypes.banner.sizes[0]; - } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = bid.sizes - } else { - utils.logWarn('no sizes are setup or found'); - } - - return sizes -} - -const addVideoParameters = (bidRequest) => { - let videoObj = {}; - let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'placement', 'skip', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'] - - for (let param of supportParamsList) { - if (bidRequest.mediaTypes.video[param] !== undefined) { - videoObj[param] = bidRequest.mediaTypes.video[param]; - } - } - - const size = parseSizes(bidRequest, 'video'); - videoObj.w = size[0]; - videoObj.h = size[1]; - return videoObj; -} - -const flatten = arr => { - return [].concat(...arr); -} - -registerBidder(spec); diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js deleted file mode 100644 index 04f4085ba24..00000000000 --- a/modules/boldwinBidAdapter.js +++ /dev/null @@ -1,110 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'boldwin'; -const AD_URL = 'https://ssp.videowalldirect.com/?c=o&m=multi'; -const SYNC_URL = 'https://cs.videowalldirect.com/?c=o&m=cookie' - -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); - case NATIVE: - return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - '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++) { - let bid = validBidRequests[i]; - let sizes - if (bid.mediaTypes) { - if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - sizes = bid.mediaTypes[BANNER].sizes - } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { - sizes = bid.mediaTypes[VIDEO].playerSize - } - } - placements.push({ - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: sizes || [], - wPlayer: sizes ? sizes[0] : 0, - hPlayer: sizes ? sizes[1] : 0, - traffic: bid.params.traffic || BANNER, - schain: bid.schain || {} - }); - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: () => { - return [{ - type: 'image', - url: SYNC_URL - }]; - } -}; - -registerBidder(spec); diff --git a/modules/byplayBidAdapter.js b/modules/byplayBidAdapter.js deleted file mode 100644 index 6133cdfa647..00000000000 --- a/modules/byplayBidAdapter.js +++ /dev/null @@ -1,67 +0,0 @@ -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { Renderer } from '../src/Renderer.js'; -import { VIDEO } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'byplay'; -const ENDPOINT_URL = 'https://prebid.byplay.net/bidder'; -const VIDEO_PLAYER_URL = 'https://cdn.byplay.net/prebid-byplay-v2.js'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [VIDEO], - isBidRequestValid: (bid) => { - return !!bid.params.sectionId; - }, - buildRequests: function(validBidRequests) { - return validBidRequests.map(req => { - const payload = { - requestId: req.bidId, - sectionId: req.params.sectionId, - ...(req.params.env ? { env: req.params.env } : {}) - }; - - return { - method: 'POST', - url: ENDPOINT_URL, - data: JSON.stringify(payload), - options: { - withCredentials: false - } - }; - }); - }, - interpretResponse: (serverResponse, bidderRequest) => { - const response = serverResponse.body; - const data = JSON.parse(bidderRequest.data); - const bidResponse = { - requestId: data.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.creativeId || '0', - ttl: config.getConfig('_bidderTimeout'), - currency: 'JPY', - netRevenue: response.netRevenue, - mediaType: VIDEO, - vastXml: response.vastXml, - renderer: createRenderer() - }; - - return [bidResponse]; - } -}; - -function createRenderer() { - const renderer = Renderer.install({ url: VIDEO_PLAYER_URL }); - - renderer.setRender(bid => { - bid.renderer.push(() => { - window.adtagRender(bid); - }); - }); - - return renderer; -} - -registerBidder(spec); diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js deleted file mode 100644 index 8e1f1487ba7..00000000000 --- a/modules/c1xBidAdapter.js +++ /dev/null @@ -1,178 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import { userSync } from '../src/userSync.js'; - -const BIDDER_CODE = 'c1x'; -const URL = 'https://ht.c1exchange.com/ht'; -const PIXEL_ENDPOINT = 'https://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, - - // check the bids sent to c1x bidder - isBidRequestValid: function(bid) { - const siteId = bid.params.siteId || ''; - if (!siteId) { - utils.logError(LOG_MSG.noSite); - } - return !!(bid.adUnitCode && siteId); - }, - - buildRequests: function(bidRequests, bidderRequest) { - let payload = {}; - let tagObj = {}; - let pixelUrl = ''; - const adunits = bidRequests.length; - const rnd = new Date().getTime(); - const c1xTags = bidRequests.map(bidToTag); - const bidIdTags = bidRequests.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)); - const pixelId = tagObj.pixelId; - - 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' - ; - } - - if (pixelId) { - pixelUrl = PIXEL_ENDPOINT + pixelId; - if (payload.consent_required) { - pixelUrl += '&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? 1 : 0); - pixelUrl += '&consent=' + encodeURIComponent(bidderRequest.gdprConsent.consentString || ''); - } - userSync.registerSync('image', BIDDER_CODE, pixelUrl); - } - - Object.assign(payload, tagObj); - - let payloadString = stringifyPayload(payload); - // ServerRequest object - return { - method: 'GET', - url: URL, - data: payloadString, - bids: bidIdTags - }; - }, - - 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; - utils.logError(LOG_MSG.invalidBid + errorMessage); - return bidResponses; - } else { - serverResponse.forEach(bid => { - if (bid.bid) { - if (bid.bidType === 'NET_BID') { - netRevenue = !netRevenue; - } - const curBid = { - width: bid.width, - height: bid.height, - cpm: bid.cpm, - ad: bid.ad, - creativeId: bid.crid, - currency: currency, - ttl: 300, - netRevenue: netRevenue - }; - - for (let i = 0; i < requests.length; i++) { - if (bid.adId === requests[i].adUnitCode) { - curBid.requestId = requests[i].bidId; - } - } - utils.logInfo(LOG_MSG.bidWin + bid.adId + ' size: ' + curBid.width + 'x' + curBid.height); - bidResponses.push(curBid); - } else { - // no bid - utils.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'; - // TODO: Multiple Floor Prices - - const sizesArr = bid.sizes; - const floorPriceMap = bid.params.floorPriceMap || ''; - tag['site'] = bid.params.siteId || ''; - - // prevent pixelId becoming undefined when publishers don't fill this param in ad units they have on the same page - if (bid.params.pixelId) { - tag['pixelId'] = bid.params.pixelId - } - - 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 bidToShortTag(bid) { - const tag = {}; - tag.adUnitCode = bid.adUnitCode; - tag.bidId = bid.bidId; - - return tag; -} - -function stringifyPayload(payload) { - let payloadString = ''; - payloadString = JSON.stringify(payload).replace(/":"|","|{"|"}/g, (foundChar) => { - if (foundChar == '":"') return '='; - else if (foundChar == '","') return '&'; - else return ''; - }); - return payloadString; -} - -registerBidder(c1xAdapter); diff --git a/modules/cedatoBidAdapter.js b/modules/cedatoBidAdapter.js deleted file mode 100644 index ab381698f01..00000000000 --- a/modules/cedatoBidAdapter.js +++ /dev/null @@ -1,229 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); - -const BIDDER_CODE = 'cedato'; -const BID_URL = 'https://h.cedatoplayer.com/hb'; -const SYNC_URL = 'https://h.cedatoplayer.com/hb_usync'; -const TTL = 10000; -const CURRENCY = 'USD'; -const NET_REVENUE = true; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid: function(bid) { - return !!( - bid && - bid.params && - bid.params.player_id && - utils.checkCookieSupport() && - storage.cookiesAreEnabled() - ); - }, - - buildRequests: function(bidRequests, bidderRequest) { - const site = { domain: document.domain }; - const device = { ua: navigator.userAgent, w: screen.width, h: screen.height }; - const currency = CURRENCY; - const tmax = bidderRequest.timeout; - const auctionId = bidderRequest.auctionId; - const auctionStart = bidderRequest.auctionStart; - const bidderRequestId = bidderRequest.bidderRequestId; - - const imp = bidRequests.map(req => { - const banner = getMediaType(req, 'banner'); - const video = getMediaType(req, 'video'); - const params = req.params; - const bidId = req.bidId; - const adUnitCode = req.adUnitCode; - const bidRequestsCount = req.bidRequestsCount; - const bidderWinsCount = req.bidderWinsCount; - const transactionId = req.transactionId; - - return { - bidId, - banner, - video, - adUnitCode, - bidRequestsCount, - bidderWinsCount, - transactionId, - params - }; - }); - - const payload = { - version: '$prebid.version$', - site, - device, - imp, - currency, - tmax, - auctionId, - auctionStart, - bidderRequestId - }; - - if (bidderRequest) { - payload.referer_info = bidderRequest.refererInfo; - payload.us_privacy = bidderRequest.uspConsent; - - if (bidderRequest.gdprConsent) { - payload.gdpr_consent = { - consent_string: bidderRequest.gdprConsent.consentString, - consent_required: bidderRequest.gdprConsent.gdprApplies - }; - } - } - - return formatRequest(payload, bidderRequest); - }, - - interpretResponse: function(resp, {bidderRequest}) { - resp = resp.body; - const bids = []; - - if (!resp) { - return bids; - } - - resp.seatbid[0].bid.map(serverBid => { - const bid = newBid(serverBid, bidderRequest); - bid.currency = resp.cur; - bids.push(bid); - }); - - return bids; - }, - - getUserSyncs: function(syncOptions, resps, gdprConsent, uspConsent) { - const syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push(getSync('iframe', gdprConsent, uspConsent)); - } else if (syncOptions.pixelEnabled) { - syncs.push(getSync('image', gdprConsent, uspConsent)); - } - return syncs; - } -} - -function getMediaType(req, type) { - const { mediaTypes } = req; - - if (!mediaTypes) { - return; - } - - switch (type) { - case 'banner': - if (mediaTypes.banner) { - const { sizes } = mediaTypes.banner; - return { - format: getFormats(sizes) - }; - } - break; - - case 'video': - if (mediaTypes.video) { - const { playerSize, context } = mediaTypes.video; - return { - context: context, - format: getFormats(playerSize) - }; - } - } -} - -function newBid(serverBid, bidderRequest) { - const bidRequest = utils.getBidRequest(serverBid.uuid, [bidderRequest]); - - const cpm = serverBid.price; - const requestId = serverBid.uuid; - const width = serverBid.w; - const height = serverBid.h; - const creativeId = serverBid.crid; - const dealId = serverBid.dealid; - const mediaType = serverBid.media_type; - const netRevenue = NET_REVENUE; - const ttl = TTL; - - const bid = { - cpm, - requestId, - width, - height, - mediaType, - creativeId, - dealId, - netRevenue, - ttl, - }; - - if (mediaType == 'video') { - const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); - - if (videoContext == 'instream') { - bid.vastUrl = serverBid.vast_url; - bid.vastImpUrl = serverBid.notify_url; - } - } else { - bid.ad = serverBid.adm; - } - - return bid; -} - -function formatRequest(payload, bidderRequest) { - const payloadByUrl = {}; - const requests = []; - - payload.imp.forEach(imp => { - const url = imp.params.bid_url || BID_URL; - if (!payloadByUrl[url]) { - payloadByUrl[url] = { - ...payload, - imp: [] - }; - } - payloadByUrl[url].imp.push(imp); - }); - - for (const url in payloadByUrl) { - requests.push({ - url, - method: 'POST', - data: JSON.stringify(payloadByUrl[url]), - bidderRequest - }); - } - - return requests; -} - -const getSync = (type, gdprConsent, uspConsent = '') => { - const syncUrl = SYNC_URL; - let params = '?type=' + type + '&us_privacy=' + uspConsent; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `&gdpr_consent=${gdprConsent.consentString}`; - } - } - return { - type: type, - url: syncUrl + params, - }; -} - -const getFormats = arr => arr.map((s) => { - return { w: s[0], h: s[1] }; -}); - -registerBidder(spec); diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js index 80e4f13e7d4..7b36117d404 100644 --- a/modules/cleanmedianetBidAdapter.js +++ b/modules/cleanmedianetBidAdapter.js @@ -113,7 +113,7 @@ export const spec = { id: transactionId, instl: params.instl === 1 ? 1 : 0, tagid: adUnitCode, - bidfloor: params.bidfloor || 0, + bidfloor: 0, bidfloorcur: 'USD', secure: 1 }; diff --git a/modules/clickforceBidAdapter.js b/modules/clickforceBidAdapter.js deleted file mode 100644 index 20408fe9177..00000000000 --- a/modules/clickforceBidAdapter.js +++ /dev/null @@ -1,126 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -const BIDDER_CODE = 'clickforce'; -const ENDPOINT_URL = 'https://ad.holmesmind.com/adserver/prebid.json?cb=' + new Date().getTime() + '&hb=1&ver=1.21'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - /** - * 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 && bid.params && !!bid.params.zone; - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests - an array of bids - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests) { - const bidParams = []; - utils._each(validBidRequests, function(bid) { - bidParams.push({ - z: bid.params.zone, - bidId: bid.bidId - }); - }); - return { - method: 'POST', - url: ENDPOINT_URL, - data: bidParams, - validBidRequests: validBidRequests - }; - }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {*} bidRequest - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - const cfResponses = []; - const bidRequestList = []; - - if (typeof bidRequest != 'undefined') { - utils._each(bidRequest.validBidRequests, function(req) { - bidRequestList[req.bidId] = req; - }); - } - - utils._each(serverResponse.body, function(response) { - if (response.requestId != null) { - // native ad size - if (response.width == 3) { - cfResponses.push({ - requestId: response.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.creativeId, - currency: response.currency, - netRevenue: response.netRevenue, - ttl: response.ttl, - native: { - title: response.tag.content.title, - body: response.tag.content.content, - image: { - url: response.tag.content.image, - height: 900, - width: 1600 - }, - icon: { - url: response.tag.content.icon, - height: 900, - width: 900 - }, - clickUrl: response.tag.cu, - cta: response.tag.content.button_text, - sponsoredBy: response.tag.content.advertiser, - impressionTrackers: response.tag.iu, - }, - mediaType: 'native', - }); - } else { - // display ad - cfResponses.push({ - requestId: response.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.creativeId, - currency: response.currency, - netRevenue: response.netRevenue, - ttl: response.ttl, - ad: response.tag, - mediaType: 'banner', - }); - } - } - }); - return cfResponses; - }, - getUserSyncs: function(syncOptions, serverResponses) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://cdn.holmesmind.com/js/capmapping.htm' - }] - } else if (syncOptions.pixelEnabled) { - return [{ - type: 'image', - url: 'https://c.holmesmind.com/cm' - }] - } - } -}; - -registerBidder(spec); diff --git a/modules/clicktripzBidAdapter.js b/modules/clicktripzBidAdapter.js deleted file mode 100644 index 2149cbe4527..00000000000 --- a/modules/clicktripzBidAdapter.js +++ /dev/null @@ -1,67 +0,0 @@ -import {logError, _each} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'clicktripz'; -const ENDPOINT_URL = 'https://www.clicktripz.com/x/prebid/v1'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['ctz'], // short code - - isBidRequestValid: function (bid) { - if (bid && bid.params && bid.params.placementId && bid.params.siteId) { - return true; - } - - return false; - }, - - buildRequests: function (validBidRequests) { - let bidRequests = []; - - _each(validBidRequests, function (bid) { - bidRequests.push({ - bidId: bid.bidId, - placementId: bid.params.placementId, - siteId: bid.params.siteId, - sizes: bid.sizes.map(function (size) { - return size.join('x') - }) - }); - }); - return { - method: 'POST', - url: ENDPOINT_URL, - data: bidRequests - }; - }, - - interpretResponse: function (serverResponse) { - let bidResponses = []; - - if (serverResponse && serverResponse.body) { - _each(serverResponse.body, function (bid) { - if (bid.errors) { - logError(bid.errors); - return; - } - - const size = bid.size.split('x'); - bidResponses.push({ - requestId: bid.bidId, - cpm: bid.cpm, - width: size[0], - height: size[1], - creativeId: bid.creativeId, - currency: bid.currency, - netRevenue: bid.netRevenue, - ttl: bid.ttl, - adUrl: bid.adUrl - }); - }); - } - return bidResponses; - } -}; - -registerBidder(spec); diff --git a/modules/collectcentBidAdapter.js b/modules/collectcentBidAdapter.js deleted file mode 100644 index add3e06430d..00000000000 --- a/modules/collectcentBidAdapter.js +++ /dev/null @@ -1,93 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'collectcent'; -const URL_MULTI = 'https://publishers.motionspots.com/?c=o&m=multi'; -const URL_SYNC = 'https://publishers.motionspots.com/?c=o&m=cookie'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - /** - * 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: (bid) => { - return Boolean(bid.bidId && - bid.params && - !isNaN(bid.params.placementId) && - spec.supportedMediaTypes.indexOf(bid.params.traffic) !== -1 - ); - }, - - /** - * 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: (validBidRequests, bidderRequest) => { - let winTop; - try { - winTop = window.top; - } catch (e) { - utils.logMessage(e); - winTop = window; - }; - - const placements = []; - const location = bidderRequest ? new URL(bidderRequest.refererInfo.referer) : winTop.location; - const request = { - 'secure': (location.protocol === 'https:') ? 1 : 0, - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'host': location.host, - 'page': location.pathname, - 'placements': placements - }; - - for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i]; - const params = bid.params; - placements.push({ - placementId: params.placementId, - bidId: bid.bidId, - sizes: bid.sizes, - traffic: params.traffic - }); - } - return { - method: 'POST', - url: URL_MULTI, - data: request - }; - }, - - /** - * 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: (serverResponse) => { - try { - serverResponse = serverResponse.body; - } catch (e) { - utils.logMessage(e); - }; - return serverResponse; - }, - - getUserSyncs: () => { - return [{ - type: 'image', - url: URL_SYNC - }]; - } -}; - -registerBidder(spec); diff --git a/modules/colombiaBidAdapter.js b/modules/colombiaBidAdapter.js deleted file mode 100644 index 55109dbaab2..00000000000 --- a/modules/colombiaBidAdapter.js +++ /dev/null @@ -1,82 +0,0 @@ -import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -const BIDDER_CODE = 'colombia'; -const ENDPOINT_URL = 'https://ade.clmbtech.com/cde/prebid.htm'; -const HOST_NAME = document.location.protocol + '//' + window.location.host; - -export const spec = { - code: BIDDER_CODE, - aliases: ['clmb'], - supportedMediaTypes: [BANNER], - isBidRequestValid: function(bid) { - return !!(bid.params.placementId); - }, - buildRequests: function(validBidRequests, bidderRequest) { - return validBidRequests.map(bidRequest => { - const params = bidRequest.params; - const sizes = utils.parseSizesInput(bidRequest.sizes)[0]; - const width = sizes.split('x')[0]; - const height = sizes.split('x')[1]; - const placementId = params.placementId; - const cb = Math.floor(Math.random() * 99999999999); - const bidId = bidRequest.bidId; - const referrer = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.referer : ''; - const payload = { - v: 'hb1', - p: placementId, - w: width, - h: height, - cb: cb, - r: referrer, - uid: bidId, - t: 'i', - d: HOST_NAME, - }; - return { - method: 'POST', - url: ENDPOINT_URL, - data: payload, - } - }); - }, - interpretResponse: function(serverResponse, bidRequest) { - const bidResponses = []; - const response = serverResponse.body; - const crid = response.creativeId || 0; - const width = response.width || 0; - const height = response.height || 0; - let cpm = response.cpm || 0; - if (width == 300 && height == 250) { - cpm = cpm * 0.2; - } - if (width == 320 && height == 50) { - cpm = cpm * 0.55; - } - if (cpm <= 0) { - return bidResponses; - } - if (width !== 0 && height !== 0 && cpm !== 0 && crid !== 0) { - const dealId = response.dealid || ''; - const currency = response.currency || 'USD'; - const netRevenue = (response.netRevenue === undefined) ? true : response.netRevenue; - const bidResponse = { - requestId: bidRequest.data.uid, - cpm: cpm, - width: response.width, - height: response.height, - creativeId: crid, - dealId: dealId, - currency: currency, - netRevenue: netRevenue, - ttl: config.getConfig('_bidderTimeout'), - referrer: bidRequest.data.r, - ad: response.ad - }; - bidResponses.push(bidResponse); - } - return bidResponses; - } -} -registerBidder(spec); diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js deleted file mode 100644 index 1afb343566d..00000000000 --- a/modules/colossussspBidAdapter.js +++ /dev/null @@ -1,156 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'colossusssp'; -const G_URL = 'https://colossusssp.com/?c=o&m=multi'; -const G_URL_SYNC = 'https://colossusssp.com/?c=o&m=cookie'; - -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); - case NATIVE: - return Boolean(bid.native); - default: - return false; - } -} - -function getUserId(eids, id, source, uidExt) { - if (id) { - var uid = { id }; - if (uidExt) { - uid.ext = uidExt; - } - eids.push({ - source, - uids: [ uid ] - }); - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - /** - * 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: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placement_id)); - }, - - /** - * 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: (validBidRequests, bidderRequest) => { - const winTop = utils.getWindowTop(); - const location = winTop.location; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language : '', - 'secure': location.protocol === 'https:' ? 1 : 0, - 'host': location.host, - 'page': location.pathname, - 'placements': placements, - }; - - if (bidderRequest) { - if (bidderRequest.uspConsent) { - request.ccpa = bidderRequest.uspConsent; - } - if (bidderRequest.gdprConsent) { - request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' - request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 - } - } - - for (let i = 0; i < validBidRequests.length; i++) { - let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER - let placement = { - placementId: bid.params.placement_id, - bidId: bid.bidId, - sizes: bid.mediaTypes[traff].sizes, - traffic: traff, - eids: [], - floor: {} - }; - if (typeof bid.getFloor === 'function') { - let tmpFloor = {}; - for (let size of placement.sizes) { - tmpFloor = bid.getFloor({ - currency: 'USD', - mediaType: traff, - size: size - }); - if (tmpFloor) { - placement.floor[`${size[0]}x${size[1]}`] = tmpFloor.floor; - } - } - } - if (bid.schain) { - placement.schain = bid.schain; - } - if (bid.userId) { - getUserId(placement.eids, bid.userId.britepoolid, 'britepool.com'); - getUserId(placement.eids, bid.userId.idl_env, 'identityLink'); - getUserId(placement.eids, bid.userId.id5id, 'id5-sync.com') - getUserId(placement.eids, bid.userId.tdid, 'adserver.org', { - rtiPartner: 'TDID' - }); - } - placements.push(placement); - } - return { - method: 'POST', - url: G_URL, - data: request - }; - }, - - /** - * 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: (serverResponse) => { - let response = []; - try { - serverResponse = serverResponse.body; - for (let i = 0; i < serverResponse.length; i++) { - let resItem = serverResponse[i]; - if (isBidResponseValid(resItem)) { - response.push(resItem); - } - } - } catch (e) { - utils.logMessage(e); - }; - return response; - }, - - getUserSyncs: () => { - return [{ - type: 'image', - url: G_URL_SYNC - }]; - } -}; - -registerBidder(spec); diff --git a/modules/convergeBidAdapter.js b/modules/convergeBidAdapter.js deleted file mode 100644 index bea3b6cb1ab..00000000000 --- a/modules/convergeBidAdapter.js +++ /dev/null @@ -1,313 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { Renderer } from '../src/Renderer.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'converge'; -const ENDPOINT_URL = 'https://tech.convergd.com/hb'; -const TIME_TO_LIVE = 360; -const SYNC_URL = 'https://tech.convergd.com/push_sync'; -const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -let hasSynced = false; - -const LOG_ERROR_MESS = { - noAuid: 'Bid from response has no auid parameter - ', - noAdm: 'Bid from response has no adm parameter - ', - noBid: 'Array of bid objects is empty', - noPlacementCode: "Can't find in requested bids the bid with auid - ", - emptyUids: 'Uids should be not empty', - emptySeatbid: 'Seatbid array from response has empty item', - emptyResponse: 'Response is empty', - hasEmptySeatbidArray: 'Response has empty seatbid array', - hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' -}; -export const spec = { - code: BIDDER_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.uid; - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests - an array of bids - * @param {bidderRequest} bidderRequest - bidder request object - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; - const bids = validBidRequests || []; - let priceType = 'net'; - let pageKeywords; - let reqId; - - bids.forEach(bid => { - if (bid.params.priceType === 'gross') { - priceType = 'gross'; - } - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - const keywords = utils.transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - pageKeywords = keywords; - } - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); - } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); - }); - - const payload = { - pt: priceType, - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); - } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } - } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap, - }; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {*} bidRequest - * @param {Renderer} RendererConst - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest, RendererConst = Renderer) { - serverResponse = serverResponse && serverResponse.body; - const bidResponses = []; - const bidsMap = bidRequest.bidsMap; - const priceType = bidRequest.data.pt; - - let errorMessage; - - if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (serverResponse.seatbid && !serverResponse.seatbid.length) { - errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - } - - if (!errorMessage && serverResponse.seatbid) { - serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses, RendererConst); - }); - } - if (errorMessage) utils.logError(errorMessage); - return bidResponses; - }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - if (!hasSynced && syncOptions.pixelEnabled) { - let params = ''; - - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent) { - params += `&us_privacy=${uspConsent}`; - } - - hasSynced = true; - return { - type: 'image', - url: SYNC_URL + params - }; - } - } -}; - -function isPopulatedArray(arr) { - return !!(utils.isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - -function _getBidFromResponse(respItem) { - if (!respItem) { - utils.logError(LOG_ERROR_MESS.emptySeatbid); - } else if (!respItem.bid) { - utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(respItem)); - } else if (!respItem.bid[0]) { - utils.logError(LOG_ERROR_MESS.noBid); - } - return respItem && respItem.bid && respItem.bid[0]; -} - -function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, RendererConst) { - if (!serverBid) return; - let errorMessage; - if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); - if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); - else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'EUR', - netRevenue: priceType !== 'gross', - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid - }; - if (serverBid.content_type === 'video' || (!serverBid.content_type && bid.mediaTypes && bid.mediaTypes.video)) { - bidResponse.vastXml = serverBid.adm; - bidResponse.mediaType = VIDEO; - bidResponse.adResponse = { - content: bidResponse.vastXml - }; - if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { - bidResponse.renderer = createRenderer(bidResponse, { - id: bid.bidId, - url: RENDERER_URL - }, RendererConst); - } - } else { - bidResponse.ad = serverBid.adm; - bidResponse.mediaType = BANNER; - } - - bidResponses.push(bidResponse); - - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } - }); - } - } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; - } - } - if (errorMessage) { - utils.logError(errorMessage); - } -} - -function outstreamRender (bid) { - bid.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - targetId: bid.adUnitCode, - adResponse: bid.adResponse - }); - }); -} - -function createRenderer (bid, rendererParams, RendererConst) { - const rendererInst = RendererConst.install({ - id: rendererParams.id, - url: rendererParams.url, - loaded: false - }); - - try { - rendererInst.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - - return rendererInst; -} - -export function resetUserSync() { - hasSynced = false; -} - -export function getSyncUrl() { - return SYNC_URL; -} - -registerBidder(spec); diff --git a/modules/cosmosBidAdapter.js b/modules/cosmosBidAdapter.js deleted file mode 100644 index 73ee5c223b3..00000000000 --- a/modules/cosmosBidAdapter.js +++ /dev/null @@ -1,392 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'cosmos'; -const BID_ENDPOINT = 'https://bid.cosmoshq.com/openrtb2/bids'; -const USER_SYNC_ENDPOINT = 'https://sync.cosmoshq.com/js/v1/usersync.html'; -const HTTP_POST = 'POST'; -const LOG_PREFIX = 'COSMOS: '; -const DEFAULT_CURRENCY = 'USD'; -const HTTPS = 'https:'; -const MEDIA_TYPES = 'mediaTypes'; -const MIMES = 'mimes'; -const DEFAULT_NET_REVENUE = false; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - /** - * generate UUID - **/ - _createUUID: function () { - return ('' + new Date().getTime()); - }, - - /** - * copy object if not null - **/ - _copyObject: function (src, dst) { - if (src) { - // copy complete object - Object.keys(src).forEach(param => dst[param] = src[param]); - } - }, - - /** - * parse object - **/ - _parse: function (rawPayload) { - try { - if (rawPayload) { - return JSON.parse(rawPayload); - } - } catch (ex) { - utils.logError(LOG_PREFIX, 'Exception: ', ex); - } - return null; - }, - - /** - * 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 || !bid.params) { - utils.logError(LOG_PREFIX, 'nil/empty bid object'); - return false; - } - - if (!utils.isEmpty(bid.params.publisherId) || - !utils.isNumber(bid.params.publisherId)) { - utils.logError(LOG_PREFIX, 'publisherId is mandatory and must be numeric. Ad Unit: ', JSON.stringify(bid)); - return false; - } - // video bid request validation - if (bid.hasOwnProperty(MEDIA_TYPES) && bid.mediaTypes.hasOwnProperty(VIDEO)) { - if (!bid.mediaTypes.video.hasOwnProperty(MIMES) || - !utils.isArray(bid.mediaTypes.video.mimes) || - bid.mediaTypes.video.mimes.length === 0) { - utils.logError(LOG_PREFIX, 'mimes are mandatory for video bid request. Ad Unit: ', JSON.stringify(bid)); - return false; - } - } - - return true; - }, - - /** - * 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) { - if (validBidRequests.length === 0) { - return []; - } - - var refererInfo; - if (bidderRequest && bidderRequest.refererInfo) { - refererInfo = bidderRequest.refererInfo; - } - - let clonedBidRequests = utils.deepClone(validBidRequests); - return clonedBidRequests.map(bidRequest => { - const oRequest = spec._createRequest(bidRequest, refererInfo); - if (oRequest) { - spec._setGDPRParams(bidderRequest, oRequest); - return { - method: HTTP_POST, - url: BID_ENDPOINT, - data: JSON.stringify(oRequest) - }; - } - }); - }, - - /** - * 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, request) { - let response = serverResponse.body; - var bidResponses = []; - try { - if (response.seatbid) { - var currency = response.cur ? response.cur : DEFAULT_CURRENCY; - response.seatbid.forEach(seatbid => { - var bids = seatbid.bid ? seatbid.bid : []; - bids.forEach(bid => { - var bidResponse = { - requestId: bid.impid, - cpm: (parseFloat(bid.price) || 0).toFixed(2), - width: bid.w, - height: bid.h, - creativeId: bid.crid, - currency: currency, - netRevenue: DEFAULT_NET_REVENUE, - ttl: 300 - }; - if (bid.dealid) { - bidResponse.dealId = bid.dealid; - } - - var req = spec._parse(request.data); - if (req.imp && req.imp.length > 0) { - req.imp.forEach(impr => { - if (impr.id === bid.impid) { - if (impr.banner) { - bidResponse.ad = bid.adm; - bidResponse.mediaType = BANNER; - } else { - bidResponse.width = bid.hasOwnProperty('w') ? bid.w : impr.video.w; - bidResponse.height = bid.hasOwnProperty('h') ? bid.h : impr.video.h; - bidResponse.vastXml = bid.adm; - bidResponse.mediaType = VIDEO; - } - } - }); - } - bidResponses.push(bidResponse); - }); - }); - } - } catch (ex) { - utils.logError(LOG_PREFIX, 'Exception: ', ex); - } - 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) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: USER_SYNC_ENDPOINT - }]; - } else { - utils.logWarn(LOG_PREFIX + 'Please enable iframe based user sync.'); - } - }, - - /** - * create IAB standard OpenRTB bid request - **/ - _createRequest: function (bidRequests, refererInfo) { - var oRequest = {}; - try { - oRequest = { - id: spec._createUUID(), - imp: spec._createImpressions(bidRequests), - user: {}, - ext: {} - }; - var site = spec._createSite(bidRequests, refererInfo); - var app = spec._createApp(bidRequests); - var device = spec._createDevice(bidRequests); - if (app) { - oRequest.app = app; - } - if (site) { - oRequest.site = site; - } - if (device) { - oRequest.device = device; - } - } catch (ex) { - utils.logError(LOG_PREFIX, 'Exception: ', ex); - oRequest = null; - } - return oRequest; - }, - - /** - * create impression array objects - **/ - _createImpressions: function (request) { - var impressions = []; - var impression = spec._creatImpression(request); - if (impression) { - impressions.push(impression); - } - return impressions; - }, - - /** - * create impression (single) object - **/ - _creatImpression: function (request) { - if (!request.hasOwnProperty(MEDIA_TYPES)) { - return undefined; - } - - var params = request && request.params ? request.params : null; - var impression = { - id: request.bidId ? request.bidId : spec._createUUID(), - secure: window.location.protocol === HTTPS ? 1 : 0, - bidfloorcur: request.params.currency ? request.params.currency : DEFAULT_CURRENCY - }; - if (params.bidFloor) { - impression.bidfloor = params.bidFloor; - } - - if (params.tagId) { - impression.tagid = params.tagId.toString(); - } - - var banner; - var video; - var mediaType; - for (mediaType in request.mediaTypes) { - switch (mediaType) { - case BANNER: - banner = spec._createBanner(request); - if (banner) { - impression.banner = banner; - } - break; - case VIDEO: - video = spec._createVideo(request); - if (video) { - impression.video = video; - } - break; - } - } - - return impression.hasOwnProperty(BANNER) || - impression.hasOwnProperty(VIDEO) ? impression : undefined; - }, - - /** - * create the banner object - **/ - _createBanner: function (request) { - if (utils.deepAccess(request, 'mediaTypes.banner')) { - var banner = {}; - var sizes = request.mediaTypes.banner.sizes; - if (sizes && utils.isArray(sizes) && sizes.length > 0) { - var format = []; - banner.w = sizes[0][0]; - banner.h = sizes[0][1]; - sizes.forEach(size => { - format.push({ - w: size[0], - h: size[1] - }); - }); - banner.format = format; - } - - spec._copyObject(request.mediaTypes.banner, banner); - spec._copyObject(request.params.banner, banner); - return banner; - } - return undefined; - }, - - /** - * create video object - **/ - _createVideo: function (request) { - if (utils.deepAccess(request, 'mediaTypes.video')) { - var video = {}; - var sizes = request.mediaTypes.video.playerSize; - if (sizes && utils.isArray(sizes) && sizes.length > 1) { - video.w = sizes[0]; - video.h = sizes[1]; - } - spec._copyObject(request.mediaTypes.video, video); - spec._copyObject(request.params.video, video); - return video; - } - return undefined; - }, - - /** - * create site object - **/ - _createSite: function (request, refererInfo) { - var rSite = request.params.site; - if (rSite || !request.params.app) { - var site = {}; - spec._copyObject(rSite, site); - - if (refererInfo) { - if (refererInfo.referer) { - site.ref = encodeURIComponent(refererInfo.referer); - } - if (utils.isArray(refererInfo.stack) && refererInfo.stack.length > 0) { - site.page = encodeURIComponent(refererInfo.stack[0]); - let anchrTag = document.createElement('a'); - anchrTag.href = site.page; - site.domain = anchrTag.hostname; - } - } - - // override publisher object - site.publisher = { - id: request.params.publisherId.toString() - }; - return site; - } - return undefined; - }, - - /** - * create app object - **/ - _createApp: function (request) { - var rApp = request.params.app; - if (rApp) { - var app = {}; - spec._copyObject(rApp, app); - // override publisher object - app.publisher = { - id: request.params.publisherId.toString() - }; - return app; - } - return undefined; - }, - - /** - * create device obejct - **/ - _createDevice: function (request) { - var device = {}; - var rDevice = request.params.device; - spec._copyObject(rDevice, device); - device.dnt = utils.getDNT() ? 1 : 0; - device.ua = navigator.userAgent; - device.language = (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage); - device.w = (window.screen.width || window.innerWidth); - device.h = (window.screen.height || window.innerHeigh); - return device; - }, - - /** - * set GDPR parameters - **/ - _setGDPRParams: function (bidderRequest, oRequest) { - if (!bidderRequest || !bidderRequest.gdprConsent) { - return; - } - - oRequest.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0 } }; - oRequest.user = { ext: { consent: bidderRequest.gdprConsent.consentString } }; - }, - -} -registerBidder(spec); diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js deleted file mode 100644 index 61ccc0e786b..00000000000 --- a/modules/cpmstarBidAdapter.js +++ /dev/null @@ -1,180 +0,0 @@ - -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'; - -const BIDDER_CODE = 'cpmstar'; - -const ENDPOINT_DEV = 'https://dev.server.cpmstar.com/view.aspx'; -const ENDPOINT_STAGING = 'https://staging.server.cpmstar.com/view.aspx'; -const ENDPOINT_PRODUCTION = 'https://server.cpmstar.com/view.aspx'; - -const DEFAULT_TTL = 300; -const DEFAULT_CURRENCY = 'USD'; - -function fixedEncodeURIComponent(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { - return '%' + c.charCodeAt(0).toString(16); - }); -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - pageID: Math.floor(Math.random() * 10e6), - - getMediaType: function (bidRequest) { - if (bidRequest == null) return BANNER; - return !utils.deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; - }, - - getPlayerSize: function (bidRequest) { - 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]; - return playerSize; - }, - - isBidRequestValid: function (bid) { - return ((typeof bid.params.placementId === 'string') && !!bid.params.placementId.length) || (typeof bid.params.placementId === 'number'); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - var requests = []; - // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. - - for (var i = 0; i < validBidRequests.length; i++) { - var bidRequest = validBidRequests[i]; - 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=' + utils.getBidIdParameter('placementId', bidRequest.params) + - '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + - '&requestid=' + bidRequest.bidId + - '&referer=' + encodeURIComponent(referer); - - if (bidRequest.schain && bidRequest.schain.nodes) { - var schain = bidRequest.schain; - var schainString = ''; - schainString += schain.ver + ',' + schain.complete; - for (var i2 = 0; i2 < schain.nodes.length; i2++) { - var node = schain.nodes[i2]; - schainString += '!' + - fixedEncodeURIComponent(node.asi || '') + ',' + - fixedEncodeURIComponent(node.sid || '') + ',' + - fixedEncodeURIComponent(node.hp || '') + ',' + - fixedEncodeURIComponent(node.rid || '') + ',' + - fixedEncodeURIComponent(node.name || '') + ',' + - fixedEncodeURIComponent(node.domain || ''); - } - url += '&schain=' + schainString - } - - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString != null) { - url += '&gdpr_consent=' + bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.gdprApplies != null) { - url += '&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? 1 : 0); - } - } - - if (bidderRequest.uspConsent != null) { - url += '&us_privacy=' + bidderRequest.uspConsent; - } - - if (config.getConfig('coppa')) { - url += '&tfcd=' + (config.getConfig('coppa') ? 1 : 0); - } - - requests.push({ - method: 'GET', - url: url, - bidRequest: bidRequest, - }); - } - - return requests; - }, - - interpretResponse: function (serverResponse, request) { - var bidRequest = request.bidRequest; - var mediaType = spec.getMediaType(bidRequest); - - var bidResponses = []; - - if (!Array.isArray(serverResponse.body)) { - serverResponse.body = [serverResponse.body]; - } - - for (var i = 0; i < serverResponse.body.length; i++) { - var raw = serverResponse.body[i]; - var rawBid = raw.creatives[0]; - if (!rawBid) { - utils.logWarn('cpmstarBidAdapter: server response failed check'); - return; - } - var cpm = (parseFloat(rawBid.cpm) || 0); - - if (!cpm) { - utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') - return; - } - - var bidResponse = { - requestId: rawBid.requestid, - cpm: cpm, - width: rawBid.width || 0, - height: rawBid.height || 0, - currency: rawBid.currency ? rawBid.currency : DEFAULT_CURRENCY, - netRevenue: rawBid.netRevenue ? rawBid.netRevenue : true, - ttl: rawBid.ttl ? rawBid.ttl : DEFAULT_TTL, - creativeId: rawBid.creativeid || 0, - }; - - if (rawBid.hasOwnProperty('dealId')) { - bidResponse.dealId = rawBid.dealId - } - - if (mediaType == BANNER && rawBid.code) { - bidResponse.ad = rawBid.code + (rawBid.px_cr ? "\n" : ''); - } else if (mediaType == VIDEO && rawBid.creativemacros && rawBid.creativemacros.HTML5VID_VASTSTRING) { - var playerSize = spec.getPlayerSize(bidRequest); - if (playerSize != null) { - bidResponse.width = playerSize[0]; - bidResponse.height = playerSize[1]; - } - bidResponse.mediaType = VIDEO; - bidResponse.vastXml = rawBid.creativemacros.HTML5VID_VASTSTRING; - } else { - return utils.logError('bad response', rawBid); - } - - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - - getUserSyncs: function (syncOptions, serverResponses) { - const syncs = []; - if (serverResponses.length == 0 || !serverResponses[0].body) return syncs; - var usersyncs = serverResponses[0].body[0].syncs; - if (!usersyncs || usersyncs.length < 0) return syncs; - for (var i = 0; i < usersyncs.length; i++) { - var us = usersyncs[i]; - if ((us.type === 'image' && syncOptions.pixelEnabled) || (us.type == 'iframe' && syncOptions.iframeEnabled)) { - syncs.push(us); - } - } - return syncs; - } - -}; -registerBidder(spec); diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 41cbb0670c8..b2866b8a751 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -4,11 +4,11 @@ import {config} from '../src/config.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; import find from 'core-js-pure/features/array/find.js'; -import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; +import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; // ref#2 import { getStorageManager } from '../src/storageManager.js'; const GVLID = 91; -export const ADAPTER_VERSION = 33; +export const ADAPTER_VERSION = 34; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; const PROFILE_ID_INLINE = 207; @@ -16,9 +16,18 @@ export const PROFILE_ID_PUBLISHERTAG = 185; const storage = getStorageManager(GVLID); const LOG_PREFIX = 'Criteo: '; -// Unminified source code can be found in: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js -const PUBLISHER_TAG_URL = 'https://static.criteo.net/js/ld/publishertag.prebid.js'; - +/* + If you don't want to use the FastBid adapter feature, you can lighten criteoBidAdapter size by : + 1. commenting the tryGetCriteoFastBid function inner content (see ref#1) + 2. removing the line 'verify' function import line (see ref#2) + + Unminified source code can be found in the privately shared repo: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js +*/ +const FAST_BID_VERSION_PLACEHOLDER = '%FAST_BID_VERSION%'; +export const FAST_BID_VERSION_CURRENT = 105; +const FAST_BID_VERSION_LATEST = 'latest'; +const FAST_BID_VERSION_NONE = 'none'; +const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js'; const FAST_BID_PUBKEY_E = 65537; const FAST_BID_PUBKEY_N = 'ztQYwCE5BU7T9CDM5he6rKoabstXRmkzx54zFPZkWbK530dwtLBDeaWBMxHBUT55CYyboR/EZ4efghPi3CoNGfGWezpjko9P6p2EwGArtHEeS4slhu/SpSIFMjG6fdrpRoNuIAMhq1Z+Pr/+HOd1pThFKeGFr2/NhtAg+TXAzaU='; @@ -65,15 +74,18 @@ export const spec = { }); // If publisher tag not already loaded try to get it from fast bid - if (!publisherTagAvailable()) { + const fastBidVersion = config.getConfig('criteo.fastBidVersion'); + const canLoadPublisherTag = canFastBid(fastBidVersion); + if (!publisherTagAvailable() && canLoadPublisherTag) { window.Criteo = window.Criteo || {}; window.Criteo.usePrebidEvents = false; tryGetCriteoFastBid(); + const fastBidUrl = getFastBidUrl(fastBidVersion); // Reload the PublisherTag after the timeout to ensure FastBid is up-to-date and tracking done properly setTimeout(() => { - loadExternalScript(PUBLISHER_TAG_URL, BIDDER_CODE); + loadExternalScript(fastBidUrl, BIDDER_CODE); }, bidderRequest.timeout); } @@ -127,6 +139,9 @@ export const spec = { height: slot.height, dealId: slot.dealCode, }; + if (slot.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: slot.adomain }); + } if (slot.native) { if (bidRequest.params.nativeCallback) { bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback); @@ -305,15 +320,14 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { mimes: bidRequest.mediaTypes.video.mimes, protocols: bidRequest.mediaTypes.video.protocols, maxduration: bidRequest.mediaTypes.video.maxduration, - api: bidRequest.mediaTypes.video.api + api: bidRequest.mediaTypes.video.api, + skip: bidRequest.mediaTypes.video.skip || bidRequest.params.video.skip, + placement: bidRequest.mediaTypes.video.placement || bidRequest.params.video.placement, + minduration: bidRequest.mediaTypes.video.minduration || bidRequest.params.video.minduration, + playbackmethod: bidRequest.mediaTypes.video.playbackmethod || bidRequest.params.video.playbackmethod, + startdelay: bidRequest.mediaTypes.video.startdelay || bidRequest.params.video.startdelay }; - video.skip = bidRequest.params.video.skip; - video.placement = bidRequest.params.video.placement; - video.minduration = bidRequest.params.video.minduration; - video.playbackmethod = bidRequest.params.video.playbackmethod; - video.startdelay = bidRequest.params.video.startdelay; - slot.video = video; } return slot; @@ -367,38 +381,27 @@ function parseNativeSize(size) { } function hasVideoMediaType(bidRequest) { - if (utils.deepAccess(bidRequest, 'params.video') === undefined) { - return false; - } return utils.deepAccess(bidRequest, 'mediaTypes.video') !== undefined; } function hasValidVideoMediaType(bidRequest) { let isValid = true; - var requiredMediaTypesParams = ['mimes', 'playerSize', 'maxduration', 'protocols', 'api']; + var requiredMediaTypesParams = ['mimes', 'playerSize', 'maxduration', 'protocols', 'api', 'skip', 'placement', 'playbackmethod']; requiredMediaTypesParams.forEach(function(param) { - if (utils.deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined) { + if (utils.deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined && utils.deepAccess(bidRequest, 'params.video.' + param) === undefined) { isValid = false; utils.logError('Criteo Bid Adapter: mediaTypes.video.' + param + ' is required'); } }); - var requiredParams = ['skip', 'placement', 'playbackmethod']; - - requiredParams.forEach(function(param) { - if (utils.deepAccess(bidRequest, 'params.video.' + param) === undefined) { - isValid = false; - utils.logError('Criteo Bid Adapter: params.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' && bidRequest.params.video.placement === 1) { + if (bidRequest.mediaTypes.video.context == 'instream' && videoPlacement === 1) { return true; - } else if (bidRequest.mediaTypes.video.context == 'outstream' && bidRequest.params.video.placement !== 1) { + } else if (bidRequest.mediaTypes.video.context == 'outstream' && videoPlacement !== 1) { return true; } } @@ -454,7 +457,29 @@ for (var i = 0; i < 10; ++i) { `; } +export function canFastBid(fastBidVersion) { + return fastBidVersion !== FAST_BID_VERSION_NONE; +} + +export function getFastBidUrl(fastBidVersion) { + let version; + if (fastBidVersion === FAST_BID_VERSION_LATEST) { + version = ''; + } else if (fastBidVersion) { + let majorVersion = String(fastBidVersion).split('.')[0]; + if (majorVersion < 102) { + utils.logWarn('Specifying a Fastbid version which is not supporting version selection.') + } + version = '.' + fastBidVersion; + } else { + version = '.' + FAST_BID_VERSION_CURRENT; + } + + return PUBLISHER_TAG_URL_TEMPLATE.replace(FAST_BID_VERSION_PLACEHOLDER, version); +} + export function tryGetCriteoFastBid() { + // begin ref#1 try { const fastBidStorageKey = 'criteo_fast_bid'; const hashPrefix = '// Hash: '; @@ -485,6 +510,7 @@ export function tryGetCriteoFastBid() { } catch (e) { // Unable to get fast bid } + // end ref#1 } registerBidder(spec); diff --git a/modules/criteoBidAdapter.md b/modules/criteoBidAdapter.md index 68599f05434..6a165978f3b 100644 --- a/modules/criteoBidAdapter.md +++ b/modules/criteoBidAdapter.md @@ -31,7 +31,8 @@ Set the "ceh" property to provides the user's hashed email if available ``` pbjs.setConfig({ criteo: { - ceh: 'hashed mail' + ceh: 'hashed mail', + fastBidVersion: "none"|"latest"| } }); -``` \ No newline at end of file +``` diff --git a/modules/dailyhuntBidAdapter.js b/modules/dailyhuntBidAdapter.js deleted file mode 100644 index 1018417300a..00000000000 --- a/modules/dailyhuntBidAdapter.js +++ /dev/null @@ -1,395 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import * as mediaTypes from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import find from 'core-js-pure/features/array/find.js'; -import { OUTSTREAM, INSTREAM } from '../src/video.js'; - -const BIDDER_CODE = 'dailyhunt'; -const BIDDER_ALIAS = 'dh'; -const SUPPORTED_MEDIA_TYPES = [mediaTypes.BANNER, mediaTypes.NATIVE, mediaTypes.VIDEO]; - -const PROD_PREBID_ENDPOINT_URL = 'https://pbs.dailyhunt.in/openrtb2/auction?partner='; -const PROD_PREBID_TEST_ENDPOINT_URL = 'https://qa-pbs-van.dailyhunt.in/openrtb2/auction?partner='; - -const ORTB_NATIVE_TYPE_MAPPING = { - img: { - '3': 'image', - '1': 'icon' - }, - data: { - '1': 'sponsoredBy', - '2': 'body', - '3': 'rating', - '4': 'likes', - '5': 'downloads', - '6': 'price', - '7': 'salePrice', - '8': 'phone', - '9': 'address', - '10': 'body2', - '11': 'displayUrl', - '12': 'cta' - } -} - -const ORTB_NATIVE_PARAMS = { - title: { - id: 0, - name: 'title' - }, - icon: { - id: 1, - type: 1, - name: 'img' - }, - image: { - id: 2, - type: 3, - name: 'img' - }, - sponsoredBy: { - id: 3, - name: 'data', - type: 1 - }, - body: { - id: 4, - name: 'data', - type: 2 - }, - cta: { - id: 5, - type: 12, - name: 'data' - }, - body2: { - id: 4, - name: 'data', - type: 10 - }, -}; - -// Encode URI. -const _encodeURIComponent = function (a) { - let b = window.encodeURIComponent(a); - b = b.replace(/'/g, '%27'); - return b; -} - -// Extract key from collections. -const extractKeyInfo = (collection, key) => { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i].params, key); - if (result) { - return result; - } - } - return undefined -} - -// Flattern Array. -const flatten = (arr) => { - return [].concat(...arr); -} - -const createOrtbRequest = (validBidRequests, bidderRequest) => { - let device = createOrtbDeviceObj(validBidRequests); - let user = createOrtbUserObj(validBidRequests) - let site = createOrtbSiteObj(validBidRequests, bidderRequest.refererInfo.referer) - return { - id: bidderRequest.auctionId, - imp: [], - site, - device, - user, - }; -} - -const createOrtbDeviceObj = (validBidRequests) => { - let device = { ...extractKeyInfo(validBidRequests, `device`) }; - device.ua = navigator.userAgent; - return device; -} - -const createOrtbUserObj = (validBidRequests) => ({ ...extractKeyInfo(validBidRequests, `user`) }) - -const createOrtbSiteObj = (validBidRequests, page) => { - let site = { ...extractKeyInfo(validBidRequests, `site`), page }; - let publisher = createOrtbPublisherObj(validBidRequests); - if (publisher) { - site.publisher = publisher - } - return site -} - -const createOrtbPublisherObj = (validBidRequests) => ({ ...extractKeyInfo(validBidRequests, `publisher`) }) - -const createOrtbImpObj = (bid) => { - let params = bid.params - let testMode = !!bid.params.test_mode - - // Validate Banner Request. - let bannerObj = utils.deepAccess(bid.mediaTypes, `banner`); - let nativeObj = utils.deepAccess(bid.mediaTypes, `native`); - let videoObj = utils.deepAccess(bid.mediaTypes, `video`); - - let imp = { - id: bid.bidId, - bidfloor: params.bidfloor ? params.bidfloor : 0, - ext: { - dailyhunt: { - placement_id: params.placement_id, - publisher_id: params.publisher_id, - partner: params.partner_name - } - } - }; - - // Test Mode Campaign. - if (testMode) { - imp.ext.test_mode = testMode; - } - - if (bannerObj) { - imp.banner = { - ...createOrtbImpBannerObj(bid, bannerObj) - } - } else if (nativeObj) { - imp.native = { - ...createOrtbImpNativeObj(bid, nativeObj) - } - } else if (videoObj) { - imp.video = { - ...createOrtbImpVideoObj(bid, videoObj) - } - } - return imp; -} - -const createOrtbImpBannerObj = (bid, bannerObj) => { - let format = []; - bannerObj.sizes.forEach(size => format.push({ w: size[0], h: size[1] })) - - return { - id: 'banner-' + bid.bidId, - format - } -} - -const createOrtbImpNativeObj = (bid, nativeObj) => { - const assets = utils._map(bid.nativeParams, (bidParams, key) => { - const props = ORTB_NATIVE_PARAMS[key]; - const asset = { - required: bidParams.required & 1, - }; - if (props) { - let h = 0; - let w = 0; - - asset.id = props.id; - - if (bidParams.sizes) { - const sizes = flatten(bidParams.sizes); - w = sizes[0]; - h = sizes[1]; - } - - asset[props.name] = { - len: bidParams.len ? bidParams.len : 20, - type: props.type, - w, - h - }; - - return asset; - } - }).filter(Boolean); - let request = { - assets, - ver: '1,0' - } - return { request: JSON.stringify(request) }; -} - -const createOrtbImpVideoObj = (bid, videoObj) => { - let obj = {}; - let params = bid.params - if (!utils.isEmpty(bid.params.video)) { - obj = { - ...params.video, - } - } else { - obj = { - mimes: ['video/mp4'], - }; - } - obj.ext = { - ...videoObj, - } - return obj; -} - -const createServerRequest = (ortbRequest, validBidRequests, isTestMode = 'false') => ({ - method: 'POST', - url: isTestMode === 'true' ? PROD_PREBID_TEST_ENDPOINT_URL + validBidRequests[0].params.partner_name : PROD_PREBID_ENDPOINT_URL + validBidRequests[0].params.partner_name, - data: JSON.stringify(ortbRequest), - options: { - contentType: 'application/json', - withCredentials: true - }, - bids: validBidRequests -}) - -const createPrebidBannerBid = (bid, bidResponse) => ({ - requestId: bid.bidId, - cpm: bidResponse.price.toFixed(2), - creativeId: bidResponse.crid, - width: bidResponse.w, - height: bidResponse.h, - ttl: 360, - netRevenue: bid.netRevenue === 'net', - currency: 'USD', - ad: bidResponse.adm, - mediaType: 'banner', - winUrl: bidResponse.nurl -}) - -const createPrebidNativeBid = (bid, bidResponse) => ({ - requestId: bid.bidId, - cpm: bidResponse.price.toFixed(2), - creativeId: bidResponse.crid, - currency: 'USD', - ttl: 360, - netRevenue: bid.netRevenue === 'net', - native: parseNative(bidResponse), - mediaType: 'native', - winUrl: bidResponse.nurl, - width: bidResponse.w, - height: bidResponse.h, -}) - -const parseNative = (bid) => { - let adm = JSON.parse(bid.adm) - const { assets, link, imptrackers, jstracker } = adm.native; - const result = { - clickUrl: _encodeURIComponent(link.url), - clickTrackers: link.clicktrackers || [], - impressionTrackers: imptrackers || [], - javascriptTrackers: jstracker ? [ jstracker ] : [] - }; - assets.forEach(asset => { - if (!utils.isEmpty(asset.title)) { - result.title = asset.title.text - } else if (!utils.isEmpty(asset.img)) { - result[ORTB_NATIVE_TYPE_MAPPING.img[asset.img.type]] = { - url: asset.img.url, - height: asset.img.h, - width: asset.img.w - } - } else if (!utils.isEmpty(asset.data)) { - result[ORTB_NATIVE_TYPE_MAPPING.data[asset.data.type]] = asset.data.value - } - }); - - return result; -} - -const createPrebidVideoBid = (bid, bidResponse) => { - let videoBid = { - requestId: bid.bidId, - cpm: bidResponse.price.toFixed(2), - creativeId: bidResponse.crid, - width: bidResponse.w, - height: bidResponse.h, - ttl: 360, - netRevenue: bid.netRevenue === 'net', - currency: 'USD', - mediaType: 'video', - winUrl: bidResponse.nurl, - }; - - let videoContext = bid.mediaTypes.video.context; - switch (videoContext) { - case OUTSTREAM: - videoBid.vastXml = bidResponse.adm; - break; - case INSTREAM: - videoBid.videoCacheKey = bidResponse.ext.bidder.cacheKey; - videoBid.vastUrl = bidResponse.ext.bidder.vastUrl; - break; - } - return videoBid; -} - -const getQueryVariable = (variable) => { - let query = window.location.search.substring(1); - let vars = query.split('&'); - for (var i = 0; i < vars.length; i++) { - let pair = vars[i].split('='); - if (decodeURIComponent(pair[0]) == variable) { - return decodeURIComponent(pair[1]); - } - } - return false; -} - -export const spec = { - code: BIDDER_CODE, - - aliases: [BIDDER_ALIAS], - - supportedMediaTypes: SUPPORTED_MEDIA_TYPES, - - isBidRequestValid: bid => !!bid.params.placement_id && !!bid.params.publisher_id && !!bid.params.partner_name, - - buildRequests: function (validBidRequests, bidderRequest) { - let serverRequests = []; - - // ORTB Request. - let ortbReq = createOrtbRequest(validBidRequests, bidderRequest); - - validBidRequests.forEach((bid) => { - let imp = createOrtbImpObj(bid) - ortbReq.imp.push(imp); - }); - - serverRequests.push({ ...createServerRequest(ortbReq, validBidRequests, getQueryVariable('dh_test')) }); - - return serverRequests; - }, - - interpretResponse: function (serverResponse, request) { - const { seatbid } = serverResponse.body; - let bids = request.bids; - let prebidResponse = []; - - let seatBids = seatbid[0].bid; - - seatBids.forEach(ortbResponseBid => { - let bidId = ortbResponseBid.impid; - let actualBid = find(bids, (bid) => bid.bidId === bidId); - let bidMediaType = ortbResponseBid.ext.prebid.type - switch (bidMediaType) { - case mediaTypes.BANNER: - prebidResponse.push(createPrebidBannerBid(actualBid, ortbResponseBid)); - break; - case mediaTypes.NATIVE: - prebidResponse.push(createPrebidNativeBid(actualBid, ortbResponseBid)); - break; - case mediaTypes.VIDEO: - prebidResponse.push(createPrebidVideoBid(actualBid, ortbResponseBid)); - break; - } - }) - return prebidResponse; - }, - - onBidWon: function(bid) { - ajax(bid.winUrl, null, null, { - method: 'GET' - }) - } -} - -registerBidder(spec); diff --git a/modules/decenteradsBidAdapter.js b/modules/decenteradsBidAdapter.js deleted file mode 100644 index 823a59a3768..00000000000 --- a/modules/decenteradsBidAdapter.js +++ /dev/null @@ -1,90 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' -import * as utils from '../src/utils.js' - -const BIDDER_CODE = 'decenterads' -const URL = 'https://supply.decenterads.com/?c=o&m=multi' -const URL_SYNC = 'https://supply.decenterads.com/?c=o&m=cookie' - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: function (opts) { - return Boolean(opts.bidId && opts.params && !isNaN(opts.params.placementId)) - }, - - buildRequests: function (validBidRequests) { - validBidRequests = validBidRequests || [] - let winTop = window - try { - window.top.location.toString() - winTop = window.top - } catch (e) { utils.logMessage(e) } - - const location = utils.getWindowLocation() - const placements = [] - - for (let i = 0; i < validBidRequests.length; i++) { - const p = validBidRequests[i] - - placements.push({ - placementId: p.params.placementId, - bidId: p.bidId, - traffic: p.params.traffic || BANNER - }) - } - - return { - method: 'POST', - url: URL, - data: { - deviceWidth: winTop.screen.width, - deviceHeight: winTop.screen.height, - language: (navigator && navigator.language) ? navigator.language : '', - secure: +(location.protocol === 'https:'), - host: location.hostname, - page: location.pathname, - placements: placements - } - } - }, - - interpretResponse: function (opts) { - const body = opts.body - const response = [] - - for (let i = 0; i < body.length; i++) { - const item = body[i] - if (isBidResponseValid(item)) { - delete item.mediaType - response.push(item) - } - } - - return response - }, - - getUserSyncs: function (syncOptions, serverResponses) { - return [{ type: 'image', url: URL_SYNC }] - } -} - -registerBidder(spec) - -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) - case NATIVE: - return Boolean(bid.title && bid.image && bid.impressionTrackers) - default: - return false - } -} diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 22c904b604c..79cb03ec001 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -123,7 +123,7 @@ export function notifyTranslationModule(fn) { fn.call(this, 'dfp'); } -getHook('registerAdserver').before(notifyTranslationModule); +if (config.getConfig('brandCategoryTranslation.translationFile')) { getHook('registerAdserver').before(notifyTranslationModule); } /** * @typedef {Object} DfpAdpodOptions @@ -271,7 +271,7 @@ function getCustParams(bid, options) { const prebidTargetingSet = Object.assign({}, // Why are we adding standard keys here ? Refer https://github.com/prebid/Prebid.js/issues/3664 { hb_uuid: bid && bid.videoCacheKey }, - // hb_uuid will be deprecated and replaced by hb_cache_id + // hb_cache_id became optional in prebid 5.0 after 4.x enabled the concept of optional keys. Discussion led to reversing the prior expectation of deprecating hb_uuid { hb_cache_id: bid && bid.videoCacheKey }, allTargetingData, adserverTargeting, diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index ec0a0a2f2e6..a35965c4cb4 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -120,7 +120,6 @@ export const spec = { bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.lotamePanoramaId`), 'lotame.com', 1); bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.parrableId`), 'parrable.com', 1); bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.netId`), 'netid.de', 1); - bindUserId(eids, utils.deepAccess(bidRequest[0], `userId.sharedid`), 'sharedid.org', 1); dmxRequest.user = dmxRequest.user || {}; dmxRequest.user.ext = dmxRequest.user.ext || {}; dmxRequest.user.ext.eids = eids; diff --git a/modules/djaxBidAdapter.js b/modules/djaxBidAdapter.js deleted file mode 100644 index ffaf61a3f15..00000000000 --- a/modules/djaxBidAdapter.js +++ /dev/null @@ -1,129 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import * as utils from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import { ajax } from '../src/ajax.js'; -import {Renderer} from '../src/Renderer.js'; - -const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; -const BIDDER_CODE = 'djax'; -const DOMAIN = 'https://demo.reviveadservermod.com/headerbidding_adminshare/'; -const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -function isBidRequestValid(bid) { - return (typeof bid.params !== 'undefined' && parseInt(utils.getValue(bid.params, 'publisherId')) > 0); -} - -function buildRequests(validBidRequests) { - return { - method: 'POST', - url: DOMAIN + 'www/admin/plugins/Prebid/getAd.php', - options: { - withCredentials: false, - crossOrigin: true - }, - data: validBidRequests, - }; -} - -function interpretResponse(serverResponse, request) { - const response = serverResponse.body; - const bidResponses = []; - var bidRequestResponses = []; - - utils._each(response, function(bidAd) { - bidAd.adResponse = { - content: bidAd.vastXml, - height: bidAd.height, - width: bidAd.width - }; - bidAd.ttl = config.getConfig('_bidderTimeout') - bidAd.renderer = bidAd.context === 'outstream' ? createRenderer(bidAd, { - id: bidAd.adUnitCode, - url: RENDERER_URL - }, bidAd.adUnitCode) : undefined; - bidResponses.push(bidAd); - }); - - bidRequestResponses.push({ - function: 'saveResponses', - request: request, - response: bidResponses - }); - sendResponseToServer(bidRequestResponses); - return bidResponses; -} - -function outstreamRender(bidAd) { - bidAd.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - sizes: [bidAd.width, bidAd.height], - width: bidAd.width, - height: bidAd.height, - targetId: bidAd.adUnitCode, - adResponse: bidAd.adResponse, - rendererOptions: { - showVolume: false, - allowFullscreen: false - } - }); - }); -} - -function createRenderer(bidAd, rendererParams, adUnitCode) { - const renderer = Renderer.install({ - id: rendererParams.id, - url: rendererParams.url, - loaded: false, - config: {'player_height': bidAd.height, 'player_width': bidAd.width}, - adUnitCode - }); - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - return renderer; -} - -function onBidWon(bid) { - let wonBids = []; - wonBids.push(bid); - wonBids[0].function = 'onBidWon'; - sendResponseToServer(wonBids); -} - -function onTimeout(details) { - details.unshift({ 'function': 'onTimeout' }); - sendResponseToServer(details); -} - -function sendResponseToServer(data) { - ajax(DOMAIN + 'www/admin/plugins/Prebid/tracking/track.php', null, JSON.stringify(data), { - withCredentials: false, - method: 'POST', - crossOrigin: true - }); -} - -function getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: DOMAIN + 'www/admin/plugins/Prebid/userSync.php' - }]; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, - onBidWon, - onTimeout -}; - -registerBidder(spec); diff --git a/modules/e_volutionBidAdapter.js b/modules/e_volutionBidAdapter.js deleted file mode 100644 index 9fc7035db32..00000000000 --- a/modules/e_volutionBidAdapter.js +++ /dev/null @@ -1,111 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'e_volution'; -const AD_URL = 'https://service.e-volution.ai/?c=o&m=multi'; -const URL_SYNC = 'https://service.e-volution.ai/?c=o&m=sync'; -const NO_SYNC = true; - -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); - case NATIVE: - return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - noSync: NO_SYNC, - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - '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++) { - let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER - - placements.push({ - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [], - traffic: traff - }); - if (bid.schain) { - placements.schain = bid.schain; - } - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses) => { - if (NO_SYNC) { - return false - } else { - return [{ - type: 'image', - url: URL_SYNC - }]; - } - } - -}; - -registerBidder(spec); diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js deleted file mode 100644 index c30c10d8a90..00000000000 --- a/modules/ebdrBidAdapter.js +++ /dev/null @@ -1,151 +0,0 @@ -import * as utils from '../src/utils.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -const BIDDER_CODE = 'ebdr'; -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [ BANNER, VIDEO ], - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.zoneid); - }, - buildRequests: function(bids) { - const rtbServerDomain = 'dsp.bnmla.com'; - let domain = window.location.host; - let page = window.location.pathname + location.search + location.hash; - let ebdrImps = []; - const ebdrReq = {}; - let ebdrParams = {}; - let zoneid = ''; - let requestId = ''; - bids.forEach(bid => { - utils.logInfo('Log bid', bid); - let bidFloor = utils.getBidIdParameter('bidfloor', bid.params); - let whArr = getWidthAndHeight(bid); - let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video) ? VIDEO : BANNER; - zoneid = utils.getBidIdParameter('zoneid', bid.params); - requestId = bid.bidderRequestId; - ebdrImps.push({ - id: bid.bidId, - [_mediaTypes]: { - w: whArr[0], - h: whArr[1] - }, - bidfloor: bidFloor - }) - ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes, - w: whArr[0], - h: whArr[1] - }; - ebdrParams['latitude'] = utils.getBidIdParameter('latitude', bid.params); - ebdrParams['longitude'] = utils.getBidIdParameter('longitude', bid.params); - ebdrParams['ifa'] = (utils.getBidIdParameter('IDFA', bid.params).length > utils.getBidIdParameter('ADID', bid.params).length) ? utils.getBidIdParameter('IDFA', bid.params) : utils.getBidIdParameter('ADID', bid.params); - }); - let ebdrBidReq = { - id: requestId, - imp: ebdrImps, - site: { - domain: domain, - page: page - }, - device: { - geo: { - lat: ebdrParams.latitude, - log: ebdrParams.longitude - }, - ifa: ebdrParams.ifa - } - }; - return { - method: 'GET', - url: 'https://' + rtbServerDomain + '/hb?' + '&zoneid=' + zoneid + '&br=' + encodeURIComponent(JSON.stringify(ebdrBidReq)), - bids: ebdrReq - }; - }, - interpretResponse: function(serverResponse, bidRequest) { - utils.logInfo('Log serverResponse', serverResponse); - utils.logInfo('Log bidRequest', bidRequest); - let ebdrResponseImps = []; - const ebdrResponseObj = serverResponse.body; - if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) { - return []; - } - ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => { - let responseCPM; - responseCPM = parseFloat(ebdrBid.price); - let adm; - let type; - let _mediaTypes; - let vastURL; - if (bidRequest.bids[ebdrBid.id].mediaTypes == BANNER) { - adm = decodeURIComponent(ebdrBid.adm) - type = 'ad'; - _mediaTypes = BANNER; - } else { - adm = ebdrBid.adm - type = 'vastXml' - _mediaTypes = VIDEO; - if (ebdrBid.nurl) { - vastURL = ebdrBid.nurl; - } - } - let response = { - requestId: ebdrBid.id, - [type]: adm, - mediaType: _mediaTypes, - creativeId: ebdrBid.crid, - cpm: responseCPM, - width: ebdrBid.w, - height: ebdrBid.h, - currency: 'USD', - netRevenue: true, - ttl: 3600 } - if (vastURL) { - response.vastUrl = vastURL; - } - ebdrResponseImps.push(response); - }); - return ebdrResponseImps; - }, - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = [] - if (syncOptions.pixelEnabled) { - const ebdrResponseObj = serverResponses.body; - if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) { - return []; - } - ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => { - if (ebdrBid.iurl && ebdrBid.iurl.length > 0) { - syncs.push({ - type: 'image', - url: ebdrBid.iurl - }); - } - }); - } - return syncs; - } -} -function getWidthAndHeight(bid) { - let adW = null; - let adH = null; - // Handing old bidder only has size object - if (bid.sizes && bid.sizes.length) { - let sizeArrayLength = bid.sizes.length; - if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') { - adW = bid.sizes[0]; - adH = bid.sizes[1]; - } - } - let _mediaTypes = bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER; - if (bid.mediaTypes && bid.mediaTypes[_mediaTypes]) { - if (_mediaTypes == BANNER && bid.mediaTypes[_mediaTypes].sizes && bid.mediaTypes[_mediaTypes].sizes[0] && bid.mediaTypes[_mediaTypes].sizes[0].length === 2) { - adW = bid.mediaTypes[_mediaTypes].sizes[0][0]; - adH = bid.mediaTypes[_mediaTypes].sizes[0][1]; - } else if (_mediaTypes == VIDEO && bid.mediaTypes[_mediaTypes].playerSize && bid.mediaTypes[_mediaTypes].playerSize.length === 2) { - adW = bid.mediaTypes[_mediaTypes].playerSize[0]; - adH = bid.mediaTypes[_mediaTypes].playerSize[1]; - } - } - return [adW, adH]; -} -registerBidder(spec); diff --git a/modules/edgequeryxBidAdapter.js b/modules/edgequeryxBidAdapter.js deleted file mode 100644 index ee50946ee18..00000000000 --- a/modules/edgequeryxBidAdapter.js +++ /dev/null @@ -1,98 +0,0 @@ -import * as utils 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 = 'edgequeryx'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['eqx'], // 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 && bid.params.accountId && bid.params.widgetId); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests an array of bids - * @param {BidderRequest} bidderRequest bidder request object - * @return {ServerRequest[]} Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - // use bidderRequest.bids[] to get bidder-dependent request info - // if your bidder supports multiple currencies, use config.getConfig(currency) - // to find which one the ad server needs - - // pull requested transaction ID from bidderRequest.bids[].transactionId - return validBidRequests.map(bid => { - // Common bid request attributes for banner, outstream and instream. - let payload = { - accountId: bid.params.accountId, - widgetId: bid.params.widgetId, - currencyCode: 'EUR', - tagId: bid.adUnitCode, - transactionId: bid.transactionId, - timeout: config.getConfig('bidderTimeout'), - bidId: bid.bidId, - prebidVersion: '$prebid.version$' - }; - - const bannerMediaType = utils.deepAccess(bid, 'mediaTypes.banner'); - payload.sizes = bannerMediaType.sizes.map(size => ({ - w: size[0], - h: size[1] - })); - - var payloadString = JSON.stringify(payload); - - return { - method: 'POST', - url: (bid.params.domain !== undefined ? bid.params.domain : 'https://deep.edgequery.io') + '/prebid/x', - data: payloadString, - }; - }); - }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {*} bidRequestString - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function (serverResponse, bidRequestString) { - const bidResponses = []; - let response = serverResponse.body; - try { - if (response) { - let bidResponse = { - requestId: response.requestId, - cpm: response.cpm, - currency: response.currency, - width: response.width, - height: response.height, - ad: response.ad, - ttl: response.ttl, - creativeId: response.creativeId, - netRevenue: response.netRevenue - }; - - bidResponses.push(bidResponse); - } - } catch (error) { - utils.logError('Error while parsing Edge Query X response', error); - } - return bidResponses; - } - -}; - -registerBidder(spec); diff --git a/modules/emoteevBidAdapter.js b/modules/emoteevBidAdapter.js deleted file mode 100644 index e0f88725d8a..00000000000 --- a/modules/emoteevBidAdapter.js +++ /dev/null @@ -1,525 +0,0 @@ -/** - * This file contains Emoteev bid adpater. - * - * It is organised as follows: - * - Constants values; - * - Spec API functions, which should be pristine pure; - * - Ancillary functions, which should be as pure as possible; - * - Adapter API, where unpure side-effects happen. - * - * The code style is « functional core, imperative shell ». - * - * @link https://www.emoteev.io - * @file This files defines the spec of EmoteevBidAdapter. - * @author Emoteev Engineering . - */ - -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import { - triggerPixel, - getUniqueIdentifierStr, - contains, - deepAccess, - isArray, - isInteger, - getParameterByName, - buildUrl -} from '../src/utils.js'; -import {config} from '../src/config.js'; -import { getStorageManager } from '../src/storageManager.js'; - -export const storage = getStorageManager(); - -export const BIDDER_CODE = 'emoteev'; - -/** - * Version number of the adapter API. - */ -export const ADAPTER_VERSION = '1.35.0'; - -export const DOMAIN = 'prebid.emoteev.xyz'; -export const DOMAIN_STAGING = 'prebid-staging.emoteev.xyz'; -export const DOMAIN_DEVELOPMENT = 'localhost:3000'; - -/** - * Path of Emoteev endpoint for events. - */ -export const EVENTS_PATH = '/api/ad_event.json'; - -/** - * Path of Emoteev bidder. - */ -export const BIDDER_PATH = '/api/prebid/bid'; -export const USER_SYNC_IFRAME_PATH = '/api/prebid/sync-iframe'; -export const USER_SYNC_IMAGE_PATH = '/api/prebid/sync-image'; - -export const PRODUCTION = 'production'; -export const STAGING = 'staging'; -export const DEVELOPMENT = 'development'; -export const DEFAULT_ENV = PRODUCTION; - -export const ON_ADAPTER_CALLED = 'on_adapter_called'; -export const ON_BID_WON = 'on_bid_won'; -export const ON_BIDDER_TIMEOUT = 'on_bidder_timeout'; - -export const IN_CONTENT = 'content'; -export const FOOTER = 'footer'; -export const OVERLAY = 'overlay'; -export const WALLPAPER = 'wallpaper'; - -/** - * Vendor ID assigned to Emoteev from the Global Vendor & CMP List. - * - * See https://vendorlist.consensu.org/vendorinfo.json for more information. - * @type {number} - */ -export const VENDOR_ID = 15; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#valid-build-requests-array for detailed semantic. - * - * @param {AdUnit.bidRequest} bidRequest - * @returns {boolean} Is this bidRequest valid? - */ -export const isBidRequestValid = (bidRequest) => { - return !!( - bidRequest && - bidRequest.params && - deepAccess(bidRequest, 'params.adSpaceId') && - validateContext(deepAccess(bidRequest, 'params.context')) && - validateExternalId(deepAccess(bidRequest, 'params.externalId')) && - bidRequest.bidder === BIDDER_CODE && - validateSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'))); -}; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#serverrequest-objects for detailed semantic. - * - * @param {string} env Emoteev environment parameter - * @param {boolean} debug Pbjs debug parameter. - * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed semantic. - * @param {Array} validBidRequests Takes an array of bid requests, which are guaranteed to have passed the isBidRequestValid() test. - * @param bidderRequest General context for a bidder request being constructed - * @returns {ServerRequest} - */ -export const buildRequests = (env, debug, currency, validBidRequests, bidderRequest) => { - return { - method: 'POST', - url: bidderUrl(env), - data: JSON.stringify(requestsPayload(debug, currency, validBidRequests, bidderRequest)) // Keys with undefined values will be filtered out. - }; -}; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response for detailed semantic. - * - * @param {Array} serverResponse.body The body of the server response is an array of bid objects. - * @returns {Array} - */ -export const interpretResponse = (serverResponse) => serverResponse.body; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-set-targeting for detailed semantic. - * - * @param {string} env Emoteev environment parameter. - * @param {BidRequest} bidRequest - * @returns {UrlObject} - */ -export function onAdapterCalled(env, bidRequest) { - return { - protocol: 'https', - hostname: domain(env), - pathname: EVENTS_PATH, - search: { - eventName: ON_ADAPTER_CALLED, - pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), - bidId: bidRequest.bidId, - adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), - cache_buster: getUniqueIdentifierStr() - } - }; -} - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-bid-won for detailed semantic. - * - * @param {string} env Emoteev environment parameter. - * @param {string} pubcId Publisher common id. See http://prebid.org/dev-docs/modules/pubCommonId.html for detailed semantic. - * @param bidObject - * @returns {UrlObject} - */ -export const onBidWon = (env, pubcId, bidObject) => { - const bidId = bidObject.requestId; - return { - protocol: 'https', - hostname: domain(env), - pathname: EVENTS_PATH, - search: { - eventName: ON_BID_WON, - pubcId, - bidId, - cache_buster: getUniqueIdentifierStr() - } - }; -}; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-timeout for detailed semantic. - * - * @param {string} env Emoteev environment parameter. - * @param {BidRequest} bidRequest - * @returns {UrlObject} - */ -export const onTimeout = (env, bidRequest) => { - return { - protocol: 'https', - hostname: domain(env), - pathname: EVENTS_PATH, - search: { - eventName: ON_BIDDER_TIMEOUT, - pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), - bidId: bidRequest.bidId, - adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), - timeout: bidRequest.timeout, - cache_buster: getUniqueIdentifierStr() - } - } -}; - -/** - * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs for detailed semantic. - * - * @param {string} env Emoteev environment parameter - * @param {SyncOptions} syncOptions - * @returns userSyncs - */ -export const getUserSyncs = (env, syncOptions) => { - let syncs = []; - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: userSyncImageUrl(env), - }); - } - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: userSyncIframeUrl(env), - }); - } - return syncs; -}; - -/** - * Pure function. - * - * @param {string} env Emoteev environment parameter - * @returns {string} The domain for network calls to Emoteev. - */ -export const domain = (env) => { - switch (env) { - case DEVELOPMENT: - return DOMAIN_DEVELOPMENT; - case STAGING: - return DOMAIN_STAGING; - default: - return DOMAIN; - } -}; - -/** - * Pure function. - * - * @param {string} env Emoteev environment parameter - * @returns {string} The full URL which events is sent to. - */ -export const eventsUrl = env => buildUrl({ - protocol: (env === DEVELOPMENT) ? 'http' : 'https', - hostname: domain(env), - pathname: EVENTS_PATH -}); - -/** - * Pure function. - * - * @param {string} env Emoteev environment parameter - * @returns {string} The full URL which bidderRequest is sent to. - */ -export const bidderUrl = env => buildUrl({ - protocol: (env === DEVELOPMENT) ? 'http' : 'https', - hostname: domain(env), - pathname: BIDDER_PATH -}); - -/** - * Pure function. - * - * @param {string} env Emoteev environment parameter - * @returns {string} The full URL called for iframe-based user sync - */ -export const userSyncIframeUrl = env => buildUrl({ - protocol: (env === DEVELOPMENT) ? 'http' : 'https', - hostname: domain(env), - pathname: USER_SYNC_IFRAME_PATH -}); - -/** - * Pure function. - * - * @param {string} env Emoteev environment parameter - * @returns {string} The full URL called for image-based user sync - */ -export const userSyncImageUrl = env => buildUrl({ - protocol: (env === DEVELOPMENT) ? 'http' : 'https', - hostname: domain(env), - pathname: USER_SYNC_IMAGE_PATH -}); - -/** - * Pure function. - * - * @param {Array>} sizes - * @returns {boolean} are sizes valid? - */ -export const validateSizes = sizes => isArray(sizes) && sizes.length > 0 && sizes.every(size => isArray(size) && size.length === 2); - -/** - * Pure function. - * - * @param {string} context - * @returns {boolean} is param `context` valid? - */ -export const validateContext = context => contains([IN_CONTENT, FOOTER, OVERLAY, WALLPAPER], context); - -/** - * Pure function. - * - * @param {(number|null|undefined)} externalId - * @returns {boolean} is param `externalId` valid? - */ -export const validateExternalId = externalId => externalId === undefined || externalId === null || (isInteger(externalId) && externalId > 0); - -/** - * Pure function. - * - * @param {BidRequest} bidRequest - * @returns {object} An object which represents a BidRequest for Emoteev server side. - */ -export const conformBidRequest = bidRequest => { - return { - params: bidRequest.params, - crumbs: bidRequest.crumbs, - sizes: bidRequest.sizes, - bidId: bidRequest.bidId, - bidderRequestId: bidRequest.bidderRequestId, - }; -}; - -/** - * Pure function. - * - * @param {object} bidderRequest - * @returns {(boolean|undefined)} raw consent data. - */ -export const gdprConsent = (bidderRequest) => (deepAccess(bidderRequest, 'gdprConsent.vendorData.vendorConsents') || {})[VENDOR_ID]; - -/** - * Pure function. - * - * @param {boolean} debug Pbjs debug parameter - * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed information - * @param {BidRequest} validBidRequests - * @param {object} bidderRequest - * @returns - */ -export const requestsPayload = (debug, currency, validBidRequests, bidderRequest) => { - return { - akPbjsVersion: ADAPTER_VERSION, - bidRequests: validBidRequests.map(conformBidRequest), - currency: currency, - debug: debug, - language: navigator.language, - refererInfo: bidderRequest.refererInfo, - deviceInfo: getDeviceInfo( - getDeviceDimensions(window), - getViewDimensions(window, document), - getDocumentDimensions(document), - isWebGLEnabled(document)), - userAgent: navigator.userAgent, - gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies'), - gdprConsent: gdprConsent(bidderRequest), - }; -}; - -/** - * Pure function - * @param {Window} window - * @param {Document} document - * @returns {{width: number, height: number}} View dimensions - */ -export const getViewDimensions = (window, document) => { - let w = window; - let prefix = 'inner'; - - if (window.innerWidth === undefined || window.innerWidth === null) { - w = document.documentElement || document.body; - prefix = 'client'; - } - - return { - width: w[`${prefix}Width`], - height: w[`${prefix}Height`], - }; -}; - -/** - * Pure function - * @param {Window} window - * @returns {{width: number, height: number}} Device dimensions - */ -export const getDeviceDimensions = (window) => { - return { - width: window.screen ? window.screen.width : '', - height: window.screen ? window.screen.height : '', - }; -}; - -/** - * Pure function - * @param {Document} document - * @returns {{width: number, height: number}} Document dimensions - */ -export const getDocumentDimensions = (document) => { - const de = document.documentElement; - const be = document.body; - - const bodyHeight = be ? Math.max(be.offsetHeight, be.scrollHeight) : 0; - - const w = Math.max(de.clientWidth, de.offsetWidth, de.scrollWidth); - const h = Math.max( - de.clientHeight, - de.offsetHeight, - de.scrollHeight, - bodyHeight - ); - - return { - width: isNaN(w) ? '' : w, - height: isNaN(h) ? '' : h, - }; -}; - -/** - * Unpure function - * @param {Document} document - * @returns {boolean} Is WebGL enabled? - */ -export const isWebGLEnabled = (document) => { - // Create test canvas - let canvas = document.createElement('canvas'); - - // The gl context - let gl = null; - - // Try to get the regular WebGL - try { - gl = canvas.getContext('webgl'); - } catch (ex) { - canvas = undefined; - return false; - } - - // No regular WebGL found - if (!gl) { - // Try experimental WebGL - try { - gl = canvas.getContext('experimental-webgl'); - } catch (ex) { - canvas = undefined; - return false; - } - } - - return !!gl; -}; - -/** - * Pure function - * @param {{width: number, height: number}} deviceDimensions - * @param {{width: number, height: number}} viewDimensions - * @param {{width: number, height: number}} documentDimensions - * @param {boolean} webGL - * @returns {object} Device information - */ -export const getDeviceInfo = (deviceDimensions, viewDimensions, documentDimensions, webGL) => { - return { - browserWidth: viewDimensions.width, - browserHeight: viewDimensions.height, - deviceWidth: deviceDimensions.width, - deviceHeight: deviceDimensions.height, - documentWidth: documentDimensions.width, - documentHeight: documentDimensions.height, - webGL: webGL, - }; -}; - -/** - * Pure function - * @param {object} config pbjs config value - * @param {string} parameter Environment override from URL query param. - * @returns {string} One of [PRODUCTION, STAGING, DEVELOPMENT]. - */ -export const resolveEnv = (config, parameter) => { - const configEnv = deepAccess(config, 'emoteev.env'); - - if (contains([PRODUCTION, STAGING, DEVELOPMENT], parameter)) return parameter; - else if (contains([PRODUCTION, STAGING, DEVELOPMENT], configEnv)) return configEnv; - else return DEFAULT_ENV; -}; - -/** - * Pure function - * @param {object} config pbjs config value - * @param {string} parameter Debug override from URL query param. - * @returns {boolean} - */ -export const resolveDebug = (config, parameter) => { - if (parameter && parameter.length && parameter.length > 0) return JSON.parse(parameter); - else if (config.debug) return config.debug; - else return false; -}; - -/** - * EmoteevBidAdapter spec - * @access public - * @type {BidderSpec} - */ -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid: isBidRequestValid, - buildRequests: (validBidRequests, bidderRequest) => - buildRequests( - resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), - resolveDebug(config.getConfig(), getParameterByName('debug')), - config.getConfig('currency'), - validBidRequests, - bidderRequest), - interpretResponse: interpretResponse, - onBidWon: (bidObject) => - triggerPixel(buildUrl(onBidWon( - resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), - storage.getCookie('_pubcid'), - bidObject))), - onTimeout: (bidRequest) => - triggerPixel(buildUrl(onTimeout( - resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), - bidRequest))), - getUserSyncs: (syncOptions) => - getUserSyncs( - resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), - syncOptions), -}; - -registerBidder(spec); diff --git a/modules/engageyaBidAdapter.js b/modules/engageyaBidAdapter.js deleted file mode 100644 index 321b3287c2b..00000000000 --- a/modules/engageyaBidAdapter.js +++ /dev/null @@ -1,133 +0,0 @@ -import { - BANNER, - NATIVE -} from '../src/mediaTypes.js'; - -const { - registerBidder -} = require('../src/adapters/bidderFactory.js'); -const BIDDER_CODE = 'engageya'; -const ENDPOINT_URL = 'https://recs.engageya.com/rec-api/getrecs.json'; -const ENDPOINT_METHOD = 'GET'; - -function getPageUrl() { - var pUrl = window.location.href; - if (isInIframe()) { - pUrl = document.referrer ? document.referrer : pUrl; - } - pUrl = encodeURIComponent(pUrl); - return pUrl; -} - -function isInIframe() { - try { - var isInIframe = (window.self !== window.top); - } catch (e) { - isInIframe = true; - } - return isInIframe; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - isBidRequestValid: function(bid) { - return bid && bid.params && bid.params.hasOwnProperty('widgetId') && bid.params.hasOwnProperty('websiteId') && !isNaN(bid.params.widgetId) && !isNaN(bid.params.websiteId); - }, - - buildRequests: function(validBidRequests, bidderRequest) { - var bidRequests = []; - if (validBidRequests && validBidRequests.length > 0) { - validBidRequests.forEach(function(bidRequest) { - if (bidRequest.params) { - var mediaType = bidRequest.hasOwnProperty('nativeParams') ? 1 : 2; - var imageWidth = -1; - var imageHeight = -1; - if (bidRequest.sizes && bidRequest.sizes.length > 0) { - imageWidth = bidRequest.sizes[0][0]; - imageHeight = bidRequest.sizes[0][1]; - } else if (bidRequest.nativeParams && bidRequest.nativeParams.image && bidRequest.nativeParams.image.sizes) { - imageWidth = bidRequest.nativeParams.image.sizes[0]; - imageHeight = bidRequest.nativeParams.image.sizes[1]; - } - - var widgetId = bidRequest.params.widgetId; - var websiteId = bidRequest.params.websiteId; - var pageUrl = (bidRequest.params.pageUrl && bidRequest.params.pageUrl != '[PAGE_URL]') ? bidRequest.params.pageUrl : ''; - if (!pageUrl) { - pageUrl = (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) ? bidderRequest.refererInfo.referer : getPageUrl(); - } - var bidId = bidRequest.bidId; - var finalUrl = ENDPOINT_URL + '?pubid=0&webid=' + websiteId + '&wid=' + widgetId + '&url=' + pageUrl + '&ireqid=' + bidId + '&pbtpid=' + mediaType + '&imw=' + imageWidth + '&imh=' + imageHeight; - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprApplies && bidderRequest.consentString) { - finalUrl += '&is_gdpr=1&gdpr_consent=' + bidderRequest.consentString; - } - bidRequests.push({ - url: finalUrl, - method: ENDPOINT_METHOD, - data: '' - }); - } - }); - } - - return bidRequests; - }, - - interpretResponse: function(serverResponse, bidRequest) { - const bidResponses = []; - if (serverResponse.body && serverResponse.body.recs && serverResponse.body.recs.length > 0) { - var response = serverResponse.body; - var isNative = response.pbtypeId == 1; - response.recs.forEach(function(rec) { - var imageSrc = rec.thumbnail_path.indexOf('http') == -1 ? 'https:' + rec.thumbnail_path : rec.thumbnail_path; - if (isNative) { - bidResponses.push({ - requestId: response.ireqId, - cpm: rec.ecpm, - width: response.imageWidth, - height: response.imageHeight, - creativeId: rec.postId, - currency: 'USD', - netRevenue: false, - ttl: 360, - native: { - title: rec.title, - body: '', - image: { - url: imageSrc, - width: response.imageWidth, - height: response.imageHeight - }, - privacyLink: '', - clickUrl: rec.clickUrl, - displayUrl: rec.url, - cta: '', - sponsoredBy: rec.displayName, - impressionTrackers: [], - }, - }); - } else { - // var htmlTag = ""; - var htmlTag = '
'; - var tag = rec.tag ? rec.tag : htmlTag; - bidResponses.push({ - requestId: response.ireqId, - cpm: rec.ecpm, - width: response.imageWidth, - height: response.imageHeight, - creativeId: rec.postId, - currency: 'USD', - netRevenue: false, - ttl: 360, - ad: tag, - }); - } - }); - } - - return bidResponses; - } -}; - -registerBidder(spec); diff --git a/modules/envivoBidAdapter.js b/modules/envivoBidAdapter.js deleted file mode 100644 index b9c80ffd468..00000000000 --- a/modules/envivoBidAdapter.js +++ /dev/null @@ -1,129 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import * as utils from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import { ajax } from '../src/ajax.js'; -import {Renderer} from '../src/Renderer.js'; - -const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; -const BIDDER_CODE = 'envivo'; -const DOMAIN = 'https://ad.nvivo.tv/'; -const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -function isBidRequestValid(bid) { - return (typeof bid.params !== 'undefined' && parseInt(utils.getValue(bid.params, 'publisherId')) > 0); -} - -function buildRequests(validBidRequests) { - return { - method: 'POST', - url: DOMAIN + 'ads/www/admin/plugins/Prebid/getAd.php', - options: { - withCredentials: false, - crossOrigin: true - }, - data: validBidRequests, - }; -} - -function interpretResponse(serverResponse, request) { - const response = serverResponse.body; - const bidResponses = []; - var bidRequestResponses = []; - - utils._each(response, function(bidAd) { - bidAd.adResponse = { - content: bidAd.vastXml, - height: bidAd.height, - width: bidAd.width - }; - bidAd.ttl = config.getConfig('_bidderTimeout') - bidAd.renderer = bidAd.context === 'outstream' ? createRenderer(bidAd, { - id: bidAd.adUnitCode, - url: RENDERER_URL - }, bidAd.adUnitCode) : undefined; - bidResponses.push(bidAd); - }); - - bidRequestResponses.push({ - function: 'saveResponses', - request: request, - response: bidResponses - }); - sendResponseToServer(bidRequestResponses); - return bidResponses; -} - -function outstreamRender(bidAd) { - bidAd.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - sizes: [bidAd.width, bidAd.height], - width: bidAd.width, - height: bidAd.height, - targetId: bidAd.adUnitCode, - adResponse: bidAd.adResponse, - rendererOptions: { - showVolume: false, - allowFullscreen: false - } - }); - }); -} - -function createRenderer(bidAd, rendererParams, adUnitCode) { - const renderer = Renderer.install({ - id: rendererParams.id, - url: rendererParams.url, - loaded: false, - config: {'player_height': bidAd.height, 'player_width': bidAd.width}, - adUnitCode - }); - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - return renderer; -} - -function onBidWon(bid) { - let wonBids = []; - wonBids.push(bid); - wonBids[0].function = 'onBidWon'; - sendResponseToServer(wonBids); -} - -function onTimeout(details) { - details.unshift({ 'function': 'onTimeout' }); - sendResponseToServer(details); -} - -function sendResponseToServer(data) { - ajax(DOMAIN + 'ads/www/admin/plugins/Prebid/tracking/track.php', null, JSON.stringify(data), { - withCredentials: false, - method: 'POST', - crossOrigin: true - }); -} - -function getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: DOMAIN + 'ads/www/admin/plugins/Prebid/userSync.php' - }]; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, - onBidWon, - onTimeout -}; - -registerBidder(spec); diff --git a/modules/fidelityBidAdapter.js b/modules/fidelityBidAdapter.js deleted file mode 100644 index fac273721ff..00000000000 --- a/modules/fidelityBidAdapter.js +++ /dev/null @@ -1,146 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'fidelity'; -const BIDDER_SERVER = 'x.fidelity-media.com'; -const FIDELITY_VENDOR_ID = 408; -export const spec = { - code: BIDDER_CODE, - gvlid: 408, - isBidRequestValid: function isBidRequestValid(bid) { - return !!(bid && bid.params && bid.params.zoneid); - }, - buildRequests: function buildRequests(validBidRequests, bidderRequest) { - return validBidRequests.map(bidRequest => { - var server = bidRequest.params.server || BIDDER_SERVER; - - const payload = { - from: 'hb', - v: '1.0', - requestid: bidRequest.bidderRequestId, - impid: bidRequest.bidId, - zoneid: bidRequest.params.zoneid, - floor: parseFloat(bidRequest.params.floor) > 0 ? bidRequest.params.floor : 0, - charset: document.charSet || document.characterSet, - subid: 'hb', - flashver: getFlashVersion(), - tmax: bidderRequest.timeout, - defloc: bidderRequest.refererInfo.referer, - referrer: getTopWindowReferrer(), - schain: getSupplyChain(bidRequest.schain), - }; - setConsentParams(bidderRequest.gdprConsent, bidderRequest.uspConsent, payload); - - return { - method: 'GET', - url: 'https://' + server + '/delivery/hb.php', - data: payload - }; - }); - }, - interpretResponse: function interpretResponse(serverResponse) { - serverResponse = serverResponse.body; - const bidResponses = []; - if (serverResponse && serverResponse.seatbid) { - serverResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { - const bidResponse = { - requestId: bid.impid, - creativeId: bid.impid, - cpm: bid.price, - width: bid.width, - height: bid.height, - ad: bid.adm, - netRevenue: bid.netRevenue, - currency: bid.cur, - ttl: bid.ttl, - }; - - bidResponses.push(bidResponse); - })); - } - return bidResponses; - }, - getUserSyncs: function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - if (syncOptions.iframeEnabled) { - var url = 'https://' + BIDDER_SERVER + '/delivery/matches.php'; - var payload = { - type: 'iframe' - }; - setConsentParams(gdprConsent, uspConsent, payload); - - return [{ - type: 'iframe', - url: url + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, '') - }]; - } - } -} - -function getFlashVersion() { - var plugins, plugin, result; - - if (navigator.plugins && navigator.plugins.length > 0) { - plugins = navigator.plugins; - for (var i = 0; i < plugins.length && !result; i++) { - plugin = plugins[i]; - if (plugin.name.indexOf('Shockwave Flash') > -1) { - result = plugin.description.split('Shockwave Flash ')[1]; - } - } - } - return result || ''; -} - -function getTopWindowReferrer() { - try { - return window.top.document.referrer; - } catch (e) { - return ''; - } -} - -function setConsentParams(gdprConsent, uspConsent, payload) { - if (gdprConsent) { - payload.gdpr = 0; - payload.consent_str = ''; - payload.consent_given = 0; - if (typeof gdprConsent.gdprApplies !== 'undefined') { - payload.gdpr = gdprConsent.gdprApplies ? 1 : 0; - } - if (typeof gdprConsent.consentString !== 'undefined') { - payload.consent_str = gdprConsent.consentString; - } - if (gdprConsent.apiVersion === 1 && gdprConsent.vendorData && gdprConsent.vendorData.vendorConsents && typeof gdprConsent.vendorData.vendorConsents[FIDELITY_VENDOR_ID.toString(10)] !== 'undefined') { - payload.consent_given = gdprConsent.vendorData.vendorConsents[FIDELITY_VENDOR_ID.toString(10)] ? 1 : 0; - } - if (gdprConsent.apiVersion === 2 && gdprConsent.vendorData && gdprConsent.vendorData.vendor && gdprConsent.vendorData.vendor.consents && typeof gdprConsent.vendorData.vendor.consents[FIDELITY_VENDOR_ID.toString(10)] !== 'undefined') { - payload.consent_given = gdprConsent.vendorData.vendor.consents[FIDELITY_VENDOR_ID.toString(10)] ? 1 : 0; - } - } - if (typeof uspConsent !== 'undefined') { - payload.us_privacy = uspConsent; - } -} - -function getSupplyChain(schain) { - var supplyChain = ''; - if (schain != null && schain.nodes) { - supplyChain = schain.ver + ',' + schain.complete; - for (let i = 0; i < schain.nodes.length; i++) { - supplyChain += '!'; - supplyChain += (schain.nodes[i].asi) ? encodeURIComponent(schain.nodes[i].asi) : ''; - supplyChain += ','; - supplyChain += (schain.nodes[i].sid) ? encodeURIComponent(schain.nodes[i].sid) : ''; - supplyChain += ','; - supplyChain += (schain.nodes[i].hp) ? encodeURIComponent(schain.nodes[i].hp) : ''; - supplyChain += ','; - supplyChain += (schain.nodes[i].rid) ? encodeURIComponent(schain.nodes[i].rid) : ''; - supplyChain += ','; - supplyChain += (schain.nodes[i].name) ? encodeURIComponent(schain.nodes[i].name) : ''; - supplyChain += ','; - supplyChain += (schain.nodes[i].domain) ? encodeURIComponent(schain.nodes[i].domain) : ''; - } - } - return supplyChain; -} -registerBidder(spec); diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js deleted file mode 100644 index 420fe04ddcb..00000000000 --- a/modules/fluctBidAdapter.js +++ /dev/null @@ -1,121 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'fluct'; -const END_POINT = 'https://hb.adingo.jp/prebid'; -const VERSION = '1.2'; -const NET_REVENUE = true; -const TTL = 300; - -export const spec = { - code: BIDDER_CODE, - aliases: ['adingo'], - - /** - * 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: (bid) => { - return !!(bid.params.groupId && 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: (validBidRequests, bidderRequest) => { - const serverRequests = []; - const referer = bidderRequest.refererInfo.referer; - - utils._each(validBidRequests, (request) => { - const data = Object(); - - data.referer = referer; - data.adUnitCode = request.adUnitCode; - data.bidId = request.bidId; - data.transactionId = request.transactionId; - - data.sizes = []; - utils._each(request.sizes, (size) => { - data.sizes.push({ - w: size[0], - h: size[1] - }); - }); - - data.params = request.params; - - serverRequests.push({ - method: 'POST', - url: END_POINT, - options: { - contentType: 'application/json', - withCredentials: true, - customHeaders: { - 'x-fluct-app': 'prebid/fluctBidAdapter', - 'x-fluct-version': VERSION, - 'x-openrtb-version': 2.5 - } - }, - data: data - }); - }); - - return serverRequests; - }, - - /* - * Unpack the respnse from the server into a list of bids. - * - * @param {serverResponse} serverResponse A successful response from the server. - * @return {bid[]} An array of bids which weer nested inside the server. - */ - interpretResponse: (serverResponse, serverRequest) => { - const bidResponses = []; - - const res = serverResponse.body; - if (!utils.isEmpty(res) && !utils.isEmpty(res.seatbid) && !utils.isEmpty(res.seatbid[0].bid)) { - const bid = res.seatbid[0].bid[0]; - const dealId = bid.dealid; - const beaconUrl = bid.burl; - const callImpBeacon = ``; - let data = { - bidderCode: BIDDER_CODE, - requestId: res.id, - currency: res.cur, - cpm: parseFloat(bid.price) || 0, - netRevenue: NET_REVENUE, - width: bid.w, - height: bid.h, - creativeId: bid.crid, - ttl: TTL, - ad: bid.adm + callImpBeacon, - }; - if (!utils.isEmpty(dealId)) { - data.dealId = dealId; - } - bidResponses.push(data); - } - return bidResponses; - }, - - /* - * Register the user sync pixels which should be dropped after the auction. - * - * @params {syncOptions} syncOptions which user syncs are allowed? - * @params {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - * - */ - getUserSyncs: (syncOptions, serverResponses) => { - return []; - }, -}; - -registerBidder(spec); diff --git a/modules/gammaBidAdapter.js b/modules/gammaBidAdapter.js deleted file mode 100644 index 5fd3c56b2c6..00000000000 --- a/modules/gammaBidAdapter.js +++ /dev/null @@ -1,101 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://hb.gammaplatform.com'; -const ENDPOINT_USERSYNC = 'https://cm-supply-web.gammaplatform.com'; -const BIDDER_CODE = 'gamma'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['gamma'], - supportedMediaTypes: ['banner', 'video'], - - /** - * 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.siteId || bid.params.zoneId); - }, - - /** - * 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(bidRequests, bidderRequest) { - const serverRequests = []; - const bidderRequestReferer = (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) || ''; - for (var i = 0, len = bidRequests.length; i < len; i++) { - const gaxObjParams = bidRequests[i]; - serverRequests.push({ - method: 'GET', - url: ENDPOINT + '/adx/request?wid=' + gaxObjParams.params.siteId + '&zid=' + gaxObjParams.params.zoneId + '&hb=pbjs&bidid=' + gaxObjParams.bidId + '&urf=' + encodeURIComponent(bidderRequestReferer) - }); - } - return serverRequests; - }, - - /** - * 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) { - serverResponse = serverResponse.body; - - const bids = []; - - if (serverResponse.id) { - const bid = newBid(serverResponse); - bids.push(bid); - } - - return bids; - }, - - getUserSyncs: function(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: ENDPOINT_USERSYNC + '/adx/usersync' - }]; - } - } -} - -/** - * Unpack the Server's Bid into a Prebid-compatible one. - * @param serverBid - * @return Bid - */ -function newBid(serverBid) { - const bid = { - ad: serverBid.seatbid[0].bid[0].adm, - cpm: serverBid.seatbid[0].bid[0].price, - creativeId: serverBid.seatbid[0].bid[0].adid, - currency: serverBid.cur, - dealId: serverBid.seatbid[0].bid[0].dealid, - width: serverBid.seatbid[0].bid[0].w, - height: serverBid.seatbid[0].bid[0].h, - mediaType: serverBid.type, - netRevenue: true, - requestId: serverBid.id, - ttl: serverBid.seatbid[0].bid[0].ttl || 300 - }; - - if (serverBid.type == 'video') { - Object.assign(bid, { - vastXml: serverBid.seatbid[0].bid[0].vastXml, - vastUrl: serverBid.seatbid[0].bid[0].vastUrl, - ttl: 3600 - }); - } - - return bid; -} - -registerBidder(spec); diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js deleted file mode 100644 index 134dd0b4fc9..00000000000 --- a/modules/getintentBidAdapter.js +++ /dev/null @@ -1,160 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { isInteger } from '../src/utils.js'; - -const BIDDER_CODE = 'getintent'; -const IS_NET_REVENUE = true; -const BID_HOST = 'px.adhigh.net'; -const BID_BANNER_PATH = '/rtb/direct_banner'; -const BID_VIDEO_PATH = '/rtb/direct_vast'; -const BID_RESPONSE_TTL_SEC = 360; -const VIDEO_PROPERTIES = [ - 'protocols', 'mimes', 'min_dur', 'max_dur', 'min_btr', 'max_btr', 'vi_format', 'api', 'skippable' -]; -const OPTIONAL_PROPERTIES = [ - 'cur', 'floor', 'sid' -]; - -export const spec = { - code: BIDDER_CODE, - aliases: ['getintentAdapter'], - supportedMediaTypes: ['video', 'banner'], - - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid to validate. - * @return {boolean} True if this is a valid bid, and false otherwise. - * */ - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.pid && bid.params.tid); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} bidRequests - an array of bids. - * @return ServerRequest[] - */ - buildRequests: function(bidRequests) { - return bidRequests.map(bidRequest => { - let giBidRequest = buildGiBidRequest(bidRequest); - return { - method: 'GET', - url: buildUrl(giBidRequest), - data: giBidRequest, - }; - }); - }, - - /** - * Callback for bids, after the call to DSP completes. - * Parse the response from the server into a list of bids. - * - * @param {object} serverResponse A response from the GetIntent's server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse) { - let responseBody = serverResponse.body; - const bids = []; - if (responseBody && responseBody.no_bid !== 1) { - let size = parseSize(responseBody.size); - let bid = { - requestId: responseBody.bid_id, - ttl: BID_RESPONSE_TTL_SEC, - netRevenue: IS_NET_REVENUE, - currency: responseBody.currency, - creativeId: responseBody.creative_id, - cpm: responseBody.cpm, - width: size[0], - height: size[1] - }; - if (responseBody.vast_url) { - bid.mediaType = 'video'; - bid.vastUrl = responseBody.vast_url; - } else { - bid.mediaType = 'banner'; - bid.ad = responseBody.ad; - } - bids.push(bid); - } - return bids; - } - -} - -function buildUrl(bid) { - return 'https://' + BID_HOST + (bid.is_video ? BID_VIDEO_PATH : BID_BANNER_PATH); -} - -/** - * Builds GI bid request from BidRequest. - * - * @param {BidRequest} bidRequest. - * @return {object} GI bid request. - * */ -function buildGiBidRequest(bidRequest) { - let giBidRequest = { - bid_id: bidRequest.bidId, - pid: bidRequest.params.pid, // required - tid: bidRequest.params.tid, // required - known: bidRequest.params.known || 1, - is_video: bidRequest.mediaType === 'video', - resp_type: 'JSON', - provider: 'direct.prebidjs' - }; - if (bidRequest.sizes) { - giBidRequest.size = produceSize(bidRequest.sizes); - } - addVideo(bidRequest.params.video, giBidRequest); - addOptional(bidRequest.params, giBidRequest, OPTIONAL_PROPERTIES); - return giBidRequest; -} - -function addVideo(video, giBidRequest) { - if (giBidRequest.is_video && video) { - for (let i = 0, l = VIDEO_PROPERTIES.length; i < l; i++) { - let key = VIDEO_PROPERTIES[i]; - if (video.hasOwnProperty(key)) { - giBidRequest[key] = Array.isArray(video[key]) ? video[key].join(',') : video[key]; - } - } - } -} - -function addOptional(params, request, props) { - for (let i = 0; i < props.length; i++) { - if (params.hasOwnProperty(props[i])) { - request[props[i]] = params[props[i]]; - } - } -} - -/** - * @param {String} s The string representing a size (e.g. "300x250"). - * @return {Number[]} An array with two elements: [width, height] (e.g.: [300, 250]). - * */ -function parseSize(s) { - return s.split('x').map(Number); -} - -/** - * @param {Array} sizes An array of sizes/numbers to be joined into single string. - * May be an array (e.g. [300, 250]) or array of arrays (e.g. [[300, 250], [640, 480]]. - * @return {String} The string with sizes, e.g. array of sizes [[50, 50], [80, 80]] becomes "50x50,80x80" string. - * */ -function produceSize (sizes) { - function sizeToStr(s) { - if (Array.isArray(s) && s.length === 2 && isInteger(s[0]) && isInteger(s[1])) { - return s.join('x'); - } else { - throw "Malformed parameter 'sizes'"; - } - } - if (Array.isArray(sizes) && Array.isArray(sizes[0])) { - return sizes.map(sizeToStr).join(','); - } else { - return sizeToStr(sizes); - } -} - -registerBidder(spec); diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js deleted file mode 100644 index 3469c897a6a..00000000000 --- a/modules/gnetBidAdapter.js +++ /dev/null @@ -1,101 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'gnet'; -const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [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. - */ - isBidRequestValid: function (bid) { - return !!(bid.params.websiteId && bid.params.externalId); - }, - - /** - * 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 bidRequests = []; - const referer = bidderRequest.refererInfo.referer; - - utils._each(validBidRequests, (request) => { - const data = {}; - - data.referer = referer; - data.adUnitCode = request.adUnitCode; - data.bidId = request.bidId; - data.transactionId = request.transactionId; - - data.sizes = utils.parseSizesInput(request.sizes); - - data.params = request.params; - - const payloadString = JSON.stringify(data); - - bidRequests.push({ - method: 'POST', - url: ENDPOINT, - mode: 'no-cors', - options: { - withCredentials: false, - }, - data: payloadString - }); - }); - - return bidRequests; - }, - - /** - * 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, requests) { - if (typeof serverResponse !== 'object') { - return []; - } - - const res = serverResponse && serverResponse.body; - - if (utils.isEmpty(res)) { - return []; - } - - if (res.bids) { - const bids = []; - utils._each(res.bids, (bidData) => { - const bid = { - requestId: bidData.bidId, - cpm: bidData.cpm, - currency: bidData.currency, - width: bidData.width, - height: bidData.height, - ad: bidData.ad, - ttl: 300, - creativeId: bidData.creativeId, - netRevenue: true, - }; - bids.push(bid); - }); - - return bids; - } - - return []; - }, -}; - -registerBidder(spec); diff --git a/modules/haxmediaBidAdapter.js b/modules/haxmediaBidAdapter.js deleted file mode 100644 index c4ce2eb3663..00000000000 --- a/modules/haxmediaBidAdapter.js +++ /dev/null @@ -1,107 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'haxmedia'; -const AD_URL = 'https://balancer.haxmedia.io/?c=o&m=multi'; - -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); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - - const placements = []; - const request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - '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 || {}, - }; - const mediaType = bid.mediaTypes - - if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { - placement.sizes = mediaType[BANNER].sizes; - placement.traffic = BANNER; - } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { - placement.wPlayer = mediaType[VIDEO].playerSize[0]; - placement.hPlayer = mediaType[VIDEO].playerSize[1]; - placement.traffic = VIDEO; - } else if (mediaType && mediaType[NATIVE]) { - placement.native = mediaType[NATIVE]; - placement.traffic = NATIVE; - } - 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)) { - response.push(resItem); - } - } - return response; - }, -}; - -registerBidder(spec); diff --git a/modules/hpmdnetworkBidAdapter.js b/modules/hpmdnetworkBidAdapter.js deleted file mode 100644 index 5cc28ab6362..00000000000 --- a/modules/hpmdnetworkBidAdapter.js +++ /dev/null @@ -1,96 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'hpmdnetwork'; -const BIDDER_CODE_ALIAS = 'hpmd'; -const HPMDNETWORK_HOST = 'https://banner.hpmdnetwork.ru/bidder/request'; -const DEFAULT_TTL = 300; -const DEFAULT_CURRENCY = 'RUB'; - -export const spec = { - code: BIDDER_CODE, - aliases: [ BIDDER_CODE_ALIAS ], - supportedMediaTypes: [ BANNER ], - isBidRequestValid: isBidRequestValid, - buildRequests: buildRequests, - interpretResponse: interpretResponse, -}; - -function isBidRequestValid(bid) { - const { placementId } = bid.params; - return !!placementId; -} - -function buildRequests(validBidRequests, bidderRequest) { - const payload = {}; - payload.places = []; - - validBidRequests.forEach((bidRequest) => { - const place = { - id: bidRequest.bidId, - placementId: bidRequest.params.placementId + '', - }; - payload.places.push(place); - }); - - payload.url = bidderRequest.refererInfo.referer; - payload.settings = { currency: DEFAULT_CURRENCY }; - - return { - method: 'POST', - url: HPMDNETWORK_HOST, - data: payload, - }; -} - -function interpretResponse(serverResponse) { - const { body } = serverResponse; - const bidResponses = []; - - if (body.bids) { - body.bids.forEach((bid) => { - const size = getCreativeSize(bid); - const bidResponse = { - requestId: bid.id, - cpm: bid.cpm, - ad: wrapDisplayUrl(bid.displayUrl), - width: size.width, - height: size.height, - creativeId: bid.creativeId || generateRandomInt(), - currency: bid.currency || DEFAULT_CURRENCY, - netRevenue: true, - ttl: bid.ttl || DEFAULT_TTL, - }; - - bidResponses.push(bidResponse); - }); - } - - return bidResponses; -} - -function wrapDisplayUrl(displayUrl) { - return ``; -} - -function getCreativeSize(creativeSize) { - const size = { - width: 1, - height: 1, - }; - - if (!!creativeSize.width && creativeSize.width !== -1) { - size.width = creativeSize.width; - } - if (!!creativeSize.height && creativeSize.height !== -1) { - size.height = creativeSize.height; - } - - return size; -} - -function generateRandomInt() { - return Math.random().toString(16).substring(2); -} - -registerBidder(spec); diff --git a/modules/iasBidAdapter.js b/modules/iasBidAdapter.js deleted file mode 100644 index 733c123e769..00000000000 --- a/modules/iasBidAdapter.js +++ /dev/null @@ -1,134 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'ias'; - -const otherBidIds = []; - -function isBidRequestValid(bid) { - const { pubId, adUnitPath } = bid.params; - return !!(pubId && adUnitPath); -} - -/** - * Converts GPT-style size array into a string - * @param {Array} sizes: list of GPT-style sizes, e.g. [[300, 250], [300, 300]] - * @return {String} a string containing sizes, e.g. '[300.250,300.300]' - */ -function stringifySlotSizes(sizes) { - let result = ''; - if (utils.isArray(sizes)) { - result = sizes.reduce((acc, size) => { - acc.push(size.join('.')); - return acc; - }, []); - result = '[' + result.join(',') + ']'; - } - return result; -} - -function stringifySlot(bidRequest) { - const id = bidRequest.adUnitCode; - const ss = stringifySlotSizes(bidRequest.sizes); - const p = bidRequest.params.adUnitPath; - const slot = { id, ss, p }; - const keyValues = utils.getKeys(slot).map(function(key) { - return [key, slot[key]].join(':'); - }); - return '{' + keyValues.join(',') + '}'; -} - -function stringifyWindowSize() { - return [ window.innerWidth || -1, window.innerHeight || -1 ].join('.'); -} - -function stringifyScreenSize() { - return [ (window.screen && window.screen.width) || -1, (window.screen && window.screen.height) || -1 ].join('.'); -} - -function buildRequests(bidRequests) { - const IAS_HOST = 'https://pixel.adsafeprotected.com/services/pub'; - const anId = bidRequests[0].params.pubId; - - let queries = []; - queries.push(['anId', anId]); - queries = queries.concat(bidRequests.reduce(function(acc, request) { - acc.push(['slot', stringifySlot(request)]); - return acc; - }, [])); - - queries.push(['wr', stringifyWindowSize()]); - queries.push(['sr', stringifyScreenSize()]); - queries.push(['url', encodeURIComponent(window.location.href)]); - - const queryString = encodeURI(queries.map(qs => qs.join('=')).join('&')); - - bidRequests.forEach(function (request) { - if (bidRequests[0].bidId != request.bidId) { - otherBidIds.push(request.bidId); - } - }); - - return { - method: 'GET', - url: IAS_HOST, - data: queryString, - bidRequest: bidRequests[0] - }; -} - -function getPageLevelKeywords(response) { - let result = {}; - shallowMerge(result, response.brandSafety); - result.fr = response.fr; - result.custom = response.custom; - return result; -} - -function shallowMerge(dest, src) { - utils.getKeys(src).reduce((dest, srcKey) => { - dest[srcKey] = src[srcKey]; - return dest; - }, dest); -} - -function interpretResponse(serverResponse, request) { - const iasResponse = serverResponse.body; - const bidResponses = []; - - // Keys in common bid response are not used; - // Necessary to get around with prebid's common bid response check - const commonBidResponse = { - requestId: request.bidRequest.bidId, - cpm: 0.01, - width: 100, - height: 200, - creativeId: 434, - dealId: 42, - currency: 'USD', - netRevenue: true, - ttl: 360 - }; - - shallowMerge(commonBidResponse, getPageLevelKeywords(iasResponse)); - commonBidResponse.slots = iasResponse.slots; - bidResponses.push(commonBidResponse); - - otherBidIds.forEach(function (bidId) { - var otherResponse = Object.assign({}, commonBidResponse); - otherResponse.requestId = bidId; - bidResponses.push(otherResponse); - }); - - return bidResponses; -} - -export const spec = { - code: BIDDER_CODE, - aliases: [], - isBidRequestValid: isBidRequestValid, - buildRequests: buildRequests, - interpretResponse: interpretResponse -}; - -registerBidder(spec); diff --git a/modules/iasBidAdapter.md b/modules/iasBidAdapter.md deleted file mode 100644 index 3224fbf4a26..00000000000 --- a/modules/iasBidAdapter.md +++ /dev/null @@ -1,30 +0,0 @@ -# Overview - -``` -Module Name: Integral Ad Science(IAS) Bidder Adapter -Module Type: Bidder Adapter -Maintainer: kat@integralads.com -``` - -# Description - -This module is an integration with prebid.js with an IAS product, pet.js. It is not a bidder per se but works in a similar way: retrieve data that publishers might be interested in setting keyword targeting. - -# Test Parameters -``` - var adUnits = [ - { - code: 'ias-dfp-test-async', - sizes: [[300, 250]], // a display size - bids: [ - { - bidder: "ias", - params: { - pubId: '99', - adUnitPath: '/57514611/news.com' - } - } - ] - } - ]; -``` diff --git a/modules/imonomyBidAdapter.js b/modules/imonomyBidAdapter.js deleted file mode 100644 index b9205943f65..00000000000 --- a/modules/imonomyBidAdapter.js +++ /dev/null @@ -1,130 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'imonomy'; -const ENDPOINT = 'https://b.imonomy.com/openrtb/hb/00000'; -const USYNCURL = 'https://b.imonomy.com/UserMatching/b/'; - -export const spec = { - code: BIDDER_CODE, - - /** - * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid - * - * @param {BidRequest} bid The bid params to validate. - * @return {boolean} True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: bid => { - return !!(bid && bid.params && bid.params.placementId && bid.params.hbid); - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests an array of bids - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: validBidRequests => { - const tags = validBidRequests.map(bid => { - // map each bid id to bid object to retrieve adUnit code in callback - let tag = { - uuid: bid.bidId, - sizes: bid.sizes, - trid: bid.transactionId, - hbid: bid.params.hbid, - placementid: bid.params.placementId - }; - - // add floor price if specified (not mandatory) - if (bid.params.floorPrice) { - tag.floorprice = bid.params.floorPrice; - } - - return tag; - }); - - // Imonomy server config - const time = new Date().getTime(); - const kbConf = { - ts_as: time, - hb_placements: [], - hb_placement_bidids: {}, - hb_floors: {}, - cb: _generateCb(time), - tz: new Date().getTimezoneOffset(), - }; - - validBidRequests.forEach(bid => { - kbConf.hdbdid = kbConf.hdbdid || bid.params.hbid; - kbConf.encode_bid = kbConf.encode_bid || bid.params.encode_bid; - kbConf.hb_placement_bidids[bid.params.placementId] = bid.bidId; - if (bid.params.floorPrice) { - kbConf.hb_floors[bid.params.placementId] = bid.params.floorPrice; - } - kbConf.hb_placements.push(bid.params.placementId); - }); - - let payload = {}; - if (!utils.isEmpty(tags)) { - payload = { bids: [...tags], kbConf }; - } - - let endpointToUse = ENDPOINT; - if (kbConf.hdbdid) { - endpointToUse = endpointToUse.replace('00000', kbConf.hdbdid); - } - - return { - method: 'POST', - url: endpointToUse, - data: JSON.stringify(payload) - }; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} response A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: (response) => { - const bidResponses = []; - if (response && response.body && response.body.bids) { - response.body.bids.forEach(bid => { - // The bid ID. Used to tie this bid back to the request. - if (bid.uuid) { - bid.requestId = bid.uuid; - } else { - utils.logError('No uuid for bid'); - } - // The creative payload of the returned bid. - if (bid.creative) { - bid.ad = bid.creative; - } else { - utils.logError('No creative for bid'); - } - bidResponses.push(bid); - }); - } - return bidResponses; - }, - /** - * Register User Sync. - */ - getUserSyncs: syncOptions => { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: USYNCURL - }]; - } - } -}; - -/** -* Generated cache baster value to be sent to bid server -* @param {*} time current time to use for creating cb. -*/ -function _generateCb(time) { - return Math.floor((time % 65536) + (Math.floor(Math.random() * 65536) * 65536)); -} - -registerBidder(spec); diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js deleted file mode 100644 index b649b5a8a73..00000000000 --- a/modules/impactifyBidAdapter.js +++ /dev/null @@ -1,260 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import {ajax} from '../src/ajax.js'; - -const BIDDER_CODE = 'impactify'; -const BIDDER_ALIAS = ['imp']; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_VIDEO_WIDTH = 640; -const DEFAULT_VIDEO_HEIGHT = 480; -const ORIGIN = 'https://sonic.impactify.media'; -const LOGGER_URI = 'https://logger.impactify.media'; -const AUCTIONURI = '/bidder'; -const COOKIESYNCURI = '/static/cookie_sync.html'; -const GVLID = 606; -const GETCONFIG = config.getConfig; - -const getDeviceType = () => { - // OpenRTB Device type - if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { - return 5; - } - if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { - return 4; - } - return 2; -} - -const createOpenRtbRequest = (validBidRequests, bidderRequest) => { - // Create request and set imp bids inside - let request = { - id: bidderRequest.auctionId, - validBidRequests, - cur: [DEFAULT_CURRENCY], - imp: [] - }; - - // Force impactify debugging parameter - if (window.localStorage.getItem('_im_db_bidder') == 3) { - request.test = 3; - } - - // Set device/user/site - if (!request.device) request.device = {}; - if (!request.site) request.site = {}; - request.device = { - w: window.innerWidth, - h: window.innerHeight, - devicetype: getDeviceType(), - ua: navigator.userAgent, - js: 1, - dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, - language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', - }; - request.site = {page: bidderRequest.refererInfo.referer}; - - // Handle privacy settings for GDPR/CCPA/COPPA - if (bidderRequest.gdprConsent) { - let gdprApplies = 0; - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - utils.deepSetValue(request, 'regs.ext.gdpr', gdprApplies); - utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - } - - if (bidderRequest.uspConsent) { - utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); - this.syncStore.uspConsent = bidderRequest.uspConsent; - } - - if (GETCONFIG('coppa') == true) utils.deepSetValue(request, 'regs.coppa', 1); - - if (bidderRequest.uspConsent) { - utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - // Set buyer uid - utils.deepSetValue(request, 'user.buyeruid', utils.generateUUID()); - - // Create imps with bids - validBidRequests.forEach((bid) => { - let imp = { - id: bid.bidId, - bidfloor: bid.params.bidfloor ? bid.params.bidfloor : 0, - ext: { - impactify: { - appId: bid.params.appId, - format: bid.params.format, - style: bid.params.style - }, - }, - video: { - playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT], - context: 'outstream', - mimes: ['video/mp4'], - }, - }; - if (bid.params.container) { - imp.ext.impactify.container = bid.params.container; - } - request.imp.push(imp); - }); - - return request; -}; - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: ['video'], - aliases: BIDDER_ALIAS, - /** - * 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.appId || typeof bid.params.appId != 'string' || !bid.params.format || typeof bid.params.format != 'string' || !bid.params.style || typeof bid.params.style != 'string') { - return false; - } - if (bid.params.format != 'screen' && bid.params.format != 'display') { - return false; - } - if (bid.params.style != 'inline' && bid.params.style != 'impact' && bid.params.style != 'static') { - return false; - } - - return true; - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @param {bidderRequest} - the bidding request - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - // Create a clean openRTB request - let request = createOpenRtbRequest(validBidRequests, bidderRequest); - - return { - method: 'POST', - url: ORIGIN + AUCTIONURI, - data: JSON.stringify(request), - }; - }, - /** - * 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; - let bidResponses = []; - - if (!serverBody) { - return bidResponses; - } - - if (!serverBody.seatbid || !serverBody.seatbid.length) { - return []; - } - - serverBody.seatbid.forEach((seatbid) => { - if (seatbid.bid.length) { - bidResponses = [ - ...bidResponses, - ...seatbid.bid - .filter((bid) => bid.price > 0) - .map((bid) => ({ - id: bid.id, - requestId: bid.impid, - cpm: bid.price, - currency: serverBody.cur, - netRevenue: true, - ad: bid.adm, - width: bid.w || 0, - height: bid.h || 0, - ttl: 300, - creativeId: bid.crid || 0, - hash: bid.hash, - expiry: bid.expiry - })), - ]; - } - }); - - 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 (!serverResponses || serverResponses.length === 0) { - return []; - } - - if (!syncOptions.iframeEnabled) { - return []; - } - - let params = ''; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr_consent=${gdprConsent.consentString}`; - } - } - - if (uspConsent) { - params += `${params ? '&' : '?'}us_privacy=${encodeURIComponent(uspConsent)}`; - } - - if (document.location.search.match(/pbs_debug=true/)) params += `&pbs_debug=true`; - - return [{ - type: 'iframe', - url: ORIGIN + COOKIESYNCURI + params - }]; - }, - - /** - * 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) { - ajax(`${LOGGER_URI}/log/bidder/won`, null, JSON.stringify(bid), { - method: 'POST', - contentType: 'application/json' - }); - - return true; - }, - - /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ - onTimeout: function(data) { - ajax(`${LOGGER_URI}/log/bidder/timeout`, null, JSON.stringify(data[0]), { - method: 'POST', - contentType: 'application/json' - }); - - return true; - } -}; -registerBidder(spec); diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js deleted file mode 100644 index aae79818daf..00000000000 --- a/modules/innityBidAdapter.js +++ /dev/null @@ -1,58 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'innity'; -const ENDPOINT = 'https://as.innity.com/synd/'; - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.pub && bid.params.zone); - }, - buildRequests: function(validBidRequests, bidderRequest) { - let refererInfo = ''; - if (bidderRequest && bidderRequest.refererInfo) { - refererInfo = bidderRequest.refererInfo.referer || ''; - } - return validBidRequests.map(bidRequest => { - let parseSized = utils.parseSizesInput(bidRequest.sizes); - let arrSize = parseSized[0].split('x'); - return { - method: 'GET', - url: ENDPOINT, - data: { - cb: utils.timestamp(), - ver: 2, - hb: 1, - output: 'js', - pub: bidRequest.params.pub, - zone: bidRequest.params.zone, - url: encodeURIComponent(refererInfo), - width: arrSize[0], - height: arrSize[1], - vpw: window.screen.width, - vph: window.screen.height, - callback: 'json', - callback_uid: bidRequest.bidId, - auction: bidRequest.auctionId, - }, - }; - }); - }, - interpretResponse: function(serverResponse, request) { - const res = serverResponse.body; - const bidResponse = { - requestId: res.callback_uid, - cpm: parseFloat(res.cpm) / 100, - width: res.width, - height: res.height, - creativeId: res.creative_id, - currency: 'USD', - netRevenue: true, - ttl: 60, - ad: '' + res.tag, - }; - return [bidResponse]; - } -} -registerBidder(spec); diff --git a/modules/ipromBidAdapter.js b/modules/ipromBidAdapter.js deleted file mode 100644 index fed8ca2ebb0..00000000000 --- a/modules/ipromBidAdapter.js +++ /dev/null @@ -1,79 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'iprom'; -const ENDPOINT_URL = 'https://core.iprom.net/programmatic'; -const VERSION = 'v1.0.1'; -const DEFAULT_CURRENCY = 'EUR'; -const DEFAULT_NETREVENUE = true; -const DEFAULT_TTL = 360; - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function ({ bidder, params = {} } = {}) { - // id parameter checks - if (!params.id) { - utils.logError(`${bidder}: Parameter 'id' missing`); - return false; - } else if (typeof params.id !== 'string') { - utils.logError(`${bidder}: Parameter 'id' needs to be a string`); - return false; - } - // dimension parameter checks - if (!params.dimension) { - utils.logError(`${bidder}: Required parameter 'dimension' missing`); - return false; - } else if (typeof params.dimension !== 'string') { - utils.logError(`${bidder}: Parameter 'dimension' needs to be a string`); - return false; - } - - return true; - }, - - buildRequests: function (validBidRequests, bidderRequest) { - const payload = { - bids: validBidRequests, - referer: bidderRequest.refererInfo, - version: VERSION - }; - const payloadString = JSON.stringify(payload); - - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString - }; - }, - - interpretResponse: function (serverResponse, request) { - let bids = serverResponse.body; - - const bidResponses = []; - - bids.forEach(bid => { - const b = { - ad: bid.ad, - requestId: bid.requestId, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - creativeId: bid.creativeId, - currency: bid.currency || DEFAULT_CURRENCY, - netRevenue: bid.netRevenue || DEFAULT_NETREVENUE, - ttl: bid.ttl || DEFAULT_TTL, - meta: {}, - }; - - if (bid.aDomains && bid.aDomains.length) { - b.meta.advertiserDomains = bid.aDomains; - } - - bidResponses.push(b); - }); - - return bidResponses; - }, -} - -registerBidder(spec); diff --git a/modules/ironsourceBidAdapter.js b/modules/ironsourceBidAdapter.js deleted file mode 100644 index 5b8531d7a85..00000000000 --- a/modules/ironsourceBidAdapter.js +++ /dev/null @@ -1,251 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; - -const SUPPORTED_AD_TYPES = [VIDEO]; -const BIDDER_CODE = 'ironsource'; -const BIDDER_VERSION = '4.0.0'; -const TTL = 360; -const SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; -const MODES = { - PRODUCTION: 'hb', - TEST: 'hb-test' -} -const SUPPORTED_SYNC_METHODS = { - IFRAME: 'iframe', - PIXEL: 'pixel' -} - -export const spec = { - code: BIDDER_CODE, - version: BIDDER_VERSION, - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid: function(bidRequest) { - return !!(bidRequest.params.isOrg); - }, - buildRequests: function (bidRequests, bidderRequest) { - if (bidRequests.length === 0) { - return []; - } - - const requests = []; - - bidRequests.forEach(bid => { - requests.push(buildVideoRequest(bid, bidderRequest)); - }); - - return requests; - }, - interpretResponse: function({body}) { - const bidResponses = []; - - const bidResponse = { - requestId: body.requestId, - cpm: body.cpm, - width: body.width, - height: body.height, - creativeId: body.requestId, - currency: body.currency, - netRevenue: body.netRevenue, - ttl: body.ttl || TTL, - vastXml: body.vastXml, - mediaType: VIDEO - }; - - bidResponses.push(bidResponse); - - return bidResponses; - }, - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - for (const response of serverResponses) { - if (syncOptions.iframeEnabled && response.body.userSyncURL) { - syncs.push({ - type: 'iframe', - url: response.body.userSyncURL - }); - } - if (syncOptions.pixelEnabled && utils.isArray(response.body.userSyncPixels)) { - const pixels = response.body.userSyncPixels.map(pixel => { - return { - type: 'image', - url: pixel - } - }) - syncs.push(...pixels) - } - } - return syncs; - } -}; - -registerBidder(spec); - -/** - * Build the video request - * @param bid {bid} - * @param bidderRequest {bidderRequest} - * @returns {Object} - */ -function buildVideoRequest(bid, bidderRequest) { - const sellerParams = generateParameters(bid, bidderRequest); - const {params} = bid; - return { - method: 'GET', - url: getEndpoint(params.testMode), - data: sellerParams - }; -} - -/** - * Get the the ad size from the bid - * @param bid {bid} - * @returns {Array} - */ -function getSizes(bid) { - if (utils.deepAccess(bid, 'mediaTypes.video.sizes')) { - return bid.mediaTypes.video.sizes[0]; - } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - return bid.sizes[0]; - } - return []; -} - -/** - * Get schain string value - * @param schainObject {Object} - * @returns {string} - */ -function getSupplyChain(schainObject) { - if (utils.isEmpty(schainObject)) { - return ''; - } - let scStr = `${schainObject.ver},${schainObject.complete}`; - schainObject.nodes.forEach((node) => { - scStr += '!'; - scStr += `${getEncodedValIfNotEmpty(node.asi)},`; - scStr += `${getEncodedValIfNotEmpty(node.sid)},`; - scStr += `${getEncodedValIfNotEmpty(node.hp)},`; - scStr += `${getEncodedValIfNotEmpty(node.rid)},`; - scStr += `${getEncodedValIfNotEmpty(node.name)},`; - scStr += `${getEncodedValIfNotEmpty(node.domain)}`; - }); - return scStr; -} - -/** - * Get encoded node value - * @param val {string} - * @returns {string} - */ -function getEncodedValIfNotEmpty(val) { - return !utils.isEmpty(val) ? encodeURIComponent(val) : ''; -} - -/** - * Get preferred user-sync method based on publisher configuration - * @param bidderCode {string} - * @returns {string} - */ -function getAllowedSyncMethod(filterSettings, bidderCode) { - const iframeConfigsToCheck = ['all', 'iframe']; - const pixelConfigToCheck = 'image'; - if (filterSettings && iframeConfigsToCheck.some(config => isSyncMethodAllowed(filterSettings[config], bidderCode))) { - return SUPPORTED_SYNC_METHODS.IFRAME; - } - if (!filterSettings || !filterSettings[pixelConfigToCheck] || isSyncMethodAllowed(filterSettings[pixelConfigToCheck], bidderCode)) { - return SUPPORTED_SYNC_METHODS.PIXEL; - } -} - -/** - * Check if sync rule is supported - * @param syncRule {Object} - * @param bidderCode {string} - * @returns {boolean} - */ -function isSyncMethodAllowed(syncRule, bidderCode) { - if (!syncRule) { - return false; - } - const isInclude = syncRule.filter === 'include'; - const bidders = utils.isArray(syncRule.bidders) ? syncRule.bidders : [bidderCode]; - return isInclude && utils.contains(bidders, bidderCode); -} - -/** - * Get the seller endpoint - * @param testMode {boolean} - * @returns {string} - */ -function getEndpoint(testMode) { - return testMode - ? SELLER_ENDPOINT + MODES.TEST - : SELLER_ENDPOINT + MODES.PRODUCTION; -} - -/** - * Generate query parameters for the request - * @param bid {bid} - * @param bidderRequest {bidderRequest} - * @returns {Object} - */ -function generateParameters(bid, bidderRequest) { - const timeout = config.getConfig('bidderTimeout'); - const { syncEnabled, filterSettings } = config.getConfig('userSync'); - const [ width, height ] = getSizes(bid); - const { params } = bid; - const { bidderCode } = bidderRequest; - const domain = window.location.hostname; - - const requestParams = { - auction_start: utils.timestamp(), - ad_unit_code: utils.getBidIdParameter('adUnitCode', bid), - tmax: timeout, - width: width, - height: height, - publisher_id: params.isOrg, - floor_price: params.floorPrice, - ua: navigator.userAgent, - bid_id: utils.getBidIdParameter('bidId', bid), - bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), - transaction_id: utils.getBidIdParameter('transactionId', bid), - session_id: params.sessionId || utils.getBidIdParameter('auctionId', bid), - is_wrapper: !!params.isWrapper, - publisher_name: domain, - site_domain: domain, - bidder_version: BIDDER_VERSION - }; - - if (syncEnabled) { - const allowedSyncMethod = getAllowedSyncMethod(filterSettings, bidderCode); - if (allowedSyncMethod) { - requestParams.cs_method = allowedSyncMethod; - } - } - - if (bidderRequest.uspConsent) { - requestParams.us_privacy = bidderRequest.uspConsent; - } - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - requestParams.gdpr = bidderRequest.gdprConsent.gdprApplies; - requestParams.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - - if (params.ifa) { - requestParams.ifa = params.ifa; - } - - if (bid.schain) { - requestParams.schain = getSupplyChain(bid.schain); - } - - if (bidderRequest && bidderRequest.refererInfo) { - requestParams.referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - requestParams.page_url = config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); - } - - return requestParams; -} diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index d326cbaf9b1..7512619019a 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -42,7 +42,6 @@ const PROVIDERS = [ 'merkleId', 'parrableId', 'connectid', - 'sharedid', 'tapadId', 'quantcastId', 'pubcid', diff --git a/modules/jcmBidAdapter.js b/modules/jcmBidAdapter.js deleted file mode 100644 index c8d2f8bdd52..00000000000 --- a/modules/jcmBidAdapter.js +++ /dev/null @@ -1,89 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -const BIDDER_CODE = 'jcm'; -const URL = 'https://media.adfrontiers.com/pq' - -export const spec = { - code: BIDDER_CODE, - aliases: ['jcarter'], - isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.siteId && bid.bidId); - }, - - buildRequests: function(validBidRequests) { - var BidRequestStr = { - bids: [] - }; - - for (var i = 0; i < validBidRequests.length; i++) { - var adSizes = ''; - var bid = validBidRequests[i]; - for (var x = 0; x < bid.sizes.length; x++) { - adSizes += utils.parseGPTSingleSizeArray(bid.sizes[x]); - if (x !== (bid.sizes.length - 1)) { - adSizes += ','; - } - } - - BidRequestStr.bids.push({ - 'callbackId': bid.bidId, - 'siteId': bid.params.siteId, - 'adSizes': adSizes, - }); - } - - var JSONStr = JSON.stringify(BidRequestStr); - var dataStr = 't=hb&ver=1.0&compact=true&bids=' + encodeURIComponent(JSONStr); - - return { - method: 'GET', - url: URL, - data: dataStr - } - }, - - interpretResponse: function(serverResponse) { - const bidResponses = []; - serverResponse = serverResponse.body; - // loop through serverResponses - if (serverResponse) { - if (serverResponse.bids) { - var bids = serverResponse.bids; - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - const bidResponse = { - requestId: bid.callbackId, - bidderCode: spec.code, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - creativeId: bid.creativeId, - currency: 'USD', - netRevenue: bid.netRevenue, - ttl: bid.ttl, - ad: decodeURIComponent(bid.ad.replace(/\+/g, '%20')) - }; - bidResponses.push(bidResponse); - }; - }; - } - return bidResponses; - }, - - getUserSyncs: function(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://media.adfrontiers.com/hb/jcm_usersync.html' - }]; - } - if (syncOptions.image) { - return [{ - type: 'image', - url: 'https://media.adfrontiers.com/hb/jcm_usersync.png' - }]; - } - } -} - -registerBidder(spec); diff --git a/modules/komoonaBidAdapter.js b/modules/komoonaBidAdapter.js deleted file mode 100644 index 6adc0eb1984..00000000000 --- a/modules/komoonaBidAdapter.js +++ /dev/null @@ -1,121 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'komoona'; -const ENDPOINT = 'https://bidder.komoona.com/v1/GetSBids'; -const USYNCURL = 'https://s.komoona.com/sync/usync.html'; - -export const spec = { - code: BIDDER_CODE, - - /** - * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: bid => { - return !!(bid && bid.params && bid.params.placementId && bid.params.hbid); - }, - /** - * 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: validBidRequests => { - const tags = validBidRequests.map(bid => { - // map each bid id to bid object to retrieve adUnit code in callback - let tag = { - uuid: bid.bidId, - sizes: bid.sizes, - trid: bid.transactionId, - hbid: bid.params.hbid, - placementid: bid.params.placementId - }; - - // add floor price if specified (not mandatory) - if (bid.params.floorPrice) { - tag.floorprice = bid.params.floorPrice; - } - - return tag; - }); - - // Komoona server config - const time = new Date().getTime(); - const kbConf = { - ts_as: time, - hb_placements: [], - hb_placement_bidids: {}, - hb_floors: {}, - cb: _generateCb(time), - tz: new Date().getTimezoneOffset(), - }; - - validBidRequests.forEach(bid => { - kbConf.hdbdid = kbConf.hdbdid || bid.params.hbid; - kbConf.encode_bid = kbConf.encode_bid || bid.params.encode_bid; - kbConf.hb_placement_bidids[bid.params.placementId] = bid.bidId; - if (bid.params.floorPrice) { - kbConf.hb_floors[bid.params.placementId] = bid.params.floorPrice; - } - kbConf.hb_placements.push(bid.params.placementId); - }); - - let payload = {}; - if (!utils.isEmpty(tags)) { - payload = { bids: [...tags], kbConf: kbConf }; - } - - return { - method: 'POST', - url: ENDPOINT, - data: JSON.stringify(payload) - }; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} response A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: (response, request) => { - const bidResponses = []; - try { - if (response.body && response.body.bids) { - response.body.bids.forEach(bid => { - // The bid ID. Used to tie this bid back to the request. - bid.requestId = bid.uuid; - // The creative payload of the returned bid. - bid.ad = bid.creative; - bidResponses.push(bid); - }); - } - } catch (error) { - utils.logError(error); - } - return bidResponses; - }, - /** - * Register User Sync. - */ - getUserSyncs: syncOptions => { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: USYNCURL - }]; - } - } -}; - -/** -* Generated cache baster value to be sent to bid server -* @param {*} time current time to use for creating cb. -*/ -function _generateCb(time) { - return Math.floor((time % 65536) + (Math.floor(Math.random() * 65536) * 65536)); -} - -registerBidder(spec); diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js deleted file mode 100644 index de1cce503e3..00000000000 --- a/modules/krushmediaBidAdapter.js +++ /dev/null @@ -1,123 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'krushmedia'; -const AD_URL = 'https://ads4.krushmedia.com/?c=rtb&m=hb'; -const SYNC_URL = 'https://cs.krushmedia.com/html?src=pbjs' - -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); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.key))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - - const placements = []; - const request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - '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 = { - key: bid.params.key, - bidId: bid.bidId, - traffic: bid.params.traffic || BANNER, - schain: bid.schain || {}, - }; - - if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - placement.sizes = bid.mediaTypes[BANNER].sizes; - } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { - placement.wPlayer = bid.mediaTypes[VIDEO].playerSize[0]; - placement.hPlayer = bid.mediaTypes[VIDEO].playerSize[1]; - } else if (bid.mediaTypes && bid.mediaTypes[NATIVE]) { - placement.native = bid.mediaTypes[NATIVE]; - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = SYNC_URL - 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}`; - } - return [{ - type: 'iframe', - url: syncUrl - }]; - } -}; - -registerBidder(spec); diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js deleted file mode 100644 index 8f6ea53ecce..00000000000 --- a/modules/kubientBidAdapter.js +++ /dev/null @@ -1,111 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'kubient'; -const END_POINT = 'https://kssp.kbntx.ch/pbjs'; -const VERSION = '1.0'; -const VENDOR_ID = 794; -export const spec = { - code: BIDDER_CODE, - gvlid: VENDOR_ID, - supportedMediaTypes: [BANNER], - isBidRequestValid: function (bid) { - return !!(bid && bid.params); - }, - buildRequests: function (validBidRequests, bidderRequest) { - if (!validBidRequests || !bidderRequest) { - return; - } - const result = validBidRequests.map(function (bid) { - let data = { - v: VERSION, - requestId: bid.bidderRequestId, - adSlots: [{ - bidId: bid.bidId, - zoneId: bid.params.zoneid || '', - floor: bid.params.floor || 0.0, - sizes: bid.sizes || [], - schain: bid.schain || {}, - mediaTypes: bid.mediaTypes - }], - referer: (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) ? bidderRequest.refererInfo.referer : null, - tmax: bidderRequest.timeout, - gdpr: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0, - consent: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) ? bidderRequest.gdprConsent.consentString : null, - consentGiven: kubientGetConsentGiven(bidderRequest.gdprConsent), - uspConsent: bidderRequest.uspConsent - }; - return { - method: 'POST', - url: END_POINT, - data: JSON.stringify(data) - }; - }); - return result; - }, - interpretResponse: function interpretResponse(serverResponse, request) { - if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { - return []; - } - let bidResponses = []; - serverResponse.body.seatbid.forEach(seatbid => { - let bids = seatbid.bid || []; - bids.forEach(bid => { - bidResponses.push({ - requestId: bid.bidId, - cpm: bid.price, - currency: bid.cur, - width: bid.w, - height: bid.h, - creativeId: bid.creativeId, - netRevenue: bid.netRevenue, - ttl: bid.ttl, - ad: bid.adm - }); - }); - }); - return bidResponses; - }, - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; - let gdprParams = ''; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - gdprParams = `?consent_str=${gdprConsent.consentString}`; - if (typeof gdprConsent.gdprApplies === 'boolean') { - gdprParams = gdprParams + `&gdpr=${Number(gdprConsent.gdprApplies)}`; - } - gdprParams = gdprParams + `&consent_given=` + kubientGetConsentGiven(gdprConsent); - } - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: 'https://kdmp.kbntx.ch/init.html' + gdprParams - }); - } - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: 'https://kdmp.kbntx.ch/init.png' + gdprParams - }); - } - return syncs; - } -}; - -function kubientGetConsentGiven(gdprConsent) { - let consentGiven = 0; - if (typeof gdprConsent !== 'undefined') { - let apiVersion = utils.deepAccess(gdprConsent, `apiVersion`); - switch (apiVersion) { - case 1: - consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendorConsents.${VENDOR_ID}`) ? 1 : 0; - break; - case 2: - consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendor.consents.${VENDOR_ID}`) ? 1 : 0; - break; - } - } - return consentGiven; -} -registerBidder(spec); diff --git a/modules/lemmaBidAdapter.js b/modules/lemmaBidAdapter.js deleted file mode 100644 index c7440743d2c..00000000000 --- a/modules/lemmaBidAdapter.js +++ /dev/null @@ -1,442 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; - -var BIDDER_CODE = 'lemma'; -var LOG_WARN_PREFIX = 'LEMMA: '; -var ENDPOINT = 'https://ads.lemmatechnologies.com/lemma/servad'; -var USER_SYNC = 'https://sync.lemmatechnologies.com/js/usersync.html?'; -var DEFAULT_CURRENCY = 'USD'; -var AUCTION_TYPE = 2; -var DEFAULT_TMAX = 300; -var DEFAULT_NET_REVENUE = false; -var pubId = 0; -var adunitId = 0; - -export var spec = { - - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid: bid => { - if (bid && bid.params) { - if (!utils.isNumber(bid.params.pubId)) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: publisherId is mandatory and cannot be string. Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); - return false; - } - if (!bid.params.adunitId) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: adUnitId is mandatory. Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); - return false; - } - // video ad validation - if (bid.params.hasOwnProperty('video')) { - if (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); - return false; - } - } - return true; - } - return false; - }, - buildRequests: (bidRequests, bidderRequest) => { - var refererInfo; - if (bidderRequest && bidderRequest.refererInfo) { - refererInfo = bidderRequest.refererInfo; - } - var conf = _initConf(refererInfo); - const request = oRTBTemplate(bidRequests, conf); - if (request.imp.length == 0) { - return; - } - setOtherParams(bidderRequest, request); - const endPoint = endPointURL(bidRequests); - return { - method: 'POST', - url: endPoint, - data: JSON.stringify(request), - }; - }, - interpretResponse: (response, request) => { - return parseRTBResponse(request, response.body); - }, - getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { - let syncurl = USER_SYNC + 'pid=' + pubId; - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: syncurl - }]; - } else { - utils.logWarn(LOG_WARN_PREFIX + 'Please enable iframe based user sync.'); - } - }, -}; - -function _initConf(refererInfo) { - var conf = {}; - conf.pageURL = (refererInfo && refererInfo.referer) ? refererInfo.referer : window.location.href; - if (refererInfo && refererInfo.referer) { - conf.refURL = refererInfo.referer; - } else { - conf.refURL = ''; - } - return conf; -} - -function _setFloor(impObj, bid) { - let bidFloor = -1; - // get lowest floor from floorModule - if (typeof bid.getFloor === 'function') { - [BANNER, VIDEO].forEach(mediaType => { - if (impObj.hasOwnProperty(mediaType)) { - let floorInfo = bid.getFloor({ currency: impObj.bidfloorcur, mediaType: mediaType, size: '*' }); - if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) { - let mediaTypeFloor = parseFloat(floorInfo.floor); - bidFloor = (bidFloor == -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor)) - } - } - }); - } - // get highest from impObj.bidfllor and floor from floor module - // as we are using Math.max, it is ok if we have not got any floor from floorModule, then value of bidFloor will be -1 - if (impObj.bidfloor) { - bidFloor = Math.max(bidFloor, impObj.bidfloor) - } - - // assign value only if bidFloor is > 0 - impObj.bidfloor = ((!isNaN(bidFloor) && bidFloor > 0) ? bidFloor : undefined); -} - -function parseRTBResponse(request, response) { - var bidResponses = []; - try { - if (response.seatbid) { - var currency = response.curr || DEFAULT_CURRENCY; - var seatbid = response.seatbid; - seatbid.forEach(seatbidder => { - var bidder = seatbidder.bid; - bidder.forEach(bid => { - var req = parse(request.data); - var newBid = { - requestId: bid.impid, - cpm: parseFloat(bid.price).toFixed(2), - width: bid.w, - height: bid.h, - creativeId: bid.crid, - currency: currency, - netRevenue: DEFAULT_NET_REVENUE, - ttl: 300, - referrer: req.site.ref, - ad: bid.adm - }; - if (bid.dealid) { - newBid.dealId = bid.dealid; - } - if (req.imp && req.imp.length > 0) { - req.imp.forEach(robj => { - if (bid.impid === robj.id) { - _checkMediaType(bid.adm, newBid); - switch (newBid.mediaType) { - case BANNER: - break; - case VIDEO: - newBid.width = bid.hasOwnProperty('w') ? bid.w : robj.video.w; - newBid.height = bid.hasOwnProperty('h') ? bid.h : robj.video.h; - newBid.vastXml = bid.adm; - break; - } - } - }); - } - bidResponses.push(newBid); - }); - }); - } - } catch (error) { - utils.logError(LOG_WARN_PREFIX, 'ERROR ', error); - } - return bidResponses; -} - -function oRTBTemplate(bidRequests, conf) { - try { - var oRTBObject = { - id: '' + new Date().getTime(), - at: AUCTION_TYPE, - tmax: DEFAULT_TMAX, - cur: [DEFAULT_CURRENCY], - imp: _getImpressionArray(bidRequests), - user: {}, - ext: {} - }; - var bid = bidRequests[0]; - var app = _getAppObject(bid); - var site = _getSiteObject(bid, conf); - var device = _getDeviceObject(bid); - if (app) { - oRTBObject.app = app; - } - if (site) { - oRTBObject.site = site; - } - if (device) { - oRTBObject.device = device; - } - return oRTBObject; - } catch (ex) { - utils.logError(LOG_WARN_PREFIX, 'ERROR ', ex); - } -} - -function _getImpressionArray(request) { - var impArray = []; - var map = request.map(bid => _getImpressionObject(bid)); - if (map) { - map.forEach(o => { - if (o) { - impArray.push(o); - } - }); - } - return impArray; -} - -function endPointURL(request) { - var params = request && request[0].params ? request[0].params : null; - if (params) { - pubId = params.pubId ? params.pubId : 0; - adunitId = params.adunitId ? params.adunitId : 0; - return ENDPOINT + '?pid=' + pubId + '&aid=' + adunitId; - } - return null; -} - -function _getDomain(url) { - var a = document.createElement('a'); - a.setAttribute('href', url); - return a.hostname; -} - -function _getSiteObject(request, conf) { - var params = request && request.params ? request.params : null; - if (params) { - pubId = params.pubId ? params.pubId : '0'; - var siteId = params.siteId ? params.siteId : '0'; - var appParams = params.app; - if (!appParams) { - return { - publisher: { - id: pubId.toString() - }, - domain: _getDomain(conf.pageURL), - id: siteId.toString(), - ref: conf.refURL, - page: conf.pageURL - }; - } - } - return null; -} - -function _getAppObject(request) { - var params = request && request.params ? request.params : null; - if (params) { - pubId = params.pubId ? params.pubId : 0; - var appParams = params.app; - if (appParams) { - return { - publisher: { - id: pubId.toString(), - }, - id: appParams.id, - name: appParams.name, - bundle: appParams.bundle, - storeurl: appParams.storeUrl, - domain: appParams.domain, - cat: appParams.categories, - pagecat: appParams.page_category - }; - } - } - return null; -} - -function _getDeviceObject(request) { - var params = request && request.params ? request.params : null; - if (params) { - return { - dnt: utils.getDNT() ? 1 : 0, - ua: navigator.userAgent, - language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), - w: (window.screen.width || window.innerWidth), - h: (window.screen.height || window.innerHeigh), - geo: { - country: params.country, - lat: params.latitude, - lon: params.longitude, - region: params.region, - city: params.city, - zip: params.zip - }, - ip: params.ip, - devicetype: params.device_type, - ifa: params.ifa, - }; - } - return null; -} - -function setOtherParams(request, ortbRequest) { - var params = request && request.params ? request.params : null; - if (params) { - ortbRequest.tmax = params.tmax; - ortbRequest.bcat = params.bcat; - } -} - -function _getSizes(request) { - if (request.sizes && utils.isArray(request.sizes[0]) && request.sizes[0].length > 0) { - return request.sizes[0]; - } - return null; -} - -function _getBannerRequest(bid) { - var bObj; - var adFormat = []; - if (bid.mediaType === 'banner' || utils.deepAccess(bid, 'mediaTypes.banner')) { - var params = bid ? bid.params : null; - var bannerData = params.banner; - var sizes = _getSizes(bid) || []; - if (sizes && sizes.length == 0) { - sizes = bid.mediaTypes.banner.sizes[0]; - } - if (sizes && sizes.length > 0) { - bObj = {}; - bObj.w = sizes[0]; - bObj.h = sizes[1]; - bObj.pos = 0; - if (bannerData) { - bObj = utils.deepClone(bannerData); - } - sizes = bid.mediaTypes.banner.sizes; - if (sizes.length > 0) { - adFormat = []; - sizes.forEach(function(size) { - if (size.length > 1) { - adFormat.push({ w: size[0], h: size[1] }); - } - }); - if (adFormat.length > 0) { - bObj.format = adFormat; - } - } - } else { - utils.logWarn(LOG_WARN_PREFIX + 'Error: mediaTypes.banner.sizes missing for adunit: ' + bid.params.adunitId); - } - } - return bObj; -} - -function _getVideoRequest(bid) { - var vObj; - if (bid.mediaType === 'video' || utils.deepAccess(bid, 'mediaTypes.video')) { - var params = bid ? bid.params : null; - var sizes = _getSizes(bid) || []; - if (sizes && sizes.length == 0) { - sizes = bid.mediaTypes && bid.mediaTypes.video ? bid.mediaTypes.video.playerSize : []; - } - if (sizes && sizes.length > 0) { - var videoData = params.video; - vObj = {}; - if (videoData) { - vObj = utils.deepClone(videoData); - } - vObj.w = sizes[0]; - vObj.h = sizes[1]; - } else { - utils.logWarn(LOG_WARN_PREFIX + 'Error: mediaTypes.video.sizes missing for adunit: ' + bid.params.adunitId); - } - } - return vObj; -} - -function _getImpressionObject(bid) { - var impression = {}; - var bObj; - var vObj; - var sizes = bid.hasOwnProperty('sizes') ? bid.sizes : []; - var mediaTypes = ''; - var format = []; - var params = bid && bid.params ? bid.params : null; - impression = { - id: bid.bidId, - tagid: params.adunitId ? params.adunitId.toString() : undefined, - secure: window.location.protocol === 'https:' ? 1 : 0, - bidfloorcur: params.currency ? params.currency : DEFAULT_CURRENCY - }; - if (params.bidFloor) { - impression.bidfloor = params.bidFloor; - } - if (bid.hasOwnProperty('mediaTypes')) { - for (mediaTypes in bid.mediaTypes) { - switch (mediaTypes) { - case BANNER: - bObj = _getBannerRequest(bid); - if (bObj) { - impression.banner = bObj; - } - break; - case VIDEO: - vObj = _getVideoRequest(bid); - if (vObj) { - impression.video = vObj; - } - break; - } - } - } else { - bObj = { - pos: 0, - w: sizes && sizes[0] ? sizes[0][0] : 0, - h: sizes && sizes[0] ? sizes[0][1] : 0, - }; - if (utils.isArray(sizes) && sizes.length > 1) { - sizes = sizes.splice(1, sizes.length - 1); - sizes.forEach(size => { - format.push({ - w: size[0], - h: size[1] - }); - }); - bObj.format = format; - } - impression.banner = bObj; - } - _setFloor(impression, bid); - return impression.hasOwnProperty(BANNER) || - impression.hasOwnProperty(VIDEO) ? impression : undefined; -} - -function parse(rawResp) { - try { - if (rawResp) { - return JSON.parse(rawResp); - } - } catch (ex) { - utils.logError(LOG_WARN_PREFIX, 'ERROR', ex); - } - return null; -} - -function _checkMediaType(adm, newBid) { - // Create a regex here to check the strings - var videoRegex = new RegExp(/VAST.*version/); - if (videoRegex.test(adm)) { - newBid.mediaType = VIDEO; - } else { - newBid.mediaType = BANNER; - } -} -registerBidder(spec); diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js deleted file mode 100644 index 4317eb8b82e..00000000000 --- a/modules/lifestreetBidAdapter.js +++ /dev/null @@ -1,139 +0,0 @@ -import * as utils 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 = utils.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 bidResponse = { - requestId: bidRequest.bidId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.creativeId, - currency: response.currency ? response.currency : 'USD', - netRevenue: response.netRevenue ? response.netRevenue : true, - ttl: response.ttl ? response.ttl : 86400 - }; - - if (response.hasOwnProperty('dealId')) { - bidResponse.dealId = response.dealId; - } - if (response.content_type.indexOf('vast') > -1) { - if (typeof response.vastUrl !== 'undefined') { - bidResponse.vastUrl = response.vastUrl; - } else { - bidResponse.vastXml = response.content; - } - - bidResponse.mediaType = VIDEO; - } else { - bidResponse.ad = response.content; - bidResponse.mediaType = BANNER; - } - - bidResponses.push(bidResponse); - return bidResponses; - } -}; - -registerBidder(spec); diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js deleted file mode 100644 index 0f5782649ad..00000000000 --- a/modules/lkqdBidAdapter.js +++ /dev/null @@ -1,284 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'lkqd'; -const BID_TTL_DEFAULT = 300; -const ENDPOINT = 'https://v.lkqd.net/ad'; - -const PARAM_OUTPUT_DEFAULT = 'vast'; -const PARAM_EXECUTION_DEFAULT = 'any'; -const PARAM_SUPPORT_DEFAULT = 'html5'; -const PARAM_PLAYINIT_DEFAULT = 'auto'; -const PARAM_VOLUME_DEFAULT = '100'; - -function _validateId(id) { - if (id && typeof id !== 'undefined' && parseInt(id) > 0) { - return true; - } - - return false; -} - -function isBidRequestValid(bidRequest) { - if (bidRequest.bidder === BIDDER_CODE && typeof bidRequest.params !== 'undefined') { - if (_validateId(bidRequest.params.siteId) && _validateId(bidRequest.params.placementId)) { - return true; - } - } - - return false; -} - -function buildRequests(validBidRequests, bidderRequest) { - let bidRequests = []; - - for (let i = 0; i < validBidRequests.length; i++) { - let bidRequest = validBidRequests[i]; - - let sizes = []; - // if width/height not provided to the ad unit for some reason then attempt request with default 640x480 size - let bidRequestSizes = bidRequest.sizes; - let bidRequestDeepSizes = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); - if ((!bidRequestSizes || !bidRequestSizes.length) && (!bidRequestDeepSizes || !bidRequestDeepSizes.length)) { - utils.logWarn('Warning: Could not find valid width/height parameters on the provided adUnit'); - sizes = [[640, 480]]; - } - - // JWPlayer demo page uses sizes: [640,480] instead of sizes: [[640,480]] so need to handle single-layer array as well as nested arrays - if (bidRequestSizes && bidRequestSizes.length > 0) { - sizes = bidRequestSizes; - if (bidRequestSizes.length === 2 && typeof bidRequestSizes[0] === 'number' && typeof bidRequestSizes[1] === 'number') { - sizes = [bidRequestSizes]; - } - } else if (bidRequestDeepSizes && bidRequestDeepSizes.length > 0) { - sizes = bidRequestDeepSizes; - if (bidRequestDeepSizes.length === 2 && typeof bidRequestDeepSizes[0] === 'number' && typeof bidRequestDeepSizes[1] === 'number') { - sizes = [bidRequestDeepSizes]; - } - } - - for (let j = 0; j < sizes.length; j++) { - let size = sizes[j]; - let playerWidth; - let playerHeight; - if (size && size.length == 2) { - playerWidth = size[0]; - playerHeight = size[1]; - } else { - utils.logWarn('Warning: Could not determine width/height from the provided adUnit'); - } - - let sspUrl = ENDPOINT.concat(); - let sspData = {}; - - // required parameters - sspData.pid = bidRequest.params.placementId; - sspData.sid = bidRequest.params.siteId; - sspData.prebid = true; - - // optional parameters - if (bidRequest.params.hasOwnProperty('output') && bidRequest.params.output != null) { - sspData.output = bidRequest.params.output; - } else { - sspData.output = PARAM_OUTPUT_DEFAULT; - } - if (bidRequest.params.hasOwnProperty('execution') && bidRequest.params.execution != null) { - sspData.execution = bidRequest.params.execution; - } else { - sspData.execution = PARAM_EXECUTION_DEFAULT; - } - if (bidRequest.params.hasOwnProperty('support') && bidRequest.params.support != null) { - sspData.support = bidRequest.params.support; - } else { - sspData.support = PARAM_SUPPORT_DEFAULT; - } - if (bidRequest.params.hasOwnProperty('playinit') && bidRequest.params.playinit != null) { - sspData.playinit = bidRequest.params.playinit; - } else { - sspData.playinit = PARAM_PLAYINIT_DEFAULT; - } - if (bidRequest.params.hasOwnProperty('volume') && bidRequest.params.volume != null) { - sspData.volume = bidRequest.params.volume; - } else { - sspData.volume = PARAM_VOLUME_DEFAULT; - } - if (playerWidth) { - sspData.width = playerWidth; - } - if (playerHeight) { - sspData.height = playerHeight; - } - if (bidRequest.params.hasOwnProperty('vpaidmode') && bidRequest.params.vpaidmode != null) { - sspData.vpaidmode = bidRequest.params.vpaidmode; - } - if (bidRequest.params.hasOwnProperty('appname') && bidRequest.params.appname != null) { - sspData.appname = bidRequest.params.appname; - } - if (bidRequest.params.hasOwnProperty('bundleid') && bidRequest.params.bundleid != null) { - sspData.bundleid = bidRequest.params.bundleid; - } - if (bidRequest.params.hasOwnProperty('aid') && bidRequest.params.aid != null) { - sspData.aid = bidRequest.params.aid; - } - if (bidRequest.params.hasOwnProperty('idfa') && bidRequest.params.idfa != null) { - sspData.idfa = bidRequest.params.idfa; - } - if (bidRequest.params.hasOwnProperty('gdpr') && bidRequest.params.gdpr != null) { - sspData.gdpr = bidRequest.params.gdpr; - } - if (bidRequest.params.hasOwnProperty('gdprcs') && bidRequest.params.gdprcs != null) { - sspData.gdprcs = bidRequest.params.gdprcs; - } - if (bidRequest.params.hasOwnProperty('flrd') && bidRequest.params.flrd != null) { - sspData.flrd = bidRequest.params.flrd; - } - if (bidRequest.params.hasOwnProperty('flrmp') && bidRequest.params.flrmp != null) { - sspData.flrmp = bidRequest.params.flrmp; - } - if (bidRequest.params.hasOwnProperty('schain') && bidRequest.params.schain != null) { - sspData.schain = bidRequest.params.schain; - } - if (bidRequest.params.hasOwnProperty('placement') && bidRequest.params.placement != null) { - sspData.placement = bidRequest.params.placement; - } - if (bidRequest.params.hasOwnProperty('timeout') && bidRequest.params.timeout != null) { - sspData.timeout = bidRequest.params.timeout; - } - if (bidRequest.params.hasOwnProperty('dnt') && bidRequest.params.dnt != null) { - sspData.dnt = bidRequest.params.dnt; - } - if (config.getConfig('coppa') === true) { - sspData.coppa = 1; - } - if (bidRequest.params.hasOwnProperty('pageurl') && bidRequest.params.pageurl != null) { - sspData.pageurl = bidRequest.params.pageurl; - } else if (bidderRequest && bidderRequest.refererInfo) { - sspData.pageurl = encodeURIComponent(encodeURIComponent(bidderRequest.refererInfo.referer)); - } - if (bidRequest.params.hasOwnProperty('contentId') && bidRequest.params.contentId != null) { - sspData.contentid = bidRequest.params.contentId; - } - if (bidRequest.params.hasOwnProperty('contentTitle') && bidRequest.params.contentTitle != null) { - sspData.contenttitle = bidRequest.params.contentTitle; - } - if (bidRequest.params.hasOwnProperty('contentLength') && bidRequest.params.contentLength != null) { - sspData.contentlength = bidRequest.params.contentLength; - } - if (bidRequest.params.hasOwnProperty('contentUrl') && bidRequest.params.contentUrl != null) { - sspData.contenturl = bidRequest.params.contentUrl; - } - if (bidRequest.params.hasOwnProperty('schain') && bidRequest.params.schain) { - sspData.schain = bidRequest.params.schain; - } - - // random number to prevent caching - sspData.rnd = Math.floor(Math.random() * 999999999); - - // Prebid.js required properties - sspData.bidId = bidRequest.bidId; - sspData.bidWidth = playerWidth; - sspData.bidHeight = playerHeight; - - for (let k = 1; k <= 40; k++) { - if (bidRequest.params.hasOwnProperty(`c${k}`) && bidRequest.params[`c${k}`]) { - sspData[`c${k}`] = bidRequest.params[`c${k}`]; - } - } - - bidRequests.push({ - method: 'GET', - url: sspUrl, - data: Object.keys(sspData).map(function (key) { return key + '=' + sspData[key] }).join('&') + '&' - }); - } - } - - return bidRequests; -} - -function interpretResponse(serverResponse, bidRequest) { - let bidResponses = []; - if (serverResponse && serverResponse.body) { - if (serverResponse.error) { - utils.logError('Error: ' + serverResponse.error); - return bidResponses; - } else { - try { - let bidResponse = {}; - if (bidRequest && bidRequest.data && typeof bidRequest.data === 'string') { - let sspData; - let sspBidId; - let sspBidWidth; - let sspBidHeight; - if (window.URLSearchParams) { - sspData = new URLSearchParams(bidRequest.data); - sspBidId = sspData.get('bidId'); - sspBidWidth = sspData.get('bidWidth'); - sspBidHeight = sspData.get('bidHeight'); - } else { - if (bidRequest.data.indexOf('bidId=') >= 0) { - sspBidId = bidRequest.data.substr(bidRequest.data.indexOf('bidId=') + 6, bidRequest.data.length); - sspBidId = sspBidId.split('&')[0]; - } - if (bidRequest.data.indexOf('bidWidth=') >= 0) { - sspBidWidth = bidRequest.data.substr(bidRequest.data.indexOf('bidWidth=') + 9, bidRequest.data.length); - sspBidWidth = sspBidWidth.split('&')[0]; - } - if (bidRequest.data.indexOf('bidHeight=') >= 0) { - sspBidHeight = bidRequest.data.substr(bidRequest.data.indexOf('bidHeight=') + 10, bidRequest.data.length); - sspBidHeight = sspBidHeight.split('&')[0]; - } - } - - if (sspBidId) { - let sspXmlString = serverResponse.body; - let sspXml = new window.DOMParser().parseFromString(sspXmlString, 'text/xml'); - if (sspXml && sspXml.getElementsByTagName('parsererror').length == 0) { - let sspUrl = bidRequest.url.concat(); - - bidResponse.requestId = sspBidId; - bidResponse.bidderCode = BIDDER_CODE; - bidResponse.ad = ''; - bidResponse.cpm = parseFloat(sspXml.getElementsByTagName('Pricing')[0].textContent); - bidResponse.width = sspBidWidth; - bidResponse.height = sspBidHeight; - bidResponse.ttl = BID_TTL_DEFAULT; - bidResponse.creativeId = sspXml.getElementsByTagName('Ad')[0].getAttribute('id'); - bidResponse.currency = sspXml.getElementsByTagName('Pricing')[0].getAttribute('currency'); - bidResponse.netRevenue = true; - bidResponse.vastUrl = sspUrl; - bidResponse.vastXml = sspXmlString; - bidResponse.mediaType = VIDEO; - - bidResponses.push(bidResponse); - } else { - utils.logError('Error: Server response contained invalid XML'); - } - } else { - utils.logError('Error: Could not associate bid request to server response'); - } - } else { - utils.logError('Error: Could not associate bid request to server response'); - } - } catch (e) { - utils.logError('Error: Could not interpret server response'); - } - } - } else { - utils.logError('Error: No server response or server response was empty for the requested URL'); - } - - return bidResponses; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse -} - -registerBidder(spec); diff --git a/modules/loganBidAdapter.js b/modules/loganBidAdapter.js deleted file mode 100644 index e55876de675..00000000000 --- a/modules/loganBidAdapter.js +++ /dev/null @@ -1,118 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'logan'; -const AD_URL = 'https://USeast2.logan.ai/?c=o&m=multi'; -const SYNC_URL = 'https://ssp-cookie.logan.ai/html?src=pbjs' - -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); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - const winTop = utils.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] : '', - 'secure': 1, - '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 || {}, - }; - const mediaType = bid.mediaTypes - - if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { - placement.sizes = mediaType[BANNER].sizes; - placement.traffic = BANNER; - } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { - placement.wPlayer = mediaType[VIDEO].playerSize[0]; - placement.hPlayer = mediaType[VIDEO].playerSize[1]; - placement.traffic = VIDEO; - } else if (mediaType && mediaType[NATIVE]) { - placement.native = mediaType[NATIVE]; - placement.traffic = NATIVE; - } - 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)) { - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncUrl = SYNC_URL - 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}`; - } - return [{ - type: 'iframe', - url: syncUrl - }]; - } -}; - -registerBidder(spec); diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js deleted file mode 100644 index 2c919f9c157..00000000000 --- a/modules/logicadBidAdapter.js +++ /dev/null @@ -1,69 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'logicad'; -const ENDPOINT_URL = 'https://pb.ladsp.com/adrequest/prebid'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - isBidRequestValid: function (bid) { - return !!(bid.params && bid.params.tid); - }, - buildRequests: function (bidRequests, bidderRequest) { - const requests = []; - for (let i = 0, len = bidRequests.length; i < len; i++) { - const request = { - method: 'POST', - url: ENDPOINT_URL, - data: JSON.stringify(newBidRequest(bidRequests[i], bidderRequest)), - options: {}, - bidderRequest - }; - requests.push(request); - } - return requests; - }, - interpretResponse: function (serverResponse, bidderRequest) { - serverResponse = serverResponse.body; - const bids = []; - if (!serverResponse || serverResponse.error) { - return bids; - } - serverResponse.seatbid.forEach(function (seatbid) { - bids.push(seatbid.bid); - }) - return bids; - }, - getUserSyncs: function (syncOptions, serverResponses) { - if (serverResponses.length > 0 && serverResponses[0].body.userSync && - syncOptions.pixelEnabled && serverResponses[0].body.userSync.type == 'image') { - return [{ - type: 'image', - url: serverResponses[0].body.userSync.url - }]; - } - return []; - }, -}; - -function newBidRequest(bid, bidderRequest) { - return { - auctionId: bid.auctionId, - bidderRequestId: bid.bidderRequestId, - bids: [{ - adUnitCode: bid.adUnitCode, - bidId: bid.bidId, - transactionId: bid.transactionId, - sizes: bid.sizes, - params: bid.params, - mediaTypes: bid.mediaTypes - }], - prebidJsVersion: '$prebid.version$', - referrer: bidderRequest.refererInfo.referer, - auctionStartTime: bidderRequest.auctionStart, - eids: bid.userIdAsEids, - }; -} - -registerBidder(spec); diff --git a/modules/loopmeBidAdapter.js b/modules/loopmeBidAdapter.js deleted file mode 100644 index 919e0b17294..00000000000 --- a/modules/loopmeBidAdapter.js +++ /dev/null @@ -1,149 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { Renderer } from '../src/Renderer.js'; - -const LOOPME_ENDPOINT = 'https://loopme.me/api/hb'; - -const entries = (obj) => { - let output = []; - for (let key in obj) { - if (obj.hasOwnProperty(key)) { - output.push([key, obj[key]]) - } - } - return output; -} - -export const spec = { - code: 'loopme', - supportedMediaTypes: [BANNER, VIDEO], - /** - * @param {object} bid - * @return boolean - */ - isBidRequestValid: function(bid) { - if (typeof bid.params !== 'object') { - return false; - } - - return !!bid.params.ak; - }, - /** - * @param {BidRequest[]} bidRequests - * @param bidderRequest - * @return ServerRequest[] - */ - buildRequests: function(bidRequests, bidderRequest) { - return bidRequests.map(bidRequest => { - bidRequest.startTime = new Date().getTime(); - let payload = bidRequest.params; - - if (bidderRequest && bidderRequest.gdprConsent) { - payload.user_consent = bidderRequest.gdprConsent.consentString; - } - - let queryString = entries(payload) - .map(item => `${item[0]}=${encodeURI(item[1])}`) - .join('&'); - - const adUnitSizes = bidRequest.mediaTypes[BANNER] - ? utils.getAdUnitSizes(bidRequest) - : utils.deepAccess(bidRequest.mediaTypes, 'video.playerSize'); - - const sizes = - '&sizes=' + - adUnitSizes - .map(size => `${size[0]}x${size[1]}`) - .join('&sizes='); - - queryString = `${queryString}${sizes}${bidRequest.mediaTypes[VIDEO] ? '&media_type=video' : ''}`; - - return { - method: 'GET', - url: `${LOOPME_ENDPOINT}`, - options: { withCredentials: false }, - bidId: bidRequest.bidId, - data: queryString - }; - }); - }, - /** - * @param {*} responseObj - * @param {BidRequest} bidRequest - * @return {Bid[]} An array of bids which - */ - interpretResponse: function(response = {}, bidRequest) { - const responseObj = response.body; - if ( - responseObj === null || - typeof responseObj !== 'object' - ) { - return []; - } - - if ( - !responseObj.hasOwnProperty('ad') && - !responseObj.hasOwnProperty('vastUrl') - ) { - return []; - } - // responseObj.vastUrl = 'https://rawgit.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%201-2.0%20Samples/Inline_NonLinear_Verification_VAST2.0.xml'; - if (responseObj.vastUrl) { - const renderer = Renderer.install({ - id: bidRequest.bidId, - url: 'https://i.loopme.me/html/vast/loopme_flex.js', - loaded: false - }); - renderer.setRender((bid) => { - renderer.push(function () { - var adverts = [{ - 'type': 'VAST', - 'url': bid.vastUrl, - 'autoClose': -1 - }]; - var config = { - containerId: bid.adUnitCode, - vastTimeout: 250, - ads: adverts, - user_consent: '%%USER_CONSENT%%', - }; - window.L.flex.loader.load(config); - }) - }); - return [ - { - requestId: bidRequest.bidId, - cpm: responseObj.cpm, - width: responseObj.width, - height: responseObj.height, - ttl: responseObj.ttl, - currency: responseObj.currency, - creativeId: responseObj.creativeId, - dealId: responseObj.dealId, - netRevenue: responseObj.netRevenue, - vastUrl: responseObj.vastUrl, - mediaType: VIDEO, - renderer - } - ]; - } - - return [ - { - requestId: bidRequest.bidId, - cpm: responseObj.cpm, - width: responseObj.width, - height: responseObj.height, - ad: responseObj.ad, - ttl: responseObj.ttl, - currency: responseObj.currency, - creativeId: responseObj.creativeId, - dealId: responseObj.dealId, - netRevenue: responseObj.netRevenue, - mediaType: BANNER - } - ]; - } -}; -registerBidder(spec); diff --git a/modules/lunamediaBidAdapter.js b/modules/lunamediaBidAdapter.js deleted file mode 100755 index b309ef42240..00000000000 --- a/modules/lunamediaBidAdapter.js +++ /dev/null @@ -1,399 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js-pure/features/array/find.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const ADAPTER_VERSION = '1.0'; -const BIDDER_CODE = 'lunamedia'; - -export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// https://api.lunamedia.io/xp/get?pubid=0cf8d6d643e13d86a5b6374148a4afac'; -export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; -export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; -export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; -export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; - -let pubid = ''; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid(bidRequest) { - if (typeof bidRequest != 'undefined') { - if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } - if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } - return true; - } else { return false; } - }, - - buildRequests(bids, bidderRequest) { - let requests = []; - let videoBids = bids.filter(bid => isVideoBidValid(bid)); - let bannerBids = bids.filter(bid => isBannerBidValid(bid)); - videoBids.forEach(bid => { - pubid = getVideoBidParam(bid, 'pubid'); - requests.push({ - method: 'POST', - url: VIDEO_ENDPOINT + pubid, - data: createVideoRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - - bannerBids.forEach(bid => { - pubid = getBannerBidParam(bid, 'pubid'); - - requests.push({ - method: 'POST', - url: BANNER_ENDPOINT + pubid, - data: createBannerRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - return requests; - }, - - interpretResponse(serverResponse, {bidRequest}) { - let response = serverResponse.body; - if (response !== null && utils.isEmpty(response) == false) { - if (isVideoBid(bidRequest)) { - let bidResponse = { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - mediaType: VIDEO, - netRevenue: true - } - - if (response.seatbid[0].bid[0].adm) { - bidResponse.vastXml = response.seatbid[0].bid[0].adm; - bidResponse.adResponse = { - content: response.seatbid[0].bid[0].adm - }; - } else { - bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; - } - - return bidResponse; - } else { - return { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ad: response.seatbid[0].bid[0].adm, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - mediaType: BANNER, - netRevenue: true - } - } - } - } -}; - -function isBannerBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); -} - -function isVideoBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.video'); -} - -function isVideoBidValid(bid) { - return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); -} - -function isBannerBidValid(bid) { - return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); -} - -function getVideoBidParam(bid, key) { - return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function getBannerBidParam(bid, key) { - return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); -} - -function isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); -} - -function getDoNotTrack() { - return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; -} - -function findAndFillParam(o, key, value) { - try { - if (typeof value === 'function') { - o[key] = value(); - } else { - o[key] = value; - } - } catch (ex) {} -} - -function getOsVersion() { - let clientStrings = [ - { s: 'Android', r: /Android/ }, - { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, - { s: 'Mac OS X', r: /Mac OS X/ }, - { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, - { s: 'Linux', r: /(Linux|X11)/ }, - { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, - { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, - { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, - { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, - { s: 'Windows Vista', r: /Windows NT 6.0/ }, - { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, - { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, - { s: 'UNIX', r: /UNIX/ }, - { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } - ]; - let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); - return cs ? cs.s : 'unknown'; -} - -function getFirstSize(sizes) { - return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; -} - -function parseSizes(sizes) { - return utils.parseSizesInput(sizes).map(size => { - let [ width, height ] = size.split('x'); - return { - w: parseInt(width, 10) || undefined, - h: parseInt(height, 10) || undefined - }; - }); -} - -function getVideoSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); -} - -function getBannerSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); -} - -function getTopWindowReferrer() { - try { - return window.top.document.referrer; - } catch (e) { - return ''; - } -} - -function getVideoTargetingParams(bid) { - return Object.keys(Object(bid.params.video)) - .filter(param => includes(VIDEO_TARGETING, param)) - .reduce((obj, param) => { - obj[ param ] = bid.params.video[ param ]; - return obj; - }, {}); -} - -function createVideoRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - // if size is explicitly given via adapter params - let paramSize = getVideoBidParam(bid, 'size'); - let sizes = []; - - if (typeof paramSize !== 'undefined' && paramSize != '') { - sizes = parseSizes(paramSize); - } else { - sizes = getVideoSizes(bid); - } - const firstSize = getFirstSize(sizes); - - let video = getVideoTargetingParams(bid); - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1, - 'os': getOsVersion() - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getVideoBidParam(bid, 'placement'); - let floor = getVideoBidParam(bid, 'floor'); - if (floor == null) { floor = 0.5; } - - for (let j = 0; j < sizes.length; j++) { - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': floor, - 'bidfloorcur': 'USD', - 'secure': secure, - 'video': Object.assign({ - 'id': utils.generateUUID(), - 'pos': 0, - 'w': firstSize.w, - 'h': firstSize.h, - 'mimes': DEFAULT_MIMES - }, video) - - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} - -function getTopWindowLocation(bidderRequest) { - let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; - return utils.parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); -} - -function createBannerRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - // if size is explicitly given via adapter params - - let paramSize = getBannerBidParam(bid, 'size'); - let sizes = []; - if (typeof paramSize !== 'undefined' && paramSize != '') { - sizes = parseSizes(paramSize); - } else { - sizes = getBannerSizes(bid); - } - - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1 - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getBannerBidParam(bid, 'placement'); - for (let j = 0; j < sizes.length; j++) { - let size = sizes[j]; - - let floor = getBannerBidParam(bid, 'floor'); - if (floor == null) { floor = 0.1; } - - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': floor, - 'bidfloorcur': 'USD', - 'secure': secure, - 'banner': { - 'id': utils.generateUUID(), - 'pos': 0, - 'w': size['w'], - 'h': size['h'] - } - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} -registerBidder(spec); diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js deleted file mode 100644 index 29b54f77fbb..00000000000 --- a/modules/luponmediaBidAdapter.js +++ /dev/null @@ -1,389 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER} from '../src/mediaTypes.js'; -import { ajax } from '../src/ajax.js'; - -const BIDDER_CODE = 'luponmedia'; -const ENDPOINT_URL = 'https://rtb.adxpremium.services/openrtb2/auction'; - -const DIGITRUST_PROP_NAMES = { - PREBID_SERVER: { - id: 'id', - keyv: 'keyv' - } -}; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid: function (bid) { - return !!(bid.params && bid.params.siteId && bid.params.keyId); // TODO: check for siteId and keyId - }, - buildRequests: function (bidRequests, bidderRequest) { - const bRequest = { - method: 'POST', - url: ENDPOINT_URL, - data: null, - options: {}, - bidderRequest - }; - - let currentImps = []; - - for (let i = 0, len = bidRequests.length; i < len; i++) { - let newReq = newOrtbBidRequest(bidRequests[i], bidderRequest, currentImps); - currentImps = newReq.imp; - bRequest.data = JSON.stringify(newReq); - } - - return bRequest; - }, - interpretResponse: (response, request) => { - const bidResponses = []; - var respCur = 'USD'; - let parsedRequest = JSON.parse(request.data); - let parsedReferrer = parsedRequest.site && parsedRequest.site.ref ? parsedRequest.site.ref : ''; - try { - if (response.body && response.body.seatbid && utils.isArray(response.body.seatbid)) { - // Supporting multiple bid responses for same adSize - respCur = response.body.cur || respCur; - response.body.seatbid.forEach(seatbidder => { - seatbidder.bid && - utils.isArray(seatbidder.bid) && - seatbidder.bid.forEach(bid => { - let newBid = { - requestId: bid.impid, - cpm: (parseFloat(bid.price) || 0).toFixed(2), - width: bid.w, - height: bid.h, - creativeId: bid.crid || bid.id, - dealId: bid.dealid, - currency: respCur, - netRevenue: false, - ttl: 300, - referrer: parsedReferrer, - ad: bid.adm - }; - - bidResponses.push(newBid); - }); - }); - } - } catch (error) { - utils.logError(error); - } - return bidResponses; - }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - let allUserSyncs = []; - if (!hasSynced && (syncOptions.iframeEnabled || syncOptions.pixelEnabled)) { - responses.forEach(csResp => { - if (csResp.body && csResp.body.ext && csResp.body.ext.usersyncs) { - try { - let response = csResp.body.ext.usersyncs - let bidders = response.bidder_status; - for (let synci in bidders) { - let thisSync = bidders[synci]; - if (thisSync.no_cookie) { - let url = thisSync.usersync.url; - let type = thisSync.usersync.type; - - if (!url) { - utils.logError(`No sync url for bidder luponmedia.`); - } else if ((type === 'image' || type === 'redirect') && syncOptions.pixelEnabled) { - utils.logMessage(`Invoking image pixel user sync for luponmedia`); - allUserSyncs.push({type: 'image', url: url}); - } else if (type == 'iframe' && syncOptions.iframeEnabled) { - utils.logMessage(`Invoking iframe user sync for luponmedia`); - allUserSyncs.push({type: 'iframe', url: url}); - } else { - utils.logError(`User sync type "${type}" not supported for luponmedia`); - } - } - } - } catch (e) { - utils.logError(e); - } - } - }); - } else { - utils.logWarn('Luponmedia: Please enable iframe/pixel based user sync.'); - } - - hasSynced = true; - return allUserSyncs; - }, - onBidWon: bid => { - const bidString = JSON.stringify(bid); - spec.sendWinningsToServer(bidString); - }, - sendWinningsToServer: data => { - let mutation = `mutation {createWin(input: {win: {eventData: "${window.btoa(data)}"}}) {win {createTime } } }`; - let dataToSend = JSON.stringify({ query: mutation }); - - ajax('https://analytics.adxpremium.services/graphql', null, dataToSend, { - contentType: 'application/json', - method: 'POST' - }); - } -}; - -function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { - bidRequest.startTime = new Date().getTime(); - - const bannerParams = utils.deepAccess(bidRequest, 'mediaTypes.banner'); - - let bannerSizes = []; - - if (bannerParams && bannerParams.sizes) { - const sizes = utils.parseSizesInput(bannerParams.sizes); - - // get banner sizes in form [{ w: , h: }, ...] - const format = sizes.map(size => { - const [ width, height ] = size.split('x'); - const w = parseInt(width, 10); - const h = parseInt(height, 10); - return { w, h }; - }); - - bannerSizes = format; - } - - const data = { - id: bidRequest.transactionId, - test: config.getConfig('debug') ? 1 : 0, - source: { - tid: bidRequest.transactionId - }, - tmax: config.getConfig('timeout') || 1500, - imp: currentImps.concat([{ - id: bidRequest.bidId, - secure: 1, - ext: { - [bidRequest.bidder]: bidRequest.params - }, - banner: { - format: bannerSizes - } - }]), - ext: { - prebid: { - targeting: { - includewinners: true, - // includebidderkeys always false for openrtb - includebidderkeys: false - } - } - }, - user: { - } - } - - const bidFloor = parseFloat(utils.deepAccess(bidRequest, 'params.floor')); - if (!isNaN(bidFloor)) { - data.imp[0].bidfloor = bidFloor; - } - appendSiteAppDevice(data, bidRequest, bidderRequest); - - const digiTrust = _getDigiTrustQueryParams(bidRequest, 'PREBID_SERVER'); - if (digiTrust) { - utils.deepSetValue(data, 'user.ext.digitrust', digiTrust); - } - - if (bidderRequest.gdprConsent) { - // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module - let gdprApplies; - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { - gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - - utils.deepSetValue(data, 'regs.ext.gdpr', gdprApplies); - utils.deepSetValue(data, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - } - - if (bidderRequest.uspConsent) { - utils.deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - // Set user uuid - utils.deepSetValue(data, 'user.id', utils.generateUUID()); - - // set crumbs - if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { - utils.deepSetValue(data, 'user.buyeruid', bidRequest.crumbs.pubcid); - } else { - utils.deepSetValue(data, 'user.buyeruid', utils.generateUUID()); - } - - if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { - utils.deepSetValue(data, 'user.ext.eids', []); - - if (bidRequest.userId.tdid) { - data.user.ext.eids.push({ - source: 'adserver.org', - uids: [{ - id: bidRequest.userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } - - if (bidRequest.userId.pubcid) { - data.user.ext.eids.push({ - source: 'pubcommon', - uids: [{ - id: bidRequest.userId.pubcid, - }] - }); - } - - // support liveintent ID - if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { - data.user.ext.eids.push({ - source: 'liveintent.com', - uids: [{ - id: bidRequest.userId.lipb.lipbid - }] - }); - - data.user.ext.tpid = { - source: 'liveintent.com', - uid: bidRequest.userId.lipb.lipbid - }; - - if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { - utils.deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); - } - } - - // support identityLink (aka LiveRamp) - if (bidRequest.userId.idl_env) { - data.user.ext.eids.push({ - source: 'liveramp.com', - uids: [{ - id: bidRequest.userId.idl_env - }] - }); - } - } - - if (config.getConfig('coppa') === true) { - utils.deepSetValue(data, 'regs.coppa', 1); - } - - if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) { - utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); - } - - const fpd = config.getLegacyFpd(config.getConfig('ortb2')) || {}; - const siteData = Object.assign({}, bidRequest.params.inventory, fpd.context); - const userData = Object.assign({}, bidRequest.params.visitor, fpd.user); - - if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { - const bidderData = { - bidders: [ bidderRequest.bidderCode ], - config: { - fpd: {} - } - }; - - if (!utils.isEmpty(siteData)) { - bidderData.config.fpd.site = siteData; - } - - if (!utils.isEmpty(userData)) { - bidderData.config.fpd.user = userData; - } - - utils.deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); - } - - const pbAdSlot = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); - } - - return data; -} - -export function hasValidSupplyChainParams(schain) { - let isValid = false; - const requiredFields = ['asi', 'sid', 'hp']; - if (!schain.nodes) return isValid; - isValid = schain.nodes.reduce((status, node) => { - if (!status) return status; - return requiredFields.every(field => node[field]); - }, true); - if (!isValid) utils.logError('LuponMedia: required schain params missing'); - return isValid; -} - -function _getDigiTrustQueryParams(bidRequest = {}, endpointName) { - if (!endpointName || !DIGITRUST_PROP_NAMES[endpointName]) { - return null; - } - const propNames = DIGITRUST_PROP_NAMES[endpointName]; - - function getDigiTrustId() { - const bidRequestDigitrust = utils.deepAccess(bidRequest, 'userId.digitrustid.data'); - if (bidRequestDigitrust) { - return bidRequestDigitrust; - } - - let digiTrustUser = (window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}))); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; - } - - let digiTrustId = getDigiTrustId(); - // Verify there is an ID and this user has not opted out - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return null; - } - - const digiTrustQueryParams = { - [propNames.id]: digiTrustId.id, - [propNames.keyv]: digiTrustId.keyv - }; - if (propNames.pref) { - digiTrustQueryParams[propNames.pref] = 0; - } - return digiTrustQueryParams; -} - -function _getPageUrl(bidRequest, bidderRequest) { - let pageUrl = config.getConfig('pageUrl'); - if (bidRequest.params.referrer) { - pageUrl = bidRequest.params.referrer; - } else if (!pageUrl) { - pageUrl = bidderRequest.refererInfo.referer; - } - return bidRequest.params.secure ? pageUrl.replace(/^http:/i, 'https:') : pageUrl; -} - -function appendSiteAppDevice(data, bidRequest, bidderRequest) { - if (!data) return; - - // ORTB specifies app OR site - if (typeof config.getConfig('app') === 'object') { - data.app = config.getConfig('app'); - } else { - data.site = { - page: _getPageUrl(bidRequest, bidderRequest) - } - } - if (typeof config.getConfig('device') === 'object') { - data.device = config.getConfig('device'); - } -} - -var hasSynced = false; - -export function resetUserSync() { - hasSynced = false; -} - -registerBidder(spec); diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js deleted file mode 100644 index d0cafbfd6b8..00000000000 --- a/modules/madvertiseBidAdapter.js +++ /dev/null @@ -1,96 +0,0 @@ -import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -// use protocol relative urls for http or https -const MADVERTISE_ENDPOINT = 'https://mobile.mng-ads.com/'; - -export const spec = { - code: 'madvertise', - /** - * @param {object} bid - * @return boolean - */ - isBidRequestValid: function (bid) { - if (typeof bid.params !== 'object') { - return false; - } - let sizes = utils.parseSizesInput(bid.sizes); - if (!sizes || sizes.length === 0) { - return false; - } - if (sizes.length > 0 && sizes[0] === undefined) { - return false; - } - if (typeof bid.params.floor == 'undefined' || parseFloat(bid.params.floor) < 0.01) { - bid.params.floor = 0.01; - } - - return typeof bid.params.s != 'undefined'; - }, - /** - * @param {BidRequest[]} bidRequests - * @param bidderRequest - * @return ServerRequest[] - */ - buildRequests: function (bidRequests, bidderRequest) { - return bidRequests.map(bidRequest => { - bidRequest.startTime = new Date().getTime(); - - // non-video request builder - var src = '?rt=bid_request&v=1.0'; - - for (var i = 0; i < bidRequest.sizes.length; i++) { - if (Array.isArray(bidRequest.sizes[i]) && bidRequest.sizes[i].length == 2) { - src = src + '&sizes[' + i + ']=' + bidRequest.sizes[i][0] + 'x' + bidRequest.sizes[i][1]; - } - } - - utils._each(bidRequest.params, (item, key) => src = src + '&' + key + '=' + item); - - if (typeof bidRequest.params.u == 'undefined') { - src = src + '&u=' + navigator.userAgent; - } - - if (bidderRequest && bidderRequest.gdprConsent) { - src = src + '&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? '1' : '0') + '&consent[0][format]=' + config.getConfig('consentManagement.cmpApi') + '&consent[0][value]=' + bidderRequest.gdprConsent.consentString; - } - - return { - method: 'GET', - url: MADVERTISE_ENDPOINT + src, - options: {withCredentials: false}, - bidId: bidRequest.bidId - }; - }); - }, - /** - * @param {*} responseObj - * @param {BidRequest} bidRequest - * @return {Bid[]} An array of bids which - */ - interpretResponse: function (responseObj, bidRequest) { - responseObj = responseObj.body; - // check overall response - if (responseObj == null || typeof responseObj !== 'object' || !responseObj.hasOwnProperty('ad')) { - return []; - } - - let bid = { - requestId: bidRequest.bidId, - cpm: responseObj.cpm, - width: responseObj.Width, - height: responseObj.height, - ad: responseObj.ad, - ttl: responseObj.ttl, - creativeId: responseObj.creativeId, - netRevenue: responseObj.netRevenue, - currency: responseObj.currency, - dealId: responseObj.dealId - }; - return [bid]; - }, - getUserSyncs: function (syncOptions) { - } -}; -registerBidder(spec); diff --git a/modules/mantisBidAdapter.js b/modules/mantisBidAdapter.js deleted file mode 100644 index 61b7c31c8e4..00000000000 --- a/modules/mantisBidAdapter.js +++ /dev/null @@ -1,310 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; - -export const storage = getStorageManager(); - -function inIframe() { - try { - return window.self !== window.top && !window.mantis_link; - } catch (e) { - return true; - } -} -function pixel(url, parent) { - var img = document.createElement('img'); - img.src = url; - img.style.cssText = 'display:none !important;'; - (parent || document.body).appendChild(img); -} -export function onVisible(win, element, doOnVisible, time, pct) { - var started = null; - var notified = false; - var onNotVisible = null; - var whenNotVisible = function () { - if (notified && onNotVisible) { - onNotVisible(); - } - notified = false; - }; - var interval; - var listener; - var doCheck = function (winWidth, winHeight, rect) { - var hidden = typeof document.hidden !== 'undefined' && document.hidden; - if (rect.width == 0 || rect.height == 0 || hidden) { - return whenNotVisible(); - } - var minHeight = (rect.height * pct); - var minWidth = (rect.width * pct); - var inView = ( - ( - (rect.top < 0 && rect.bottom >= minHeight) || - (rect.top > 0 && (winHeight - rect.top) >= minHeight) - ) && - ( - (rect.left < 0 && rect.right >= minWidth) || - (rect.left > 0 && (winWidth - rect.left) >= minWidth) - ) - ); - if (!inView) { - return whenNotVisible(); - } - if (!started && time) { - started = Date.now(); - return whenNotVisible(); - } - if (time && Date.now() - started < time) { - return whenNotVisible(); - } - if (notified) { - return; - } - doOnVisible(function (ack) { - if (ack) { - notified = true; - } else { - interval && clearInterval(interval); - listener && listener(); - } - }, function (onHidden) { - onNotVisible = onHidden; - }); - }; - if (isAmp()) { - listener = win.context.observeIntersection(function (changes) { - changes.forEach(function (change) { - doCheck(change.rootBounds.width, change.rootBounds.height, change.boundingClientRect); - }); - }); - } - interval = setInterval(function () { - var winHeight = (win.innerHeight || document.documentElement.clientHeight); - var winWidth = (win.innerWidth || document.documentElement.clientWidth); - doCheck(winWidth, winHeight, element.getBoundingClientRect()); - }, 100); -} -function storeUuid(uuid) { - if (window.mantis_uuid) { - return false; - } - window.mantis_uuid = uuid; - if (storage.hasLocalStorage()) { - try { - storage.setDataInLocalStorage('mantis:uuid', uuid); - } catch (ex) { - } - } -} - -function onMessage(type, callback) { - window.addEventListener('message', function (event) { - if (event.data.mantis && event.data.type == type) { - callback(event.data.data); - } - }, false); -} -function isSendable(val) { - if (val === null || val === undefined) { - return false; - } - if (typeof val === 'string') { - return !(!val || /^\s*$/.test(val)); - } - if (typeof val === 'number') { - return !isNaN(val); - } - return true; -} -function isObject(value) { - return Object.prototype.toString.call(value) === '[object Object]'; -} -function isAmp() { - return typeof window.context === 'object' && (window.context.tagName === 'AMP-AD' || window.context.tagName === 'AMP-EMBED'); -} -function isSecure() { - return document.location.protocol === 'https:'; -} -function isArray(value) { - return Object.prototype.toString.call(value) === '[object Array]'; -} - -function jsonToQuery(data, chain, form) { - var parts = form || []; - for (var key in data) { - var queryKey = key; - if (chain) { - queryKey = chain + '[' + key + ']'; - } - var val = data[key]; - if (isArray(val)) { - for (var index = 0; index < val.length; index++) { - var akey = queryKey + '[' + index + ']'; - var aval = val[index]; - if (isObject(aval)) { - jsonToQuery(aval, akey, parts); - } - } - } else if (isObject(val) && val != data) { - jsonToQuery(val, queryKey, parts); - } else if (isSendable(val)) { - parts.push(queryKey + '=' + encodeURIComponent(val)); - } - } - return parts.join('&'); -} - -function buildMantisUrl(path, data, domain) { - var params = { - referrer: document.referrer, - tz: new Date().getTimezoneOffset(), - buster: new Date().getTime(), - secure: isSecure(), - version: 9 - }; - - if (window.mantis_uuid) { - params.uuid = window.mantis_uuid; - } else if (storage.hasLocalStorage()) { - var localUuid = storage.getDataFromLocalStorage('mantis:uuid'); - if (localUuid) { - params.uuid = localUuid; - } - } - if (!inIframe()) { - try { - params.title = window.top.document.title; - params.referrer = window.top.document.referrer; - params.url = window.top.document.location.href; - } catch (ex) { - } - } else { - params.iframe = true; - } - if (isAmp()) { - params.amp = true; - if (!params.url && window.context.canonicalUrl) { - params.url = window.context.canonicalUrl; - } - if (!params.url && window.context.location) { - params.url = window.context.location.href; - } - if (!params.referrer && window.context.referrer) { - params.referrer = window.context.referrer; - } - } - Object.keys(data).forEach(function (key) { - params[key] = data[key]; - }); - var query = jsonToQuery(params); - return (window.mantis_domain === undefined ? domain || 'https://mantodea.mantisadnetwork.com' : window.mantis_domain) + path + '?' + query; -} - -export const spec = { - code: 'mantis', - supportedMediaTypes: ['banner'], - isBidRequestValid: function (bid) { - return !!(bid.params.property && (bid.params.code || bid.params.zoneId || bid.params.zone)); - }, - buildRequests: function (validBidRequests, bidderRequest) { - var property = null; - validBidRequests.some(function (bid) { - if (bid.params.property) { - property = bid.params.property; - return true; - } - }); - const query = { - measurable: true, - usp: bidderRequest && bidderRequest.uspConsent, - bids: validBidRequests.map(function (bid) { - return { - bidId: bid.bidId, - config: bid.params, - sizes: bid.sizes.map(function (size) { - return {width: size[0], height: size[1]}; - }) - }; - }), - property: property - }; - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - // we purposefully do not track data for users in the EU - query.consent = false; - } - - return { - method: 'GET', - url: buildMantisUrl('/prebid/display', query) + '&foo', - data: '' - }; - }, - interpretResponse: function (serverResponse) { - storeUuid(serverResponse.body.uuid); - return serverResponse.body.ads.map(function (ad) { - return { - requestId: ad.bid, - cpm: ad.cpm, - width: ad.width, - height: ad.height, - ad: ad.html, - meta: { - advertiserDomains: ad.domains || [] - }, - ttl: ad.ttl || serverResponse.body.ttl || 86400, - creativeId: ad.view, - netRevenue: true, - currency: 'USD' - }; - }); - }, - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: buildMantisUrl('/prebid/iframe', {gdpr: gdprConsent, uspConsent: uspConsent}) - }]; - } - if (syncOptions.pixelEnabled) { - return [{ - type: 'image', - url: buildMantisUrl('/prebid/pixel', {gdpr: gdprConsent, uspConsent: uspConsent}) - }]; - } - } -}; - -export function sfPostMessage ($sf, width, height, callback) { - var viewed = false; - // eslint-disable-next-line no-undef - $sf.ext.register(width, height, function () { - // eslint-disable-next-line no-undef - if ($sf.ext.inViewPercentage() < 50 || viewed) { - return; - } - viewed = true; - callback(); - }); -}; - -export function iframePostMessage (win, name, callback) { - var frames = document.getElementsByTagName('iframe'); - for (var i = 0; i < frames.length; i++) { - var frame = frames[i]; - if (frame.name == name) { - onVisible(win, frame, function (stop) { - callback(); - stop(); - }, 1000, 0.50); - } - } -} - -onMessage('iframe', function (data) { - if (window.$sf) { - sfPostMessage(window.$sf, data.width, data.height, () => pixel(data.pixel)); - } else { - iframePostMessage(window, data.frame, () => pixel(data.pixel)); - } -}); - -registerBidder(spec); diff --git a/modules/meazyBidAdapter.js b/modules/meazyBidAdapter.js deleted file mode 100644 index 9640210cc12..00000000000 --- a/modules/meazyBidAdapter.js +++ /dev/null @@ -1,149 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'meazy'; -const PREBID_ENDPOINT = 'rtb-filter.meazy.co'; -const SYNC_ENDPOINT = 'https://sync.meazy.co/sync/iframe'; -const ENDPOINT_CONFIG = { - defaultCurrency: ['USD'], - availableSize: ['300x250', '320x480', '160x600'] -}; - -const buildURI = (pid) => { - return `https://${PREBID_ENDPOINT}/pbjs?host=${utils.getOrigin()}&api_key=${pid}`; -} - -const validateSize = (size) => { - return ENDPOINT_CONFIG.availableSize.indexOf(size.join('x')) !== -1; -} - -const buildImpression = (bidRequest) => { - const impression = { - id: utils.getUniqueIdentifierStr(), - tagid: bidRequest.adUnitCode, - banner: { - format: bidRequest.sizes.map(size => ({ w: size[0], h: size[1] })) - } - }; - - return impression; -} - -export const spec = { - 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. - */ - isBidRequestValid: function(bid) { - return !!bid.params.pid && bid.sizes.some(validateSize); - }, - - /** - * 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(bidRequests, bidderRequest) { - const payload = { - id: bidRequests[0].bidId, - site: { - domain: utils.getOrigin() - }, - device: { - w: window.screen.width, - h: window.screen.height, - language: navigator.language - }, - cur: ENDPOINT_CONFIG.defaultCurrency, - imp: bidRequests.map(buildImpression), - user: {} - }; - - if (bidderRequest.refererInfo) { - if (bidderRequest.refererInfo.referer) { - payload.site.ref = bidderRequest.refererInfo.referer; - } - - if (utils.isArray(bidderRequest.refererInfo) && bidderRequest.refererInfo.stack.length > 0) { - payload.site.page = bidderRequest.refererInfo.stack[0]; - } - } - - if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) { - payload.user.ext = { - consent: bidderRequest.gdprConsent.consentString, - gdpr: bidderRequest.gdprConsent.gdprApplies & 1 - } - } - - const payloadString = JSON.stringify(payload); - - return { - method: 'POST', - url: buildURI(bidRequests[0].params.pid), - data: payloadString - }; - }, - - /** - * 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) { - const bids = []; - - if (!utils.isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid[0]) { - return bids; - } - - serverResponse.body.seatbid[0].bid.forEach(bidResponse => { - const bid = { - requestId: serverResponse.body.id, - cpm: bidResponse.price, - width: bidResponse.w, - height: bidResponse.h, - creativeId: bidResponse.crid, - netRevenue: bidResponse.netRevenue !== undefined ? bidResponse.netRevenue : true, - dealId: bidResponse.dealid, - currency: ENDPOINT_CONFIG.defaultCurrency[0], - ttl: bidResponse.exp || 900, - ad: bidResponse.adm - } - - bids.push(bid); - }); - - return bids; - }, - - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - - if (syncOptions.pixelEnabled && serverResponses[0] && utils.deepAccess(serverResponses[0], 'body.ext.syncUrl')) { - syncs.push({ - type: 'image', - url: serverResponses[0].body.ext.syncUrl - }); - } - - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: SYNC_ENDPOINT - }); - } - - return syncs; - } -} - -registerBidder(spec); diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index bd7e7f74f1a..4c303ea9b37 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -123,7 +123,7 @@ export const spec = { validBidRequests.forEach(bid => { isTest = isTest || bid.params.is_test; let tagid = bid.params.placement_id; - let bidfloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : 0; + let bidfloor = 0; let validImp = false; let impObj = { id: bid.bidId, diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js deleted file mode 100644 index 022d216a84a..00000000000 --- a/modules/mediagoBidAdapter.js +++ /dev/null @@ -1,375 +0,0 @@ -/** - * gulp serve --modules=mediagoBidAdapter --nolint --notest - */ - -import * as utils from '../src/utils.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { - registerBidder -} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'mediago'; -const PROTOCOL = window.document.location.protocol; -const IS_SECURE = (PROTOCOL === 'https:') ? 1 : 0; -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 -) { - return a // if the placeholder was passed, return - ? ( // a random number from 0 to 15 - 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|bb\d+|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; -} - -/** - * 将尺寸转为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; -} - -/** - * 获取广告位配置 - * @param {Array} validBidRequests an an array of bids - * @param {Object} bidderRequest The master bidRequest object - * @return {Object} - */ -function getItems(validBidRequests, bidderRequest) { - let items = []; - for (let i in validBidRequests) { - let req = validBidRequests[i]; - let ret; - let mediaTypes = getProperty(req, 'mediaTypes'); - - let sizes = transformSizes(getProperty(req, 'sizes')); - let matchSize; - - // 确认尺寸是否符合我们要求 - for (let size of sizes) { - if (size.width === 300 && size.height === 250) { - matchSize = size; - break; - } - } - - // Continue only if there is a matching size - if (matchSize) { - // banner广告类型 - if (mediaTypes.banner) { - let id = '' + (+i + 1); - ret = { - id: id, - // bidFloor: 0, // todo - banner: { - h: matchSize.height, - w: matchSize.width, - pos: 1, - }, - secure: IS_SECURE // for server-side to check if it's secure page - }; - itemMaps[id] = { - req, - ret - }; - } - } - - if (ret) { - items.push(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) { - // console.log(validBidRequests, bidderRequest); - let isMobile = isMobileAndTablet() ? 1 : 0; - let isTest = 0; - let auctionId = getProperty(bidderRequest, 'auctionId') || getRandomId(); - let items = getItems(validBidRequests, bidderRequest); - - const domain = document.domain; - const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - - if (items && items.length) { - let c = { - 'id': 'mgprebidjs_' + auctionId, - 'test': +isTest, - 'at': 1, - 'cur': ['USD'], - 'device': { - // 'dnt':0, - // 'devicetype':2, - 'js': 1, - 'os': navigator.platform || '', - 'ua': navigator.userAgent, - 'language': /en/.test(navigator.language) ? 'en' : navigator.language, - // 'geo':{ - // 'country':'USA' - // } - }, - 'user': { - 'id': getUserID() // todo - }, - 'site': { - 'name': domain, - 'domain': domain, - 'page': location, - 'ref': location, - 'mobile': isMobile, - 'cat': [], // todo - 'publisher': { // todo - 'id': domain, - 'name': domain - } - }, - 'imp': items - }; - 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); - - // request ad only if there is a matching size - if (payload) { - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: ENDPOINT_URL + globals['token'], - data: payloadString, - }; - } else { - return null; - } - }, - - /** - * 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') - }; - 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); - // Bidder specific code - }, - - /** - * 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/mgidBidAdapter.js b/modules/mgidBidAdapter.js deleted file mode 100644 index 957b9a1d703..00000000000 --- a/modules/mgidBidAdapter.js +++ /dev/null @@ -1,576 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); -const DEFAULT_CUR = 'USD'; -const BIDDER_CODE = 'mgid'; -const ENDPOINT_URL = 'https://prebid.mgid.com/prebid/'; -const LOG_WARN_PREFIX = '[MGID warn]: '; -const LOG_INFO_PREFIX = '[MGID info]: '; -const NATIVE_ASSETS = { - 'TITLE': { ID: 1, KEY: 'title', TYPE: 0 }, - 'IMAGE': { ID: 2, KEY: 'image', TYPE: 0 }, - 'ICON': { ID: 3, KEY: 'icon', TYPE: 0 }, - 'SPONSOREDBY': { ID: 4, KEY: 'sponsoredBy', TYPE: 1 }, // please note that type of SPONSORED is also 1 - 'DESC': { ID: 5, KEY: 'data', TYPE: 2 }, // please note that type of BODY is also set to 2 - 'PRICE': { ID: 6, KEY: 'price', TYPE: 6 }, - 'SALEPRICE': { ID: 7, KEY: 'saleprice', TYPE: 7 }, - 'DISPLAYURL': { ID: 8, KEY: 'displayurl', TYPE: 11 }, - 'CTA': { ID: 9, KEY: 'cta', TYPE: 12 }, - 'BODY': { ID: 10, KEY: 'body', TYPE: 2 }, // please note that type of DESC is also set to 2 - 'SPONSORED': { ID: 11, KEY: 'sponsored', TYPE: 1 }, // please note that type of SPONSOREDBY is also set to 1 -}; -const NATIVE_ASSET_IMAGE_TYPE = { - 'ICON': 1, - 'IMAGE': 3 -}; -const DEFAULT_IMAGE_WIDTH = 492; -const DEFAULT_IMAGE_HEIGHT = 328; -const DEFAULT_ICON_WIDTH = 50; -const DEFAULT_ICON_HEIGHT = 50; -const DEFAULT_TITLE_LENGTH = 80; - -let isInvalidNativeRequest = false; - -// check if title, image can be added with mandatory field default values -const NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS = [ - { - id: NATIVE_ASSETS.SPONSOREDBY.ID, - required: true, - data: { - type: 1 - } - }, - { - id: NATIVE_ASSETS.TITLE.ID, - required: true, - }, - { - id: NATIVE_ASSETS.IMAGE.ID, - required: true, - } -]; -let _NATIVE_ASSET_ID_TO_KEY_MAP = {}; -let _NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; - -// loading _NATIVE_ASSET_ID_TO_KEY_MAP -utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); -// loading _NATIVE_ASSET_KEY_TO_ASSET_MAP -utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); - -export const spec = { - VERSION: '1.4', - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - reId: /^[1-9][0-9]*$/, - NATIVE_ASSET_ID_TO_KEY_MAP: _NATIVE_ASSET_ID_TO_KEY_MAP, - NATIVE_ASSET_KEY_TO_ASSET_MAP: _NATIVE_ASSET_KEY_TO_ASSET_MAP, - /** - * 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: (bid) => { - const banner = utils.deepAccess(bid, 'mediaTypes.banner'); - const native = utils.deepAccess(bid, 'mediaTypes.native'); - let nativeOk = utils.isPlainObject(native); - if (nativeOk) { - const nativeParams = utils.deepAccess(bid, 'nativeParams'); - let assetsCount = 0; - if (utils.isPlainObject(nativeParams)) { - for (let k in nativeParams) { - let v = nativeParams[k]; - const supportProp = spec.NATIVE_ASSET_KEY_TO_ASSET_MAP.hasOwnProperty(k); - if (supportProp) { - assetsCount++ - } - if (!utils.isPlainObject(v) || (!supportProp && utils.deepAccess(v, 'required'))) { - nativeOk = false; - break; - } - } - } - nativeOk = nativeOk && (assetsCount > 0); - } - let bannerOk = utils.isPlainObject(banner); - if (bannerOk) { - const sizes = utils.deepAccess(banner, 'sizes'); - bannerOk = utils.isArray(sizes) && sizes.length > 0; - for (let f = 0; bannerOk && f < sizes.length; f++) { - bannerOk = sizes[f].length === 2; - } - } - let acc = Number(bid.params.accountId); - let plcmt = Number(bid.params.placementId); - return (bannerOk || nativeOk) && utils.isPlainObject(bid.params) && !!bid.adUnitCode && utils.isStr(bid.adUnitCode) && (plcmt > 0 ? bid.params.placementId.toString().search(spec.reId) === 0 : true) && - !!acc && acc > 0 && bid.params.accountId.toString().search(spec.reId) === 0; - }, - /** - * 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: (validBidRequests, bidderRequest) => { - utils.logInfo(LOG_INFO_PREFIX + `buildRequests`); - if (validBidRequests.length === 0) { - return; - } - const info = pageInfo(); - const page = info.location || utils.deepAccess(bidderRequest, 'refererInfo.referer') || utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl'); - const hostname = utils.parseUrl(page).hostname; - let domain = extractDomainFromHost(hostname) || hostname; - const accountId = setOnAny(validBidRequests, 'params.accountId'); - const muid = getLocalStorageSafely('mgMuidn'); - let url = (setOnAny(validBidRequests, 'params.bidUrl') || ENDPOINT_URL) + accountId; - if (utils.isStr(muid) && muid.length > 0) { - url += '?muid=' + muid; - } - const cur = [setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || config.getConfig('currency.adServerCurrency') || DEFAULT_CUR]; - const secure = window.location.protocol === 'https:' ? 1 : 0; - let imp = []; - validBidRequests.forEach(bid => { - let tagid = utils.deepAccess(bid, 'params.placementId') || 0; - tagid = !tagid ? bid.adUnitCode : tagid + '/' + bid.adUnitCode; - let impObj = { - id: bid.bidId, - tagid, - secure, - }; - const bidFloor = utils.deepAccess(bid, 'params.bidFloor') || utils.deepAccess(bid, 'params.bidfloor') || 0; - if (bidFloor && utils.isNumber(bidFloor)) { - impObj.bidfloor = bidFloor; - } - for (let mediaTypes in bid.mediaTypes) { - switch (mediaTypes) { - case BANNER: - impObj.banner = createBannerRequest(bid); - imp.push(impObj); - break; - case NATIVE: - const native = createNativeRequest(bid.nativeParams); - if (!isInvalidNativeRequest) { - impObj.native = { - 'request': native - }; - imp.push(impObj); - } - break; - } - } - }); - - if (imp.length === 0) { - return; - } - - let request = { - id: utils.deepAccess(bidderRequest, 'bidderRequestId'), - site: {domain, page}, - cur: cur, - geo: {utcoffset: info.timeOffset}, - device: { - ua: navigator.userAgent, - js: 1, - dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, - h: screen.height, - w: screen.width, - language: getLanguage() - }, - ext: {mgid_ver: spec.VERSION, prebid_ver: $$PREBID_GLOBAL$$.version}, - imp - }; - if (bidderRequest && bidderRequest.gdprConsent) { - request.user = {ext: {consent: bidderRequest.gdprConsent.consentString}}; - request.regs = {ext: {gdpr: (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)}} - } - if (info.referrer) { - request.site.ref = info.referrer - } - utils.logInfo(LOG_INFO_PREFIX + `buildRequest:`, request); - return { - method: 'POST', - url: url, - data: JSON.stringify(request), - }; - }, - /** - * 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: (serverResponse, bidRequests) => { - utils.logInfo(LOG_INFO_PREFIX + `interpretResponse`, serverResponse); - if (serverResponse == null || serverResponse.body == null || serverResponse.body === '' || !utils.isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid.length) { - return; - } - const returnedBids = []; - const muidn = utils.deepAccess(serverResponse.body, 'ext.muidn') - if (utils.isStr(muidn) && muidn.length > 0) { - setLocalStorageSafely('mgMuidn', muidn) - } - serverResponse.body.seatbid.forEach((bids) => { - bids.bid.forEach((bid) => { - const pbid = prebidBid(bid, serverResponse.body.cur); - if (pbid.mediaType === NATIVE && utils.isEmpty(pbid.native)) { - return; - } - returnedBids.push(pbid); - }) - }); - - utils.logInfo(LOG_INFO_PREFIX + `interpretedResponse`, returnedBids); - return returnedBids; - }, - onBidWon: (bid) => { - const cpm = utils.deepAccess(bid, 'adserverTargeting.hb_pb') || ''; - if (utils.isStr(bid.nurl) && bid.nurl !== '') { - bid.nurl = bid.nurl.replace( - /\${AUCTION_PRICE}/, - cpm - ); - utils.triggerPixel(bid.nurl); - } - if (bid.isBurl) { - if (bid.mediaType === BANNER) { - bid.ad = bid.ad.replace( - /\${AUCTION_PRICE}/, - cpm - ) - } else { - bid.burl = bid.burl.replace( - /\${AUCTION_PRICE}/, - cpm - ); - utils.triggerPixel(bid.burl); - } - } - utils.logInfo(LOG_INFO_PREFIX + `onBidWon`); - }, - getUserSyncs: (syncOptions, serverResponses) => { - utils.logInfo(LOG_INFO_PREFIX + `getUserSyncs`); - } -}; - -registerBidder(spec); - -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - if (result) { - return result; - } - } -} - -/** - * Unpack the Server's Bid into a Prebid-compatible one. - * @param serverBid - * @return Bid - */ -function prebidBid(serverBid, cur) { - if (!utils.isStr(cur) || cur === '') { - cur = DEFAULT_CUR; - } - const bid = { - requestId: serverBid.impid, - ad: serverBid.adm, - cpm: serverBid.price, - creativeId: serverBid.adid, - currency: cur, - dealId: serverBid.dealid || '', - width: serverBid.w, - height: serverBid.h, - mediaType: 'banner', - netRevenue: true, - ttl: serverBid.ttl || 300, - nurl: serverBid.nurl || '', - burl: serverBid.burl || '', - isBurl: utils.isStr(serverBid.burl) && serverBid.burl.length > 0, - }; - setMediaType(serverBid, bid); - switch (bid.mediaType) { - case BANNER: - break; - case NATIVE: - parseNativeResponse(serverBid, bid); - break; - } - return bid; -} - -function setMediaType(bid, newBid) { - if (utils.deepAccess(bid, 'ext.crtype') === 'native') { - newBid.mediaType = NATIVE; - } else { - newBid.mediaType = BANNER; - } -} - -function extractDomainFromHost(pageHost) { - if (pageHost == 'localhost') { - return 'localhost' - } - let domain = null; - try { - let domains = /[-\w]+\.([-\w]+|[-\w]{3,}|[-\w]{1,3}\.[-\w]{2})$/i.exec(pageHost); - if (domains != null && domains.length > 0) { - domain = domains[0]; - for (let i = 1; i < domains.length; i++) { - if (domains[i].length > domain.length) { - domain = domains[i]; - } - } - } - } catch (e) { - domain = null; - } - return domain; -} - -function getLanguage() { - const language = navigator.language ? 'language' : 'userLanguage'; - const lang2 = navigator[language].split('-')[0]; - if (lang2.length === 2 || lang2.length === 3) { - return lang2; - } - return ''; -} - -function getLocalStorageSafely(key) { - try { - return storage.getDataFromLocalStorage(key); - } catch (e) { - return null; - } -} - -function setLocalStorageSafely(key, val) { - try { - return storage.setDataInLocalStorage(key, val); - } catch (e) { - return null; - } -} - -function createBannerRequest(bid) { - const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes'); - let format = []; - if (sizes.length > 1) { - for (let f = 0; f < sizes.length; f++) { - if (sizes[f].length === 2) { - format.push({w: sizes[f][0], h: sizes[f][1]}); - } - } - } - let r = { - w: sizes && sizes[0][0], - h: sizes && sizes[0][1], - }; - if (format.length) { - r.format = format - } - return r -} - -function createNativeRequest(params) { - let nativeRequestObject = { - plcmtcnt: 1, - assets: [] - }; - for (let key in params) { - let assetObj = {}; - if (params.hasOwnProperty(key)) { - if (!(nativeRequestObject.assets && nativeRequestObject.assets.length > 0 && nativeRequestObject.assets.hasOwnProperty(key))) { - switch (key) { - case NATIVE_ASSETS.TITLE.KEY: - assetObj = { - id: NATIVE_ASSETS.TITLE.ID, - required: params[key].required ? 1 : 0, - title: { - len: params[key].len || params[key].length || DEFAULT_TITLE_LENGTH - } - }; - break; - case NATIVE_ASSETS.IMAGE.KEY: - const wmin = params[key].wmin || params[key].minimumWidth || (utils.isArray(params[key].minsizes) && params[key].minsizes.length > 0 ? params[key].minsizes[0] : 0); - const hmin = params[key].hmin || params[key].minimumHeight || (utils.isArray(params[key].minsizes) && params[key].minsizes.length > 1 ? params[key].minsizes[1] : 0); - assetObj = { - id: NATIVE_ASSETS.IMAGE.ID, - required: params[key].required ? 1 : 0, - img: { - type: NATIVE_ASSET_IMAGE_TYPE.IMAGE, - w: params[key].w || params[key].width || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[0] : 0), - h: params[key].h || params[key].height || (utils.isArray(params[key].sizes) && params[key].sizes.length > 1 ? params[key].sizes[1] : 0), - mimes: params[key].mimes, - ext: params[key].ext, - } - }; - if (wmin > 0) { - assetObj.img.wmin = wmin; - } - if (hmin > 0) { - assetObj.img.hmin = hmin; - } - if (!assetObj.img.w) { - assetObj.img.w = DEFAULT_IMAGE_WIDTH; - } - if (!assetObj.img.h) { - assetObj.img.h = DEFAULT_IMAGE_HEIGHT; - } - break; - case NATIVE_ASSETS.ICON.KEY: - assetObj = { - id: NATIVE_ASSETS.ICON.ID, - required: params[key].required ? 1 : 0, - img: { - type: NATIVE_ASSET_IMAGE_TYPE.ICON, - w: params[key].w || params[key].width || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[0] : 0), - h: params[key].h || params[key].height || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[1] : 0), - } - }; - if (!assetObj.img.w) { - assetObj.img.w = DEFAULT_ICON_WIDTH; - } - if (!assetObj.img.h) { - assetObj.img.h = DEFAULT_ICON_HEIGHT; - } - break; - case NATIVE_ASSETS.SPONSORED.KEY: - case NATIVE_ASSETS.SPONSOREDBY.KEY: - case NATIVE_ASSETS.PRICE.KEY: - case NATIVE_ASSETS.SALEPRICE.KEY: - case NATIVE_ASSETS.DESC.KEY: - case NATIVE_ASSETS.BODY.KEY: - case NATIVE_ASSETS.DISPLAYURL.KEY: - case NATIVE_ASSETS.CTA.KEY: - assetObj = commonNativeRequestObject(spec.NATIVE_ASSET_KEY_TO_ASSET_MAP[key], params); - break; - default: - if (params[key].required) { - isInvalidNativeRequest = true; - return; - } - } - } - } - if (assetObj.id) { - nativeRequestObject.assets[nativeRequestObject.assets.length] = assetObj; - } - } - - // for native image adtype prebid has to have few required assests i.e. title,sponsoredBy, image - // if any of these are missing from the request then request will not be sent - let requiredAssetCount = NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.length; - let presentrequiredAssetCount = 0; - NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.forEach(ele => { - let lengthOfExistingAssets = nativeRequestObject.assets.length; - for (let i = 0; i < lengthOfExistingAssets; i++) { - if (ele.id === nativeRequestObject.assets[i].id) { - presentrequiredAssetCount++; - break; - } else { - if (ele.id === 4 && nativeRequestObject.assets[i].id === 11) { - if (utils.deepAccess(nativeRequestObject.assets[i], 'data.type') === ele.data.type) { - presentrequiredAssetCount++; - break; - } - } - } - } - }); - isInvalidNativeRequest = requiredAssetCount !== presentrequiredAssetCount; - return nativeRequestObject; -} - -function commonNativeRequestObject(nativeAsset, params) { - const key = nativeAsset.KEY; - return { - id: nativeAsset.ID, - required: params[key].required ? 1 : 0, - data: { - type: nativeAsset.TYPE, - len: params[key].len, - ext: params[key].ext - } - }; -} - -function parseNativeResponse(bid, newBid) { - newBid.native = {}; - if (bid.hasOwnProperty('adm')) { - let adm = ''; - try { - adm = JSON.parse(bid.adm); - } catch (ex) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native response for ad response: ' + newBid.adm); - return; - } - if (adm && adm.native && adm.native.assets && adm.native.assets.length > 0) { - newBid.mediaType = NATIVE; - for (let i = 0, len = adm.native.assets.length; i < len; i++) { - switch (adm.native.assets[i].id) { - case NATIVE_ASSETS.TITLE.ID: - newBid.native.title = adm.native.assets[i].title && adm.native.assets[i].title.text; - break; - case NATIVE_ASSETS.IMAGE.ID: - newBid.native.image = { - url: adm.native.assets[i].img && adm.native.assets[i].img.url, - height: adm.native.assets[i].img && adm.native.assets[i].img.h, - width: adm.native.assets[i].img && adm.native.assets[i].img.w, - }; - break; - case NATIVE_ASSETS.ICON.ID: - newBid.native.icon = { - url: adm.native.assets[i].img && adm.native.assets[i].img.url, - height: adm.native.assets[i].img && adm.native.assets[i].img.h, - width: adm.native.assets[i].img && adm.native.assets[i].img.w, - }; - break; - case NATIVE_ASSETS.SPONSOREDBY.ID: - case NATIVE_ASSETS.SPONSORED.ID: - case NATIVE_ASSETS.PRICE: - case NATIVE_ASSETS.SALEPRICE.ID: - case NATIVE_ASSETS.DESC.ID: - case NATIVE_ASSETS.BODY.ID: - case NATIVE_ASSETS.DISPLAYURL.ID: - case NATIVE_ASSETS.CTA.ID: - newBid.native[spec.NATIVE_ASSET_ID_TO_KEY_MAP[adm.native.assets[i].id]] = adm.native.assets[i].data && adm.native.assets[i].data.value; - break; - } - } - newBid.native.clickUrl = adm.native.link && adm.native.link.url; - newBid.native.clickTrackers = (adm.native.link && adm.native.link.clicktrackers) || []; - newBid.native.impressionTrackers = adm.native.imptrackers || []; - newBid.native.jstracker = adm.native.jstracker || []; - newBid.width = 0; - newBid.height = 0; - } - } -} - -function pageInfo() { - var w, d, l, r, m, p, t; - for (w = window, d = w.document, l = d.location.href, r = d.referrer, m = 0, t = new Date(); w !== w.parent;) { - try { - p = w.parent; l = p.location.href; r = p.document.referrer; w = p; - } catch (e) { - m = top !== w.parent ? 2 : 1; - break - } - } - return { - location: l, - referrer: r || '', - masked: m, - wWidth: w.innerWidth, - wHeight: w.innerHeight, - date: t.toUTCString(), - timeOffset: t.getTimezoneOffset() - }; -} diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js deleted file mode 100644 index 9611946b495..00000000000 --- a/modules/microadBidAdapter.js +++ /dev/null @@ -1,151 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'microad'; - -const ENDPOINT_URLS = { - 'production': 'https://s-rtb-pb.send.microad.jp/prebid', - 'test': 'https://rtbtest.send.microad.jp/prebid' -}; -export let ENVIRONMENT = 'production'; - -/* eslint-disable no-template-curly-in-string */ -const EXT_URL_STRING = '${COMPASS_EXT_URL}'; -const EXT_REF_STRING = '${COMPASS_EXT_REF}'; -const EXT_IFA_STRING = '${COMPASS_EXT_IFA}'; -const EXT_APPID_STRING = '${COMPASS_EXT_APPID}'; -const EXT_GEO_STRING = '${COMPASS_EXT_GEO}'; -/* eslint-enable no-template-curly-in-string */ - -const BANNER_CODE = 1; -const NATIVE_CODE = 2; -const VIDEO_CODE = 4; - -function createCBT() { - const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); - const date = new Date().getTime().toString(16); - return randomValue + date; -} - -function createBitSequenceFromMediaType(hi, code) { - return (hi ? -1 : 0) & code; -} - -function convertMediaTypes(bid) { - return createBitSequenceFromMediaType(bid.mediaTypes.banner, BANNER_CODE) | - createBitSequenceFromMediaType(bid.mediaTypes.native, NATIVE_CODE) | - createBitSequenceFromMediaType(bid.mediaTypes.video, VIDEO_CODE); -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.spot && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); - }, - buildRequests: function(validBidRequests, bidderRequest) { - const requests = []; - - validBidRequests.forEach(bid => { - const bidParams = bid.params; - const params = { - spot: bidParams.spot, - url: bidderRequest.refererInfo.canonicalUrl || window.location.href, - referrer: bidderRequest.refererInfo.referer, - bid_id: bid.bidId, - transaction_id: bid.transactionId, - media_types: convertMediaTypes(bid), - cbt: createCBT() - }; - - if (bidParams.url) { - params['url_macro'] = bidParams.url.replace(EXT_URL_STRING, ''); - } - - if (bidParams.referrer) { - params['referrer_macro'] = bidParams.referrer.replace(EXT_REF_STRING, ''); - } - - if (bidParams.ifa) { - params['ifa'] = bidParams.ifa.replace(EXT_IFA_STRING, ''); - } - - if (bidParams.appid) { - params['appid'] = bidParams.appid.replace(EXT_APPID_STRING, ''); - } - - if (bidParams.geo) { - const geo = bidParams.geo.replace(EXT_GEO_STRING, ''); - if (/^[0-9.\-]+,[0-9.\-]+$/.test(geo)) { - params['geo'] = geo; - } - } - - requests.push({ - method: 'GET', - url: ENDPOINT_URLS[ENVIRONMENT], - data: params, - options: { Accept: 'application/json' } - }); - }); - return requests; - }, - interpretResponse: function(serverResponse) { - const body = serverResponse.body; - const bidResponses = []; - - if (body.cpm && body.cpm > 0) { - const bidResponse = { - requestId: body.requestId, - cpm: body.cpm, - width: body.width, - height: body.height, - ad: body.ad, - ttl: body.ttl, - creativeId: body.creativeId, - netRevenue: body.netRevenue, - currency: body.currency, - }; - - if (body.dealId) { - bidResponse['dealId'] = body.dealId; - } - - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - - if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { - return syncs; - } - - serverResponses.forEach(resp => { - const syncIframeUrls = resp.body.syncUrls.iframe; - const syncImageUrls = resp.body.syncUrls.image; - if (syncOptions.iframeEnabled && syncIframeUrls) { - syncIframeUrls.forEach(syncIframeUrl => { - syncs.push({ - type: 'iframe', - url: syncIframeUrl - }); - }); - } - if (syncOptions.pixelEnabled && syncImageUrls) { - syncImageUrls.forEach(syncImageUrl => { - syncs.push({ - type: 'image', - url: syncImageUrl - }); - }); - } - }); - - return syncs; - } -}; - -registerBidder(spec); diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js deleted file mode 100644 index 2b1d6bdb118..00000000000 --- a/modules/missenaBidAdapter.js +++ /dev/null @@ -1,94 +0,0 @@ -import * as utils from '../src/utils.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'missena'; -const ENDPOINT_URL = 'https://bid.missena.io/'; - -export const spec = { - aliases: [BIDDER_CODE], - code: BIDDER_CODE, - gvlid: 687, - supportedMediaTypes: [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. - */ - isBidRequestValid: function (bid) { - return typeof bid == 'object' && !!bid.params.apiKey; - }, - - /** - * 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) { - return validBidRequests.map((bidRequest) => { - const payload = { - request_id: bidRequest.bidId, - timeout: bidderRequest.timeout, - }; - - if (bidderRequest && bidderRequest.refererInfo) { - payload.referer = bidderRequest.refererInfo.referer; - payload.referer_canonical = bidderRequest.refererInfo.canonicalUrl; - } - - if (bidderRequest && bidderRequest.gdprConsent) { - payload.consent_string = bidderRequest.gdprConsent.consentString; - payload.consent_required = bidderRequest.gdprConsent.gdprApplies; - } - - return { - method: 'POST', - url: - ENDPOINT_URL + - '?' + - utils.formatQS({ - t: bidRequest.params.apiKey, - }), - data: JSON.stringify(payload), - }; - }); - }, - - /** - * 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 bidResponses = []; - const response = serverResponse.body; - - if (response && !response.timeout && !!response.ad) { - bidResponses.push(response); - } - - return bidResponses; - }, - - /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ - onTimeout: function onTimeout(timeoutData) { - utils.logInfo('Missena - Timeout from adapter', timeoutData); - }, - - /** - * 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) { - utils.logInfo('Missena - Bid won', bid); - }, -}; - -registerBidder(spec); diff --git a/modules/mobfoxBidAdapter.js b/modules/mobfoxBidAdapter.js deleted file mode 100644 index 7c356e71089..00000000000 --- a/modules/mobfoxBidAdapter.js +++ /dev/null @@ -1,133 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const utils = require('../src/utils.js'); -const BIDDER_CODE = 'mobfox'; -const BID_REQUEST_BASE_URL = 'https://my.mobfox.com/request.php'; -const CPM_HEADER = 'X-Pricing-CPM'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['mf'], // short code - isBidRequestValid: function (bid) { - return bid.params.s !== null && bid.params.s !== undefined; - }, - buildRequests: function (validBidRequests) { - if (validBidRequests.length > 1) { - throw ('invalid number of valid bid requests, expected 1 element') - } - - let bidParams = validBidRequests[0].params; - let bid = validBidRequests[0]; - - let params = { - // -------------------- Mandatory Parameters ------------------ - rt: bidParams.rt || 'api-fetchip', - r_type: bidParams.r_type || 'banner', - r_resp: bidParams.r_resp || 'json', // string | vast20 - // i: bidParams.i || undefined , // string | 69.197.148.18 - s: bidParams.s, // string | 80187188f458cfde788d961b6882fd53 - u: bidParams.u || window.navigator.userAgent, // string - - // ------------------- Global Parameters ---------------------- - adspace_width: bidParams.adspace_width || bid.sizes[0][0], // integer | 320 - adspace_height: bidParams.adspace_height || bid.sizes[0][1], // integer | 48 - r_floor: bidParams.r_floor || undefined, // 0.8 - - o_andadvid: bidParams.o_andadvid || undefined, // 'c6292267-56ad-4326-965d-deef6fcd5er9' - longitude: bidParams.longitude || undefined, // 12.12 - latitude: bidParams.latitude || undefined, // 280.12 - demo_age: bidParams.demo_age || undefined, // 1978 - - // ------------------- banner / interstitial ---------------------- - adspace_strict: bidParams.adspace_strict || undefined, - - // ------------------- interstitial / video ---------------------- - imp_instl: bidParams.imp_instl || undefined, // integer | 1 - - // ------------------- mraid ---------------------- - c_mraid: bidParams.c_mraid || undefined, // integer | 1 - - // ------------------- video ---------------------- - v_dur_min: bidParams.v_dur_min || undefined, // integer | 0 - v_dur_max: bidParams.v_dur_max || undefined, // integer | 999 - v_autoplay: bidParams.v_autoplay || undefined, // integer | 1 - v_startmute: bidParams.v_startmute || undefined, // integer | 0 - v_rewarded: bidParams.v_rewarded || undefined, // integer | 0 - v_api: bidParams.v_api || undefined, // string | vpaid20 - n_ver: bidParams.n_ver || undefined, // - n_adunit: bidParams.n_adunit || undefined, // - n_layout: bidParams.n_layout || undefined, // - n_context: bidParams.n_context || undefined, // - n_plcmttype: bidParams.n_plcmttype || undefined, // - n_img_icon_req: bidParams.n_img_icon_req || undefined, // boolean0 - n_img_icon_size: bidParams.n_img_icon_size || undefined, // string80 - n_img_large_req: bidParams.n_img_large_req || undefined, // boolean0 - n_img_large_w: bidParams.n_img_large_w || undefined, // integer1200 - n_img_large_h: bidParams.n_img_large_h || undefined, // integer627 - n_title_req: bidParams.n_title_req || undefined, // boolean0 - n_title_len: bidParams.n_title_len || undefined, // string25 - n_desc_req: bidParams.n_desc_req || undefined, // boolean0 - n_desc_len: bidParams.n_desc_len || undefined, // string140 - n_rating_req: bidParams.n_rating_req || undefined - }; - - let payloadString = buildPayloadString(params); - - return { - method: 'GET', - url: BID_REQUEST_BASE_URL, - data: payloadString, - requestId: bid.bidId - }; - }, - interpretResponse: function (serverResponse, bidRequest) { - const bidResponses = []; - let serverResponseBody = serverResponse.body; - - if (!serverResponseBody || serverResponseBody.error) { - let errorMessage = `in response for ${BIDDER_CODE} adapter`; - if (serverResponseBody && serverResponseBody.error) { - errorMessage += `: ${serverResponseBody.error}`; - } - utils.logError(errorMessage); - return bidResponses; - } - try { - let serverResponseHeaders = serverResponse.headers; - let bidRequestData = bidRequest.data.split('&'); - const bidResponse = { - requestId: bidRequest.requestId, - cpm: serverResponseHeaders.get(CPM_HEADER), - width: bidRequestData[5].split('=')[1], - height: bidRequestData[6].split('=')[1], - creativeId: bidRequestData[3].split('=')[1], - currency: 'USD', - netRevenue: true, - ttl: 360, - referrer: serverResponseBody.request.clickurl, - ad: serverResponseBody.request.htmlString - }; - bidResponses.push(bidResponse); - } catch (e) { - throw 'could not build bid response: ' + e; - } - return bidResponses; - } -}; - -function buildPayloadString(params) { - for (let key in params) { - if (params.hasOwnProperty(key)) { - if (params[key] === undefined) { - delete params[key]; - } else { - params[key] = encodeURIComponent(params[key]); - } - } - } - - return utils._map(Object.keys(params), key => `${key}=${params[key]}`) - .join('&') -} - -registerBidder(spec); diff --git a/modules/mobfoxpbBidAdapter.js b/modules/mobfoxpbBidAdapter.js deleted file mode 100644 index c7e96b95179..00000000000 --- a/modules/mobfoxpbBidAdapter.js +++ /dev/null @@ -1,99 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'mobfoxpb'; -const AD_URL = 'https://bes.mobfox.com/?c=o&m=multi'; - -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); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers); - default: - return false; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId))); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - const winTop = utils.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] : '', - 'secure': 1, - '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 || {}, - }; - const mediaType = bid.mediaTypes - - if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { - placement.sizes = mediaType[BANNER].sizes; - placement.traffic = BANNER; - } else if (mediaType && mediaType[VIDEO] && mediaType[VIDEO].playerSize) { - placement.wPlayer = mediaType[VIDEO].playerSize[0]; - placement.hPlayer = mediaType[VIDEO].playerSize[1]; - placement.traffic = VIDEO; - } else if (mediaType && mediaType[NATIVE]) { - placement.native = mediaType[NATIVE]; - placement.traffic = NATIVE; - } - 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)) { - response.push(resItem); - } - } - return response; - }, -}; - -registerBidder(spec); diff --git a/modules/mobsmartBidAdapter.js b/modules/mobsmartBidAdapter.js deleted file mode 100644 index e5ff38ec69a..00000000000 --- a/modules/mobsmartBidAdapter.js +++ /dev/null @@ -1,94 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'mobsmart'; -const ENDPOINT = 'https://prebid.mobsmart.net/prebid/endpoint'; - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - if (bid.bidder !== BIDDER_CODE) { - return false; - } - - return true; - }, - buildRequests: function(validBidRequests, bidderRequest) { - const timeout = config.getConfig('bidderTimeout'); - const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); - - return validBidRequests.map(bidRequest => { - const adUnit = { - code: bidRequest.adUnitCode, - bids: { - bidder: bidRequest.bidder, - params: bidRequest.params - }, - mediaTypes: bidRequest.mediaTypes - }; - - if (bidRequest.hasOwnProperty('sizes') && bidRequest.sizes.length > 0) { - adUnit.sizes = bidRequest.sizes; - } - - const request = { - auctionId: bidRequest.auctionId, - requestId: bidRequest.bidId, - bidRequestsCount: bidRequest.bidRequestsCount, - bidderRequestId: bidRequest.bidderRequestId, - transactionId: bidRequest.transactionId, - referrer: referrer, - timeout: timeout, - adUnit: adUnit - }; - - if (bidRequest.userId && bidRequest.userId.pubcid) { - request.userId = {pubcid: bidRequest.userId.pubcid}; - } - - return { - method: 'POST', - url: ENDPOINT, - data: JSON.stringify(request) - } - }); - }, - interpretResponse: function(serverResponse) { - const bidResponses = []; - - if (serverResponse.body) { - const response = serverResponse.body; - const bidResponse = { - requestId: response.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - creativeId: response.creativeId, - currency: response.currency, - netRevenue: response.netRevenue, - ttl: response.ttl, - ad: response.ad, - }; - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - getUserSyncs: function(syncOptions, serverResponses) { - let syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: 'https://tags.mobsmart.net/tags/iframe' - }); - } else if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: 'https://tags.mobsmart.net/tags/image' - }); - } - - return syncs; - } -} -registerBidder(spec); diff --git a/modules/mytargetBidAdapter.js b/modules/mytargetBidAdapter.js deleted file mode 100644 index bcf8e7ad17f..00000000000 --- a/modules/mytargetBidAdapter.js +++ /dev/null @@ -1,113 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'mytarget'; -const BIDDER_URL = 'https://ad.mail.ru/hbid_prebid/'; -const DEFAULT_CURRENCY = 'RUB'; -const DEFAULT_TTL = 180; - -function buildPlacement(bidRequest) { - let { bidId, params } = bidRequest; - let { placementId, position, response, bidfloor } = params; - let placement = { - placementId, - id: bidId, - position: position || 0, - response: response || 0 - }; - - if (typeof bidfloor !== 'undefined') { - placement.bidfloor = bidfloor; - } - - return placement; -} - -function getSiteName(referrer) { - let sitename = config.getConfig('mytarget.sitename'); - - if (!sitename) { - sitename = utils.parseUrl(referrer).hostname; - } - - return sitename; -} - -function getCurrency() { - let currency = config.getConfig('currency.adServerCurrency'); - - return (currency === 'USD') ? currency : DEFAULT_CURRENCY; -} - -function generateRandomId() { - return Math.random().toString(16).substring(2); -} - -export const spec = { - code: BIDDER_CODE, - - isBidRequestValid: function(bid) { - return !!bid.params.placementId; - }, - - buildRequests: function(validBidRequests, bidderRequest) { - let referrer = ''; - - if (bidderRequest && bidderRequest.refererInfo) { - referrer = bidderRequest.refererInfo.referer; - } - - const payload = { - places: utils._map(validBidRequests, buildPlacement), - site: { - sitename: getSiteName(referrer), - page: referrer - }, - settings: { - currency: getCurrency(), - windowSize: { - width: window.screen.width, - height: window.screen.height - } - } - }; - - return { - method: 'POST', - url: BIDDER_URL, - data: payload, - }; - }, - - interpretResponse: function(serverResponse, bidRequest) { - let { body } = serverResponse; - - if (body.bids) { - return utils._map(body.bids, (bid) => { - let bidResponse = { - requestId: bid.id, - cpm: bid.price, - width: bid.size.width, - height: bid.size.height, - ttl: bid.ttl || DEFAULT_TTL, - currency: bid.currency || DEFAULT_CURRENCY, - creativeId: bid.creativeId || generateRandomId(), - netRevenue: true - } - - if (bid.adm) { - bidResponse.ad = bid.adm; - } else { - bidResponse.adUrl = bid.displayUrl; - } - - return bidResponse; - }); - } - - return []; - } -} - -registerBidder(spec); diff --git a/modules/nafdigitalBidAdapter.js b/modules/nafdigitalBidAdapter.js deleted file mode 100644 index d64e079b52a..00000000000 --- a/modules/nafdigitalBidAdapter.js +++ /dev/null @@ -1,245 +0,0 @@ -import * as utils 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 = 'nafdigital'; -const URL = 'https://nafdigitalbidder.com/hb'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -}; - -function buildRequests(bidReqs, bidderRequest) { - try { - let referrer = ''; - if (bidderRequest && bidderRequest.refererInfo) { - referrer = bidderRequest.refererInfo.referer; - } - const nafdigitalImps = []; - const publisherId = utils.getBidIdParameter('publisherId', bidReqs[0].params); - utils._each(bidReqs, function (bid) { - bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); - bid.sizes = bid.sizes.filter(size => utils.isArray(size)); - const processedSizes = bid.sizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); - - const element = document.getElementById(bid.adUnitCode); - const minSize = _getMinSize(processedSizes); - const viewabilityAmount = _isViewabilityMeasurable(element) - ? _getViewability(element, utils.getWindowTop(), minSize) - : 'na'; - const viewabilityAmountRounded = isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); - - const imp = { - id: bid.bidId, - banner: { - format: processedSizes, - ext: { - viewability: viewabilityAmountRounded - } - }, - tagid: String(bid.adUnitCode) - }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); - if (bidFloor) { - imp.bidfloor = bidFloor; - } - nafdigitalImps.push(imp); - }); - const nafdigitalBidReq = { - id: utils.getUniqueIdentifierStr(), - imp: nafdigitalImps, - site: { - domain: utils.parseUrl(referrer).host, - page: referrer, - publisher: { - id: publisherId - } - }, - device: { - devicetype: _getDeviceType(), - w: screen.width, - h: screen.height - }, - tmax: config.getConfig('bidderTimeout') - }; - - return { - method: 'POST', - url: URL, - data: JSON.stringify(nafdigitalBidReq), - options: {contentType: 'text/plain', withCredentials: false} - }; - } catch (e) { - utils.logError(e, {bidReqs, bidderRequest}); - } -} - -function isBidRequestValid(bid) { - if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { - return false; - } - - if (typeof bid.params.publisherId === 'undefined') { - return false; - } - - return true; -} - -function interpretResponse(serverResponse) { - if (!serverResponse.body || typeof serverResponse.body != 'object') { - utils.logWarn('NAF digital server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); - return []; - } - const { body: {id, seatbid} } = serverResponse; - try { - const nafdigitalBidResponses = []; - if (id && - seatbid && - seatbid.length > 0 && - seatbid[0].bid && - seatbid[0].bid.length > 0) { - seatbid[0].bid.map(nafdigitalBid => { - nafdigitalBidResponses.push({ - requestId: nafdigitalBid.impid, - cpm: parseFloat(nafdigitalBid.price), - width: parseInt(nafdigitalBid.w), - height: parseInt(nafdigitalBid.h), - creativeId: nafdigitalBid.crid || nafdigitalBid.id, - currency: 'USD', - netRevenue: true, - mediaType: BANNER, - ad: _getAdMarkup(nafdigitalBid), - ttl: 60 - }); - }); - } - return nafdigitalBidResponses; - } catch (e) { - utils.logError(e, {id, seatbid}); - } -} - -// Don't do user sync for now -function getUserSyncs(syncOptions, responses, gdprConsent) { - return []; -} - -function _isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); -} - -function _isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); -} - -function _getDeviceType() { - return _isMobile() ? 1 : _isConnectedTV() ? 3 : 2; -} - -function _getAdMarkup(bid) { - let adm = bid.adm; - if ('nurl' in bid) { - adm += utils.createTrackPixelHtml(bid.nurl); - } - return adm; -} - -function _isViewabilityMeasurable(element) { - return !_isIframe() && element !== null; -} - -function _getViewability(element, topWin, { w, h } = {}) { - return utils.getWindowTop().document.visibilityState === 'visible' - ? _getPercentInView(element, topWin, { w, h }) - : 0; -} - -function _isIframe() { - try { - return utils.getWindowSelf() !== utils.getWindowTop(); - } catch (e) { - return true; - } -} - -function _getMinSize(sizes) { - return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min); -} - -function _getBoundingBox(element, { w, h } = {}) { - let { width, height, left, top, right, bottom } = element.getBoundingClientRect(); - - if ((width === 0 || height === 0) && w && h) { - width = w; - height = h; - right = left + w; - bottom = top + h; - } - - return { width, height, left, top, right, bottom }; -} - -function _getIntersectionOfRects(rects) { - const bbox = { - left: rects[0].left, - right: rects[0].right, - top: rects[0].top, - bottom: rects[0].bottom - }; - - for (let i = 1; i < rects.length; ++i) { - bbox.left = Math.max(bbox.left, rects[i].left); - bbox.right = Math.min(bbox.right, rects[i].right); - - if (bbox.left >= bbox.right) { - return null; - } - - bbox.top = Math.max(bbox.top, rects[i].top); - bbox.bottom = Math.min(bbox.bottom, rects[i].bottom); - - if (bbox.top >= bbox.bottom) { - return null; - } - } - - bbox.width = bbox.right - bbox.left; - bbox.height = bbox.bottom - bbox.top; - - return bbox; -} - -function _getPercentInView(element, topWin, { w, h } = {}) { - const elementBoundingBox = _getBoundingBox(element, { w, h }); - - // Obtain the intersection of the element and the viewport - const elementInViewBoundingBox = _getIntersectionOfRects([ { - left: 0, - top: 0, - right: topWin.innerWidth, - bottom: topWin.innerHeight - }, elementBoundingBox ]); - - let elementInViewArea, elementTotalArea; - - if (elementInViewBoundingBox !== null) { - // Some or all of the element is in view - elementInViewArea = elementInViewBoundingBox.width * elementInViewBoundingBox.height; - elementTotalArea = elementBoundingBox.width * elementBoundingBox.height; - - return ((elementInViewArea / elementTotalArea) * 100); - } - - // No overlap between element and the viewport; therefore, the element - // lies completely out of view - return 0; -} - -registerBidder(spec); diff --git a/modules/nanointeractiveBidAdapter.js b/modules/nanointeractiveBidAdapter.js deleted file mode 100644 index 42a343efc03..00000000000 --- a/modules/nanointeractiveBidAdapter.js +++ /dev/null @@ -1,159 +0,0 @@ -import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); - -export const BIDDER_CODE = 'nanointeractive'; -export const END_POINT_URL = 'https://ad.audiencemanager.de'; - -export const SSP_PLACEMENT_ID = 'pid'; -export const NQ = 'nq'; -export const NQ_NAME = 'name'; -export const CATEGORY = 'category'; -export const CATEGORY_NAME = 'categoryName'; -export const SUB_ID = 'subId'; -export const REF = 'ref'; -export const LOCATION = 'loc'; - -var nanoPid = '5a1ec660eb0a191dfa591172'; - -export const spec = { - - code: BIDDER_CODE, - aliases: ['ni'], - - isBidRequestValid(bid) { - const pid = bid.params[SSP_PLACEMENT_ID]; - return !!(pid); - }, - - buildRequests(validBidRequests, bidderRequest) { - let payload = []; - validBidRequests.forEach( - bid => payload.push(createSingleBidRequest(bid, bidderRequest)) - ); - const url = getEndpointUrl() + '/hb'; - - return { - method: 'POST', - url: url, - data: JSON.stringify(payload) - }; - }, - interpretResponse(serverResponse) { - const bids = []; - serverResponse.body.forEach(serverBid => { - if (isEngineResponseValid(serverBid)) { - bids.push(createSingleBidResponse(serverBid)); - } - }); - return bids; - }, - getUserSyncs: function(syncOptions) { - const syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: getEndpointUrl() + '/hb/cookieSync/' + nanoPid - }); - } - - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: getEndpointUrl() + '/hb/cookieSync/' + nanoPid - }); - } - return syncs; - } - -}; - -function createSingleBidRequest(bid, bidderRequest) { - const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - const origin = utils.getOrigin(); - - nanoPid = bid.params[SSP_PLACEMENT_ID] || nanoPid; - - const data = { - [SSP_PLACEMENT_ID]: bid.params[SSP_PLACEMENT_ID], - [NQ]: [createNqParam(bid)], - [CATEGORY]: [createCategoryParam(bid)], - [SUB_ID]: createSubIdParam(bid), - [REF]: createRefParam(), - sizes: bid.sizes.map(value => value[0] + 'x' + value[1]), - bidId: bid.bidId, - cors: origin, - [LOCATION]: location, - lsUserId: getLsUserId() - }; - - if (bidderRequest && bidderRequest.gdprConsent) { - data['gdprConsent'] = bidderRequest.gdprConsent.consentString; - data['gdprApplies'] = (bidderRequest.gdprConsent.gdprApplies) ? '1' : '0'; - } - - return data; -} - -function createSingleBidResponse(serverBid) { - if (serverBid.userId) { - storage.setDataInLocalStorage('lsUserId', serverBid.userId); - } - return { - requestId: serverBid.id, - cpm: serverBid.cpm, - width: serverBid.width, - height: serverBid.height, - ad: serverBid.ad, - ttl: serverBid.ttl, - creativeId: serverBid.creativeId, - netRevenue: serverBid.netRevenue || true, - currency: serverBid.currency - }; -} - -function createNqParam(bid) { - return bid.params[NQ_NAME] ? utils.getParameterByName(bid.params[NQ_NAME]) : bid.params[NQ] || null; -} - -function createCategoryParam(bid) { - return bid.params[CATEGORY_NAME] ? utils.getParameterByName(bid.params[CATEGORY_NAME]) : bid.params[CATEGORY] || null; -} - -function createSubIdParam(bid) { - return bid.params[SUB_ID] || null; -} - -function createRefParam() { - try { - return window.top.document.referrer; - } catch (ex) { - return document.referrer; - } -} - -function isEngineResponseValid(response) { - return !!response.cpm && !!response.ad; -} - -/** - * Used mainly for debugging - * - * @returns string - */ -function getEndpointUrl() { - const nanoConfig = config.getConfig('nano'); - return (nanoConfig && nanoConfig['endpointUrl']) || END_POINT_URL; -} - -function getLsUserId() { - if (storage.getDataFromLocalStorage('lsUserId') != null) { - return storage.getDataFromLocalStorage('lsUserId'); - } - return null; -} - -registerBidder(spec); diff --git a/modules/nasmediaAdmixerBidAdapter.js b/modules/nasmediaAdmixerBidAdapter.js deleted file mode 100644 index fd7a7baa58a..00000000000 --- a/modules/nasmediaAdmixerBidAdapter.js +++ /dev/null @@ -1,85 +0,0 @@ - -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; - -const ADMIXER_ENDPOINT = 'https://adn.admixer.co.kr:10443/prebid/ad_req'; -const BIDDER_CODE = 'nasmediaAdmixer'; - -const DEFAULT_BID_TTL = 360; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_REVENUE = false; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - isBidRequestValid: function (bid) { - return !!(bid && bid.params && bid.params.media_key && bid.params.adunit_id); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - return validBidRequests.map(bid => { - const payload = { - media_key: bid.params.media_key, - adunit_id: bid.params.adunit_id, - req_id: bid.bidId, - referrer: bidderRequest.refererInfo.referer, - os: getOs(), - platform: getPlatform(), - }; - - return { - method: 'GET', - url: ADMIXER_ENDPOINT, - data: payload, - } - }) - }, - - interpretResponse: function (serverResponse, bidRequest) { - const serverBody = serverResponse.body; - const bidResponses = []; - - if (serverBody && serverBody.error_code === 0 && serverBody.body && serverBody.body.length > 0) { - let bidData = serverBody.body[0]; - - const bidResponse = { - ad: bidData.ad, - requestId: serverBody.req_id, - creativeId: bidData.ad_id, - cpm: bidData.cpm, - width: bidData.width, - height: bidData.height, - currency: bidData.currency ? bidData.currency : DEFAULT_CURRENCY, - netRevenue: DEFAULT_REVENUE, - ttl: DEFAULT_BID_TTL - }; - - bidResponses.push(bidResponse); - } - return bidResponses; - } -} - -function getOs() { - let ua = navigator.userAgent; - if (ua.match(/(iPhone|iPod|iPad)/)) { - return 'ios'; - } else if (ua.match(/Android/)) { - return 'android'; - } else if (ua.match(/Window/)) { - return 'windows'; - } else { - return 'etc'; - } -} - -function getPlatform() { - return (isMobile()) ? 'm_web' : 'pc_web'; -} - -function isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent.toLowerCase()); -} - -registerBidder(spec); diff --git a/modules/newborntownWebBidAdapter.js b/modules/newborntownWebBidAdapter.js deleted file mode 100644 index 56c63e2bb4d..00000000000 --- a/modules/newborntownWebBidAdapter.js +++ /dev/null @@ -1,159 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); -const BIDDER_CODE = 'newborntownWeb'; - -const REQUEST_URL = 'https://us-west.solortb.com/adx/api/rtb?from=4' - -function randomn(n) { - return parseInt((Math.random() + 1) * Math.pow(10, n - 1)) + ''; -} -function generateGUID() { - var d = new Date().getTime(); - var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); - }) - return guid; -} -function _isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); -} -function _isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); -} -function _getDeviceType() { - return _isMobile() ? 1 : _isConnectedTV() ? 3 : 2; -} -var platform = (function getPlatform() { - var ua = navigator.userAgent; - if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1) { - return 'Android' - } - if (ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) { - return 'iOS' - } - return 'windows' -})(); -function getLanguage() { - const language = navigator.language ? 'language' : 'userLanguage'; - return navigator[language].split('-')[0]; -} -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - isBidRequestValid: function(bid) { - return !!(bid.params.publisher_id && bid.params.slot_id && bid.params.bidfloor); - }, - - buildRequests: function(validBidRequests, bidderRequest) { - let requestArr = [] - if (validBidRequests.length === 0) { - return null; - } - var guid; - if (storage.getDataFromLocalStorage('sax_user_id') == null) { - storage.setDataInLocalStorage('sax_user_id', generateGUID()) - } - guid = storage.getDataFromLocalStorage('sax_user_id') - utils._each(validBidRequests, function(bidRequest) { - const bidRequestObj = bidRequest.params - var req = { - id: randomn(12) + randomn(12), - tmax: bidderRequest.timeout, - bidId: bidRequest.bidId, - user: { - id: guid - }, - imp: [ - { - id: '1', - bidfloor: bidRequestObj.bidfloor, - bidfloorcur: 'USD', - banner: { - w: 0, - h: 0 - } - } - ], - site: { - domain: window.location.host, - id: bidRequestObj.slot_id, - page: window.location.href, - publisher: { - id: bidRequestObj.publisher_id - }, - }, - device: { - ip: '', - ua: navigator.userAgent, - os: platform, - geo: { - country: '', - type: 0, - ipservice: 1, - region: '', - city: '', - }, - language: getLanguage(), - devicetype: _getDeviceType() - }, - ext: { - solomath: { - slotid: bidRequestObj.slot_id - } - } - }; - var sizes = bidRequest.sizes; - if (sizes) { - if (sizes && utils.isArray(sizes[0])) { - req.imp[0].banner.w = sizes[0][0]; - req.imp[0].banner.h = sizes[0][1]; - } else if (sizes && utils.isNumber(sizes[0])) { - req.imp[0].banner.w = sizes[0]; - req.imp[0].banner.h = sizes[1]; - } - } else { - return false; - } - const options = { - withCredentials: false - } - requestArr.push({ - method: 'POST', - url: REQUEST_URL, - data: req, - bidderRequest, - options: options - }) - }) - return requestArr; - }, - interpretResponse: function(serverResponse, request) { - var bidResponses = []; - if (serverResponse.body.seatbid && serverResponse.body.seatbid.length > 0 && serverResponse.body.seatbid[0].bid && serverResponse.body.seatbid[0].bid.length > 0 && serverResponse.body.seatbid[0].bid[0].adm) { - utils._each(serverResponse.body.seatbid[0].bid, function(bodyAds) { - var adstr = ''; - adstr = bodyAds.adm; - var bidResponse = { - requestId: request.data.bidId || 0, - cpm: bodyAds.price || 0, - width: bodyAds.w ? bodyAds.w : 0, - height: bodyAds.h ? bodyAds.h : 0, - ad: adstr, - netRevenue: true, - currency: serverResponse.body.cur || 'USD', - ttl: 600, - creativeId: bodyAds.cid - }; - bidResponses.push(bidResponse); - }); - } - return bidResponses; - } -} -registerBidder(spec); diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js deleted file mode 100644 index 86da82753b6..00000000000 --- a/modules/nextMillenniumBidAdapter.js +++ /dev/null @@ -1,75 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'nextMillennium'; -const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; -const TIME_TO_LIVE = 360; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - isBidRequestValid: function(bid) { - return !!( - bid.params.placement_id && utils.isStr(bid.params.placement_id) - ); - }, - - buildRequests: function(validBidRequests) { - let requests = []; - - utils._each(validBidRequests, function(bid) { - requests.push({ - method: 'POST', - url: ENDPOINT, - options: { - contentType: 'application/json', - withCredentials: true - }, - data: JSON.stringify({ - 'ext': { - 'prebid': { - 'storedrequest': { - 'id': utils.getBidIdParameter('placement_id', bid.params) - } - } - } - }), - bidId: bid.bidId - }); - }); - - return requests; - }, - - interpretResponse: function(serverResponse, bidRequest) { - const response = serverResponse.body; - const bidResponses = []; - - try { - utils._each(response.seatbid, (resp) => { - utils._each(resp.bid, (bid) => { - bidResponses.push({ - requestId: bidRequest.bidId, - cpm: bid.price, - width: bid.w, - height: bid.h, - creativeId: bid.adid, - currency: response.cur, - netRevenue: false, - ttl: TIME_TO_LIVE, - meta: { - advertiserDomains: bid.adomain || [] - }, - ad: bid.adm - }); - }); - }) - } catch (err) { - utils.logError(err); - } - return bidResponses; - } -}; -registerBidder(spec); diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js deleted file mode 100644 index cb317190bea..00000000000 --- a/modules/nextrollBidAdapter.js +++ /dev/null @@ -1,345 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; - -import find from 'core-js-pure/features/array/find.js'; - -const BIDDER_CODE = 'nextroll'; -const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/'; -const ADAPTER_VERSION = 5; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, NATIVE], - - /** - * 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 (bidRequest) { - return bidRequest !== undefined && !!bidRequest.params && !!bidRequest.bidId; - }, - - /** - * 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) { - let topLocation = utils.parseUrl(utils.deepAccess(bidderRequest, 'refererInfo.referer')); - - return validBidRequests.map((bidRequest) => { - return { - method: 'POST', - options: { - withCredentials: true, - }, - url: BIDDER_ENDPOINT, - data: { - id: bidRequest.bidId, - imp: { - id: bidRequest.bidId, - bidfloor: utils.getBidIdParameter('bidfloor', bidRequest.params), - banner: _getBanner(bidRequest), - native: _getNative(utils.deepAccess(bidRequest, 'mediaTypes.native')), - ext: { - zone: { - id: utils.getBidIdParameter('zoneId', bidRequest.params) - }, - nextroll: { - adapter_version: ADAPTER_VERSION - } - } - }, - - user: _getUser(validBidRequests), - site: _getSite(bidRequest, topLocation), - seller: _getSeller(bidRequest), - device: _getDevice(bidRequest), - regs: _getRegs(bidderRequest) - } - }; - }); - }, - - /** - * 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) { - if (!serverResponse.body) { - return []; - } else { - let response = serverResponse.body - let bids = response.seatbid.reduce((acc, seatbid) => acc.concat(seatbid.bid), []); - return bids.map((bid) => _buildResponse(response, bid)); - } - } -} - -function _getBanner(bidRequest) { - let sizes = _getSizes(bidRequest); - if (sizes === undefined) return undefined; - return {format: sizes}; -} - -function _getNative(mediaTypeNative) { - if (mediaTypeNative === undefined) return undefined; - let assets = _getNativeAssets(mediaTypeNative); - if (assets === undefined || assets.length == 0) return undefined; - return { - request: { - native: { - assets: assets - } - } - }; -} - -/* - id: Unique numeric id for the asset - kind: OpenRTB kind of asset. Supported: title, img and data. - key: Name of property that comes in the mediaType.native object. - type: OpenRTB type for that spefic kind of asset. - required: Overrides the asset required field configured, only overrides when is true. -*/ -const NATIVE_ASSET_MAP = [ - {id: 1, kind: 'title', key: 'title', required: true}, - {id: 2, kind: 'img', key: 'image', type: 3, required: true}, - {id: 3, kind: 'img', key: 'icon', type: 1}, - {id: 4, kind: 'img', key: 'logo', type: 2}, - {id: 5, kind: 'data', key: 'sponsoredBy', type: 1}, - {id: 6, kind: 'data', key: 'body', type: 2} -]; - -const ASSET_KIND_MAP = { - title: _getTitleAsset, - img: _getImageAsset, - data: _getDataAsset, -}; - -function _getAsset(mediaTypeNative, assetMap) { - const asset = mediaTypeNative[assetMap.key]; - if (asset === undefined) return undefined; - const assetFunc = ASSET_KIND_MAP[assetMap.kind]; - return { - id: assetMap.id, - required: (assetMap.required || !!asset.required) ? 1 : 0, - [assetMap.kind]: assetFunc(asset, assetMap) - }; -} - -function _getTitleAsset(title, _assetMap) { - return {len: title.len || 0}; -} - -function _getMinAspectRatio(aspectRatio, property) { - if (!utils.isPlainObject(aspectRatio)) return 1; - - const ratio = aspectRatio['ratio_' + property]; - const min = aspectRatio['min_' + property]; - - if (utils.isNumber(ratio)) return ratio; - if (utils.isNumber(min)) return min; - - return 1; -} - -function _getImageAsset(image, assetMap) { - const sizes = image.sizes; - const aspectRatio = image.aspect_ratios ? image.aspect_ratios[0] : undefined; - - return { - type: assetMap.type, - w: (sizes ? sizes[0] : undefined), - h: (sizes ? sizes[1] : undefined), - wmin: _getMinAspectRatio(aspectRatio, 'width'), - hmin: _getMinAspectRatio(aspectRatio, 'height'), - }; -} - -function _getDataAsset(data, assetMap) { - return { - type: assetMap.type, - len: data.len || 0 - }; -} - -function _getNativeAssets(mediaTypeNative) { - return NATIVE_ASSET_MAP - .map(assetMap => _getAsset(mediaTypeNative, assetMap)) - .filter(asset => asset !== undefined); -} - -function _getUser(requests) { - const id = utils.deepAccess(requests, '0.userId.nextrollId'); - if (id === undefined) { - return; - } - - return { - ext: { - eid: [{ - 'source': 'nextroll', - id - }] - } - }; -} - -function _buildResponse(bidResponse, bid) { - let response = { - requestId: bidResponse.id, - cpm: bid.price, - width: bid.w, - height: bid.h, - creativeId: bid.crid, - dealId: bidResponse.dealId, - currency: 'USD', - netRevenue: true, - ttl: 300 - }; - if (utils.isStr(bid.adm)) { - response.mediaType = BANNER; - response.ad = utils.replaceAuctionPrice(bid.adm, bid.price); - } else { - response.mediaType = NATIVE; - response.native = _getNativeResponse(bid.adm, bid.price); - } - return response; -} - -const privacyLink = 'https://info.evidon.com/pub_info/573'; -const privacyIcon = 'https://c.betrad.com/pub/icon1.png'; - -function _getNativeResponse(adm, price) { - let baseResponse = { - clickTrackers: (adm.link && adm.link.clicktrackers) || [], - jstracker: adm.jstracker || [], - clickUrl: utils.replaceAuctionPrice(adm.link.url, price), - impressionTrackers: adm.imptrackers.map(impTracker => utils.replaceAuctionPrice(impTracker, price)), - privacyLink: privacyLink, - privacyIcon: privacyIcon - }; - return adm.assets.reduce((accResponse, asset) => { - const assetMaps = NATIVE_ASSET_MAP.filter(assetMap => assetMap.id === asset.id && asset[assetMap.kind] !== undefined); - if (assetMaps.length === 0) return accResponse; - const assetMap = assetMaps[0]; - accResponse[assetMap.key] = _getAssetResponse(asset, assetMap); - return accResponse; - }, baseResponse); -} - -function _getAssetResponse(asset, assetMap) { - switch (assetMap.kind) { - case 'title': - return asset.title.text; - - case 'img': - return { - url: asset.img.url, - width: asset.img.w, - height: asset.img.h - }; - - case 'data': - return asset.data.value; - } -} - -function _getSite(bidRequest, topLocation) { - return { - page: topLocation.href, - domain: topLocation.hostname, - publisher: { - id: utils.getBidIdParameter('publisherId', bidRequest.params) - } - }; -} - -function _getSeller(bidRequest) { - return { - id: utils.getBidIdParameter('sellerId', bidRequest.params) - }; -} - -function _getSizes(bidRequest) { - if (!utils.isArray(bidRequest.sizes)) { - return undefined; - } - return bidRequest.sizes.filter(_isValidSize).map(size => { - return { - w: size[0], - h: size[1] - } - }); -} - -function _isValidSize(size) { - const isNumber = x => typeof x === 'number'; - return (size.length === 2 && isNumber(size[0]) && isNumber(size[1])); -} - -function _getDevice(_bidRequest) { - return { - ua: navigator.userAgent, - language: navigator['language'], - os: _getOs(navigator.userAgent.toLowerCase()), - osv: _getOsVersion(navigator.userAgent) - }; -} - -function _getRegs(bidderRequest) { - if (!bidderRequest || !bidderRequest.uspConsent) { - return undefined; - } - return { - ext: { - us_privacy: bidderRequest.uspConsent - } - }; -} - -function _getOs(userAgent) { - const osTable = { - 'android': /android/i, - 'ios': /iphone|ipad/i, - 'mac': /mac/i, - 'linux': /linux/i, - 'windows': /windows/i - }; - - return find(Object.keys(osTable), os => { - if (userAgent.match(osTable[os])) { - return os; - } - }) || 'etc'; -} - -function _getOsVersion(userAgent) { - const clientStrings = [ - { s: 'Android', r: /Android/ }, - { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, - { s: 'Mac OS X', r: /Mac OS X/ }, - { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, - { s: 'Linux', r: /(Linux|X11)/ }, - { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, - { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, - { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, - { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, - { s: 'Windows Vista', r: /Windows NT 6.0/ }, - { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, - { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, - { s: 'UNIX', r: /UNIX/ }, - { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } - ]; - let cs = find(clientStrings, cs => cs.r.test(userAgent)); - return cs ? cs.s : 'unknown'; -} - -registerBidder(spec); diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index c1762ac0cd3..149a4b20e2f 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -348,7 +348,7 @@ var adUnits = [ id: 1, page: 'https://verizonmedia.com', referrer: 'https://verizonmedia.com' - }, + }, pubId: 'HBExchange' } } diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js deleted file mode 100644 index 744cce2b5f9..00000000000 --- a/modules/open8BidAdapter.js +++ /dev/null @@ -1,185 +0,0 @@ -import { Renderer } from '../src/Renderer.js'; -import {ajax} from '../src/ajax.js'; -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'open8'; -const URL = 'https://as.vt.open8.com/v1/control/prebid'; -const AD_TYPE = { - VIDEO: 1, - BANNER: 2 -}; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [VIDEO, BANNER], - - isBidRequestValid: function(bid) { - return !!(bid.params.slotKey); - }, - - buildRequests: function(validBidRequests, bidderRequest) { - var requests = []; - for (var i = 0; i < validBidRequests.length; i++) { - var bid = validBidRequests[i]; - var queryString = ''; - var slotKey = utils.getBidIdParameter('slotKey', bid.params); - queryString = utils.tryAppendQueryString(queryString, 'slot_key', slotKey); - queryString = utils.tryAppendQueryString(queryString, 'imp_id', generateImpId()); - queryString += ('bid_id=' + bid.bidId); - - requests.push({ - method: 'GET', - url: URL, - data: queryString - }); - } - return requests; - }, - - interpretResponse: function(serverResponse, request) { - var bidderResponse = serverResponse.body; - - if (!bidderResponse.isAdReturn) { - return []; - } - - var ad = bidderResponse.ad; - - const bid = { - slotKey: bidderResponse.slotKey, - userId: bidderResponse.userId, - impId: bidderResponse.impId, - media: bidderResponse.media, - ds: ad.ds, - spd: ad.spd, - fa: ad.fa, - pr: ad.pr, - mr: ad.mr, - nurl: ad.nurl, - requestId: ad.bidId, - cpm: ad.price, - creativeId: ad.creativeId, - dealId: ad.dealId, - currency: ad.currency || 'JPY', - netRevenue: true, - ttl: 360, // 6 minutes - } - - if (ad.adType === AD_TYPE.VIDEO) { - const videoAd = bidderResponse.ad.video; - Object.assign(bid, { - vastXml: videoAd.vastXml, - width: videoAd.w, - height: videoAd.h, - renderer: newRenderer(bidderResponse), - adResponse: bidderResponse, - mediaType: VIDEO - }); - } else if (ad.adType === AD_TYPE.BANNER) { - const bannerAd = bidderResponse.ad.banner; - Object.assign(bid, { - width: bannerAd.w, - height: bannerAd.h, - ad: bannerAd.adm, - mediaType: BANNER - }); - if (bannerAd.imps) { - try { - bannerAd.imps.forEach(impTrackUrl => { - const tracker = utils.createTrackPixelHtml(impTrackUrl); - bid.ad += tracker; - }); - } catch (error) { - utils.logError('Error appending imp tracking pixel', error); - } - } - } - return [bid]; - }, - - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - if (syncOptions.iframeEnabled && serverResponses.length) { - const syncIFs = serverResponses[0].body.syncIFs; - if (syncIFs) { - syncIFs.forEach(sync => { - syncs.push({ - type: 'iframe', - url: sync - }); - }); - } - } - if (syncOptions.pixelEnabled && serverResponses.length) { - const syncPixs = serverResponses[0].body.syncPixels; - if (syncPixs) { - syncPixs.forEach(sync => { - syncs.push({ - type: 'image', - url: sync - }); - }); - } - } - return syncs; - }, - onBidWon: function(bid) { - if (!bid.nurl) { return; } - const winUrl = bid.nurl.replace( - /\$\{AUCTION_PRICE\}/, - bid.cpm - ); - ajax(winUrl, null); - } -} - -function generateImpId() { - var l = 16; - var c = 'abcdefghijklmnopqrstuvwsyz0123456789'; - var cl = c.length; - var r = ''; - for (var i = 0; i < l; i++) { - r += c[Math.floor(Math.random() * cl)]; - } - return r; -} - -function newRenderer(bidderResponse) { - const renderer = Renderer.install({ - id: bidderResponse.ad.bidId, - url: bidderResponse.ad.video.purl, - loaded: false, - }); - - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on newRenderer', err); - } - - return renderer; -} - -function outstreamRender(bid) { - bid.renderer.push(() => { - window.op8.renderPrebid({ - vastXml: bid.vastXml, - adUnitCode: bid.adUnitCode, - slotKey: bid.slotKey, - impId: bid.impId, - userId: bid.userId, - media: bid.media, - ds: bid.ds, - spd: bid.spd, - fa: bid.fa, - pr: bid.pr, - mr: bid.mr, - adResponse: bid.adResponse, - mediaType: bid.mediaType - }); - }); -} - -registerBidder(spec); diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index a398a20a5c5..b49f7ef2c3c 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -31,7 +31,6 @@ export const USER_ID_CODE_TO_QUERY_ARG = { parrableId: 'parrableid', // Parrable ID pubcid: 'pubcid', // PubCommon ID quantcastId: 'quantcastid', // Quantcast ID - sharedId: 'sharedid', // Shared ID User ID tapadId: 'tapadid', // Tapad Id tdid: 'ttduuid', // The Trade Desk Unified ID verizonMediaId: 'verizonmediaid', // Verizon Media ConnectID diff --git a/modules/optimeraBidAdapter.js b/modules/optimeraBidAdapter.js deleted file mode 100644 index b470e901ec6..00000000000 --- a/modules/optimeraBidAdapter.js +++ /dev/null @@ -1,85 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { deepAccess } from '../src/utils.js'; - -const BIDDER_CODE = 'optimera'; -const SCORES_BASE_URL = 'https://dyv1bugovvq1g.cloudfront.net/'; - -export const spec = { - code: BIDDER_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 (bidRequest) { - if (typeof bidRequest.params !== 'undefined' && typeof bidRequest.params.clientID !== 'undefined') { - return true; - } - return false; - }, - /** - * Make a server request from the list of BidRequests. - * - * We call the existing scores data file for ad slot placement scores. - * These scores will be added to the dealId to be pushed to DFP. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ - buildRequests (validBidRequests) { - const optimeraHost = window.location.host; - const optimeraPathName = window.location.pathname; - if (typeof validBidRequests[0].params.clientID !== 'undefined') { - const { clientID } = validBidRequests[0].params; - const scoresURL = `${SCORES_BASE_URL + clientID}/${optimeraHost}${optimeraPathName}.js`; - return { - method: 'GET', - url: scoresURL, - payload: validBidRequests, - }; - } - return {}; - }, - /** - * Unpack the response from the server into a list of bids. - * - * Some required bid params are not needed for this so default - * values are used. - * - * @param {*} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse (serverResponse, bidRequest) { - const validBids = bidRequest.payload; - const bidResponses = []; - let dealId = ''; - if (typeof serverResponse.body !== 'undefined') { - const scores = serverResponse.body; - for (let i = 0; i < validBids.length; i += 1) { - if (typeof validBids[i].params.clientID !== 'undefined') { - if (validBids[i].adUnitCode in scores) { - const deviceDealId = deepAccess(scores, `device.${validBids[i].params.device}.${validBids[i].adUnitCode}`); - dealId = deviceDealId || scores[validBids[i].adUnitCode]; - } - const bidResponse = { - requestId: validBids[i].bidId, - ad: '
', - cpm: 0.01, - width: 0, - height: 0, - dealId, - ttl: 300, - creativeId: '1', - netRevenue: '0', - currency: 'USD' - }; - bidResponses.push(bidResponse); - } - } - } - return bidResponses; - } -} - -registerBidder(spec); diff --git a/modules/optimeraBidAdapter.md b/modules/optimeraBidAdapter.md deleted file mode 100644 index 25da9b6236f..00000000000 --- a/modules/optimeraBidAdapter.md +++ /dev/null @@ -1,58 +0,0 @@ -# Overview - -``` -Module Name: Optimera Bidder Adapter -Module Type: Bidder Adapter -Maintainer: kcandiotti@optimera.nyc -``` - -# Description - -Module that adds ad placement visibility scores for DFP. - -# Test Parameters -``` - var adUnits = [{ - code: 'div-1', - sizes: [[300, 250], [300,600]], - bids: [ - { - bidder: 'optimera', - params: { - clientID: '9999', - device: 'mo' - } - }] - },{ - code: 'div-0', - sizes: [[728, 90]], - bids: [ - { - bidder: 'optimera', - params: { - clientID: '9999', - device: 'mo' - } - }] - }]; -``` - -# AppNexus Issue -There is an issue where the plugin sometimes doesn't return impressions with AppNexus. - -There is an open issue here: [#3597](https://github.com/prebid/Prebid.js/issues/3597) - -## Configuration Workaround - -Optimera's configuration requires the use of size 0,0 which, in some instances, causes the AppNexus ad server to respond to ad requests but not fill impressions. AppNexus and vendors using the AppNexus ad server should monitor 3rd party numbers to ensure there is no decline in fill rate. - -Configuration Example: - -``` -code: ‘leaderboard', -mediaTypes: { - banner: { - sizes: [[970, 250],[970, 90],[970, 66],[728, 90],[320, 50],[320, 100],[300, 250],[0, 0]], - } -} -``` diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index 4a7d686a7bc..0ca0eeafe47 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -21,7 +21,6 @@ export const spec = { return !!(bid.sizes && bid.bidId && bid.params && (bid.params.accountId && (typeof bid.params.accountId === 'string')) && (bid.params.placementId && (typeof bid.params.placementId === 'string')) && - ((typeof bid.params.bidfloor === 'undefined') || (typeof bid.params.bidfloor === 'number')) && ((typeof bid.params.profile === 'undefined') || (typeof bid.params.profile === 'object'))); }, diff --git a/modules/otmBidAdapter.js b/modules/otmBidAdapter.js deleted file mode 100644 index 23f6d434ae1..00000000000 --- a/modules/otmBidAdapter.js +++ /dev/null @@ -1,95 +0,0 @@ -import {BANNER} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -export const spec = { - code: 'otm', - supportedMediaTypes: [BANNER], - isBidRequestValid: function (bid) { - return !!bid.params.tid; - }, - buildRequests: function (bidRequests) { - const requests = bidRequests.map(function (bid) { - const size = getMaxPrioritySize(bid.sizes); - const params = { - tz: getTz(), - w: size[0], - h: size[1], - s: bid.params.tid, - bidid: bid.bidId, - transactionid: bid.transactionId, - auctionid: bid.auctionId, - bidfloor: bid.params.bidfloor - }; - - return {method: 'GET', url: 'https://ssp.otm-r.com/adjson', data: params} - }); - - return requests; - }, - interpretResponse: function (serverResponse, bidRequest) { - if (!serverResponse || !serverResponse.body) { - return []; - } - - const answer = []; - - serverResponse.body.forEach(bid => { - if (bid.ad) { - answer.push({ - requestId: bid.bidid, - cpm: bid.cpm, - width: bid.w, - height: bid.h, - creativeId: bid.creativeid, - currency: bid.currency || 'RUB', - netRevenue: true, - ad: bid.ad, - ttl: bid.ttl, - transactionId: bid.transactionid - }); - } - }); - - return answer; - }, -}; - -function getTz() { - return new Date().getTimezoneOffset(); -} - -function getMaxPrioritySize(sizes) { - var maxPrioritySize = null; - - const sizesByPriority = [ - [300, 250], - [240, 400], - [728, 90], - [300, 600], - [970, 250], - [300, 50], - [320, 100] - ]; - - const sizeToString = (size) => { - return size[0] + 'x' + size[1]; - }; - - const sizesAsString = sizes.map(sizeToString); - - sizesByPriority.forEach(size => { - if (!maxPrioritySize) { - if (sizesAsString.indexOf(sizeToString(size)) !== -1) { - maxPrioritySize = size; - } - } - }); - - if (maxPrioritySize) { - return maxPrioritySize; - } else { - return sizes[0]; - } -} - -registerBidder(spec); diff --git a/modules/outconBidAdapter.js b/modules/outconBidAdapter.js deleted file mode 100644 index 0c3ac90172a..00000000000 --- a/modules/outconBidAdapter.js +++ /dev/null @@ -1,69 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'outcon'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: ['banner', 'video'], - isBidRequestValid: function(bid) { - return !!((bid.params.pod || (bid.params.internalId && bid.params.publisher)) && bid.params.env); - }, - buildRequests: function(validBidRequests) { - for (let i = 0; i < validBidRequests.length; i++) { - let url = ''; - let par = ''; - if (validBidRequests[i].params.pod != undefined) par = 'get?pod=' + validBidRequests[i].params.pod + '&bidId=' + validBidRequests[i].bidId; - else par = 'get?internalId=' + validBidRequests[i].params.internalId + '&publisher=' + validBidRequests[i].params.publisher + '&bidId=' + validBidRequests[i].bidId; - par = par + '&vast=true'; - switch (validBidRequests[i].params.env) { - case 'test': - par = par + '&demo=true'; - url = 'https://test.outcondigital.com/ad/' + par; - break; - case 'api': - url = 'https://api.outcondigital.com/ad/' + par; - break; - case 'stg': - url = 'https://stg.outcondigital.com/ad/' + par; - break; - } - return { - method: 'GET', - url: url, - data: {} - }; - } - }, - interpretResponse: function(serverResponse, bidRequest) { - const bidResponses = []; - const bidResponse = { - requestId: serverResponse.body.bidId, - cpm: serverResponse.body.cpm, - width: serverResponse.body.creatives[0].width, - height: serverResponse.body.creatives[0].height, - creativeId: serverResponse.body.creatives[0].id, - currency: serverResponse.body.cur, - netRevenue: true, - ttl: 300, - ad: wrapDisplayUrl(serverResponse.body.creatives[0].url, serverResponse.body.type), - vastImpUrl: serverResponse.body.trackingURL, - mediaType: serverResponse.body.type - }; - if (serverResponse.body.type == 'video') { - Object.assign(bidResponse, { - vastUrl: serverResponse.body.vastURL, - ttl: 3600 - }); - } - bidResponses.push(bidResponse); - return bidResponses; - }, -} - -function wrapDisplayUrl(displayUrl, type) { - if (type == 'video') return `
`; - if (type == 'banner') return `
`; - return null; -} - -registerBidder(spec); diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 928344be74f..974fe3a4304 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -707,6 +707,7 @@ export const spec = { // @todo - what is Neustar fabrick called & where to look for it? If it's a simple value then it will automatically be ok // it is not in the table 'Bidder Adapter Implementation' on https://docs.prebid.org/dev-docs/modules/userId.html#prebidjs-adapters let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; + if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; diff --git a/modules/papyrusBidAdapter.js b/modules/papyrusBidAdapter.js deleted file mode 100644 index a27c5cf618a..00000000000 --- a/modules/papyrusBidAdapter.js +++ /dev/null @@ -1,77 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const PAPYRUS_ENDPOINT = 'https://prebid.papyrus.global'; -const PAPYRUS_CODE = 'papyrus'; - -export const spec = { - code: PAPYRUS_CODE, - - /** - * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: bid => { - return !!(bid && bid.params && bid.params.address && bid.params.placementId); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests - an array of bids - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests) { - const bidParams = []; - utils._each(validBidRequests, function(bid) { - bidParams.push({ - address: bid.params.address, - placementId: bid.params.placementId, - bidId: bid.bidId, - transactionId: bid.transactionId, - sizes: utils.parseSizesInput(bid.sizes) - }); - }); - - return { - method: 'POST', - url: PAPYRUS_ENDPOINT, - data: bidParams - }; - }, - - /** - * 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) { - const bidResponses = []; - - if (serverResponse && serverResponse.body && serverResponse.body.bids) { - serverResponse.body.bids.forEach(bid => { - const bidResponse = { - requestId: bid.id, - creativeId: bid.id, - adId: bid.id, - transactionId: bid.transactionId, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - currency: bid.currency, - netRevenue: true, - ttl: 300, - ad: bid.ad - } - bidResponses.push(bidResponse); - }); - } - - return bidResponses; - } -}; - -registerBidder(spec); diff --git a/modules/performaxBidAdapter.js b/modules/performaxBidAdapter.js deleted file mode 100644 index 8e22a0b2da9..00000000000 --- a/modules/performaxBidAdapter.js +++ /dev/null @@ -1,56 +0,0 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const CLIENT = 'hellboy:v0.0.1' -const BIDDER_CODE = 'performax'; -const BIDDER_SHORT_CODE = 'px'; -const ENDPOINT = 'https://dale.performax.cz/hb'; - -export const spec = { - code: BIDDER_CODE, - aliases: [BIDDER_SHORT_CODE], - - isBidRequestValid: function (bid) { - return !!bid.params.slotId; - }, - - buildUrl: function (validBidRequests, bidderRequest) { - const slotIds = validBidRequests.map(request => request.params.slotId); - let url = [`${ENDPOINT}?slotId[]=${slotIds.join()}`]; - url.push('client=' + CLIENT); - url.push('auctionId=' + bidderRequest.auctionId); - return url.join('&'); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - return { - method: 'POST', - url: this.buildUrl(validBidRequests, bidderRequest), - data: {'validBidRequests': validBidRequests, 'bidderRequest': bidderRequest}, - options: {contentType: 'application/json'}, - } - }, - - buildHtml: function (ad) { - const keys = Object.keys(ad.data || {}); - return ad.code.replace( - new RegExp('\\$(' + keys.join('|') + ')\\$', 'g'), - (matched, key) => ad.data[key] || matched - ); - }, - - interpretResponse: function (serverResponse, request) { - let bidResponses = []; - for (let i = 0; i < serverResponse.body.length; i++) { - const ad = serverResponse.body[i].ad; - if (ad.type === 'empty') { - logWarn(`One of ads is empty (reason=${ad.reason})`); - continue; - } - serverResponse.body[i].ad = this.buildHtml(ad); - bidResponses.push(serverResponse.body[i]); - } - return bidResponses; - } -} -registerBidder(spec); diff --git a/modules/piximediaBidAdapter.js b/modules/piximediaBidAdapter.js deleted file mode 100644 index 2617cc8fe42..00000000000 --- a/modules/piximediaBidAdapter.js +++ /dev/null @@ -1,47 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'piximedia'; -const ENDPOINT = 'https://ad.piximedia.com/prebid'; - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - return !!(bid.params && bid.params.siteId && bid.params.placementId); - }, - buildRequests: function(validBidRequests) { - return validBidRequests.map(bidRequest => { - let parseSized = utils.parseSizesInput(bidRequest.sizes); - let arrSize = parseSized[0].split('x'); - return { - method: 'GET', - url: ENDPOINT, - data: { - timestamp: utils.timestamp(), - pver: '1.0', - pbparams: JSON.stringify(bidRequest.params), - pbsizes: JSON.stringify(parseSized), - pbwidth: arrSize[0], - pbheight: arrSize[1], - pbbidid: bidRequest.bidId, - }, - }; - }); - }, - interpretResponse: function(serverResponse, request) { - const res = serverResponse.body; - const bidResponse = { - requestId: res.bidId, - cpm: parseFloat(res.cpm), - width: res.width, - height: res.height, - creativeId: res.creative_id, - currency: res.currency, - netRevenue: true, - ttl: 300, - ad: res.adm - }; - return [bidResponse]; - } -} -registerBidder(spec); diff --git a/modules/platformioBidAdapter.js b/modules/platformioBidAdapter.js deleted file mode 100644 index 314f738ef81..00000000000 --- a/modules/platformioBidAdapter.js +++ /dev/null @@ -1,307 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const NATIVE_DEFAULTS = { - TITLE_LEN: 100, - DESCR_LEN: 200, - SPONSORED_BY_LEN: 50, - IMG_MIN: 150, - ICON_MIN: 50, -}; -const DEFAULT_MIMES = ['video/mp4', 'video/webm', 'application/x-shockwave-flash', 'application/javascript']; -const VIDEO_TARGETING = ['mimes', 'skippable', 'playback_method', 'protocols', 'api']; -const DEFAULT_PROTOCOLS = [2, 3, 5, 6]; -const DEFAULT_APIS = [1, 2]; - -export const spec = { - - code: 'platformio', - supportedMediaTypes: ['banner', 'native', 'video'], - - isBidRequestValid: bid => ( - !!(bid && bid.params && bid.params.pubId && bid.params.placementId) - ), - buildRequests: (bidRequests, bidderRequest) => { - const request = { - id: bidRequests[0].bidderRequestId, - at: 2, - imp: bidRequests.map(slot => impression(slot)), - site: site(bidRequests), - app: app(bidRequests), - device: device(bidRequests), - }; - applyGdpr(bidderRequest, request); - return { - method: 'POST', - url: 'https://piohbdisp.hb.adx1.com/', - data: JSON.stringify(request), - }; - }, - interpretResponse: (response, request) => ( - bidResponseAvailable(request, response.body) - ), -}; - -function bidResponseAvailable(bidRequest, bidResponse) { - const idToImpMap = {}; - const idToBidMap = {}; - const ortbRequest = parse(bidRequest.data); - ortbRequest.imp.forEach(imp => { - idToImpMap[imp.id] = imp; - }); - if (bidResponse) { - bidResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { - idToBidMap[bid.impid] = bid; - })); - } - const bids = []; - Object.keys(idToImpMap).forEach(id => { - if (idToBidMap[id]) { - const bid = {}; - bid.requestId = id; - bid.adId = id; - bid.creativeId = id; - bid.cpm = idToBidMap[id].price; - bid.currency = bidResponse.cur; - bid.ttl = 360; - bid.netRevenue = true; - if (idToImpMap[id]['native']) { - bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); - let nurl = idToBidMap[id].nurl; - nurl = nurl.replace(/\$(%7B|\{)AUCTION_IMP_ID(%7D|\})/gi, idToBidMap[id].impid); - nurl = nurl.replace(/\$(%7B|\{)AUCTION_PRICE(%7D|\})/gi, idToBidMap[id].price); - nurl = nurl.replace(/\$(%7B|\{)AUCTION_CURRENCY(%7D|\})/gi, bidResponse.cur); - nurl = nurl.replace(/\$(%7B|\{)AUCTION_BID_ID(%7D|\})/gi, bidResponse.bidid); - bid['native']['impressionTrackers'] = [nurl]; - bid.mediaType = 'native'; - } else if (idToImpMap[id]['video']) { - bid.vastUrl = idToBidMap[id].adm; - bid.vastUrl = bid.vastUrl.replace(/\$(%7B|\{)AUCTION_PRICE(%7D|\})/gi, idToBidMap[id].price); - bid.crid = idToBidMap[id].crid; - bid.width = idToImpMap[id].video.w; - bid.height = idToImpMap[id].video.h; - bid.mediaType = 'video'; - } else if (idToImpMap[id]['banner']) { - bid.ad = idToBidMap[id].adm; - bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_IMP_ID(%7D|\})/gi, idToBidMap[id].impid); - bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_AD_ID(%7D|\})/gi, idToBidMap[id].adid); - bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_PRICE(%7D|\})/gi, idToBidMap[id].price); - bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_CURRENCY(%7D|\})/gi, bidResponse.cur); - bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_BID_ID(%7D|\})/gi, bidResponse.bidid); - bid.width = idToBidMap[id].w; - bid.height = idToBidMap[id].h; - bid.mediaType = 'banner'; - } - bids.push(bid); - } - }); - return bids; -} -function impression(slot) { - return { - id: slot.bidId, - secure: window.location.protocol === 'https:' ? 1 : 0, - 'banner': banner(slot), - 'native': nativeImpression(slot), - 'video': videoImpression(slot), - bidfloor: slot.params.bidFloor || '0.000001', - tagid: slot.params.placementId.toString(), - }; -} - -function banner(slot) { - if (slot.mediaType === 'banner' || utils.deepAccess(slot, 'mediaTypes.banner')) { - const sizes = utils.deepAccess(slot, 'mediaTypes.banner.sizes'); - if (sizes.length > 1) { - let format = []; - for (let f = 0; f < sizes.length; f++) { - format.push({'w': sizes[f][0], 'h': sizes[f][1]}); - } - return {'format': format}; - } else { - return { - w: sizes[0][0], - h: sizes[0][1] - } - } - } - return null; -} - -function videoImpression(slot) { - if (slot.mediaType === 'video' || utils.deepAccess(slot, 'mediaTypes.video')) { - const sizes = utils.deepAccess(slot, 'mediaTypes.video.playerSize'); - const video = { - w: sizes[0][0], - h: sizes[0][1], - mimes: DEFAULT_MIMES, - protocols: DEFAULT_PROTOCOLS, - api: DEFAULT_APIS, - }; - if (slot.params.video) { - Object.keys(slot.params.video).filter(param => includes(VIDEO_TARGETING, param)).forEach(param => video[param] = slot.params.video[param]); - } - return video; - } - return null; -} - -function nativeImpression(slot) { - if (slot.mediaType === 'native' || utils.deepAccess(slot, 'mediaTypes.native')) { - const assets = []; - addAsset(assets, titleAsset(1, slot.nativeParams.title, NATIVE_DEFAULTS.TITLE_LEN)); - addAsset(assets, dataAsset(2, slot.nativeParams.body, 2, NATIVE_DEFAULTS.DESCR_LEN)); - addAsset(assets, dataAsset(3, slot.nativeParams.sponsoredBy, 1, NATIVE_DEFAULTS.SPONSORED_BY_LEN)); - addAsset(assets, imageAsset(4, slot.nativeParams.icon, 1, NATIVE_DEFAULTS.ICON_MIN, NATIVE_DEFAULTS.ICON_MIN)); - addAsset(assets, imageAsset(5, slot.nativeParams.image, 3, NATIVE_DEFAULTS.IMG_MIN, NATIVE_DEFAULTS.IMG_MIN)); - return { - request: JSON.stringify({ assets }), - ver: '1.1', - }; - } - return null; -} - -function addAsset(assets, asset) { - if (asset) { - assets.push(asset); - } -} - -function titleAsset(id, params, defaultLen) { - if (params) { - return { - id, - required: params.required ? 1 : 0, - title: { - len: params.len || defaultLen, - }, - }; - } - return null; -} - -function imageAsset(id, params, type, defaultMinWidth, defaultMinHeight) { - return params ? { - id, - required: params.required ? 1 : 0, - img: { - type, - wmin: params.wmin || defaultMinWidth, - hmin: params.hmin || defaultMinHeight, - } - } : null; -} - -function dataAsset(id, params, type, defaultLen) { - return params ? { - id, - required: params.required ? 1 : 0, - data: { - type, - len: params.len || defaultLen, - } - } : null; -} - -function site(bidderRequest) { - const pubId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.pubId : '0'; - const siteId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.siteId : '0'; - const appParams = bidderRequest[0].params.app; - if (!appParams) { - return { - publisher: { - id: pubId.toString(), - domain: window.location.hostname, - }, - id: siteId.toString(), - ref: window.top.document.referrer, - page: window.location.href, - } - } - return null; -} - -function app(bidderRequest) { - const pubId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.pubId : '0'; - const appParams = bidderRequest[0].params.app; - if (appParams) { - return { - publisher: { - id: pubId.toString(), - }, - id: appParams.id, - name: appParams.name, - bundle: appParams.bundle, - storeurl: appParams.storeUrl, - domain: appParams.domain, - } - } - return null; -} - -function device(bidderRequest) { - const lat = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.latitude : ''; - const lon = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.longitude : ''; - const ifa = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.ifa : ''; - return { - dnt: utils.getDNT() ? 1 : 0, - ua: navigator.userAgent, - language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), - w: (window.screen.width || window.innerWidth), - h: (window.screen.height || window.innerHeigh), - geo: { - lat: lat, - lon: lon, - }, - ifa: ifa, - }; -} - -function parse(rawResponse) { - try { - if (rawResponse) { - return JSON.parse(rawResponse); - } - } catch (ex) { - utils.logError('platformio.parse', 'ERROR', ex); - } - return null; -} - -function applyGdpr(bidderRequest, ortbRequest) { - if (bidderRequest && bidderRequest.gdprConsent) { - ortbRequest.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0 } }; - ortbRequest.user = { ext: { consent: bidderRequest.gdprConsent.consentString } }; - } -} - -function nativeResponse(imp, bid) { - if (imp['native']) { - const nativeAd = parse(bid.adm); - const keys = {}; - keys.image = {}; - keys.icon = {}; - if (nativeAd && nativeAd['native'] && nativeAd['native'].assets) { - nativeAd['native'].assets.forEach(asset => { - keys.title = asset.title ? asset.title.text : keys.title; - keys.body = asset.data && asset.id === 2 ? asset.data.value : keys.body; - keys.sponsoredBy = asset.data && asset.id === 3 ? asset.data.value : keys.sponsoredBy; - keys.icon.url = asset.img && asset.id === 4 ? asset.img.url : keys.icon.url; - keys.icon.width = asset.img && asset.id === 4 ? asset.img.w : keys.icon.width; - keys.icon.height = asset.img && asset.id === 4 ? asset.img.h : keys.icon.height; - keys.image.url = asset.img && asset.id === 5 ? asset.img.url : keys.image.url; - keys.image.width = asset.img && asset.id === 5 ? asset.img.w : keys.image.width; - keys.image.height = asset.img && asset.id === 5 ? asset.img.h : keys.image.height; - }); - if (nativeAd['native'].link) { - keys.clickUrl = encodeURIComponent(nativeAd['native'].link.url); - } - return keys; - } - } - return null; -} - -registerBidder(spec); diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index ad5cb70cc23..bbf301fb072 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -602,6 +602,8 @@ const OPEN_RTB_PROTOCOL = { }); mediaTypes['banner'] = {format}; + + if (bannerParams.pos) mediaTypes['banner'].pos = bannerParams.pos; } if (!utils.isEmpty(videoParams)) { @@ -728,7 +730,8 @@ const OPEN_RTB_PROTOCOL = { source: {tid: s2sBidRequest.tid}, tmax: s2sConfig.timeout, imp: imps, - test: getConfig('debug') ? 1 : 0, + // to do: add setconfig option to pass test = 1 + test: 0, ext: { prebid: { // set ext.prebid.auctiontimestamp with the auction timestamp. Data type is long integer. @@ -746,6 +749,11 @@ const OPEN_RTB_PROTOCOL = { // Sets pbjs version, can be overwritten below if channel exists in s2sConfig.extPrebid 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: 1}) + } + // s2sConfig video.ext.prebid is passed through openrtb to PBS if (s2sConfig.extPrebid && typeof s2sConfig.extPrebid === 'object') { request.ext.prebid = Object.assign(request.ext.prebid, s2sConfig.extPrebid); diff --git a/modules/projectLimeLightBidAdapter.js b/modules/projectLimeLightBidAdapter.js deleted file mode 100644 index 1beba906917..00000000000 --- a/modules/projectLimeLightBidAdapter.js +++ /dev/null @@ -1,122 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'project-limelight'; - -/** - * Determines whether or not the given bid response is valid. - * - * @param {object} bid The bid to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ -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.vastXml || bid.vastUrl); - } - return false; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - /** - * 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: (bid) => { - return Boolean(bid.bidId && bid.params); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: (validBidRequests, bidderRequest) => { - let winTop; - try { - winTop = window.top; - winTop.location.toString(); - } catch (e) { - utils.logMessage(e); - winTop = window; - } - const placements = utils.groupBy(validBidRequests.map(bidRequest => buildPlacement(bidRequest)), 'host') - return Object.keys(placements) - .map(host => buildRequest(winTop, host, placements[host].map(placement => placement.adUnit))); - }, - - onBidWon: (bid) => { - const cpm = bid.pbMg; - if (bid.nurl !== '') { - bid.nurl = bid.nurl.replace( - /\$\{AUCTION_PRICE\}/, - cpm - ); - ajax(bid.nurl, null); - }; - }, - - /** - * 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: (bidResponses) => { - const res = []; - const bidResponsesBody = bidResponses.body; - const len = bidResponsesBody.length; - for (let i = 0; i < len; i++) { - const bid = bidResponsesBody[i]; - if (isBidResponseValid(bid)) { - res.push(bid); - } - } - return res; - }, -}; - -registerBidder(spec); - -function buildRequest(winTop, host, adUnits) { - return { - method: 'POST', - url: `https://${host}/hb`, - data: { - secure: (location.protocol === 'https:'), - deviceWidth: winTop.screen.width, - deviceHeight: winTop.screen.height, - adUnits: adUnits - } - } -} - -function buildPlacement(bidRequest) { - return { - host: bidRequest.params.host, - adUnit: { - id: bidRequest.params.adUnitId, - bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, - sizes: bidRequest.sizes.map(size => { - return { - width: size[0], - height: size[1] - } - }), - type: bidRequest.params.adUnitType.toUpperCase() - } - } -} diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js deleted file mode 100644 index ff07ee9ede9..00000000000 --- a/modules/proxistoreBidAdapter.js +++ /dev/null @@ -1,151 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -const BIDDER_CODE = 'proxistore'; -const PROXISTORE_VENDOR_ID = 418; - -function _createServerRequest(bidRequests, bidderRequest) { - var sizeIds = []; - bidRequests.forEach(function (bid) { - var sizeId = { - id: bid.bidId, - sizes: bid.sizes.map(function (size) { - return { - width: size[0], - height: size[1], - }; - }), - floor: _assignFloor(bid), - segments: _assignSegments(bid), - }; - sizeIds.push(sizeId); - }); - var payload = { - auctionId: bidRequests[0].auctionId, - transactionId: bidRequests[0].auctionId, - bids: sizeIds, - website: bidRequests[0].params.website, - language: bidRequests[0].params.language, - gdpr: { - applies: false, - consentGiven: false - }, - }; - - if (bidderRequest && bidderRequest.gdprConsent) { - const { gdprConsent } = bidderRequest; - if (typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) { - payload.gdpr.applies = true; - } - - if (typeof gdprConsent.consentString === 'string' && gdprConsent.consentString) { - payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; - } - if (gdprConsent.vendorData) { - const {vendorData} = gdprConsent; - const {apiVersion} = gdprConsent; - if (apiVersion === 2 && vendorData.vendor && vendorData.vendor.consents && typeof vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined') { - payload.gdpr.consentGiven = !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; - } else if (apiVersion === 1 && vendorData.vendorConsents && typeof vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined') { - payload.gdpr.consentGiven = !!vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; - } - } - } - - const options = { - contentType: 'application/json', - withCredentials: payload.gdpr.consentGiven, - }; - - const endPointUri = payload.gdpr.consentGiven || !payload.gdpr.applies - ? `https://abs.proxistore.com/${payload.language}/v3/rtb/prebid/multi` - : `https://abs.cookieless-proxistore.com/${payload.language}/v3/rtb/prebid/multi`; - - return { - method: 'POST', - url: endPointUri, - data: JSON.stringify(payload), - options: options, - }; -} - -function _assignSegments(bid) { - if (bid.ortb2 && bid.ortb2.user && bid.ortb2.user.ext && bid.ortb2.user.ext.data) { - return bid.ortb2.user.ext.data || {segments: [], contextual_categories: {}}; - } - return {segments: [], contextual_categories: {}}; -} - -function _createBidResponse(response) { - return { - requestId: response.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - ad: response.ad, - ttl: response.ttl, - creativeId: response.creativeId, - currency: response.currency, - netRevenue: response.netRevenue, - vastUrl: response.vastUrl, - vastXml: response.vastXml, - dealId: response.dealId, - }; -} -/** - * Determines whether or not the given bid request is valid. - * - * @param bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - -function isBidRequestValid(bid) { - return !!(bid.params.website && bid.params.language); -} -/** - * Make a server request from the list of BidRequests. - * - * @param bidRequests - an array of bids - * @param bidderRequest - * @return ServerRequest Info describing the request to the server. - */ - -function buildRequests(bidRequests, bidderRequest) { - var request = _createServerRequest(bidRequests, bidderRequest); - return request; -} -/** - * Unpack the response from the server into a list of bids. - * - * @param serverResponse A successful response from the server. - * @param bidRequest Request original server request - * @return An array of bids which were nested inside the server. - */ - -function interpretResponse(serverResponse, bidRequest) { - return serverResponse.body.map(_createBidResponse); -} - -function _assignFloor(bid) { - if (typeof bid.getFloor === 'function') { - var floorInfo = bid.getFloor({ - currency: 'EUR', - mediaType: 'banner', - size: '*', - }); - - if (floorInfo.currency === 'EUR') { - return floorInfo.floor; - } - } - - return null; -} - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: isBidRequestValid, - buildRequests: buildRequests, - interpretResponse: interpretResponse, - -}; - -registerBidder(spec); diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js deleted file mode 100644 index 95e539a4d6a..00000000000 --- a/modules/pubCommonIdSystem.js +++ /dev/null @@ -1,345 +0,0 @@ -/** - * This module adds PubCommonId to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/pubCommonIdSystem - * @requires module:modules/userId - */ - -import * as utils from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; -import { uspDataHandler, coppaDataHandler } from '../src/adapterManager.js'; - -const PUB_COMMON_ID = 'PublisherCommonId'; -const MODULE_NAME = 'pubCommonId'; - -const COOKIE = 'cookie'; -const LOCAL_STORAGE = 'html5'; -const SHAREDID_OPT_OUT_VALUE = '00000000000000000000000000'; -const SHAREDID_URL = 'https://id.sharedid.org/id'; -const SHAREDID_SUFFIX = '_sharedid'; -const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const SHAREDID_DEFAULT_STATE = false; -const GVLID = 887; - -const storage = getStorageManager(GVLID, 'pubCommonId'); - -/** - * Store sharedid in either cookie or local storage - * @param {Object} config Need config.storage object to derive key, expiry time, and storage type. - * @param {string} value Shareid value to store - */ - -function storeData(config, value) { - try { - if (value) { - const key = config.storage.name + SHAREDID_SUFFIX; - const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); - - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - storage.setCookie(key, value, expiresStr, 'LAX', pubCommonIdSubmodule.domainOverride()); - } - } else if (config.storage.type === LOCAL_STORAGE) { - if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(`${key}_exp`, expiresStr); - storage.setDataInLocalStorage(key, value); - } - } - } - } catch (error) { - utils.logError(error); - } -} - -/** - * Read sharedid from cookie or local storage - * @param config Need config.storage to derive key and storage type - * @return {string} - */ -function readData(config) { - try { - const key = config.storage.name + SHAREDID_SUFFIX; - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - return storage.getCookie(key); - } - } else if (config.storage.type === LOCAL_STORAGE) { - if (storage.hasLocalStorage()) { - const expValue = storage.getDataFromLocalStorage(`${key}_exp`); - if (!expValue) { - return storage.getDataFromLocalStorage(key); - } else if ((new Date(expValue)).getTime() - Date.now() > 0) { - return storage.getDataFromLocalStorage(key) - } - } - } - } catch (error) { - utils.logError(error); - } -} - -/** - * Delete sharedid from cookie or local storage - * @param config Need config.storage to derive key and storage type - */ -function delData(config) { - try { - const key = config.storage.name + SHAREDID_SUFFIX; - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - storage.setCookie(key, '', EXPIRED_COOKIE_DATE); - } - } else if (config.storage.type === LOCAL_STORAGE) { - storage.removeDataFromLocalStorage(`${key}_exp`); - storage.removeDataFromLocalStorage(key); - } - } catch (error) { - utils.logError(error); - } -} - -/** - * setup success and error handler for sharedid callback thru ajax - * @param {string} pubcid Current pubcommon id - * @param {function} callback userId module callback. - * @param {Object} config Need config.storage to derive sharedid storage params - * @return {{success: success, error: error}} - */ - -function handleResponse(pubcid, callback, config) { - return { - success: function (responseBody) { - if (responseBody) { - try { - let responseObj = JSON.parse(responseBody); - utils.logInfo('PubCommonId: Generated SharedId: ' + responseObj.sharedId); - if (responseObj.sharedId) { - if (responseObj.sharedId !== SHAREDID_OPT_OUT_VALUE) { - // Store sharedId locally - storeData(config, responseObj.sharedId); - } else { - // Delete local copy if the user has opted out - delData(config); - } - } - // Pass pubcid even though there is no change in order to trigger decode - callback(pubcid); - } catch (error) { - utils.logError(error); - } - } - }, - error: function (statusText, responseBody) { - utils.logInfo('PubCommonId: failed to get sharedid'); - } - } -} - -/** - * Builds and returns the shared Id URL with attached consent data if applicable - * @param {Object} consentData - * @return {string} - */ -function sharedIdUrl(consentData) { - const usPrivacyString = uspDataHandler.getConsentData(); - let sharedIdUrl = SHAREDID_URL; - if (usPrivacyString && typeof usPrivacyString === 'string') { - sharedIdUrl = `${SHAREDID_URL}?us_privacy=${usPrivacyString}`; - } - if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return sharedIdUrl; - if (usPrivacyString) { - sharedIdUrl = `${sharedIdUrl}&gdpr=1&gdpr_consent=${consentData.consentString}` - return sharedIdUrl; - } - sharedIdUrl = `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}`; - return sharedIdUrl -} - -/** - * Wraps pixelCallback in order to call sharedid sync - * @param {string} pubcid Pubcommon id value - * @param {function|undefined} pixelCallback fires a pixel to first party server - * @param {Object} config Need config.storage to derive sharedid storage params. - * @return {function(...[*]=)} - */ - -function getIdCallback(pubcid, pixelCallback, config, consentData) { - return function (callback) { - if (typeof pixelCallback === 'function') { - pixelCallback(); - } - ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); - } -} - -/** @type {Submodule} */ -export const pubCommonIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - /** - * Vendor id of prebid - * @type {Number} - */ - gvlid: GVLID, - /** - * Return a callback function that calls the pixelUrl with id as a query parameter - * @param pixelUrl - * @param id - * @returns {function} - */ - makeCallback: function (pixelUrl, id = '') { - if (!pixelUrl) { - return; - } - - // Use pubcid as a cache buster - const urlInfo = utils.parseUrl(pixelUrl); - urlInfo.search.id = encodeURIComponent('pubcid:' + id); - const targetUrl = utils.buildUrl(urlInfo); - - return function () { - utils.triggerPixel(targetUrl); - }; - }, - /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} value - * @param {SubmoduleConfig} config - * @returns {{pubcid:string}} - */ - decode(value, config) { - const idObj = {'pubcid': value}; - const {params: {enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; - - if (enableSharedId) { - const sharedId = readData(config); - if (sharedId) idObj['sharedid'] = {id: sharedId}; - } - - return idObj; - }, - /** - * performs action to obtain id - * @function - * @param {SubmoduleConfig} [config] Config object with params and storage properties - * @param {Object} consentData - * @param {string} storedId Existing pubcommon id - * @returns {IdResponse} - */ - getId: function (config = {}, consentData, storedId) { - const coppa = coppaDataHandler.getCoppa(); - if (coppa) { - utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); - return; - } - const {params: {create = true, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; - let newId = storedId; - if (!newId) { - try { - if (typeof window[PUB_COMMON_ID] === 'object') { - // If the page includes its own pubcid module, then save a copy of id. - newId = window[PUB_COMMON_ID].getId(); - } - } catch (e) { - } - - if (!newId) newId = (create && utils.hasDeviceAccess()) ? utils.generateUUID() : undefined; - } - - const pixelCallback = this.makeCallback(pixelUrl, newId); - const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback; - - return {id: newId, callback: combinedCallback}; - }, - /** - * performs action to extend an id. There are generally two ways to extend the expiration time - * of stored id: using pixelUrl or return the id and let main user id module write it again with - * the new expiration time. - * - * PixelUrl, if defined, should point back to a first party domain endpoint. On the server - * side, there is either a plugin, or customized logic to read and write back the pubcid cookie. - * The extendId function itself should return only the callback, and not the id itself to avoid - * having the script-side overwriting server-side. This applies to both pubcid and sharedid. - * - * On the other hand, if there is no pixelUrl, then the extendId should return storedId so that - * its expiration time is updated. Sharedid, however, will have to be updated by this submodule - * separately. - * - * @function - * @param {SubmoduleParams} [config] - * @param {ConsentData|undefined} consentData - * @param {Object} storedId existing id - * @returns {IdResponse|undefined} - */ - extendId: function(config = {}, consentData, storedId) { - const coppa = coppaDataHandler.getCoppa(); - if (coppa) { - utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); - return; - } - const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; - - if (extend) { - try { - if (typeof window[PUB_COMMON_ID] === 'object') { - if (enableSharedId) { - // If the page includes its own pubcid module, then there is nothing to do - // except to update sharedid's expiration time - storeData(config, readData(config)); - } - return; - } - } catch (e) { - } - - if (pixelUrl) { - const callback = this.makeCallback(pixelUrl, storedId); - return {callback: callback}; - } else { - if (enableSharedId) { - // Update with the same value to extend expiration time - storeData(config, readData(config)); - } - return {id: storedId}; - } - } - }, - - /** - * @param {string} domain - * @param {HTMLDocument} document - * @return {(string|undefined)} - */ - domainOverride: function () { - const domainElements = document.domain.split('.'); - const cookieName = `_gd${Date.now()}`; - for (let i = 0, topDomain, testCookie; i < domainElements.length; i++) { - const nextDomain = domainElements.slice(i).join('.'); - - // write test cookie - storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); - - // read test cookie to verify domain was valid - testCookie = storage.getCookie(cookieName); - - // delete test cookie - storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); - - if (testCookie === '1') { - // cookie was written successfully using test domain so the topDomain is updated - topDomain = nextDomain; - } else { - // cookie failed to write using test domain so exit by returning the topDomain - return topDomain; - } - } - } -}; - -submodule('userId', pubCommonIdSubmodule); diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js deleted file mode 100644 index 35d75e96f95..00000000000 --- a/modules/pubxBidAdapter.js +++ /dev/null @@ -1,94 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -const BIDDER_CODE = 'pubx'; -const BID_ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; -const USER_SYNC_URL = 'https://api.primecaster.net/primecaster_dmppv.html' -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - if (!(bid.params.sid)) { - return false; - } else { return true } - }, - buildRequests: function(validBidRequests) { - return validBidRequests.map(bidRequest => { - const bidId = bidRequest.bidId; - const params = bidRequest.params; - const sid = params.sid; - const payload = { - sid: sid - }; - return { - id: bidId, - method: 'GET', - url: BID_ENDPOINT, - data: payload, - } - }); - }, - interpretResponse: function(serverResponse, bidRequest) { - const body = serverResponse.body; - const bidResponses = []; - if (body.cid) { - const bidResponse = { - requestId: bidRequest.id, - cpm: body.cpm, - currency: body.currency, - width: body.width, - height: body.height, - creativeId: body.cid, - netRevenue: true, - ttl: body.TTL, - ad: body.adm - }; - bidResponses.push(bidResponse); - } else {}; - return bidResponses; - }, - /** - * Determine which user syncs should occur - * @param {object} syncOptions - * @param {array} serverResponses - * @returns {array} User sync pixels - */ - getUserSyncs: function (syncOptions, serverResponses) { - const kwTag = document.getElementsByName('keywords'); - let kwString = ''; - let kwEnc = ''; - let titleContent = !!document.title && document.title; - let titleEnc = ''; - let descContent = !!document.getElementsByName('description') && !!document.getElementsByName('description')[0] && document.getElementsByName('description')[0].content; - let descEnc = ''; - const pageUrl = location.href.replace(/\?.*$/, ''); - const pageEnc = encodeURIComponent(pageUrl); - const refUrl = document.referrer.replace(/\?.*$/, ''); - const refEnc = encodeURIComponent(refUrl); - if (kwTag.length) { - const kwContents = kwTag[0].content; - if (kwContents.length > 20) { - const kwArray = kwContents.substr(0, 20).split(','); - kwArray.pop(); - kwString = kwArray.join(); - } else { - kwString = kwContents; - } - kwEnc = encodeURIComponent(kwString) - } else { } - if (titleContent) { - if (titleContent.length > 30) { - titleContent = titleContent.substr(0, 30); - } else {}; - titleEnc = encodeURIComponent(titleContent); - } else { }; - if (descContent) { - if (descContent.length > 60) { - descContent = descContent.substr(0, 60); - } else {}; - descEnc = encodeURIComponent(descContent); - } else { }; - return (syncOptions.iframeEnabled) ? [{ - type: 'iframe', - url: USER_SYNC_URL + '?pkw=' + kwEnc + '&pd=' + descEnc + '&pu=' + pageEnc + '&pref=' + refEnc + '&pt=' + titleEnc - }] : []; - } -} -registerBidder(spec); diff --git a/modules/reklamstoreBidAdapter.js b/modules/reklamstoreBidAdapter.js deleted file mode 100644 index 3d78cf95978..00000000000 --- a/modules/reklamstoreBidAdapter.js +++ /dev/null @@ -1,148 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'reklamstore'; -const ENDPOINT_URL = 'https://ads.rekmob.com/m/prebid'; -const CURRENCY = 'USD'; -const TIME_TO_LIVE = 360; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [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. - */ - isBidRequestValid: function (bid) { - return !!(bid.params.regionId); - }, - /** - * 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 url = bidderRequest.refererInfo.referer; - let requests = []; - utils._each(validBidRequests, function(bid) { - requests.push({ - method: 'GET', - url: ENDPOINT_URL, - data: { - regionId: bid.params.regionId, - dt: getDeviceType(), - os: getOS(), - ref: extractDomain(url), - _: (new Date().getTime()), - mobile_web: 1 - }, - bidId: bid.bidId - }); - }); - return requests; - }, - - /** - * 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) { - try { - const bidResponse = serverResponse.body; - const bidResponses = []; - if (bidResponse) { - bidResponses.push({ - requestId: bidRequest.bidId, - cpm: parseFloat(bidResponse.cpm), - width: bidResponse.w, - height: bidResponse.h, - creativeId: bidResponse.adId || 1, - currency: CURRENCY, - netRevenue: true, - ttl: TIME_TO_LIVE, - ad: bidResponse.ad - }); - } - return bidResponses; - } catch (err) { - utils.logError(err); - return []; - } - }, - /** - * 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) { - const syncs = []; - utils._each(serverResponses, function(bidResponse) { - utils._each(bidResponse.body.syncs, function(sync) { - if (syncOptions.pixelEnabled && sync.type == 'image') { - syncs.push({ - type: sync.type, - url: sync.url - }); - } else if (syncOptions.iframeEnabled && sync.type == 'iframe') { - syncs.push({ - type: sync.type, - url: sync.url - }); - } - }); - }); - return syncs; - } -} -registerBidder(spec); - -function getDeviceType() { - let PHONE = 0; - let TABLET = 2; - let DESKTOP = 3; - if (isPhone()) { - return PHONE; - } else if (isTablet()) { - return TABLET; - } else { - return DESKTOP; - } -} -function isPhone() { - var check = false; - (function (a) { if (/(android|bb\d+|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/i.test(a) || /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\-/i.test(a.substr(0, 4))) check = true })(navigator.userAgent || navigator.vendor || window.opera); - return check; -} -function isTablet() { - var check = false; - (function(a) { if (/ipad|android|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(a)) { check = true; } })(navigator.userAgent || navigator.vendor || window.opera); - return check; -} -function getOS() { - var ua = navigator.userAgent; - if (ua.match(/(iPhone|iPod|iPad)/)) { - return '1'; - } else if (ua.match(/Android/)) { - return '0'; - } else { - return '3'; - } -} -function extractDomain(url) { - var domain; - if (url.indexOf('://') > -1) { - domain = url.split('/')[2]; - } else { - domain = url.split('/')[0]; - } - domain = domain.split(':')[0]; - return domain; -} diff --git a/modules/reloadBidAdapter.js b/modules/reloadBidAdapter.js deleted file mode 100644 index 94ea4be281f..00000000000 --- a/modules/reloadBidAdapter.js +++ /dev/null @@ -1,419 +0,0 @@ -import { - registerBidder -} - from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); - -const BIDDER_CODE = 'reload'; -const VERSION_ADAPTER = '1.10'; -export const spec = { - code: BIDDER_CODE, - png: {}, - /** - * 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 && bid.params.plcmID && bid.params.partID && 'opdomID' in bid.params && - 'bsrvID' in bid.params && bid.params.bsrvID >= 0 && bid.params.bsrvID <= 99); - }, - /** - * 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) { - let vRequests = []; - let bidReq = { - id: Math.random().toString(10).substring(2), - imp: [] - }; - let vgdprConsent = null; - if (utils.deepAccess(bidderRequest, 'gdprConsent')) { - vgdprConsent = bidderRequest.gdprConsent; - } - let vPrxClientTool = null; - let vSrvUrl = null; - for (let vIdx = 0; vIdx < validBidRequests.length; vIdx++) { - let bidRequest = validBidRequests[vIdx]; - vPrxClientTool = new ReloadClientTool({ - prxVer: VERSION_ADAPTER, - prxType: 'bd', - plcmID: bidRequest.params.plcmID, - partID: bidRequest.params.partID, - opdomID: bidRequest.params.opdomID, - bsrvID: bidRequest.params.bsrvID, - gdprObj: vgdprConsent, - mediaObj: bidRequest.mediaTypes, - wnd: utils.getWindowTop(), - rtop: utils.deepAccess(bidderRequest, 'refererInfo.reachedTop') || false - }); - if (vSrvUrl === null) vSrvUrl = vPrxClientTool.getSrvUrl(); - let vImpression = { - id: bidRequest.bidId, - bidId: bidRequest.bidId, - adUnitCode: bidRequest.adUnitCode, - transactionId: bidRequest.transactionId, - bidderRequestId: bidRequest.bidderRequestId, - auctionId: bidRequest.auctionId, - banner: { - ext: { - type: bidRequest.params.type || 'pcm', - pcmdata: vPrxClientTool.getPCMObj() - } - } - }; - bidReq.imp.push(vImpression); - } - if (bidReq.imp.length > 0) { - const payloadString = JSON.stringify(bidReq); - vRequests.push({ - method: 'POST', - url: vSrvUrl, - data: payloadString - }); - } - return vRequests; - }, - /** - * 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 = []; - for (let vIdx = 0; vIdx < serverBody.seatbid.length; vIdx++) { - let vSeatBid = serverBody.seatbid[vIdx]; - for (let vIdxBid = 0; vIdxBid < vSeatBid.bid.length; vIdxBid++) { - let vBid = vSeatBid.bid[vIdxBid]; - let vPrxClientTool = new ReloadClientTool({ - plcmID: vBid.ext.plcmID, - partID: vBid.ext.partID, - opdomID: vBid.ext.opdomID, - bsrvID: vBid.ext.bsrvID - }); - vPrxClientTool.setPCMObj(vBid.ext.pcmdata); - if (vPrxClientTool.getBP() > 0) { - let bidResponse = { - requestId: vBid.impid, - ad: vPrxClientTool.getAM(), - cpm: vPrxClientTool.getBP() / 100, - width: vPrxClientTool.getW(), - height: vPrxClientTool.getH(), - creativeId: vBid.id, - currency: vPrxClientTool.getBC(), - ttl: 300, - netRevenue: true - }; - bidResponses.push(bidResponse); - this.png[vBid.ext.adUnitCode] = vPrxClientTool.getPingUrl('bidwon'); - } - } - } - return bidResponses; - }, - /** - * 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) { - if (typeof this.png[bid.adUnitCode] !== 'string' || this.png[bid.adUnitCode] === '') return; - (new Image()).src = this.png[bid.adUnitCode]; - } -}; - -function ReloadClientTool(args) { - var that = this; - var _pcmClientVersion = '120'; - var _pcmFilePref = 'prx_root_'; - var _resFilePref = 'prx_pnws_'; - var _pcmInputObjVers = '120'; - var _instObj = null; - var _status = 'NA'; - var _message = ''; - var _log = ''; - var _memFile = _getMemFile(); - - if (_memFile.status !== 'ok') { - _log += 'WARNING: clnt-int mem file initialized\n'; - } - - that.getPCMObj = function () { - return { - thisVer: _pcmInputObjVers, - statStr: _memFile.statStr, - plcmData: _getPlcmData(), - clntData: _getClientData(args.wnd, args.rtop), - resultData: _getRD(), - gdprObj: _getGdpr(), - mediaObj: _getMediaObj(), - proxetString: null, - dboData: null, - plcmSett: null, - }; - }; - - that.setPCMObj = function (obj) { - if (obj.thisVer !== '100') { - _status = 'error'; - _message = 'incomp_output_obj_version'; - _log += ' ERROR incomp_output_obj_version'; - return; - } - - _status = obj.status; - _message = obj.message; - _log += ' ' + obj.log; - - if (obj.status !== 'ok') return; - - _saveMemFile(obj.statStr, obj.srvUrl); - _instObj = obj.instr; - }; - - that.getSrvUrl = function () { - var effSrvUrl = getBidServerUrl(0); - - if (isNaN(parseInt(args.bsrvID)) !== true) effSrvUrl = getBidServerUrl(parseInt(args.bsrvID)); - - if (typeof _memFile.srvUrl === 'string' && _memFile.srvUrl !== '') effSrvUrl = _memFile.srvUrl; - - return 'https://' + effSrvUrl + '/bid'; - - function getBidServerUrl (idx) { - return 'bidsrv' + getTwoDigitString(idx) + '.reload.net'; - - function getTwoDigitString (idx) { - if (idx >= 10) return '' + idx; - else return '0' + idx; - } - } - }; - - that.getMT = function () { - return _checkInstProp('mtype', 'dsp'); - }; - - that.getW = function () { - return _checkInstProp('width', 0); - }; - - that.getH = function () { - return _checkInstProp('height', 0); - }; - - that.getBP = function () { - return _checkInstProp('prc', 0); - }; - - that.getBC = function () { - return _checkInstProp('cur', 'USD'); - }; - - that.getAM = function () { - return _checkInstProp('am', null); - }; - - that.getPingUrl = function (pingName) { - var pingData = _checkInstProp('pingdata', {}); - if (pingData[pingName] !== 'undefined') return pingData[pingName]; - return ''; - }; - - that.setRD = function (data) { - return _setRD(data); - }; - - that.getStat = function () { - return _status; - }; - - that.getMsg = function () { - return _message; - }; - - that.getLog = function () { - return _log; - }; - - function _checkInstProp (key, def) { - if (_instObj === null) return def; - if (typeof _instObj === 'undefined') return def; - if (_instObj.go !== true) return def; - if (typeof _instObj[key] === 'undefined') return def; - return _instObj[key]; - } - - function _getPlcmData () { - return { - prxVer: args.prxVer, - prxType: args.prxType, - plcmID: args.plcmID, - partID: args.partID, - opdomID: args.opdomID, - bsrvID: args.bsrvID, - dmod: args.dmod, - lmod: args.lmod, - lplcmID: args.lplcmID, - }; - } - - function _getClientData (wnd, rtop) { - return { - version: 200, - locTime: Date.now(), - winInfo: _winInf(wnd), - envInfo: getEnvInfo(), - topw: rtop === true, - prot: wnd.document.location.protocol, - host: wnd.document.location.host, - title: wnd.document.title, - }; - - function _winInf (wnd) { - return { - phs: { - w: wnd.screen.width, - h: wnd.screen.height - }, - avl: { - w: wnd.screen.availWidth, - h: wnd.screen.availHeight - }, - inr: { - w: wnd.innerWidth, - h: wnd.innerHeight - }, - bdy: { - w: wnd.document.body.clientWidth, - h: wnd.document.body.clientHeight - } - }; - } - - function getEnvInfo() { - return { - userAgent: navigator.userAgent, - appName: navigator.appName, - appVersion: navigator.appVersion - }; - } - } - - function _getMemFile () { - try { - var memFileObj = _getItem(_getMemFileName()); - - if (memFileObj === null) throw { s: 'init' }; - - if (typeof memFileObj.statStr !== 'string') throw { s: 'error' }; - if (typeof memFileObj.srvUrl !== 'string') throw { s: 'error' }; - - memFileObj.status = 'ok'; - - return memFileObj; - } catch (err) { - var retObj = { - statStr: null, - srvUrl: null - }; - retObj.status = err.s; - - return retObj; - } - } - - function _saveMemFile (statStr, srvUrl) { - try { - var fileData = { - statStr: statStr, - srvUrl: srvUrl, - }; - _setItem(_getMemFileName(), fileData); - return true; - } catch (err) { - return false; - } - } - - function _getMemFileName () { - return _pcmFilePref + args.plcmID + '_' + args.partID; - } - - function _getRD () { - try { - return _getItem(_getResltStatusFileName()); - } catch (err) { - return null; - } - } - - function _setRD (fileData) { - try { - _setItem(_getResltStatusFileName(), fileData); - return true; - } catch (err) { - return false; - } - } - - function _getGdpr() { - return args.gdprObj; - } - - function _getMediaObj() { - return args.mediaObj; - } - - function _getResltStatusFileName () { - if (args.lmod === true) return _resFilePref + args.lplcmID + '_' + args.partID; - else return _resFilePref + args.plcmID + '_' + args.partID; - } - - function _setItem (name, data) { - var stgFileObj = { - ver: _pcmClientVersion, - ts: Date.now(), - }; - - if (typeof data === 'string') { - stgFileObj.objtype = false; - stgFileObj.memdata = data; - } else { - stgFileObj.objtype = true; - stgFileObj.memdata = JSON.stringify(data); - } - - var stgFileStr = JSON.stringify(stgFileObj); - - storage.setDataInLocalStorage(name, stgFileStr); - - return true; - } - - function _getItem (name) { - try { - var obStgFileStr = storage.getDataFromLocalStorage(name); - if (obStgFileStr === null) return null; - - var stgFileObj = JSON.parse(obStgFileStr); - - if (stgFileObj.ver !== _pcmClientVersion) throw { message: 'version_error' }; - - if (stgFileObj.objtype === true) return JSON.parse(stgFileObj.memdata); - else return '' + stgFileObj.memdata; - } catch (err) { - return null; - } - } -}; - -registerBidder(spec); diff --git a/modules/resultsmediaBidAdapter.js b/modules/resultsmediaBidAdapter.js deleted file mode 100644 index beb9991e1e2..00000000000 --- a/modules/resultsmediaBidAdapter.js +++ /dev/null @@ -1,269 +0,0 @@ -'use strict'; - -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; - -function ResultsmediaAdapter() { - this.code = 'resultsmedia'; - this.aliases = ['resultsmedia']; - this.supportedMediaTypes = [VIDEO, BANNER]; - - let SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6]; - let SUPPORTED_VIDEO_MIMES = ['video/mp4']; - let SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4]; - let SUPPORTED_VIDEO_DELIVERY = [1]; - let SUPPORTED_VIDEO_API = [1, 2, 5]; - let slotsToBids = {}; - let that = this; - let version = '2.1'; - - this.isBidRequestValid = function (bid) { - return !!(bid.params && bid.params.zoneId); - }; - - this.getUserSyncs = function (syncOptions, responses, gdprConsent) { - return []; - }; - - function frameImp(BRs, bidderRequest) { - var impList = []; - var isSecure = 0; - if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.stack.length) { - // clever trick to get the protocol - var el = document.createElement('a'); - el.href = bidderRequest.refererInfo.stack[0]; - isSecure = (el.protocol == 'https:') ? 1 : 0; - } - for (var i = 0; i < BRs.length; i++) { - slotsToBids[BRs[i].adUnitCode] = BRs[i]; - var impObj = {}; - impObj.id = BRs[i].adUnitCode; - impObj.secure = isSecure; - - if (utils.deepAccess(BRs[i], 'mediaTypes.banner') || utils.deepAccess(BRs[i], 'mediaType') === 'banner') { - let banner = frameBanner(BRs[i]); - if (banner) { - impObj.banner = banner; - } - } - if (utils.deepAccess(BRs[i], 'mediaTypes.video') || utils.deepAccess(BRs[i], 'mediaType') === 'video') { - impObj.video = frameVideo(BRs[i]); - } - if (!(impObj.banner || impObj.video)) { - continue; - } - impObj.ext = frameExt(BRs[i]); - impList.push(impObj); - } - return impList; - } - - function frameSite(bidderRequest) { - var site = { - domain: '', - page: '', - ref: '' - } - if (bidderRequest && bidderRequest.refererInfo) { - var ri = bidderRequest.refererInfo; - site.ref = ri.referer; - - if (ri.stack.length) { - site.page = ri.stack[ri.stack.length - 1]; - - // clever trick to get the domain - var el = document.createElement('a'); - el.href = ri.stack[0]; - site.domain = el.hostname; - } - } - return site; - } - - function frameDevice() { - return { - ua: navigator.userAgent, - ip: '', // Empty Ip string is required, server gets the ip from HTTP header - dnt: utils.getDNT() ? 1 : 0, - } - } - - function getValidSizeSet(dimensionList) { - let w = parseInt(dimensionList[0]); - let h = parseInt(dimensionList[1]); - // clever check for NaN - if (! (w !== w || h !== h)) { // eslint-disable-line - return [w, h]; - } - return false; - } - - function frameBanner(adUnit) { - // adUnit.sizes is scheduled to be deprecated, continue its support but prefer adUnit.mediaTypes.banner - var sizeList = adUnit.sizes; - if (adUnit.mediaTypes && adUnit.mediaTypes.banner) { - sizeList = adUnit.mediaTypes.banner.sizes; - } - var sizeStringList = utils.parseSizesInput(sizeList); - var format = []; - sizeStringList.forEach(function(size) { - if (size) { - var dimensionList = getValidSizeSet(size.split('x')); - if (dimensionList) { - format.push({ - 'w': dimensionList[0], - 'h': dimensionList[1], - }); - } - } - }); - if (format.length) { - return { - 'format': format - }; - } - - return false; - } - - function frameVideo(bid) { - var size = []; - if (utils.deepAccess(bid, 'mediaTypes.video.playerSize')) { - var dimensionSet = bid.mediaTypes.video.playerSize; - if (utils.isArray(bid.mediaTypes.video.playerSize[0])) { - dimensionSet = bid.mediaTypes.video.playerSize[0]; - } - var validSize = getValidSizeSet(dimensionSet) - if (validSize) { - size = validSize; - } - } - return { - mimes: utils.deepAccess(bid, 'mediaTypes.video.mimes') || SUPPORTED_VIDEO_MIMES, - protocols: utils.deepAccess(bid, 'mediaTypes.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS, - w: size[0], - h: size[1], - startdelay: utils.deepAccess(bid, 'mediaTypes.video.startdelay') || 0, - skip: utils.deepAccess(bid, 'mediaTypes.video.skip') || 0, - playbackmethod: utils.deepAccess(bid, 'mediaTypes.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS, - delivery: utils.deepAccess(bid, 'mediaTypes.video.delivery') || SUPPORTED_VIDEO_DELIVERY, - api: utils.deepAccess(bid, 'mediaTypes.video.api') || SUPPORTED_VIDEO_API, - } - } - - function frameExt(bid) { - return { - bidder: { - zoneId: bid.params['zoneId'] - } - } - } - - function frameBid(BRs, bidderRequest) { - let bid = { - id: BRs[0].bidderRequestId, - imp: frameImp(BRs, bidderRequest), - site: frameSite(bidderRequest), - device: frameDevice(), - user: { - ext: { - consent: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? bidderRequest.gdprConsent.consentString : '' - } - }, - at: 1, - tmax: 1000, - regs: { - ext: { - gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? Boolean(bidderRequest.gdprConsent.gdprApplies & 1) : false - } - } - }; - if (BRs[0].schain) { - bid.source = { - 'ext': { - 'schain': BRs[0].schain - } - } - } - return bid; - } - - function getFirstParam(key, validBidRequests) { - for (let i = 0; i < validBidRequests.length; i++) { - if (validBidRequests[i].params && validBidRequests[i].params[key]) { - return validBidRequests[i].params[key]; - } - } - } - - this.buildRequests = function (BRs, bidderRequest) { - let fallbackZoneId = getFirstParam('zoneId', BRs); - if (fallbackZoneId === undefined || BRs.length < 1) { - return []; - } - - var uri = 'https://bid306.rtbsrv.com/bidder/?bid=3mhdom&zoneId=' + fallbackZoneId; - - var fat = /(^v|(\.0)+$)/gi; - var prebidVersion = '$prebid.version$'; - uri += '&hbv=' + prebidVersion.replace(fat, '') + ',' + version.replace(fat, ''); - - var bidRequest = frameBid(BRs, bidderRequest); - if (!bidRequest.imp.length) { - return {}; - } - - return { - method: 'POST', - url: uri, - data: JSON.stringify(bidRequest) - }; - }; - - this.interpretResponse = function (serverResponse) { - let responses = serverResponse.body || []; - let bids = []; - let i = 0; - - if (responses.seatbid) { - let temp = []; - for (i = 0; i < responses.seatbid.length; i++) { - for (let j = 0; j < responses.seatbid[i].bid.length; j++) { - temp.push(responses.seatbid[i].bid[j]); - } - } - responses = temp; - } - - for (i = 0; i < responses.length; i++) { - let bid = responses[i]; - let bidRequest = slotsToBids[bid.impid]; - let bidResponse = { - requestId: bidRequest.id, - bidderCode: that.code, - cpm: parseFloat(bid.price), - width: bid.w, - height: bid.h, - creativeId: bid.crid, - currency: 'USD', - netRevenue: true, - ttl: 350 - }; - - if (bidRequest.mediaTypes && bidRequest.mediaTypes.video) { - bidResponse.vastUrl = bid.adm; - bidResponse.mediaType = 'video'; - bidResponse.ttl = 600; - } else { - bidResponse.ad = bid.adm; - } - bids.push(bidResponse); - } - - return bids; - }; -} - -export const spec = new ResultsmediaAdapter(); -registerBidder(spec); diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js deleted file mode 100644 index b429f94eae0..00000000000 --- a/modules/revcontentBidAdapter.js +++ /dev/null @@ -1,275 +0,0 @@ -// jshint esversion: 6, es3: false, node: true -'use strict'; - -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; - -const BIDDER_CODE = 'revcontent'; -const NATIVE_PARAMS = { - title: { - id: 0, - name: 'title' - }, - image: { - id: 3, - type: 3, - name: 'img' - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - } -}; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: ['native'], - isBidRequestValid: function (bid) { - return (typeof bid.params.apiKey !== 'undefined' && typeof bid.params.userId !== 'undefined' && bid.hasOwnProperty('nativeParams')); - }, - buildRequests: (validBidRequests, bidderRequest) => { - const userId = validBidRequests[0].params.userId; - const widgetId = validBidRequests[0].params.widgetId; - const apiKey = validBidRequests[0].params.apiKey; - var domain = validBidRequests[0].params.domain; - var host = validBidRequests[0].params.endpoint; - - if (typeof host === 'undefined') { - host = 'trends.revcontent.com'; - } - - let serverRequests = []; - var refererInfo; - if (bidderRequest && bidderRequest.refererInfo) { - refererInfo = bidderRequest.refererInfo.referer; - } - - if (typeof domain === 'undefined') { - domain = extractHostname(refererInfo); - } - - var endpoint = 'https://' + host + '/rtb?apiKey=' + apiKey + '&userId=' + userId; - - if (!isNaN(widgetId) && widgetId > 0) { - endpoint = endpoint + '&widgetId=' + widgetId; - } - - let bidfloor = 0.1; - if (!isNaN(validBidRequests[0].params.bidfloor) && validBidRequests[0].params.bidfloor > 0) { - bidfloor = validBidRequests[0].params.bidfloor; - } - - const imp = validBidRequests.map((bid, id) => { - if (bid.hasOwnProperty('nativeParams')) { - const assets = utils._map(bid.nativeParams, (bidParams, key) => { - const props = NATIVE_PARAMS[key]; - const asset = { - required: bidParams.required & 1 - }; - if (props) { - asset.id = props.id; - let wmin, hmin, w, h; - let aRatios = bidParams.aspect_ratios; - - if (aRatios && aRatios[0]) { - aRatios = aRatios[0]; - wmin = aRatios.min_width || 0; - hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; - } - - asset[props.name] = { - len: bidParams.len, - type: props.type, - wmin, - hmin, - w, - h - }; - - return asset; - } - }).filter(Boolean); - - return { - id: id + 1, - tagid: bid.params.mid, - bidderRequestId: bid.bidderRequestId, - auctionId: bid.auctionId, - transactionId: bid.transactionId, - native: { - request: { - ver: '1.1', - context: 2, - contextsubtype: 21, - plcmttype: 1, - plcmtcnt: 1, - assets: assets - }, - ver: '1.1', - battr: [1, 3, 8, 11, 17] - }, - instl: 0, - bidfloor: bidfloor, - secure: '1' - }; - } - }); - - let data = { - id: bidderRequest.auctionId, - imp: imp, - site: { - id: widgetId, - domain: domain, - page: refererInfo, - cat: ['IAB17'], - publisher: { - id: userId, - domain: domain - } - }, - device: { - ua: navigator.userAgent, - language: 'en' - }, - user: { - id: 1 - }, - at: 2, - bcat: [ - 'IAB24', - 'IAB25', - 'IAB25-1', - 'IAB25-2', - 'IAB25-3', - 'IAB25-4', - 'IAB25-5', - 'IAB25-6', - 'IAB25-7', - 'IAB26', - 'IAB26-1', - 'IAB26-2', - 'IAB26-3', - 'IAB26-4' - ] - }; - serverRequests.push({ - method: 'POST', - options: { - contentType: 'application/json' - }, - url: endpoint, - data: JSON.stringify(data), - bid: validBidRequests - }); - - return serverRequests; - }, - interpretResponse: function (serverResponse, originalBidRequest) { - if (!serverResponse.body) { - return; - } - const seatbid = serverResponse.body.seatbid[0]; - const bidResponses = []; - - for (var x in seatbid.bid) { - let adm = JSON.parse(seatbid.bid[x]['adm']); - let ad = { - clickUrl: adm.link.url - }; - - adm.assets.forEach(asset => { - switch (asset.id) { - case 3: - ad['image'] = { - url: asset.img.url, - height: 1, - width: 1 - }; - break; - case 0: - ad['title'] = asset.title.text; - break; - case 5: - ad['sponsoredBy'] = asset.data.value; - break; - } - }); - - var size = originalBidRequest.bid[0].params.size; - - const bidResponse = { - bidder: BIDDER_CODE, - requestId: originalBidRequest.bid[0].bidId, - cpm: seatbid.bid[x]['price'], - creativeId: seatbid.bid[x]['adid'], - currency: 'USD', - netRevenue: true, - ttl: 360, - nurl: seatbid.bid[x]['nurl'], - bidderCode: 'revcontent', - mediaType: 'native', - native: ad, - width: size.width, - height: size.height, - ad: displayNative(ad, getTemplate(size, originalBidRequest.bid[0].params.template)) - }; - - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - onBidWon: function (bid) { - utils.triggerPixel(bid.nurl); - return true; - } -}; - -registerBidder(spec); - -function displayNative(ad, template) { - template = template.replace(/{image}/g, ad['image']['url']); - template = template.replace(/{title}/g, ad['title']); - template = template.replace(/{clickUrl}/g, ad['clickUrl']); - template = template.replace(/{sponsoredBy}/g, ad['sponsoredBy']); - return template; -} - -function getTemplate(size, customTemplate) { - if (typeof (customTemplate) !== 'undefined' && customTemplate !== '') { - return customTemplate; - } - - if (size.width == 300 && size.height == 250) { - return '

{title}

SEE MORE
'; - } - - if (size.width == 728 && size.height == 90) { - return '

{title}

>
'; - } - - if (size.width == 300 && size.height == 600) { - return '

{title}

>
'; - } - - return ''; -} - -function extractHostname(url) { - if (typeof url == 'undefined' || url == null) { - return ''; - } - var hostname; - if (url.indexOf('//') > -1) { - hostname = url.split('/')[2]; - } else { - hostname = url.split('/')[0]; - } - - hostname = hostname.split(':')[0]; - hostname = hostname.split('?')[0]; - - return hostname; -} diff --git a/modules/rhythmoneBidAdapter.js b/modules/rhythmoneBidAdapter.js index fa090044f05..36b2c369213 100644 --- a/modules/rhythmoneBidAdapter.js +++ b/modules/rhythmoneBidAdapter.js @@ -38,7 +38,7 @@ function RhythmOneBidAdapter() { slotsToBids[BRs[i].adUnitCode] = BRs[i]; var impObj = {}; impObj.id = BRs[i].adUnitCode; - impObj.bidfloor = parseFloat(utils.deepAccess(BRs[i], 'params.floor')) || 0; + impObj.bidfloor = 0; impObj.secure = isSecure; if (utils.deepAccess(BRs[i], 'mediaTypes.banner') || utils.deepAccess(BRs[i], 'mediaType') === 'banner') { diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js deleted file mode 100644 index e3265ad5d3e..00000000000 --- a/modules/riseBidAdapter.js +++ /dev/null @@ -1,251 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; - -const SUPPORTED_AD_TYPES = [VIDEO]; -const BIDDER_CODE = 'rise'; -const BIDDER_VERSION = '4.0.0'; -const TTL = 360; -const SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; -const MODES = { - PRODUCTION: 'hb', - TEST: 'hb-test' -} -const SUPPORTED_SYNC_METHODS = { - IFRAME: 'iframe', - PIXEL: 'pixel' -} - -export const spec = { - code: BIDDER_CODE, - version: BIDDER_VERSION, - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid: function(bidRequest) { - return !!(bidRequest.params.org); - }, - buildRequests: function (bidRequests, bidderRequest) { - if (bidRequests.length === 0) { - return []; - } - - const requests = []; - - bidRequests.forEach(bid => { - requests.push(buildVideoRequest(bid, bidderRequest)); - }); - - return requests; - }, - interpretResponse: function({body}) { - const bidResponses = []; - - const bidResponse = { - requestId: body.requestId, - cpm: body.cpm, - width: body.width, - height: body.height, - creativeId: body.requestId, - currency: body.currency, - netRevenue: body.netRevenue, - ttl: body.ttl || TTL, - vastXml: body.vastXml, - mediaType: VIDEO - }; - - bidResponses.push(bidResponse); - - return bidResponses; - }, - getUserSyncs: function(syncOptions, serverResponses) { - const syncs = []; - for (const response of serverResponses) { - if (syncOptions.iframeEnabled && response.body.userSyncURL) { - syncs.push({ - type: 'iframe', - url: response.body.userSyncURL - }); - } - if (syncOptions.pixelEnabled && utils.isArray(response.body.userSyncPixels)) { - const pixels = response.body.userSyncPixels.map(pixel => { - return { - type: 'image', - url: pixel - } - }) - syncs.push(...pixels) - } - } - return syncs; - } -}; - -registerBidder(spec); - -/** - * Build the video request - * @param bid {bid} - * @param bidderRequest {bidderRequest} - * @returns {Object} - */ -function buildVideoRequest(bid, bidderRequest) { - const sellerParams = generateParameters(bid, bidderRequest); - const {params} = bid; - return { - method: 'GET', - url: getEndpoint(params.testMode), - data: sellerParams - }; -} - -/** - * Get the the ad size from the bid - * @param bid {bid} - * @returns {Array} - */ -function getSizes(bid) { - if (utils.deepAccess(bid, 'mediaTypes.video.sizes')) { - return bid.mediaTypes.video.sizes[0]; - } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - return bid.sizes[0]; - } - return []; -} - -/** - * Get schain string value - * @param schainObject {Object} - * @returns {string} - */ -function getSupplyChain(schainObject) { - if (utils.isEmpty(schainObject)) { - return ''; - } - let scStr = `${schainObject.ver},${schainObject.complete}`; - schainObject.nodes.forEach((node) => { - scStr += '!'; - scStr += `${getEncodedValIfNotEmpty(node.asi)},`; - scStr += `${getEncodedValIfNotEmpty(node.sid)},`; - scStr += `${getEncodedValIfNotEmpty(node.hp)},`; - scStr += `${getEncodedValIfNotEmpty(node.rid)},`; - scStr += `${getEncodedValIfNotEmpty(node.name)},`; - scStr += `${getEncodedValIfNotEmpty(node.domain)}`; - }); - return scStr; -} - -/** - * Get encoded node value - * @param val {string} - * @returns {string} - */ -function getEncodedValIfNotEmpty(val) { - return !utils.isEmpty(val) ? encodeURIComponent(val) : ''; -} - -/** - * Get preferred user-sync method based on publisher configuration - * @param bidderCode {string} - * @returns {string} - */ -function getAllowedSyncMethod(filterSettings, bidderCode) { - const iframeConfigsToCheck = ['all', 'iframe']; - const pixelConfigToCheck = 'image'; - if (filterSettings && iframeConfigsToCheck.some(config => isSyncMethodAllowed(filterSettings[config], bidderCode))) { - return SUPPORTED_SYNC_METHODS.IFRAME; - } - if (!filterSettings || !filterSettings[pixelConfigToCheck] || isSyncMethodAllowed(filterSettings[pixelConfigToCheck], bidderCode)) { - return SUPPORTED_SYNC_METHODS.PIXEL; - } -} - -/** - * Check if sync rule is supported - * @param syncRule {Object} - * @param bidderCode {string} - * @returns {boolean} - */ -function isSyncMethodAllowed(syncRule, bidderCode) { - if (!syncRule) { - return false; - } - const isInclude = syncRule.filter === 'include'; - const bidders = utils.isArray(syncRule.bidders) ? syncRule.bidders : [bidderCode]; - return isInclude && utils.contains(bidders, bidderCode); -} - -/** - * Get the seller endpoint - * @param testMode {boolean} - * @returns {string} - */ -function getEndpoint(testMode) { - return testMode - ? SELLER_ENDPOINT + MODES.TEST - : SELLER_ENDPOINT + MODES.PRODUCTION; -} - -/** - * Generate query parameters for the request - * @param bid {bid} - * @param bidderRequest {bidderRequest} - * @returns {Object} - */ -function generateParameters(bid, bidderRequest) { - const timeout = config.getConfig('bidderTimeout'); - const { syncEnabled, filterSettings } = config.getConfig('userSync') || {}; - const [ width, height ] = getSizes(bid); - const { params } = bid; - const { bidderCode } = bidderRequest; - const domain = window.location.hostname; - - const requestParams = { - auction_start: utils.timestamp(), - ad_unit_code: utils.getBidIdParameter('adUnitCode', bid), - tmax: timeout, - width: width, - height: height, - publisher_id: params.org, - floor_price: params.floorPrice, - ua: navigator.userAgent, - bid_id: utils.getBidIdParameter('bidId', bid), - bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), - transaction_id: utils.getBidIdParameter('transactionId', bid), - session_id: params.sessionId || utils.getBidIdParameter('auctionId', bid), - is_wrapper: !!params.isWrapper, - publisher_name: domain, - site_domain: domain, - bidder_version: BIDDER_VERSION - }; - - if (syncEnabled) { - const allowedSyncMethod = getAllowedSyncMethod(filterSettings, bidderCode); - if (allowedSyncMethod) { - requestParams.cs_method = allowedSyncMethod; - } - } - - if (bidderRequest.uspConsent) { - requestParams.us_privacy = bidderRequest.uspConsent; - } - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - requestParams.gdpr = bidderRequest.gdprConsent.gdprApplies; - requestParams.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - - if (params.ifa) { - requestParams.ifa = params.ifa; - } - - if (bid.schain) { - requestParams.schain = getSupplyChain(bid.schain); - } - - if (bidderRequest && bidderRequest.refererInfo) { - requestParams.referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - requestParams.page_url = config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); - } - - return requestParams; -} diff --git a/modules/rtbdemandBidAdapter.js b/modules/rtbdemandBidAdapter.js deleted file mode 100644 index be5fb39f53a..00000000000 --- a/modules/rtbdemandBidAdapter.js +++ /dev/null @@ -1,123 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'rtbdemand'; -const BIDDER_SERVER = 'bidding.rtbdemand.com'; -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.zoneid); - }, - buildRequests: function(validBidRequests, bidderRequest) { - return validBidRequests.map(bidRequest => { - var server = bidRequest.params.server || BIDDER_SERVER; - var parse = getSize(bidderRequest.bids[0].sizes); - const payload = { - from: 'hb', - v: '1.0', - request_id: bidRequest.bidderRequestId, - imp_id: bidRequest.bidId, - aff: bidRequest.params.zoneid, - bid_floor: parseFloat(bidRequest.params.floor) > 0 ? bidRequest.params.floor : 0, - charset: document.charSet || document.characterSet, - site_domain: document.location.hostname, - site_page: window.location.href, - subid: 'hb', - flashver: getFlashVersion(), - tmax: bidderRequest.timeout, - hb: '1', - name: document.location.hostname, - width: parse.width, - height: parse.height, - device_width: screen.width, - device_height: screen.height, - dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, - secure: isSecure(), - make: navigator.vendor ? navigator.vendor : '', - }; - if (document.referrer) { - payload.referrer = document.referrer; - } - - return { - method: 'GET', - url: 'https://' + server + '/hb', - data: payload - }; - }); - }, - interpretResponse: function(serverResponse) { - serverResponse = serverResponse.body; - const bidResponses = []; - if (serverResponse && serverResponse.seatbid) { - serverResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { - const bidResponse = { - requestId: bid.impid, - creativeId: bid.impid, - cpm: bid.price, - width: bid.w, - height: bid.h, - ad: bid.adm, - netRevenue: true, - currency: 'USD', - ttl: 360, - }; - - bidResponses.push(bidResponse); - })); - } - return bidResponses; - }, - getUserSyncs: function getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://' + BIDDER_SERVER + '/delivery/matches.php?type=iframe', - }]; - } - } -} - -function getFlashVersion() { - var plugins, plugin, result; - - if (navigator.plugins && navigator.plugins.length > 0) { - plugins = navigator.plugins; - for (var i = 0; i < plugins.length && !result; i++) { - plugin = plugins[i]; - if (plugin.name.indexOf('Shockwave Flash') > -1) { - result = plugin.description.split('Shockwave Flash ')[1]; - } - } - } - return result || ''; -} - -/* Get parsed size from request size */ -function getSize(requestSizes) { - const parsed = {}; - const size = utils.parseSizesInput(requestSizes)[0]; - - if (typeof size !== 'string') { - return parsed; - } - - const parsedSize = size.toUpperCase().split('X'); - const width = parseInt(parsedSize[0], 10); - if (width) { - parsed.width = width; - } - - const height = parseInt(parsedSize[1], 10); - if (height) { - parsed.height = height; - } - - return parsed; -} - -function isSecure() { - return document.location.protocol === 'https:'; -} - -registerBidder(spec); diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js deleted file mode 100644 index 8473ef4dbb3..00000000000 --- a/modules/rtbsapeBidAdapter.js +++ /dev/null @@ -1,142 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {OUTSTREAM} from '../src/video.js'; -import {Renderer} from '../src/Renderer.js'; -import {triggerPixel} from '../src/utils.js'; - -const BIDDER_CODE = 'rtbsape'; -const ENDPOINT = 'https://ssp-rtb.sape.ru/prebid'; -const RENDERER_SRC = 'https://cdn-rtb.sape.ru/js/player.js'; -const MATCH_SRC = 'https://www.acint.net/mc/?dp=141'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['sape'], - 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 && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.video) && bid.params && bid.params.placeId); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests an array of bids - * @param bidderRequest - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - let tz = (new Date()).getTimezoneOffset() - let padInt = (v) => (v < 10 ? '0' + v : '' + v); - - return { - url: ENDPOINT, - method: 'POST', - data: { - auctionId: bidderRequest.auctionId, - requestId: bidderRequest.bidderRequestId, - bids: validBidRequests, - timezone: (tz > 0 ? '-' : '+') + padInt(Math.floor(Math.abs(tz) / 60)) + ':' + padInt(Math.abs(tz) % 60), - refererInfo: bidderRequest.refererInfo - }, - } - }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {{data: {bids: [{mediaTypes: {banner: boolean}}]}}} bidRequest Info describing the request to the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function (serverResponse, bidRequest) { - if (!(serverResponse.body && Array.isArray(serverResponse.body.bids))) { - return []; - } - - let bids = {}; - bidRequest.data.bids.forEach(bid => bids[bid.bidId] = bid); - - return serverResponse.body.bids.map(bid => { - let requestBid = bids[bid.requestId]; - let context = utils.deepAccess(requestBid, 'mediaTypes.video.context'); - - if (context === OUTSTREAM && (bid.vastUrl || bid.vastXml)) { - let renderer = Renderer.install({ - id: bid.requestId, - url: RENDERER_SRC, - loaded: false - }); - - let muted = utils.deepAccess(requestBid, 'params.video.playerMuted'); - if (typeof muted === 'undefined') { - muted = true; - } - - bid.playerMuted = muted; - bid.renderer = renderer - - renderer.setRender(setOutstreamRenderer); - } - - return bid; - }); - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function (syncOptions) { - const sync = []; - if (syncOptions.iframeEnabled) { - sync.push({ - type: 'iframe', - url: MATCH_SRC - }); - } - return sync; - }, - - /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} bid The bid that won the auction - */ - onBidWon: function(bid) { - if (bid.nurl) { - triggerPixel(bid.nurl); - } - } -} - -/** - * Initialize RtbSape outstream player - * - * @param bid - */ -function setOutstreamRenderer(bid) { - let props = {}; - if (bid.vastUrl) { - props.url = bid.vastUrl; - } - if (bid.vastXml) { - props.xml = bid.vastXml; - } - bid.renderer.push(() => { - let player = window.sapeRtbPlayerHandler(bid.adUnitCode, bid.width, bid.height, bid.playerMuted, {singleton: true}); - props.onComplete = () => player.destroy(); - props.onError = () => player.destroy(); - player.addSlot(props); - }); -} - -registerBidder(spec); diff --git a/modules/rtbsolutionsBidAdapter.js b/modules/rtbsolutionsBidAdapter.js deleted file mode 100644 index 244ab8a4eba..00000000000 --- a/modules/rtbsolutionsBidAdapter.js +++ /dev/null @@ -1,100 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; - -const BIDDER_CODE = 'rtbsolutions'; -const ENDPOINT_URL = 'https://dsp-eu-lb.rtbsolutions.pro/bid/hb'; - -export const spec = { - version: '1.0', - code: BIDDER_CODE, - aliases: ['rtbss'], // short code - nurls: {}, - isBidRequestValid: function(bid) { - return !!bid.params.blockId; - }, - buildRequests: function(validBidRequests, bidderRequest) { - let req = []; - - bidderRequest.bids.forEach(item => { - const width = item.sizes[0][0]; - const height = item.sizes[0][1]; - - let imp = { - referer: bidderRequest.refererInfo.referer, - ua: navigator.userAgent, - lang: this.getLanguage(), - domain: this.getDomain(), - width: width, - height: height, - type: 'banner', - }; - - if (item.params.s1 !== undefined) imp.s1 = item.params.s1; - if (item.params.s2 !== undefined) imp.s2 = item.params.s2; - if (item.params.s3 !== undefined) imp.s3 = item.params.s3; - if (item.params.s4 !== undefined) imp.s4 = item.params.s4; - - req.push({ - bid_id: item.bidId, - block_id: item.params.blockId, - ver: this.version, - imp - }); - }); - - return { - method: 'POST', - url: ENDPOINT_URL, - data: req, - options: { - contentType: 'application/json' - } - } - }, - interpretResponse: function(serverResponse, request) { - const bidResponses = []; - - serverResponse.body.forEach(item => { - this.nurls[item.bid_id] = item.nurl; - - const bidResponse = { - requestId: item.bid_id, - cpm: item.cpm, - width: item.width, - height: item.height, - creativeId: item.creative_id, - currency: item.currency, - netRevenue: true, - ttl: 360, - ad: item.ad, - }; - - bidResponses.push(bidResponse); - }); - - return bidResponses; - }, - onBidWon: function(bid) { - ajax(this.nurls[bid.requestId], null); - }, - - getLanguage() { - const language = navigator.language ? 'language' : 'userLanguage'; - const lang2 = navigator[language].split('-')[0]; - if (lang2.length === 2 || lang2.length === 3) { - return lang2; - } - return ''; - }, - getDomain() { - if (!utils.inIframe()) { - return window.location.hostname - } - let origins = window.document.location.ancestorOrigins; - if (origins && origins.length > 0) { - return origins[origins.length - 1] - } - } -}; -registerBidder(spec); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 078b5404baf..222d40043f6 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -482,7 +482,9 @@ export const spec = { // add p_pos only if specified and valid // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value - data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; + let posMapping = {1: 'atf', 3: 'btf'}; + let pos = posMapping[utils.deepAccess(bidRequest, 'mediaTypes.banner.pos')] || ''; + data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : pos; // pass publisher provided userId if configured const configUserId = config.getConfig('user.id'); @@ -505,8 +507,6 @@ export const spec = { } } else if (eid.source === 'liveramp.com') { data['x_liverampidl'] = eid.uids[0].id; - } else if (eid.source === 'sharedid.org') { - data['eid_sharedid.org'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.uids[0].ext && eid.uids[0].ext.third) || ''}`; } else if (eid.source === 'id5-sync.com') { data['eid_id5-sync.com'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.uids[0].ext && eid.uids[0].ext.linkType) || ''}`; } else { diff --git a/modules/saambaaBidAdapter.js b/modules/saambaaBidAdapter.js deleted file mode 100755 index 0e53d2a300d..00000000000 --- a/modules/saambaaBidAdapter.js +++ /dev/null @@ -1,401 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import find from 'core-js-pure/features/array/find.js'; -import includes from 'core-js-pure/features/array/includes.js'; - -const ADAPTER_VERSION = '1.0'; -const BIDDER_CODE = 'saambaa'; - -export const VIDEO_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; -export const BANNER_ENDPOINT = 'https://nep.advangelists.com/xp/get?pubid='; -export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; -export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; -export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; - -let pubid = ''; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid(bidRequest) { - if (typeof bidRequest != 'undefined') { - if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } - if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } - return true; - } else { return false; } - }, - - buildRequests(bids, bidderRequest) { - let requests = []; - let videoBids = bids.filter(bid => isVideoBidValid(bid)); - let bannerBids = bids.filter(bid => isBannerBidValid(bid)); - videoBids.forEach(bid => { - pubid = getVideoBidParam(bid, 'pubid'); - requests.push({ - method: 'POST', - url: VIDEO_ENDPOINT + pubid, - data: createVideoRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - - bannerBids.forEach(bid => { - pubid = getBannerBidParam(bid, 'pubid'); - - requests.push({ - method: 'POST', - url: BANNER_ENDPOINT + pubid, - data: createBannerRequestData(bid, bidderRequest), - bidRequest: bid - }); - }); - return requests; - }, - - interpretResponse(serverResponse, {bidRequest}) { - let response = serverResponse.body; - if (response !== null && utils.isEmpty(response) == false) { - if (isVideoBid(bidRequest)) { - let bidResponse = { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, - mediaType: VIDEO, - netRevenue: true - } - - if (response.seatbid[0].bid[0].adm) { - bidResponse.vastXml = response.seatbid[0].bid[0].adm; - bidResponse.adResponse = { - content: response.seatbid[0].bid[0].adm - }; - } else { - bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; - } - - return bidResponse; - } else { - return { - requestId: response.id, - bidderCode: BIDDER_CODE, - cpm: response.seatbid[0].bid[0].price, - width: response.seatbid[0].bid[0].w, - height: response.seatbid[0].bid[0].h, - ad: response.seatbid[0].bid[0].adm, - ttl: response.seatbid[0].bid[0].ttl || 60, - creativeId: response.seatbid[0].bid[0].crid, - currency: response.cur, - meta: { 'advertiserDomains': response.seatbid[0].bid[0].adomain }, - mediaType: BANNER, - netRevenue: true - } - } - } - } -}; - -function isBannerBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); -} - -function isVideoBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.video'); -} - -function isVideoBidValid(bid) { - return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); -} - -function isBannerBidValid(bid) { - return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); -} - -function getVideoBidParam(bid, key) { - return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function getBannerBidParam(bid, key) { - return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); -} - -function isMobile() { - return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); -} - -function isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); -} - -function getDoNotTrack() { - return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; -} - -function findAndFillParam(o, key, value) { - try { - if (typeof value === 'function') { - o[key] = value(); - } else { - o[key] = value; - } - } catch (ex) {} -} - -function getOsVersion() { - let clientStrings = [ - { s: 'Android', r: /Android/ }, - { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, - { s: 'Mac OS X', r: /Mac OS X/ }, - { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, - { s: 'Linux', r: /(Linux|X11)/ }, - { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, - { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, - { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, - { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, - { s: 'Windows Vista', r: /Windows NT 6.0/ }, - { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, - { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, - { s: 'UNIX', r: /UNIX/ }, - { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } - ]; - let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); - return cs ? cs.s : 'unknown'; -} - -function getFirstSize(sizes) { - return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; -} - -function parseSizes(sizes) { - return utils.parseSizesInput(sizes).map(size => { - let [ width, height ] = size.split('x'); - return { - w: parseInt(width, 10) || undefined, - h: parseInt(height, 10) || undefined - }; - }); -} - -function getVideoSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); -} - -function getBannerSizes(bid) { - return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); -} - -function getTopWindowReferrer() { - try { - return window.top.document.referrer; - } catch (e) { - return ''; - } -} - -function getVideoTargetingParams(bid) { - return Object.keys(Object(bid.params.video)) - .filter(param => includes(VIDEO_TARGETING, param)) - .reduce((obj, param) => { - obj[ param ] = bid.params.video[ param ]; - return obj; - }, {}); -} - -function createVideoRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - // if size is explicitly given via adapter params - let paramSize = getVideoBidParam(bid, 'size'); - let sizes = []; - - if (typeof paramSize !== 'undefined' && paramSize != '') { - sizes = parseSizes(paramSize); - } else { - sizes = getVideoSizes(bid); - } - const firstSize = getFirstSize(sizes); - - let video = getVideoTargetingParams(bid); - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1, - 'os': getOsVersion() - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getVideoBidParam(bid, 'placement'); - let floor = getVideoBidParam(bid, 'floor'); - if (floor == null) { floor = 0.5; } - - for (let j = 0; j < sizes.length; j++) { - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': floor, - 'bidfloorcur': 'USD', - 'secure': secure, - 'video': Object.assign({ - 'id': utils.generateUUID(), - 'pos': 0, - 'w': firstSize.w, - 'h': firstSize.h, - 'mimes': DEFAULT_MIMES - }, video) - - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} - -function getTopWindowLocation(bidderRequest) { - let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; - return utils.parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); -} - -function createBannerRequestData(bid, bidderRequest) { - let topLocation = getTopWindowLocation(bidderRequest); - let topReferrer = getTopWindowReferrer(); - - // if size is explicitly given via adapter params - - let paramSize = getBannerBidParam(bid, 'size'); - let sizes = []; - if (typeof paramSize !== 'undefined' && paramSize != '') { - sizes = parseSizes(paramSize); - } else { - sizes = getBannerSizes(bid); - } - - const o = { - 'device': { - 'langauge': (global.navigator.language).split('-')[0], - 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), - 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, - 'js': 1 - }, - 'at': 2, - 'site': {}, - 'tmax': 3000, - 'cur': ['USD'], - 'id': bid.bidId, - 'imp': [], - 'regs': { - 'ext': { - } - }, - 'user': { - 'ext': { - } - } - }; - - o.site['page'] = topLocation.href; - o.site['domain'] = topLocation.hostname; - o.site['search'] = topLocation.search; - o.site['domain'] = topLocation.hostname; - o.site['ref'] = topReferrer; - o.site['mobile'] = isMobile() ? 1 : 0; - const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - - o.device['dnt'] = getDoNotTrack() ? 1 : 0; - - findAndFillParam(o.site, 'name', function() { - return global.top.document.title; - }); - - findAndFillParam(o.device, 'h', function() { - return global.screen.height; - }); - findAndFillParam(o.device, 'w', function() { - return global.screen.width; - }); - - let placement = getBannerBidParam(bid, 'placement'); - for (let j = 0; j < sizes.length; j++) { - let size = sizes[j]; - - let floor = getBannerBidParam(bid, 'floor'); - if (floor == null) { floor = 0.1; } - - o.imp.push({ - 'id': '' + j, - 'displaymanager': '' + BIDDER_CODE, - 'displaymanagerver': '' + ADAPTER_VERSION, - 'tagId': placement, - 'bidfloor': floor, - 'bidfloorcur': 'USD', - 'secure': secure, - 'banner': { - 'id': utils.generateUUID(), - 'pos': 0, - 'w': size['w'], - 'h': size['h'] - } - }); - } - - if (bidderRequest && bidderRequest.gdprConsent) { - let { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; - } - - return o; -} -registerBidder(spec); diff --git a/modules/seedingAllianceBidAdapter.js b/modules/seedingAllianceBidAdapter.js deleted file mode 100755 index d85ae856317..00000000000 --- a/modules/seedingAllianceBidAdapter.js +++ /dev/null @@ -1,225 +0,0 @@ -// jshint esversion: 6, es3: false, node: true -'use strict'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { NATIVE } from '../src/mediaTypes.js'; -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'seedingAlliance'; -const DEFAULT_CUR = 'EUR'; -const ENDPOINT_URL = 'https://b.nativendo.de/cds/rtb/bid?format=openrtb2.5&ssp=nativendo'; - -const NATIVE_ASSET_IDS = {0: 'title', 1: 'body', 2: 'sponsoredBy', 3: 'image', 4: 'cta', 5: 'icon'}; - -const NATIVE_PARAMS = { - title: { - id: 0, - name: 'title' - }, - - body: { - id: 1, - name: 'data', - type: 2 - }, - - sponsoredBy: { - id: 2, - name: 'data', - type: 1 - }, - - image: { - id: 3, - type: 3, - name: 'img' - }, - - cta: { - id: 4, - type: 12, - name: 'data' - }, - - icon: { - id: 5, - type: 1, - name: 'img' - } -}; - -export const spec = { - code: BIDDER_CODE, - - supportedMediaTypes: [NATIVE], - - isBidRequestValid: function(bid) { - return !!bid.params.adUnitId; - }, - - buildRequests: (validBidRequests, bidderRequest) => { - const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = validBidRequests[0].transactionId; - const cur = [config.getConfig('currency.adServerCurrency') || DEFAULT_CUR]; - let url = bidderRequest.refererInfo.referer; - - const imp = validBidRequests.map((bid, id) => { - const assets = utils._map(bid.nativeParams, (bidParams, key) => { - const props = NATIVE_PARAMS[key]; - - const asset = { - required: bidParams.required & 1 - }; - - if (props) { - asset.id = props.id; - - let w, h; - - if (bidParams.sizes) { - w = bidParams.sizes[0]; - h = bidParams.sizes[1]; - } - - asset[props.name] = { - len: bidParams.len, - type: props.type, - w, - h - }; - - return asset; - } - }) - .filter(Boolean); - - if (bid.params.url) { - url = bid.params.url; - } - - return { - id: String(id + 1), - tagid: bid.params.adUnitId, - tid: tid, - pt: pt, - native: { - request: { - assets - } - } - }; - }); - - const request = { - id: bidderRequest.auctionId, - site: { - page: url - }, - device: { - ua: navigator.userAgent - }, - cur, - imp, - user: {}, - regs: { - ext: { - gdpr: 0 - } - } - }; - - if (bidderRequest && bidderRequest.gdprConsent) { - utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - utils.deepSetValue(request, 'regs.ext.gdpr', (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0); - } - - return { - method: 'POST', - url: ENDPOINT_URL, - data: JSON.stringify(request), - options: { - contentType: 'application/json' - }, - bids: validBidRequests - }; - }, - - interpretResponse: function(serverResponse, { bids }) { - if (utils.isEmpty(serverResponse.body)) { - return []; - } - - const { seatbid, cur } = serverResponse.body; - - const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { - result[bid.impid - 1] = bid; - return result; - }, []); - - return bids - .map((bid, id) => { - const bidResponse = bidResponses[id]; - - if (bidResponse) { - return { - requestId: bid.bidId, - cpm: bidResponse.price, - creativeId: bidResponse.crid, - ttl: 1000, - netRevenue: bid.netRevenue === 'net', - currency: cur, - mediaType: NATIVE, - bidderCode: BIDDER_CODE, - native: parseNative(bidResponse) - }; - } - }) - .filter(Boolean); - } -}; - -registerBidder(spec); - -function parseNative(bid) { - const {assets, link, imptrackers} = bid.adm.native; - - link.clicktrackers.forEach(function (clicktracker, index) { - link.clicktrackers[index] = clicktracker.replace(/\$\{AUCTION_PRICE\}/, bid.price); - }); - - imptrackers.forEach(function (imptracker, index) { - imptrackers[index] = imptracker.replace(/\$\{AUCTION_PRICE\}/, bid.price); - }); - - const result = { - url: link.url, - clickUrl: link.url, - clickTrackers: link.clicktrackers || undefined, - impressionTrackers: imptrackers || undefined - }; - - assets.forEach(asset => { - const kind = NATIVE_ASSET_IDS[asset.id]; - const content = kind && asset[NATIVE_PARAMS[kind].name]; - - if (content) { - result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; - } - }); - - return result; -} - -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - if (result) { - return result; - } - } -} - -function flatten(arr) { - return [].concat(...arr); -} diff --git a/modules/segmentoBidAdapter.js b/modules/segmentoBidAdapter.js deleted file mode 100644 index a042bdf4942..00000000000 --- a/modules/segmentoBidAdapter.js +++ /dev/null @@ -1,85 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'segmento'; -const URL = 'https://prebid-bidder.rutarget.ru/bid'; -const SYNC_IFRAME_URL = 'https://tag.rutarget.ru/tag?event=otherPage&check=true&response=syncframe&synconly=true'; -const SYNC_IMAGE_URL = 'https://tag.rutarget.ru/tag?event=otherPage&check=true&synconly=true'; -const RUB = 'RUB'; -const TIME_TO_LIVE = 0; - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: (bid) => { - return Boolean(bid && bid.params && !isNaN(bid.params.placementId)); - }, - buildRequests: (validBidRequests, bidderRequest) => { - const payload = { - places: [], - settings: { - currency: RUB, - referrer: bidderRequest.refererInfo && bidderRequest.refererInfo.referer - } - }; - - for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i]; - - payload.places.push({ - id: bid.bidId, - placementId: bid.params.placementId, - sizes: bid.sizes - }); - } - - return { - method: 'POST', - url: URL, - data: payload - }; - }, - interpretResponse: (serverResponse) => { - const bids = serverResponse.body && serverResponse.body.bids; - if (!bids) { - return []; - } - - const bidResponses = []; - - for (let i = 0; i < bids.length; i++) { - const bid = bids[i]; - - bidResponses.push({ - requestId: bid.id, - cpm: bid.cpm, - width: bid.size.width, - height: bid.size.height, - creativeId: bid.creativeId, - currency: RUB, - netRevenue: true, - ttl: TIME_TO_LIVE, - adUrl: bid.displayUrl - }); - } - - return bidResponses; - }, - getUserSyncs: (syncOptions) => { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: SYNC_IFRAME_URL - }]; - } - - if (syncOptions.pixelEnabled) { - return [{ - type: 'image', - url: SYNC_IMAGE_URL - }]; - } - - return []; - } -}; - -registerBidder(spec); diff --git a/modules/sekindoUMBidAdapter.js b/modules/sekindoUMBidAdapter.js deleted file mode 100644 index bea25173747..00000000000 --- a/modules/sekindoUMBidAdapter.js +++ /dev/null @@ -1,119 +0,0 @@ -import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -export const spec = { - code: 'sekindoUM', - 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) { - if (bid.mediaType == 'video' || (typeof bid.mediaTypes == 'object' && typeof bid.mediaTypes.video == 'object')) { - if (typeof bid.params.video != 'object' || typeof bid.params.video.playerWidth == 'undefined' || typeof bid.params.video.playerHeight == 'undefined') { - return false; - } - } - return !!(bid.params.spaceId); - }, - /** - * 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) { - var pubUrl = null; - try { - if (window.top == window) { - pubUrl = window.location.href; - } else { - try { - pubUrl = window.top.location.href; - } catch (e2) { - pubUrl = document.referrer; - } - } - } catch (e1) {} - - return validBidRequests.map(bidRequest => { - var subId = utils.getBidIdParameter('subId', bidRequest.params); - var spaceId = utils.getBidIdParameter('spaceId', bidRequest.params); - var bidfloor = utils.getBidIdParameter('bidfloor', bidRequest.params); - var protocol = (document.location.protocol === 'https:' ? 's' : ''); - var queryString = ''; - - queryString = utils.tryAppendQueryString(queryString, 's', spaceId); - queryString = utils.tryAppendQueryString(queryString, 'subId', subId); - queryString = utils.tryAppendQueryString(queryString, 'pubUrl', pubUrl); - queryString = utils.tryAppendQueryString(queryString, 'hbTId', bidRequest.transactionId); - queryString = utils.tryAppendQueryString(queryString, 'hbBidId', bidRequest.bidId); - queryString = utils.tryAppendQueryString(queryString, 'hbver', '4'); - queryString = utils.tryAppendQueryString(queryString, 'hbcb', '1');/// legasy - queryString = utils.tryAppendQueryString(queryString, 'dcpmflr', bidfloor); - queryString = utils.tryAppendQueryString(queryString, 'protocol', protocol); - queryString = utils.tryAppendQueryString(queryString, 'x', bidRequest.params.width); - queryString = utils.tryAppendQueryString(queryString, 'y', bidRequest.params.height); - if (bidderRequest && bidderRequest.gdprConsent) { - queryString = utils.tryAppendQueryString(queryString, 'gdprConsent', bidderRequest.gdprConsent.consentString); - queryString = utils.tryAppendQueryString(queryString, 'gdpr', (bidderRequest.gdprConsent.gdprApplies) ? '1' : '0'); - } - if (bidRequest.mediaType === 'video' || (typeof bidRequest.mediaTypes == 'object' && typeof bidRequest.mediaTypes.video == 'object')) { - queryString = utils.tryAppendQueryString(queryString, 'x', bidRequest.params.playerWidth); - queryString = utils.tryAppendQueryString(queryString, 'y', bidRequest.params.playerHeight); - if (typeof vid_vastType != 'undefined') { // eslint-disable-line camelcase - queryString = utils.tryAppendQueryString(queryString, 'vid_vastType', bidRequest.params.vid_vastType); - } - if (typeof bidRequest.mediaTypes == 'object' && typeof bidRequest.mediaTypes.video == 'object' && typeof bidRequest.mediaTypes.video.context == 'string') { - queryString = utils.tryAppendQueryString(queryString, 'vid_context', bidRequest.mediaTypes.video.context); - } - } - - var endpointUrl = 'https' + '://hb.sekindo.com/live/liveView.php'; - - return { - method: 'GET', - url: endpointUrl, - data: queryString, - }; - }); - }, - /** - * 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, bidRequest) { - if (typeof serverResponse !== 'object') { - return []; - } - - let bidResponses = []; - var bidResponse = { - requestId: serverResponse.body.id, - bidderCode: spec.code, - cpm: serverResponse.body.cpm, - width: serverResponse.body.width, - height: serverResponse.body.height, - creativeId: serverResponse.body.creativeId, - currency: serverResponse.body.currency, - netRevenue: serverResponse.body.netRevenue, - ttl: serverResponse.body.ttl - }; - if (bidRequest.mediaType == 'video') { - if (typeof serverResponse.body.vastUrl != 'undefined') { - bidResponse.vastUrl = serverResponse.body.vastUrl; - } else { - bidResponse.vastXml = serverResponse.body.vastXml; - } - } else { - bidResponse.ad = serverResponse.body.ad; - } - - bidResponses.push(bidResponse); - return bidResponses; - } -} -registerBidder(spec); diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index defa8d22639..5bb20492758 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -1,280 +1,138 @@ /** - * This module adds Shared ID support to the User ID module - * The {@link module:modules/userId} module is required. + * This module adds SharedId to the User ID module + * The {@link module:modules/userId} module is required * @module modules/sharedIdSystem * @requires module:modules/userId */ -import * as utils from '../src/utils.js' -import {ajax} from '../src/ajax.js'; +import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; +import {ajax} from '../src/ajax.js'; import { uspDataHandler, coppaDataHandler } from '../src/adapterManager.js'; +import {getStorageManager} from '../src/storageManager.js'; -const MODULE_NAME = 'sharedId'; -const ID_SVC = 'https://id.sharedid.org/id'; -const DEFAULT_24_HOURS = 86400; -const OPT_OUT_VALUE = '00000000000000000000000000'; -// These values should NEVER change. If -// they do, we're no longer making ulids! -const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 -const ENCODING_LEN = ENCODING.length; -const TIME_MAX = Math.pow(2, 48) - 1; -const TIME_LEN = 10; -const RANDOM_LEN = 16; -const id = factory(); const GVLID = 887; -/** - * Constructs cookie value - * @param value - * @param needsSync - * @returns {string} - */ -function constructCookieValue(value, needsSync) { - const cookieValue = {}; - cookieValue.id = value; - cookieValue.ts = utils.timestamp(); - if (needsSync) { - cookieValue.ns = true; - } - utils.logInfo('SharedId: cookie Value: ' + JSON.stringify(cookieValue)); - return cookieValue; -} +const storage = getStorageManager(GVLID, 'pubCommonId'); +const COOKIE = 'cookie'; +const LOCAL_STORAGE = 'html5'; +const SHAREDID_OPT_OUT_VALUE = '00000000000000000000000000'; +const SHAREDID_URL = 'https://id.sharedid.org/id'; +const SHAREDID_SUFFIX = '_sharedid'; +const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const SHAREDID_DEFAULT_STATE = true; + +const PUB_COMMON_ID = 'PublisherCommonId'; /** - * Checks if id needs to be synced - * @param configParams - * @param storedId - * @returns {boolean} + * Store sharedid in either cookie or local storage + * @param {Object} config Need config.storage object to derive key, expiry time, and storage type. + * @param {string} value Shareid value to store */ -function isIdSynced(configParams, storedId) { - const needSync = storedId.ns; - if (needSync) { - return true; - } - if (!configParams || typeof configParams.syncTime !== 'number') { - utils.logInfo('SharedId: Sync time is not configured or is not a number'); - } - let syncTime = (!configParams || typeof configParams.syncTime !== 'number') ? DEFAULT_24_HOURS : configParams.syncTime; - if (syncTime > DEFAULT_24_HOURS) { - syncTime = DEFAULT_24_HOURS; - } - const cookieTimestamp = storedId.ts; - if (cookieTimestamp) { - var secondBetweenTwoDate = timeDifferenceInSeconds(utils.timestamp(), cookieTimestamp); - return secondBetweenTwoDate >= syncTime; + +function storeData(config, value) { + try { + if (value) { + const key = config.storage.name + SHAREDID_SUFFIX; + const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); + + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + storage.setCookie(key, value, expiresStr, 'LAX', sharedIdSystemSubmodule.domainOverride()); + } + } else if (config.storage.type === LOCAL_STORAGE) { + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(`${key}_exp`, expiresStr); + storage.setDataInLocalStorage(key, value); + } + } + } + } catch (error) { + utils.logError(error); } - return false; } /** - * Gets time difference in secounds - * @param date1 - * @param date2 - * @returns {number} + * Read sharedid from cookie or local storage + * @param config Need config.storage to derive key and storage type + * @return {string} */ -function timeDifferenceInSeconds(date1, date2) { - const diff = (date1 - date2) / 1000; - return Math.abs(Math.round(diff)); +function readData(config) { + try { + const key = config.storage.name + SHAREDID_SUFFIX; + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + return storage.getCookie(key); + } + } else if (config.storage.type === LOCAL_STORAGE) { + if (storage.hasLocalStorage()) { + const expValue = storage.getDataFromLocalStorage(`${key}_exp`); + if (!expValue) { + return storage.getDataFromLocalStorage(key); + } else if ((new Date(expValue)).getTime() - Date.now() > 0) { + return storage.getDataFromLocalStorage(key) + } + } + } + } catch (error) { + utils.logError(error); + } } /** - * id generation call back - * @param result - * @param callback - * @returns {{success: success, error: error}} + * Delete sharedid from cookie or local storage + * @param config Need config.storage to derive key and storage type */ -function idGenerationCallback(callback) { - return { - success: function (responseBody) { - let value = {}; - if (responseBody) { - try { - let responseObj = JSON.parse(responseBody); - utils.logInfo('SharedId: Generated SharedId: ' + responseObj.sharedId); - value = constructCookieValue(responseObj.sharedId, false); - } catch (error) { - utils.logError(error); - } +function delData(config) { + try { + const key = config.storage.name + SHAREDID_SUFFIX; + if (config.storage.type === COOKIE) { + if (storage.cookiesAreEnabled()) { + storage.setCookie(key, '', EXPIRED_COOKIE_DATE); } - callback(value); - }, - error: function (statusText, responseBody) { - const value = constructCookieValue(id(), true); - utils.logInfo('SharedId: Ulid Generated SharedId: ' + value.id); - callback(value); + } else if (config.storage.type === LOCAL_STORAGE) { + storage.removeDataFromLocalStorage(`${key}_exp`); + storage.removeDataFromLocalStorage(key); } + } catch (error) { + utils.logError(error); } } /** - * existing id generation call back - * @param result - * @param callback - * @returns {{success: success, error: error}} + * setup success and error handler for sharedid callback thru ajax + * @param {string} pubcid Current pubcommon id + * @param {function} callback userId module callback. + * @param {Object} config Need config.storage to derive sharedid storage params + * @return {{success: success, error: error}} */ -function existingIdCallback(storedId, callback) { + +function handleResponse(pubcid, callback, config) { return { success: function (responseBody) { - utils.logInfo('SharedId: id to be synced: ' + storedId.id); if (responseBody) { try { let responseObj = JSON.parse(responseBody); - storedId = constructCookieValue(responseObj.sharedId, false); - utils.logInfo('SharedId: Older SharedId: ' + storedId.id); + utils.logInfo('PubCommonId: Generated SharedId: ' + responseObj.sharedId); + if (responseObj.sharedId) { + if (responseObj.sharedId !== SHAREDID_OPT_OUT_VALUE) { + // Store sharedId locally + storeData(config, responseObj.sharedId); + } else { + // Delete local copy if the user has opted out + delData(config); + } + } + // Pass pubcid even though there is no change in order to trigger decode + callback(responseObj.sharedId); } catch (error) { utils.logError(error); } } - callback(storedId); }, - error: function () { - utils.logInfo('SharedId: Sync error for id : ' + storedId.id); - callback(storedId); - } - } -} - -/** - * Encode the id - * @param value - * @returns {string|*} - */ -function encodeId(value) { - const result = {}; - const sharedId = (value && typeof value['id'] === 'string') ? value['id'] : undefined; - if (sharedId == OPT_OUT_VALUE) { - return undefined; - } - if (sharedId) { - const bidIds = { - id: sharedId, - } - const ns = (value && typeof value['ns'] === 'boolean') ? value['ns'] : undefined; - if (ns == undefined) { - bidIds.third = sharedId; - } - result.sharedid = bidIds; - utils.logInfo('SharedId: Decoded value ' + JSON.stringify(result)); - return result; - } - return sharedId; -} - -/** - * the factory to generate unique identifier based on time and current pseudorandom number - * @param {string} the current pseudorandom number generator - * @returns {function(*=): *} - */ -function factory(currPrng) { - if (!currPrng) { - currPrng = detectPrng(); - } - return function ulid(seedTime) { - if (isNaN(seedTime)) { - seedTime = Date.now(); + error: function (statusText, responseBody) { + utils.logInfo('PubCommonId: failed to get sharedid'); } - return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng); - }; -} - -/** - * creates and logs the error message - * @function - * @param {string} error message - * @returns {Error} - */ -function createError(message) { - utils.logError(message); - const err = new Error(message); - err.source = 'sharedId'; - return err; -} - -/** - * gets a a random charcter from generated pseudorandom number - * @param {string} the generated pseudorandom number - * @returns {string} - */ -function randomChar(prng) { - let rand = Math.floor(prng() * ENCODING_LEN); - if (rand === ENCODING_LEN) { - rand = ENCODING_LEN - 1; - } - return ENCODING.charAt(rand); -} - -/** - * encodes the time based on the length - * @param now - * @param len - * @returns {string} encoded time. - */ -function encodeTime (now, len) { - if (isNaN(now)) { - throw new Error(now + ' must be a number'); - } - - if (Number.isInteger(now) === false) { - throw createError('time must be an integer'); - } - - if (now > TIME_MAX) { - throw createError('cannot encode time greater than ' + TIME_MAX); - } - if (now < 0) { - throw createError('time must be positive'); - } - - if (Number.isInteger(len) === false) { - throw createError('length must be an integer'); - } - if (len < 0) { - throw createError('length must be positive'); } - - let mod; - let str = ''; - for (; len > 0; len--) { - mod = now % ENCODING_LEN; - str = ENCODING.charAt(mod) + str; - now = (now - mod) / ENCODING_LEN; - } - return str; -} - -/** - * encodes random character - * @param len - * @param prng - * @returns {string} - */ -function encodeRandom (len, prng) { - let str = ''; - for (; len > 0; len--) { - str = randomChar(prng) + str; - } - return str; -} - -/** - * detects the pseudorandom number generator and generates the random number - * @function - * @param {string} error message - * @returns {string} a random number - */ -function detectPrng(root) { - if (!root) { - root = typeof window !== 'undefined' ? window : null; - } - const browserCrypto = root && (root.crypto || root.msCrypto); - if (browserCrypto) { - return () => { - const buffer = new Uint8Array(1); - browserCrypto.getRandomValues(buffer); - return buffer[0] / 0xff; - }; - } - return () => Math.random(); } /** @@ -284,90 +142,195 @@ function detectPrng(root) { */ function sharedIdUrl(consentData) { const usPrivacyString = uspDataHandler.getConsentData(); - let sharedIdUrl = ID_SVC; - if (usPrivacyString) { - sharedIdUrl = `${ID_SVC}?us_privacy=${usPrivacyString}`; + let sharedIdUrl = SHAREDID_URL; + if (usPrivacyString && typeof usPrivacyString === 'string') { + sharedIdUrl = `${SHAREDID_URL}?us_privacy=${usPrivacyString}`; } if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return sharedIdUrl; if (usPrivacyString) { sharedIdUrl = `${sharedIdUrl}&gdpr=1&gdpr_consent=${consentData.consentString}` return sharedIdUrl; } - sharedIdUrl = `${ID_SVC}?gdpr=1&gdpr_consent=${consentData.consentString}`; + sharedIdUrl = `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}`; return sharedIdUrl } -/** @type {Submodule} */ -export const sharedIdSubmodule = { +/** + * Wraps pixelCallback in order to call sharedid sync + * @param {string} pubcid Pubcommon id value + * @param {function|undefined} pixelCallback fires a pixel to first party server + * @param {Object} config Need config.storage to derive sharedid storage params. + * @return {function(...[*]=)} + */ + +function getIdCallback(pubcid, pixelCallback, config, consentData) { + return function (callback) { + if (typeof pixelCallback === 'function') { + pixelCallback(); + } + ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); + } +} +export const sharedIdSystemSubmodule = { /** * used to link submodule with config * @type {string} */ - name: MODULE_NAME, - + name: 'sharedId', + aliasName: 'pubCommonId', /** - * Vendor id of Prebid + * Vendor id of prebid * @type {Number} */ gvlid: GVLID, + makeCallback: function (pixelUrl, id = '') { + if (!pixelUrl) { + return; + } + + // Use pubcid as a cache buster + const urlInfo = utils.parseUrl(pixelUrl); + urlInfo.search.id = encodeURIComponent('pubcid:' + id); + const targetUrl = utils.buildUrl(urlInfo); + + return function () { + utils.triggerPixel(targetUrl); + }; + }, /** * decode the stored id value for passing to bid requests * @function * @param {string} value - * @returns {{sharedid:{ id: string, third:string}} or undefined if value doesn't exists + * @param {SubmoduleConfig} config + * @returns {{pubcid:string}} */ - decode(value) { - return (value) ? encodeId(value) : undefined; - }, + decode(value, config) { + const idObj = {'pubcid': value}; + const {params: {enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + + if (enableSharedId) { + const sharedId = readData(config); + if (sharedId) idObj['sharedid'] = {id: sharedId}; + } + return idObj; + }, /** - * performs action to obtain id and return a value. + * performs action to obtain id * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData|undefined} consentData - * @returns {sharedId} + * @param {SubmoduleConfig} [config] Config object with params and storage properties + * @param {Object} consentData + * @param {string} storedId Existing pubcommon id + * @returns {IdResponse} */ - getId(config, consentData) { + getId: function (config = {}, consentData, storedId) { const coppa = coppaDataHandler.getCoppa(); if (coppa) { - utils.logInfo('SharedId: IDs not provided for coppa requests, exiting SharedId'); + utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const resp = function (callback) { - utils.logInfo('SharedId: Sharedid doesnt exists, new cookie creation'); - ajax(sharedIdUrl(consentData), idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true}); - }; - return {callback: resp}; - }, + const {params: {create = true, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + let newId = storedId; + if (!newId) { + try { + if (typeof window[PUB_COMMON_ID] === 'object') { + // If the page includes its own pubcid module, then save a copy of id. + newId = window[PUB_COMMON_ID].getId(); + } + } catch (e) { + } + + if (!newId) newId = (create && utils.hasDeviceAccess()) ? utils.generateUUID() : undefined; + } + + const pixelCallback = this.makeCallback(pixelUrl, newId); + const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback; + return {id: newId, callback: combinedCallback}; + }, /** - * performs actions even if the id exists and returns a value - * @param config - * @param consentData - * @param storedId - * @returns {{callback: *}} + * performs action to extend an id. There are generally two ways to extend the expiration time + * of stored id: using pixelUrl or return the id and let main user id module write it again with + * the new expiration time. + * + * PixelUrl, if defined, should point back to a first party domain endpoint. On the server + * side, there is either a plugin, or customized logic to read and write back the pubcid cookie. + * The extendId function itself should return only the callback, and not the id itself to avoid + * having the script-side overwriting server-side. This applies to both pubcid and sharedid. + * + * On the other hand, if there is no pixelUrl, then the extendId should return storedId so that + * its expiration time is updated. Sharedid, however, will have to be updated by this submodule + * separately. + * + * @function + * @param {SubmoduleParams} [config] + * @param {ConsentData|undefined} consentData + * @param {Object} storedId existing id + * @returns {IdResponse|undefined} */ - extendId(config, consentData, storedId) { + extendId: function(config = {}, consentData, storedId) { const coppa = coppaDataHandler.getCoppa(); if (coppa) { - utils.logInfo('SharedId: IDs not provided for coppa requests, exiting SharedId'); + utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const configParams = (config && config.params) || {}; - utils.logInfo('SharedId: Existing shared id ' + storedId.id); - const resp = function (callback) { - const needSync = isIdSynced(configParams, storedId); - if (needSync) { - utils.logInfo('SharedId: Existing shared id ' + storedId + ' is not synced'); - const sharedIdPayload = {}; - sharedIdPayload.sharedId = storedId.id; - const payloadString = JSON.stringify(sharedIdPayload); - ajax(sharedIdUrl(consentData), existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true}); + const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + + if (extend) { + try { + if (typeof window[PUB_COMMON_ID] === 'object') { + if (enableSharedId) { + // If the page includes its own pubcid module, then there is nothing to do + // except to update sharedid's expiration time + storeData(config, readData(config)); + } + return; + } + } catch (e) { + } + + if (pixelUrl) { + const callback = this.makeCallback(pixelUrl, storedId); + return {callback: callback}; + } else { + if (enableSharedId) { + // Update with the same value to extend expiration time + storeData(config, readData(config)); + } + return {id: storedId}; + } + } + }, + + /** + * @param {string} domain + * @param {HTMLDocument} document + * @return {(string|undefined)} + */ + domainOverride: function () { + const domainElements = document.domain.split('.'); + const cookieName = `_gd${Date.now()}`; + for (let i = 0, topDomain, testCookie; i < domainElements.length; i++) { + const nextDomain = domainElements.slice(i).join('.'); + + // write test cookie + storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); + + // read test cookie to verify domain was valid + testCookie = storage.getCookie(cookieName); + + // delete test cookie + storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); + + if (testCookie === '1') { + // cookie was written successfully using test domain so the topDomain is updated + topDomain = nextDomain; + } else { + // cookie failed to write using test domain so exit by returning the topDomain + return topDomain; } - }; - return {callback: resp}; + } } }; -// Register submodule for userId -submodule('userId', sharedIdSubmodule); +submodule('userId', sharedIdSystemSubmodule); diff --git a/modules/sharedIdSystem.md b/modules/sharedIdSystem.md deleted file mode 100644 index a4541c16c49..00000000000 --- a/modules/sharedIdSystem.md +++ /dev/null @@ -1,43 +0,0 @@ -## Shared ID User ID Submodule - -Shared ID User ID Module generates a UUID that can be utilized to improve user matching.This module enables timely synchronization which handles sharedId.org optout. This module does not require any registration. - -### Building Prebid with Shared Id Support -Your Prebid build must include the modules for both **userId** and **sharedId** submodule. Follow the build instructions for Prebid as -explained in the top level README.md file of the Prebid source tree. - -ex: $ gulp build --modules=userId,sharedIdSystem - -### Prebid Params - -Individual params may be set for the Shared ID User ID Submodule. -``` -pbjs.setConfig({ - userSync: { - userIds: [{ - name: 'sharedId', - params: { - syncTime: 60 // in seconds, default is 24 hours - }, - storage: { - name: 'sharedid', - type: 'cookie', - expires: 28 - }, - }] - } -}); -``` - -### Parameter Descriptions for the `usersync` Configuration Section -The below parameters apply only to the Shared ID User ID Module integration. - -| Params under usersync.userIds[]| Scope | Type | Description | Example | -| --- | --- | --- | --- | --- | -| name | Required | String | ID value for the Shared ID module - `"sharedId"` | `"sharedId"` | -| params | Optional | Object | Details for sharedId syncing. | | -| params.syncTime | Optional | Object | Configuration to define the frequency(in seconds) of id synchronization. By default id is synchronized every 24 hours | 60 | -| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | | -| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` | -| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"sharedid"` | -| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. | `28` | diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 59cf2a3a035..eea42af4a4c 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -166,9 +166,6 @@ function handleUniversalIds(bidRequest) { const lipb = utils.deepAccess(bidRequest, 'userId.lipb.lipbid'); if (lipb) universalIds.liuid = lipb; - const shd = utils.deepAccess(bidRequest, 'userId.sharedid'); - if (shd) universalIds.shduid = shd; // object with keys: id & third - return universalIds; } diff --git a/modules/shinezBidAdapter.js b/modules/shinezBidAdapter.js deleted file mode 100644 index d5734d23fdc..00000000000 --- a/modules/shinezBidAdapter.js +++ /dev/null @@ -1,83 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'shinez'; -const BIDDER_SHORT_CODE = 'shz'; -const ADAPTER_VERSION = '1.0.0'; - -const TARGET_URL = 'https://shinez-ssp.shinez.workers.dev/prebid'; - -export const spec = { - code: BIDDER_CODE, - version: ADAPTER_VERSION, - aliases: { - code: BIDDER_SHORT_CODE - }, - supportedMediaTypes: [ BANNER ], - isBidRequestValid: isBidRequestValid, - buildRequests: buildRequests, - interpretResponse: interpretResponse, -}; - -export const internal = { - TARGET_URL -} - -function isBidRequestValid(bid) { - return !!(bid && bid.params && - bid.params.placementId && typeof bid.params.placementId === 'string' && - (bid.params.unit == null || (typeof bid.params.unit === 'string' && bid.params.unit.length > 0)) - ); -} - -function buildRequests(validBidRequests, bidderRequest) { - const utcOffset = (new Date()).getTimezoneOffset(); - const data = []; - validBidRequests.forEach(function(bidRequest) { - data.push(_buildServerBidRequest(bidRequest, bidderRequest, utcOffset)); - }); - const request = { - method: 'POST', - url: TARGET_URL, - data: data - }; - return request; -} - -function interpretResponse(serverResponse, request) { - const bids = []; - serverResponse.body.forEach(function(serverBid) { - bids.push(_convertServerBid(serverBid)); - }); - return bids; -} - -function _buildServerBidRequest(bidRequest, bidderRequest, utcOffset) { - return { - bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, - crumbs: bidRequest.crumbs, - mediaTypes: bidRequest.mediaTypes, - refererInfo: bidderRequest.refererInfo, - placementId: bidRequest.params.placementId, - utcOffset: utcOffset, - adUnitCode: bidRequest.adUnitCode, - unit: bidRequest.params.unit - } -} - -function _convertServerBid(response) { - return { - requestId: response.bidId, - cpm: response.cpm, - currency: response.currency, - width: response.width, - height: response.height, - ad: response.ad, - ttl: response.ttl, - creativeId: response.creativeId, - netRevenue: response.netRevenue - }; -} - -registerBidder(spec); diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js deleted file mode 100644 index d717f3a88bd..00000000000 --- a/modules/slimcutBidAdapter.js +++ /dev/null @@ -1,128 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { ajax } from '../src/ajax.js'; - -const BIDDER_CODE = 'slimcut'; -const ENDPOINT_URL = 'https://sb.freeskreen.com/pbr'; - -export const spec = { - code: BIDDER_CODE, - aliases: ['scm'], - 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. - */ - isBidRequestValid: function(bid) { - let isValid = false; - if (typeof bid.params !== 'undefined' && !isNaN(parseInt(utils.getValue(bid.params, 'placementId'))) && parseInt(utils.getValue(bid.params, 'placementId')) > 0) { - isValid = true; - } - 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. - */ - buildRequests: function(validBidRequests, bidderRequest) { - const bids = validBidRequests.map(buildRequestObject); - const payload = { - referrer: getReferrerInfo(bidderRequest), - data: bids, - deviceWidth: screen.width - }; - - let gdpr = bidderRequest.gdprConsent; - if (bidderRequest && gdpr) { - let isCmp = (typeof gdpr.gdprApplies === 'boolean') - let isConsentString = (typeof gdpr.consentString === 'string') - payload.gdpr_iab = { - consent: isConsentString ? gdpr.consentString : '', - status: isCmp ? gdpr.gdprApplies : -1 - }; - } - - const payloadString = JSON.stringify(payload); - return { - method: 'POST', - url: ENDPOINT_URL, - data: payloadString, - }; - }, - - /** - * 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) { - const bidResponses = []; - serverResponse = serverResponse.body; - - if (serverResponse.responses) { - serverResponse.responses.forEach(function (bid) { - const bidResponse = { - cpm: bid.cpm, - width: bid.width, - height: bid.height, - currency: bid.currency, - netRevenue: bid.netRevenue, - ttl: bid.ttl, - ad: bid.ad, - requestId: bid.requestId, - creativeId: bid.creativeId, - transactionId: bid.tranactionId, - winUrl: bid.winUrl - }; - bidResponses.push(bidResponse); - }); - } - return bidResponses; - }, - - getUserSyncs: function(syncOptions, serverResponses) { - if (syncOptions.iframeEnabled) { - return [{ - type: 'iframe', - url: 'https://sb.freeskreen.com/async_usersync.html' - }]; - } - return []; - }, - - onBidWon: function(bid) { - ajax(bid.winUrl + bid.cpm, null); - } -} - -function buildRequestObject(bid) { - const reqObj = {}; - let placementId = utils.getValue(bid.params, 'placementId'); - - reqObj.sizes = utils.parseSizesInput(bid.sizes); - reqObj.bidId = utils.getBidIdParameter('bidId', bid); - reqObj.bidderRequestId = utils.getBidIdParameter('bidderRequestId', bid); - reqObj.placementId = parseInt(placementId); - reqObj.adUnitCode = utils.getBidIdParameter('adUnitCode', bid); - reqObj.auctionId = utils.getBidIdParameter('auctionId', bid); - reqObj.transactionId = utils.getBidIdParameter('transactionId', bid); - - return reqObj; -} - -function getReferrerInfo(bidderRequest) { - let ref = window.location.href; - if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - ref = bidderRequest.refererInfo.referer; - } - return ref; -} - -registerBidder(spec); diff --git a/modules/smarticoBidAdapter.js b/modules/smarticoBidAdapter.js deleted file mode 100644 index 9107ce5f908..00000000000 --- a/modules/smarticoBidAdapter.js +++ /dev/null @@ -1,116 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import find from 'core-js-pure/features/array/find.js'; - -const SMARTICO_CONFIG = { - bidRequestUrl: 'https://trmads.eu/preBidRequest', - widgetUrl: 'https://trmads.eu/get', - method: 'POST' -} - -const BIDDER_CODE = 'smartico'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - isBidRequestValid: function (bid) { - return !!(bid && bid.params && bid.params.token && bid.params.placementId); - }, - buildRequests: function (validBidRequests, bidderRequest) { - var i - var j - var bid - var bidParam - var bidParams = [] - var sizes - var frameWidth = Math.round(window.screen.width) - var frameHeight = Math.round(window.screen.height) - for (i = 0; i < validBidRequests.length; i++) { - bid = validBidRequests[i] - if (bid.sizes) { - sizes = bid.sizes - } else if (typeof (BANNER) != 'undefined' && bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - sizes = bid.mediaTypes[BANNER].sizes - } else if (frameWidth && frameHeight) { - sizes = [[frameWidth, frameHeight]] - } else { - sizes = [] - } - for (j = 0; j < sizes.length; j++) { - bidParam = { - token: bid.params.token || '', - bidId: bid.bidId, - 'banner-format-width': sizes[j][0], - 'banner-format-height': sizes[j][1] - } - if (bid.params.bannerFormat) { - bidParam['banner-format'] = bid.params.bannerFormat - } - if (bid.params.language) { - bidParam.language = bid.params.language - } - if (bid.params.region) { - bidParam.region = bid.params.region - } - if (bid.params.regions && (bid.params.regions instanceof String || (bid.params.regions instanceof Array && bid.params.regions.length))) { - bidParam.regions = bid.params.regions - if (bidParam.regions instanceof Array) { - bidParam.regions = bidParam.regions.join(',') - } - } - bidParams.push(bidParam) - } - } - - var ServerRequestObjects = { - method: SMARTICO_CONFIG.method, - url: SMARTICO_CONFIG.bidRequestUrl, - bids: validBidRequests, - data: {bidParams: bidParams, auctionId: bidderRequest.auctionId, origin: window.location.origin} - } - - return ServerRequestObjects; - }, - interpretResponse: function (serverResponse, bidRequest) { - var i - var bid - var bidObject - var url - var html - var ad - var token - var language - var scriptId - var bidResponses = [] - - for (i = 0; i < serverResponse.length; i++) { - ad = serverResponse[i]; - bid = find(bidRequest.bids, bid => bid.bidId === ad.bidId) - if (bid) { - token = bid.params.token || '' - - language = bid.params.language || SMARTICO_CONFIG.language || '' - - scriptId = encodeURIComponent('smartico-widget-' + bid.params.placementId + '-' + i) - - url = SMARTICO_CONFIG.widgetUrl + '?token=' + encodeURIComponent(token) + '&auction-id=' + encodeURIComponent(bid.auctionId) + '&from-auction-buffer=1&own_session=1&ad=' + encodeURIComponent(ad.id) + '&scriptid=' + scriptId + (ad.bannerFormatAlias ? '&banner-format=' + encodeURIComponent(ad.bannerFormatAlias) : '') + (language ? '&language=' + encodeURIComponent(language) : '') - - html = '`; - break; - } - }); - } - return result; -} - -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - if (result) { - return result; - } - } -} - -function flatten(arr) { - return [].concat(...arr); -} - -function getNativeAssets(bid) { - return utils._map(bid.nativeParams, (bidParams, key) => { - const props = NATIVE_PARAMS[key]; - const asset = { - required: bidParams.required & 1, - }; - if (props) { - asset.id = props.id; - let wmin, hmin, w, h; - let aRatios = bidParams.aspect_ratios; - - if (aRatios && aRatios[0]) { - aRatios = aRatios[0]; - wmin = aRatios.min_width || 0; - hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; - } - - if (bidParams.sizes) { - const sizes = flatten(bidParams.sizes); - w = sizes[0]; - h = sizes[1]; - } - - asset[props.name] = { - len: bidParams.len, - type: props.type, - wmin, - hmin, - w, - h - }; - - return asset; - } - }).filter(Boolean); -} - -/* Turn bid request sizes into ut-compatible format */ -function transformSizes(requestSizes) { - if (!utils.isArray(requestSizes)) { - return []; - } - - if (requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { - return [{ - w: parseInt(requestSizes[0], 10), - h: parseInt(requestSizes[1], 10) - }]; - } else if (utils.isArray(requestSizes[0])) { - return requestSizes.map(item => - ({ - w: parseInt(item[0], 10), - h: parseInt(item[1], 10) - }) - ); - } - - return []; -} diff --git a/modules/zemantaBidAdapter.md b/modules/zemantaBidAdapter.md deleted file mode 100644 index fa933ecd922..00000000000 --- a/modules/zemantaBidAdapter.md +++ /dev/null @@ -1,111 +0,0 @@ -# Overview - -``` -Module Name: Zemanta Adapter -Module Type: Bidder Adapter -Maintainer: prog-ops-team@outbrain.com -``` - -# Description - -Module that connects to zemanta bidder to fetch bids. -Both native and display formats are supported but not at the same time. Using OpenRTB standard. - -# Configuration - -## Bidder and usersync URLs - -The Zemanta adapter does not work without setting the correct bidder and usersync URLs. -You will receive the URLs when contacting us. - -``` -pbjs.setConfig({ - zemanta: { - bidderUrl: 'https://bidder-url.com', - usersyncUrl: 'https://usersync-url.com' - } -}); -``` - - -# Test Native Parameters -``` - var adUnits = [ - code: '/19968336/prebid_native_example_1', - mediaTypes: { - native: { - image: { - required: false, - sizes: [100, 50] - }, - title: { - required: false, - len: 140 - }, - sponsoredBy: { - required: false - }, - clickUrl: { - required: false - }, - body: { - required: false - }, - icon: { - required: false, - sizes: [50, 50] - } - } - }, - bids: [{ - bidder: 'zemanta', - params: { - publisher: { - id: '2706', // required - name: 'Publishers Name', - domain: 'publisher.com' - }, - tagid: 'tag-id', - bcat: ['IAB1-1'], - badv: ['example.com'] - } - }] - ]; - - pbjs.setConfig({ - zemanta: { - bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' - } - }); -``` - -# Test Display Parameters -``` - var adUnits = [ - code: '/19968336/prebid_display_example_1', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [{ - bidder: 'zemanta', - params: { - publisher: { - id: '2706', // required - name: 'Publishers Name', - domain: 'publisher.com' - }, - tagid: 'tag-id', - bcat: ['IAB1-1'], - badv: ['example.com'] - }, - }] - ]; - - pbjs.setConfig({ - zemanta: { - bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' - } - }); -``` diff --git a/src/constants.json b/src/constants.json index c43e88cf75f..c6fad4df3c6 100644 --- a/src/constants.json +++ b/src/constants.json @@ -79,10 +79,8 @@ "PRICE_BUCKET": "hb_pb", "SIZE": "hb_size", "DEAL": "hb_deal", - "SOURCE": "hb_source", "FORMAT": "hb_format", "UUID": "hb_uuid", - "CACHE_ID": "hb_cache_id", "CACHE_HOST": "hb_cache_host" }, "NATIVE_KEYS": { diff --git a/src/prebid.js b/src/prebid.js index f58c97ec581..7cdaa709d20 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -140,6 +140,20 @@ function validateNativeMediaType(adUnit) { return validatedAdUnit; } +function validateAdUnitPos(adUnit, mediaType) { + let pos = utils.deepAccess(adUnit, `mediaTypes.${mediaType}.pos`); + + if (!pos || !utils.isNumber(pos) || !isFinite(pos)) { + let warning = `Value of property 'pos' on ad unit ${adUnit.code} should be of type: Number`; + + utils.logWarn(warning); + events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, {type: 'WARNING', arguments: warning}); + delete adUnit.mediaTypes[mediaType].pos; + } + + return adUnit +} + export const adUnitSetupChecks = { validateBannerMediaType, validateVideoMediaType, @@ -167,10 +181,12 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { if (mediaTypes.banner) { validatedBanner = validateBannerMediaType(adUnit); + if (mediaTypes.banner.hasOwnProperty('pos')) validatedBanner = validateAdUnitPos(validatedBanner, 'banner'); } if (mediaTypes.video) { validatedVideo = validatedBanner ? validateVideoMediaType(validatedBanner) : validateVideoMediaType(adUnit); + if (mediaTypes.video.hasOwnProperty('pos')) validatedVideo = validateAdUnitPos(validatedVideo, 'video'); } if (mediaTypes.native) { diff --git a/src/refererDetection.js b/src/refererDetection.js index 56d1fa43f7b..7e9f2a7e6c7 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.js @@ -42,6 +42,10 @@ export function detectReferer(win) { * @returns {string|null} */ function getCanonicalUrl(doc) { + let pageURL = config.getConfig('pageUrl'); + + if (pageURL) return pageURL; + try { const element = doc.querySelector("link[rel='canonical']"); diff --git a/src/sizeMapping.js b/src/sizeMapping.js index 313da3f422a..cd5f1190069 100644 --- a/src/sizeMapping.js +++ b/src/sizeMapping.js @@ -121,22 +121,17 @@ function evaluateSizeConfig(configs) { return configs.reduce((results, config) => { if ( typeof config === 'object' && - typeof config.mediaQuery === 'string' + typeof config.mediaQuery === 'string' && + config.mediaQuery.length > 0 ) { let ruleMatch = false; - // TODO: (Prebid - 4.0) Remove empty mediaQuery string check. Disallow empty mediaQuery in sizeConfig. - // Refer: https://github.com/prebid/Prebid.js/pull/4691, https://github.com/prebid/Prebid.js/issues/4810 for more details. - if (config.mediaQuery === '') { - ruleMatch = true; - } else { - try { - ruleMatch = getWindowTop().matchMedia(config.mediaQuery).matches; - } catch (e) { - logWarn('Unfriendly iFrame blocks sizeConfig from being correctly evaluated'); - - ruleMatch = matchMedia(config.mediaQuery).matches; - } + try { + ruleMatch = getWindowTop().matchMedia(config.mediaQuery).matches; + } catch (e) { + logWarn('Unfriendly iFrame blocks sizeConfig from being correctly evaluated'); + + ruleMatch = matchMedia(config.mediaQuery).matches; } if (ruleMatch) { diff --git a/test/spec/modules/1ad4goodBidAdapter_spec.js b/test/spec/modules/1ad4goodBidAdapter_spec.js deleted file mode 100644 index b9cd86a4cf7..00000000000 --- a/test/spec/modules/1ad4goodBidAdapter_spec.js +++ /dev/null @@ -1,548 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/1ad4goodBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as bidderFactory from 'src/adapters/bidderFactory.js'; -import { deepClone } from 'src/utils.js'; -import { config } from 'src/config.js'; - -const ENDPOINT = 'https://hb.1ad4good.org/prebid'; - -describe('AdforgoodAdapter', 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': '1ad4good', - 'params': { - 'placementId': '10433394' - }, - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'placementId': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': '1ad4good', - 'params': { - 'placementId': '10433394' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should parse out private sizes', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - privateSizes: [300, 250] - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].private_sizes).to.exist; - expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); - }); - - it('should add source and verison to the tag', function () { - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.sdk).to.exist; - expect(payload.sdk).to.deep.equal({ - source: 'pbjs', - version: '$prebid.version$' - }); - }); - - it('should populate the ad_types array on all requests', function () { - ['banner', 'video'].forEach(type => { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes[type] = {}; - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].ad_types).to.deep.equal([type]); - }); - }); - - it('should populate the ad_types array on outstream requests', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes.video = {context: 'outstream'}; - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].ad_types).to.deep.equal(['video']); - }); - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should attach valid video params to the tag', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - video: { - id: 123, - minduration: 100, - foobar: 'invalid' - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - id: 123, - minduration: 100 - }); - }); - - it('should add video property when adUnit includes a renderer', function () { - const videoData = { - mediaTypes: { - video: { - context: 'outstream', - mimes: ['video/mp4'] - } - }, - params: { - placementId: '10433394', - video: { - skippable: true, - playback_method: ['auto_play_sound_off'] - } - } - }; - - let bidRequest1 = deepClone(bidRequests[0]); - bidRequest1 = Object.assign({}, bidRequest1, videoData, { - renderer: { - url: 'http://test.renderer.url', - render: function () {} - } - }); - - let bidRequest2 = deepClone(bidRequests[0]); - bidRequest2.adUnitCode = 'adUnit_code_2'; - bidRequest2 = Object.assign({}, bidRequest2, videoData); - - const request = spec.buildRequests([bidRequest1, bidRequest2]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - skippable: true, - playback_method: ['auto_play_sound_off'], - custom_renderer_present: true - }); - expect(payload.tags[1].video).to.deep.equal({ - skippable: true, - playback_method: ['auto_play_sound_off'] - }); - }); - - it('should attach valid user params to the tag', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - user: { - externalUid: '123', - foobar: 'invalid' - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.user).to.exist; - expect(payload.user).to.deep.equal({ - externalUid: '123', - }); - }); - - // it('should always populated tags[].sizes with 1,1 for native if otherwise not defined', function () { - // let bidRequest = Object.assign({}, - // bidRequests[0], - // { - // mediaType: 'native', - // nativeParams: { - // image: { required: true } - // } - // } - // ); - // bidRequest.sizes = [[150, 100], [300, 250]]; - - // let request = spec.buildRequests([bidRequest]); - // let payload = JSON.parse(request.data); - // expect(payload.tags[0].sizes).to.deep.equal([{width: 150, height: 100}, {width: 300, height: 250}]); - - // delete bidRequest.sizes; - - // request = spec.buildRequests([bidRequest]); - // payload = JSON.parse(request.data); - - // expect(payload.tags[0].sizes).to.deep.equal([{width: 1, height: 1}]); - // }); - - it('should convert keyword params to proper form and attaches to request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - keywords: { - single: 'val', - singleArr: ['val'], - singleArrNum: [5], - multiValMixed: ['value1', 2, 'value3'], - singleValNum: 123, - emptyStr: '', - emptyArr: [''], - badValue: {'foo': 'bar'} // should be dropped - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['5'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); - }); - - it('should add payment rules to the request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - usePaymentRule: true - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].use_pmt_rule).to.equal(true); - }); - - it('should add gdpr consent information to the request', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let bidderRequest = { - 'bidderCode': '1ad4good', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - gdprApplies: true - } - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - 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; - }); - - it('supports sending hybrid mobile app parameters', function () { - let appRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - app: { - id: 'B1O2W3M4AN.com.prebid.webview', - geo: { - lat: 40.0964439, - lng: -75.3009142 - }, - device_id: { - idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', // Apple advertising identifier - aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', // Android advertising identifier - md5udid: '5756ae9022b2ea1e47d84fead75220c8', // MD5 hash of the ANDROID_ID - sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', // SHA1 hash of the ANDROID_ID - windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' // Windows advertising identifier - } - } - } - } - ); - const request = spec.buildRequests([appRequest]); - const payload = JSON.parse(request.data); - expect(payload.app).to.exist; - expect(payload.app).to.deep.equal({ - appid: 'B1O2W3M4AN.com.prebid.webview' - }); - expect(payload.device.device_id).to.exist; - expect(payload.device.device_id).to.deep.equal({ - aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', - idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', - md5udid: '5756ae9022b2ea1e47d84fead75220c8', - sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', - windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' - }); - expect(payload.device.geo).to.exist; - expect(payload.device.geo).to.deep.equal({ - lat: 40.0964439, - lng: -75.3009142 - }); - }); - - it('should add referer info to payload', function () { - const bidRequest = Object.assign({}, bidRequests[0]) - const bidderRequest = { - 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 request = spec.buildRequests([bidRequest], bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.referrer_detection).to.exist; - expect(payload.referrer_detection).to.deep.equal({ - rd_ref: 'http%3A%2F%2Fexample.com%2Fpage.html', - rd_top: true, - rd_ifs: 2, - rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - }); - }); - }) - - describe('interpretResponse', function () { - let bfStub; - before(function() { - bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); - }); - - after(function() { - bfStub.restore(); - }); - - let response = { - 'version': '3.0.0', - 'tags': [ - { - 'uuid': '3db3773286ee59', - 'tag_id': 10433394, - 'auction_id': '4534722592064951574', - 'nobid': false, - 'no_ad_url': 'http://lax1-ib.adnxs.com/no-ad', - 'timeout_ms': 10000, - 'ad_profile_id': 27079, - 'ads': [ - { - 'content_source': 'rtb', - 'ad_type': 'banner', - 'buyer_member_id': 958, - 'creative_id': 29681110, - 'media_type_id': 1, - 'media_subtype_id': 1, - 'cpm': 0.5, - 'cpm_publisher_currency': 0.5, - 'publisher_currency_code': '$', - 'client_initiated_ad_counting': true, - 'viewability': { - 'config': '' - }, - 'rtb': { - 'banner': { - 'content': '', - 'width': 300, - 'height': 250 - }, - 'trackers': [ - { - 'impression_urls': [ - 'http://lax1-ib.adnxs.com/impression' - ], - 'video_events': {} - } - ] - } - } - ] - } - ] - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - 'requestId': '3db3773286ee59', - 'cpm': 0.5, - 'creativeId': 29681110, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '', - 'mediaType': 'banner', - 'currency': 'USD', - 'ttl': 300, - 'netRevenue': true, - 'adUnitCode': 'code', - 'ads4good': { - 'buyerMemberId': 958 - } - } - ]; - let bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function () { - let response = { - 'version': '0.0.1', - 'tags': [{ - 'uuid': '84ab500420319d', - 'tag_id': 5976557, - 'auction_id': '297492697822162468', - 'nobid': true - }] - }; - let bidderRequest; - - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result.length).to.equal(0); - }); - - it('handles non-banner media responses', function () { - let response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'content': '' - } - }, - 'javascriptTrackers': '' - }] - }] - }; - let bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code' - }] - } - - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result[0]).to.have.property('vastUrl'); - expect(result[0]).to.have.property('vastImpUrl'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); - - it('should add deal_priority and deal_code', function() { - let responseWithDeal = deepClone(response); - responseWithDeal.tags[0].ads[0].deal_priority = 'high'; - responseWithDeal.tags[0].ads[0].deal_code = '123'; - - let bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - let result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); - expect(Object.keys(result[0].ads4good)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); - }); - - it('should add advertiser id', function() { - let responseAdvertiserId = deepClone(response); - responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; - - let bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); - expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); - }) - }); -}); diff --git a/test/spec/modules/7xbidBidAdapter_spec.js b/test/spec/modules/7xbidBidAdapter_spec.js deleted file mode 100644 index bed2c604349..00000000000 --- a/test/spec/modules/7xbidBidAdapter_spec.js +++ /dev/null @@ -1,160 +0,0 @@ -import {expect} from 'chai'; -import {spec, _getUrlVars} from 'modules/7xbidBidAdapter.js'; -import * as utils from 'src/utils.js'; - -const BASE_URI = '//bidder.7xbid.com/api/v1/prebid/banner' -const NATIVE_BASE_URI = '//bidder.7xbid.com/api/v1/prebid/native' - -describe('7xbid adapter', function() { - let bidRequests; - let nativeBidRequests; - - beforeEach(function() { - bidRequests = [ - { - bidder: '7xbid', - params: { - placementId: 1425292, - currency: 'USD' - } - } - ] - - nativeBidRequests = [ - { - bidder: '7xbid', - params: { - placementId: 1429695, - currency: 'USD' - }, - nativeParams: { - title: { - required: true, - len: 80 - }, - image: { - required: true, - sizes: [150, 50] - }, - sponsoredBy: { - required: true - } - } - } - ] - }) - describe('isBidRequestValid', function () { - it('valid bid case', function () { - let validBid = { - bidder: '7xbid', - params: { - placementId: 1425292, - currency: 'USD' - } - } - let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - - it('invalid bid case: placementId is not passed', function() { - let validBid = { - bidder: '7xbid', - params: { - } - } - let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }) - - it('invalid bid case: currency is not support', function() { - let validBid = { - bidder: '7xbid', - params: { - placementId: 1108295, - currency: 'AUD' - } - } - let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }) - }) - - describe('buildRequests', function () { - it('sends bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(bidRequests)[0]; - expect(request.url).to.equal(BASE_URI); - expect(request.method).to.equal('GET'); - }); - - it('sends native bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(nativeBidRequests)[0]; - expect(request.url).to.equal(NATIVE_BASE_URI); - expect(request.method).to.equal('GET'); - }); - - it('buildRequests function should not modify original bidRequests object', function () { - let originalBidRequests = utils.deepClone(bidRequests); - let request = spec.buildRequests(bidRequests); - expect(bidRequests).to.deep.equal(originalBidRequests); - }); - - it('buildRequests function should not modify original nativeBidRequests object', function () { - let originalBidRequests = utils.deepClone(nativeBidRequests); - let request = spec.buildRequests(nativeBidRequests); - expect(nativeBidRequests).to.deep.equal(originalBidRequests); - }); - - it('Request params check', function() { - let request = spec.buildRequests(bidRequests)[0]; - const data = _getUrlVars(request.data) - expect(parseInt(data.placementid)).to.exist.and.to.equal(bidRequests[0].params.placementId); - expect(data.cur).to.exist.and.to.equal(bidRequests[0].params.currency); - }) - - it('Native request params check', function() { - let request = spec.buildRequests(nativeBidRequests)[0]; - const data = _getUrlVars(request.data) - expect(parseInt(data.placementid)).to.exist.and.to.equal(nativeBidRequests[0].params.placementId); - expect(data.cur).to.exist.and.to.equal(nativeBidRequests[0].params.currency); - }) - }) - - describe('interpretResponse', function () { - let response = { - 1425292: - { - 'creativeId': '', - 'cur': 'USD', - 'price': 0.0920, - 'width': 300, - 'height': 250, - 'requestid': '2e42361a6172bf', - 'adm': '' - } - } - - it('should get correct bid response', function () { - let expectedResponse = [ - { - 'requestId': '2e42361a6172bf', - 'cpm': 0.0920, - 'width': 300, - 'height': 250, - 'netRevenue': true, - 'currency': 'USD', - 'creativeId': '', - 'ttl': 700, - 'ad': '' - } - ]; - let request = spec.buildRequests(bidRequests)[0]; - let result = spec.interpretResponse({body: response}, request); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - expect(result[0].cpm).to.not.equal(null); - expect(result[0].creativeId).to.not.equal(null); - expect(result[0].ad).to.not.equal(null); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(true); - }); - }) -}) diff --git a/test/spec/modules/aardvarkBidAdapter_spec.js b/test/spec/modules/aardvarkBidAdapter_spec.js deleted file mode 100644 index 9671f961407..00000000000 --- a/test/spec/modules/aardvarkBidAdapter_spec.js +++ /dev/null @@ -1,569 +0,0 @@ -import { expect } from 'chai'; -import * as utils from 'src/utils.js'; -import { spec, resetUserSync } from 'modules/aardvarkBidAdapter.js'; - -describe('aardvarkAdapterTest', function () { - describe('forming valid bidRequests', function () { - it('should accept valid bidRequests', function () { - expect(spec.isBidRequestValid({ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - sizes: [[300, 250]] - })).to.equal(true); - }); - - it('should reject invalid bidRequests', function () { - expect(spec.isBidRequestValid({ - bidder: 'aardvark', - params: { - ai: 'xiby', - }, - sizes: [[300, 250]] - })).to.equal(false); - }); - }); - - describe('executing network requests', function () { - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337', - userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } - }, - { - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'RAZd', - host: 'adzone.pub.com' - }, - adUnitCode: 'bbb', - transactionId: '193995b4-7122-4739-959b-2463282a138b', - sizes: [[800, 600]], - bidId: '22aidtbx5eabd9', - bidderRequestId: '70deaff71c281d', - auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should use HTTP GET method', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.method).to.equal('GET'); - }); - }); - - it('should call the correct bidRequest url', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].url).to.match(new RegExp('^https:\/\/adzone.pub.com/xiby/TdAx_RAZd/aardvark\?')); - }); - - it('should have correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.version).to.equal(1); - expect(requests[0].data.jsonp).to.equal(false); - expect(requests[0].data.TdAx).to.equal('1abgs362e0x48a8'); - expect(requests[0].data.rtkreferer).to.not.be.undefined; - expect(requests[0].data.RAZd).to.equal('22aidtbx5eabd9'); - }); - - it('should have tdid, it is available in bidRequest', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.data.tdid).to.equal('eff98622-b5fd-44fa-9a49-6e846922d532'); - }); - }); - }); - - describe('splitting multi-auction ad units into own requests', function () { - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'Toby', - sc: 'TdAx', - categories: ['cat1', 'cat2'] - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }, - { - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'RAZd', - host: 'adzone.pub.com' - }, - adUnitCode: 'bbb', - transactionId: '193995b4-7122-4739-959b-2463282a138b', - sizes: [[800, 600]], - bidId: '22aidtbx5eabd9', - bidderRequestId: '70deaff71c281d', - auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should use HTTP GET method', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.method).to.equal('GET'); - }); - }); - - it('should call the correct bidRequest urls for each auction', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].url).to.match(new RegExp('^https:\/\/bidder.rtk.io/Toby/TdAx/aardvark\?')); - expect(requests[0].data.categories.length).to.equal(2); - expect(requests[1].url).to.match(new RegExp('^https:\/\/adzone.pub.com/xiby/RAZd/aardvark\?')); - }); - - it('should have correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(2); - expect(requests[0].data.version).to.equal(1); - expect(requests[0].data.jsonp).to.equal(false); - expect(requests[0].data.TdAx).to.equal('1abgs362e0x48a8'); - expect(requests[0].data.rtkreferer).to.not.be.undefined; - expect(requests[0].data.RAZd).to.be.undefined; - expect(requests[1].data.version).to.equal(1); - expect(requests[1].data.jsonp).to.equal(false); - expect(requests[1].data.TdAx).to.be.undefined; - expect(requests[1].data.rtkreferer).to.not.be.undefined; - expect(requests[1].data.RAZd).to.equal('22aidtbx5eabd9'); - }); - - it('should have no tdid, it is not available in bidRequest', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.data.tdid).to.be.undefined; - }); - }); - }); - - describe('GDPR conformity', function () { - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }]; - - const bidderRequest = { - gdprConsent: { - consentString: 'awefasdfwefasdfasd', - gdprApplies: true - }, - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should transmit correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.gdpr).to.equal(true); - expect(requests[0].data.consent).to.equal('awefasdfwefasdfasd'); - }); - }); - - describe('GDPR absence conformity', function () { - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }]; - - const bidderRequest = { - gdprConsent: undefined, - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should transmit correct data', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.gdpr).to.be.undefined; - expect(requests[0].data.consent).to.be.undefined; - }); - }); - - describe('CCPA conformity', function () { - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' - }]; - - it('should transmit us_privacy data', function () { - const usp = '1NY-'; - const bidderRequest = { - gdprConsent: { - consentString: 'awefasdfwefasdfasd', - gdprApplies: true - }, - refererInfo: { - referer: 'http://example.com' - }, - uspConsent: usp - }; - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.gdpr).to.equal(true); - expect(requests[0].data.consent).to.equal('awefasdfwefasdfasd'); - expect(requests[0].data.us_privacy).to.equal(usp); - }); - - it('should not send us_privacy', function () { - const bidderRequest = { - refererInfo: { - referer: 'http://example.com' - } - }; - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests.length).to.equal(1); - expect(requests[0].data.gdpr).to.be.undefined; - expect(requests[0].data.consent).to.be.undefined; - expect(requests[0].data.us_privacy).to.be.undefined; - }); - }); - - describe('interpretResponse', function () { - it('should handle bid responses', function () { - const serverResponse = { - body: [ - { - media: 'banner', - nurl: 'https://www.nurl.com/0', - cpm: 0.09, - width: 300, - height: 250, - cid: '22aidtbx5eabd9', - adm: '
', - dealId: 'dealing', - ttl: 200, - }, - { - media: 'banner', - nurl: 'https://www.nurl.com/1', - cpm: 0.19, - width: 300, - height: 250, - cid: '1abgs362e0x48a8', - adm: '', - ttl: 200, - ex: 'extraproperty' - } - ], - headers: {} - }; - - const result = spec.interpretResponse(serverResponse, {}); - expect(result.length).to.equal(2); - - expect(result[0].requestId).to.equal('22aidtbx5eabd9'); - expect(result[0].cpm).to.equal(0.09); - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].currency).to.equal('USD'); - expect(result[0].ttl).to.equal(200); - expect(result[0].dealId).to.equal('dealing'); - expect(result[0].ex).to.be.undefined; - expect(result[0].ad).to.not.be.undefined; - - expect(result[1].requestId).to.equal('1abgs362e0x48a8'); - expect(result[1].cpm).to.equal(0.19); - expect(result[1].width).to.equal(300); - expect(result[1].height).to.equal(250); - expect(result[1].currency).to.equal('USD'); - expect(result[1].ttl).to.equal(200); - expect(result[1].ad).to.not.be.undefined; - expect(result[1].ex).to.equal('extraproperty'); - }); - - it('should handle nobid responses', function () { - var emptyResponse = [{ - nurl: '', - cid: '9e5a09319e18f1', - media: 'banner', - error: 'No bids received for 9DgF', - adm: '', - id: '9DgF', - cpm: 0.00 - }]; - - var result = spec.interpretResponse({ body: emptyResponse }, {}); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function () { - const syncOptions = { - iframeEnabled: true - }; - - it('should produce sync url', function () { - const syncs = spec.getUserSyncs(syncOptions); - expect(syncs.length).to.equal(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://sync.rtk.io/cs'); - }); - - it('should return empty, as we sync only once', function () { - const syncs = spec.getUserSyncs(syncOptions); - expect(syncs.length).to.equal(0); - }); - - it('should reset hasSynced flag, allowing another sync', function () { - resetUserSync(); - - const syncs = spec.getUserSyncs(syncOptions); - expect(syncs.length).to.equal(1); - }); - - it('should return empty when iframe disallowed', function () { - resetUserSync(); - - const noIframeOptions = { iframeEnabled: false }; - const syncs = spec.getUserSyncs(noIframeOptions); - expect(syncs.length).to.equal(0); - }); - - it('should produce sync url with gdpr params', function () { - const gdprConsent = { - gdprApplies: true, - consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA' - }; - - resetUserSync(); - - const syncs = spec.getUserSyncs(syncOptions, null, gdprConsent); - expect(syncs.length).to.equal(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://sync.rtk.io/cs?g=1&c=BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'); - }); - - it('should produce sync url with ccpa params', function () { - resetUserSync(); - - const syncs = spec.getUserSyncs(syncOptions, null, {}, '1YYN'); - expect(syncs.length).to.equal(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://sync.rtk.io/cs?us_privacy=1YYN'); - }); - }); - - describe('reading window.top properties', function () { - const bidCategories = ['bcat1', 'bcat2', 'bcat3']; - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - host: 'adzone.pub.com', - categories: bidCategories - }, - adUnitCode: 'RTK_aaaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337', - userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - - const topWin = { - innerWidth: 1366, - innerHeight: 768, - rtkcategories: ['cat1', 'cat2', 'cat3'] - }; - - let sandbox; - beforeEach(function () { - sandbox = sinon.createSandbox(); - }); - - afterEach(function () { - sandbox.restore(); - }); - - it('should have window.top dimensions', function () { - sandbox.stub(utils, 'getWindowTop').returns(topWin); - - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.data.w).to.equal(topWin.innerWidth); - expect(requestItem.data.h).to.equal(topWin.innerHeight); - }); - }); - - it('should have window dimensions, as backup', function () { - sandbox.stub(utils, 'getWindowTop').returns(undefined); - - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - expect(requestItem.data.w).to.equal(window.innerWidth); - expect(requestItem.data.h).to.equal(window.innerHeight); - }); - }); - - it('should have window.top & bid categories', function () { - sandbox.stub(utils, 'getWindowTop').returns(topWin); - - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function (requestItem) { - utils._each(topWin.categories, function (cat) { - expect(requestItem.data.categories).to.contain(cat); - }); - utils._each(bidCategories, function (cat) { - expect(requestItem.data.categories).to.contain(cat); - }); - }); - }); - }); - - describe('schain support', function() { - const nodePropsOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; - let schainConfig = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'rtk.io', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'first pub', - domain: 'first.com' - }, - { - asi: 'rtk.io', - sid: '5678', - hp: 1, - rid: 'bid-request-2', - name: 'second pub', - domain: 'second.com' - } - ] - }; - - const bidRequests = [{ - bidder: 'aardvark', - params: { - ai: 'xiby', - sc: 'TdAx', - }, - adUnitCode: 'aaa', - transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - sizes: [300, 250], - bidId: '1abgs362e0x48a8', - bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337', - schain: schainConfig, - }]; - - const bidderRequest = { - gdprConsent: undefined, - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should properly serialize schain object with correct delimiters', () => { - const results = spec.buildRequests(bidRequests, bidderRequest); - const numNodes = schainConfig.nodes.length; - - const schain = results[0].data.schain; - - // each node serialization should start with an ! - expect(schain.match(/!/g).length).to.equal(numNodes); - - // 5 commas per node plus 1 for version - expect(schain.match(/,/g).length).to.equal(numNodes * 5 + 1); - }); - - it('should send the proper version for the schain', () => { - const results = spec.buildRequests(bidRequests, bidderRequest); - const schain = decodeURIComponent(results[0].data.schain).split('!'); - const version = schain.shift().split(',')[0]; - expect(version).to.equal(bidRequests[0].schain.ver); - }); - - it('should send the correct value for complete in schain', () => { - const results = spec.buildRequests(bidRequests, bidderRequest); - const schain = decodeURIComponent(results[0].data.schain).split('!'); - const complete = schain.shift().split(',')[1]; - expect(complete).to.equal(String(bidRequests[0].schain.complete)); - }); - - it('should send available params in the right order', () => { - const results = spec.buildRequests(bidRequests, bidderRequest); - const schain = decodeURIComponent(results[0].data.schain).split('!'); - schain.shift(); - - schain.forEach((serializeNode, nodeIndex) => { - const nodeProps = serializeNode.split(','); - nodeProps.forEach((nodeProp, propIndex) => { - const node = schainConfig.nodes[nodeIndex]; - const key = nodePropsOrder[propIndex]; - expect(nodeProp).to.equal(node[key] ? String(node[key]) : ''); - }); - }); - }); - }); -}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 4b66a96be16..53d1be78703 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -696,7 +696,7 @@ describe('Adagio bid adapter', () => { describe('with userID modules', function() { const userId = { - sharedid: {id: '01EAJWWNEPN3CYMM5N8M5VXY22', third: '01EAJWWNEPN3CYMM5N8M5VXY22'}, + pubcid: '01EAJWWNEPN3CYMM5N8M5VXY22', unsuported: '666' }; @@ -710,13 +710,10 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); const expected = [{ - source: 'sharedid.org', + source: 'pubcid.org', uids: [ { atype: 1, - ext: { - third: '01EAJWWNEPN3CYMM5N8M5VXY22' - }, id: '01EAJWWNEPN3CYMM5N8M5VXY22' } ] diff --git a/test/spec/modules/adbutlerBidAdapter_spec.js b/test/spec/modules/adbutlerBidAdapter_spec.js deleted file mode 100644 index 6dedce321d8..00000000000 --- a/test/spec/modules/adbutlerBidAdapter_spec.js +++ /dev/null @@ -1,231 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adbutlerBidAdapter.js'; - -describe('AdButler adapter', function () { - let bidRequests; - - beforeEach(function () { - bidRequests = [ - { - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093', - keyword: 'red', - minCPM: '1.00', - maxCPM: '5.00', - extra: { - foo: 'bar', - } - }, - placementCode: '/19968336/header-bid-tag-1', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: '23acc48ad47af5', - auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' - } - ]; - }); - - describe('implementation', function () { - describe('for requests', function () { - it('should accept valid bid', function () { - let validBid = { - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093' - } - }, - isValid = spec.isBidRequestValid(validBid); - - expect(isValid).to.equal(true); - }); - - it('should reject invalid bid', function () { - let invalidBid = { - bidder: 'adbutler', - params: { - accountID: '167283', - } - }, - isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should use custom domain string', function () { - let bidRequests = [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '107878', - zoneID: '86133', - domain: 'servedbyadbutler.com.dan.test' - }, - auctionId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - ], - requests = spec.buildRequests(bidRequests), - requestURL = requests[0].url; - - expect(requestURL).to.have.string('.dan.test'); - }); - - it('should set default domain', function () { - let requests = spec.buildRequests(bidRequests), - request = requests[0]; - - let [domain] = request.url.split('/adserve/'); - - expect(domain).to.equal('https://servedbyadbutler.com'); - }); - - it('should set the keyword parameter', function () { - let requests = spec.buildRequests(bidRequests), - requestURL = requests[0].url; - - expect(requestURL).to.have.string(';kw=red;'); - }); - - it('should set the extra parameter', () => { - let requests = spec.buildRequests(bidRequests); - let requestURL = requests[0].url; - - expect(requestURL).to.have.string(';foo=bar;'); - }); - - it('should increment the count for the same zone', function () { - let bidRequests = [ - { - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '107878', - zoneID: '86133', - } - }, { - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '107878', - zoneID: '86133', - } - }, - ], - requests = spec.buildRequests(bidRequests), - firstRequest = requests[0].url, - secondRequest = requests[1].url; - - expect(firstRequest).to.have.string(';place=0;'); - expect(secondRequest).to.have.string(';place=1;'); - }); - }); - - describe('bid responses', function () { - it('should return complete bid response', function () { - let serverResponse = { - body: { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0, - ad_code: '', - tracking_pixels: [ - 'http://tracking.pixel.com/params=info' - ] - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(1); - - expect(bids[0].bidderCode).to.equal('adbutler'); - expect(bids[0].cpm).to.equal(1.5); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(250); - expect(bids[0].currency).to.equal('USD'); - expect(bids[0].netRevenue).to.equal(true); - expect(bids[0].ad).to.have.length.above(1); - expect(bids[0].ad).to.have.string('http://tracking.pixel.com/params=info'); - }); - - it('should return empty bid response', function () { - let serverResponse = { - body: { - status: 'NO_ELIGIBLE_ADS', - zone_id: 210083, - width: 300, - height: 250, - place: 0 - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(0); - }); - - it('should return empty bid response on incorrect size', function () { - let serverResponse = { - body: { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210083, - cpm: 1.5, - width: 728, - height: 90, - place: 0 - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(0); - }); - - it('should return empty bid response with CPM too low', function () { - let serverResponse = { - body: { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 0.75, - width: 300, - height: 250, - place: 0 - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(0); - }); - - it('should return empty bid response with CPM too high', function () { - let serverResponse = { - body: { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 7, - width: 300, - height: 250, - place: 0 - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(0); - }); - }); - }); -}); diff --git a/test/spec/modules/adfinityBidAdapter_spec.js b/test/spec/modules/adfinityBidAdapter_spec.js deleted file mode 100644 index 479a2303dd5..00000000000 --- a/test/spec/modules/adfinityBidAdapter_spec.js +++ /dev/null @@ -1,151 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/adfinityBidAdapter.js'; - -describe('AdfinityAdapter', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'adfinity', - bidderRequestId: '145e1d6a7837c9', - params: { - placement_id: 0 - }, - placementCode: 'placementid_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - domain: 'example.com' - } - ] - } - }; - let bidderRequest = { - bidderCode: 'adfinity', - auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', - bidderRequestId: 'ffffffffffffff', - start: 1472239426002, - auctionStart: 1472239426000, - timeout: 5000, - uspConsent: '1YN-', - refererInfo: { - referer: 'http://www.example.com', - reachedTop: true, - }, - bids: [bid] - } - - describe('isBidRequestValid', function () { - it('Should return true when placement_id can be cast to a number', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when placement_id is not a number', function () { - bid.params.placement_id = 'aaa'; - 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://stat.adfinity.pro/?c=o&m=multi'); - }); - - 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'); - 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'); - let placements = data['placements']; - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); - expect(placement.schain).to.be.an('object') - expect(placement.placementId).to.be.a('number'); - expect(placement.bidId).to.be.a('string'); - expect(placement.traffic).to.be.a('string'); - expect(placement.sizes).to.be.an('array'); - } - }); - 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 () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - - describe('getUserSyncs', function () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://stat.adfinity.pro/?c=o&m=cookie'); - }); - }); -}); diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js deleted file mode 100644 index 79ea76da8dd..00000000000 --- a/test/spec/modules/adformBidAdapter_spec.js +++ /dev/null @@ -1,550 +0,0 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/adformBidAdapter.js'; -import { BANNER, VIDEO } from 'src/mediaTypes.js'; -import { config } from 'src/config.js'; -import { createEidsArray } from 'modules/userId/eids.js'; - -describe('Adform adapter', function () { - let serverResponse, bidRequest, bidResponses; - let bids = []; - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'adform', - 'params': { - 'mid': '19910113' - } - }; - - it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); - }); - - it('should return false when required params are missing', function () { - bid.params = { - adxDomain: 'adx.adform.net' - }; - assert.isFalse(spec.isBidRequestValid(bid)); - }); - }); - - describe('buildRequests', function () { - it('should pass multiple bids via single request', function () { - let request = spec.buildRequests(bids); - let parsedUrl = parseUrl(request.url); - assert.lengthOf(parsedUrl.items, 7); - }); - - it('should handle global request parameters', function () { - let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); - let query = parsedUrl.query; - - assert.equal(parsedUrl.path, 'https://newDomain/adx'); - assert.equal(query.tid, 45); - assert.equal(query.rp, 4); - assert.equal(query.fd, 1); - assert.equal(query.stid, '7aefb970-2045'); - assert.equal(query.url, encodeURIComponent('some// there')); - }); - - it('should set correct request method', function () { - let request = spec.buildRequests([bids[0]]); - assert.equal(request.method, 'GET'); - }); - - it('should pass request currency from config', function () { - config.setConfig({ currency: { adServerCurrency: 'PLN' } }); - let request = parseUrl(spec.buildRequests(bids).url); - - request.items.forEach(item => { - assert.equal(item.rcur, 'PLN'); - }); - }); - - it('should prefer bid currency over global config', function () { - config.setConfig({ currency: { adServerCurrency: 'PLN' } }); - bids[0].params.rcur = 'USD'; - let request = parseUrl(spec.buildRequests(bids).url); - const currencies = request.items.map(item => item.rcur); - - assert.deepEqual(currencies, [ 'USD', 'PLN', 'PLN', 'PLN', 'PLN', 'PLN', 'PLN' ]); - }); - - it('should correctly form bid items', function () { - let bidList = bids; - let request = spec.buildRequests(bidList); - let parsedUrl = parseUrl(request.url); - assert.deepEqual(parsedUrl.items, [ - { - mid: '1', - transactionId: '5f33781f-9552-4ca1' - }, - { - mid: '2', - someVar: 'someValue', - pt: 'gross', - transactionId: '5f33781f-9552-4iuy' - }, - { - mid: '3', - pdom: 'home', - transactionId: '5f33781f-9552-7ev3' - }, - { - mid: '3', - pdom: 'home', - transactionId: '5f33781f-9552-7ev3' - }, - { - mid: '3', - pdom: 'home', - transactionId: '5f33781f-9552-7ev3' - }, - { - mid: '5', - pt: 'net', - transactionId: '5f33781f-9552-7ev3', - }, - { - mid: '6', - pt: 'gross', - transactionId: '5f33781f-9552-7ev3' - } - ]); - }); - - it('should not change original validBidRequests object', function () { - var resultBids = JSON.parse(JSON.stringify(bids[0])); - let request = spec.buildRequests([bids[0]]); - assert.deepEqual(resultBids, bids[0]); - }); - - it('should set gross to the request, if there is any gross priceType', function () { - let request = spec.buildRequests([bids[5], bids[5]]); - let parsedUrl = parseUrl(request.url); - - assert.equal(parsedUrl.query.pt, 'net'); - - request = spec.buildRequests([bids[4], bids[3]]); - parsedUrl = parseUrl(request.url); - - assert.equal(parsedUrl.query.pt, 'gross'); - }); - - it('should pass extended ids', function () { - bids[0].userIdAsEids = createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE', - pubcid: 'pubCommonId_FROM_USER_ID_MODULE' - }); - let request = spec.buildRequests(bids); - let eids = parseUrl(request.url).query.eids; - - assert.equal(eids, 'eyJhZHNlcnZlci5vcmciOnsiVFREX0lEX0ZST01fVVNFUl9JRF9NT0RVTEUiOlsxXX0sInB1YmNpZC5vcmciOnsicHViQ29tbW9uSWRfRlJPTV9VU0VSX0lEX01PRFVMRSI6WzFdfX0%3D'); - assert.deepEqual(JSON.parse(atob(decodeURIComponent(eids))), { - 'adserver.org': { - 'TTD_ID_FROM_USER_ID_MODULE': [1] - }, - 'pubcid.org': { - 'pubCommonId_FROM_USER_ID_MODULE': [1] - } - }); - }); - - it('should allow to pass custom extended ids', function () { - bids[0].params.eids = 'some_id_value'; - let request = spec.buildRequests(bids); - let eids = parseUrl(request.url).query.eids; - - assert.equal(eids, 'some_id_value'); - }); - - it('should add parameter to global parameters if it exists in all bids', function () { - const _bids = []; - _bids.push(bids[0]); - _bids.push(bids[1]); - _bids.push(bids[2]); - _bids[0].params.mkv = 'key:value,key1:value1'; - _bids[1].params.mkv = 'key:value,key1:value1,keyR:removed'; - _bids[2].params.mkv = 'key:value,key1:value1,keyR:removed,key8:value1'; - _bids[0].params.mkw = 'targeting'; - _bids[1].params.mkw = 'targeting'; - _bids[2].params.mkw = 'targeting,targeting2'; - _bids[0].params.msw = 'search:word,search:word2'; - _bids[1].params.msw = 'search:word'; - _bids[2].params.msw = 'search:word,search:word5'; - let bidList = _bids; - let request = spec.buildRequests(bidList); - let parsedUrl = parseUrl(request.url); - assert.equal(parsedUrl.query.mkv, encodeURIComponent('key:value,key1:value1')); - assert.equal(parsedUrl.query.mkw, 'targeting'); - assert.equal(parsedUrl.query.msw, encodeURIComponent('search:word')); - assert.ok(!parsedUrl.items[0].mkv); - assert.ok(!parsedUrl.items[0].mkw); - assert.equal(parsedUrl.items[0].msw, 'search:word2'); - assert.equal(parsedUrl.items[1].mkv, 'keyR:removed'); - assert.ok(!parsedUrl.items[1].mkw); - assert.ok(!parsedUrl.items[1].msw); - assert.equal(parsedUrl.items[2].mkv, 'keyR:removed,key8:value1'); - assert.equal(parsedUrl.items[2].mkw, 'targeting2'); - assert.equal(parsedUrl.items[2].msw, 'search:word5'); - }); - - describe('user privacy', function () { - it('should send GDPR Consent data to adform if gdprApplies', function () { - let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); - let parsedUrl = parseUrl(request.url).query; - - assert.equal(parsedUrl.gdpr, '1'); - assert.equal(parsedUrl.gdpr_consent, 'concentDataString'); - }); - - it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { - let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: false, consentString: 'concentDataString'}}); - let parsedUrl = parseUrl(request.url).query; - - assert.equal(parsedUrl.gdpr, '0'); - assert.equal(parsedUrl.gdpr_consent, 'concentDataString'); - - request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: undefined, consentString: 'concentDataString'}}); - parsedUrl = parseUrl(request.url).query; - assert.ok(!parsedUrl.gdpr); - assert.ok(!parsedUrl.gdpr_consent); - }); - - it('should return GDPR Consent data with request data', function () { - let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); - - assert.deepEqual(request.gdpr, { - gdpr: true, - gdpr_consent: 'concentDataString' - }); - - request = spec.buildRequests([bids[0]]); - assert.ok(!request.gdpr); - }); - - it('should send CCPA Consent data to adform', function () { - const request = spec.buildRequests([bids[0]], {uspConsent: '1YA-'}); - const parsedUrl = parseUrl(request.url).query; - - assert.equal(parsedUrl.us_privacy, '1YA-'); - }); - }); - }); - - describe('interpretResponse', function () { - it('should respond with empty response when there is empty serverResponse', function () { - let result = spec.interpretResponse({ body: {} }, {}); - assert.deepEqual(result, []); - }); - it('should respond with empty response when response from server is not banner', function () { - serverResponse.body[0].response = 'not banner'; - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - let result = spec.interpretResponse(serverResponse, bidRequest); - - assert.deepEqual(result, []); - }); - it('should interpret server response correctly with one bid', function () { - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - let result = spec.interpretResponse(serverResponse, bidRequest)[0]; - - assert.equal(result.requestId, '2a0cf4e'); - assert.equal(result.cpm, 13.9); - assert.equal(result.width, 300); - assert.equal(result.height, 250); - assert.equal(result.creativeId, '2a0cf4e'); - assert.equal(result.dealId, '123abc'); - assert.equal(result.currency, 'EUR'); - assert.equal(result.netRevenue, true); - assert.equal(result.ttl, 360); - assert.deepEqual(result.meta.advertiserDomains, []) - assert.equal(result.ad, ''); - assert.equal(result.bidderCode, 'adform'); - assert.equal(result.transactionId, '5f33781f-9552-4ca1'); - }); - - it('should set correct netRevenue', function () { - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[1]]; - bidRequest.netRevenue = 'gross'; - let result = spec.interpretResponse(serverResponse, bidRequest)[0]; - - assert.equal(result.netRevenue, false); - }); - - it('should create bid response item for every requested item', function () { - let result = spec.interpretResponse(serverResponse, bidRequest); - assert.lengthOf(result, 5); - }); - - it('should create bid response with vast xml', function () { - const result = spec.interpretResponse(serverResponse, bidRequest)[3]; - assert.equal(result.vastXml, ''); - }); - - it('should create bid response with vast url', function () { - const result = spec.interpretResponse(serverResponse, bidRequest)[4]; - assert.equal(result.vastUrl, 'vast://url'); - }); - - it('should set mediaType on bid response', function () { - const expected = [ BANNER, BANNER, BANNER, VIDEO, VIDEO ]; - const result = spec.interpretResponse(serverResponse, bidRequest); - for (let i = 0; i < result.length; i++) { - assert.equal(result[i].mediaType, expected[i]); - } - }); - - it('should set default netRevenue as gross', function () { - bidRequest.netRevenue = 'gross'; - const result = spec.interpretResponse(serverResponse, bidRequest); - for (let i = 0; i < result.length; i++) { - assert.equal(result[i].netRevenue, false); - } - }); - - it('should set gdpr if it exist in bidRequest', function () { - bidRequest.gdpr = { - gdpr: true, - gdpr_consent: 'ERW342EIOWT34234KMGds' - }; - let result = spec.interpretResponse(serverResponse, bidRequest); - for (let i = 0; i < result.length; i++) { - assert.equal(result[i].gdpr, true); - assert.equal(result[i].gdpr_consent, 'ERW342EIOWT34234KMGds'); - } - - bidRequest.gdpr = undefined; - result = spec.interpretResponse(serverResponse, bidRequest); - for (let i = 0; i < result.length; i++) { - assert.ok(!result[i].gdpr); - assert.ok(!result[i].gdpr_consent); - } - }); - - it('should set a renderer only for an outstream context', function () { - serverResponse.body = [serverResponse.body[3], serverResponse.body[2]]; - bidRequest.bids = [bidRequest.bids[6], bidRequest.bids[6]]; - let result = spec.interpretResponse(serverResponse, bidRequest); - assert.ok(result[0].renderer); - assert.equal(result[1].renderer, undefined); - }); - - describe('verifySizes', function () { - it('should respond with empty response when sizes doesn\'t match', function () { - serverResponse.body[0].response = 'banner'; - serverResponse.body[0].width = 100; - serverResponse.body[0].height = 150; - - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - let result = spec.interpretResponse(serverResponse, bidRequest); - - assert.equal(serverResponse.body.length, 1); - assert.equal(serverResponse.body[0].response, 'banner'); - assert.deepEqual(result, []); - }); - it('should respond with empty response when sizes as a strings doesn\'t match', function () { - serverResponse.body[0].response = 'banner'; - serverResponse.body[0].width = 100; - serverResponse.body[0].height = 150; - - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - - bidRequest.bids[0].sizes = [['101', '150']]; - let result = spec.interpretResponse(serverResponse, bidRequest); - - assert.equal(serverResponse.body.length, 1); - assert.equal(serverResponse.body[0].response, 'banner'); - assert.deepEqual(result, []); - }); - it('should support size dimensions as a strings', function () { - serverResponse.body[0].response = 'banner'; - serverResponse.body[0].width = 300; - serverResponse.body[0].height = 600; - - serverResponse.body = [serverResponse.body[0]]; - bidRequest.bids = [bidRequest.bids[0]]; - - bidRequest.bids[0].sizes = [['300', '250'], ['250', '300'], ['300', '600'], ['600', '300']]; - let result = spec.interpretResponse(serverResponse, bidRequest); - - assert.equal(result[0].width, 300); - assert.equal(result[0].height, 600); - }); - }); - }); - - beforeEach(function () { - config.setConfig({ currency: {} }); - - let sizes = [[250, 300], [300, 250], [300, 600], [600, 300]]; - let placementCode = ['div-01', 'div-02', 'div-03', 'div-04', 'div-05']; - let mediaTypes = [{video: {context: 'outstream'}, banner: {sizes: sizes[3]}}]; - let params = [{ mid: 1, url: 'some// there' }, {adxDomain: null, mid: 2, someVar: 'someValue', pt: 'gross'}, { adxDomain: null, mid: 3, pdom: 'home' }, {mid: 5, pt: 'net'}, {mid: 6, pt: 'gross'}]; - bids = [ - { - adUnitCode: placementCode[0], - auctionId: '7aefb970-2045', - bidId: '2a0cf4e', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[0], - adxDomain: 'newDomain', - tid: 45, - placementCode: placementCode[0], - sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-4ca1' - }, - { - adUnitCode: placementCode[1], - auctionId: '7aefb970-2045', - bidId: '2a0cf5b', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[1], - placementCode: placementCode[1], - sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-4iuy' - }, - { - adUnitCode: placementCode[2], - auctionId: '7aefb970-2045', - bidId: '2a0cf6n', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[2], - placementCode: placementCode[2], - sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], - transactionId: '5f33781f-9552-7ev3' - }, - { - adUnitCode: placementCode[3], - auctionId: '7aefb970-2045', - bidId: '2a0cf6n', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[2], - placementCode: placementCode[2], - sizes: [], - transactionId: '5f33781f-9552-7ev3' - }, - { - adUnitCode: placementCode[4], - auctionId: '7aefb970-2045', - bidId: '2a0cf6n', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[2], - placementCode: placementCode[2], - sizes: [], - transactionId: '5f33781f-9552-7ev3' - }, - { - adUnitCode: placementCode[4], - auctionId: '7aefb970-2045', - bidId: '2a0cf6n', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[3], - placementCode: placementCode[2], - sizes: [], - transactionId: '5f33781f-9552-7ev3' - }, - { - adUnitCode: placementCode[4], - auctionId: '7aefb970-2045', - bidId: '2a0cf6n', - bidder: 'adform', - bidderRequestId: '1ab8d9', - params: params[4], - placementCode: placementCode[2], - sizes: [], - mediaTypes: mediaTypes[0], - transactionId: '5f33781f-9552-7ev3' - } - ]; - serverResponse = { - body: [ - { - banner: '', - deal_id: '123abc', - height: 250, - response: 'banner', - width: 300, - win_bid: 13.9, - win_cur: 'EUR' - }, - { - banner: '', - deal_id: '123abc', - height: 300, - response: 'banner', - width: 250, - win_bid: 13.9, - win_cur: 'EUR' - }, - { - banner: '', - deal_id: '123abc', - height: 300, - response: 'banner', - width: 600, - win_bid: 10, - win_cur: 'EUR' - }, - { - deal_id: '123abc', - height: 300, - response: 'vast_content', - width: 600, - win_bid: 10, - win_cur: 'EUR', - vast_content: '' - }, - { - deal_id: '123abc', - height: 300, - response: 'vast_url', - width: 600, - win_bid: 10, - win_cur: 'EUR', - vast_url: 'vast://url' - } - ], - headers: {} - }; - bidRequest = { - bidder: 'adform', - bids: bids, - method: 'GET', - url: 'url', - netRevenue: 'net' - }; - }); -}); - -function parseUrl(url) { - const parts = url.split('/'); - const query = parts.pop().split('&'); - return { - path: parts.join('/'), - items: query - .filter((i) => !~i.indexOf('=')) - .map((i) => atob(decodeURIComponent(i)) - .split('&') - .reduce(toObject, {})), - query: query - .filter((i) => ~i.indexOf('=')) - .map((i) => i.replace('?', '')) - .reduce(toObject, {}) - }; -} - -function toObject(cache, string) { - const keyValue = string.split('='); - cache[keyValue[0]] = keyValue[1]; - return cache; -} diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js deleted file mode 100644 index 927e7910723..00000000000 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ /dev/null @@ -1,407 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adgenerationBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {NATIVE} from 'src/mediaTypes.js'; -import {config} from 'src/config.js'; -import prebid from '../../../package.json'; - -describe('AdgenerationAdapter', function () { - const adapter = newBidder(spec); - const ENDPOINT = ['https://api-test.scaleout.jp/adsv/v1', 'https://d.socdm.com/adsv/v1']; - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - const bid = { - 'bidder': 'adg', - 'params': { - id: '58278', // banner - } - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { // banner - bidder: 'adg', - params: { - id: '58278', - currency: 'JPY', - }, - adUnitCode: 'adunit-code', - sizes: [[300, 250], [320, 100]], - bidId: '2f6ac468a9c15e', - bidderRequestId: '14a9f773e30243', - auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', - transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' - }, - { // native - bidder: 'adg', - params: { - id: '58278', - currency: 'JPY', - }, - mediaTypes: { - native: { - image: { - required: true - }, - title: { - required: true, - len: 80 - }, - sponsoredBy: { - required: true - }, - clickUrl: { - required: true - }, - body: { - required: true - }, - icon: { - required: true - } - }, - }, - adUnitCode: 'adunit-code', - sizes: [[1, 1]], - bidId: '2f6ac468a9c15e', - bidderRequestId: '14a9f773e30243', - auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', - transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' - } - ]; - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - 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.0.1&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.0.1&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.0.1&tp=https%3A%2F%2Fexample.com' - }; - it('sends bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.url).to.equal(ENDPOINT[1]); - expect(request.method).to.equal('GET'); - }); - - it('sends bid request to debug ENDPOINT via GET', function () { - bidRequests[0].params.debug = true; - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.url).to.equal(ENDPOINT[0]); - expect(request.method).to.equal('GET'); - }); - - it('should attache params to the banner request', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data).to.equal(data.banner); - }); - - it('should attache params to the native request', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[1]; - expect(request.data).to.equal(data.native); - }); - it('allows setConfig to set bidder currency for JPY', function () { - config.setConfig({ - currency: { - adServerCurrency: 'JPY' - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data).to.equal(data.banner); - config.resetConfig(); - }); - it('allows setConfig to set bidder currency for USD', function () { - config.setConfig({ - currency: { - adServerCurrency: 'USD' - } - }); - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data).to.equal(data.bannerUSD); - config.resetConfig(); - }); - }); - describe('interpretResponse', function () { - const bidRequests = { - banner: { - bidRequest: { - 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' - }, - }, - native: { - bidRequest: { - bidder: 'adg', - params: { - id: '58278', // banner - }, - mediaTypes: { - native: { - image: { - required: true - }, - title: { - required: true, - len: 80 - }, - sponsoredBy: { - required: true - }, - clickUrl: { - required: true - }, - body: { - required: true - }, - icon: { - required: true - } - } - }, - adUnitCode: 'adunit-code', - sizes: [[1, 1]], - bidId: '2f6ac468a9c15e', - bidderRequestId: '14a9f773e30243', - auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', - transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' - }, - }, - }; - - const serverResponse = { - noAd: { - results: [], - }, - banner: { - ad: '
', - beacon: '', - cpm: 36.0008, - displaytype: '1', - ids: {}, - w: 320, - h: 100, - location_params: null, - locationid: '58279', - rotation: '0', - scheduleid: '512603', - sdktype: '0', - creativeid: '1k2kv35vsa5r', - dealid: 'fd5sa5fa7f', - ttl: 1000, - results: [ - {ad: '
'}, - ] - }, - native: { - ad: '↵ ↵ ↵ ↵ ↵
↵ ', - beacon: '', - cpm: 36.0008, - displaytype: '1', - ids: {}, - location_params: null, - locationid: '58279', - native_ad: { - assets: [ - { - data: { - label: 'accompanying_text', - value: 'AD' - }, - id: 501 - }, - { - data: { - label: 'optout_url', - value: 'https://supership.jp/optout/#' - }, - id: 502 - }, - { - data: { - ext: { - black_back: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_white.png', - }, - label: 'information_icon_url', - value: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_gray.png', - id: 503 - } - }, - { - id: 1, - required: 1, - title: {text: 'Title'} - }, - { - id: 2, - img: { - h: 250, - url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', - w: 300 - }, - required: 1 - }, - { - id: 3, - img: { - h: 300, - url: 'https://placehold.jp/300x300.png', - w: 300 - }, - required: 1 - }, - { - data: {value: 'Description'}, - id: 5, - required: 0 - }, - { - data: {value: 'CTA'}, - id: 6, - required: 0 - }, - { - data: {value: 'Sponsored'}, - id: 4, - required: 0 - } - ], - imptrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'], - link: { - clicktrackers: [ - 'https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif' - ], - url: 'https://supership.jp' - }, - }, - results: [ - {ad: 'Creative<\/body>'} - ], - rotation: '0', - scheduleid: '512603', - sdktype: '0', - creativeid: '1k2kv35vsa5r', - dealid: 'fd5sa5fa7f', - ttl: 1000 - } - }; - - const bidResponses = { - banner: { - requestId: '2f6ac468a9c15e', - cpm: 36.0008, - width: 320, - height: 100, - creativeId: '1k2kv35vsa5r', - dealId: 'fd5sa5fa7f', - currency: 'JPY', - netRevenue: true, - ttl: 1000, - ad: '
', - }, - native: { - requestId: '2f6ac468a9c15e', - cpm: 36.0008, - width: 1, - height: 1, - creativeId: '1k2kv35vsa5r', - dealId: 'fd5sa5fa7f', - currency: 'JPY', - netRevenue: true, - ttl: 1000, - ad: '↵
', - native: { - title: 'Title', - image: { - url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', - height: 250, - width: 300 - }, - icon: { - url: 'https://placehold.jp/300x300.png', - height: 300, - width: 300 - }, - sponsoredBy: 'Sponsored', - body: 'Description', - cta: 'CTA', - privacyLink: 'https://supership.jp/optout/#', - clickUrl: 'https://supership.jp', - clickTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif'], - impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] - }, - mediaType: NATIVE - } - }; - - it('no bid responses', function () { - const result = spec.interpretResponse({body: serverResponse.noAd}, bidRequests.banner); - expect(result.length).to.equal(0); - }); - - it('handles banner responses', function () { - const result = spec.interpretResponse({body: serverResponse.banner}, bidRequests.banner)[0]; - expect(result.requestId).to.equal(bidResponses.banner.requestId); - expect(result.width).to.equal(bidResponses.banner.width); - expect(result.height).to.equal(bidResponses.banner.height); - expect(result.creativeId).to.equal(bidResponses.banner.creativeId); - expect(result.dealId).to.equal(bidResponses.banner.dealId); - expect(result.currency).to.equal(bidResponses.banner.currency); - expect(result.netRevenue).to.equal(bidResponses.banner.netRevenue); - expect(result.ttl).to.equal(bidResponses.banner.ttl); - expect(result.ad).to.equal(bidResponses.banner.ad); - }); - - it('handles native responses', function () { - const result = spec.interpretResponse({body: serverResponse.native}, bidRequests.native)[0]; - expect(result.requestId).to.equal(bidResponses.native.requestId); - expect(result.width).to.equal(bidResponses.native.width); - expect(result.height).to.equal(bidResponses.native.height); - expect(result.creativeId).to.equal(bidResponses.native.creativeId); - expect(result.dealId).to.equal(bidResponses.native.dealId); - expect(result.currency).to.equal(bidResponses.native.currency); - expect(result.netRevenue).to.equal(bidResponses.native.netRevenue); - expect(result.ttl).to.equal(bidResponses.native.ttl); - expect(result.native.title).to.equal(bidResponses.native.native.title); - expect(result.native.image.url).to.equal(bidResponses.native.native.image.url); - expect(result.native.image.height).to.equal(bidResponses.native.native.image.height); - expect(result.native.image.width).to.equal(bidResponses.native.native.image.width); - expect(result.native.icon.url).to.equal(bidResponses.native.native.icon.url); - expect(result.native.icon.width).to.equal(bidResponses.native.native.icon.width); - expect(result.native.icon.height).to.equal(bidResponses.native.native.icon.height); - expect(result.native.sponsoredBy).to.equal(bidResponses.native.native.sponsoredBy); - expect(result.native.body).to.equal(bidResponses.native.native.body); - expect(result.native.cta).to.equal(bidResponses.native.native.cta); - expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.native.native.privacyLink); - expect(result.native.clickUrl).to.equal(bidResponses.native.native.clickUrl); - expect(result.native.impressionTrackers[0]).to.equal(bidResponses.native.native.impressionTrackers[0]); - expect(result.native.clickTrackers[0]).to.equal(bidResponses.native.native.clickTrackers[0]); - expect(result.mediaType).to.equal(bidResponses.native.mediaType); - }); - }); -}); diff --git a/test/spec/modules/adglareBidAdapter_spec.js b/test/spec/modules/adglareBidAdapter_spec.js deleted file mode 100644 index d0dbe891f9d..00000000000 --- a/test/spec/modules/adglareBidAdapter_spec.js +++ /dev/null @@ -1,138 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adglareBidAdapter.js'; - -describe('AdGlare Adapter Tests', function () { - let bidRequests; - - beforeEach(function () { - bidRequests = [ - { - bidder: 'adglare', - params: { - domain: 'try.engine.adglare.net', - zID: '475579334', - type: 'banner' - }, - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: '23acc48ad47af5', - auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' - } - ]; - }); - - describe('implementation', function () { - describe('for requests', function () { - it('should accept valid bid', function () { - let validBid = { - bidder: 'adglare', - params: { - domain: 'try.engine.adglare.net', - zID: '475579334', - type: 'banner' - } - }, - isValid = spec.isBidRequestValid(validBid); - - expect(isValid).to.equal(true); - }); - - it('should reject invalid bid', function () { - let invalidBid = { - bidder: 'adglare', - params: { - domain: 'somedomain.com', - zID: 'not an integer', - type: 'unsupported' - } - }, - isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should build a valid endpoint URL', function () { - let bidRequests = [ - { - bidder: 'adglare', - params: { - domain: 'try.engine.adglare.net', - zID: '475579334', - type: 'banner' - }, - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: '23acc48ad47af5', - auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' - } - ], - bidderRequest = { - bidderCode: 'adglare', - auctionID: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - auctionStart: 1581497568252, - timeout: 5000, - refererInfo: { - referer: 'https://www.somedomain.com', - reachedTop: true, - numFrames: 0 - }, - start: 1581497568254 - }, - requests = spec.buildRequests(bidRequests, bidderRequest), - requestURL = requests[0].url; - - expect(requestURL).to.have.string('https://try.engine.adglare.net/?475579334'); - }); - }); - - describe('bid responses', function () { - it('should return complete bid response', function () { - let serverResponse = { - body: { - status: 'OK', - zID: 475579334, - cID: 501658124, - crID: 442123173, - cpm: 1.5, - ttl: 3600, - currency: 'USD', - width: 300, - height: 250, - adhtml: 'I am an ad.' - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(1); - expect(bids[0].bidderCode).to.equal('adglare'); - expect(bids[0].cpm).to.equal(1.5); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(250); - expect(bids[0].currency).to.equal('USD'); - expect(bids[0].netRevenue).to.equal(true); - }); - - it('should return empty bid response', function () { - let serverResponse = { - body: { - status: 'NOADS' - } - }, - bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); - - expect(bids).to.be.lengthOf(0); - }); - }); - }); -}); diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js deleted file mode 100644 index ab4df84c093..00000000000 --- a/test/spec/modules/adhashBidAdapter_spec.js +++ /dev/null @@ -1,155 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/adhashBidAdapter.js'; - -describe('adhashBidAdapter', function () { - describe('isBidRequestValid', function () { - const validBid = { - bidder: 'adhash', - params: { - publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb', - platformURL: 'https://adhash.org/p/struma/' - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: 'adunit-code', - sizes: [[300, 250]], - bidId: '12345678901234', - bidderRequestId: '98765432109876', - auctionId: '01234567891234', - }; - - it('should return true when all mandatory parameters are there', function () { - expect(spec.isBidRequestValid(validBid)).to.equal(true); - }); - - it('should return false when there are no params', function () { - const bid = { ...validBid }; - delete bid.params; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when unsupported media type is requested', function () { - const bid = { ...validBid }; - bid.mediaTypes = { native: { sizes: [[300, 250]] } }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when publisherId is not a string', function () { - const bid = { ...validBid }; - bid.params.publisherId = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when publisherId is not valid', function () { - const bid = { ...validBid }; - bid.params.publisherId = 'short string'; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when publisherId is not a string', function () { - const bid = { ...validBid }; - bid.params.platformURL = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when publisherId is not valid', function () { - const bid = { ...validBid }; - bid.params.platformURL = 'https://'; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequest = { - params: { - publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb' - }, - sizes: [[300, 250]], - adUnitCode: 'adUnitCode' - }; - it('should build the request correctly', function () { - const result = spec.buildRequests( - [ bidRequest ], - { gdprConsent: true, refererInfo: { referer: 'http://example.com/' } } - ); - expect(result.length).to.equal(1); - expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); - expect(result[0].bidRequest).to.equal(bidRequest); - expect(result[0].data).to.have.property('timezone'); - expect(result[0].data).to.have.property('location'); - expect(result[0].data).to.have.property('publisherId'); - expect(result[0].data).to.have.property('size'); - expect(result[0].data).to.have.property('navigator'); - expect(result[0].data).to.have.property('creatives'); - expect(result[0].data).to.have.property('blockedCreatives'); - expect(result[0].data).to.have.property('currentTimestamp'); - expect(result[0].data).to.have.property('recentAds'); - }); - it('should build the request correctly without referer', function () { - const result = spec.buildRequests([ bidRequest ], { gdprConsent: true }); - expect(result.length).to.equal(1); - expect(result[0].method).to.equal('POST'); - expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true'); - expect(result[0].bidRequest).to.equal(bidRequest); - expect(result[0].data).to.have.property('timezone'); - expect(result[0].data).to.have.property('location'); - expect(result[0].data).to.have.property('publisherId'); - expect(result[0].data).to.have.property('size'); - expect(result[0].data).to.have.property('navigator'); - expect(result[0].data).to.have.property('creatives'); - expect(result[0].data).to.have.property('blockedCreatives'); - expect(result[0].data).to.have.property('currentTimestamp'); - expect(result[0].data).to.have.property('recentAds'); - }); - }); - - describe('interpretResponse', function () { - const request = { - data: { some: 'data' }, - bidRequest: { - bidId: '12345678901234', - adUnitCode: 'adunit-code', - sizes: [[300, 250]], - params: { - platformURL: 'https://adhash.org/p/struma/' - } - } - }; - - it('should interpret the response correctly', function () { - const serverResponse = { - body: { - creatives: [{ - costEUR: 1.234 - }] - } - }; - const result = spec.interpretResponse(serverResponse, request); - expect(result.length).to.equal(1); - expect(result[0].requestId).to.equal('12345678901234'); - expect(result[0].cpm).to.equal(1.234); - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal('adunit-code'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].currency).to.equal('EUR'); - expect(result[0].ttl).to.equal(60); - }); - - it('should return empty array when there are no creatives returned', function () { - expect(spec.interpretResponse({body: {creatives: []}}, request).length).to.equal(0); - }); - - it('should return empty array when there is no creatives key in the response', function () { - expect(spec.interpretResponse({body: {}}, request).length).to.equal(0); - }); - - it('should return empty array when something is not right', function () { - expect(spec.interpretResponse(null, request).length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js deleted file mode 100644 index 526102c51fe..00000000000 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ /dev/null @@ -1,509 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adheseBidAdapter.js'; - -const BID_ID = 456; -const TTL = 360; -const NET_REVENUE = true; - -let minimalBid = function() { - return { - 'bidId': BID_ID, - 'bidder': 'adhese', - 'params': { - account: 'demo', - location: '_main_page_', - format: 'leaderboard' - } - } -}; - -let bidWithParams = function(data) { - let bid = minimalBid(); - bid.params.data = data; - return bid; -}; - -describe('AdheseAdapter', function () { - describe('getUserSyncs', function () { - const serverResponses = [{ - account: 'demo' - }]; - const gdprConsent = { - gdprApplies: true, - consentString: 'CONSENT_STRING' - }; - it('should return empty when iframe disallowed', function () { - expect(spec.getUserSyncs({ iframeEnabled: false }, serverResponses, gdprConsent)).to.be.empty; - }); - it('should return empty when no serverResponses present', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent)).to.be.empty; - }); - it('should return empty when no account info present in the response', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, [{}], gdprConsent)).to.be.empty; - }); - it('should return usersync url when iframe allowed', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses, gdprConsent)).to.deep.equal([{ type: 'iframe', url: 'https://user-sync.adhese.com/iframe/user_sync.html?account=demo&gdpr=1&consentString=CONSENT_STRING' }]); - }); - }); - - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(minimalBid())).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, minimalBid()); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'CONSENT_STRING' - }, - refererInfo: { - referer: 'http://prebid.org/dev-docs/subjects?_d=1' - } - }; - - it('should include requested slots', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(JSON.parse(req.data).slots[0].slotname).to.equal('_main_page_-leaderboard'); - }); - - it('should include all extra bid params', function () { - let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }) ], bidderRequest); - - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }); - }); - - it('should assign bid params per slot', function () { - let req = spec.buildRequests([ bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' }) ], bidderRequest); - - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }).and.not.to.deep.include({ 'ci': [ 'gent' ] }); - expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ag': [ '25' ] }).and.to.deep.include({ 'ci': [ 'gent' ] }); - }); - - it('should split multiple target values', function () { - let req = spec.buildRequests([ bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' }) ], bidderRequest); - - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ci': [ 'london' ] }); - expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ci': [ 'gent' ] }); - }); - - it('should filter out empty params', function () { - let req = spec.buildRequests([ bidWithParams({ 'aa': [], 'bb': null, 'cc': '', 'dd': [ '', '' ], 'ee': [ 0, 1, null ], 'ff': 0, 'gg': [ 'x', 'y', '' ] }) ], bidderRequest); - - let params = JSON.parse(req.data).slots[0].parameters; - expect(params).to.not.have.any.keys('aa', 'bb', 'cc', 'dd'); - expect(params).to.deep.include({ 'ee': [ 0, 1 ], 'ff': [ 0 ], 'gg': [ 'x', 'y' ] }); - }); - - it('should include gdpr consent param', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': [ 'CONSENT_STRING' ] }); - }); - - it('should include referer param in base64url format', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(JSON.parse(req.data).parameters).to.deep.include({ 'xf': [ 'aHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ' ] }); - }); - - it('should include eids', function () { - let bid = minimalBid(); - bid.userIdAsEids = [{ source: 'id5-sync.com', uids: [{ id: 'ID5@59sigaS-...' }] }]; - - let req = spec.buildRequests([ bid ], bidderRequest); - - expect(JSON.parse(req.data).user.ext.eids).to.deep.equal(bid.userIdAsEids); - }); - - it('should not include eids field when userid module disabled', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(JSON.parse(req.data)).to.not.have.key('eids'); - }); - - it('should request vast content as url', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(JSON.parse(req.data).vastContentAsUrl).to.equal(true); - }); - - it('should include bids', function () { - let bid = minimalBid(); - let req = spec.buildRequests([ bid ], bidderRequest); - - expect(req.bids).to.deep.equal([ bid ]); - }); - - it('should make a POST request', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(req.method).to.equal('POST'); - }); - - it('should request the json endpoint', function () { - let req = spec.buildRequests([ minimalBid() ], bidderRequest); - - expect(req.url).to.equal('https://ads-demo.adhese.com/json'); - }); - }); - - describe('interpretResponse', () => { - let bidRequest = { - bids: [ minimalBid() ] - }; - - it('should get correct ssp banner response', () => { - let sspBannerResponse = { - body: [ - { - origin: 'APPNEXUS', - originInstance: '', - ext: 'js', - slotID: '10', - slotName: '_main_page_-leaderboard', - adType: 'leaderboard', - originData: { - seatbid: [{ - bid: [{ - crid: '60613369', - dealid: null - }], - seat: '958' - }] - }, - width: '728', - height: '90', - body: '
', - tracker: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', - impressionCounter: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', - extension: {'prebid': {'cpm': {'amount': '1.000000', 'currency': 'USD'}}, mediaType: 'banner'} - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - ad: '
', - cpm: 1, - currency: 'USD', - creativeId: '60613369', - dealId: '', - width: 728, - height: 90, - mediaType: 'banner', - netRevenue: NET_REVENUE, - ttl: TTL, - adhese: { - origin: 'APPNEXUS', - originInstance: '', - originData: { - adType: 'leaderboard', - seatbid: [ - { - bid: [ { crid: '60613369', dealid: null } ], - seat: '958' - } - ], - slotId: '10', - slotName: '_main_page_-leaderboard' - } - } - }]; - expect(spec.interpretResponse(sspBannerResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should get correct ssp video response', () => { - let sspVideoResponse = { - body: [ - { - origin: 'RUBICON', - ext: 'js', - slotName: '_main_page_-leaderboard', - adType: 'leaderboard', - width: '640', - height: '350', - body: '', - extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - vastXml: '', - cpm: 2.1, - currency: 'USD', - creativeId: 'RUBICON', - dealId: '', - width: 640, - height: 350, - mediaType: 'video', - netRevenue: NET_REVENUE, - ttl: TTL, - adhese: { - origin: 'RUBICON', - originInstance: '', - originData: {} - } - }]; - expect(spec.interpretResponse(sspVideoResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should get correct ssp cache video response', () => { - let sspCachedVideoResponse = { - body: [ - { - origin: 'RUBICON', - ext: 'js', - slotName: '_main_page_-leaderboard', - adType: 'leaderboard', - width: '640', - height: '350', - cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', - extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', - cpm: 2.1, - currency: 'USD', - creativeId: 'RUBICON', - dealId: '', - width: 640, - height: 350, - mediaType: 'video', - netRevenue: NET_REVENUE, - ttl: TTL, - adhese: { - origin: 'RUBICON', - originInstance: '', - originData: {} - } - }]; - expect(spec.interpretResponse(sspCachedVideoResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should get correct Adhese banner response', () => { - const adheseBannerResponse = { - body: [ - { - adType: 'largeleaderboard', // it can differ from the requested slot - adFormat: 'largeleaderboard', - timeStamp: '1544009030000', - orderId: '22051', - adspaceId: '162363', - body: '', - tag: '', - tracker: 'https://hosts-demo.adhese.com/track/tracker', - altText: '', - height: '150', - width: '840', - tagUrl: 'https://pool-demo.adhese.com/pool/lib/90511.js', - libId: '90511', - id: '742898', - advertiserId: '2081', - ext: 'js', - url: 'https://hosts-demo.adhese.com/raylene/url', - clickTag: 'https://hosts-demo.adhese.com/raylene/clickTag', - poolPath: 'https://hosts-demo.adhese.com/pool/lib/', - orderName: 'Luminus boiler comodity-Pareto -201812', - creativeName: 'nl_demo _network_ron_dlbd_840x150_fix_dir_asv_std_dis_brd_nrt_na_red', - slotName: '_main_page_-leaderboard', - slotID: '29306', - impressionCounter: 'https://hosts-demo.adhese.com/track/742898', - origin: 'JERLICIA', - originData: {}, - auctionable: true, - extension: { - prebid: { - cpm: { - amount: '5.96', - currency: 'USD' - } - }, - mediaType: 'banner' - } - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - ad: '', - adhese: { - origin: '', - originInstance: '', - originData: { - adFormat: 'largeleaderboard', - adId: '742898', - adType: 'largeleaderboard', - adspaceId: '162363', - libId: '90511', - orderProperty: undefined, - priority: undefined, - viewableImpressionCounter: undefined, - slotId: '29306', - slotName: '_main_page_-leaderboard', - advertiserId: '2081' - } - }, - cpm: 5.96, - currency: 'USD', - creativeId: '742898', - dealId: '22051', - width: 840, - height: 150, - mediaType: 'banner', - netRevenue: NET_REVENUE, - ttl: TTL, - }]; - expect(spec.interpretResponse(adheseBannerResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should get correct Adhese video response', () => { - const adheseVideoResponse = { - body: [ - { - adType: 'preroll', - adFormat: '', - orderId: '22248', - adspaceId: '164196', - body: '', - height: '360', - width: '640', - tag: "", - libId: '89860', - id: '742470', - advertiserId: '2263', - ext: 'advar', - orderName: 'Smartphoto EOY-20181112', - creativeName: 'PREROLL', - slotName: '_main_page_-leaderboard', - slotID: '41711', - impressionCounter: 'https://hosts-demo.adhese.com/track/742898', - origin: 'JERLICIA', - originData: {}, - auctionable: true, - extension: { - mediaType: 'video' - } - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - vastXml: '', - adhese: { - origin: '', - originInstance: '', - originData: { - adFormat: '', - adId: '742470', - adType: 'preroll', - adspaceId: '164196', - libId: '89860', - orderProperty: undefined, - priority: undefined, - viewableImpressionCounter: undefined, - slotId: '41711', - slotName: '_main_page_-leaderboard', - advertiserId: '2263', - } - }, - cpm: 0, - currency: 'USD', - creativeId: '742470', - dealId: '22248', - width: 640, - height: 360, - mediaType: 'video', - netRevenue: NET_REVENUE, - ttl: TTL, - }]; - expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should get correct Adhese cached video response', () => { - const adheseVideoResponse = { - body: [ - { - adType: 'preroll', - adFormat: '', - orderId: '22248', - adspaceId: '164196', - body: '', - height: '360', - width: '640', - extension: { - mediaType: 'video' - }, - cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', - libId: '89860', - id: '742470', - advertiserId: '2263', - ext: 'advar', - orderName: 'Smartphoto EOY-20181112', - creativeName: 'PREROLL', - slotName: '_main_page_-leaderboard', - slotID: '41711', - impressionCounter: 'https://hosts-demo.adhese.com/track/742898', - origin: 'JERLICIA', - originData: {}, - auctionable: true - } - ] - }; - - let expectedResponse = [{ - requestId: BID_ID, - vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', - adhese: { - origin: '', - originInstance: '', - originData: { - adFormat: '', - adId: '742470', - adType: 'preroll', - adspaceId: '164196', - libId: '89860', - orderProperty: undefined, - priority: undefined, - viewableImpressionCounter: undefined, - slotId: '41711', - slotName: '_main_page_-leaderboard', - advertiserId: '2263', - } - }, - cpm: 0, - currency: 'USD', - creativeId: '742470', - dealId: '22248', - width: 640, - height: 360, - mediaType: 'video', - netRevenue: NET_REVENUE, - ttl: TTL, - }]; - expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); - }); - - it('should return no bids for empty adserver response', () => { - let adserverResponse = { body: [] }; - expect(spec.interpretResponse(adserverResponse, bidRequest)).to.be.empty; - }); - }); -}); diff --git a/test/spec/modules/adliveBidAdapter_spec.js b/test/spec/modules/adliveBidAdapter_spec.js deleted file mode 100644 index ddf8f82f20f..00000000000 --- a/test/spec/modules/adliveBidAdapter_spec.js +++ /dev/null @@ -1,78 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/adliveBidAdapter.js'; - -describe('adliveBidAdapterTests', function() { - let bidRequestData = { - bids: [ - { - bidId: 'transaction_1234', - bidder: 'adlive', - params: { - hashes: ['1e100887dd614b0909bf6c49ba7f69fdd1360437'] - }, - sizes: [[300, 250]] - } - ] - }; - let request = []; - - it('validate_pub_params', function() { - expect( - spec.isBidRequestValid({ - bidder: 'adlive', - params: { - hashes: ['1e100887dd614b0909bf6c49ba7f69fdd1360437'] - } - }) - ).to.equal(true); - }); - - it('validate_generated_params', function() { - request = spec.buildRequests(bidRequestData.bids); - let req_data = JSON.parse(request[0].data); - - expect(req_data.transaction_id).to.equal('transaction_1234'); - }); - - it('validate_response_params', function() { - let serverResponse = { - body: [ - { - hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', - content: 'Ad html', - price: 1.12, - size: [300, 250], - is_passback: 0 - } - ] - }; - - let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); - expect(bids).to.have.lengthOf(1); - - let bid = bids[0]; - - expect(bid.creativeId).to.equal('1e100887dd614b0909bf6c49ba7f69fdd1360437'); - expect(bid.ad).to.equal('Ad html'); - expect(bid.cpm).to.equal(1.12); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.currency).to.equal('USD'); - }); - - it('validate_response_params_with passback', function() { - let serverResponse = { - body: [ - { - hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', - content: 'Ad html passback', - size: [300, 250], - is_passback: 1 - } - ] - }; - let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); - - expect(bids).to.have.lengthOf(0); - }); -}); diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js deleted file mode 100644 index f3212dec2f5..00000000000 --- a/test/spec/modules/admanBidAdapter_spec.js +++ /dev/null @@ -1,231 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/admanBidAdapter.js'; - -describe('AdmanMediaBidAdapter', function () { - let bid = { - bidId: '23fhj33i987f', - bidder: 'adman', - params: { - placementId: 0, - traffic: 'banner' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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]); - 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://pub.admanmedia.com/?c=o&m=multi'); - }); - 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'); - 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'); - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal('banner'); - }); - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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 () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://pub.admanmedia.com/?c=o&m=sync'); - }); - }); -}); diff --git a/test/spec/modules/admediaBidAdapter_spec.js b/test/spec/modules/admediaBidAdapter_spec.js deleted file mode 100644 index 5dc7b9a02a8..00000000000 --- a/test/spec/modules/admediaBidAdapter_spec.js +++ /dev/null @@ -1,138 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/admediaBidAdapter.js'; - -describe('admediaAdapterTests', function () { - describe('bidRequestValidity', function () { - it('bidRequest with aid', function () { - expect(spec.isBidRequestValid({ - bidder: 'admedia', - params: { - aid: 86858, - } - })).to.equal(true); - }); - - it('bidRequest without aid', function () { - expect(spec.isBidRequestValid({ - bidder: 'a4g', - params: { - key: 86858 - } - })).to.equal(false); - }); - }); - - describe('bidRequest', function () { - const validBidRequests = [{ - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', - 'bidId': '2758de47c84ab58', - 'bidRequestsCount': 1, - 'bidder': 'admedia', - 'bidderRequestId': '1033407c6af0c7', - 'params': { - 'aid': 86858, - }, - 'sizes': [[300, 250], [300, 600]], - 'transactionId': '5851b2cf-ee2d-4022-abd2-d581ef01604e' - }, { - 'adUnitCode': 'div-gpt-ad-1460505748561-1', - 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', - 'bidId': '3d2aaa400371fa', - 'bidRequestsCount': 1, - 'bidder': 'admedia', - 'bidderRequestId': '1033407c6af0c7', - 'params': { - 'aid': 84977, - }, - 'sizes': [[728, 90]], - 'transactionId': 'f8b5247e-7715-4e60-9d51-33153e78c190' - }]; - - const bidderRequest = { - 'auctionId': 'e3010a3c-5b95-4475-9ba2-1b004c737c30', - 'bidderCode': 'admedia', - 'bidderRequestId': '1033407c6af0c7', - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'https://test.com/index.html?pbjs_debug=true' - } - - }; - - const request = spec.buildRequests(validBidRequests, bidderRequest); - - it('bidRequest method', function () { - expect(request.method).to.equal('POST'); - }); - - it('bidRequest url', function () { - expect(request.url).to.equal('https://prebid.admedia.com/bidder/'); - }); - - it('bidRequest data', function () { - const data = JSON.parse(request.data); - expect(decodeURIComponent(data.referer)).to.be.eql(bidderRequest.refererInfo.referer); - expect(data.tags).to.be.an('array'); - expect(data.tags[0].aid).to.be.eql(validBidRequests[0].params.aid); - expect(data.tags[0].id).to.be.eql(validBidRequests[0].bidId); - expect(data.tags[0].sizes).to.be.eql(validBidRequests[0].sizes); - expect(data.tags[1].aid).to.be.eql(validBidRequests[1].params.aid); - expect(data.tags[1].id).to.be.eql(validBidRequests[1].bidId); - expect(data.tags[1].sizes).to.be.eql(validBidRequests[1].sizes); - }); - }); - - describe('interpretResponse', function () { - const serverResponse = { - body: { - tags: [ - { - ad: '', - cpm: 0.9, - height: 250, - id: '5582180864bc41', - width: 300, - }, - { - error: 'Error message', - id: '6dc6ee4e157749' - }, - { - ad: '', - cpm: 0, - height: 728, - id: '5762180864bc41', - width: 90, - } - ] - }, - headers: {} - }; - - const bidRequest = {}; - - const result = spec.interpretResponse(serverResponse, bidRequest); - - it('Should return an empty array if empty or no tags in response', function () { - expect(spec.interpretResponse({body: ''}, {}).length).to.equal(0); - }); - - it('Should have only one bid', function () { - expect(result.length).to.equal(1); - }); - - it('Should have required keys', function () { - expect(result[0].requestId).to.be.eql(serverResponse.body.tags[0].id); - expect(result[0].cpm).to.be.eql(serverResponse.body.tags[0].cpm); - expect(result[0].width).to.be.eql(serverResponse.body.tags[0].width); - expect(result[0].height).to.be.eql(serverResponse.body.tags[0].height); - expect(result[0].creativeId).to.be.eql(serverResponse.body.tags[0].id); - expect(result[0].dealId).to.be.eql(serverResponse.body.tags[0].id); - expect(result[0].netRevenue).to.be.eql(true); - expect(result[0].ttl).to.be.eql(120); - expect(result[0].ad).to.be.eql(serverResponse.body.tags[0].ad); - }) - }); -}); diff --git a/test/spec/modules/adpartnerBidAdapter_spec.js b/test/spec/modules/adpartnerBidAdapter_spec.js deleted file mode 100644 index d30ef7ebf71..00000000000 --- a/test/spec/modules/adpartnerBidAdapter_spec.js +++ /dev/null @@ -1,234 +0,0 @@ -import {expect} from 'chai'; -import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/adpartnerBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -const BIDDER_CODE = 'adpartner'; - -describe('AdpartnerAdapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.be.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - let validRequest = { - 'params': { - 'unitId': 123 - } - }; - expect(spec.isBidRequestValid(validRequest)).to.equal(true); - }); - - it('should return true when required params is srting', function () { - let validRequest = { - 'params': { - 'unitId': '456' - } - }; - expect(spec.isBidRequestValid(validRequest)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let validRequest = { - 'params': { - 'unknownId': 123 - } - }; - expect(spec.isBidRequestValid(validRequest)).to.equal(false); - }); - - it('should return false when required params is 0', function () { - let validRequest = { - 'params': { - 'unitId': 0 - } - }; - expect(spec.isBidRequestValid(validRequest)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let validEndpoint = ENDPOINT_PROTOCOL + '://' + ENDPOINT_DOMAIN + ENDPOINT_PATH + '?tag=123,456&sizes=300x250|300x600,728x90&referer=https%3A%2F%2Ftest.domain'; - - let validRequest = [ - { - 'bidder': BIDDER_CODE, - 'params': { - 'unitId': 123 - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e' - }, - { - 'bidder': BIDDER_CODE, - 'params': { - 'unitId': '456' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '22aidtbx5eabd9' - } - ]; - - let bidderRequest = { - refererInfo: { - referer: 'https://test.domain' - } - }; - - it('bidRequest HTTP method', function () { - const request = spec.buildRequests(validRequest, bidderRequest); - expect(request.method).to.equal('POST'); - }); - - it('bidRequest url', function () { - const request = spec.buildRequests(validRequest, bidderRequest); - expect(request.url).to.equal(validEndpoint); - }); - - it('bidRequest data', function () { - const request = spec.buildRequests(validRequest, bidderRequest); - const payload = JSON.parse(request.data); - expect(payload[0].unitId).to.equal(123); - expect(payload[0].sizes).to.deep.equal([[300, 250], [300, 600]]); - expect(payload[0].bidId).to.equal('30b31c1838de1e'); - expect(payload[1].unitId).to.equal(456); - expect(payload[1].sizes).to.deep.equal([[728, 90]]); - expect(payload[1].bidId).to.equal('22aidtbx5eabd9'); - }); - }); - - describe('joinSizesToString', function () { - it('success convert sizes list to string', function () { - const sizesStr = spec.joinSizesToString([[300, 250], [300, 600]]); - expect(sizesStr).to.equal('300x250|300x600'); - }); - }); - - describe('interpretResponse', function () { - const bidRequest = { - 'method': 'POST', - 'url': ENDPOINT_PROTOCOL + '://' + ENDPOINT_DOMAIN + ENDPOINT_PATH + '?tag=123,456&code=adunit-code-1,adunit-code-2&bid=30b31c1838de1e,22aidtbx5eabd9&sizes=300x250|300x600,728x90&referer=https%3A%2F%2Ftest.domain', - 'data': '[{"unitId": 13144370,"adUnitCode": "div-gpt-ad-1460505748561-0","sizes": [[300, 250], [300, 600]],"bidId": "2bdcb0b203c17d","referer": "https://test.domain/index.html"},{"unitId": 13144370,"adUnitCode":"div-gpt-ad-1460505748561-1","sizes": [[768, 90]],"bidId": "3dc6b8084f91a8","referer": "https://test.domain/index.html"}]' - }; - - const bidResponse = { - body: { - 'div-gpt-ad-1460505748561-0': - { - 'ad': '
ad
', - 'width': 300, - 'height': 250, - 'creativeId': '8:123456', - 'syncs': [ - {'type': 'image', 'url': 'https://test.domain/tracker_1.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_2.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_3.gif'} - ], - 'winNotification': [ - { - 'method': 'POST', - 'path': '/hb/bid_won?test=1', - 'data': { - 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'} - ], - 'unit_id': 1234, - 'site_id': 123 - } - } - ], - 'cpm': 0.01, - 'currency': 'USD', - 'netRevenue': true - } - }, - headers: {} - }; - - it('result is correct', function () { - const result = spec.interpretResponse(bidResponse, bidRequest); - expect(result[0].requestId).to.equal('2bdcb0b203c17d'); - expect(result[0].cpm).to.equal(0.01); - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal('8:123456'); - expect(result[0].currency).to.equal('USD'); - expect(result[0].ttl).to.equal(60); - expect(result[0].winNotification[0]).to.deep.equal({'method': 'POST', 'path': '/hb/bid_won?test=1', 'data': {'ad': [{'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'}], 'unit_id': 1234, 'site_id': 123}}); - }); - }); - - describe('adResponse', function () { - const bid = { - 'unitId': 13144370, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2bdcb0b203c17d', - 'referer': 'https://test.domain/index.html' - }; - const ad = { - 'ad': '
ad
', - 'width': 300, - 'height': 250, - 'creativeId': '8:123456', - 'syncs': [], - 'winNotification': [], - 'cpm': 0.01, - 'currency': 'USD', - 'netRevenue': true - }; - - it('fill ad for response', function () { - const result = spec.adResponse(bid, ad); - expect(result.requestId).to.equal('2bdcb0b203c17d'); - expect(result.cpm).to.equal(0.01); - expect(result.width).to.equal(300); - expect(result.height).to.equal(250); - expect(result.creativeId).to.equal('8:123456'); - expect(result.currency).to.equal('USD'); - expect(result.ttl).to.equal(60); - }); - }); - - describe('onBidWon', function () { - const bid = { - winNotification: [ - { - 'method': 'POST', - 'path': '/hb/bid_won?test=1', - 'data': { - 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 0.01, 'nurl': 'http://test.domain/'} - ], - 'unit_id': 1234, - 'site_id': 123 - } - } - ] - }; - - let ajaxStub; - - beforeEach(() => { - ajaxStub = sinon.stub(spec, 'postRequest') - }) - - afterEach(() => { - ajaxStub.restore() - }) - - it('calls adpartner\'s callback endpoint', () => { - const result = spec.onBidWon(bid); - expect(result).to.equal(true); - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(ENDPOINT_PROTOCOL + '://' + ENDPOINT_DOMAIN + '/hb/bid_won?test=1'); - expect(ajaxStub.firstCall.args[1]).to.deep.equal(JSON.stringify(bid.winNotification[0].data)); - }); - }); -}); diff --git a/test/spec/modules/adprimeBidAdapter_spec.js b/test/spec/modules/adprimeBidAdapter_spec.js deleted file mode 100644 index 8627941dc80..00000000000 --- a/test/spec/modules/adprimeBidAdapter_spec.js +++ /dev/null @@ -1,297 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/adprimeBidAdapter.js'; -import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; - -describe('AdprimebBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'adprime', - params: { - placementId: 0, - traffic: BANNER - } - }; - - const bidderRequest = { - refererInfo: { - referer: 'test.com' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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://delta.adprime.com/?c=o&m=multi'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'identeties', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'keywords'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - 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('buildRequests with user ids', function () { - bid.userId = {} - bid.userId.idl_env = 'idl_env123'; - let serverRequest = spec.buildRequests([bid], bidderRequest); - it('Return bids with user identeties', function () { - let data = serverRequest.data; - let placements = data['placements']; - expect(data).to.be.an('object'); - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.property('identeties') - expect(placement.identeties).to.be.an('object') - expect(placement.identeties).to.have.property('identityLink') - expect(placement.identeties.identityLink).to.be.equal('idl_env123') - } - }); - }); - 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' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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 () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://delta.adprime.com/?c=rtb&m=sync'); - }); - }); -}); diff --git a/test/spec/modules/adtrueBidAdapter_spec.js b/test/spec/modules/adtrueBidAdapter_spec.js index 8e1c872d460..b499d077a3c 100644 --- a/test/spec/modules/adtrueBidAdapter_spec.js +++ b/test/spec/modules/adtrueBidAdapter_spec.js @@ -21,7 +21,7 @@ describe('AdTrueBidAdapter', function () { params: { publisherId: '1212', zoneId: '21423', - reserve: 0.2 + reserve: 0 }, placementCode: 'adunit-code-1', sizes: [[300, 250]], diff --git a/test/spec/modules/advangelistsBidAdapter_spec.js b/test/spec/modules/advangelistsBidAdapter_spec.js deleted file mode 100755 index 2b9615fb572..00000000000 --- a/test/spec/modules/advangelistsBidAdapter_spec.js +++ /dev/null @@ -1,137 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/advangelistsBidAdapter.js'; -import { BANNER, VIDEO } from 'src/mediaTypes.js'; - -describe('advangelistsBidAdapter', function () { - let bidRequests; - let bidRequestsVid; - - beforeEach(function () { - bidRequests = [{'bidder': 'advangelists', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - - bidRequestsVid = [{'bidder': 'advangelists', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - }); - - describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed for banner', function () { - const bidRequest = bidRequests[0]; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('should return true when the required params are passed for video', function () { - const bidRequests = bidRequestsVid[0]; - expect(spec.isBidRequestValid(bidRequests)).to.equal(true); - }); - - it('should return false when no pub id params are passed', function () { - const bidRequest = bidRequests[0]; - bidRequest.params.pubid = ''; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); - - it('should return false when no placement params are passed', function () { - const bidRequest = bidRequests[0]; - bidRequest.params.placement = ''; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); - - it('should return false when a bid request is not passed', function () { - expect(spec.isBidRequestValid()).to.equal(false); - expect(spec.isBidRequestValid({})).to.equal(false); - }); - }); - - describe('spec.buildRequests', function () { - it('should create a POST request for each bid', function () { - const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].method).to.equal('POST'); - }); - - it('should create a POST request for each bid in video request', function () { - const bidRequest = bidRequestsVid[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].method).to.equal('POST'); - }); - - it('should have domain in request', function () { - const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].data.site.domain).length !== 0; - }); - }); - - describe('spec.interpretResponse', function () { - describe('for banner bids', function () { - it('should return no bids if the response is not valid', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); - - if (typeof bidResponse !== 'undefined') { - expect(bidResponse.length).to.equal(0); - } else { - expect(true).to.equal(true); - } - }); - - it('should return no bids if the response is empty', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); - if (typeof bidResponse !== 'undefined') { - expect(bidResponse.length).to.equal(0); - } else { expect(true).to.equal(true); } - }); - - it('should return valid video bid responses', function () { - let _mediaTypes = VIDEO; - const advangelistsbidreqVid = {'bidRequest': {'mediaTypes': {'video': {'w': 320, 'h': 480}}}}; - const serverResponseVid = {'cur': 'USD', 'id': '25c6ab92aa0e81', 'seatbid': [{'seat': '3', 'bid': [{'crid': '1855', 'h': 480, 'protocol': 2, 'nurl': 'http://nep.advangelists.com/xp/evt?pp=1MO1wiaMhhq7wLRzZZwwwPkJxxKpYEnM5k5MH4qSGm1HR8rp3Nl7vDocvzZzSAvE4pnREL9mQ1kf5PDjk6E8em6DOk7vVrYUH1TYQyqCucd58PFpJNN7h30RXKHHFg3XaLuQ3PKfMuI1qZATBJ6WHcu875y0hqRdiewn0J4JsCYF53M27uwmcV0HnQxARQZZ72mPqrW95U6wgkZljziwKrICM3aBV07TU6YK5R5AyzJRuD6mtrQ2xtHlQ3jXVYKE5bvWFiUQd90t0jOGhPtYBNoOfP7uQ4ZZj4pyucxbr96orHe9PSOn9UpCSWArdx7s8lOfDpwOvbMuyGxynbStDWm38sDgd4bMHnIt762m5VMDNJfiUyX0vWzp05OsufJDVEaWhAM62i40lQZo7mWP4ipoOWLkmlaAzFIMsTcNaHAHiKKqGEOZLkCEhFNM0SLcvgN2HFRULOOIZvusq7TydOKxuXgCS91dLUDxDDDFUK83BFKlMkTxnCzkLbIR1bd9GKcr1TRryOrulyvRWAKAIhEsUzsc5QWFUhmI2dZ1eqnBQJ0c89TaPcnoaP2WipF68UgyiOstf2CBy0M34858tC5PmuQwQYwXscg6zyqDwR0i9MzGH4FkTyU5yeOlPcsA0ht6UcoCdFpHpumDrLUwAaxwGk1Nj8S6YlYYT5wNuTifDGbg22QKXzZBkUARiyVvgPn9nRtXnrd7WmiMYq596rya9RQj7LC0auQW8bHVQLEe49shsZDnAwZTWr4QuYKqgRGZcXteG7RVJe0ryBZezOq11ha9C0Lv0siNVBahOXE35Wzoq4c4BDaGpqvhaKN7pjeWLGlQR04ufWekwxiMWAvjmfgAfexBJ7HfbYNZpq__', 'adid': '61_1855', 'adomain': ['chevrolet.com.ar'], 'price': 2, 'w': 320, 'iurl': 'https://daf37cpxaja7f.cloudfront.net/c61/creative_url_14922301369663_1.png', 'cat': ['IAB2'], 'id': '7f570b40-aca1-4806-8ea8-818ea679c82b_0', 'attr': [], 'impid': '0', 'cid': '61'}]}], 'bidid': '7f570b40-aca1-4806-8ea8-818ea679c82b'} - const bidResponseVid = spec.interpretResponse({ body: serverResponseVid }, advangelistsbidreqVid); - delete bidResponseVid['vastUrl']; - delete bidResponseVid['ad']; - expect(bidResponseVid).to.deep.equal({ - requestId: bidRequestsVid[0].bidId, - bidderCode: 'advangelists', - creativeId: serverResponseVid.seatbid[0].bid[0].crid, - cpm: serverResponseVid.seatbid[0].bid[0].price, - width: serverResponseVid.seatbid[0].bid[0].w, - height: serverResponseVid.seatbid[0].bid[0].h, - mediaType: 'video', - currency: 'USD', - netRevenue: true, - ttl: 60 - }); - }); - - it('should return valid banner bid responses', function () { - const advangelistsbidreq = {bids: {}}; - bidRequests.forEach(bid => { - let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); - advangelistsbidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, - w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], - h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] - - }; - }); - const serverResponse = {'id': '2aa73f571eaf29', 'seatbid': [{'bid': [{'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
', 'adomain': ['advertiserdomain.com'], 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; - - const bidResponse = spec.interpretResponse({ body: serverResponse }, advangelistsbidreq); - expect(bidResponse).to.deep.equal({ - requestId: bidRequests[0].bidId, - ad: serverResponse.seatbid[0].bid[0].adm, - bidderCode: 'advangelists', - creativeId: serverResponse.seatbid[0].bid[0].crid, - cpm: serverResponse.seatbid[0].bid[0].price, - width: serverResponse.seatbid[0].bid[0].w, - height: serverResponse.seatbid[0].bid[0].h, - mediaType: 'banner', - currency: 'USD', - netRevenue: true, - ttl: 60 - }); - }); - }); - }); -}); diff --git a/test/spec/modules/advenueBidAdapter_spec.js b/test/spec/modules/advenueBidAdapter_spec.js deleted file mode 100644 index 2d7739361b4..00000000000 --- a/test/spec/modules/advenueBidAdapter_spec.js +++ /dev/null @@ -1,107 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/advenueBidAdapter.js'; - -describe('AdvenueAdapter', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'advenue', - bidderRequestId: '145e1d6a7837c9', - params: { - placementId: 123, - traffic: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - }; - - describe('isBidRequestValid', function () { - it('Should return true when placementId can be cast to a number', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when placementId is not a number', function () { - bid.params.placementId = 'aaa'; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid]); - 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://ssp.advenuemedia.co.uk/?c=o&m=multi'); - }); - 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', 'secure', 'host', 'page', 'placements'); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.secure).to.be.within(0, 1); - expect(data.host).to.be.a('string'); - expect(data.page).to.be.a('string'); - let placements = data['placements']; - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.be.a('number'); - expect(placement.bidId).to.be.a('string'); - expect(placement.traffic).to.be.a('string'); - expect(placement.sizes).to.be.an('array'); - } - }); - 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 () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); -}); diff --git a/test/spec/modules/advertlyBidAdapter_spec.js b/test/spec/modules/advertlyBidAdapter_spec.js deleted file mode 100755 index 7825f11948a..00000000000 --- a/test/spec/modules/advertlyBidAdapter_spec.js +++ /dev/null @@ -1,159 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/advertlyBidAdapter.js'; - -const ENDPOINT = 'https://api.advertly.com/www/admin/plugins/Prebid/getAd.php'; - -describe('The Advertly bidding adapter', function () { - describe('isBidRequestValid', function () { - it('should return false when given an invalid bid', function () { - const bid = { - bidder: 'advertly', - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - - it('should return true when given a publisherId in bid', function () { - const bid = { - bidder: 'advertly', - params: { - publisherId: 2 - }, - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(true); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - 'bidder': 'advertly', - 'params': { - 'publisherId': 2 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ] - }]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('check endpoint url', function () { - expect(request.url).to.equal(ENDPOINT) - }); - - it('sets the proper banner object', function () { - expect(bidRequests[0].params.publisherId).to.equal(2); - }) - }); - const response = { - body: [ - { - 'requestId': '2ee937f15015c6', - 'cpm': '0.2000', - 'width': 300, - 'height': 600, - 'creativeId': '4', - 'currency': 'USD', - 'netRevenue': true, - 'ad': 'ads.html', - 'mediaType': 'banner' - }, - { - 'requestId': '3e1af92622bdc', - 'cpm': '0.2000', - 'creativeId': '4', - 'context': 'outstream', - 'currency': 'USD', - 'netRevenue': true, - 'vastUrl': 'tezt.xml', - 'width': 640, - 'height': 480, - 'mediaType': 'video' - }] - }; - - const request = [ - { - 'bidder': 'advertly', - 'params': { - 'publisherId': 2 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 600] - ] - } - }, - 'bidId': '2ee937f15015c6', - 'src': 'client', - }, - { - 'bidder': 'advertly', - 'params': { - 'publisherId': 2 - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'playerSize': [ - [640, 480] - ] - } - }, - 'bidId': '3e1af92622bdc', - 'src': 'client', - } - ]; - - describe('interpretResponse', function () { - it('return empty array when no ad found', function () { - const response = {}; - const request = { bidRequests: [] }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(0); - }); - - it('check response for banner and video', function () { - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(2); - expect(bids[0].requestId).to.equal('2ee937f15015c6'); - expect(bids[0].cpm).to.equal('0.2000'); - expect(bids[1].cpm).to.equal('0.2000'); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(600); - expect(bids[1].vastUrl).to.not.equal(''); - expect(bids[0].ad).to.not.equal(''); - expect(bids[1].adResponse).to.not.equal(''); - expect(bids[1].renderer).not.to.be.an('undefined'); - }); - }); - - describe('On winning bid', function () { - const bids = spec.interpretResponse(response, request); - spec.onBidWon(bids); - }); - - describe('On bid Time out', function () { - const bids = spec.interpretResponse(response, request); - spec.onTimeout(bids); - }); - - describe('user sync', function () { - it('to check the user sync iframe', function () { - let syncs = spec.getUserSyncs({ - iframeEnabled: true - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - }); - }); -}); diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js deleted file mode 100644 index 80ecab764e8..00000000000 --- a/test/spec/modules/ajaBidAdapter_spec.js +++ /dev/null @@ -1,311 +0,0 @@ -import { spec } from 'modules/ajaBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://ad.as.amanad.adtdp.com/v2/prebid'; - -describe('AjaAdapter', function () { - const adapter = newBidder(spec); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'aja', - 'params': { - 'asi': '123456' - }, - 'adUnitCode': 'adunit', - 'sizes': [[300, 250]], - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'asi': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'aja', - 'params': { - 'asi': '123456' - }, - 'adUnitCode': 'adunit', - 'sizes': [[300, 250]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - let bidderRequest = { - refererInfo: { - referer: 'https://hoge.com' - } - }; - - it('sends bid request to ENDPOINT via GET', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&'); - }); - }); - - describe('interpretResponse', function () { - it('should get correct banner bid response', function () { - let response = { - 'is_ad_return': true, - 'ad': { - 'ad_type': 1, - 'prebid_id': '51ef8751f9aead', - 'price': 12.34, - 'currency': 'USD', - 'creative_id': '123abc', - 'banner': { - 'w': 300, - 'h': 250, - 'tag': '
', - 'imps': [ - 'https://as.amanad.adtdp.com/v1/imp' - ] - } - }, - 'syncs': [ - 'https://example.com' - ] - }; - - let expectedResponse = [ - { - 'requestId': '51ef8751f9aead', - 'cpm': 12.34, - 'creativeId': '123abc', - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
', - 'mediaType': 'banner', - 'currency': 'USD', - 'ttl': 300, - 'netRevenue': true - } - ]; - - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles video responses', function () { - let response = { - 'is_ad_return': true, - 'ad': { - 'ad_type': 3, - 'prebid_id': '51ef8751f9aead', - 'price': 12.34, - 'currency': 'JPY', - 'creative_id': '123abc', - 'video': { - 'w': 300, - 'h': 250, - 'vtag': '', - 'purl': 'https://cdn/player', - 'progress': true, - 'loop': false, - 'inread': false - } - }, - 'syncs': [ - 'https://example.com' - ] - }; - - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result[0]).to.have.property('vastXml'); - expect(result[0]).to.have.property('renderer'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); - - it('handles native response', function () { - let response = { - 'is_ad_return': true, - 'ad': { - 'ad_type': 2, - 'prebid_id': '51ef8751f9aead', - 'price': 12.34, - 'currency': 'JPY', - 'creative_id': '123abc', - 'native': { - 'template_and_ads': { - 'head': '', - 'body_wrapper': '', - 'body': '', - 'ads': [ - { - 'ad_format_id': 10, - 'assets': { - 'ad_spot_id': '123abc', - 'index': 0, - 'adchoice_url': 'https://aja-kk.co.jp/optout', - 'cta_text': 'cta', - 'img_icon': 'https://example.com/img_icon', - 'img_icon_width': '50', - 'img_icon_height': '50', - 'img_main': 'https://example.com/img_main', - 'img_main_width': '200', - 'img_main_height': '100', - 'lp_link': 'https://example.com/lp?k=v', - 'sponsor': 'sponsor', - 'title': 'ad_title', - 'description': 'ad_desc' - }, - 'imps': [ - 'https://example.com/imp' - ], - 'inviews': [ - 'https://example.com/inview' - ], - 'jstracker': '', - 'disable_trimming': false - } - ] - } - } - }, - 'syncs': [ - 'https://example.com' - ] - }; - - let expectedResponse = [ - { - 'requestId': '51ef8751f9aead', - 'cpm': 12.34, - 'creativeId': '123abc', - 'dealId': undefined, - 'mediaType': 'native', - 'currency': 'JPY', - 'ttl': 300, - 'netRevenue': true, - 'native': { - 'title': 'ad_title', - 'body': 'ad_desc', - 'cta': 'cta', - 'sponsoredBy': 'sponsor', - 'image': { - 'url': 'https://example.com/img_main', - 'width': 200, - 'height': 100 - }, - 'icon': { - 'url': 'https://example.com/img_icon', - 'width': 50, - 'height': 50 - }, - 'clickUrl': 'https://example.com/lp?k=v', - 'impressionTrackers': [ - 'https://example.com/imp' - ], - 'privacyLink': 'https://aja-kk.co.jp/optout', - } - } - ]; - - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}) - expect(result).to.deep.equal(expectedResponse) - }); - - it('handles nobid responses', function () { - let response = { - 'is_ad_return': false, - 'ad': {} - }; - - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function () { - const bidResponse1 = { - body: { - 'is_ad_return': true, - 'ad': { /* ad body */ }, - 'syncs': [ - 'https://example.test/pixel/1' - ], - 'sync_htmls': [ - 'https://example.test/iframe/1' - ] - } - }; - - const bidResponse2 = { - body: { - 'is_ad_return': true, - 'ad': { /* ad body */ }, - 'syncs': [ - 'https://example.test/pixel/2' - ] - } - }; - - it('should use a sync url from first response (pixel and iframe)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://example.test/pixel/1' - }, - { - type: 'iframe', - url: 'https://example.test/iframe/1' - } - ]); - }); - - it('handle empty response (e.g. timeout)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('returns empty syncs when not pixel enabled and not iframe enabled', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([]); - }); - - it('returns pixel syncs when pixel enabled and not iframe enabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://example.test/pixel/1' - } - ]); - }); - - it('returns iframe syncs when not pixel enabled and iframe enabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://example.test/iframe/1' - } - ]); - }); - }); -}); diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index 5f6a935c453..527d16159bb 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -219,15 +219,6 @@ describe('ApacdexBidAdapter', function () { 'id': '2ae366c2-2576-45e5-bd21-72ed10598f17', 'atype': 1 }] - }, { - 'source': 'sharedid.org', - 'uids': [{ - 'id': '01EZXQDVAPER4KE1VBS29XKV4Z', - 'atype': 1, - 'ext': { - 'third': '01EZXQDVAPER4KE1VBS29XKV4Z' - } - }] }], }, { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index fa2baf0db5f..9396c1e1928 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -215,6 +215,40 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].hb_source).to.deep.equal(1); }); + it('should include ORTB video values when video params were not set', function() { + let bidRequest = deepClone(bidRequests[0]); + bidRequest.params = { + placementId: '1234235', + video: { + skippable: true, + playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], + context: 'outstream' + } + }; + bidRequest.mediaTypes = { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'], + skip: 0, + minduration: 5, + api: [1, 5, 6], + playbackmethod: [2, 4] + } + }; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].video).to.deep.equal({ + minduration: 5, + playback_method: 2, + skippable: true, + context: 4 + }); + expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) + }); + it('should add video property when adUnit includes a renderer', function () { const videoData = { mediaTypes: { diff --git a/test/spec/modules/atomxBidAdapter_spec.js b/test/spec/modules/atomxBidAdapter_spec.js deleted file mode 100644 index d798bd6308c..00000000000 --- a/test/spec/modules/atomxBidAdapter_spec.js +++ /dev/null @@ -1,119 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/atomxBidAdapter.js'; - -describe('atomxAdapterTest', function () { - describe('bidRequestValidity', function () { - it('bidRequest with id param', function () { - expect(spec.isBidRequestValid({ - bidder: 'atomx', - params: { - id: 1234, - }, - })).to.equal(true); - }); - - it('bidRequest with no id param', function () { - expect(spec.isBidRequestValid({ - bidder: 'atomx', - params: { - }, - })).to.equal(false); - }); - }); - - describe('bidRequest', function () { - const bidRequests = [{ - 'bidder': 'atomx', - 'params': { - 'id': '123' - }, - 'adUnitCode': 'aaa', - 'transactionId': '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - 'sizes': [300, 250], - 'bidId': '1abgs362e0x48a8', - 'bidderRequestId': '70deaff71c281d', - 'auctionId': '5c66da22-426a-4bac-b153-77360bef5337' - }, - { - 'bidder': 'atomx', - 'params': { - 'id': '456', - }, - 'adUnitCode': 'bbb', - 'transactionId': '193995b4-7122-4739-959b-2463282a138b', - 'sizes': [[800, 600]], - 'bidId': '22aidtbx5eabd9', - 'bidderRequestId': '70deaff71c281d', - 'auctionId': 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' - }]; - - it('bidRequest HTTP method', function () { - const requests = spec.buildRequests(bidRequests); - requests.forEach(function(requestItem) { - expect(requestItem.method).to.equal('GET'); - }); - }); - - it('bidRequest url', function () { - const requests = spec.buildRequests(bidRequests); - requests.forEach(function(requestItem) { - expect(requestItem.url).to.match(new RegExp('p\\.ato\\.mx/placement')); - }); - }); - - it('bidRequest data', function () { - const requests = spec.buildRequests(bidRequests); - expect(requests[0].data.id).to.equal('123'); - expect(requests[0].data.size).to.equal('300x250'); - expect(requests[0].data.prebid).to.equal('1abgs362e0x48a8'); - expect(requests[1].data.id).to.equal('456'); - expect(requests[1].data.size).to.equal('800x600'); - expect(requests[1].data.prebid).to.equal('22aidtbx5eabd9'); - }); - }); - - describe('interpretResponse', function () { - const bidRequest = { - 'method': 'GET', - 'url': 'https://p.ato.mx/placement', - 'data': { - 'v': 12, - 'id': '123', - 'size': '300x250', - 'prebid': '22aidtbx5eabd9', - 'b': 0, - 'h': '7t3y9', - 'type': 'javascript', - 'screen': '800x600x32', - 'timezone': 0, - 'domain': 'https://example.com', - 'r': '', - } - }; - - const bidResponse = { - body: { - 'cpm': 0.00009, - 'width': 300, - 'height': 250, - 'url': 'https://atomx.com', - 'creative_id': 456, - 'code': '22aidtbx5eabd9', - }, - headers: {} - }; - - it('result is correct', function () { - const result = spec.interpretResponse(bidResponse, bidRequest); - - expect(result[0].requestId).to.equal('22aidtbx5eabd9'); - expect(result[0].cpm).to.equal(0.00009 * 1000); - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal(456); - expect(result[0].currency).to.equal('USD'); - expect(result[0].ttl).to.equal(60); - expect(result[0].adUrl).to.equal('https://atomx.com'); - }); - }); -}); diff --git a/test/spec/modules/avocetBidAdapter_spec.js b/test/spec/modules/avocetBidAdapter_spec.js deleted file mode 100644 index 2a2f29e48d2..00000000000 --- a/test/spec/modules/avocetBidAdapter_spec.js +++ /dev/null @@ -1,167 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/avocetBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory'; -import { config } from 'src/config'; - -describe('Avocet adapter', function () { - beforeEach(function () { - config.setConfig({ - currency: { - adServerCurrency: 'USD', - }, - publisherDomain: 'test.com', - fpd: { - some: 'data', - }, - }); - }); - - afterEach(function () { - config.resetConfig(); - }); - - describe('inherited functions', function () { - it('exists and is a function', function () { - const adapter = newBidder(spec); - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('should return false for bid request missing params', () => { - const invalidBidRequest = { - bid: {}, - }; - expect(spec.isBidRequestValid(invalidBidRequest)).to.equal(false); - }); - it('should return false for an invalid type placement param', () => { - const invalidBidRequest = { - params: { - placement: 123, - }, - }; - expect(spec.isBidRequestValid(invalidBidRequest)).to.equal(false); - }); - it('should return false for an invalid length placement param', () => { - const invalidBidRequest = { - params: { - placement: '123', - }, - }; - expect(spec.isBidRequestValid(invalidBidRequest)).to.equal(false); - }); - it('should return true for a valid length placement param', () => { - const validBidRequest = { - params: { - placement: '012345678901234567890123', - }, - }; - expect(spec.isBidRequestValid(validBidRequest)).to.equal(true); - }); - }); - describe('buildRequests', function () { - it('constructs a valid POST request', function () { - const request = spec.buildRequests( - [ - { - bidder: 'avct', - params: { - placement: '012345678901234567890123', - }, - userId: { - id5id: { - uid: 'test' - } - } - }, - { - bidder: 'avct', - params: { - placement: '012345678901234567890123', - }, - }, - ], - exampleBidderRequest - ); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://ads.avct.cloud/prebid'); - - const requestData = JSON.parse(request.data); - expect(requestData.ext).to.be.an('object'); - expect(requestData.ext.currency).to.equal('USD'); - expect(requestData.ext.publisherDomain).to.equal('test.com'); - expect(requestData.ext.fpd).to.deep.equal({ some: 'data' }); - expect(requestData.ext.schain).to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1, - }, - ], - }, - }); - expect(requestData.ext.id5id).to.equal('test'); - expect(requestData.bids).to.be.an('array'); - expect(requestData.bids.length).to.equal(2); - }); - }); - describe('interpretResponse', function () { - it('no response', function () { - const response = spec.interpretResponse(); - expect(response).to.be.an('array'); - expect(response.length).to.equal(0); - }); - it('no body', function () { - const response = spec.interpretResponse({}); - expect(response).to.be.an('array'); - expect(response.length).to.equal(0); - }); - it('null body', function () { - const response = spec.interpretResponse({ body: null }); - expect(response).to.be.an('array'); - expect(response.length).to.equal(0); - }); - it('empty body', function () { - const response = spec.interpretResponse({ body: {} }); - expect(response).to.be.an('array'); - expect(response.length).to.equal(0); - }); - it('null body.responses', function () { - const response = spec.interpretResponse({ body: { responses: null } }); - expect(response).to.be.an('array'); - expect(response.length).to.equal(0); - }); - it('array body', function () { - const response = spec.interpretResponse({ body: [{}] }); - expect(response).to.be.an('array'); - expect(response.length).to.equal(1); - }); - it('array body.responses', function () { - const response = spec.interpretResponse({ body: { responses: [{}] } }); - expect(response).to.be.an('array'); - expect(response.length).to.equal(1); - }); - }); -}); - -const exampleBidderRequest = { - schain: { - validation: 'strict', - config: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1, - }, - ], - }, - }, -}; diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 0e772e7be02..62f36182d55 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -222,51 +222,6 @@ describe('betweenBidAdapterTests', function () { expect(req_data.sizes).to.deep.equal(['970x250', '240x400', '728x90']) }); - it('check sharedId with id and third', function() { - const bidRequestData = [{ - bidId: 'bid123', - bidder: 'between', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - s: 1112, - }, - userId: { - sharedid: { - id: '01EXQE7JKNDRDDVATB0S2GX1NT', - third: '01EXQE7JKNDRDDVATB0S2GX1NT' - } - } - }]; - const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; - const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; - expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT'); - }); - it('check sharedId with only id', function() { - const bidRequestData = [{ - bidId: 'bid123', - bidder: 'between', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - s: 1112, - }, - userId: { - sharedid: { - id: '01EXQE7JKNDRDDVATB0S2GX1NT', - } - } - }]; - const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; - const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; - expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal(''); - }); it('check adomain', function() { const serverResponse = { body: [{ diff --git a/test/spec/modules/bidfluenceBidAdapter_spec.js b/test/spec/modules/bidfluenceBidAdapter_spec.js deleted file mode 100644 index 6b3a0c2b044..00000000000 --- a/test/spec/modules/bidfluenceBidAdapter_spec.js +++ /dev/null @@ -1,114 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/bidfluenceBidAdapter.js'; - -const BIDDER_CODE = 'bidfluence'; -const PLACEMENT_ID = '1000'; -const PUB_ID = '1000'; -const CONSENT_STRING = 'DUXDSDFSFWRRR8345F=='; - -const validBidRequests = [{ - 'bidder': BIDDER_CODE, - 'params': { - 'placementId': PLACEMENT_ID, - 'publisherId': PUB_ID, - 'reservePrice': 0 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250]], - 'bidId': '2b1f23307fb8ef', - 'bidderRequestId': '10edf38ec1a719', - 'auctionId': '1025ba77-5463-4877-b0eb-14b205cb9304' -}]; - -const bidderRequest = { - 'bidderCode': 'bidfluence', - 'auctionId': '1025ba77-5463-4877-b0eb-14b205cb9304', - 'bidderRequestId': '10edf38ec1a719', - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'test', - 'stack': ['test'] - }, - 'timeout': 1000, - 'gdprConsent': { - 'gdprApplies': true, - 'consentString': CONSENT_STRING, - 'vendorData': '' - } -}; - -bidderRequest.bids = validBidRequests; - -describe('Bidfluence Adapter test', () => { - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(validBidRequests[0])).to.equal(true); - }); - it('should return the right bidder code', function () { - expect(spec.code).to.eql(BIDDER_CODE); - }); - }); - - describe('buildRequests', function () { - it('sends bid request to our endpoint via POST', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.method).to.equal('POST'); - const payload = JSON.parse(request.data); - - expect(payload.bids[0].bid).to.equal(validBidRequests[0].bidId); - expect(payload.azr).to.equal(true); - expect(payload.ck).to.not.be.undefined; - expect(payload.bids[0].tid).to.equal(PLACEMENT_ID); - expect(payload.bids[0].pid).to.equal(PUB_ID); - expect(payload.bids[0].rp).to.be.a('number'); - expect(payload.re).to.not.be.undefined; - expect(payload.st).to.not.be.undefined; - expect(payload.tz).to.not.be.undefined; - expect(payload.sr).to.not.be.undefined; - expect(payload.vp).to.not.be.undefined; - expect(payload.sdt).to.not.be.undefined; - expect(payload.bids[0].w).to.equal('300'); - expect(payload.bids[0].h).to.equal('250'); - }); - - it('sends gdpr info if exists', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.gdpr).to.equal(true); - expect(payload.gdprc).to.equal(CONSENT_STRING); - }); - }); - - describe('interpretResponse', function () { - const response = { - body: { - Bids: - [{ - 'CreativeId': '1000', - 'Cpm': 0.50, - 'Ad': '
', - 'Height': 250, - 'Width': 300 - }] - } - }; - - it('should get correct bid response', function () { - const expectedResponse = [{ - requestId: response.body.Bids[0].BidId, - cpm: response.body.Bids[0].Cpm, - width: response.body.Bids[0].Width, - height: response.body.Bids[0].Height, - creativeId: response.body.Bids[0].CreativeId, - ad: response.body.Bids[0].Ad, - currency: 'USD', - netRevenue: true, - ttl: 360 - }]; - - let result = spec.interpretResponse(response, { 'bidderRequest': validBidRequests[0] }); - expect(result).to.deep.equal(expectedResponse); - }); - }); -}); diff --git a/test/spec/modules/bidlabBidAdapter_spec.js b/test/spec/modules/bidlabBidAdapter_spec.js deleted file mode 100644 index cffd43ae6ca..00000000000 --- a/test/spec/modules/bidlabBidAdapter_spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/bidlabBidAdapter.js'; - -describe('BidlabBidAdapter', function () { - let bid = { - bidId: '23fhj33i987f', - bidder: 'bidlab', - params: { - placementId: 0, - traffic: 'banner' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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]); - 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://service.bidlab.ai/?c=o&m=multi'); - }); - 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'); - 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'); - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal('banner'); - }); - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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 () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', function () { - if (spec.noSync) { - expect(userSync).to.be.equal(false); - } else { - 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://service.bidlab.ai/?c=o&m=sync'); - } - }); - }); -}); diff --git a/test/spec/modules/bidphysicsBidAdapter_spec.js b/test/spec/modules/bidphysicsBidAdapter_spec.js deleted file mode 100644 index fc15c39cf81..00000000000 --- a/test/spec/modules/bidphysicsBidAdapter_spec.js +++ /dev/null @@ -1,261 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/bidphysicsBidAdapter.js'; - -const REQUEST = { - 'bidderCode': 'bidphysics', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708', - 'bidderRequestId': 'requestId', - 'bidRequest': [{ - 'bidder': 'bidphysics', - 'params': { - 'unitId': 123456, - }, - 'placementCode': 'div-gpt-dummy-placement-code', - 'sizes': [ - [300, 250] - ], - 'bidId': 'bidId1', - 'bidderRequestId': 'bidderRequestId', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' - }, - { - 'bidder': 'bidphysics', - 'params': { - 'unitId': 123456, - }, - 'placementCode': 'div-gpt-dummy-placement-code', - 'sizes': [ - [300, 250] - ], - 'bidId': 'bidId2', - 'bidderRequestId': 'bidderRequestId', - 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' - }], - 'start': 1487883186070, - 'auctionStart': 1487883186069, - 'timeout': 3000 -}; - -const RESPONSE = { - 'headers': null, - 'body': { - 'id': 'responseId', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'bidId1', - 'impid': 'bidId1', - 'price': 0.18, - 'adm': '', - 'adid': '144762342', - 'adomain': [ - 'https://dummydomain.com' - ], - 'iurl': 'iurl', - 'cid': '109', - 'crid': 'creativeId', - 'cat': [], - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'type': 'banner' - }, - 'bidder': { - 'appnexus': { - 'brand_id': 334553, - 'auction_id': 514667951122925701, - 'bidder_id': 2, - 'bid_ad_type': 0 - } - } - } - }, - { - 'id': 'bidId2', - 'impid': 'bidId2', - 'price': 0.1, - 'adm': '', - 'adid': '144762342', - 'adomain': [ - 'https://dummydomain.com' - ], - 'iurl': 'iurl', - 'cid': '109', - 'crid': 'creativeId', - 'cat': [], - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'type': 'banner' - }, - 'bidder': { - 'appnexus': { - 'brand_id': 386046, - 'auction_id': 517067951122925501, - 'bidder_id': 2, - 'bid_ad_type': 0 - } - } - } - } - ], - 'seat': 'bidphysics' - } - ], - 'ext': { - 'usersync': { - 'sovrn': { - 'status': 'none', - 'syncs': [ - { - 'url': 'urlsovrn', - 'type': 'iframe' - } - ] - }, - 'appnexus': { - 'status': 'none', - 'syncs': [ - { - 'url': 'urlappnexus', - 'type': 'pixel' - } - ] - } - }, - 'responsetimemillis': { - 'appnexus': 127 - } - } - } -}; - -describe('BidPhysics bid adapter', function () { - describe('isBidRequestValid', function () { - it('should accept request if only unitId is passed', function () { - let bid = { - bidder: 'bidphysics', - params: { - unitId: 'unitId', - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should accept request if only networkId is passed', function () { - let bid = { - bidder: 'bidphysics', - params: { - networkId: 'networkId', - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should accept request if only publisherId is passed', function () { - let bid = { - bidder: 'bidphysics', - params: { - publisherId: 'publisherId', - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('reject requests without params', function () { - let bid = { - bidder: 'bidphysics', - params: {} - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - it('creates request data', function () { - let request = spec.buildRequests(REQUEST.bidRequest, REQUEST); - - expect(request).to.exist.and.to.be.a('object'); - const payload = JSON.parse(request.data); - expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId); - expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId); - }); - - it('has gdpr data if applicable', function () { - const req = Object.assign({}, REQUEST, { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true, - } - }); - let request = spec.buildRequests(REQUEST.bidRequest, req); - - const payload = JSON.parse(request.data); - expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); - expect(payload.regs.ext).to.have.property('gdpr', 1); - }); - }); - - describe('interpretResponse', function () { - it('have bids', function () { - let bids = spec.interpretResponse(RESPONSE, REQUEST); - expect(bids).to.be.an('array').that.is.not.empty; - validateBidOnIndex(0); - validateBidOnIndex(1); - - function validateBidOnIndex(index) { - expect(bids[index]).to.have.property('currency', 'USD'); - expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].impid); - expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); - expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); - 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]).to.have.property('ttl', 30); - expect(bids[index]).to.have.property('netRevenue', true); - } - }); - - it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); - const bids = spec.interpretResponse(EMPTY_RESP, REQUEST); - - expect(bids).to.be.empty; - }); - }); - - describe('getUserSyncs', function () { - it('handles no parameters', function () { - let opts = spec.getUserSyncs({}); - expect(opts).to.be.an('array').that.is.empty; - }); - it('returns non if sync is not allowed', function () { - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); - - expect(opts).to.be.an('array').that.is.empty; - }); - - it('iframe sync enabled should return results', function () { - let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); - - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('iframe'); - expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['sovrn'].syncs[0].url); - }); - - it('pixel sync enabled should return results', function () { - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); - - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('image'); - expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['appnexus'].syncs[0].url); - }); - - it('all sync enabled should return all results', function () { - let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); - - expect(opts.length).to.equal(2); - }); - }); -}); diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js deleted file mode 100644 index e0698c9eda8..00000000000 --- a/test/spec/modules/bizzclickBidAdapter_spec.js +++ /dev/null @@ -1,396 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/bizzclickBidAdapter.js'; -import {config} from 'src/config.js'; - -const NATIVE_BID_REQUEST = { - code: 'native_example', - mediaTypes: { - native: { - title: { - required: true, - len: 800 - }, - image: { - required: true, - len: 80 - }, - sponsoredBy: { - required: true - }, - clickUrl: { - required: true - }, - privacyLink: { - required: false - }, - body: { - required: true - }, - icon: { - required: true, - sizes: [50, 50] - } - } - }, - bidder: 'bizzclick', - params: { - placementId: 'hash', - accountId: 'accountId' - }, - timeout: 1000 - -}; - -const BANNER_BID_REQUEST = { - code: 'banner_example', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidder: 'bizzclick', - params: { - placementId: 'hash', - accountId: 'accountId' - }, - timeout: 1000, - gdprConsent: { - consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', - gdprApplies: 1, - }, - uspConsent: 'uspConsent' -} - -const bidRequest = { - refererInfo: { - referer: 'test.com' - } -} - -const VIDEO_BID_REQUEST = { - code: 'video1', - sizes: [640, 480], - mediaTypes: { video: { - minduration: 0, - maxduration: 999, - boxingallowed: 1, - skip: 0, - mimes: [ - 'application/javascript', - 'video/mp4' - ], - w: 1920, - h: 1080, - protocols: [ - 2 - ], - linearity: 1, - api: [ - 1, - 2 - ] - } - }, - - bidder: 'bizzclick', - params: { - placementId: 'hash', - accountId: 'accountId' - }, - timeout: 1000 - -} - -const BANNER_BID_RESPONSE = { - id: 'request_id', - bidid: 'request_imp_id', - seatbid: [{ - bid: [{ - id: 'bid_id', - impid: 'request_imp_id', - price: 5, - adomain: ['example.com'], - adm: 'admcode', - crid: 'crid', - ext: { - mediaType: 'banner' - } - }], - }], -}; - -const VIDEO_BID_RESPONSE = { - id: 'request_id', - bidid: 'request_imp_id', - seatbid: [{ - bid: [{ - id: 'bid_id', - impid: 'request_imp_id', - price: 5, - adomain: ['example.com'], - adm: 'admcode', - crid: 'crid', - ext: { - mediaType: 'video', - vastUrl: 'http://example.vast', - } - }], - }], -}; - -let imgData = { - url: `https://example.com/image`, - w: 1200, - h: 627 -}; - -const NATIVE_BID_RESPONSE = { - id: 'request_id', - bidid: 'request_imp_id', - seatbid: [{ - bid: [{ - id: 'bid_id', - impid: 'request_imp_id', - price: 5, - adomain: ['example.com'], - adm: { native: - { - assets: [ - {id: 0, title: 'dummyText'}, - {id: 3, image: imgData}, - { - id: 5, - data: {value: 'organization.name'} - } - ], - link: {url: 'example.com'}, - imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], - jstracker: 'tracker1.com' - } - }, - crid: 'crid', - ext: { - mediaType: 'native' - } - }], - }], -}; - -describe('BizzclickAdapter', function() { - describe('with COPPA', function() { - beforeEach(function() { - sinon.stub(config, 'getConfig') - .withArgs('coppa') - .returns(true); - }); - afterEach(function() { - config.getConfig.restore(); - }); - - it('should send the Coppa "required" flag set to "1" in the request', function () { - let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]); - expect(serverRequest.data[0].regs.coppa).to.equal(1); - }); - }); - - describe('isBidRequestValid', function() { - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, NATIVE_BID_REQUEST); - delete bid.params; - bid.params = { - 'IncorrectParam': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('build Native Request', function () { - const request = spec.buildRequests([NATIVE_BID_REQUEST], bidRequest); - - it('Creates a ServerRequest object with method, URL and data', function () { - expect(request).to.exist; - expect(request.method).to.exist; - expect(request.url).to.exist; - expect(request.data).to.exist; - }); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('Returns valid URL', function () { - expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); - }); - - it('Returns empty data if no valid requests are passed', function () { - let serverRequest = spec.buildRequests([]); - expect(serverRequest).to.be.an('array').that.is.empty; - }); - }); - - describe('build Banner Request', function () { - const request = spec.buildRequests([BANNER_BID_REQUEST]); - - it('Creates a ServerRequest object with method, URL and data', function () { - expect(request).to.exist; - expect(request.method).to.exist; - expect(request.url).to.exist; - expect(request.data).to.exist; - }); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('check consent and ccpa string is set properly', function() { - expect(request.data[0].regs.ext.gdpr).to.equal(1); - expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString); - expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent); - }) - - it('Returns valid URL', function () { - expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); - }); - }); - - describe('build Video Request', function () { - const request = spec.buildRequests([VIDEO_BID_REQUEST]); - - it('Creates a ServerRequest object with method, URL and data', function () { - expect(request).to.exist; - expect(request.method).to.exist; - expect(request.url).to.exist; - expect(request.data).to.exist; - }); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('Returns valid URL', function () { - expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); - }); - }); - - describe('interpretResponse', function () { - it('Empty response must return empty array', function() { - const emptyResponse = null; - let response = spec.interpretResponse(emptyResponse); - - expect(response).to.be.an('array').that.is.empty; - }) - - it('Should interpret banner response', function () { - const bannerResponse = { - body: [BANNER_BID_RESPONSE] - } - - const expectedBidResponse = { - requestId: BANNER_BID_RESPONSE.id, - cpm: BANNER_BID_RESPONSE.seatbid[0].bid[0].price, - width: BANNER_BID_RESPONSE.seatbid[0].bid[0].w, - height: BANNER_BID_RESPONSE.seatbid[0].bid[0].h, - ttl: BANNER_BID_RESPONSE.ttl || 1200, - currency: BANNER_BID_RESPONSE.cur || 'USD', - netRevenue: true, - creativeId: BANNER_BID_RESPONSE.seatbid[0].bid[0].crid, - dealId: BANNER_BID_RESPONSE.seatbid[0].bid[0].dealid, - mediaType: 'banner', - ad: BANNER_BID_RESPONSE.seatbid[0].bid[0].adm - } - - let bannerResponses = spec.interpretResponse(bannerResponse); - - 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'); - expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); - expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.ad).to.equal(expectedBidResponse.ad); - expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); - expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal(expectedBidResponse.currency); - expect(dataItem.width).to.equal(expectedBidResponse.width); - expect(dataItem.height).to.equal(expectedBidResponse.height); - }); - - it('Should interpret video response', function () { - const videoResponse = { - body: [VIDEO_BID_RESPONSE] - } - - const expectedBidResponse = { - requestId: VIDEO_BID_RESPONSE.id, - cpm: VIDEO_BID_RESPONSE.seatbid[0].bid[0].price, - width: VIDEO_BID_RESPONSE.seatbid[0].bid[0].w, - height: VIDEO_BID_RESPONSE.seatbid[0].bid[0].h, - ttl: VIDEO_BID_RESPONSE.ttl || 1200, - currency: VIDEO_BID_RESPONSE.cur || 'USD', - netRevenue: true, - creativeId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid, - dealId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].dealid, - mediaType: 'video', - vastXml: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm, - vastUrl: VIDEO_BID_RESPONSE.seatbid[0].bid[0].ext.vastUrl - } - - let videoResponses = spec.interpretResponse(videoResponse); - - expect(videoResponses).to.be.an('array').that.is.not.empty; - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); - expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); - expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml) - expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); - expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal(expectedBidResponse.currency); - expect(dataItem.width).to.equal(expectedBidResponse.width); - expect(dataItem.height).to.equal(expectedBidResponse.height); - }); - - it('Should interpret native response', function () { - const nativeResponse = { - body: [NATIVE_BID_RESPONSE] - } - - const expectedBidResponse = { - requestId: NATIVE_BID_RESPONSE.id, - cpm: NATIVE_BID_RESPONSE.seatbid[0].bid[0].price, - width: NATIVE_BID_RESPONSE.seatbid[0].bid[0].w, - height: NATIVE_BID_RESPONSE.seatbid[0].bid[0].h, - ttl: NATIVE_BID_RESPONSE.ttl || 1200, - currency: NATIVE_BID_RESPONSE.cur || 'USD', - netRevenue: true, - creativeId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].crid, - dealId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].dealid, - mediaType: 'native', - native: {clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url} - } - - let nativeResponses = spec.interpretResponse(nativeResponse); - - expect(nativeResponses).to.be.an('array').that.is.not.empty; - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); - expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); - expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); - expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl) - expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); - expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal(expectedBidResponse.currency); - expect(dataItem.width).to.equal(expectedBidResponse.width); - expect(dataItem.height).to.equal(expectedBidResponse.height); - }); - }); -}) diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js deleted file mode 100644 index a353665ec33..00000000000 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ /dev/null @@ -1,281 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/boldwinBidAdapter.js'; -import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; - -describe('BoldwinBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'boldwin', - params: { - placementId: 0, - traffic: BANNER - } - }; - - const bidderRequest = { - refererInfo: { - referer: 'test.com' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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://ssp.videowalldirect.com/?c=o&m=multi'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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 () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://cs.videowalldirect.com/?c=o&m=cookie'); - }); - }); -}); diff --git a/test/spec/modules/byplayBidAdapter_spec.js b/test/spec/modules/byplayBidAdapter_spec.js deleted file mode 100644 index 57aad403c4e..00000000000 --- a/test/spec/modules/byplayBidAdapter_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/byplayBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as bidderFactory from 'src/adapters/bidderFactory.js'; - -describe('byplayBidAdapter', () => { - describe('isBidRequestValid', () => { - describe('exist sectionId', () => { - const bid = { - 'bidder': 'byplay', - 'params': { - 'sectionId': '11111' - }, - }; - - it('should equal true', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - - describe('not exist sectionId', () => { - const bid = { - 'bidder': 'byplay', - 'params': { - }, - }; - - it('should equal false', () => { - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - }); - - describe('buildRequests', () => { - const bids = [ - { - 'bidder': 'byplay', - 'bidId': '1234', - 'params': { - 'sectionId': '1111' - }, - } - ]; - - const request = spec.buildRequests(bids); - - it('should return POST', () => { - expect(request[0].method).to.equal('POST'); - }); - - it('should return data', () => { - expect(request[0].data).to.equal('{"requestId":"1234","sectionId":"1111"}'); - }); - }); - - describe('interpretResponse', () => { - const serverResponse = { - body: { - 'cpm': 1500, - 'width': 320, - 'height': 180, - 'netRevenue': true, - 'creativeId': '1', - 'requestId': '209c1fb5ad88f5', - 'vastXml': '' - } - }; - - const bidderRequest = { - 'method': 'GET', - 'url': 'https://tasp0g98f2.execute-api.ap-northeast-1.amazonaws.com/v1/bidder', - 'data': '{"requestId":"209c1fb5ad88f5","sectionId":7986}' - }; - - const result = spec.interpretResponse(serverResponse, bidderRequest); - - it('should return Array', () => { - expect(Array.isArray(result)).to.equal(true); - }); - - it('should get the correct bid response', () => { - expect(result[0].cpm).to.equal(1500); - expect(result[0].creativeId).to.equal('1'); - expect(result[0].width).to.equal(320); - expect(result[0].height).to.equal(180); - expect(result[0].mediaType).to.equal('video'); - expect(result[0].netRevenue).to.equal(true); - expect(result[0].requestId).to.equal('209c1fb5ad88f5'); - expect(result[0].ttl).to.equal(3000); - expect(result[0].vastXml).to.equal(''); - }); - }); -}); diff --git a/test/spec/modules/c1xBidAdapter_spec.js b/test/spec/modules/c1xBidAdapter_spec.js deleted file mode 100644 index 00741abda7a..00000000000 --- a/test/spec/modules/c1xBidAdapter_spec.js +++ /dev/null @@ -1,182 +0,0 @@ -import { expect } from 'chai'; -import { c1xAdapter } from 'modules/c1xBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://ht.c1exchange.com/ht'; -const BIDDER_CODE = 'c1x'; - -describe('C1XAdapter', function () { - const adapter = newBidder(c1xAdapter); - - 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, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'params': { - 'siteId': '9999' - } - }; - - it('should return true when required params are passed', function () { - expect(c1xAdapter.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not found', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'siteId': null - }; - expect(c1xAdapter.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': BIDDER_CODE, - 'params': { - 'siteId': '9999' - }, - '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', function () { - const request = c1xAdapter.buildRequests(bidRequests); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('GET'); - }); - - it('should generate correct bid Id tag', function () { - const request = c1xAdapter.buildRequests(bidRequests); - 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', function () { - const request = c1xAdapter.buildRequests(bidRequests); - 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.site).to.equal('9999'); - }); - - it('should convert floor price to proper form and attach to request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - 'params': { - 'siteId': '9999', - 'floorPriceMap': { - '300x250': 4.35 - } - } - }); - const request = c1xAdapter.buildRequests([bidRequest]); - 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', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - 'params': { - 'siteId': '9999', - 'pageurl': 'https://c1exchange.com/' - } - }); - const request = c1xAdapter.buildRequests([bidRequest]); - const originalPayload = parseRequest(request.data); - const payloadObj = JSON.parse(originalPayload); - expect(payloadObj.pageurl).to.equal('https://c1exchange.com/'); - }); - - it('should convert GDPR Consent to proper form and attach to request', function () { - let consentString = 'BOP2gFWOQIFovABABAENBGAAAAAAMw'; - let bidderRequest = { - 'bidderCode': 'c1x', - 'gdprConsent': { - 'consentString': consentString, - 'gdprApplies': true - } - } - bidderRequest.bids = bidRequests; - - const request = c1xAdapter.buildRequests(bidRequests, bidderRequest); - 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', function () { - 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', function () { - 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', function () { - let response = { - bid: false, - adId: 'c1x-test' - }; - let bidderRequest = {}; - let result = c1xAdapter.interpretResponse({ body: [response] }, bidderRequest); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/cedatoBidAdapter_spec.js b/test/spec/modules/cedatoBidAdapter_spec.js deleted file mode 100644 index a7f4875afff..00000000000 --- a/test/spec/modules/cedatoBidAdapter_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/cedatoBidAdapter.js'; - -describe('the cedato adapter', function () { - function getValidBidObject() { - return { - bidId: '2f4a613a702b6c', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - params: { - player_id: 1450133326, - } - }; - }; - - describe('isBidRequestValid', function() { - var bid; - - beforeEach(function() { - bid = getValidBidObject(); - }); - - it('should fail validation if the bid isn\'t defined or not an object', function() { - var result = spec.isBidRequestValid(); - - expect(result).to.equal(false); - - result = spec.isBidRequestValid('not an object'); - - expect(result).to.equal(false); - }); - }); - - describe('buildRequests', function() { - var bid, bidRequestObj; - - beforeEach(function() { - bid = getValidBidObject(); - bidRequestObj = { - refererInfo: {referer: 'prebid.js'}, - gdprConsent: { - consentString: 'test-string', - gdprApplies: true - }, - uspConsent: '1NYN' - }; - }); - - it('should build a very basic request', function() { - var [request] = spec.buildRequests([bid], bidRequestObj); - expect(request.method).to.equal('POST'); - }); - - it('should pass gdpr and usp strings to server', function() { - var [request] = spec.buildRequests([bid], bidRequestObj); - var payload = JSON.parse(request.data); - expect(payload.gdpr_consent).to.not.be.undefined; - expect(payload.gdpr_consent.consent_string).to.equal(bidRequestObj.gdprConsent.consentString); - expect(payload.gdpr_consent.consent_required).to.equal(bidRequestObj.gdprConsent.gdprApplies); - expect(payload.us_privacy).to.equal(bidRequestObj.uspConsent); - }); - }); - - describe('interpretResponse', function() { - var bid, serverResponse, bidderRequest; - - beforeEach(function() { - bid = getValidBidObject(); - serverResponse = { - body: { - bidid: '0.36157306192821', - seatbid: [ - { - seat: '0', - bid: [{ - gp: { - 'negative': 0.496954, - 'positive': 0.503046, - 'class': '0' - }, - id: '0.75549202124378', - adomain: 'cedato.com', - uuid: bid.bidId, - crid: '1450133326', - adm: "
\n\n\n", - h: 250, - w: 300, - price: '0.1' - }] - } - ], - cur: 'USD' - } - }; - bidderRequest = { - bids: [bid] - }; - }); - - it('should return an array of bid responses', function() { - var responses = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(responses).to.be.an('array').with.length(1); - }); - }); - - describe('getUserSyncs', function() { - var bid; - - beforeEach(function() { - bid = getValidBidObject(); - }); - - it('should sync with iframe', function() { - var syncs = spec.getUserSyncs({ iframeEnabled: true }, null, { - consentString: '', - gdprApplies: true - }); - - expect(syncs).to.be.an('array').with.length(1); - expect(syncs[0].type).to.equal('iframe'); - }); - - it('should sync with image', function() { - var syncs = spec.getUserSyncs({ pixelEnabled: true }); - - expect(syncs).to.be.an('array').with.length(1); - expect(syncs[0].type).to.equal('image'); - }); - }); -}); diff --git a/test/spec/modules/cleanmedianetBidAdapter_spec.js b/test/spec/modules/cleanmedianetBidAdapter_spec.js index 5438f6c8701..c2eea6f32d7 100644 --- a/test/spec/modules/cleanmedianetBidAdapter_spec.js +++ b/test/spec/modules/cleanmedianetBidAdapter_spec.js @@ -31,22 +31,6 @@ describe('CleanmedianetAdapter', function () { ).to.equal(true); }); - it('should validate bid floor', function() { - expect( - spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) - ).to.equal(true); // bidfloor has a default - expect( - spec.isBidRequestValid({ - params: { supplyPartnerId: '123', bidfloor: '123' } - }) - ).to.equal(false); - expect( - spec.isBidRequestValid({ - params: { supplyPartnerId: '123', bidfloor: 0.1 } - }) - ).to.equal(true); - }); - it('should validate adpos', function() { expect( spec.isBidRequestValid({ params: { supplyPartnerId: '123' } }) @@ -159,15 +143,6 @@ describe('CleanmedianetAdapter', function () { expect(response.data.imp[0].instl).to.equal( bidRequestWithInstlEquals0.params.instl ); - const bidRequestWithBidfloorEquals1 = utils.deepClone(bidRequest); - bidRequestWithBidfloorEquals1.params.bidfloor = 1; - response = spec.buildRequests( - [bidRequestWithBidfloorEquals1], - bidRequest2 - )[0]; - expect(response.data.imp[0].bidfloor).to.equal( - bidRequestWithBidfloorEquals1.params.bidfloor - ); }); it('builds request banner object correctly', function() { diff --git a/test/spec/modules/clickforceBidAdapter_spec.js b/test/spec/modules/clickforceBidAdapter_spec.js deleted file mode 100644 index dad00f94641..00000000000 --- a/test/spec/modules/clickforceBidAdapter_spec.js +++ /dev/null @@ -1,190 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/clickforceBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('ClickforceAdapter', 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': 'clickforce', - 'params': { - 'zone': '6682' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'someIncorrectParam': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [{ - 'bidder': 'clickforce', - 'params': { - 'zone': '6682' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - }]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - }); - - describe('interpretResponse', function () { - let response = [{ - 'cpm': 0.5, - 'width': '300', - 'height': '250', - 'callback_uid': '220ed41385952a', - 'type': 'Default Ad', - 'tag': '', - 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', - 'requestId': '220ed41385952a', - 'currency': 'USD', - 'ttl': 60, - 'netRevenue': true, - 'zone': '6682' - }]; - - let response1 = [{ - 'cpm': 0.0625, - 'width': '3', - 'height': '3', - 'callback_uid': '2e27ec595bf1a', - 'type': 'public Bid', - 'tag': { - 'content': { - 'title': 'title', - 'content': 'content', - 'advertiser': 'advertiser', - 'button_text': 'button_text', - 'image': 'image', - 'icon': 'icon' - }, - 'cu': ['cu'], - 'iu': ['iu'], - 'p': '6878:11062:32586:8380573788dad9b9fc17edde444c4dcf:2795' - }, - 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', - 'requestId': '2e27ec595bf1a', - 'currency': 'USD', - 'ttl': 60, - 'netRevenue': true, - 'zone': '6878' - }]; - - let expectedResponse = [{ - 'requestId': '220ed41385952a', - 'cpm': 0.5, - 'width': '300', - 'height': '250', - 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 60, - 'ad': '', - 'mediaType': 'banner', - }]; - - let expectedResponse1 = [{ - 'requestId': '2e27ec595bf1a', - 'cpm': 0.0625, - 'width': '3', - 'height': '3', - 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 60, - 'mediaType': 'native', - 'native': { - 'image': { - 'url': 'image', - 'width': 1600, - 'height': 900 - }, - 'title': 'title', - 'sponsoredBy': 'advertiser', - 'body': 'content', - 'icon': { - 'url': 'icon', - 'width': 900, - 'height': 900 - }, - 'clickUrl': 'cu', - 'impressionTrackers': ['iu'] - } - }]; - - it('should get the correct bid response by display ad', function () { - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('should get the correct bid response by native ad', function () { - let bidderRequest; - let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse1[0])); - }); - - it('handles empty bid response', function () { - let response = { - body: {} - }; - let result = spec.interpretResponse(response); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs function', function () { - it('should register type is iframe', function () { - const syncOptions = { - 'iframeEnabled': 'true' - } - let userSync = spec.getUserSyncs(syncOptions); - expect(userSync[0].type).to.equal('iframe'); - expect(userSync[0].url).to.equal('https://cdn.holmesmind.com/js/capmapping.htm'); - }); - - it('should register type is image', function () { - const syncOptions = { - 'pixelEnabled': 'true' - } - let userSync = spec.getUserSyncs(syncOptions); - expect(userSync[0].type).to.equal('image'); - expect(userSync[0].url).to.equal('https://c.holmesmind.com/cm'); - }); - }); -}); diff --git a/test/spec/modules/clicktripzBidAdapter_spec.js b/test/spec/modules/clicktripzBidAdapter_spec.js deleted file mode 100644 index fed94811c4e..00000000000 --- a/test/spec/modules/clicktripzBidAdapter_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/clicktripzBidAdapter.js'; - -const ENDPOINT_URL = 'https://www.clicktripz.com/x/prebid/v1'; - -describe('clicktripzBidAdapter', function () { - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'clicktripz', - 'params': { - placementId: 'testPlacementId', - siteId: 'testSiteId' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '1234asdf1234', - 'bidderRequestId': '1234asdf1234asdf', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' - }; - - let bid2 = { - 'bidder': 'clicktripz', - 'params': { - placementId: 'testPlacementId' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '1234asdf1234', - 'bidderRequestId': '1234asdf1234asdf', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' - }; - - let bid3 = { - 'bidder': 'clicktripz', - 'params': { - siteId: 'testSiteId' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '1234asdf1234', - 'bidderRequestId': '1234asdf1234asdf', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are NOT found', function () { - expect(spec.isBidRequestValid(bid2)).to.equal(false); - expect(spec.isBidRequestValid(bid3)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let validBidRequests = [{ - 'bidder': 'clicktripz', - 'params': { - placementId: 'testPlacementId', - siteId: 'testSiteId' - }, - 'sizes': [ - [300, 250], - [300, 300] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }, { - 'bidder': 'clicktripz', - 'params': { - placementId: 'testPlacementId2', - siteId: 'testSiteId2' - }, - 'sizes': [ - [300, 250] - ], - 'bidId': '25beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }]; - - const request = spec.buildRequests(validBidRequests); - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('sends bid request to our endpoint at the correct URL', function () { - expect(request.url).to.equal(ENDPOINT_URL); - }); - it('sends bid request to our endpoint at the correct URL', function () { - expect(request.url).to.equal(ENDPOINT_URL); - }); - - it('transforms sizes into an array of strings. Pairs of concatenated sizes joined with an x', function () { - expect(request.data[0].sizes.toString()).to.equal('300x250,300x300'); - }); - it('transforms sizes into an array of strings. Pairs of concatenated sizes joined with an x', function () { - expect(request.data[1].sizes.toString()).to.equal('300x250'); - }); - - it('includes bidId, siteId, and placementId in payload', function () { - expect(request.data[0].bidId).to.equal('23beaa6af6cdde'); - expect(request.data[0].siteId).to.equal('testSiteId'); - expect(request.data[0].placementId).to.equal('testPlacementId'); - }); - it('includes bidId, siteId, and placementId in payload', function () { - expect(request.data[1].bidId).to.equal('25beaa6af6cdde'); - expect(request.data[1].siteId).to.equal('testSiteId2'); - expect(request.data[1].placementId).to.equal('testPlacementId2'); - }); - }); - - describe('interpretResponse', function () { - let serverResponse = { - body: [{ - 'bidId': 'bid-request-id', - 'ttl': 120, - 'netRevenue': true, - 'size': '300x200', - 'currency': 'USD', - 'adUrl': 'https://www.clicktripz.com/n3/crane/v0/render?t=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoiaHR0cHM6XC9cL3d3dy5jbGlja3RyaXB6LmNvbVwvY2xpY2sucGhwP2NhbXBhaWduSUQ9MTkxNTYmcHJlQ2hlY2tlZD0xJnB1Ymxpc2hlcklEPTM2MCZzZWFyY2hLZXk9N2M5MzQ0NzhlM2M1NTc3Y2EyN2ZmN2Y1NTg5N2NkMzkmc2VhcmNoRGlzcGxheVR5cGU9MSZkaXNwbGF5VHlwZT00JmNyZWF0aXZlVHlwZT1zaW5nbGUmaXNQb3BVbmRlcj0wJnBvc2l0aW9uPTEmdHlwZT0xJmNpdHk9TWFkcmlkJTJDK1NwYWluJmNoZWNrSW5EYXRlPTAzJTJGMDElMkYyMDIwJmNoZWNrT3V0RGF0ZT0wMyUyRjA1JTJGMjAyMCZndWVzdHM9MiZyb29tcz0xIn0.WBDGYr1qfkSvOuK02VpMW3iAua1E02jjDGDViFc2kaE', - 'creativeId': '25ef9876abc5681f153', - 'cpm': 50 - }] - }; - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': 'bid-request-id', - 'cpm': 50, - 'netRevenue': true, - 'width': '300', - 'height': '200', - 'creativeId': '25ef9876abc5681f153', - 'currency': 'USD', - 'adUrl': 'https://www.clicktripz.com/n3/crane/v0/render?t=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoiaHR0cHM6XC9cL3d3dy5jbGlja3RyaXB6LmNvbVwvY2xpY2sucGhwP2NhbXBhaWduSUQ9MTkxNTYmcHJlQ2hlY2tlZD0xJnB1Ymxpc2hlcklEPTM2MCZzZWFyY2hLZXk9N2M5MzQ0NzhlM2M1NTc3Y2EyN2ZmN2Y1NTg5N2NkMzkmc2VhcmNoRGlzcGxheVR5cGU9MSZkaXNwbGF5VHlwZT00JmNyZWF0aXZlVHlwZT1zaW5nbGUmaXNQb3BVbmRlcj0wJnBvc2l0aW9uPTEmdHlwZT0xJmNpdHk9TWFkcmlkJTJDK1NwYWluJmNoZWNrSW5EYXRlPTAzJTJGMDElMkYyMDIwJmNoZWNrT3V0RGF0ZT0wMyUyRjA1JTJGMjAyMCZndWVzdHM9MiZyb29tcz0xIn0.WBDGYr1qfkSvOuK02VpMW3iAua1E02jjDGDViFc2kaE', - 'ttl': 120 - }]; - let result = spec.interpretResponse(serverResponse); - expect(result).to.deep.equal(expectedResponse); - }); - }); -}); diff --git a/test/spec/modules/collectcentBidAdapter_spec.js b/test/spec/modules/collectcentBidAdapter_spec.js deleted file mode 100644 index 0ab83a8024b..00000000000 --- a/test/spec/modules/collectcentBidAdapter_spec.js +++ /dev/null @@ -1,118 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/collectcentBidAdapter.js'; - -describe('Collectcent', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'collectcent', - bidderRequestId: '145e1d6a7837c9', - params: { - placementId: 123, - traffic: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - }; - - describe('isBidRequestValid', function () { - it('Should return true when placementId can be cast to a number', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when placementId is not a number', function () { - bid.params.placementId = 'aaa'; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid]); - 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://publishers.motionspots.com/?c=o&m=multi'); - }); - 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', 'secure', 'host', 'page', 'placements'); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.secure).to.be.within(0, 1); - expect(data.host).to.be.a('string'); - expect(data.page).to.be.a('string'); - let placements = data['placements']; - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.be.a('number'); - expect(placement.bidId).to.be.a('string'); - expect(placement.traffic).to.be.a('string'); - expect(placement.sizes).to.be.an('array'); - } - }); - 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 () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - - describe('getUserSyncs', function () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and `', 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('image'); - expect(userSync[0].url).to.be.equal('https://publishers.motionspots.com/?c=o&m=cookie'); - }); - }); -}); diff --git a/test/spec/modules/colombiaBidAdapter_spec.js b/test/spec/modules/colombiaBidAdapter_spec.js deleted file mode 100644 index 4e80c6b1d9d..00000000000 --- a/test/spec/modules/colombiaBidAdapter_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/colombiaBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory'; - -const HOST_NAME = document.location.protocol + '//' + window.location.host; -const ENDPOINT = 'https://ade.clmbtech.com/cde/prebid.htm'; - -describe('colombiaBidAdapter', function() { - const adapter = newBidder(spec); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'colombia', - 'params': { - placementId: '307466' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when placementId not passed correctly', function () { - bid.params.placementId = ''; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when require params are not passed', function () { - let bid = Object.assign({}, bid); - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'colombia', - 'params': { - placementId: '307466' - }, - 'adUnitCode': 'adunit-code1', - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }, - { - 'bidder': 'colombia', - 'params': { - placementId: '307466' - }, - 'adUnitCode': 'adunit-code2', - 'sizes': [ - [300, 250] - ], - 'bidId': '382091349b149f"', - 'bidderRequestId': '"1f9c98192de251"', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - } - ]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via POST', function () { - expect(request[0].method).to.equal('POST'); - expect(request[1].method).to.equal('POST'); - }); - - it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.equal(ENDPOINT); - expect(request[1].url).to.equal(ENDPOINT); - }); - }); - - describe('interpretResponse', function () { - let bidRequest = [ - { - 'method': 'POST', - 'url': 'https://ade.clmbtech.com/cde/prebid.htm', - 'data': { - 'v': 'hb1', - 'p': '307466', - 'w': '300', - 'h': '250', - 'cb': 12892917383, - 'r': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', - 'uid': '23beaa6af6cdde', - 't': 'i', - } - } - ]; - - let serverResponse = { - body: { - 'ad': '
This is test case for colombia adapter
', - 'cpm': 3.14, - 'creativeId': '6b958110-612c-4b03-b6a9-7436c9f746dc-1sk24', - 'currency': 'USD', - 'uid': '23beaa6af6cdde', - 'width': 728, - 'height': 90, - 'netRevenue': true, - 'ttl': 600, - 'dealid': '', - 'referrer': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836' - } - }; - - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': '23beaa6af6cdde', - 'cpm': 3.14, - 'width': 728, - 'height': 90, - 'creativeId': '6b958110-612c-4b03-b6a9-7436c9f746dc-1sk24', - 'dealId': '', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'referrer': 'http%3A%2F%2Flocalhost%3A9876%2F%3Fid%3D74552836', - 'ad': '
This is test case for colombia adapter
' - }]; - let result = spec.interpretResponse(serverResponse, bidRequest[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('handles empty bid response', function () { - let response = { - body: { - 'uid': '23beaa6af6cdde', - 'height': 0, - 'creativeId': '', - 'statusMessage': 'Bid returned empty or error response', - 'width': 0, - 'cpm': 0 - } - }; - let result = spec.interpretResponse(response, bidRequest[0]); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js deleted file mode 100644 index f6e24d07c63..00000000000 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ /dev/null @@ -1,185 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/colossussspBidAdapter.js'; - -describe('ColossussspAdapter', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'colossusssp', - bidderRequestId: '145e1d6a7837c9', - params: { - placement_id: 0 - }, - placementCode: 'placementid_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - // name: 'alladsallthetime', - domain: 'example.com' - } - ] - } - }; - let bidderRequest = { - bidderCode: 'colossus', - auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', - bidderRequestId: 'ffffffffffffff', - start: 1472239426002, - auctionStart: 1472239426000, - timeout: 5000, - uspConsent: '1YN-', - refererInfo: { - referer: 'http://www.example.com', - reachedTop: true, - }, - bids: [bid] - } - - describe('isBidRequestValid', function () { - it('Should return true when placement_id can be cast to a number', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when placement_id is not a number', function () { - bid.params.placement_id = 'aaa'; - 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://colossusssp.com/?c=o&m=multi'); - }); - it('Should contain ccpa', function() { - expect(serverRequest.data.ccpa).to.be.an('string') - }) - - 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'); - 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'); - let placements = data['placements']; - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'floor'); - expect(placement.schain).to.be.an('object') - expect(placement.placementId).to.be.a('number'); - expect(placement.bidId).to.be.a('string'); - expect(placement.traffic).to.be.a('string'); - expect(placement.sizes).to.be.an('array'); - expect(placement.floor).to.be.an('object'); - } - }); - 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('buildRequests with user ids', function () { - bid.userId = {} - bid.userId.britepoolid = 'britepoolid123'; - bid.userId.idl_env = 'idl_env123'; - bid.userId.tdid = 'tdid123'; - bid.userId.id5id = { uid: 'id5id123' }; - let serverRequest = spec.buildRequests([bid], bidderRequest); - it('Returns valid data if array of bids is valid', function () { - let data = serverRequest.data; - let placements = data['placements']; - expect(data).to.be.an('object'); - for (let i = 0; i < placements.length; i++) { - let placement = placements[i]; - expect(placement).to.have.property('eids') - expect(placement.eids).to.be.an('array') - expect(placement.eids.length).to.be.equal(4) - for (let index in placement.eids) { - let v = placement.eids[index]; - expect(v).to.have.all.keys('source', 'uids') - expect(v.source).to.be.oneOf(['britepool.com', 'identityLink', 'adserver.org', 'id5-sync.com']) - expect(v.uids).to.be.an('array'); - expect(v.uids.length).to.be.equal(1) - expect(v.uids[0]).to.have.property('id') - } - } - }); - }); - - describe('interpretResponse', function () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - - describe('getUserSyncs', function () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://colossusssp.com/?c=o&m=cookie'); - }); - }); -}); diff --git a/test/spec/modules/convergeBidAdapter_spec.js b/test/spec/modules/convergeBidAdapter_spec.js deleted file mode 100644 index e92ed475497..00000000000 --- a/test/spec/modules/convergeBidAdapter_spec.js +++ /dev/null @@ -1,899 +0,0 @@ -import { expect } from 'chai'; -import { spec, resetUserSync, getSyncUrl } from 'modules/convergeBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('ConvergeAdapter', 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': 'converge', - 'params': { - 'uid': '1' - }, - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'uid': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - function parseRequest(url) { - const res = {}; - url.split('&').forEach((it) => { - const couple = it.split('='); - res[couple[0]] = decodeURIComponent(couple[1]); - }); - return res; - } - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - const referrer = bidderRequest.refererInfo.referer; - - let bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90], [300, 250]], - 'bidId': '3150ccb55da321', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '42dbe3a7168a6a', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '59'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - }); - - it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '59,59,60'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('pt parameter must be "gross" if params.priceType === "gross"', function () { - bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'gross'); - expect(payload).to.have.property('auids', '59,59,60'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - delete bidRequests[1].params.priceType; - }); - - it('pt parameter must be "net" or "gross"', function () { - bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '59,59,60'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - delete bidRequests[1].params.priceType; - }); - - it('if gdprConsent is present payload must have gdpr params', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if gdprApplies is false gdpr_applies must be 0', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '0'); - }); - - it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('us_privacy', '1YNN'); - }); - - it('should convert keyword params to proper form and attaches to request', function () { - const bidRequestWithKeywords = [].concat(bidRequests); - bidRequestWithKeywords[1] = Object.assign({}, - bidRequests[1], - { - params: { - uid: '59', - keywords: { - single: 'val', - singleArr: ['val'], - singleArrNum: [5], - multiValMixed: ['value1', 2, 'value3'], - singleValNum: 123, - emptyStr: '', - emptyArr: [''], - badValue: {'foo': 'bar'} // should be dropped - } - } - } - ); - - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.keywords).to.be.an('string'); - payload.keywords = JSON.parse(payload.keywords); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['5'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); - }); - }); - - describe('interpretResponse', function () { - const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 59, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 60, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 59, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 61, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, - undefined, - {'bid': [], 'seat': '1'}, - {'seat': '1'}, - ]; - - it('should get correct bid response', function () { - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', - 'bidderRequestId': '5f2009617a7c0a', - 'auctionId': '1cbd2feafe5e8b', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '659423fff799cb', - 'cpm': 1.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('should get correct multi bid response', function () { - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71a5b', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4dff80cc4ee346', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '5703af74d0472a', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '300bfeb0d71a5b', - 'cpm': 1.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 60, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 2
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '5703af74d0472a', - 'cpm': 0.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 728, - 'height': 90, - 'ad': '
test content 3
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('handles wrong and nobid responses', function () { - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '61' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d7190gf', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '65' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71321', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '70' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '300bfeb0d7183bb', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - } - ]; - const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); - expect(result.length).to.equal(0); - }); - - it('complicated case', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 59, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 60, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 59, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 59, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 60, 'h': 600, 'w': 350}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2164be6358b9', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '326bde7fbf69', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4e111f1b66e4', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '26d6f897b516', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '1751cd90161', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '2164be6358b9', - 'cpm': 1.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '4e111f1b66e4', - 'cpm': 0.5, - 'creativeId': 60, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 2
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '26d6f897b516', - 'cpm': 0.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 728, - 'height': 90, - 'ad': '
test content 3
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '326bde7fbf69', - 'cpm': 0.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 4
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('dublicate uids and sizes in one slot', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 59, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 59, 'h': 250, 'w': 300}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '5126e301f4be', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57b2ebe70e16', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '59' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '225fcd44b18c', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '5126e301f4be', - 'cpm': 1.15, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '57b2ebe70e16', - 'cpm': 0.5, - 'creativeId': 59, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 2
', - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - }); - - it('should get correct video bid response', function () { - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '58' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57dfefb80eca', - 'bidderRequestId': '20394420a762a2', - 'auctionId': '140132d07b031', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'e893c787c22dd', - 'bidderRequestId': '20394420a762a2', - 'auctionId': '140132d07b031', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - } - ]; - const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 58, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 60, content_type: 'video'}], 'seat': '2'} - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '57dfefb80eca', - 'cpm': 1.15, - 'creativeId': 58, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': response}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('should have right renderer in the bid response', function () { - const spySetRenderer = sinon.spy(); - const stubRenderer = { - setRender: spySetRenderer - }; - const spyRendererInstall = sinon.spy(function() { return stubRenderer; }); - const stubRendererConst = { - install: spyRendererInstall - }; - const bidRequests = [ - { - 'bidder': 'converge', - 'params': { - 'uid': '58' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'e6e65553fc8', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3', - 'mediaTypes': { - 'video': { - 'context': 'outstream' - } - } - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '60' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c8fdcb3f269f', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3' - }, - { - 'bidder': 'converge', - 'params': { - 'uid': '61' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '1de036c37685', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3', - 'renderer': {} - } - ]; - const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 58, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 60, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, - {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 61, content_type: 'video', w: 300, h: 250}], 'seat': '2'} - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': 'e6e65553fc8', - 'cpm': 1.15, - 'creativeId': 58, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - }, - 'renderer': stubRenderer - }, - { - 'requestId': 'c8fdcb3f269f', - 'cpm': 1.00, - 'creativeId': 60, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - }, - 'renderer': stubRenderer - }, - { - 'requestId': '1de036c37685', - 'cpm': 1.20, - 'creativeId': 61, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'bidderCode': 'converge', - 'currency': 'EUR', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': response}}, request, stubRendererConst); - - expect(spySetRenderer.calledTwice).to.equal(true); - expect(spySetRenderer.getCall(0).args[0]).to.be.a('function'); - expect(spySetRenderer.getCall(1).args[0]).to.be.a('function'); - - expect(spyRendererInstall.calledTwice).to.equal(true); - expect(spyRendererInstall.getCall(0).args[0]).to.deep.equal({ - id: 'e6e65553fc8', - url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', - loaded: false - }); - expect(spyRendererInstall.getCall(1).args[0]).to.deep.equal({ - id: 'c8fdcb3f269f', - url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', - loaded: false - }); - - expect(result).to.deep.equal(expectedResponse); - }); - - describe('user sync', function () { - const syncUrl = getSyncUrl(); - - beforeEach(function () { - resetUserSync(); - }); - - it('should register sync image', function () { - let syncs = spec.getUserSyncs({ - pixelEnabled: true - }); - - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); - }); - - it('should not register sync image more than once', function () { - let syncs = spec.getUserSyncs({ - pixelEnabled: true - }); - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); - - // when called again, should still have only been called once - syncs = spec.getUserSyncs(); - expect(syncs).to.equal(undefined); - }); - - it('should pass gdpr params if consent is true', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - gdprApplies: true, consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr=1&gdpr_consent=foo` - }); - }); - - it('should pass gdpr params if consent is false', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - gdprApplies: false, consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr=0&gdpr_consent=foo` - }); - }); - - it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr_consent=foo` - }); - }); - - it('should pass no params if gdpr consentString is not defined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {})).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is a number', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: 0 - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is null', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: null - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is a object', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: {} - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr is not defined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined)).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass usPrivacy param if it is available', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {}, '1YNN')).to.deep.equal({ - type: 'image', url: `${syncUrl}&us_privacy=1YNN` - }); - }); - }); -}); diff --git a/test/spec/modules/cosmosBidAdapter_spec.js b/test/spec/modules/cosmosBidAdapter_spec.js deleted file mode 100644 index b33f53221e2..00000000000 --- a/test/spec/modules/cosmosBidAdapter_spec.js +++ /dev/null @@ -1,355 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/cosmosBidAdapter.js'; -import * as utils from 'src/utils.js'; -const constants = require('src/constants.json'); - -describe('Cosmos adapter', function () { - let bannerBidRequests; - let bannerBidResponse; - let videoBidRequests; - let videoBidResponse; - - beforeEach(function () { - bannerBidRequests = [ - { - bidder: 'cosmos', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - params: { - publisherId: '1001', - currency: 'USD', - geo: { - lat: '09.5', - lon: '21.2', - } - }, - bidId: '29f8bd96defe76' - } - ]; - - videoBidRequests = - [ - { - mediaTypes: { - video: { - mimes: ['video/mp4', 'video/x-flv'], - context: 'instream' - } - }, - bidder: 'cosmos', - params: { - publisherId: 1001, - video: { - skippable: true, - minduration: 5, - maxduration: 30 - } - }, - bidId: '39f5cc6eff9b37' - } - ]; - - bannerBidResponse = { - 'body': { - 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', - 'seatbid': [{ - 'bid': [{ - 'id': '82DAAE22-FF66-4FAB-84AB-347B0C5CD02C', - 'impid': '29f8bd96defe76', - 'price': 1.858309, - 'adm': '

COSMOS\"Connecting Advertisers and Publishers directly\"

', - 'adid': 'v55jutrh', - 'adomain': ['febreze.com'], - 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', - 'cid': '1234', - 'crid': 'v55jutrh', - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'type': 'banner' - } - } - }], - 'seat': 'zeta' - }] - } - }; - - videoBidResponse = { - 'body': { - 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', - 'seatbid': [{ - 'bid': [{ - 'id': '82DAAE22-FF66-4FAB-84AB-347B0C5CD02C', - 'impid': '39f5cc6eff9b37', - 'price': 0.858309, - 'adm': 'CosmosHQVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://track.cosmoshq.com/event?data=%7B%22id%22%3A%221566011421045%22%2C%22bid%22%3A%2282DAAE22-FF66-4FAB-84AB-347B0C5CD02C%22%2C%22ts%22%3A%2220190817031021%22%2C%22pid%22%3A1001%2C%22plcid%22%3A1%2C%22aid%22%3A1%2C%22did%22%3A1%2C%22cid%22%3A%2222918%22%2C%22af%22%3A3%2C%22at%22%3A1%2C%22w%22%3A300%2C%22h%22%3A250%2C%22crid%22%3A%22v55jutrh%22%2C%22pp%22%3A0.858309%2C%22cp%22%3A0.858309%2C%22mg%22%3A0%7D&type=1https//track.dsp.impression.com/impression00:00:60https//sync.cosmoshq.com/static/video/SampleVideo_1280x720_10mb.mp4', - 'adid': 'v55jutrh', - 'adomain': ['febreze.com'], - 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', - 'cid': '1234', - 'crid': 'v55jutrh', - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'type': 'video' - } - } - }], - 'seat': 'zeta' - }] - } - }; - }); - - describe('isBidRequestValid', function () { - describe('validate the bid object: valid bid', function () { - it('valid bid case', function () { - let validBid = { - bidder: 'cosmos', - params: { - publisherId: 1001, - tagId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - - it('validate the bid object: nil/empty bid object', function () { - let validBid = { - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('validate the bid object: publisherId not passed', function () { - let validBid = { - bidder: 'cosmos', - params: { - tagId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('validate the bid object: publisherId is not number', function () { - let validBid = { - bidder: 'cosmos', - params: { - publisherId: '301', - tagId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('validate the bid object: mimes absent', function () { - let validBid = { - bidder: 'cosmos', - mediaTypes: { - video: {} - }, - params: { - publisherId: 1001 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('validate the bid object: mimes present', function () { - let validBid = { - bidder: 'cosmos', - mediaTypes: { - video: { - mimes: ['video/mp4', 'application/javascript'] - } - }, - params: { - publisherId: 1001 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - - it('validate the bid object: tagId is not passed', function () { - let validBid = { - bidder: 'cosmos', - params: { - publisherId: 1001 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - }); - - describe('buildRequests', function () { - it('build request object: buildRequests function should not modify original bannerBidRequests object', function () { - let originalBidRequests = utils.deepClone(bannerBidRequests); - let request = spec.buildRequests(bannerBidRequests); - expect(bannerBidRequests).to.deep.equal(originalBidRequests); - }); - - it('build request object: endpoint check', function () { - let request = spec.buildRequests(bannerBidRequests); - expect(request[0].url).to.equal('https://bid.cosmoshq.com/openrtb2/bids'); - expect(request[0].method).to.equal('POST'); - }); - - it('build request object: request params check', function () { - let request = spec.buildRequests(bannerBidRequests); - let data = JSON.parse(request[0].data); - expect(data.site.publisher.id).to.equal(bannerBidRequests[0].params.publisherId); // publisher Id - expect(data.imp[0].bidfloorcur).to.equal(bannerBidRequests[0].params.currency); - }); - - it('build request object: request params check without tagId', function () { - delete bannerBidRequests[0].params.tagId; - let request = spec.buildRequests(bannerBidRequests); - let data = JSON.parse(request[0].data); - expect(data.site.publisher.id).to.equal(bannerBidRequests[0].params.publisherId); // publisher Id - expect(data.imp[0].tagid).to.equal(undefined); // tagid - expect(data.imp[0].bidfloorcur).to.equal(bannerBidRequests[0].params.currency); - }); - - it('build request object: request params multi size format object check', function () { - let bidRequest = [ - { - bidder: 'cosmos', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - params: { - publisherId: 1001, - currency: 'USD' - } - } - ]; - /* case 1 - size passed in adslot */ - let request = spec.buildRequests(bidRequest); - let data = JSON.parse(request[0].data); - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(250); // height - - /* case 2 - size passed in adslot as well as in sizes array */ - bidRequest[0].sizes = [[300, 600], [300, 250]]; - bidRequest[0].mediaTypes = { - banner: { - sizes: [[300, 600], [300, 250]] - } - }; - request = spec.buildRequests(bidRequest); - data = JSON.parse(request[0].data); - - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(600); // height - - /* case 3 - size passed in sizes but not in adslot */ - bidRequest[0].params.tagId = 1; - bidRequest[0].sizes = [[300, 250], [300, 600]]; - bidRequest[0].mediaTypes = { - banner: { - sizes: [[300, 250], [300, 600]] - } - }; - request = spec.buildRequests(bidRequest); - data = JSON.parse(request[0].data); - - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].banner.format).exist.and.to.be.an('array'); - expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); - expect(data.imp[0].banner.format[0].w).to.equal(300); // width - expect(data.imp[0].banner.format[0].h).to.equal(250); // height - }); - - it('build request object: request params currency check', function () { - let bidRequest = [ - { - bidder: 'cosmos', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - params: { - publisherId: 1001, - tagId: 1, - currency: 'USD' - }, - sizes: [[300, 250], [300, 600]] - } - ]; - - /* case 1 - - currency specified in adunits - output: imp[0] use currency specified in bannerBidRequests[0].params.currency - - */ - let request = spec.buildRequests(bidRequest); - let data = JSON.parse(request[0].data); - expect(data.imp[0].bidfloorcur).to.equal(bidRequest[0].params.currency); - - /* case 2 - - currency specified in adunit - output: imp[0] use default currency - USD - - */ - delete bidRequest[0].params.currency; - request = spec.buildRequests(bidRequest); - data = JSON.parse(request[0].data); - expect(data.imp[0].bidfloorcur).to.equal('USD'); - }); - - it('build request object: request params check for video ad', function () { - let request = spec.buildRequests(videoBidRequests); - let data = JSON.parse(request[0].data); - expect(data.imp[0].video).to.exist; - expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].mediaTypes.video['mimes'][0]); - expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].mediaTypes.video['mimes'][1]); - expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); - expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); - }); - - describe('interpretResponse', function () { - it('check for banner response', function () { - let request = spec.buildRequests(bannerBidRequests); - let data = JSON.parse(request[0].data); - let response = spec.interpretResponse(bannerBidResponse, request[0]); - expect(response).to.be.an('array').with.length.above(0); - expect(response[0].requestId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].impid); - expect(response[0].cpm).to.equal((bannerBidResponse.body.seatbid[0].bid[0].price).toFixed(2)); - expect(response[0].width).to.equal(bannerBidResponse.body.seatbid[0].bid[0].w); - expect(response[0].height).to.equal(bannerBidResponse.body.seatbid[0].bid[0].h); - if (bannerBidResponse.body.seatbid[0].bid[0].crid) { - expect(response[0].creativeId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].crid); - } else { - expect(response[0].creativeId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].id); - } - expect(response[0].dealId).to.equal(bannerBidResponse.body.seatbid[0].bid[0].dealid); - expect(response[0].currency).to.equal('USD'); - expect(response[0].netRevenue).to.equal(false); - expect(response[0].ttl).to.equal(300); - }); - it('check for video response', function () { - let request = spec.buildRequests(videoBidRequests); - let data = JSON.parse(request[0].data); - let response = spec.interpretResponse(videoBidResponse, request[0]); - }); - }); - }); - }); -}); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js deleted file mode 100644 index 285fca9690a..00000000000 --- a/test/spec/modules/cpmstarBidAdapter_spec.js +++ /dev/null @@ -1,231 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/cpmstarBidAdapter.js'; -import { deepClone } from 'src/utils.js'; -import { config } from 'src/config.js'; - -const valid_bid_requests = [{ - 'bidder': 'cpmstar', - 'params': { - 'placementId': '57' - }, - 'sizes': [[300, 250]], - 'bidId': 'bidId' -}]; - -const bidderRequest = { - refererInfo: { - referer: 'referer', - reachedTop: false, - } -}; - -const serverResponse = { - body: [{ - creatives: [{ - cpm: 1, - width: 0, - height: 0, - currency: 'USD', - netRevenue: true, - ttl: 1, - creativeid: '1234', - requestid: '11123', - code: 'no idea', - media: 'banner', - } - ], - syncs: [{ type: 'image', url: 'https://server.cpmstar.com/pixel.aspx' }] - }] -}; - -describe('Cpmstar Bid Adapter', function () { - describe('isBidRequestValid', function () { - it('should return true since the bid is valid', - function () { - var bid = { params: { placementId: 123456 } }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }) - - it('should return false since the bid is invalid', function () { - var bid = { params: { placementId: '' } }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }) - - it('should return a valid player size', function () { - var bid = { - mediaTypes: { - video: { - playerSize: [[960, 540]] - } - } - } - expect(spec.getPlayerSize(bid)[0]).to.equal(960); - expect(spec.getPlayerSize(bid)[1]).to.equal(540); - }) - - it('should return a default player size', function () { - var bid = { - mediaTypes: { - video: { - playerSize: null - } - } - } - expect(spec.getPlayerSize(bid)[0]).to.equal(640); - expect(spec.getPlayerSize(bid)[1]).to.equal(440); - }) - }); - - describe('buildRequests', function () { - it('should produce a valid production request', function () { - var requests = spec.buildRequests(valid_bid_requests, bidderRequest); - expect(requests[0]).to.have.property('method'); - expect(requests[0]).to.have.property('url'); - expect(requests[0]).to.have.property('bidRequest'); - expect(requests[0].url).to.include('https://server.cpmstar.com/view.aspx'); - }); - it('should produce a valid staging request', function () { - var stgReq = deepClone(valid_bid_requests); - stgReq[0].params.endpoint = 'staging'; - var requests = spec.buildRequests(stgReq, bidderRequest); - expect(requests[0]).to.have.property('method'); - expect(requests[0]).to.have.property('url'); - expect(requests[0]).to.have.property('bidRequest'); - expect(requests[0].url).to.include('https://staging.server.cpmstar.com/view.aspx'); - }); - it('should produce a valid dev request', function () { - var devReq = deepClone(valid_bid_requests); - devReq[0].params.endpoint = 'dev'; - var requests = spec.buildRequests(devReq, bidderRequest); - expect(requests[0]).to.have.property('method'); - expect(requests[0]).to.have.property('url'); - expect(requests[0]).to.have.property('bidRequest'); - expect(requests[0].url).to.include('https://dev.server.cpmstar.com/view.aspx'); - }); - it('should produce a request with support for GDPR', function () { - var gdpr_bidderRequest = deepClone(bidderRequest); - gdpr_bidderRequest.gdprConsent = { - consentString: 'consentString', - gdprApplies: true - }; - var requests = spec.buildRequests(valid_bid_requests, gdpr_bidderRequest); - expect(requests[0]).to.have.property('url'); - expect(requests[0].url).to.include('gdpr_consent=consentString'); - expect(requests[0].url).to.include('gdpr=1'); - }); - it('should produce a request with support for USP', function () { - var usp_bidderRequest = deepClone(bidderRequest); - usp_bidderRequest.uspConsent = '1YYY'; - var requests = spec.buildRequests(valid_bid_requests, usp_bidderRequest); - expect(requests[0]).to.have.property('url'); - expect(requests[0].url).to.include('us_privacy=1YYY'); - }); - it('should produce a request with support for COPPA', function () { - sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); - var requests = spec.buildRequests(valid_bid_requests, bidderRequest); - config.getConfig.restore(); - expect(requests[0]).to.have.property('url'); - expect(requests[0].url).to.include('tfcd=1'); - }); - }); - - it('should produce a request with support for OpenRTB SupplyChain', function () { - var reqs = deepClone(valid_bid_requests); - reqs[0].schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1 - }, - { - 'asi': 'exchange2.com', - 'sid': 'abcd', - 'hp': 1 - } - ] - }; - var requests = spec.buildRequests(reqs, bidderRequest); - expect(requests[0]).to.have.property('url'); - expect(requests[0].url).to.include('&schain=1.0,1!exchange1.com,1234,1,,,!exchange2.com,abcd,1,,,'); - }); - - describe('interpretResponse', function () { - const request = { - bidRequest: { - mediaType: 'BANNER' - } - }; - - it('should return a valid bidresponse array', function () { - var r = spec.interpretResponse(serverResponse, request) - var c = serverResponse.body[0].creatives[0]; - expect(r[0].length).to.not.equal(0); - expect(r[0].requestId).equal(c.requestid); - expect(r[0].creativeId).equal(c.creativeid); - expect(r[0].cpm).equal(c.cpm); - expect(r[0].width).equal(c.width); - expect(r[0].height).equal(c.height); - expect(r[0].currency).equal(c.currency); - expect(r[0].netRevenue).equal(c.netRevenue); - expect(r[0].ttl).equal(c.ttl); - expect(r[0].ad).equal(c.code); - }); - - it('should return a valid bidresponse array from a non-array-body', function () { - var r = spec.interpretResponse({ body: serverResponse.body[0] }, request) - var c = serverResponse.body[0].creatives[0]; - expect(r[0].length).to.not.equal(0); - expect(r[0].requestId).equal(c.requestid); - expect(r[0].creativeId).equal(c.creativeid); - expect(r[0].cpm).equal(c.cpm); - expect(r[0].width).equal(c.width); - expect(r[0].height).equal(c.height); - expect(r[0].currency).equal(c.currency); - expect(r[0].netRevenue).equal(c.netRevenue); - expect(r[0].ttl).equal(c.ttl); - expect(r[0].ad).equal(c.code); - }); - - it('should return undefined due to an invalid cpm value', function () { - var badServer = deepClone(serverResponse); - badServer.body[0].creatives[0].cpm = 0; - var c = spec.interpretResponse(badServer, request); - expect(c).to.be.undefined; - }); - - it('should return undefined due to a bad response', function () { - var badServer = deepClone(serverResponse); - badServer.body[0].creatives[0].code = null; - var c = spec.interpretResponse(badServer, request); - expect(c).to.be.undefined; - }); - - it('should return a valid response with a dealId', function () { - var dealServer = deepClone(serverResponse); - dealServer.body[0].creatives[0].dealId = 'deal'; - expect(spec.interpretResponse(dealServer, request)[0].dealId).to.equal('deal'); - }); - }); - - describe('getUserSyncs', function () { - var sres = [deepClone(serverResponse)]; - - it('should return a valid pixel sync', function () { - var syncs = spec.getUserSyncs({ pixelEnabled: true }, sres); - expect(syncs.length).equal(1); - expect(syncs[0].type).equal('image'); - expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); - }); - - it('should return a valid iframe sync', function () { - sres[0].body[0].syncs[0].type = 'iframe'; - var syncs = spec.getUserSyncs({ iframeEnabled: true }, sres); - expect(syncs.length).equal(1); - expect(syncs[0].type).equal('iframe'); - expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); - }); - }); -}); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index cad1e3f8114..550ead83380 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1,5 +1,11 @@ import { expect } from 'chai'; -import { tryGetCriteoFastBid, spec, PROFILE_ID_PUBLISHERTAG, ADAPTER_VERSION } from 'modules/criteoBidAdapter.js'; +import { + tryGetCriteoFastBid, + spec, + PROFILE_ID_PUBLISHERTAG, + ADAPTER_VERSION, + canFastBid, getFastBidUrl, FAST_BID_VERSION_CURRENT +} from 'modules/criteoBidAdapter.js'; import { createBid } from 'src/bidfactory.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; @@ -66,7 +72,7 @@ describe('The Criteo bidding adapter', function () { expect(isValid).to.equal(true); }); - it('should return true when given a valid video bid request', function () { + it('should return true when given a valid video bid request using mix custom bidder video parameters', function () { expect(spec.isBidRequestValid({ bidder: 'criteo', mediaTypes: { @@ -112,6 +118,30 @@ describe('The Criteo bidding adapter', function () { })).to.equal(true); }); + it('should return true when given a valid video bid request using only mediaTypes.video parameters', function () { + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2], + skip: 1, + placement: 1, + minduration: 0, + playbackmethod: 1, + startdelay: 0 + } + }, + params: { + networkId: 456 + }, + })).to.equal(true); + }); + it('should return false when given an invalid video bid request', function () { expect(spec.isBidRequestValid({ bidder: 'criteo', @@ -896,6 +926,7 @@ describe('The Criteo bidding adapter', function () { width: 728, height: 90, dealCode: 'myDealCode', + adomain: ['criteo.com'], }], }, }; @@ -916,6 +947,7 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].width).to.equal(728); expect(bids[0].height).to.equal(90); expect(bids[0].dealId).to.equal('myDealCode'); + expect(bids[0].meta.advertiserDomains[0]).to.equal('criteo.com'); }); it('should properly parse a bid response with a zoneId', function () { @@ -1189,6 +1221,34 @@ describe('The Criteo bidding adapter', function () { }); }); + describe('canFastBid', function () { + it('should properly detect if can do fastbid', function () { + const testCasesAndExpectedResult = [['none', false], ['', true], [undefined, true], [123, true]]; + testCasesAndExpectedResult.forEach(testCase => { + const result = canFastBid(testCase[0]); + expect(result).to.equal(testCase[1]); + }) + }); + }); + + describe('getFastBidUrl', function () { + it('should properly detect the version of fastbid', function () { + const testCasesAndExpectedResult = [ + ['', 'https://static.criteo.net/js/ld/publishertag.prebid.' + FAST_BID_VERSION_CURRENT + '.js'], + [undefined, 'https://static.criteo.net/js/ld/publishertag.prebid.' + FAST_BID_VERSION_CURRENT + '.js'], + [null, 'https://static.criteo.net/js/ld/publishertag.prebid.' + FAST_BID_VERSION_CURRENT + '.js'], + [NaN, 'https://static.criteo.net/js/ld/publishertag.prebid.' + FAST_BID_VERSION_CURRENT + '.js'], + [123, 'https://static.criteo.net/js/ld/publishertag.prebid.123.js'], + ['123', 'https://static.criteo.net/js/ld/publishertag.prebid.123.js'], + ['latest', 'https://static.criteo.net/js/ld/publishertag.prebid.js'] + ]; + testCasesAndExpectedResult.forEach(testCase => { + const result = getFastBidUrl(testCase[0]); + expect(result).to.equal(testCase[1]); + }) + }); + }); + describe('tryGetCriteoFastBid', function () { const VALID_HASH = 'vBeD8Q7GU6lypFbzB07W8hLGj7NL+p7dI9ro2tCxkrmyv0F6stNuoNd75Us33iNKfEoW+cFWypelr6OJPXxki2MXWatRhJuUJZMcK4VBFnxi3Ro+3a0xEfxE4jJm4eGe98iC898M+/YFHfp+fEPEnS6pEyw124ONIFZFrcejpHU='; const INVALID_HASH = 'invalid'; diff --git a/test/spec/modules/dailyhuntBidAdapter_spec.js b/test/spec/modules/dailyhuntBidAdapter_spec.js deleted file mode 100644 index d571150dbee..00000000000 --- a/test/spec/modules/dailyhuntBidAdapter_spec.js +++ /dev/null @@ -1,400 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/dailyhuntBidAdapter.js'; - -const PROD_PREBID_ENDPOINT_URL = 'https://pbs.dailyhunt.in/openrtb2/auction?partner=dailyhunt'; -const PROD_PREBID_TEST_ENDPOINT_URL = 'https://qa-pbs-van.dailyhunt.in/openrtb2/auction?partner=dailyhunt'; - -const _encodeURIComponent = function (a) { - if (!a) { return } - let b = window.encodeURIComponent(a); - b = b.replace(/'/g, '%27'); - return b; -} - -describe('DailyhuntAdapter', function () { - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'dailyhunt', - 'params': { - placement_id: 1, - publisher_id: 1, - partner_name: 'dailyhunt' - } - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - describe('buildRequests', function() { - let bidRequests = [ - { - bidder: 'dailyhunt', - params: { - placement_id: 1, - publisher_id: 1, - partner_name: 'dailyhunt', - bidfloor: 0.1, - device: { - ip: '47.9.247.217' - }, - site: { - cat: ['1', '2', '3'] - } - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: 'adunit-code', - sizes: [[300, 50]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' - } - ]; - let nativeBidRequests = [ - { - bidder: 'dailyhunt', - params: { - placement_id: 1, - publisher_id: 1, - partner_name: 'dailyhunt', - }, - nativeParams: { - title: { - required: true, - len: 80 - }, - image: { - required: true, - sizes: [150, 50] - }, - }, - mediaTypes: { - native: { - title: { - required: true - }, - } - }, - adUnitCode: 'adunit-code', - sizes: [[300, 250], [300, 50]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' - } - ]; - let videoBidRequests = [ - { - bidder: 'dailyhunt', - params: { - placement_id: 1, - publisher_id: 1, - partner_name: 'dailyhunt' - }, - nativeParams: { - video: { - context: 'instream' - } - }, - mediaTypes: { - video: { - context: 'instream' - } - }, - adUnitCode: 'adunit-code', - sizes: [[300, 250], [300, 50]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' - } - ]; - let bidderRequest = { - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'bidderCode': 'dailyhunt', - 'bids': [ - { - ...bidRequests[0] - } - ], - 'refererInfo': { - 'referer': 'http://m.dailyhunt.in/' - } - }; - let nativeBidderRequest = { - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'bidderCode': 'dailyhunt', - 'bids': [ - { - ...nativeBidRequests[0] - } - ], - 'refererInfo': { - 'referer': 'http://m.dailyhunt.in/' - } - }; - let videoBidderRequest = { - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'bidderCode': 'dailyhunt', - 'bids': [ - { - ...videoBidRequests[0] - } - ], - 'refererInfo': { - 'referer': 'http://m.dailyhunt.in/' - } - }; - - it('sends display bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); - expect(request.method).to.equal('POST'); - }); - - it('sends native bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(nativeBidRequests, nativeBidderRequest)[0]; - expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); - expect(request.method).to.equal('POST'); - }); - - it('sends video bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(videoBidRequests, videoBidderRequest)[0]; - expect(request.url).to.equal(PROD_PREBID_ENDPOINT_URL); - expect(request.method).to.equal('POST'); - }); - }); - describe('interpretResponse', function () { - let bidResponses = { - id: 'da32def7-6779-403c-ada7-0b201dbc9744', - seatbid: [ - { - bid: [ - { - id: 'id1', - impid: 'banner-impid', - price: 1.4, - adm: 'adm', - adid: '66658', - crid: 'asd5ddbf014cac993.66466212', - dealid: 'asd5ddbf014cac993.66466212', - w: 300, - h: 250, - nurl: 'winUrl', - ext: { - prebid: { - type: 'banner' - } - } - }, - { - id: '5caccc1f-94a6-4230-a1f9-6186ee65da99', - impid: 'video-impid', - price: 1.4, - nurl: 'winUrl', - adm: 'adm', - adid: '980', - crid: '2394', - w: 300, - h: 250, - ext: { - prebid: { - 'type': 'video' - }, - bidder: { - cacheKey: 'cache_key', - vastUrl: 'vastUrl' - } - } - }, - { - id: '74973faf-cce7-4eff-abd0-b59b8e91ca87', - impid: 'native-impid', - price: 50, - nurl: 'winUrl', - adm: '{"native":{"link":{"url":"url","clicktrackers":[]},"assets":[{"id":1,"required":1,"img":{},"video":{},"data":{},"title":{"text":"TITLE"},"link":{}},{"id":1,"required":1,"img":{},"video":{},"data":{"type":2,"value":"Lorem Ipsum Lorem Ipsum Lorem Ipsum."},"title":{},"link":{}},{"id":1,"required":1,"img":{},"video":{},"data":{"type":12,"value":"Install Here"},"title":{},"link":{}},{"id":1,"required":1,"img":{"type":3,"url":"urk","w":990,"h":505},"video":{},"data":{},"title":{},"link":{}}],"imptrackers":[]}}', - adid: '968', - crid: '2370', - w: 300, - h: 250, - ext: { - prebid: { - type: 'native' - }, - bidder: null - } - }, - { - id: '5caccc1f-94a6-4230-a1f9-6186ee65da99', - impid: 'video-outstream-impid', - price: 1.4, - nurl: 'winUrl', - adm: 'adm', - adid: '980', - crid: '2394', - w: 300, - h: 250, - ext: { - prebid: { - 'type': 'video' - }, - bidder: { - cacheKey: 'cache_key', - vastUrl: 'vastUrl' - } - } - }, - ], - seat: 'dailyhunt' - } - ], - ext: { - responsetimemillis: { - dailyhunt: 119 - } - } - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: '1', - cpm: 1.4, - creativeId: 'asd5ddbf014cac993.66466212', - width: 300, - height: 250, - ttl: 360, - netRevenue: true, - currency: 'USD', - ad: 'adm', - mediaType: 'banner', - winUrl: 'winUrl' - }, - { - requestId: '2', - cpm: 1.4, - creativeId: '2394', - width: 300, - height: 250, - ttl: 360, - netRevenue: true, - currency: 'USD', - mediaType: 'video', - winUrl: 'winUrl', - videoCacheKey: 'cache_key', - vastUrl: 'vastUrl', - }, - { - requestId: '3', - cpm: 1.4, - creativeId: '2370', - width: 300, - height: 250, - ttl: 360, - netRevenue: true, - currency: 'USD', - mediaType: 'native', - winUrl: 'winUrl', - native: { - clickUrl: 'https%3A%2F%2Fmontu1996.github.io%2F', - clickTrackers: [], - impressionTrackers: [], - javascriptTrackers: [], - title: 'TITLE', - body: 'Lorem Ipsum Lorem Ipsum Lorem Ipsum.', - cta: 'Install Here', - image: { - url: 'url', - height: 505, - width: 990 - } - } - }, - { - requestId: '4', - cpm: 1.4, - creativeId: '2394', - width: 300, - height: 250, - ttl: 360, - netRevenue: true, - currency: 'USD', - mediaType: 'video', - winUrl: 'winUrl', - vastXml: 'adm', - }, - ]; - let bidderRequest = { - bids: [ - { - bidId: 'banner-impid', - adUnitCode: 'code1', - requestId: '1' - }, - { - bidId: 'video-impid', - adUnitCode: 'code2', - requestId: '2', - mediaTypes: { - video: { - context: 'instream' - } - } - }, - { - bidId: 'native-impid', - adUnitCode: 'code3', - requestId: '3' - }, - { - bidId: 'video-outstream-impid', - adUnitCode: 'code4', - requestId: '4', - mediaTypes: { - video: { - context: 'outstream' - } - } - }, - ] - } - let result = spec.interpretResponse({ body: bidResponses }, bidderRequest); - result.forEach((r, i) => { - expect(Object.keys(r)).to.have.members(Object.keys(expectedResponse[i])); - }); - }); - }) - describe('onBidWon', function () { - it('should hit win url when bid won', function () { - let bid = { - requestId: '1', - cpm: 1.4, - creativeId: 'asd5ddbf014cac993.66466212', - width: 300, - height: 250, - ttl: 360, - netRevenue: true, - currency: 'USD', - ad: 'adm', - mediaType: 'banner', - winUrl: 'winUrl' - }; - expect(spec.onBidWon(bid)).to.equal(undefined); - }); - }) -}) diff --git a/test/spec/modules/decenteradsBidAdapter_spec.js b/test/spec/modules/decenteradsBidAdapter_spec.js deleted file mode 100644 index 257094cae3a..00000000000 --- a/test/spec/modules/decenteradsBidAdapter_spec.js +++ /dev/null @@ -1,207 +0,0 @@ -import { expect } from 'chai' -import { spec } from '../../../modules/decenteradsBidAdapter.js' -import { deepStrictEqual, notEqual, ok, strictEqual } from 'assert' - -describe('DecenteradsAdapter', () => { - const bid = { - bidId: '9ec5b177515ee2e5', - bidder: 'decenterads', - params: { - placementId: 0, - traffic: 'banner' - } - } - - describe('isBidRequestValid', () => { - it('Should return true if there are bidId, params and placementId parameters present', () => { - strictEqual(true, spec.isBidRequestValid(bid)) - }) - - it('Should return false if at least one of parameters is not present', () => { - const b = { ...bid } - delete b.params.placementId - strictEqual(false, spec.isBidRequestValid(b)) - }) - }) - - describe('buildRequests', () => { - const serverRequest = spec.buildRequests([bid]) - - it('Creates a ServerRequest object with method, URL and data', () => { - ok(serverRequest) - ok(serverRequest.method) - ok(serverRequest.url) - ok(serverRequest.data) - }) - - it('Returns POST method', () => { - strictEqual('POST', serverRequest.method) - }) - - it('Returns valid URL', () => { - strictEqual('https://supply.decenterads.com/?c=o&m=multi', serverRequest.url) - }) - - it('Returns valid data if array of bids is valid', () => { - const { data } = serverRequest - strictEqual('object', typeof data) - deepStrictEqual(['deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) - strictEqual('number', typeof data.deviceWidth) - strictEqual('number', typeof data.deviceHeight) - strictEqual('string', typeof data.language) - strictEqual('string', typeof data.host) - strictEqual('string', typeof data.page) - notEqual(-1, [0, 1].indexOf(data.secure)) - - const placement = data.placements[0] - deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) - strictEqual(0, placement.placementId) - strictEqual('9ec5b177515ee2e5', placement.bidId) - strictEqual('banner', placement.traffic) - }) - - it('Returns empty data if no valid requests are passed', () => { - const { placements } = spec.buildRequests([]).data - - expect(spec.buildRequests([]).data.placements).to.be.an('array') - strictEqual(0, placements.length) - }) - }) - - describe('interpretResponse', () => { - const validData = [ - { - body: [{ - mediaType: 'banner', - width: 300, - height: 250, - cpm: 0.4, - ad: 'Test', - requestId: '9ec5b177515ee2e5', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }, - { - body: [{ - vastUrl: 'decenterads.com', - mediaType: 'video', - cpm: 0.5, - requestId: '9ec5b177515ee2e5', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }, - { - body: [{ - mediaType: 'native', - clickUrl: 'decenterads.com', - title: 'Test', - image: 'decenterads.com', - creativeId: '2', - impressionTrackers: ['decenterads.com'], - ttl: 120, - cpm: 0.4, - requestId: '9ec5b177515ee2e5', - netRevenue: true, - currency: 'USD', - }] - } - ] - - for (const obj of validData) { - const { mediaType } = obj.body[0] - - it(`Should interpret ${mediaType} response`, () => { - const response = spec.interpretResponse(obj) - - expect(response).to.be.an('array') - strictEqual(1, response.length) - - const copy = { ...obj.body[0] } - delete copy.mediaType - deepStrictEqual(copy, response[0]) - }) - } - - const invalidData = [ - { - body: [{ - width: 300, - cpm: 0.4, - ad: 'Test', - requestId: '9ec5b177515ee2e5', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }, - { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '9ec5b177515ee2e5', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }, - { - body: [{ - mediaType: 'native', - clickUrl: 'decenterads.com', - title: 'Test', - impressionTrackers: ['decenterads.com'], - ttl: 120, - requestId: '9ec5b177515ee2e5', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - } - ] - - for (const obj of invalidData) { - const { mediaType } = obj.body[0] - - it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { - const response = spec.interpretResponse(obj) - - expect(response).to.be.an('array') - strictEqual(0, response.length) - }) - } - - it('Should return an empty array if invalid response is passed', () => { - const response = spec.interpretResponse({ - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }) - - expect(response).to.be.an('array') - strictEqual(0, response.length) - }) - }) - - describe('getUserSyncs', () => { - it('Returns valid URL and type', () => { - const expectedResult = [{ type: 'image', url: 'https://supply.decenterads.com/?c=o&m=cookie' }] - deepStrictEqual(expectedResult, spec.getUserSyncs()) - }) - }) -}) diff --git a/test/spec/modules/districtmDmxBidAdapter_spec.js b/test/spec/modules/districtmDmxBidAdapter_spec.js index 90c18c6a84f..4c060b1f5a4 100644 --- a/test/spec/modules/districtmDmxBidAdapter_spec.js +++ b/test/spec/modules/districtmDmxBidAdapter_spec.js @@ -114,7 +114,6 @@ const bidRequest = [{ lotamePanoramaId: {}, parrableId: {}, netId: {}, - sharedid: {}, lipb: { lipbid: {} }, diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js deleted file mode 100644 index bef2b1fddc5..00000000000 --- a/test/spec/modules/djaxBidAdapter_spec.js +++ /dev/null @@ -1,159 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/djaxBidAdapter.js'; - -const ENDPOINT = 'https://demo.reviveadservermod.com/headerbidding_adminshare/www/admin/plugins/Prebid/getAd.php'; - -describe('The Djax bidding adapter', function () { - describe('isBidRequestValid', function () { - it('should return false when given an invalid bid', function () { - const bid = { - bidder: 'djax', - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - - it('should return true when given a publisherId in bid', function () { - const bid = { - bidder: 'djax', - params: { - publisherId: 2 - }, - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(true); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - 'bidder': 'djax', - 'params': { - 'publisherId': 2 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ] - }]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('check endpoint url', function () { - expect(request.url).to.equal(ENDPOINT) - }); - - it('sets the proper banner object', function () { - expect(bidRequests[0].params.publisherId).to.equal(2); - }) - }); - const response = { - body: [ - { - 'requestId': '2ee937f15015c6', - 'cpm': '0.2000', - 'width': 300, - 'height': 600, - 'creativeId': '4', - 'currency': 'USD', - 'netRevenue': true, - 'ad': 'ads.html', - 'mediaType': 'banner' - }, - { - 'requestId': '3e1af92622bdc', - 'cpm': '0.2000', - 'creativeId': '4', - 'context': 'outstream', - 'currency': 'USD', - 'netRevenue': true, - 'vastUrl': 'tezt.xml', - 'width': 640, - 'height': 480, - 'mediaType': 'video' - }] - }; - - const request = [ - { - 'bidder': 'djax', - 'params': { - 'publisherId': 2 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 600] - ] - } - }, - 'bidId': '2ee937f15015c6', - 'src': 'client', - }, - { - 'bidder': 'djax', - 'params': { - 'publisherId': 2 - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'playerSize': [ - [640, 480] - ] - } - }, - 'bidId': '3e1af92622bdc', - 'src': 'client', - } - ]; - - describe('interpretResponse', function () { - it('return empty array when no ad found', function () { - const response = {}; - const request = { bidRequests: [] }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(0); - }); - - it('check response for banner and video', function () { - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(2); - expect(bids[0].requestId).to.equal('2ee937f15015c6'); - expect(bids[0].cpm).to.equal('0.2000'); - expect(bids[1].cpm).to.equal('0.2000'); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(600); - expect(bids[1].vastUrl).to.not.equal(''); - expect(bids[0].ad).to.not.equal(''); - expect(bids[1].adResponse).to.not.equal(''); - expect(bids[1].renderer).not.to.be.an('undefined'); - }); - }); - - describe('On winning bid', function () { - const bids = spec.interpretResponse(response, request); - spec.onBidWon(bids); - }); - - describe('On bid Time out', function () { - const bids = spec.interpretResponse(response, request); - spec.onTimeout(bids); - }); - - describe('user sync', function () { - it('to check the user sync iframe', function () { - let syncs = spec.getUserSyncs({ - iframeEnabled: true - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - }); - }); -}); diff --git a/test/spec/modules/e_volutionBidAdapter_spec.js b/test/spec/modules/e_volutionBidAdapter_spec.js deleted file mode 100644 index 447420616d1..00000000000 --- a/test/spec/modules/e_volutionBidAdapter_spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/e_volutionBidAdapter.js'; - -describe('EvolutionTechBidAdapter', function () { - let bid = { - bidId: '23fhj33i987f', - bidder: 'e_volution', - params: { - placementId: 0, - traffic: 'banner' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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]); - 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://service.e-volution.ai/?c=o&m=multi'); - }); - 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'); - 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'); - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal('banner'); - }); - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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 () { - let userSync = spec.getUserSyncs(); - it('Returns valid URL and type', function () { - if (spec.noSync) { - expect(userSync).to.be.equal(false); - } else { - 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://service.e-volution.ai/?c=o&m=sync'); - } - }); - }); -}); diff --git a/test/spec/modules/ebdrBidAdapter_spec.js b/test/spec/modules/ebdrBidAdapter_spec.js deleted file mode 100644 index ba1cad475da..00000000000 --- a/test/spec/modules/ebdrBidAdapter_spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/ebdrBidAdapter.js'; -import { VIDEO, BANNER } from 'src/mediaTypes.js'; -import * as utils from 'src/utils.js'; - -describe('ebdrBidAdapter', function () { - let bidRequests; - - beforeEach(function () { - bidRequests = [ - { - code: 'div-gpt-ad-1460505748561-0', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - bidder: 'ebdr', - params: { - zoneid: '99999', - bidfloor: '1.00', - IDFA: 'xxx-xxx', - ADID: 'xxx-xxx', - latitude: '34.089811', - longitude: '-118.392805' - }, - bidId: '2c5e8a1a84522d', - bidderRequestId: '1d0c4017f02458', - auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' - }, { - adUnitCode: 'div-gpt-ad-1460505748561-1', - mediaTypes: { - video: { - context: 'instream', - playerSize: [300, 250] - } - }, - bidder: 'ebdr', - params: { - zoneid: '99998', - bidfloor: '1.00', - IDFA: 'xxx-xxx', - ADID: 'xxx-xxx', - latitude: '34.089811', - longitude: '-118.392805' - }, - bidId: '23a01e95856577', - bidderRequestId: '1d0c4017f02458', - auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' - } - ]; - }); - - describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed', function () { - const bidRequest = bidRequests[0]; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('should return true when the only required param is missing', function () { - const bidRequest = bidRequests[0]; - bidRequest.params = { - zoneid: '99998', - bidfloor: '1.00', - }; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('should return true when the "bidfloor" param is missing', function () { - const bidRequest = bidRequests[0]; - bidRequest.params = { - zoneid: '99998', - }; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('should return false when no bid params are passed', function () { - const bidRequest = bidRequests[0]; - bidRequest.params = {}; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); - - it('should return false when a bid request is not passed', function () { - expect(spec.isBidRequestValid()).to.equal(false); - expect(spec.isBidRequestValid({})).to.equal(false); - }); - }); - - describe('spec.buildRequests', function () { - describe('for banner bids', function () { - it('must handle an empty bid size', function () { - bidRequests[0].mediaTypes = { banner: {} }; - const requests = spec.buildRequests(bidRequests); - const bidRequest = {}; - bidRequest['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: null, h: null }; - expect(requests.bids['2c5e8a1a84522d']).to.deep.equals(bidRequest['2c5e8a1a84522d']); - }); - it('should create a single GET', function () { - bidRequests[0].mediaTypes = { banner: {} }; - bidRequests[1].mediaTypes = { banner: {} }; - const requests = spec.buildRequests(bidRequests); - expect(requests.method).to.equal('GET'); - }); - it('must parse bid size from a nested array', function () { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {sizes: [[ width, height ]]} }; - const requests = spec.buildRequests([ bidRequest ]); - const data = {}; - data['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: width, h: height }; - expect(requests.bids['2c5e8a1a84522d']).to.deep.equal(data['2c5e8a1a84522d']); - }); - }); - describe('for video bids', function () { - it('must handle an empty bid size', function () { - bidRequests[1].mediaTypes = { video: {} }; - const requests = spec.buildRequests(bidRequests); - const bidRequest = {}; - bidRequest['23a01e95856577'] = { mediaTypes: VIDEO, w: null, h: null }; - expect(requests.bids['23a01e95856577']).to.deep.equals(bidRequest['23a01e95856577']); - }); - - it('should create a GET request for each bid', function () { - const bidRequest = bidRequests[1]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests.method).to.equal('GET'); - }); - }); - }); - - describe('spec.interpretResponse', function () { - describe('for video bids', function () { - it('should return no bids if the response is not valid', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { video: {} }; - const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); - expect(bidResponse.length).to.equal(0); - }); - - it('should return a valid video bid response', function () { - const ebdrReq = {bids: {}}; - bidRequests.forEach(bid => { - let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); - ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, - w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], - h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] - }; - }); - const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '23a01e95856577', impid: '23a01e95856577', price: 0.81, adid: 'abcde-12345', nurl: 'https://cdn0.bnmla.com/vtest.xml', adm: '\nStatic VASTStatic VAST Tag00:00:15https//www.engagebdr.com/c', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD'}; - const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); - expect(bidResponse[0]).to.deep.equal({ - requestId: bidRequests[1].bidId, - vastXml: serverResponse.seatbid[0].bid[0].adm, - mediaType: 'video', - creativeId: serverResponse.seatbid[0].bid[0].crid, - cpm: serverResponse.seatbid[0].bid[0].price, - width: serverResponse.seatbid[0].bid[0].w, - height: serverResponse.seatbid[0].bid[0].h, - currency: 'USD', - netRevenue: true, - ttl: 3600, - vastUrl: serverResponse.seatbid[0].bid[0].nurl - }); - }); - }); - - describe('for banner bids', function () { - it('should return no bids if the response is not valid', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); - expect(bidResponse.length).to.equal(0); - }); - - it('should return no bids if the response is empty', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); - expect(bidResponse.length).to.equal(0); - }); - - it('should return valid banner bid responses', function () { - const ebdrReq = {bids: {}}; - bidRequests.forEach(bid => { - let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); - ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, - w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], - h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] - }; - }); - const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; - const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); - expect(bidResponse[0]).to.deep.equal({ - requestId: bidRequests[ 0 ].bidId, - ad: serverResponse.seatbid[0].bid[0].adm, - mediaType: 'banner', - creativeId: serverResponse.seatbid[0].bid[0].crid, - cpm: serverResponse.seatbid[0].bid[0].price, - width: serverResponse.seatbid[0].bid[0].w, - height: serverResponse.seatbid[0].bid[0].h, - currency: 'USD', - netRevenue: true, - ttl: 3600 - }); - }); - }); - }); - describe('spec.getUserSyncs', function () { - let syncOptions - beforeEach(function () { - syncOptions = { - enabledBidders: ['ebdr'], // only these bidders are allowed to sync - pixelEnabled: true - } - }); - it('sucess with usersync url', function () { - const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: 'https://match.bnmla.com/usersync?sspid=59&redir=', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; - const result = []; - result.push({type: 'image', url: 'https://match.bnmla.com/usersync?sspid=59&redir='}); - expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); - }); - - it('sucess without usersync url', function () { - const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; - const result = []; - expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); - }); - it('empty response', function () { - const serverResponse = {}; - const result = []; - expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); - }); - }); -}); diff --git a/test/spec/modules/edgequeryxBidAdapter_spec.js b/test/spec/modules/edgequeryxBidAdapter_spec.js deleted file mode 100644 index a66c546bd7c..00000000000 --- a/test/spec/modules/edgequeryxBidAdapter_spec.js +++ /dev/null @@ -1,116 +0,0 @@ -import { - expect -} from 'chai'; -import { - spec -} from 'modules/edgequeryxBidAdapter.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'; - -// Default params with optional ones -describe('Edge Query X bid adapter tests', function () { - var DEFAULT_PARAMS = [{ - bidId: 'abcd1234', - mediaTypes: { - banner: { - sizes: [ - [1, 1] - ] - } - }, - bidder: 'edgequeryx', - params: { - accountId: 'test', - widgetId: 'test' - }, - requestId: 'efgh5678', - transactionId: 'zsfgzzg' - }]; - var BID_RESPONSE = { - body: { - requestId: 'abcd1234', - cpm: 22, - width: 1, - height: 1, - creativeId: 'EQXTest', - currency: 'EUR', - netRevenue: true, - ttl: 360, - ad: '< --- awesome script --- >' - } - }; - - it('Verify build request', function () { - config.setConfig({ - 'currency': { - 'adServerCurrency': 'EUR' - } - }); - const request = spec.buildRequests(DEFAULT_PARAMS); - expect(request[0]).to.have.property('url').and.to.equal('https://deep.edgequery.io/prebid/x'); - expect(request[0]).to.have.property('method').and.to.equal('POST'); - const requestContent = JSON.parse(request[0].data); - - expect(requestContent).to.have.property('accountId').and.to.equal('test'); - expect(requestContent).to.have.property('widgetId').and.to.equal('test'); - expect(requestContent).to.have.property('sizes'); - expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(1); - expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(1); - }); - - it('Verify parse response', function () { - const request = spec.buildRequests(DEFAULT_PARAMS); - const bids = spec.interpretResponse(BID_RESPONSE, request[0]); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.cpm).to.equal(22); - expect(bid.ad).to.equal('< --- awesome script --- >'); - expect(bid.width).to.equal(1); - expect(bid.height).to.equal(1); - expect(bid.creativeId).to.equal('EQXTest'); - expect(bid.currency).to.equal('EUR'); - expect(bid.netRevenue).to.equal(true); - expect(bid.ttl).to.equal(360); - expect(bid.requestId).to.equal(DEFAULT_PARAMS[0].bidId); - - expect(function () { - spec.interpretResponse(BID_RESPONSE, { - data: 'invalid Json' - }) - }).to.not.throw(); - }); - - it('Verifies bidder code', function () { - expect(spec.code).to.equal('edgequeryx'); - }); - - it('Verifies bidder aliases', function () { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('eqx'); - }); - - it('Verifies if bid request valid', function () { - expect(spec.isBidRequestValid(DEFAULT_PARAMS[0])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ - params: {} - })).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - widgetyId: 'abcdef' - } - })).to.equal(false); - expect(spec.isBidRequestValid({ - params: { - widgetId: 'test', - accountId: 'test' - } - })).to.equal(true); - }); -}); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 1ccaab2b302..c343f4359f7 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -224,42 +224,6 @@ describe('eids array generation for known sub-modules', function() { uids: [{id: 'some-random-id-value', atype: 1}] }); }); - it('Sharedid', function() { - const userId = { - sharedid: { - id: 'test_sharedId', - third: 'test_sharedId' - } - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1, - ext: { - third: 'test_sharedId' - } - }] - }); - }); - it('Sharedid: Not Synched', function() { - const userId = { - sharedid: { - id: 'test_sharedId' - } - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1 - }] - }); - }); it('zeotapIdPlus', function() { const userId = { diff --git a/test/spec/modules/emoteevBidAdapter_spec.js b/test/spec/modules/emoteevBidAdapter_spec.js deleted file mode 100644 index 43ae62f1eb9..00000000000 --- a/test/spec/modules/emoteevBidAdapter_spec.js +++ /dev/null @@ -1,876 +0,0 @@ -import { - assert, expect -} from 'chai'; -import { - ADAPTER_VERSION, - DOMAIN, - DOMAIN_DEVELOPMENT, - DOMAIN_STAGING, - domain, - BIDDER_PATH, - bidderUrl, - buildRequests, - conformBidRequest, - DEFAULT_ENV, - DEVELOPMENT, - EVENTS_PATH, - eventsUrl, - FOOTER, - gdprConsent, - getDeviceDimensions, - getDeviceInfo, - getDocumentDimensions, - getUserSyncs, - getViewDimensions, - IN_CONTENT, - interpretResponse, - isBidRequestValid, - ON_ADAPTER_CALLED, - ON_BID_WON, - ON_BIDDER_TIMEOUT, - onBidWon, - onAdapterCalled, - onTimeout, - OVERLAY, - PRODUCTION, - requestsPayload, - resolveDebug, - resolveEnv, - spec, - STAGING, - USER_SYNC_IFRAME_PATH, - USER_SYNC_IMAGE_PATH, - userSyncIframeUrl, - userSyncImageUrl, - validateSizes, - validateContext, - validateExternalId, - VENDOR_ID, - WALLPAPER, - storage -} from 'modules/emoteevBidAdapter.js'; -import * as utils from '../../../src/utils.js'; -import {config} from '../../../src/config.js'; - -const cannedValidBidRequests = [{ - adUnitCode: '/19968336/header-bid-tag-1', - auctionId: 'fcbf2b27-a951-496f-b5bb-1324ce7c0558', - bidId: '2b8de6572e8193', - bidRequestsCount: 1, - bidder: 'emoteev', - bidderRequestId: '1203b39fecc6a5', - crumbs: {pubcid: 'f3371d16-4e8b-42b5-a770-7e5be1fdf03d'}, - params: { - adSpaceId: 5084, - context: IN_CONTENT, - externalId: 42 - }, - sizes: [[300, 250], [250, 300], [300, 600]], - transactionId: '58dbd732-7a39-45f1-b23e-1c24051a941c', -}]; -const cannedBidderRequest = { - auctionId: 'fcbf2b27-a951-496f-b5bb-1324ce7c0558', - auctionStart: 1544200122837, - bidderCode: 'emoteev', - bidderRequestId: '1203b39fecc6a5', - doneCbCallCount: 0, - refererInfo: { - canonicalUrl: undefined, - numIframes: 0, - reachedTop: true, - referer: 'https://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html', - stack: ['https://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html'] - }, - start: 1544200012839, - timeout: 3000, - gdprConsent: { - gdprApplies: true, - vendorData: {vendorConsents: {[VENDOR_ID]: true}}, - } -}; -const serverResponse = - { - body: [ - { - requestId: cannedValidBidRequests[0].bidId, - cpm: 1, - width: cannedValidBidRequests[0].sizes[0][0], - height: cannedValidBidRequests[0].sizes[0][1], - ad: '
', - ttl: 360, - creativeId: 123, - netRevenue: false, - currency: 'EUR', - } - ] - }; - -describe('emoteevBidAdapter', function () { - describe('isBidRequestValid', function () { - it('should return true when valid', function () { - const validBid = { - bidder: 'emoteev', - bidId: '23a45b4e3', - params: { - adSpaceId: 12345, - context: IN_CONTENT, - externalId: 42 - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - }; - expect(isBidRequestValid(validBid)).to.equal(true); - - expect(spec.isBidRequestValid(validBid)).to.exist.and.to.be.a('boolean'); - expect(spec.isBidRequestValid({})).to.exist.and.to.be.a('boolean'); - }); - - it('should return false when required params are invalid', function () { - expect(isBidRequestValid({ - bidder: '', // invalid bidder - params: { - adSpaceId: 12345, - context: IN_CONTENT, - externalId: 42 - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - })).to.equal(false); - expect(isBidRequestValid({ - bidder: 'emoteev', - params: { - adSpaceId: '', // invalid adSpaceId - context: IN_CONTENT, - externalId: 42 - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - })).to.equal(false); - expect(isBidRequestValid({ - bidder: 'emoteev', - params: { - adSpaceId: 12345, - context: 'something', // invalid context - externalId: 42 - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - })).to.equal(false); - expect(isBidRequestValid({ - bidder: 'emoteev', - params: { - adSpaceId: 12345, - context: IN_CONTENT, - externalId: 'lol' // invalid externalId - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - })).to.equal(false); - expect(isBidRequestValid({ - bidder: 'emoteev', - params: { - adSpaceId: 12345, - context: IN_CONTENT, - externalId: 42 - }, - mediaTypes: { - banner: { - sizes: [[750]] // invalid size - } - }, - })).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const - env = DEFAULT_ENV, - debug = true, - currency = 'EUR', - request = buildRequests(env, debug, currency, cannedValidBidRequests, cannedBidderRequest); - - expect(request).to.exist.and.have.all.keys( - 'method', - 'url', - 'data', - ); - - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(bidderUrl(env)); - - expect(spec.buildRequests(cannedValidBidRequests, cannedBidderRequest)).to.exist.and.to.be.an('object'); - }); - - describe('interpretResponse', function () { - it('bid objects from response', function () { - const bidResponses = interpretResponse(serverResponse); - expect(bidResponses).to.be.an('array').that.is.not.empty; - expect(bidResponses[0]).to.have.property('requestId', cannedValidBidRequests[0].bidId); - expect(bidResponses[0]).to.have.property('cpm', serverResponse.body[0].cpm); - expect(bidResponses[0]).to.have.property('width', serverResponse.body[0].width); - expect(bidResponses[0]).to.have.property('height', serverResponse.body[0].height); - expect(bidResponses[0]).to.have.property('ad', serverResponse.body[0].ad); - expect(bidResponses[0]).to.have.property('ttl', serverResponse.body[0].ttl); - expect(bidResponses[0]).to.have.property('creativeId', serverResponse.body[0].creativeId); - expect(bidResponses[0]).to.have.property('netRevenue', serverResponse.body[0].netRevenue); - expect(bidResponses[0]).to.have.property('currency', serverResponse.body[0].currency); - }); - }); - - describe('onAdapterCalled', function () { - const - bidRequest = cannedValidBidRequests[0], - url = onAdapterCalled(DEFAULT_ENV, bidRequest); - - expect(url).to.have.property('protocol'); - expect(url).to.have.property('hostname'); - expect(url).to.have.property('pathname', EVENTS_PATH); - expect(url).to.have.nested.property('search.eventName', ON_ADAPTER_CALLED); - expect(url).to.have.nested.property('search.pubcId', bidRequest.crumbs.pubcid); - expect(url).to.have.nested.property('search.bidId', bidRequest.bidId); - expect(url).to.have.nested.property('search.adSpaceId', bidRequest.params.adSpaceId); - expect(url).to.have.nested.property('search.cache_buster'); - }); - - describe('onBidWon', function () { - const - pubcId = cannedValidBidRequests[0].crumbs.pubcid, - bidObject = serverResponse.body[0], - url = onBidWon(DEFAULT_ENV, pubcId, bidObject); - - expect(url).to.have.property('protocol'); - expect(url).to.have.property('hostname'); - expect(url).to.have.property('pathname', EVENTS_PATH); - expect(url).to.have.nested.property('search.eventName', ON_BID_WON); - expect(url).to.have.nested.property('search.pubcId', pubcId); - expect(url).to.have.nested.property('search.bidId', bidObject.requestId); - expect(url).to.have.nested.property('search.cache_buster'); - }); - - describe('onTimeout', function () { - const - data = { - ...cannedValidBidRequests[0], - timeout: 123, - }, - url = onTimeout(DEFAULT_ENV, data); - - expect(url).to.have.property('protocol'); - expect(url).to.have.property('hostname'); - expect(url).to.have.property('pathname', EVENTS_PATH); - expect(url).to.have.nested.property('search.eventName', ON_BIDDER_TIMEOUT); - expect(url).to.have.nested.property('search.bidId', data.bidId); - expect(url).to.have.nested.property('search.pubcId', data.crumbs.pubcid); - expect(url).to.have.nested.property('search.adSpaceId', data.params.adSpaceId); - expect(url).to.have.nested.property('search.timeout', data.timeout); - expect(url).to.have.nested.property('search.cache_buster'); - }); - - describe('getUserSyncs', function () { - expect(getUserSyncs( - DEFAULT_ENV, - { - iframeEnabled: false, - pixelEnabled: false - })).to.deep.equal([]); - expect(getUserSyncs( - PRODUCTION, - { - iframeEnabled: false, - pixelEnabled: true - })).to.deep.equal([{ - type: 'image', - url: userSyncImageUrl(PRODUCTION) - }]); - expect(getUserSyncs( - STAGING, - { - iframeEnabled: true, - pixelEnabled: false - })).to.deep.equal([{ - type: 'iframe', - url: userSyncIframeUrl(STAGING) - }]); - expect(getUserSyncs( - DEVELOPMENT, - { - iframeEnabled: true, - pixelEnabled: true - })).to.deep.equal([{ - type: 'image', - url: userSyncImageUrl(DEVELOPMENT) - }, { - type: 'iframe', - url: userSyncIframeUrl(DEVELOPMENT) - }]); - }); - - describe('domain', function () { - expect(domain(null)).to.deep.equal(DOMAIN); - expect(domain('anything')).to.deep.equal(DOMAIN); - expect(domain(PRODUCTION)).to.deep.equal(DOMAIN); - expect(domain(STAGING)).to.deep.equal(DOMAIN_STAGING); - expect(domain(DEVELOPMENT)).to.deep.equal(DOMAIN_DEVELOPMENT); - }); - - describe('eventsUrl', function () { - expect(eventsUrl(null)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: EVENTS_PATH - })); - expect(eventsUrl('anything')).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: EVENTS_PATH - })); - expect(eventsUrl(PRODUCTION)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(PRODUCTION), - pathname: EVENTS_PATH - })); - expect(eventsUrl(STAGING)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(STAGING), - pathname: EVENTS_PATH - })); - expect(eventsUrl(DEVELOPMENT)).to.deep.equal(utils.buildUrl({ - hostname: domain(DEVELOPMENT), - pathname: EVENTS_PATH - })); - }); - - describe('bidderUrl', function () { - expect(bidderUrl(null)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: BIDDER_PATH - })); - expect(bidderUrl('anything')).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: BIDDER_PATH - })); - expect(bidderUrl(PRODUCTION)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(PRODUCTION), - pathname: BIDDER_PATH - })); - expect(bidderUrl(STAGING)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(STAGING), - pathname: BIDDER_PATH - })); - expect(bidderUrl(DEVELOPMENT)).to.deep.equal(utils.buildUrl({ - hostname: domain(DEVELOPMENT), - pathname: BIDDER_PATH - })); - }); - - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(null)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: USER_SYNC_IFRAME_PATH - })); - expect(userSyncIframeUrl('anything')).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: USER_SYNC_IFRAME_PATH - })); - expect(userSyncIframeUrl(PRODUCTION)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(PRODUCTION), - pathname: USER_SYNC_IFRAME_PATH - })); - expect(userSyncIframeUrl(STAGING)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(STAGING), - pathname: USER_SYNC_IFRAME_PATH - })); - expect(userSyncIframeUrl(DEVELOPMENT)).to.deep.equal(utils.buildUrl({ - hostname: domain(DEVELOPMENT), - pathname: USER_SYNC_IFRAME_PATH - })); - }); - - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(null)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: USER_SYNC_IMAGE_PATH - })); - expect(userSyncImageUrl('anything')).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(DEFAULT_ENV), - pathname: USER_SYNC_IMAGE_PATH - })); - expect(userSyncImageUrl(PRODUCTION)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(PRODUCTION), - pathname: USER_SYNC_IMAGE_PATH - })); - expect(userSyncImageUrl(STAGING)).to.deep.equal(utils.buildUrl({ - protocol: 'https', - hostname: domain(STAGING), - pathname: USER_SYNC_IMAGE_PATH - })); - expect(userSyncImageUrl(DEVELOPMENT)).to.deep.equal(utils.buildUrl({ - hostname: domain(DEVELOPMENT), - pathname: USER_SYNC_IMAGE_PATH - })); - }); - - describe('conformBidRequest', function () { - expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ - params: cannedValidBidRequests[0].params, - crumbs: cannedValidBidRequests[0].crumbs, - sizes: cannedValidBidRequests[0].sizes, - bidId: cannedValidBidRequests[0].bidId, - bidderRequestId: cannedValidBidRequests[0].bidderRequestId, - }); - }); - - describe('gdprConsent', function () { - describe('gdpr applies, consent given', function () { - const bidderRequest = { - ...cannedBidderRequest, - gdprConsent: { - gdprApplies: true, - vendorData: {vendorConsents: {[VENDOR_ID]: true}}, - } - }; - expect(gdprConsent(bidderRequest)).to.deep.equal(true); - }); - describe('gdpr applies, consent withdrawn', function () { - const bidderRequest = { - ...cannedBidderRequest, - gdprConsent: { - gdprApplies: true, - vendorData: {vendorConsents: {[VENDOR_ID]: false}}, - } - }; - expect(gdprConsent(bidderRequest)).to.deep.equal(false); - }); - describe('gdpr applies, consent unknown', function () { - const bidderRequest = { - ...cannedBidderRequest, - gdprConsent: { - gdprApplies: true, - vendorData: {}, - } - }; - expect(gdprConsent(bidderRequest)).to.deep.equal(undefined); - }); - }); - - describe('requestsPayload', function () { - const - currency = 'EUR', - debug = true; - - const payload = requestsPayload(debug, currency, cannedValidBidRequests, cannedBidderRequest); - - expect(payload).to.exist.and.have.all.keys( - 'akPbjsVersion', - 'bidRequests', - 'currency', - 'debug', - 'language', - 'refererInfo', - 'deviceInfo', - 'userAgent', - 'gdprApplies', - 'gdprConsent', - ); - - expect(payload.bidRequests[0]).to.exist.and.have.all.keys( - 'params', - 'crumbs', - 'sizes', - 'bidId', - 'bidderRequestId', - ); - - expect(payload.akPbjsVersion).to.deep.equal(ADAPTER_VERSION); - expect(payload.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); - expect(payload.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); - expect(payload.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); - expect(payload.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); - expect(payload.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); - expect(payload.currency).to.deep.equal(currency); - expect(payload.debug).to.deep.equal(debug); - expect(payload.language).to.deep.equal(navigator.language); - expect(payload.deviceInfo).to.exist.and.have.all.keys( - 'browserWidth', - 'browserHeight', - 'deviceWidth', - 'deviceHeight', - 'documentWidth', - 'documentHeight', - 'webGL', - ); - expect(payload.userAgent).to.deep.equal(navigator.userAgent); - expect(payload.gdprApplies).to.deep.equal(cannedBidderRequest.gdprConsent.gdprApplies); - }); - - describe('getViewDimensions', function () { - const window = { - innerWidth: 1024, - innerHeight: 768 - }; - const documentWithElement = { - documentElement: - { - clientWidth: 512, - clientHeight: 384 - } - }; - const documentWithBody = { - body: - { - clientWidth: 512, - clientHeight: 384 - } - }; - expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ - width: 1024, - height: 768 - }); - expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); - expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ - width: 1024, - height: 768 - }); - expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); - expect(getViewDimensions({}, documentWithElement)).to.deep.equal({width: 512, height: 384}); - expect(getViewDimensions({}, documentWithBody)).to.deep.equal({width: 512, height: 384}); - }); - - describe('getDeviceDimensions', function () { - const window = {screen: {width: 1024, height: 768}}; - expect(getDeviceDimensions(window)).to.deep.equal({width: 1024, height: 768}); - expect(getDeviceDimensions({})).to.deep.equal({width: '', height: ''}); - }); - - describe('getDocumentDimensions', function () { - expect(getDocumentDimensions({ - documentElement: { - clientWidth: 1, - clientHeight: 1, - offsetWidth: 0, - offsetHeight: 0, - scrollWidth: 0, - scrollHeight: 0, - }, - })).to.deep.equal({width: 1, height: 1}); - - expect(getDocumentDimensions({ - documentElement: { - clientWidth: 1, - clientHeight: 1, - offsetWidth: 0, - offsetHeight: 0, - scrollWidth: 0, - scrollHeight: 0, - }, - body: { - scrollHeight: 0, - offsetHeight: 0, - } - })).to.deep.equal({width: 1, height: 1}); - - expect(getDocumentDimensions({ - documentElement: { - clientWidth: 0, - clientHeight: 0, - offsetWidth: 1, - offsetHeight: 1, - scrollWidth: 0, - scrollHeight: 0, - }, - body: { - scrollHeight: 0, - offsetHeight: 0, - } - })).to.deep.equal({width: 1, height: 1}); - - expect(getDocumentDimensions({ - documentElement: { - clientWidth: 0, - clientHeight: 0, - offsetWidth: 0, - offsetHeight: 0, - scrollWidth: 1, - scrollHeight: 1, - }, - body: { - scrollHeight: 0, - offsetHeight: 0, - } - })).to.deep.equal({width: 1, height: 1}); - - expect(getDocumentDimensions({ - documentElement: { - clientWidth: undefined, - clientHeight: undefined, - offsetWidth: undefined, - offsetHeight: undefined, - scrollWidth: undefined, - scrollHeight: undefined, - }, - body: { - scrollHeight: undefined, - offsetHeight: undefined, - } - })).to.deep.equal({width: '', height: ''}); - }); - - // describe('isWebGLEnabled', function () { - // it('handles no webgl', function () { - // const - // document = new Document(), - // canvas = sinon.createStubInstance(HTMLCanvasElement); - // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); - // canvas.getContext.withArgs('webgl').returns(undefined); - // canvas.getContext.withArgs('experimental-webgl').returns(undefined); - // expect(isWebGLEnabled(document)).to.equal(false); - // }); - // - // it('handles webgl exception', function () { - // const - // document = new Document(), - // canvas = sinon.createStubInstance(HTMLCanvasElement); - // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); - // canvas.getContext.withArgs('webgl').throws(DOMException); - // expect(isWebGLEnabled(document)).to.equal(false); - // }); - // - // it('handles experimental webgl', function () { - // const - // document = new Document(), - // canvas = sinon.createStubInstance(HTMLCanvasElement); - // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); - // canvas.getContext.withArgs('webgl').returns(undefined); - // canvas.getContext.withArgs('experimental-webgl').returns(true); - // expect(isWebGLEnabled(document)).to.equal(true); - // }); - // - // it('handles experimental webgl exception', function () { - // const - // document = new Document(), - // canvas = sinon.createStubInstance(HTMLCanvasElement); - // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); - // canvas.getContext.withArgs('webgl').returns(undefined); - // canvas.getContext.withArgs('experimental-webgl').throws(DOMException); - // expect(isWebGLEnabled(document)).to.equal(false); - // }); - // - // it('handles webgl', function () { - // const - // document = new Document(), - // canvas = sinon.createStubInstance(HTMLCanvasElement); - // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); - // canvas.getContext.withArgs('webgl').returns(true); - // expect(isWebGLEnabled(document)).to.equal(true); - // }); - // }); - - describe('getDeviceInfo', function () { - expect(getDeviceInfo( - {width: 1, height: 2}, - {width: 3, height: 4}, - {width: 5, height: 6}, - true - )).to.deep.equal({ - deviceWidth: 1, - deviceHeight: 2, - browserWidth: 3, - browserHeight: 4, - documentWidth: 5, - documentHeight: 6, - webGL: true - }); - }); - - describe('resolveEnv', function () { - it('defaults to production', function () { - expect(resolveEnv({}, null)).to.deep.equal(DEFAULT_ENV); - }); - expect(resolveEnv({}, PRODUCTION)).to.deep.equal(PRODUCTION); - expect(resolveEnv({}, STAGING)).to.deep.equal(STAGING); - expect(resolveEnv({}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); - expect(resolveEnv({emoteev: {env: PRODUCTION}}, null)).to.deep.equal(PRODUCTION); - expect(resolveEnv({emoteev: {env: STAGING}}, null)).to.deep.equal(STAGING); - expect(resolveEnv({emoteev: {env: DEVELOPMENT}}, null)).to.deep.equal(DEVELOPMENT); - it('prioritizes parameter over configuration', function () { - expect(resolveEnv({emoteev: {env: STAGING}}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); - }); - }); - - describe('resolveDebug', function () { - it('defaults to production', function () { - expect(resolveDebug({}, null)).to.deep.equal(false); - }); - expect(resolveDebug({}, 'false')).to.deep.equal(false); - expect(resolveDebug({}, 'true')).to.deep.equal(true); - expect(resolveDebug({debug: true}, null)).to.deep.equal(true); - it('prioritizes parameter over configuration', function () { - expect(resolveDebug({debug: true}, 'false')).to.deep.equal(false); - }); - }); - - describe('side effects', function () { - let triggerPixelStub; - let getCookieSpy; - let getConfigSpy; - let getParameterByNameSpy; - - before(function() { - config.resetConfig(); - }); - after(function() { - config.resetConfig(); - }); - beforeEach(function () { - triggerPixelStub = sinon.stub(utils, 'triggerPixel'); - getCookieSpy = sinon.spy(storage, 'getCookie'); - getConfigSpy = sinon.spy(config, 'getConfig'); - getParameterByNameSpy = sinon.spy(utils, 'getParameterByName'); - }); - afterEach(function () { - triggerPixelStub.restore(); - getCookieSpy.restore(); - getConfigSpy.restore(); - getParameterByNameSpy.restore(); - }); - - describe('isBidRequestValid', function () { - it('has intended side-effects', function () { - const validBidRequest = { - bidder: 'emoteev', - bidId: '23a45b4e3', - params: { - adSpaceId: 12345, - }, - mediaTypes: { - banner: { - sizes: [[750, 200]] - } - }, - }; - spec.isBidRequestValid(validBidRequest); - sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // sinon.assert.notCalled(config.getConfig); - sinon.assert.notCalled(utils.getParameterByName); - }); - }); - describe('isBidRequestValid empty request', function() { - it('has intended side-effects empty request', function () { - const invalidBidRequest = {}; - spec.isBidRequestValid(invalidBidRequest); - sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // disabling these getConfig tests as they have been flaky in browserstack testing - // sinon.assert.notCalled(config.getConfig); - sinon.assert.notCalled(utils.getParameterByName); - }); - }); - describe('buildRequests', function () { - it('has intended side-effects', function () { - spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); - sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // sinon.assert.callCount(config.getConfig, 3); - sinon.assert.callCount(utils.getParameterByName, 2); - }); - }); - describe('interpretResponse', function () { - it('has intended side-effects', function () { - spec.interpretResponse(serverResponse); - sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // sinon.assert.notCalled(config.getConfig); - sinon.assert.notCalled(utils.getParameterByName); - }); - }); - describe('onBidWon', function () { - it('has intended side-effects', function () { - const bidObject = serverResponse.body[0]; - spec.onBidWon(bidObject); - sinon.assert.calledOnce(utils.triggerPixel); - sinon.assert.calledOnce(storage.getCookie); - // sinon.assert.calledOnce(config.getConfig); - sinon.assert.calledOnce(utils.getParameterByName); - }); - }); - describe('onTimeout', function () { - it('has intended side-effects', function () { - spec.onTimeout(cannedValidBidRequests[0]); - sinon.assert.calledOnce(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // sinon.assert.calledOnce(config.getConfig); - sinon.assert.calledOnce(utils.getParameterByName); - }); - }); - describe('getUserSyncs', function () { - it('has intended side-effects', function () { - spec.getUserSyncs({}); - sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(storage.getCookie); - // sinon.assert.calledOnce(config.getConfig); - sinon.assert.calledOnce(utils.getParameterByName); - }); - }); - }); - - describe('validateSizes', function () { - it('only accepts valid array of sizes', function () { - expect(validateSizes([])).to.deep.equal(false); - expect(validateSizes([[]])).to.deep.equal(false); - expect(validateSizes([[450, 450], undefined])).to.deep.equal(false); - expect(validateSizes([[450, 450], 'size'])).to.deep.equal(false); - expect(validateSizes([[1, 1]])).to.deep.equal(true); - expect(validateSizes([[1, 1], [450, 450]])).to.deep.equal(true); - }); - }); - - describe('validateContext', function () { - it('only accepts valid context', function () { - expect(validateContext(IN_CONTENT)).to.deep.equal(true); - expect(validateContext(FOOTER)).to.deep.equal(true); - expect(validateContext(OVERLAY)).to.deep.equal(true); - expect(validateContext(WALLPAPER)).to.deep.equal(true); - expect(validateContext(null)).to.deep.equal(false); - expect(validateContext('anything else')).to.deep.equal(false); - }); - }); - - describe('validateExternalId', function () { - it('only accepts a positive integer or null', function () { - expect(validateExternalId(0)).to.deep.equal(false); - expect(validateExternalId(42)).to.deep.equal(true); - expect(validateExternalId(42.0)).to.deep.equal(true); // edge case: valid externalId - expect(validateExternalId(3.14159)).to.deep.equal(false); - expect(validateExternalId('externalId')).to.deep.equal(false); - expect(validateExternalId(undefined)).to.deep.equal(true); - expect(validateExternalId(null)).to.deep.equal(true); - }); - }); -}); diff --git a/test/spec/modules/engageyaBidAdapter_spec.js b/test/spec/modules/engageyaBidAdapter_spec.js deleted file mode 100644 index ad411fc9350..00000000000 --- a/test/spec/modules/engageyaBidAdapter_spec.js +++ /dev/null @@ -1,161 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/engageyaBidAdapter.js'; -import * as utils from 'src/utils.js'; - -const ENDPOINT_URL = 'https://recs.engageya.com/rec-api/getrecs.json'; - -export const _getUrlVars = function(url) { - var hash; - var myJson = {}; - var hashes = url.slice(url.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - myJson[hash[0]] = hash[1]; - } - return myJson; -} - -describe('engageya adapter', function() { - let bidRequests; - let nativeBidRequests; - - beforeEach(function() { - bidRequests = [ - { - bidder: 'engageya', - params: { - widgetId: 85610, - websiteId: 91140, - pageUrl: '[PAGE_URL]' - } - } - ] - - nativeBidRequests = [ - { - bidder: 'engageya', - params: { - widgetId: 85610, - websiteId: 91140, - pageUrl: '[PAGE_URL]' - }, - nativeParams: { - title: { - required: true, - len: 80 - }, - image: { - required: true, - sizes: [150, 50] - }, - sponsoredBy: { - required: true - } - } - } - ] - }) - describe('isBidRequestValid', function () { - it('valid bid case', function () { - let validBid = { - bidder: 'engageya', - params: { - widgetId: 85610, - websiteId: 91140, - pageUrl: '[PAGE_URL]' - } - } - let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - - it('invalid bid case: widgetId and websiteId is not passed', function() { - let validBid = { - bidder: 'engageya', - params: { - } - } - let isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }) - - it('invalid bid case: widget id must be number', function() { - let invalidBid = { - bidder: 'engageya', - params: { - widgetId: '157746a', - websiteId: 91140, - pageUrl: '[PAGE_URL]' - } - } - let isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(false); - }) - }) - - describe('buildRequests', function () { - it('sends bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(bidRequests)[0]; - expect(request.url).to.include(ENDPOINT_URL); - expect(request.method).to.equal('GET'); - }); - - it('buildRequests function should not modify original bidRequests object', function () { - let originalBidRequests = utils.deepClone(bidRequests); - let request = spec.buildRequests(bidRequests); - expect(bidRequests).to.deep.equal(originalBidRequests); - }); - - it('buildRequests function should not modify original nativeBidRequests object', function () { - let originalBidRequests = utils.deepClone(nativeBidRequests); - let request = spec.buildRequests(nativeBidRequests); - expect(nativeBidRequests).to.deep.equal(originalBidRequests); - }); - - it('Request params check', function() { - let request = spec.buildRequests(bidRequests)[0]; - const data = _getUrlVars(request.url) - expect(parseInt(data.wid)).to.exist.and.to.equal(bidRequests[0].params.widgetId); - expect(parseInt(data.webid)).to.exist.and.to.equal(bidRequests[0].params.websiteId); - }) - }) - - describe('interpretResponse', function () { - let response = {recs: [ - { - 'ecpm': 0.0920, - 'postId': '', - 'ad': '', - 'thumbnail_path': 'https://engageya.live/wp-content/uploads/2019/05/images.png' - } - ], - imageWidth: 300, - imageHeight: 250, - ireqId: '1d236f7890b', - pbtypeId: 2}; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - 'requestId': '1d236f7890b', - 'cpm': 0.0920, - 'width': 300, - 'height': 250, - 'netRevenue': false, - 'currency': 'USD', - 'creativeId': '', - 'ttl': 700, - 'ad': '' - } - ]; - let request = spec.buildRequests(bidRequests)[0]; - let result = spec.interpretResponse({body: response}, request); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - expect(result[0].cpm).to.not.equal(null); - expect(result[0].creativeId).to.not.equal(null); - expect(result[0].ad).to.not.equal(null); - expect(result[0].currency).to.equal('USD'); - expect(result[0].netRevenue).to.equal(false); - }); - }) -}) diff --git a/test/spec/modules/envivoBidAdapter_spec.js b/test/spec/modules/envivoBidAdapter_spec.js deleted file mode 100644 index 7bd1dd1ccf1..00000000000 --- a/test/spec/modules/envivoBidAdapter_spec.js +++ /dev/null @@ -1,159 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/envivoBidAdapter.js'; - -const ENDPOINT = 'https://ad.nvivo.tv/ads/www/admin/plugins/Prebid/getAd.php'; - -describe('The Envivo bidding adapter', function () { - describe('isBidRequestValid', function () { - it('should return false when given an invalid bid', function () { - const bid = { - bidder: 'envivo', - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - - it('should return true when given a publisherId in bid', function () { - const bid = { - bidder: 'envivo', - params: { - publisherId: 14 - }, - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(true); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - 'bidder': 'envivo', - 'params': { - 'publisherId': 14 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ] - }]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to our endpoint via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('check endpoint url', function () { - expect(request.url).to.equal(ENDPOINT) - }); - - it('sets the proper banner object', function () { - expect(bidRequests[0].params.publisherId).to.equal(14); - }) - }); - const response = { - body: [ - { - 'requestId': '2ee937f15015c6', - 'cpm': '0.2000', - 'width': 300, - 'height': 600, - 'creativeId': '4', - 'currency': 'USD', - 'netRevenue': true, - 'ad': 'ads.html', - 'mediaType': 'banner' - }, - { - 'requestId': '3e1af92622bdc', - 'cpm': '0.2000', - 'creativeId': '4', - 'context': 'outstream', - 'currency': 'USD', - 'netRevenue': true, - 'vastUrl': 'tezt.xml', - 'width': 640, - 'height': 480, - 'mediaType': 'video' - }] - }; - - const request = [ - { - 'bidder': 'envivo', - 'params': { - 'publisherId': 14 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [300, 600] - ] - } - }, - 'bidId': '2ee937f15015c6', - 'src': 'client', - }, - { - 'bidder': 'envivo', - 'params': { - 'publisherId': 14 - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'playerSize': [ - [640, 480] - ] - } - }, - 'bidId': '3e1af92622bdc', - 'src': 'client', - } - ]; - - describe('interpretResponse', function () { - it('return empty array when no ad found', function () { - const response = {}; - const request = { bidRequests: [] }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(0); - }); - - it('check response for banner and video', function () { - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(2); - expect(bids[0].requestId).to.equal('2ee937f15015c6'); - expect(bids[0].cpm).to.equal('0.2000'); - expect(bids[1].cpm).to.equal('0.2000'); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(600); - expect(bids[1].vastUrl).to.not.equal(''); - expect(bids[0].ad).to.not.equal(''); - expect(bids[1].adResponse).to.not.equal(''); - expect(bids[1].renderer).not.to.be.an('undefined'); - }); - }); - - describe('On winning bid', function () { - const bids = spec.interpretResponse(response, request); - spec.onBidWon(bids); - }); - - describe('On bid Time out', function () { - const bids = spec.interpretResponse(response, request); - spec.onTimeout(bids); - }); - - describe('user sync', function () { - it('to check the user sync iframe', function () { - let syncs = spec.getUserSyncs({ - iframeEnabled: true - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - }); - }); -}); diff --git a/test/spec/modules/fidelityBidAdapter_spec.js b/test/spec/modules/fidelityBidAdapter_spec.js deleted file mode 100644 index 304a98675b3..00000000000 --- a/test/spec/modules/fidelityBidAdapter_spec.js +++ /dev/null @@ -1,233 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/fidelityBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('FidelityAdapter', 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': 'fidelity', - 'params': { - 'zoneid': '27248', - 'floor': '0.05', - 'server': 'x.fidelity-media.com', - }, - '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 true when required params found', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'zoneid': '27248', - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'zoneid': 0, - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidderRequest = { - bidderCode: 'fidelity', - requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - bidderRequestId: '178e34bad3658f', - bids: [ - { - bidder: 'fidelity', - params: { - zoneid: '27248', - floor: '0.05', - server: 'x.fidelity-media.com', - }, - placementCode: '/19968336/header-bid-tag-0', - sizes: [[300, 250], [320, 50]], - bidId: '2ffb201a808da7', - bidderRequestId: '178e34bad3658f', - requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b', - schain: { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'exchange1.com', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'publisher', - domain: 'publisher.com' - }] - } - } - ], - start: 1472239426002, - auctionStart: 1472239426000, - timeout: 5000, - refererInfo: { - referer: 'http://test.com/index.html' - } - }; - - it('should add params to the request', function () { - let schainString = '1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'; - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const payload = request.data; - expect(payload.from).to.exist; - expect(payload.v).to.exist; - expect(payload.requestid).to.exist; - expect(payload.impid).to.exist; - expect(payload.zoneid).to.exist; - expect(payload.floor).to.exist; - expect(payload.charset).to.exist; - expect(payload.subid).to.exist; - expect(payload.flashver).to.exist; - expect(payload.tmax).to.exist; - expect(payload.defloc).to.exist; - expect(payload.schain).to.exist.and.to.be.a('string'); - expect(payload.schain).to.equal(schainString); - }); - - it('should add consent information to the request - TCF v1', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let uspConsentString = '1YN-'; - bidderRequest.gdprConsent = { - gdprApplies: true, - allowAuctionWithoutConsent: true, - consentString: consentString, - vendorData: { - vendorConsents: { - '408': true - }, - }, - apiVersion: 1 - }; - bidderRequest.uspConsent = uspConsentString; - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const payload = request.data; - expect(payload.gdpr).to.exist.and.to.be.a('number'); - expect(payload.gdpr).to.equal(1); - expect(payload.consent_str).to.exist.and.to.be.a('string'); - expect(payload.consent_str).to.equal(consentString); - expect(payload.consent_given).to.exist.and.to.be.a('number'); - expect(payload.consent_given).to.equal(1); - expect(payload.us_privacy).to.exist.and.to.be.a('string'); - expect(payload.us_privacy).to.equal(uspConsentString); - }); - - it('should add consent information to the request - TCF v2', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let uspConsentString = '1YN-'; - bidderRequest.gdprConsent = { - gdprApplies: true, - allowAuctionWithoutConsent: true, - consentString: consentString, - vendorData: { - vendor: { - consents: { - '408': true - } - }, - }, - apiVersion: 2 - }; - bidderRequest.uspConsent = uspConsentString; - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const payload = request.data; - expect(payload.gdpr).to.exist.and.to.be.a('number'); - expect(payload.gdpr).to.equal(1); - expect(payload.consent_str).to.exist.and.to.be.a('string'); - expect(payload.consent_str).to.equal(consentString); - expect(payload.consent_given).to.exist.and.to.be.a('number'); - expect(payload.consent_given).to.equal(1); - expect(payload.us_privacy).to.exist.and.to.be.a('string'); - expect(payload.us_privacy).to.equal(uspConsentString); - }); - - it('sends bid request to ENDPOINT via GET', function () { - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.url).to.equal('https://x.fidelity-media.com/delivery/hb.php'); - expect(request.method).to.equal('GET'); - }); - }) - - describe('interpretResponse', function () { - let response = { - 'id': '543210', - 'seatbid': [ { - 'bid': [ { - 'id': '1111111', - 'impid': 'bidId-123456-1', - 'price': 0.09, - 'adm': '', - 'width': 728, - 'height': 90, - } ] - } ] - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: 'bidId-123456-1', - creativeId: 'bidId-123456-1', - cpm: 0.09, - width: 728, - height: 90, - ad: '', - netRevenue: true, - currency: 'USD', - ttl: 360, - } - ]; - - let result = spec.interpretResponse({ body: response }); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function () { - let response = { - 'id': '543210', - 'seatbid': [ ] - }; - - let result = spec.interpretResponse({ body: response }); - expect(result.length).to.equal(0); - }); - }); - - describe('user sync', function () { - const syncUrl = 'https://x.fidelity-media.com/delivery/matches.php?type=iframe'; - - it('should register the sync iframe', function () { - expect(spec.getUserSyncs({})).to.be.undefined; - expect(spec.getUserSyncs({iframeEnabled: false})).to.be.undefined; - const options = spec.getUserSyncs({iframeEnabled: true}); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal(syncUrl); - }); - }); -}); diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js deleted file mode 100644 index 6530a3c36cf..00000000000 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ /dev/null @@ -1,201 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/fluctBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; - -describe('fluctAdapter', 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 () { - const bid = { - bidder: 'fluct', - params: { - dfpUnitCode: '/1000/dfp_unit_code', - tagId: '10000:100000001', - groupId: '1000000002', - } - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when dfpUnitCode is not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - tagId: '10000:100000001', - groupId: '1000000002', - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when groupId is not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - dfpUnitCode: '/1000/dfp_unit_code', - tagId: '10000:100000001', - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - bidder: 'fluct', - params: { - dfpUnitCode: '/100000/unit_code', - tagId: '10000:100000001', - groupId: '1000000002', - }, - adUnitCode: '/10000/unit_code', - sizes: [[300, 250], [336, 280]], - bidId: '237f4d1a293f99', - bidderRequestId: '1a857fa34c1c96', - auctionId: 'a297d1aa-7900-4ce4-a0aa-caa8d46c4af7', - transactionId: '00b2896c-2731-4f01-83e4-7a3ad5da13b6', - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com' - } - }; - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.method).to.equal('POST'); - }); - }); - - describe('interpretResponse', function() { - const callBeaconSnippet = ''; - - it('should get correct bid response', function() { - const bidRequest = { - bidder: 'fluct', - params: { - dfpUnitCode: '/10000/unit_code', - tagid: '10000:100000001', - groupId: '1000000002', - }, - adUnitCode: '/10000/unit_code', - sizes: [[300, 250], [336, 280]], - bidId: '237f4d1a293f99', - bidderRequestId: '1a857fa34c1c96', - auctionId: 'a297d1aa-7900-4ce4-a0aa-caa8d46c4af7', - transactionId: '00b2896c-2731-4f01-83e4-7a3ad5da13b6', - }; - - const serverResponse = { - body: { - id: '237f4d1a293f99', - cur: 'JPY', - seatbid: [{ - bid: [{ - price: 100, - w: 300, - h: 250, - adm: '', - burl: 'https://i.adingo.jp/?test=1&et=hb&bidid=237f4d1a293f99', - crid: 'test_creative', - }] - }] - } - }; - - const expectedResponse = [ - { - bidderCode: 'fluct', - requestId: '237f4d1a293f99', - currency: 'JPY', - cpm: 100, - netRevenue: true, - width: 300, - height: 250, - creativeId: 'test_creative', - ttl: 300, - ad: '' + callBeaconSnippet, - } - ]; - - const result = spec.interpretResponse(serverResponse, bidRequest); - expect(result).to.have.lengthOf(1); - expect(result).to.deep.have.same.members(expectedResponse); - }); - - it('should get correct bid response with dealId', function() { - const bidRequest = { - bidder: 'fluct', - params: { - dfpUnitCode: '/10000/unit_code', - tagid: '10000:100000001', - groupId: '1000000002' - }, - adUnitCode: '/10000/unit_code', - sizes: [[300, 250], [336, 280]], - bidId: '237f4d1a293f99', - bidderRequestId: '1a857fa34c1c96', - auctionId: 'a297d1aa-7900-4ce4-a0aa-caa8d46c4af7', - transactionId: '00b2896c-2731-4f01-83e4-7a3ad5da13b6', - }; - - const serverResponse = { - body: { - id: '237f4d1a293f99', - cur: 'JPY', - seatbid: [{ - bid: [{ - price: 100, - w: 300, - h: 250, - adm: '', - burl: 'https://i.adingo.jp/?test=1&et=hb&bidid=237f4d1a293f99', - crid: 'test_creative', - dealid: 'test_deal', - }] - }] - } - }; - - const expectedResponse = [ - { - bidderCode: 'fluct', - requestId: '237f4d1a293f99', - currency: 'JPY', - cpm: 100, - netRevenue: true, - width: 300, - height: 250, - creativeId: 'test_creative', - ttl: 300, - ad: '' + callBeaconSnippet, - dealId: 'test_deal', - } - ]; - - const result = spec.interpretResponse(serverResponse, bidRequest); - expect(result).to.have.lengthOf(1); - expect(result).to.deep.have.same.members(expectedResponse); - }); - - it('should get empty response when bid server returns 204', function() { - expect(spec.interpretResponse({})).to.be.empty; - }); - }); -}); diff --git a/test/spec/modules/gammaBidAdapter_spec.js b/test/spec/modules/gammaBidAdapter_spec.js deleted file mode 100644 index cdaa1b5448a..00000000000 --- a/test/spec/modules/gammaBidAdapter_spec.js +++ /dev/null @@ -1,104 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/gammaBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('gammaBidAdapter', function() { - const adapter = newBidder(spec); - - let bid = { - 'bidder': 'gamma', - 'params': { - siteId: '1465446377', - zoneId: '1515999290' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - }; - let bidArray = [bid]; - - describe('isBidRequestValid', () => { - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when require params are not passed', () => { - let bid = Object.assign({}, bid); - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when params not passed correctly', () => { - bid.params.siteId = ''; - bid.params.zoneId = ''; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', () => { - it('should attempt to send bid requests to the endpoint via GET', () => { - const requests = spec.buildRequests(bidArray); - requests.forEach(function(requestItem) { - expect(requestItem.method).to.equal('GET'); - expect(requestItem.url).to.match(new RegExp(`hb.gammaplatform.com`)); - }); - }); - }); - - describe('interpretResponse', () => { - let serverResponse; - - beforeEach(() => { - serverResponse = { - body: { - 'id': '23beaa6af6cdde', - 'bid': '5611802021800040585', - 'type': 'banner', - 'cur': 'USD', - 'seatbid': [{ - 'seat': '5611802021800040585', - 'bid': [{ - 'id': '1515999070', - 'impid': '1', - 'price': 0.45, - 'adm': '', - 'adid': '1515999070', - 'dealid': 'gax-paj2qarjf2g', - 'h': 250, - 'w': 300 - }] - }] - } - }; - }) - - it('should get the correct bid response', () => { - let expectedResponse = [{ - 'requestId': '23beaa6af6cdde', - 'cpm': 0.45, - 'width': 300, - 'height': 250, - 'creativeId': '1515999070', - 'dealId': 'gax-paj2qarjf2g', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 300, - 'ad': '' - }]; - let result = spec.interpretResponse(serverResponse); - expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); - }); - - it('handles empty bid response', () => { - let response = { - body: {} - }; - let result = spec.interpretResponse(response); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js deleted file mode 100644 index 1959bda5c39..00000000000 --- a/test/spec/modules/getintentBidAdapter_spec.js +++ /dev/null @@ -1,142 +0,0 @@ -import { expect } from 'chai' -import { spec } from 'modules/getintentBidAdapter.js' - -describe('GetIntent Adapter Tests:', function () { - const bidRequests = [{ - bidId: 'bid12345', - params: { - pid: 'p1000', - tid: 't1000' - }, - sizes: [[300, 250]] - }, - { - bidId: 'bid54321', - params: { - pid: 'p1000', - tid: 't1000' - }, - sizes: [[50, 50], [100, 100]] - }] - const videoBidRequest = { - bidId: 'bid789', - params: { - pid: 'p1001', - tid: 't1001', - video: { - mimes: ['video/mp4', 'application/javascript'], - max_dur: 20, - api: [1, 2], - skippable: true - } - }, - sizes: [300, 250], - mediaType: 'video' - }; - - it('Verify build request', function () { - const serverRequests = spec.buildRequests(bidRequests); - let serverRequest = serverRequests[0]; - expect(serverRequest.url).to.equal('https://px.adhigh.net/rtb/direct_banner'); - expect(serverRequest.method).to.equal('GET'); - expect(serverRequest.data.bid_id).to.equal('bid12345'); - expect(serverRequest.data.pid).to.equal('p1000'); - expect(serverRequest.data.tid).to.equal('t1000'); - expect(serverRequest.data.size).to.equal('300x250'); - expect(serverRequest.data.is_video).to.equal(false); - serverRequest = serverRequests[1]; - expect(serverRequest.data.size).to.equal('50x50,100x100'); - }); - - it('Verify build video request', function () { - const serverRequests = spec.buildRequests([videoBidRequest]); - let serverRequest = serverRequests[0]; - expect(serverRequest.url).to.equal('https://px.adhigh.net/rtb/direct_vast'); - expect(serverRequest.method).to.equal('GET'); - expect(serverRequest.data.bid_id).to.equal('bid789'); - expect(serverRequest.data.pid).to.equal('p1001'); - expect(serverRequest.data.tid).to.equal('t1001'); - expect(serverRequest.data.size).to.equal('300x250'); - expect(serverRequest.data.is_video).to.equal(true); - expect(serverRequest.data.mimes).to.equal('video/mp4,application/javascript'); - expect(serverRequest.data.max_dur).to.equal(20); - expect(serverRequest.data.api).to.equal('1,2'); - expect(serverRequest.data.skippable).to.equal(true); - }); - - it('Verify parse response', function () { - const serverResponse = { - body: { - bid_id: 'bid12345', - cpm: 2.25, - currency: 'USD', - size: '300x250', - creative_id: '1000', - ad: 'Ad markup' - }, - headers: { - } - }; - const bids = spec.interpretResponse(serverResponse); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.cpm).to.equal(2.25); - expect(bid.currency).to.equal('USD'); - expect(bid.creativeId).to.equal('1000'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.requestId).to.equal('bid12345'); - expect(bid.mediaType).to.equal('banner'); - expect(bid.ad).to.equal('Ad markup'); - }); - - it('Verify parse video response', function () { - const serverResponse = { - body: { - bid_id: 'bid789', - cpm: 3.25, - currency: 'USD', - size: '300x250', - creative_id: '2000', - vast_url: 'https://vast.xml/url' - }, - headers: { - } - }; - const bids = spec.interpretResponse(serverResponse); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.cpm).to.equal(3.25); - expect(bid.currency).to.equal('USD'); - expect(bid.creativeId).to.equal('2000'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.requestId).to.equal('bid789'); - expect(bid.mediaType).to.equal('video'); - expect(bid.vastUrl).to.equal('https://vast.xml/url'); - }); - - it('Verify bidder code', function () { - expect(spec.code).to.equal('getintent'); - }); - - it('Verify bidder aliases', function () { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('getintentAdapter'); - }); - - it('Verify supported media types', function () { - expect(spec.supportedMediaTypes).to.have.lengthOf(2); - expect(spec.supportedMediaTypes[0]).to.equal('video'); - expect(spec.supportedMediaTypes[1]).to.equal('banner'); - }); - - it('Verify if bid request valid', function () { - expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); - expect(spec.isBidRequestValid(bidRequests[1])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); - expect(spec.isBidRequestValid({ params: { test: 123 } })).to.equal(false); - expect(spec.isBidRequestValid({ params: { pid: 111, tid: 222 } })).to.equal(true); - }); -}); diff --git a/test/spec/modules/gnetBidAdapter_spec.js b/test/spec/modules/gnetBidAdapter_spec.js deleted file mode 100644 index 40f8ad50d78..00000000000 --- a/test/spec/modules/gnetBidAdapter_spec.js +++ /dev/null @@ -1,145 +0,0 @@ -import { - expect -} from 'chai'; -import { - spec -} from 'modules/gnetBidAdapter.js'; -import { - newBidder -} from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; - -describe('gnetAdapter', 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: 'gnet', - params: { - websiteId: '4', - externalId: '4d52cccf30309282256012cf30309282' - } - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - bidder: 'gnet', - params: { - websiteId: '4', - externalId: '4d52cccf30309282256012cf30309282' - }, - adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', - sizes: [ - [300, 250], - ], - bidId: '2a19afd5173318', - bidderRequestId: '1f4001782ac16c', - auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', - transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://gnetproject.com/' - } - }; - - it('sends bid request to ENDPOINT via POST', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('POST'); - expect(requests[0].data).to.equal(JSON.stringify({ - 'referer': 'https://gnetproject.com/', - 'adUnitCode': '/150790500/4_ZONA_IAB_300x250_5', - 'bidId': '2a19afd5173318', - 'transactionId': '894bdff6-61ec-4bec-a5a9-f36a5bfccef5', - 'sizes': ['300x250'], - 'params': { - 'websiteId': '4', - 'externalId': '4d52cccf30309282256012cf30309282' - } - })); - }); - }); - - describe('interpretResponse', function () { - const bidderRequests = [{ - bidder: 'gnet', - params: { - clientId: '123456' - }, - adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', - sizes: [ - [300, 250], - ], - bidId: '2a19afd5173318', - bidderRequestId: '1f4001782ac16c', - auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', - transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' - }]; - - it('should get correct banner bid response', function () { - const response = { - bids: [ - { - bidId: '2a19afd5173318', - cpm: 0.1, - currency: 'BRL', - width: 300, - height: 250, - ad: '

I am an ad

', - creativeId: '173560700', - } - ] - }; - - const expectedResponse = [ - { - requestId: '2a19afd5173318', - cpm: 0.1, - currency: 'BRL', - width: 300, - height: 250, - ad: '

I am an ad

', - ttl: 300, - creativeId: '173560700', - netRevenue: true - } - ]; - - const result = spec.interpretResponse({ - body: response - }, bidderRequests); - expect(result).to.have.lengthOf(1); - expect(result).to.deep.have.same.members(expectedResponse); - }); - - it('handles nobid responses', function () { - const response = ''; - - const result = spec.interpretResponse({ - body: response - }, bidderRequests); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/haxmediaBidAdapter_spec.js b/test/spec/modules/haxmediaBidAdapter_spec.js deleted file mode 100644 index 2e39d771bdf..00000000000 --- a/test/spec/modules/haxmediaBidAdapter_spec.js +++ /dev/null @@ -1,304 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/haxmediaBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; - -describe('haxmediaBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'haxmedia', - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 783, - traffic: BANNER - } - }; - - 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://balancer.haxmedia.io/?c=o&m=multi'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); - expect(placement.placementId).to.equal(783); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - expect(placement.sizes).to.be.an('array'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - it('Returns valid data for mediatype native', function () { - const native = { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - }; - - bid.mediaTypes = {}; - bid.params.traffic = NATIVE; - bid.mediaTypes[NATIVE] = native; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); - expect(placement.traffic).to.equal(NATIVE); - expect(placement.native).to.equal(native); - }); - - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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; - }); - }); -}); diff --git a/test/spec/modules/hpmdnetworkBidAdapter_spec.js b/test/spec/modules/hpmdnetworkBidAdapter_spec.js deleted file mode 100644 index 9023fb248e9..00000000000 --- a/test/spec/modules/hpmdnetworkBidAdapter_spec.js +++ /dev/null @@ -1,148 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/hpmdnetworkBidAdapter.js'; - -describe('HPMDNetwork Adapter', function() { - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - const validBid = { - bidder: 'hpmdnetwork', - params: { - placementId: '1' - } - }; - - expect(spec.isBidRequestValid(validBid)).to.equal(true); - }); - - it('should return false for when required params are not passed', function () { - const invalidBid = { - bidder: 'hpmdnetwork', - params: {} - }; - - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - bidId: 'bid1', - bidder: 'hpmdnetwork', - params: { - placementId: '1' - } - }, - { - bidId: 'bid2', - bidder: 'hpmdnetwork', - params: { - placementId: '2', - } - } - ]; - const bidderRequest = { - refererInfo: { - referer: 'https://example.com?foo=bar' - } - }; - - const bidRequest = spec.buildRequests(bidRequests, bidderRequest); - - it('should build single POST request for multiple bids', function() { - expect(bidRequest.method).to.equal('POST'); - expect(bidRequest.url).to.equal('https://banner.hpmdnetwork.ru/bidder/request'); - expect(bidRequest.data).to.be.an('object'); - expect(bidRequest.data.places).to.be.an('array'); - expect(bidRequest.data.places).to.have.lengthOf(2); - }); - - it('should pass bid parameters', function() { - const place1 = bidRequest.data.places[0]; - const place2 = bidRequest.data.places[1]; - - expect(place1.placementId).to.equal('1'); - expect(place2.placementId).to.equal('2'); - expect(place1.id).to.equal('bid1'); - expect(place2.id).to.equal('bid2'); - }); - - it('should pass site parameters', function() { - const url = bidRequest.data.url; - - expect(url).to.be.an('String'); - expect(url).to.equal('https://example.com?foo=bar'); - }); - - it('should pass settings', function() { - const settings = bidRequest.data.settings; - - expect(settings).to.be.an('object'); - expect(settings.currency).to.equal('RUB'); - }); - }); - - describe('interpretResponse', function () { - const serverResponse = { - body: { - 'bids': - [ - { - 'cpm': 20, - 'currency': 'RUB', - 'displayUrl': 'https://banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=168', - 'id': '1', - 'creativeId': '11111', - }, - { - 'cpm': 30, - 'currency': 'RUB', - 'displayUrl': 'https://banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=170', - 'id': '2', - 'creativeId': '22222', - 'width': 300, - 'height': 250, - }, - ] - } - }; - - const bids = spec.interpretResponse(serverResponse); - - it('should return empty array for response with no bids', function() { - const emptyBids = spec.interpretResponse({ body: {} }); - - expect(emptyBids).to.have.lengthOf(0); - }); - - it('should parse all bids from response', function() { - expect(bids).to.have.lengthOf(2); - }); - - it('should parse bid without sizes', function() { - expect(bids[0].requestId).to.equal('1'); - expect(bids[0].cpm).to.equal(20); - expect(bids[0].width).to.equal(1); - expect(bids[0].height).to.equal(1); - expect(bids[0].ttl).to.equal(300); - expect(bids[0].currency).to.equal('RUB'); - expect(bids[0]).to.have.property('creativeId'); - expect(bids[0].creativeId).to.equal('11111'); - expect(bids[0].netRevenue).to.equal(true); - expect(bids[0].ad).to.include(''); - }); - - it('should parse bid with sizes', function() { - expect(bids[1].requestId).to.equal('2'); - expect(bids[1].cpm).to.equal(30); - expect(bids[1].width).to.equal(300); - expect(bids[1].height).to.equal(250); - expect(bids[1].ttl).to.equal(300); - expect(bids[1].currency).to.equal('RUB'); - expect(bids[1]).to.have.property('creativeId'); - expect(bids[1].creativeId).to.equal('22222'); - expect(bids[1].netRevenue).to.equal(true); - expect(bids[1].ad).to.include(''); - }); - }); -}); diff --git a/test/spec/modules/iasBidAdapter_spec.js b/test/spec/modules/iasBidAdapter_spec.js deleted file mode 100644 index 1743ac947e6..00000000000 --- a/test/spec/modules/iasBidAdapter_spec.js +++ /dev/null @@ -1,343 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/iasBidAdapter.js'; - -describe('iasBidAdapter is an adapter that', function () { - it('has the correct bidder code', function () { - expect(spec.code).to.equal('ias'); - }); - describe('has a method `isBidRequestValid` that', function () { - it('exists', function () { - expect(spec.isBidRequestValid).to.be.a('function'); - }); - it('returns false if bid params misses `pubId`', function () { - expect(spec.isBidRequestValid( - { - params: { - adUnitPath: 'someAdUnitPath' - } - })).to.equal(false); - }); - it('returns false if bid params misses `adUnitPath`', function () { - expect(spec.isBidRequestValid( - { - params: { - pubId: 'somePubId' - } - })).to.equal(false); - }); - it('returns true otherwise', function () { - expect(spec.isBidRequestValid( - { - params: { - adUnitPath: 'someAdUnitPath', - pubId: 'somePubId', - someOtherParam: 'abc' - } - })).to.equal(true); - }); - }); - - describe('has a method `buildRequests` that', function () { - it('exists', function () { - expect(spec.buildRequests).to.be.a('function'); - }); - describe('given bid requests, returns a `ServerRequest` instance that', function () { - let bidRequests, IAS_HOST; - beforeEach(function () { - IAS_HOST = 'https://pixel.adsafeprotected.com/services/pub'; - bidRequests = [ - { - adUnitCode: 'one-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/a/b/c' - }, - sizes: [ - [10, 20], - [300, 400] - ], - transactionId: 'someTransactionId' - }, - { - adUnitCode: 'two-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/d/e/f' - }, - sizes: [ - [50, 60] - ], - transactionId: 'someTransactionId' - } - ]; - }); - it('has property `method` of `GET`', function () { - expect(spec.buildRequests(bidRequests)).to.deep.include({ - method: 'GET' - }); - }); - it('has property `url` to be the correct IAS endpoint', function () { - expect(spec.buildRequests(bidRequests)).to.deep.include({ - url: IAS_HOST - }); - }); - it('only includes the first `bidRequest` as the bidRequest variable on a multiple slot request', function () { - expect(spec.buildRequests(bidRequests).bidRequest.adUnitCode).to.equal(bidRequests[0].adUnitCode); - }); - describe('has property `data` that is an encode query string containing information such as', function () { - let val; - const ANID_PARAM = 'anId'; - const SLOT_PARAM = 'slot'; - const SLOT_ID_PARAM = 'id'; - const SLOT_SIZE_PARAM = 'ss'; - const SLOT_AD_UNIT_PATH_PARAM = 'p'; - - beforeEach(function () { - val = decodeURI(spec.buildRequests(bidRequests).data); - }); - it('publisher id', function () { - expect(val).to.have.string(`${ANID_PARAM}=1234`); - }); - it('ad slot`s id, size and ad unit path', function () { - expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:one-div-id,${SLOT_SIZE_PARAM}:[10.20,300.400],${SLOT_AD_UNIT_PATH_PARAM}:/a/b/c}`); - expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:two-div-id,${SLOT_SIZE_PARAM}:[50.60],${SLOT_AD_UNIT_PATH_PARAM}:/d/e/f}`); - }); - it('window size', function () { - expect(val).to.match(/.*wr=[0-9]*\.[0-9]*/); - }); - it('screen size', function () { - expect(val).to.match(/.*sr=[0-9]*\.[0-9]*/); - }); - it('url value', function () { - expect(val).to.match(/.*url=https?%3A%2F%2F[^\s$.?#].[^\s]*/); - }); - }); - it('has property `bidRequest` that is the first passed in bid request', function () { - expect(spec.buildRequests(bidRequests)).to.deep.include({ - bidRequest: bidRequests[0] - }); - }); - }); - }); - describe('has a method `interpretResponse` that', function () { - it('exists', function () { - expect(spec.interpretResponse).to.be.a('function'); - }); - describe('returns a list of bid response that', function () { - let bidRequests, bidResponse, slots, serverResponse; - beforeEach(function () { - bidRequests = [ - { - adUnitCode: 'one-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId1', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/a/b/c' - }, - sizes: [ - [10, 20], - [300, 400] - ], - transactionId: 'someTransactionId' - }, - { - adUnitCode: 'two-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId2', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/d/e/f' - }, - sizes: [ - [50, 60] - ], - transactionId: 'someTransactionId' - } - ]; - const request = { - bidRequest: { - bidId: '102938' - } - }; - slots = {}; - slots['test-div-id'] = { - id: '1234', - vw: ['60', '70'] - }; - slots['test-div-id-two'] = { - id: '5678', - vw: ['80', '90'] - }; - serverResponse = { - body: { - brandSafety: { - adt: 'adtVal', - alc: 'alcVal', - dlm: 'dlmVal', - drg: 'drgVal', - hat: 'hatVal', - off: 'offVal', - vio: 'vioVal' - }, - fr: 'false', - slots: slots - }, - headers: {} - }; - bidResponse = spec.interpretResponse(serverResponse, request); - }); - it('has IAS keyword `adt` as property', function () { - expect(bidResponse[0]).to.deep.include({ adt: 'adtVal' }); - }); - it('has IAS keyword `alc` as property', function () { - expect(bidResponse[0]).to.deep.include({ alc: 'alcVal' }); - }); - it('has IAS keyword `dlm` as property', function () { - expect(bidResponse[0]).to.deep.include({ dlm: 'dlmVal' }); - }); - it('has IAS keyword `drg` as property', function () { - expect(bidResponse[0]).to.deep.include({ drg: 'drgVal' }); - }); - it('has IAS keyword `hat` as property', function () { - expect(bidResponse[0]).to.deep.include({ hat: 'hatVal' }); - }); - it('has IAS keyword `off` as property', function () { - expect(bidResponse[0]).to.deep.include({ off: 'offVal' }); - }); - it('has IAS keyword `vio` as property', function () { - expect(bidResponse[0]).to.deep.include({ vio: 'vioVal' }); - }); - it('has IAS keyword `fr` as property', function () { - expect(bidResponse[0]).to.deep.include({ fr: 'false' }); - }); - it('has property `slots`', function () { - expect(bidResponse[0]).to.deep.include({ slots: slots }); - }); - it('response is the same for multiple slots', function () { - var adapter = spec; - var requests = adapter.buildRequests(bidRequests); - expect(adapter.interpretResponse(serverResponse, requests)).to.length(2); - }); - }); - describe('returns a list of bid response that with custom value', function () { - let bidRequests, bidResponse, slots, custom, serverResponse; - beforeEach(function () { - bidRequests = [ - { - adUnitCode: 'one-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId1', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/a/b/c' - }, - sizes: [ - [10, 20], - [300, 400] - ], - transactionId: 'someTransactionId' - }, - { - adUnitCode: 'two-div-id', - auctionId: 'someAuctionId', - bidId: 'someBidId2', - bidder: 'ias', - bidderRequestId: 'someBidderRequestId', - params: { - pubId: '1234', - adUnitPath: '/d/e/f' - }, - sizes: [ - [50, 60] - ], - transactionId: 'someTransactionId' - } - ]; - const request = { - bidRequest: { - bidId: '102938' - } - }; - slots = {}; - slots['test-div-id'] = { - id: '1234', - vw: ['60', '70'] - }; - slots['test-div-id-two'] = { - id: '5678', - vw: ['80', '90'] - }; - custom = {}; - custom['ias-kw'] = ['IAS_1_KW', 'IAS_2_KW']; - serverResponse = { - body: { - brandSafety: { - adt: 'adtVal', - alc: 'alcVal', - dlm: 'dlmVal', - drg: 'drgVal', - hat: 'hatVal', - off: 'offVal', - vio: 'vioVal' - }, - fr: 'false', - slots: slots, - custom: custom - }, - headers: {} - }; - bidResponse = spec.interpretResponse(serverResponse, request); - }); - it('has IAS keyword `adt` as property', function () { - expect(bidResponse[0]).to.deep.include({ adt: 'adtVal' }); - }); - it('has IAS keyword `alc` as property', function () { - expect(bidResponse[0]).to.deep.include({ alc: 'alcVal' }); - }); - it('has IAS keyword `dlm` as property', function () { - expect(bidResponse[0]).to.deep.include({ dlm: 'dlmVal' }); - }); - it('has IAS keyword `drg` as property', function () { - expect(bidResponse[0]).to.deep.include({ drg: 'drgVal' }); - }); - it('has IAS keyword `hat` as property', function () { - expect(bidResponse[0]).to.deep.include({ hat: 'hatVal' }); - }); - it('has IAS keyword `off` as property', function () { - expect(bidResponse[0]).to.deep.include({ off: 'offVal' }); - }); - it('has IAS keyword `vio` as property', function () { - expect(bidResponse[0]).to.deep.include({ vio: 'vioVal' }); - }); - it('has IAS keyword `fr` as property', function () { - expect(bidResponse[0]).to.deep.include({ fr: 'false' }); - }); - it('has property `slots`', function () { - expect(bidResponse[0]).to.deep.include({ slots: slots }); - }); - it('has property `custom`', function () { - expect(bidResponse[0]).to.deep.include({ custom: custom }); - }); - it('response is the same for multiple slots', function () { - var adapter = spec; - var requests = adapter.buildRequests(bidRequests); - expect(adapter.interpretResponse(serverResponse, requests)).to.length(3); - }); - }); - }); -}); diff --git a/test/spec/modules/imonomyBidAdapter_spec.js b/test/spec/modules/imonomyBidAdapter_spec.js deleted file mode 100644 index 45b3bed6e77..00000000000 --- a/test/spec/modules/imonomyBidAdapter_spec.js +++ /dev/null @@ -1,164 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/imonomyBidAdapter.js'; - -describe('Imonomy Adapter Tests', function () { - const bidsRequest = [ - { - bidder: 'imonomy', - params: { - placementId: '170577', - hbid: '14567718624', - }, - placementCode: 'div-gpt-ad-1460505748561-0', - transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', - sizes: [ - [300, 250] - ], - bidId: '2faedf1095f815', - bidderRequestId: '18065867f8ae39', - auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' - }, - { - bidder: 'imonomy', - params: { - placementId: '281277', - hbid: '14567718624', - floorPrice: 0.5 - }, - placementCode: 'div-gpt-ad-1460505748561-0', - transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', - sizes: [ - [728, 90] - ], - bidId: '3c34e2367a3f59', - bidderRequestId: '18065867f8ae39', - auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' - }]; - - const bidsResponse = { - body: { - bids: [ - { - placementid: '170577', - uuid: '2faedf1095f815', - width: 300, - height: 250, - cpm: 0.51, - creative: '', - ttl: 360, - currency: 'USD', - netRevenue: true, - creativeId: 'd30b58c2ba' - } - ] - } - }; - - it('Verifies imonomyAdapter bidder code', function () { - expect(spec.code).to.equal('imonomy'); - }); - - it('Verifies imonomyAdapter bid request validation', function () { - expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); - expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); - expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false); - expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false); - expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true); - expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); - }); - - it('Verify imonomyAdapter build request', function () { - var startTime = new Date().getTime(); - - const request = spec.buildRequests(bidsRequest); - expect(request.url).to.equal('https://b.imonomy.com/openrtb/hb/14567718624'); - expect(request.method).to.equal('POST'); - const requestData = JSON.parse(request.data); - - // bids object - let bids = requestData.bids; - expect(bids).to.have.lengthOf(2); - - // first bid request: no floor price - expect(bids[0].uuid).to.equal('2faedf1095f815'); - expect(bids[0].floorprice).to.be.undefined; - expect(bids[0].placementid).to.equal('170577'); - expect(bids[0].hbid).to.equal('14567718624'); - expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); - expect(bids[0].sizes).to.have.lengthOf(1); - expect(bids[0].sizes[0][0]).to.equal(300); - expect(bids[0].sizes[0][1]).to.equal(250); - - // second bid request: with floor price - expect(bids[1].uuid).to.equal('3c34e2367a3f59'); - expect(bids[1].floorprice).to.equal(0.5); - expect(bids[1].placementid).to.equal('281277'); - expect(bids[1].hbid).to.equal('14567718624'); - expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); - expect(bids[1]).to.have.property('sizes') - .that.is.an('array') - .of.length(1) - .that.deep.equals([[728, 90]]); - - // kbConf object - let kbConf = requestData.kbConf; - expect(kbConf.hdbdid).to.equal(bids[0].hbid); - expect(kbConf.hdbdid).to.equal(bids[1].hbid); - expect(kbConf.encode_bid).to.be.undefined; - // kbConf timezone and cb - expect(kbConf.cb).not.to.be.undefined; - expect(kbConf.ts_as).to.be.above(startTime - 1); - expect(kbConf.tz).to.equal(new Date().getTimezoneOffset()); - // kbConf bid ids - expect(kbConf.hb_placement_bidids) - .to.have.property(bids[0].placementid) - .that.equal(bids[0].uuid); - expect(kbConf.hb_placement_bidids) - .to.have.property(bids[1].placementid) - .that.equal(bids[1].uuid); - // kbConf floor price - expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid) - expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice); - // kbConf placement ids - expect(kbConf.hb_placements).to.have.lengthOf(2); - expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid); - expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); - }); - - it('Verify imonomyAdapter build response', function () { - const request = spec.buildRequests(bidsRequest); - const bids = spec.interpretResponse(bidsResponse, request); - - // 'server' return single bid - expect(bids).to.have.lengthOf(1); - - // verify bid object - const bid = bids[0]; - const responseBids = bidsResponse.body.bids; - - expect(bid.cpm).to.equal(responseBids[0].cpm); - expect(bid.ad).to.equal(responseBids[0].creative); - expect(bid.requestId).equal(responseBids[0].uuid); - expect(bid.uuid).equal(responseBids[0].uuid); - expect(bid.width).to.equal(responseBids[0].width); - expect(bid.height).to.equal(responseBids[0].height); - expect(bid.ttl).to.equal(responseBids[0].ttl); - expect(bid.currency).to.equal('USD'); - expect(bid.netRevenue).to.equal(true); - expect(bid.creativeId).to.equal(responseBids[0].creativeId); - }); - - it('Verifies imonomyAdapter sync options', function () { - // user sync disabled - expect(spec.getUserSyncs({})).to.be.undefined; - expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; - // user sync enabled - const options = spec.getUserSyncs({ iframeEnabled: true }); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal('https://b.imonomy.com/UserMatching/b/'); - }); -}); diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js deleted file mode 100644 index d14cea8cad3..00000000000 --- a/test/spec/modules/impactifyBidAdapter_spec.js +++ /dev/null @@ -1,398 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/impactifyBidAdapter.js'; -import * as utils from 'src/utils.js'; - -const BIDDER_CODE = 'impactify'; -const BIDDER_ALIAS = ['imp']; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_VIDEO_WIDTH = 640; -const DEFAULT_VIDEO_HEIGHT = 480; -const ORIGIN = 'https://sonic.impactify.media'; -const LOGGER_URI = 'https://logger.impactify.media'; -const AUCTIONURI = '/bidder'; -const COOKIESYNCURI = '/static/cookie_sync.html'; -const GVLID = 606; - -var gdprData = { - 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', - 'gdprApplies': true -}; - -describe('ImpactifyAdapter', function () { - describe('isBidRequestValid', function () { - let validBid = { - bidder: 'impactify', - params: { - appId: '1', - format: 'screen', - style: 'inline' - } - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(validBid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, validBid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when appId is missing', () => { - const bid = utils.deepClone(validBid); - delete bid.params.appId; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when appId is not a string', () => { - const bid = utils.deepClone(validBid); - - bid.params.appId = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.appId = false; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.appId = void (0); - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.appId = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when format is missing', () => { - const bid = utils.deepClone(validBid); - delete bid.params.format; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when format is not a string', () => { - const bid = utils.deepClone(validBid); - - bid.params.format = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.format = false; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.format = void (0); - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.format = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when format is not equals to screen or display', () => { - const bid = utils.deepClone(validBid); - if (bid.params.format != 'screen' && bid.params.format != 'display') { - expect(spec.isBidRequestValid(bid)).to.equal(false); - } - }); - - it('should return false when style is missing', () => { - const bid = utils.deepClone(validBid); - delete bid.params.style; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when style is not a string', () => { - const bid = utils.deepClone(validBid); - - bid.params.style = 123; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.style = false; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.style = void (0); - expect(spec.isBidRequestValid(bid)).to.equal(false); - - bid.params.style = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - describe('buildRequests', function () { - let videoBidRequests = [ - { - bidder: 'impactify', - params: { - appId: '1', - format: 'screen', - style: 'inline' - }, - mediaTypes: { - video: { - context: 'instream' - } - }, - adUnitCode: 'adunit-code', - sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], - bidId: '123456789', - bidderRequestId: '987654321', - auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' - } - ]; - let videoBidderRequest = { - bidderRequestId: '98845765110', - auctionId: '165410516454', - bidderCode: 'impactify', - bids: [ - { - ...videoBidRequests[0] - } - ], - refererInfo: { - referer: 'https://impactify.io' - } - }; - - it('sends video bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(videoBidRequests, videoBidderRequest); - expect(request.url).to.equal(ORIGIN + AUCTIONURI); - expect(request.method).to.equal('POST'); - }); - }); - describe('interpretResponse', function () { - it('should get correct bid response', function () { - let response = { - id: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - seatbid: [ - { - bid: [ - { - id: '65820304700829014', - impid: '462c08f20d428', - price: 3.40, - adm: '', - adid: '97517771', - adomain: [ - '' - ], - iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', - cid: '9325', - crid: '97517771', - w: 1, - h: 1, - ext: { - prebid: { - 'type': 'video' - }, - bidder: { - prebid: { - type: 'video', - video: { - duration: 30, - primary_category: '' - } - }, - bidder: { - appnexus: { - brand_id: 182979, - auction_id: 8657683934873599656, - bidder_id: 2, - bid_ad_type: 1, - creative_info: { - video: { - duration: 30, - mimes: [ - 'video/x-flv', - 'video/mp4', - 'video/webm' - ] - } - } - } - } - } - } - } - ], - seat: 'impactify' - } - ], - cur: DEFAULT_CURRENCY, - ext: { - responsetimemillis: { - impactify: 114 - }, - prebid: { - auctiontimestamp: 1614587024591 - } - } - }; - let bidderRequest = { - bids: [ - { - bidId: '462c08f20d428', - adUnitCode: '/19968336/header-bid-tag-1', - auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - bidder: 'impactify', - sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], - mediaTypes: { - video: { - context: 'outstream' - } - } - }, - ] - } - let expectedResponse = [ - { - id: '65820304700829014', - requestId: '462c08f20d428', - cpm: 3.40, - currency: DEFAULT_CURRENCY, - netRevenue: true, - ad: '', - width: 1, - height: 1, - hash: 'test', - expiry: 166192938, - ttl: 300, - creativeId: '97517771' - } - ]; - let result = spec.interpretResponse({ body: response }, bidderRequest); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - }); - describe('getUserSyncs', function () { - let videoBidRequests = [ - { - bidder: 'impactify', - params: { - appId: '1', - format: 'screen', - style: 'inline' - }, - mediaTypes: { - video: { - context: 'instream' - } - }, - adUnitCode: 'adunit-code', - sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], - bidId: '123456789', - bidderRequestId: '987654321', - auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' - } - ]; - let videoBidderRequest = { - bidderRequestId: '98845765110', - auctionId: '165410516454', - bidderCode: 'impactify', - bids: [ - { - ...videoBidRequests[0] - } - ], - refererInfo: { - referer: 'https://impactify.io' - } - }; - let validResponse = { - id: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - seatbid: [ - { - bid: [ - { - id: '65820304700829014', - impid: '462c08f20d428', - price: 3.40, - adm: '', - adid: '97517771', - adomain: [ - '' - ], - iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', - cid: '9325', - crid: '97517771', - w: 1, - h: 1, - ext: { - prebid: { - 'type': 'video' - }, - bidder: { - prebid: { - type: 'video', - video: { - duration: 30, - primary_category: '' - } - }, - bidder: { - appnexus: { - brand_id: 182979, - auction_id: 8657683934873599656, - bidder_id: 2, - bid_ad_type: 1, - creative_info: { - video: { - duration: 30, - mimes: [ - 'video/x-flv', - 'video/mp4', - 'video/webm' - ] - } - } - } - } - } - } - } - ], - seat: 'impactify' - } - ], - cur: DEFAULT_CURRENCY, - ext: { - responsetimemillis: { - impactify: 114 - }, - prebid: { - auctiontimestamp: 1614587024591 - } - } - }; - it('should return empty response if server response is false', function () { - const result = spec.getUserSyncs('bad', false, gdprData); - expect(result).to.be.empty; - }); - it('should return empty response if server response is empty', function () { - const result = spec.getUserSyncs('bad', [], gdprData); - expect(result).to.be.empty; - }); - it('should append the various values if they exist', function() { - const result = spec.getUserSyncs({iframeEnabled: true}, validResponse, gdprData); - expect(result[0].url).to.include('gdpr=1'); - expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); - }); - }); - - describe('On winning bid', function () { - const bid = { - ad: '', - cpm: '2' - }; - const result = spec.onBidWon(bid); - assert.ok(result); - }); - - describe('On bid Time out', function () { - const bid = { - ad: '', - cpm: '2' - }; - const result = spec.onTimeout(bid); - assert.ok(result); - }); -}) diff --git a/test/spec/modules/innityBidAdapter_spec.js b/test/spec/modules/innityBidAdapter_spec.js deleted file mode 100644 index 80c00252632..00000000000 --- a/test/spec/modules/innityBidAdapter_spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/innityBidAdapter.js'; - -describe('innityAdapterTest', () => { - describe('bidRequestValidity', () => { - it('bidRequest with pub ID and zone ID param', () => { - expect(spec.isBidRequestValid({ - bidder: 'innity', - params: { - 'pub': 267, - 'zone': 62546 - }, - })).to.equal(true); - }); - - it('bidRequest with no required params', () => { - expect(spec.isBidRequestValid({ - bidder: 'innity', - params: { - }, - })).to.equal(false); - }); - }); - - describe('bidRequest', () => { - const bidRequests = [{ - 'bidder': 'innity', - 'params': { - 'pub': 267, - 'zone': 62546 - }, - 'adUnitCode': '/19968336/header-bid-tag-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'sizes': [300, 250], - 'bidId': '51ef8751f9aead', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - - it('bidRequest HTTP method', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function(requestItem) { - expect(requestItem.method).to.equal('GET'); - }); - }); - - it('bidRequest data', () => { - const requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].data.pub).to.equal(267); - expect(requests[0].data.zone).to.equal(62546); - expect(requests[0].data.width).to.equal('300'); - expect(requests[0].data.height).to.equal('250'); - expect(requests[0].data.callback_uid).to.equal('51ef8751f9aead'); - }); - }); - - describe('interpretResponse', () => { - const bidRequest = { - 'method': 'GET', - 'url': 'https://as.innity.com/synd/?', - 'data': { - 'ver': 2, - 'hb': 1, - 'output': 'js', - 'pub': 267, - 'zone': 62546, - 'width': '300', - 'height': '250', - 'callback': 'json', - 'callback_uid': '51ef8751f9aead', - 'url': 'https://example.com', - 'cb': '', - } - }; - - const bidResponse = { - body: { - 'cpm': 100, - 'width': '300', - 'height': '250', - 'creative_id': '148186', - 'callback_uid': '51ef8751f9aead', - 'tag': '', - }, - headers: {} - }; - - it('result is correct', () => { - const result = spec.interpretResponse(bidResponse, bidRequest); - expect(result[0].requestId).to.equal('51ef8751f9aead'); - expect(result[0].cpm).to.equal(1); - expect(result[0].width).to.equal('300'); - expect(result[0].height).to.equal('250'); - expect(result[0].creativeId).to.equal('148186'); - expect(result[0].currency).to.equal('USD'); - expect(result[0].ttl).to.equal(60); - expect(result[0].ad).to.equal(''); - }); - }); -}); diff --git a/test/spec/modules/ipromBidAdapter_spec.js b/test/spec/modules/ipromBidAdapter_spec.js deleted file mode 100644 index a3310a33cc2..00000000000 --- a/test/spec/modules/ipromBidAdapter_spec.js +++ /dev/null @@ -1,195 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/ipromBidAdapter.js'; - -describe('iPROM Adapter', function () { - let bidRequests; - let bidderRequest; - - beforeEach(function () { - bidRequests = [ - { - bidder: 'iprom', - params: { - id: '1234', - dimension: '300x250', - }, - adUnitCode: '/19966331/header-bid-tag-1', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - bidId: '29a72b151f7bd3', - auctionId: 'e36abb27-g3b1-1ad6-8a4c-701c8919d3hh', - bidderRequestId: '2z76da40m1b3cb8', - transactionId: 'j51lhf58-1ad6-g3b1-3j6s-912c9493g0gu' - } - ]; - - bidderRequest = { - timeout: 3000, - refererInfo: { - referer: 'https://adserver.si/index.html', - reachedTop: true, - numIframes: 1, - stack: [ - 'https://adserver.si/index.html', - 'https://adserver.si/iframe1.html', - ] - } - } - }); - - describe('validating bids', function () { - it('should accept valid bid', function () { - let validBid = { - bidder: 'iprom', - params: { - id: '1234', - dimension: '300x250', - }, - }; - - const isValid = spec.isBidRequestValid(validBid); - - expect(isValid).to.equal(true); - }); - - it('should reject bid if missing dimension and id', function () { - let invalidBid = { - bidder: 'iprom', - params: {} - }; - - const isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should reject bid if missing dimension', function () { - let invalidBid = { - bidder: 'iprom', - params: { - id: '1234', - } - }; - - const isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should reject bid if dimension is not a string', function () { - let invalidBid = { - bidder: 'iprom', - params: { - id: '1234', - dimension: 404, - } - }; - - const isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should reject bid if missing id', function () { - let invalidBid = { - bidder: 'iprom', - params: { - dimension: '300x250', - } - }; - - const isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - - it('should reject bid if id is not a string', function () { - let invalidBid = { - bidder: 'iprom', - params: { - id: 1234, - dimension: '300x250', - } - }; - - const isValid = spec.isBidRequestValid(invalidBid); - - expect(isValid).to.equal(false); - }); - }); - - describe('building requests', function () { - it('should go to correct endpoint', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.method).to.exist; - expect(request.method).to.equal('POST'); - expect(request.url).to.exist; - expect(request.url).to.equal('https://core.iprom.net/programmatic'); - }); - - it('should add referer info', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - const requestparse = JSON.parse(request.data); - - expect(requestparse.referer).to.exist; - expect(requestparse.referer.referer).to.equal('https://adserver.si/index.html'); - }); - - it('should add adapter version', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - const requestparse = JSON.parse(request.data); - - expect(requestparse.version).to.exist; - }); - - it('should contain id and dimension', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - const requestparse = JSON.parse(request.data); - - expect(requestparse.bids[0].params.id).to.equal('1234'); - expect(requestparse.bids[0].params.dimension).to.equal('300x250'); - }); - }); - - describe('handling responses', function () { - it('should return complete bid response', function () { - const serverResponse = { - body: [{ - requestId: '29a72b151f7bd3', - cpm: 0.5, - width: '300', - height: '250', - creativeId: 1234, - ad: 'Iprom Header bidding example', - aDomains: ['https://example.com'], - } - ]}; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const bids = spec.interpretResponse(serverResponse, request); - - expect(bids).to.be.lengthOf(1); - expect(bids[0].requestId).to.equal('29a72b151f7bd3'); - expect(bids[0].cpm).to.equal(0.5); - expect(bids[0].width).to.equal('300'); - expect(bids[0].height).to.equal('250'); - expect(bids[0].ad).to.have.length.above(1); - expect(bids[0].meta.advertiserDomains).to.deep.equal(['https://example.com']); - }); - - it('should return empty bid response', function () { - const emptyServerResponse = { - body: [] - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const bids = spec.interpretResponse(emptyServerResponse, request); - - expect(bids).to.be.lengthOf(0); - }); - }); -}); diff --git a/test/spec/modules/ironsourceBidAdapter_spec.js b/test/spec/modules/ironsourceBidAdapter_spec.js deleted file mode 100644 index cca928ff28b..00000000000 --- a/test/spec/modules/ironsourceBidAdapter_spec.js +++ /dev/null @@ -1,381 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/ironsourceBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; -import { VIDEO } from '../../../src/mediaTypes.js'; - -const ENDPOINT = 'https://hb.yellowblue.io/hb'; -const TEST_ENDPOINT = 'https://hb.yellowblue.io/hb-test'; -const TTL = 360; - -describe('ironsourceAdapter', 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 () { - const bid = { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [['640', '480']], - 'params': { - 'isOrg': 'jdye8weeyirk00000001' - } - }; - - it('should return true when required params are passed', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not found', function () { - const newBid = Object.assign({}, bid); - delete newBid.params; - newBid.params = { - 'isOrg': null - }; - expect(spec.isBidRequestValid(newBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'isOrg': 'jdye8weeyirk00000001' - }, - 'bidId': '299ffc8cca0b87', - 'bidderRequestId': '1144f487e563f9', - 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', - } - ]; - - const testModeBidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'isOrg': 'jdye8weeyirk00000001', - 'testMode': true - }, - 'bidId': '299ffc8cca0b87', - 'bidderRequestId': '1144f487e563f9', - 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', - } - ]; - - const bidderRequest = { - bidderCode: 'ironsource', - } - - const customSessionId = '12345678'; - - it('sends bid request to ENDPOINT via GET', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('GET'); - } - }); - - it('sends bid request to test ENDPOINT via GET', function () { - const requests = spec.buildRequests(testModeBidRequests, bidderRequest); - for (const request of requests) { - expect(request.url).to.equal(TEST_ENDPOINT); - expect(request.method).to.equal('GET'); - } - }); - - it('should send the correct bid Id', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.bid_id).to.equal('299ffc8cca0b87'); - } - }); - - it('sends the is_wrapper query param', function () { - bidRequests[0].params.isWrapper = true; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.is_wrapper).to.equal(true); - } - }); - - it('sends the custom session id as a query param', function () { - bidRequests[0].params.sessionId = customSessionId; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.session_id).to.equal(customSessionId); - } - }); - - it('should send the correct width and height', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('width', 640); - expect(request.data).to.have.property('height', 480); - } - }); - - it('should respect syncEnabled option', function() { - config.setConfig({ - userSync: { - syncEnabled: false, - filterSettings: { - all: { - bidders: '*', - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('cs_method'); - } - }); - - it('should respect "iframe" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - iframe: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'iframe'); - } - }); - - it('should respect "all" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - all: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'iframe'); - } - }); - - it('should send the pixel user sync param if userSync is enabled and no "iframe" or "all" configs are present', function () { - config.setConfig({ - userSync: { - syncEnabled: true - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'pixel'); - } - }); - - it('should respect total exclusion', function() { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - image: { - bidders: [spec.code], - filter: 'exclude' - }, - iframe: { - bidders: [spec.code], - filter: 'exclude' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('cs_method'); - } - }); - - it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithUSP); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('us_privacy', '1YNN'); - } - }); - - it('should have an empty us_privacy param if usPrivacy is missing in the bidRequest', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('us_privacy'); - } - }); - - it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('gdpr'); - expect(request.data).to.not.have.property('gdpr_consent'); - } - }); - - it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('gdpr', true); - expect(request.data).to.have.property('gdpr_consent', 'test-consent-string'); - } - }); - - it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], - }; - bidRequests[0].schain = schain; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,,,,'); - } - }); - }); - - describe('interpretResponse', function () { - const response = { - cpm: 12.5, - vastXml: '', - width: 640, - height: 480, - requestId: '21e12606d47ba7', - netRevenue: true, - currency: 'USD' - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: '21e12606d47ba7', - cpm: 12.5, - width: 640, - height: 480, - creativeId: '21e12606d47ba7', - currency: 'USD', - netRevenue: true, - ttl: TTL, - vastXml: '', - mediaType: VIDEO - } - ]; - const result = spec.interpretResponse({ body: response }); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - }) - - describe('getUserSyncs', function() { - const imageSyncResponse = { - body: { - userSyncPixels: [ - 'https://image-sync-url.test/1', - 'https://image-sync-url.test/2', - 'https://image-sync-url.test/3' - ] - } - }; - - const iframeSyncResponse = { - body: { - userSyncURL: 'https://iframe-sync-url.test' - } - }; - - it('should register all img urls from the response', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should register the iframe url from the response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, [iframeSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - } - ]); - }); - - it('should register both image and iframe urls from the responses', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [iframeSyncResponse, imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - }, - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should handle an empty response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('should handle when user syncs are disabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imageSyncResponse]); - expect(syncs).to.deep.equal([]); - }); - }) -}); diff --git a/test/spec/modules/jcmBidAdapter_spec.js b/test/spec/modules/jcmBidAdapter_spec.js deleted file mode 100644 index 9d84bca5513..00000000000 --- a/test/spec/modules/jcmBidAdapter_spec.js +++ /dev/null @@ -1,139 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/jcmBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://media.adfrontiers.com/'; - -describe('jcmAdapter', 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': 'jcm', - 'params': { - 'siteId': '3608' - }, - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'jcm', - 'params': { - 'siteId': '3608' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - - ]; - - const request = spec.buildRequests(bidRequests); - - it('sends bid request to ENDPOINT via GET', function () { - expect(request.method).to.equal('GET'); - }); - - it('sends correct bid parameters', function () { - const payloadArr = request.data.split('&'); - expect(request.method).to.equal('GET'); - expect(payloadArr.length).to.equal(4); - expect(payloadArr[0]).to.equal('t=hb'); - expect(payloadArr[1]).to.equal('ver=1.0'); - expect(payloadArr[2]).to.equal('compact=true'); - const adReqStr = request.data.split('&bids=')[1]; - const adReq = JSON.parse(decodeURIComponent(adReqStr)); - const adReqBid = JSON.parse(decodeURIComponent(adReqStr)).bids[0]; - expect(adReqBid.siteId).to.equal('3608'); - expect(adReqBid.callbackId).to.equal('30b31c1838de1e'); - expect(adReqBid.adSizes).to.equal('300x250,300x600'); - }); - }); - - describe('interpretResponse', function () { - it('should get correct bid response', function () { - let serverResponse = {'bids': [{'width': 300, 'height': 250, 'creativeId': '29681110', 'ad': '', 'cpm': 0.5, 'callbackId': '30b31c1838de1e'}]}; - - let expectedResponse = [ - { - 'requestId': '30b31c1838de1e', - 'bidderCode': 'jcm', - 'cpm': 0.5, - 'creativeId': '29681110', - 'width': 300, - 'height': 250, - 'ttl': 60, - 'currency': 'USA', - 'netRevenue': true, - 'ad': '', - } - ]; - - let result = spec.interpretResponse({ body: serverResponse }); - expect(Object.keys(result[0]).length).to.equal(Object.keys(expectedResponse[0]).length); - expect(Object.keys(result[0]).requestId).to.equal(Object.keys(expectedResponse[0]).requestId); - expect(Object.keys(result[0]).bidderCode).to.equal(Object.keys(expectedResponse[0]).bidderCode); - expect(Object.keys(result[0]).cpm).to.equal(Object.keys(expectedResponse[0]).cpm); - expect(Object.keys(result[0]).creativeId).to.equal(Object.keys(expectedResponse[0]).creativeId); - expect(Object.keys(result[0]).width).to.equal(Object.keys(expectedResponse[0]).width); - expect(Object.keys(result[0]).height).to.equal(Object.keys(expectedResponse[0]).height); - expect(Object.keys(result[0]).ttl).to.equal(Object.keys(expectedResponse[0]).ttl); - expect(Object.keys(result[0]).currency).to.equal(Object.keys(expectedResponse[0]).currency); - expect(Object.keys(result[0]).netRevenue).to.equal(Object.keys(expectedResponse[0]).netRevenue); - - expect(Object.keys(result[0]).ad).to.equal(Object.keys(expectedResponse[0]).ad); - }); - - it('handles nobid responses', function () { - let serverResponse = {'bids': []}; - - let result = spec.interpretResponse({ body: serverResponse }); - expect(result.length).to.equal(0); - }); - }); - describe('getUserSyncs', function () { - it('Verifies sync iframe option', function () { - expect(spec.getUserSyncs({})).to.be.undefined; - expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; - const options = spec.getUserSyncs({ iframeEnabled: true }); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal('https://media.adfrontiers.com/hb/jcm_usersync.html'); - }); - - it('Verifies sync image option', function () { - expect(spec.getUserSyncs({ image: false })).to.be.undefined; - const options = spec.getUserSyncs({ image: true }); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('image'); - expect(options[0].url).to.equal('https://media.adfrontiers.com/hb/jcm_usersync.png'); - }); - }); -}); diff --git a/test/spec/modules/komoonaBidAdapter_spec.js b/test/spec/modules/komoonaBidAdapter_spec.js deleted file mode 100644 index 3d62f91cae6..00000000000 --- a/test/spec/modules/komoonaBidAdapter_spec.js +++ /dev/null @@ -1,164 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/komoonaBidAdapter.js'; - -describe('Komoona.com Adapter Tests', function () { - const bidsRequest = [ - { - bidder: 'komoona', - params: { - placementId: '170577', - hbid: 'abc12345678', - }, - placementCode: 'div-gpt-ad-1460505748561-0', - transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', - sizes: [ - [300, 250] - ], - bidId: '2faedf1095f815', - bidderRequestId: '18065867f8ae39', - auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' - }, - { - bidder: 'komoona', - params: { - placementId: '281277', - hbid: 'abc12345678', - floorPrice: 0.5 - }, - placementCode: 'div-gpt-ad-1460505748561-0', - transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', - sizes: [ - [728, 90] - ], - bidId: '3c34e2367a3f59', - bidderRequestId: '18065867f8ae39', - auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' - }]; - - const bidsResponse = { - body: { - bids: [ - { - placementid: '170577', - uuid: '2faedf1095f815', - width: 300, - height: 250, - cpm: 0.51, - creative: '', - ttl: 360, - currency: 'USD', - netRevenue: true, - creativeId: 'd30b58c2ba' - } - ] - } - }; - - it('Verifies komoonaAdapter bidder code', function () { - expect(spec.code).to.equal('komoona'); - }); - - it('Verifies komoonaAdapter bid request validation', function () { - expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); - expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); - expect(spec.isBidRequestValid({})).to.equal(false); - expect(spec.isBidRequestValid({ params: {} })).to.equal(false); - expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false); - expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false); - expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true); - expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); - }); - - it('Verify komoonaAdapter build request', function () { - var startTime = new Date().getTime(); - - const request = spec.buildRequests(bidsRequest); - expect(request.url).to.equal('https://bidder.komoona.com/v1/GetSBids'); - expect(request.method).to.equal('POST'); - const requestData = JSON.parse(request.data); - - // bids object - let bids = requestData.bids; - expect(bids).to.have.lengthOf(2); - - // first bid request: no floor price - expect(bids[0].uuid).to.equal('2faedf1095f815'); - expect(bids[0].floorprice).to.be.undefined; - expect(bids[0].placementid).to.equal('170577'); - expect(bids[0].hbid).to.equal('abc12345678'); - expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); - expect(bids[0].sizes).to.have.lengthOf(1); - expect(bids[0].sizes[0][0]).to.equal(300); - expect(bids[0].sizes[0][1]).to.equal(250); - - // second bid request: with floor price - expect(bids[1].uuid).to.equal('3c34e2367a3f59'); - expect(bids[1].floorprice).to.equal(0.5); - expect(bids[1].placementid).to.equal('281277'); - expect(bids[1].hbid).to.equal('abc12345678'); - expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); - expect(bids[1]).to.have.property('sizes') - .that.is.an('array') - .of.length(1) - .that.deep.equals([[728, 90]]); - - // kbConf object - let kbConf = requestData.kbConf; - expect(kbConf.hdbdid).to.equal(bids[0].hbid); - expect(kbConf.hdbdid).to.equal(bids[1].hbid); - expect(kbConf.encode_bid).to.be.undefined; - // kbConf timezone and cb - expect(kbConf.cb).not.to.be.undefined; - expect(kbConf.ts_as).to.be.above(startTime - 1); - expect(kbConf.tz).to.equal(new Date().getTimezoneOffset()); - // kbConf bid ids - expect(kbConf.hb_placement_bidids) - .to.have.property(bids[0].placementid) - .that.equal(bids[0].uuid); - expect(kbConf.hb_placement_bidids) - .to.have.property(bids[1].placementid) - .that.equal(bids[1].uuid); - // kbConf floor price - expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid) - expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice); - // kbConf placement ids - expect(kbConf.hb_placements).to.have.lengthOf(2); - expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid); - expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); - }); - - it('Verify komoonaAdapter build response', function () { - const request = spec.buildRequests(bidsRequest); - const bids = spec.interpretResponse(bidsResponse, request); - - // 'server' return single bid - expect(bids).to.have.lengthOf(1); - - // verify bid object - const bid = bids[0]; - const responseBids = bidsResponse.body.bids; - - expect(bid.cpm).to.equal(responseBids[0].cpm); - expect(bid.ad).to.equal(responseBids[0].creative); - expect(bid.requestId).equal(responseBids[0].uuid); - expect(bid.uuid).equal(responseBids[0].uuid); - expect(bid.width).to.equal(responseBids[0].width); - expect(bid.height).to.equal(responseBids[0].height); - expect(bid.ttl).to.equal(responseBids[0].ttl); - expect(bid.currency).to.equal('USD'); - expect(bid.netRevenue).to.equal(true); - expect(bid.creativeId).to.equal(responseBids[0].creativeId); - }); - - it('Verifies komoonaAdapter sync options', function () { - // user sync disabled - expect(spec.getUserSyncs({})).to.be.undefined; - expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; - // user sync enabled - const options = spec.getUserSyncs({ iframeEnabled: true }); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal('https://s.komoona.com/sync/usync.html'); - }); -}); diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js deleted file mode 100644 index 3af9ed64c43..00000000000 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ /dev/null @@ -1,329 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/krushmediaBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; - -describe('KrushmediabBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'krushmedia', - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - key: 783, - traffic: BANNER - } - }; - - 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.key; - 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://ads4.krushmedia.com/?c=rtb&m=hb'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'schain'); - expect(placement.key).to.equal(783); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - expect(placement.sizes).to.be.an('array'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - it('Returns valid data for mediatype native', function () { - const native = { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - }; - - bid.mediaTypes = {}; - bid.params.traffic = NATIVE; - bid.mediaTypes[NATIVE] = native; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'native', 'schain'); - expect(placement.traffic).to.equal(NATIVE); - expect(placement.native).to.equal(native); - }); - - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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('iframe') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&gdpr=1&gdpr_consent=ALL') - }); - it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1NNN' - }); - 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('iframe') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&ccpa_consent=1NNN') - }); - }); -}); diff --git a/test/spec/modules/kubientBidAdapter_spec.js b/test/spec/modules/kubientBidAdapter_spec.js deleted file mode 100644 index 1df4370b2ba..00000000000 --- a/test/spec/modules/kubientBidAdapter_spec.js +++ /dev/null @@ -1,259 +0,0 @@ -import { expect, assert } from 'chai'; -import { spec } from 'modules/kubientBidAdapter.js'; - -describe('KubientAdapter', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'kubient', - bidderRequestId: '145e1d6a7837c9', - params: { - zoneid: '5678', - floor: 0.05, - }, - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '0', - hp: 1, - rid: 'bidrequestid', - domain: 'example.com' - } - ] - } - }; - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let uspConsentData = '1YCC'; - let bidderRequest = { - bidderCode: 'kubient', - auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', - bidderRequestId: 'ffffffffffffff', - start: 1472239426002, - auctionStart: 1472239426000, - timeout: 5000, - refererInfo: { - referer: 'http://www.example.com', - reachedTop: true, - }, - gdprConsent: { - consentString: consentString, - gdprApplies: true - }, - uspConsent: uspConsentData, - bids: [bid] - }; - describe('buildRequests', function () { - let serverRequests = spec.buildRequests([bid], bidderRequest); - it('Creates a ServerRequest object with method, URL and data', function () { - expect(serverRequests).to.be.an('array'); - }); - for (let i = 0; i < serverRequests.length; i++) { - let serverRequest = serverRequests[i]; - it('Creates a ServerRequest object with method, URL and data', function () { - expect(serverRequest.method).to.be.a('string'); - expect(serverRequest.url).to.be.a('string'); - expect(serverRequest.data).to.be.a('string'); - }); - it('Returns POST method', function () { - expect(serverRequest.method).to.equal('POST'); - }); - it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://kssp.kbntx.ch/pbjs'); - }); - it('Returns valid data if array of bids is valid', function () { - let data = JSON.parse(serverRequest.data); - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('v', 'requestId', 'adSlots', 'gdpr', 'referer', 'tmax', 'consent', 'consentGiven', 'uspConsent'); - expect(data.v).to.exist.and.to.be.a('string'); - expect(data.requestId).to.exist.and.to.be.a('string'); - expect(data.referer).to.be.a('string'); - expect(data.tmax).to.exist.and.to.be.a('number'); - expect(data.gdpr).to.exist.and.to.be.within(0, 1); - expect(data.consent).to.equal(consentString); - expect(data.uspConsent).to.exist.and.to.equal(uspConsentData); - for (let j = 0; j < data['adSlots'].length; j++) { - let adSlot = data['adSlots'][i]; - expect(adSlot).to.have.all.keys('bidId', 'zoneId', 'floor', 'sizes', 'schain', 'mediaTypes'); - expect(adSlot.bidId).to.be.a('string'); - expect(adSlot.zoneId).to.be.a('string'); - expect(adSlot.floor).to.be.a('number'); - expect(adSlot.sizes).to.be.an('array'); - expect(adSlot.schain).to.be.an('object'); - expect(adSlot.mediaTypes).to.be.an('object'); - } - }); - } - }); - - describe('isBidRequestValid', function () { - it('Should return true when required params are found', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when required params are not found', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when params are not found', function () { - delete bid.params; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('interpretResponse', function () { - it('Should interpret response', function () { - const serverResponse = { - body: - { - seatbid: [ - { - bid: [ - { - bidId: '000', - price: 1.5, - adm: '
test
', - creativeId: 'creativeId', - w: 300, - h: 250, - cur: 'USD', - netRevenue: false, - ttl: 360 - } - ] - } - ] - } - }; - let bannerResponses = spec.interpretResponse(serverResponse); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'ad', 'creativeId', 'width', 'height', 'currency', 'netRevenue', 'ttl'); - expect(dataItem.requestId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].bidId); - expect(dataItem.cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); - expect(dataItem.ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); - expect(dataItem.creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].creativeId); - expect(dataItem.width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); - expect(dataItem.height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); - expect(dataItem.currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].cur); - expect(dataItem.netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(serverResponse.body.seatbid[0].bid[0].netRevenue); - expect(dataItem.ttl).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].ttl); - }); - - it('Should return no ad when not given a server response', function () { - const ads = spec.interpretResponse(null); - expect(ads).to.be.an('array').and.to.have.length(0); - }); - }); - - describe('getUserSyncs', function () { - it('should register the sync iframe without gdpr', function () { - let syncOptions = { - iframeEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - consentString: consentString - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&consent_given=0'); - }); - it('should register the sync iframe with gdpr', function () { - let syncOptions = { - iframeEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - gdprApplies: true, - consentString: consentString - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=0'); - }); - it('should register the sync iframe with gdpr vendor', function () { - let syncOptions = { - iframeEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - gdprApplies: true, - consentString: consentString, - apiVersion: 1, - vendorData: { - vendorConsents: { - 794: 1 - } - } - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=1'); - }); - it('should register the sync image without gdpr', function () { - let syncOptions = { - pixelEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - consentString: consentString - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('image'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&consent_given=0'); - }); - it('should register the sync image with gdpr', function () { - let syncOptions = { - pixelEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - gdprApplies: true, - consentString: consentString - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('image'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=0'); - }); - it('should register the sync image with gdpr vendor', function () { - let syncOptions = { - pixelEnabled: true - }; - let serverResponses = null; - let gdprConsent = { - gdprApplies: true, - consentString: consentString, - apiVersion: 2, - vendorData: { - vendor: { - consents: { - 794: 1 - } - } - } - }; - let uspConsent = null; - let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); - expect(syncs).to.be.an('array').and.to.have.length(1); - expect(syncs[0].type).to.equal('image'); - expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=1'); - }); - }) -}); diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js deleted file mode 100644 index a3a70a39731..00000000000 --- a/test/spec/modules/lemmaBidAdapter_spec.js +++ /dev/null @@ -1,426 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/lemmaBidAdapter.js'; -import * as utils from 'src/utils.js'; -const constants = require('src/constants.json'); - -describe('lemmaBidAdapter', function() { - var bidRequests; - var videoBidRequests; - var bidResponses; - beforeEach(function() { - bidRequests = [{ - bidder: 'lemma', - mediaType: 'banner', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600] - ], - } - }, - params: { - pubId: 1001, - adunitId: 1, - currency: 'AUD', - bidFloor: 1.3, - geo: { - lat: '12.3', - lon: '23.7', - } - }, - sizes: [ - [300, 250], - [300, 600] - ] - }]; - videoBidRequests = [{ - code: 'video1', - mediaType: 'video', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream' - } - }, - bidder: 'lemma', - params: { - pubId: 1001, - adunitId: 1, - bidFloor: 1.3, - video: { - mimes: ['video/mp4', 'video/x-flv'], - skippable: true, - minduration: 5, - maxduration: 30 - } - } - }]; - bidResponses = { - 'body': { - 'id': '93D3BAD6-E2E2-49FB-9D89-920B1761C865', - 'seatbid': [{ - 'bid': [{ - 'id': '74858439-49D7-4169-BA5D-44A046315B2F', - 'impid': '22bddb28db77d', - 'price': 1.3, - 'adm': '

lemma"Connecting Advertisers and Publishers directly"

', - 'adomain': ['amazon.com'], - 'iurl': 'https://thetradedesk-t-general.s3.amazonaws.com/AdvertiserLogos/vgl908z.png', - 'cid': '22918', - 'crid': 'v55jutrh', - 'h': 250, - 'w': 300, - 'ext': {} - }] - }] - } - }; - }); - describe('implementation', function() { - describe('Bid validations', function() { - it('valid bid case', function() { - var validBid = { - bidder: 'lemma', - params: { - pubId: 1001, - adunitId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - it('invalid bid case', function() { - var isValid = spec.isBidRequestValid(); - expect(isValid).to.equal(false); - }); - it('invalid bid case: pubId not passed', function() { - var validBid = { - bidder: 'lemma', - params: { - adunitId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - it('invalid bid case: pubId is not number', function() { - var validBid = { - bidder: 'lemma', - params: { - pubId: '301', - adunitId: 1 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - it('invalid bid case: adunitId is not passed', function() { - var validBid = { - bidder: 'lemma', - params: { - pubId: 1001 - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - it('invalid bid case: video bid request mimes is not passed', function() { - var validBid = { - bidder: 'lemma', - params: { - pubId: 1001, - adunitId: 1, - video: { - skippable: true, - minduration: 5, - maxduration: 30 - } - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - validBid.params.video.mimes = []; - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - }); - describe('Request formation', function() { - it('buildRequests function should not modify original bidRequests object', function() { - var originalBidRequests = utils.deepClone(bidRequests); - var request = spec.buildRequests(bidRequests); - expect(bidRequests).to.deep.equal(originalBidRequests); - }); - it('Endpoint checking', function() { - var request = spec.buildRequests(bidRequests); - expect(request.url).to.equal('https://ads.lemmatechnologies.com/lemma/servad?pid=1001&aid=1'); - expect(request.method).to.equal('POST'); - }); - it('Request params check', function() { - var request = spec.buildRequests(bidRequests); - var data = JSON.parse(request.data); - expect(data.site.domain).to.be.a('string'); // domain should be set - expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id - expect(data.imp[0].tagid).to.equal('1'); // tagid - expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); - expect(data.imp[0].bidfloor).to.equal(bidRequests[0].params.bidFloor); - }); - it('Request params check without mediaTypes object', function() { - var bidRequests = [{ - bidder: 'lemma', - params: { - pubId: 1001, - adunitId: 1, - currency: 'AUD' - }, - sizes: [ - [300, 250], - [300, 600] - ] - }]; - var request = spec.buildRequests(bidRequests); - var data = JSON.parse(request.data); - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].banner.format).exist.and.to.be.an('array'); - expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); - expect(data.imp[0].banner.format[0].w).to.equal(300); // width - expect(data.imp[0].banner.format[0].h).to.equal(600); // height - }); - it('Request params check: without tagId', function() { - delete bidRequests[0].params.adunitId; - var request = spec.buildRequests(bidRequests); - var data = JSON.parse(request.data); - expect(data.site.domain).to.be.a('string'); // domain should be set - expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id - expect(data.imp[0].tagid).to.equal(undefined); // tagid - expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); - expect(data.imp[0].bidfloor).to.equal(bidRequests[0].params.bidFloor); - }); - it('Request params multi size format object check', function() { - var bidRequests = [{ - bidder: 'lemma', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600] - ], - } - }, - params: { - pubId: 1001, - adunitId: 1, - currency: 'AUD' - }, - sizes: [ - [300, 250], - [300, 600] - ] - }]; - /* case 1 - size passed in adslot */ - var request = spec.buildRequests(bidRequests); - var data = JSON.parse(request.data); - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(250); // height - /* case 2 - size passed in adslot as well as in sizes array */ - bidRequests[0].sizes = [ - [300, 600], - [300, 250] - ]; - bidRequests[0].mediaTypes = { - banner: { - sizes: [ - [300, 600], - [300, 250] - ] - } - }; - request = spec.buildRequests(bidRequests); - data = JSON.parse(request.data); - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(600); // height - /* case 3 - size passed in sizes but not in adslot */ - bidRequests[0].params.adunitId = 1; - bidRequests[0].sizes = [ - [300, 250], - [300, 600] - ]; - bidRequests[0].mediaTypes = { - banner: { - sizes: [ - [300, 250], - [300, 600] - ] - } - }; - request = spec.buildRequests(bidRequests); - data = JSON.parse(request.data); - expect(data.imp[0].banner.w).to.equal(300); // width - expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].banner.format).exist.and.to.be.an('array'); - expect(data.imp[0].banner.format[0]).exist.and.to.be.an('object'); - expect(data.imp[0].banner.format[0].w).to.equal(300); // width - expect(data.imp[0].banner.format[0].h).to.equal(250); // height - }); - it('Request params currency check', function() { - var bidRequest = [{ - bidder: 'lemma', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600] - ], - } - }, - params: { - pubId: 1001, - adunitId: 1, - currency: 'AUD' - }, - sizes: [ - [300, 250], - [300, 600] - ] - }]; - /* case 1 - - currency specified in adunits - output: imp[0] use currency specified in bidRequests[0].params.currency - */ - var request = spec.buildRequests(bidRequest); - var data = JSON.parse(request.data); - expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); - /* case 2 - - currency specified in adunit - output: imp[0] use default currency - USD - */ - delete bidRequest[0].params.currency; - request = spec.buildRequests(bidRequest); - data = JSON.parse(request.data); - expect(data.imp[0].bidfloorcur).to.equal('USD'); - }); - it('Request params check for video ad', function() { - var request = spec.buildRequests(videoBidRequests); - var data = JSON.parse(request.data); - expect(data.imp[0].video).to.exist; - expect(data.imp[0].tagid).to.equal('1'); - expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].params.video['mimes'][0]); - expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].params.video['mimes'][1]); - expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); - expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); - expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); - }); - describe('setting imp.floor using floorModule', function() { - /* - Use the minimum value among floor from floorModule per mediaType - If params.bidFloor is set then take max(floor, min(floors from floorModule)) - set imp.bidfloor only if it is more than 0 - */ - - let newRequest; - let floorModuleTestData; - let getFloor = function(req) { - return floorModuleTestData[req.mediaType]; - }; - - beforeEach(() => { - floorModuleTestData = { - 'banner': { - 'currency': 'AUD', - 'floor': 1.50 - }, - 'video': { - 'currency': 'AUD', - 'floor': 2.00 - } - }; - newRequest = utils.deepClone(bidRequests); - newRequest[0].getFloor = getFloor; - }); - - it('bidfloor should be undefined if calculation is <= 0', function() { - floorModuleTestData.banner.floor = 0; // lowest of them all - newRequest[0].params.bidFloor = undefined; - let request = spec.buildRequests(newRequest); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(undefined); - }); - - it('ignore floormodule o/p if floor is not number', function() { - floorModuleTestData.banner.floor = 'INR'; - newRequest[0].params.bidFloor = undefined; - let request = spec.buildRequests(newRequest); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(undefined); // video will be lowest now - }); - - it('ignore floormodule o/p if currency is not matched', function() { - floorModuleTestData.banner.currency = 'INR'; - newRequest[0].params.bidFloor = undefined; - let request = spec.buildRequests(newRequest); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(undefined); // video will be lowest now - }); - - it('bidFloor is not passed, use minimum from floorModule', function() { - newRequest[0].params.bidFloor = undefined; - let request = spec.buildRequests(newRequest); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(1.5); - }); - - it('bidFloor is passed as 1, use min of floorModule as it is highest', function() { - newRequest[0].params.bidFloor = '1.0';// yes, we want it as a string - let request = spec.buildRequests(newRequest); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(1.5); - }); - }); - describe('Response checking', function() { - it('should check for valid response values', function() { - var request = spec.buildRequests(bidRequests); - var data = JSON.parse(request.data); - var 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].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) { - expect(response[0].creativeId).to.equal(bidResponses.body.seatbid[0].bid[0].crid); - } else { - expect(response[0].creativeId).to.equal(bidResponses.body.seatbid[0].bid[0].id); - } - expect(response[0].dealId).to.equal(bidResponses.body.seatbid[0].bid[0].dealid); - expect(response[0].currency).to.equal('USD'); - expect(response[0].netRevenue).to.equal(false); - expect(response[0].ttl).to.equal(300); - }); - }); - }); - describe('getUserSyncs', function() { - const syncurl_iframe = 'https://sync.lemmatechnologies.com/js/usersync.html?pid=1001'; - let sandbox; - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - afterEach(function() { - sandbox.restore(); - }); - - it('execute as per config', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: syncurl_iframe - }]); - }); - }); - }); -}); diff --git a/test/spec/modules/lifestreetBidAdapter_spec.js b/test/spec/modules/lifestreetBidAdapter_spec.js deleted file mode 100644 index d66727da644..00000000000 --- a/test/spec/modules/lifestreetBidAdapter_spec.js +++ /dev/null @@ -1,232 +0,0 @@ -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: '', - 'adid': '56380110', - 'cid': '44724710', - 'crid': '443801010', - 'w': 300, - 'h': 250, - 'ext': { - 'prebid': { - 'targeting': { - 'hb_bidder': 'luponmedia', - 'hb_pb': '0.40', - 'hb_size': '300x250' - }, - 'type': 'banner' - } - } - } - ], - 'seat': 'luponmedia' - } - ], - 'cur': 'USD', - 'ext': { - 'responsetimemillis': { - 'luponmedia': 233 - }, - 'tmaxrequest': 1500, - 'usersyncs': { - 'status': 'ok', - 'bidder_status': [] - } - } - }; - - let expectedResponse = [ - { - 'requestId': '2a122246ef72ea', - 'cpm': '0.43', - 'width': 300, - 'height': 250, - 'creativeId': '443801010', - 'currency': 'USD', - 'dealId': '23425', - 'netRevenue': false, - 'ttl': 300, - 'referrer': '', - 'ad': ' ' - } - ]; - - let bidderRequest = { - 'data': '{"site":{"page":"https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines"}}' - }; - - let result = spec.interpretResponse({ body: response }, bidderRequest); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function () { - let noBidResponse = []; - - let noBidBidderRequest = { - 'data': '{"site":{"page":""}}' - } - let noBidResult = spec.interpretResponse({ body: noBidResponse }, noBidBidderRequest); - expect(noBidResult.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function () { - const bidResponse1 = { - 'body': { - 'ext': { - 'responsetimemillis': { - 'luponmedia': 233 - }, - 'tmaxrequest': 1500, - 'usersyncs': { - 'status': 'ok', - 'bidder_status': [ - { - 'bidder': 'luponmedia', - 'no_cookie': true, - 'usersync': { - 'url': 'https://adxpremium.services/api/usersync', - 'type': 'redirect' - } - }, - { - 'bidder': 'luponmedia', - 'no_cookie': true, - 'usersync': { - 'url': 'https://adxpremium.services/api/iframeusersync', - 'type': 'iframe' - } - } - ] - } - } - } - }; - - const bidResponse2 = { - 'body': { - 'ext': { - 'responsetimemillis': { - 'luponmedia': 233 - }, - 'tmaxrequest': 1500, - 'usersyncs': { - 'status': 'no_cookie', - 'bidder_status': [] - } - } - } - }; - - it('should use a sync url from first response (pixel and iframe)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://adxpremium.services/api/usersync' - }, - { - type: 'iframe', - url: 'https://adxpremium.services/api/iframeusersync' - } - ]); - }); - - it('handle empty response (e.g. timeout)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('returns empty syncs when not pixel enabled and not iframe enabled', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([]); - }); - - it('returns pixel syncs when pixel enabled and not iframe enabled', function() { - resetUserSync(); - - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://adxpremium.services/api/usersync' - } - ]); - }); - - it('returns iframe syncs when not pixel enabled and iframe enabled', function() { - resetUserSync(); - - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://adxpremium.services/api/iframeusersync' - } - ]); - }); - }); - - describe('hasValidSupplyChainParams', function () { - it('returns true if schain is valid', function () { - const schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'novi.ba', - 'sid': '199424', - 'hp': 1 - } - ] - }; - - const checkSchain = hasValidSupplyChainParams(schain); - expect(checkSchain).to.equal(true); - }); - - it('returns false if schain is invalid', function () { - const schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'invalid': 'novi.ba' - } - ] - }; - - const checkSchain = hasValidSupplyChainParams(schain); - expect(checkSchain).to.equal(false); - }); - }); - - describe('onBidWon', function () { - const bidWonEvent = { - 'bidderCode': 'luponmedia', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '105bbf8c54453ff', - 'requestId': '934b8752185955', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 0.364, - 'creativeId': '443801010', - 'currency': 'USD', - 'netRevenue': false, - 'ttl': 300, - 'referrer': '', - 'ad': '', - 'auctionId': '926a8ea3-3dd4-4bf2-95ab-c85c2ce7e99b', - 'responseTimestamp': 1598527728026, - 'requestTimestamp': 1598527727629, - 'bidder': 'luponmedia', - 'adUnitCode': 'div-gpt-ad-1533155193780-5', - 'timeToRespond': 397, - 'size': '300x250', - 'status': 'rendered' - }; - - let ajaxStub; - - beforeEach(() => { - ajaxStub = sinon.stub(spec, 'sendWinningsToServer') - }) - - afterEach(() => { - ajaxStub.restore() - }) - - it('calls luponmedia\'s callback endpoint', () => { - const result = spec.onBidWon(bidWonEvent); - expect(result).to.equal(undefined); - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.deep.equal(JSON.stringify(bidWonEvent)); - }); - }); -}); diff --git a/test/spec/modules/madvertiseBidAdapter_spec.js b/test/spec/modules/madvertiseBidAdapter_spec.js deleted file mode 100644 index 041b49ef69e..00000000000 --- a/test/spec/modules/madvertiseBidAdapter_spec.js +++ /dev/null @@ -1,203 +0,0 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import * as utils from 'src/utils.js'; -import {spec} from 'modules/madvertiseBidAdapter.js'; - -describe('madvertise adapater', function () { - describe('Test validate req', function () { - it('should accept minimum valid bid', function () { - let bid = { - bidder: 'madvertise', - sizes: [[728, 90]], - params: { - s: 'test' - } - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(true); - }); - it('should reject no sizes', function () { - let bid = { - bidder: 'madvertise', - params: { - s: 'test' - } - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(false); - }); - it('should reject empty sizes', function () { - let bid = { - bidder: 'madvertise', - sizes: [], - params: { - s: 'test' - } - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(false); - }); - it('should reject wrong format sizes', function () { - let bid = { - bidder: 'madvertise', - sizes: [['728x90']], - params: { - s: 'test' - } - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - it('should reject no params', function () { - let bid = { - bidder: 'madvertise', - sizes: [[728, 90]] - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(false); - }); - it('should reject missing s', function () { - let bid = { - bidder: 'madvertise', - params: {} - }; - const isValid = spec.isBidRequestValid(bid); - - expect(isValid).to.equal(false); - }); - }); - - describe('Test build request', function () { - beforeEach(function () { - let mockConfig = { - consentManagement: { - cmpApi: 'IAB', - timeout: 1111, - allowAuctionWithoutConsent: 'cancel' - } - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - }); - afterEach(function () { - config.getConfig.restore(); - }); - let bid = [{ - bidder: 'madvertise', - sizes: [[728, 90], [300, 100]], - bidId: '51ef8751f9aead', - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - auctionId: '18fd8b8b0bd757', - bidderRequestId: '418b37f85e772c', - params: { - s: 'test', - } - }]; - it('minimum request with gdpr consent', function () { - let bidderRequest = { - gdprConsent: { - consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: {}, - gdprApplies: true - } - }; - const req = spec.buildRequests(bid, bidderRequest); - - expect(req).to.exist.and.to.be.a('array'); - expect(req[0]).to.have.property('method'); - expect(req[0].method).to.equal('GET'); - expect(req[0]).to.have.property('url'); - expect(req[0].url).to.contain('https://mobile.mng-ads.com/?rt=bid_request&v=1.0'); - expect(req[0].url).to.contain(`&s=test`); - expect(req[0].url).to.contain(`&sizes[0]=728x90`); - expect(req[0].url).to.contain(`&gdpr=1`); - expect(req[0].url).to.contain(`&consent[0][format]=IAB`); - expect(req[0].url).to.contain(`&consent[0][value]=BOJ/P2HOJ/P2HABABMAAAAAZ+A==`) - }); - - it('minimum request without gdpr consent', function () { - let bidderRequest = {}; - const req = spec.buildRequests(bid, bidderRequest); - - expect(req).to.exist.and.to.be.a('array'); - expect(req[0]).to.have.property('method'); - expect(req[0].method).to.equal('GET'); - expect(req[0]).to.have.property('url'); - expect(req[0].url).to.contain('https://mobile.mng-ads.com/?rt=bid_request&v=1.0'); - expect(req[0].url).to.contain(`&s=test`); - expect(req[0].url).to.contain(`&sizes[0]=728x90`); - expect(req[0].url).not.to.contain(`&gdpr=1`); - expect(req[0].url).not.to.contain(`&consent[0][format]=`); - expect(req[0].url).not.to.contain(`&consent[0][value]=`) - }); - }); - - describe('Test interpret response', function () { - it('General banner response', function () { - let bid = { - bidder: 'madvertise', - sizes: [[728, 90]], - bidId: '51ef8751f9aead', - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - auctionId: '18fd8b8b0bd757', - bidderRequestId: '418b37f85e772c', - params: { - s: 'test', - connection_type: 'WIFI', - age: 25, - } - }; - let resp = spec.interpretResponse({body: { - requestId: 'REQUEST_ID', - cpm: 1, - ad: '

I am an ad

', - Width: 320, - height: 50, - creativeId: 'CREATIVE_ID', - dealId: 'DEAL_ID', - ttl: 180, - currency: 'EUR', - netRevenue: true - }}, {bidId: bid.bidId}); - - expect(resp).to.exist.and.to.be.a('array'); - expect(resp[0]).to.have.property('requestId', bid.bidId); - expect(resp[0]).to.have.property('cpm', 1); - expect(resp[0]).to.have.property('width', 320); - expect(resp[0]).to.have.property('height', 50); - expect(resp[0]).to.have.property('ad', '

I am an ad

'); - expect(resp[0]).to.have.property('ttl', 180); - expect(resp[0]).to.have.property('creativeId', 'CREATIVE_ID'); - expect(resp[0]).to.have.property('netRevenue', true); - expect(resp[0]).to.have.property('currency', 'EUR'); - expect(resp[0]).to.have.property('dealId', 'DEAL_ID'); - }); - it('No response', function () { - let bid = { - bidder: 'madvertise', - sizes: [[728, 90]], - bidId: '51ef8751f9aead', - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - auctionId: '18fd8b8b0bd757', - bidderRequestId: '418b37f85e772c', - params: { - s: 'test', - connection_type: 'WIFI', - age: 25, - } - }; - let resp = spec.interpretResponse({body: null}, {bidId: bid.bidId}); - - expect(resp).to.exist.and.to.be.a('array').that.is.empty; - }); - }); -}); diff --git a/test/spec/modules/mantisBidAdapter_spec.js b/test/spec/modules/mantisBidAdapter_spec.js deleted file mode 100644 index 579f41e620d..00000000000 --- a/test/spec/modules/mantisBidAdapter_spec.js +++ /dev/null @@ -1,361 +0,0 @@ -import {expect} from 'chai'; -import {spec, storage} from 'modules/mantisBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {sfPostMessage, iframePostMessage} from 'modules/mantisBidAdapter'; - -describe('MantisAdapter', function () { - const adapter = newBidder(spec); - const sandbox = sinon.sandbox.create(); - let clock; - - beforeEach(function () { - clock = sandbox.useFakeTimers(); - }); - - afterEach(function () { - sandbox.restore(); - }); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'mantis', - 'params': { - 'property': '10433394', - 'zone': 'zone' - }, - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('viewability', function() { - it('iframe (viewed)', () => { - let viewed = false; - - sandbox.stub(document, 'getElementsByTagName').withArgs('iframe').returns([ - { - name: 'mantis', - getBoundingClientRect: () => ({ - top: 10, - bottom: 260, - left: 10, - right: 190, - width: 300, - height: 250 - }) - } - ]); - - iframePostMessage({innerHeight: 500, innerWidth: 500}, 'mantis', () => viewed = true); - - sandbox.clock.runAll(); - - expect(viewed).to.equal(true); - }); - - it('safeframe (viewed)', () => { - let viewed = false; - - sfPostMessage({ - ext: { - register: (width, height, callback) => { - expect(width).to.equal(100); - expect(height).to.equal(200); - - callback(); - }, - inViewPercentage: () => 60 - } - }, 100, 200, () => viewed = true); - - expect(viewed).to.equal(true); - }); - - it('safeframe (unviewed)', () => { - let viewed = false; - - sfPostMessage({ - ext: { - register: (width, height, callback) => { - expect(width).to.equal(100); - expect(height).to.equal(200); - - callback(); - }, - inViewPercentage: () => 30 - } - }, 100, 200, () => viewed = true); - - expect(viewed).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'mantis', - 'params': { - 'property': '10433394', - 'zone': 'zone' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('gdpr consent not required', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: false}}); - - expect(request.url).not.to.include('consent=false'); - }); - - it('gdpr consent required', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: true}}); - - expect(request.url).to.include('consent=false'); - }); - - it('usp consent', function () { - const request = spec.buildRequests(bidRequests, {uspConsent: 'foobar'}); - - expect(request.url).to.include('usp=foobar'); - }); - - it('domain override', function () { - window.mantis_domain = 'https://foo'; - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.include('https://foo'); - - delete window.mantis_domain; - }); - - it('standard request', function () { - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.include('property=10433394'); - expect(request.url).to.include('bids[0][bidId]=30b31c1838de1e'); - expect(request.url).to.include('bids[0][config][zone]=zone'); - expect(request.url).to.include('bids[0][sizes][0][width]=300'); - expect(request.url).to.include('bids[0][sizes][0][height]=250'); - expect(request.url).to.include('bids[0][sizes][1][width]=300'); - expect(request.url).to.include('bids[0][sizes][1][height]=600'); - }); - - it('use window uuid', function () { - window.mantis_uuid = 'foo'; - - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.include('uuid=foo'); - - delete window.mantis_uuid; - }); - - it('use storage uuid', function () { - sandbox.stub(storage, 'hasLocalStorage').callsFake(() => true); - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('mantis:uuid').returns('bar'); - - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.include('uuid=bar'); - }); - - it('detect amp', function () { - var oldContext = window.context; - - window.context = {}; - window.context.tagName = 'AMP-AD'; - window.context.canonicalUrl = 'foo'; - - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.include('amp=true'); - expect(request.url).to.include('url=foo'); - - delete window.context.tagName; - delete window.context.canonicalUrl; - - window.context = oldContext; - }); - }); - - describe('getUserSyncs', function () { - it('iframe', function () { - let result = spec.getUserSyncs({ - iframeEnabled: true - }); - - expect(result[0].type).to.equal('iframe'); - expect(result[0].url).to.include('https://mantodea.mantisadnetwork.com/prebid/iframe'); - }); - - it('pixel', function () { - let result = spec.getUserSyncs({ - pixelEnabled: true - }); - - expect(result[0].type).to.equal('image'); - expect(result[0].url).to.include('https://mantodea.mantisadnetwork.com/prebid/pixel'); - }); - }); - - describe('interpretResponse', function () { - it('use ad ttl if provided', function () { - let response = { - body: { - ttl: 360, - uuid: 'uuid', - ads: [ - { - bid: 'bid', - cpm: 1, - view: 'view', - width: 300, - ttl: 250, - height: 250, - html: '' - } - ] - } - }; - - let expectedResponse = [ - { - requestId: 'bid', - cpm: 1, - width: 300, - height: 250, - ttl: 250, - ad: '', - creativeId: 'view', - netRevenue: true, - meta: { - advertiserDomains: [] - }, - currency: 'USD' - } - ]; - let bidderRequest; - - let result = spec.interpretResponse(response, {bidderRequest}); - expect(result[0]).to.deep.equal(expectedResponse[0]); - }); - - it('use global ttl if provded', function () { - let response = { - body: { - ttl: 360, - uuid: 'uuid', - ads: [ - { - bid: 'bid', - cpm: 1, - view: 'view', - domains: ['foobar.com'], - width: 300, - height: 250, - html: '' - } - ] - } - }; - - let expectedResponse = [ - { - requestId: 'bid', - cpm: 1, - width: 300, - height: 250, - ttl: 360, - ad: '', - creativeId: 'view', - netRevenue: true, - meta: { - advertiserDomains: ['foobar.com'] - }, - currency: 'USD' - } - ]; - let bidderRequest; - - let result = spec.interpretResponse(response, {bidderRequest}); - expect(result[0]).to.deep.equal(expectedResponse[0]); - }); - - it('display ads returned', function () { - let response = { - body: { - uuid: 'uuid', - ads: [ - { - bid: 'bid', - cpm: 1, - view: 'view', - width: 300, - domains: ['foobar.com'], - height: 250, - html: '' - } - ] - } - }; - - let expectedResponse = [ - { - requestId: 'bid', - cpm: 1, - width: 300, - height: 250, - ttl: 86400, - ad: '', - creativeId: 'view', - netRevenue: true, - meta: { - advertiserDomains: ['foobar.com'] - }, - currency: 'USD' - } - ]; - let bidderRequest; - - sandbox.stub(storage, 'hasLocalStorage').returns(true); - const spy = sandbox.spy(storage, 'setDataInLocalStorage'); - - let result = spec.interpretResponse(response, {bidderRequest}); - - expect(spy.calledWith('mantis:uuid', 'uuid')); - expect(result[0]).to.deep.equal(expectedResponse[0]); - expect(window.mantis_uuid).to.equal(response.body.uuid); - }); - - it('no ads returned', function () { - let response = { - body: { - ads: [] - } - }; - let bidderRequest; - - let result = spec.interpretResponse(response, {bidderRequest}); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/meazyBidAdapter_spec.js b/test/spec/modules/meazyBidAdapter_spec.js deleted file mode 100644 index 2c24791f515..00000000000 --- a/test/spec/modules/meazyBidAdapter_spec.js +++ /dev/null @@ -1,177 +0,0 @@ -import * as utils from 'src/utils.js'; -import { expect } from 'chai'; -import { spec } from 'modules/meazyBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const MEAZY_PID = '6910b7344ae566a1' -const VALID_ENDPOINT = `https://rtb-filter.meazy.co/pbjs?host=${utils.getOrigin()}&api_key=${MEAZY_PID}`; - -const bidderRequest = { - refererInfo: { - referer: 'page', - stack: ['page', 'page1'] - } -}; - -const bidRequest = { - bidder: 'meazy', - adUnitCode: 'test-div', - sizes: [[300, 250], [300, 600]], - params: { - pid: MEAZY_PID - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', -}; - -const bidContent = { - 'id': '30b31c1838de1e', - 'bidid': '9780a52ff05c0e92780f5baf9cf3f4e8', - 'cur': 'USD', - 'seatbid': [{ - 'bid': [{ - 'id': 'ccf05fb8effb3d02', - 'impid': 'B19C34BBD69DAF9F', - 'burl': 'https://track.meazy.co/imp?bidid=9780a52ff05c0e92780f5baf9cf3f4e8&user=fdc401a2-92f1-42bd-ac22-d570520ad0ec&burl=1&ssp=5&project=2&cost=${AUCTION_PRICE}', - 'adm': '', - 'adid': 'ad-2.6.75.300x250', - 'price': 1.5, - 'w': 300, - 'h': 250, - 'cid': '2.6.75', - 'crid': '2.6.75.300x250', - 'dealid': 'default' - }], - 'seat': '2' - }] -}; - -const bidContentExt = { - ...bidContent, - ext: { - 'syncUrl': 'https://sync.meazy.co/sync/img?api_key=6910b7344ae566a1' - } -}; - -const bidResponse = { - body: bidContent -}; - -const noBidResponse = { body: {'nbr': 2} }; - -describe('meazyBidAdapter', 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 false', function () { - let bid = Object.assign({}, bidRequest); - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true', function () { - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - }); - - describe('buildRequests', function () { - it('should format valid url', function () { - const request = spec.buildRequests([bidRequest], bidderRequest); - expect(request.url).to.equal(VALID_ENDPOINT); - }); - - it('should format valid url', function () { - const request = spec.buildRequests([bidRequest], bidderRequest); - expect(request.url).to.equal(VALID_ENDPOINT); - }); - - it('should format valid request body', function () { - const request = spec.buildRequests([bidRequest], bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.id).to.exist; - expect(payload.imp).to.exist; - expect(payload.imp[0]).to.exist; - expect(payload.imp[0].banner).to.exist; - expect(payload.imp[0].banner.format).to.exist; - expect(payload.device).to.exist; - expect(payload.site).to.exist; - expect(payload.site.domain).to.exist; - expect(payload.cur).to.exist; - }); - - it('should format valid url', function () { - const request = spec.buildRequests([bidRequest], bidderRequest); - expect(request.url).to.equal(VALID_ENDPOINT); - }); - - it('should not fill user.ext object', function () { - const request = spec.buildRequests([bidRequest], bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.user.ext).to.equal(undefined); - }); - - it('should fill user.ext object', function () { - const consentString = 'hellogdpr'; - const request = spec.buildRequests([bidRequest], { ...bidderRequest, gdprConsent: { gdprApplies: true, consentString } }); - const payload = JSON.parse(request.data); - expect(payload.user.ext).to.exist.and.to.be.a('object'); - expect(payload.user.ext.consent).to.equal(consentString); - expect(payload.user.ext.gdpr).to.equal(1); - }); - }); - - describe('interpretResponse', function () { - it('should get correct bid response', function () { - const result = spec.interpretResponse(bidResponse); - const validResponse = [{ - requestId: '30b31c1838de1e', - cpm: 1.5, - width: 300, - height: 250, - creativeId: '2.6.75.300x250', - netRevenue: true, - dealId: 'default', - currency: 'USD', - ttl: 900, - ad: '' - }]; - - expect(result).to.deep.equal(validResponse); - }); - - it('handles nobid responses', function () { - let result = spec.interpretResponse(noBidResponse); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function () { - const syncOptionsFF = { iframeEnabled: false }; - const syncOptionsEF = { iframeEnabled: true }; - const syncOptionsEE = { pixelEnabled: true, iframeEnabled: true }; - const syncOptionsFE = { pixelEnabled: true, iframeEnabled: false }; - - const successIFrame = { type: 'iframe', url: 'https://sync.meazy.co/sync/iframe' }; - const successPixel = { type: 'image', url: 'https://sync.meazy.co/sync/img?api_key=6910b7344ae566a1' }; - - it('should return an empty array', function () { - expect(spec.getUserSyncs(syncOptionsFF, [])).to.be.empty; - expect(spec.getUserSyncs(syncOptionsFF, [ bidResponse ])).to.be.empty; - expect(spec.getUserSyncs(syncOptionsFE, [ bidResponse ])).to.be.empty; - }); - - it('should be equal to the expected result', function () { - expect(spec.getUserSyncs(syncOptionsEF, [ bidResponse ])).to.deep.equal([successIFrame]); - expect(spec.getUserSyncs(syncOptionsFE, [ { body: bidContentExt } ])).to.deep.equal([successPixel]); - expect(spec.getUserSyncs(syncOptionsEE, [ { body: bidContentExt } ])).to.deep.equal([successPixel, successIFrame]); - expect(spec.getUserSyncs(syncOptionsEE, [])).to.deep.equal([successIFrame]); - }) - }); -}); diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index be9b528aedd..0b5c4d00f53 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -27,7 +27,7 @@ describe('mediaforce bid adapter', function () { bidder: 'mediaforce', params: { property: '10433394', - bidfloor: 0.3, + bidfloor: 0, }, }; @@ -157,7 +157,7 @@ describe('mediaforce bid adapter', function () { it('should return proper banner imp', function () { let bid = utils.deepClone(defaultBid); - bid.params.bidfloor = 0.5; + bid.params.bidfloor = 0; let bidRequests = [bid]; let bidderRequest = { @@ -219,7 +219,7 @@ describe('mediaforce bid adapter', function () { assert.deepEqual(request, { method: 'POST', url: requestUrl, - data: '{"id":"' + data.id + '","site":{"page":"' + pageUrl + '","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"ext":{"mediaforce":{"hb_key":"210a474e-88f0-4646-837f-4253b7cf14fb"}},"tmax":1500,"imp":[{"tagid":"202","secure":' + secure + ',"bidfloor":0.5,"ext":{"mediaforce":{"transactionId":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b"}},"banner":{"w":300,"h":250},"native":{"ver":"1.2","request":{"assets":[{"required":1,"id":1,"title":{"len":800}},{"required":1,"id":3,"img":{"type":3,"w":300,"h":250}},{"required":1,"id":5,"data":{"type":1}}],"context":1,"plcmttype":1,"ver":"1.2"}}}]}', + data: '{"id":"' + data.id + '","site":{"page":"' + pageUrl + '","ref":"https%3A%2F%2Fwww.prebid.org","id":"pub123","publisher":{"id":"pub123"}},"device":{"ua":"' + navigator.userAgent + '","js":1,"dnt":' + dnt + ',"language":"' + language + '"},"ext":{"mediaforce":{"hb_key":"210a474e-88f0-4646-837f-4253b7cf14fb"}},"tmax":1500,"imp":[{"tagid":"202","secure":' + secure + ',"bidfloor":0,"ext":{"mediaforce":{"transactionId":"d45dd707-a418-42ec-b8a7-b70a6c6fab0b"}},"banner":{"w":300,"h":250},"native":{"ver":"1.2","request":{"assets":[{"required":1,"id":1,"title":{"len":800}},{"required":1,"id":3,"img":{"type":3,"w":300,"h":250}},{"required":1,"id":5,"data":{"type":1}}],"context":1,"plcmttype":1,"ver":"1.2"}}}]}', }); }); diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js deleted file mode 100644 index 25cfc72672b..00000000000 --- a/test/spec/modules/mediagoBidAdapter_spec.js +++ /dev/null @@ -1,101 +0,0 @@ -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' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 - ] - ] - } - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '5e24a2ce-db03-4565-a8a3-75dbddca9377', - 'sizes': [ - [ - 300, - 250 - ] - ], - 'bidId': '54d73f19c9d47a', - 'bidderRequestId': '4fec04e87ad785', - 'auctionId': '7fae02a9-0195-472f-ba94-708d3bc2c0d9', - '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); - // console.log(request); - let req_data = JSON.parse(request.data); - expect(req_data.imp).to.have.lengthOf(1); - }); - - it('mediago:validate_response_params', function() { - let serverResponse = { - body: { - 'id': '7244645c-a81a-4760-8fd6-9d908d2c4a44', - 'seatbid': [{ - 'bid': [{ - 'id': 'aa86796a857ebedda9a2d7128a87dab1', - 'impid': '1', - 'price': 0.05, - 'nurl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=1\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=-BFDu2NYtc4PYTplFW_2JcnDSRVLOOfaERbwJABjFyG6NUB4ywA3dUaXt5zPlyCUpBCOxjH9gk4E6yWTshzuSfQSx7g_TxvcXYUgh7YtY9NQZxx14InmNCTsezqID5UztV7llz8SXWHQ-ZsutH1nJIZzl1jH3i2uCPi91shqIZLN1bLJ5guAr5O4WyxVeOqIKyD_GiVcY9Olm51iI_3wgwFyDEN_dIDv-ObgNxpbPD0L11-62bjhGw3__7RuEo6XLdox-g46Fcqk6i0zayfsPM4QeMAhWJ4lsg-xswSI0YAfzyoOIeTWB78mdpt_GmN5PKZZPqyO7VkbwHEasn-mTyYTddbz5v2fzEkRO0AQZtAZx96PANGrNvcOHnRVmCdkzN96b5Ur1_8ipdyzHOFRtJ-z_KmKaxig6himvMCePozZvrvihiGhigP4RGiFT7ytVYKHyUGAV2PF5SwtgnB0uGCltd7o1CLhZyZEQNgE7LSESyGztZ5kM9N_VZV9gPZVhvlJDfYFNRW9i6D2pZxV0Gd63rA9gpeUJ3mhbkj-B27VRKrNTBSrwIAU7P0RPD5_opl3G8nPD1Ce2vKuQK8qynHWQblfeA61nDok-fRezSKbzwepqi8oxXadFrCmN7KxP_mPqA794xYzIw5-mS64NA', - 'burl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=2\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=dXerAvyp4zYQzsQ56eGB4JtiA4yFaYlTqcHffccrvCg', - 'lurl': 'http://d21uzv52i0cqie.cloudfront.net/api/winnotice?tn=341443089c0cb829164455a42d216ee3\u0026winloss=0\u0026id=aa86796a857ebedda9a2d7128a87dab1\u0026seat_id=${AUCTION_SEAT_ID}\u0026currency=${AUCTION_CURRENCY}\u0026bid_id=${AUCTION_BID_ID}\u0026ad_id=${AUCTION_AD_ID}\u0026loss=${AUCTION_LOSS}\u0026imp_id=1\u0026price=${AUCTION_PRICE}\u0026test=0\u0026time=1597714943\u0026adp=Dtnz0O4U8sdAU-XGGijCAgMbjDIeMGyCLXeSg1laXxM\u0026dsp_id=22\u0026url=ptSxg_vR7-fdx-WAkkUADJb__BntE5a6-RSeYdUewvk', - 'adm': '\u003clink rel=\"stylesheet\" href=\"//cdn.mediago.io/js/style/style_banner_300*250.css\"\u003e\u003cdiv id=\"mgcontainer-583ce3286b442001205b2fb9a5488efc\" class=\"mediago-placement imgTopTitleBottom\" style=\"position:relative;width:298px;height:248px;overflow:hidden\"\u003e\u003ca href=\"http://trace.mediago.io/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=102\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026clickid=25_aa86796a857ebedda9a2d7128a87dab1_25-300x175-1\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\u0026jt=2\u0026url=PQFFci337KgCVkx7KTgRItClLaWH0lgTcIlgBRTpfKngVJES4uKLfxXz9mjLcDWIbWQcEVVk_gfTcIaK8oKO2YyVG5lc3hjZeZr0VaIDHbWggPJaqtfDK9T0HZIKvrpe\" target=\"_blank\"\u003e\u003cimg alt=\"Robert Vowed To Keep Silent, But Decided To Put The People First And Speak\" src=\"https://d2cli4kgl5uxre.cloudfront.net/ML/b5e361889beef5eaf69987384b7a56e8__300x175.png\" style=\"height:70%;width:100%;border-width:0;border:none;\"\u003e\u003ch3 class=\"title\" style=\"font-size:16px;\"\u003eRobert Vowed To Keep Silent, But Decided To Put The People First And Speak\u003c/h3\u003e\u003c/a\u003e\u003cspan class=\"source\"\u003e\u003ca class=\"sourcename\" href=\"//www.mediago.io\" target=\"_blank\"\u003e\u003cspan\u003eAd\u003c/span\u003e \u003c/a\u003e\u003ca class=\"srcnameadslabelurl\" href=\"//www.mediago.io/privacy\" target=\"_blank\"\u003e\u003cspan\u003eViral Net News\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/div\u003e\u003cscript\u003e!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){\"undefined\"!=typeof Symbol\u0026\u0026Symbol.toStringTag\u0026\u0026Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},n.t=function(e,t){if(1\u0026t\u0026\u0026(e=n(e)),8\u0026t)return e;if(4\u0026t\u0026\u0026\"object\"==typeof e\u0026\u0026e\u0026\u0026e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,\"default\",{enumerable:!0,value:e}),2\u0026t\u0026\u0026\"string\"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e\u0026\u0026e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\"a\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\"\",n(n.s=24)}({24:function(e,t,n){\"use strict\";function o(e){var t=new Image;t.src=e,t.style=\"display:none;visibility:hidden\",t.width=0,t.height=0,document.body.appendChild(t)}o(\"http://d21uzv52i0cqie.cloudfront.net/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=101\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\");var r=document.getElementById(\"mgcontainer-583ce3286b442001205b2fb9a5488efc\"),i=!1;!function e(){setTimeout((function(){var t,n;!i\u0026\u0026(t=r,n=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,(t.getBoundingClientRect()\u0026\u0026t.getBoundingClientRect().top)\u003c=n-.75*(t.offsetHeight||t.clientHeight))?(i=!0,o(\"http://d21uzv52i0cqie.cloudfront.net/api/bidder/track?tn=341443089c0cb829164455a42d216ee3\u0026price=PRMC8pCHtH55ipgXubUs8jlsKTBxWRSEweH8Mee_aUQ\u0026evt=104\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026campaignid=1003540\u0026impid=25-300x175-1\u0026offerid=1107782\u0026test=0\u0026time=1597714943\u0026cp=GJA03gjm-ugPTmN7prOpvhfu1aA04IgpTcW4oRX22Lg\u0026acid=164\u0026trackingid=583ce3286b442001205b2fb9a5488efc\u0026uid=6dda6c6b70eb4e2d9ab3469d921f2c74\u0026sid=16__11__13\u0026format=\u0026crid=b5e361889beef5eaf69987384b7a56e8\")):e()}),500)}()}});\u003c/script\u003e\u003cscript type=\"text/javascript\" src=\"http://d21uzv52i0cqie.cloudfront.net/api/track?tn=341443089c0cb829164455a42d216ee3\u0026price=${AUCTION_PRICE}\u0026evt=5\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026impid=1\u0026offerid=\u0026tagid=\u0026test=0\u0026time=1597714943\u0026adp=5zrCZ2rC-WLafYkNpeTnzA72tDqVZUlOA3Js6_eJjYU\u0026dsp_id=22\u0026cp=${cp}\u0026url=\u0026type=script\"\u003e\u003c/script\u003e\u003cscript\u003edocument.addEventListener\u0026\u0026document.addEventListener(\"click\",function(){var a=document.createElement(\"script\");a.src=\"http://d21uzv52i0cqie.cloudfront.net/api/track?tn=341443089c0cb829164455a42d216ee3\u0026price=${AUCTION_PRICE}\u0026evt=6\u0026rid=aa86796a857ebedda9a2d7128a87dab1\u0026impid=1\u0026offerid=\u0026tagid=\u0026test=0\u0026time=1597714943\u0026adp=5zrCZ2rC-WLafYkNpeTnzA72tDqVZUlOA3Js6_eJjYU\u0026dsp_id=22\u0026cp=${cp}\u0026url=\u0026clickid=25_aa86796a857ebedda9a2d7128a87dab1_1\";document.body.appendChild(a)});\u003c/script\u003e', - 'cid': '1003540', - 'crid': 'b5e361889beef5eaf69987384b7a56e8', - '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('b5e361889beef5eaf69987384b7a56e8'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.currency).to.equal('USD'); - }); -}); diff --git a/test/spec/modules/mgidBidAdapter_spec.js b/test/spec/modules/mgidBidAdapter_spec.js deleted file mode 100644 index 16f4f0b4607..00000000000 --- a/test/spec/modules/mgidBidAdapter_spec.js +++ /dev/null @@ -1,702 +0,0 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/mgidBidAdapter.js'; -import * as utils from '../../../src/utils.js'; - -describe('Mgid bid adapter', function () { - let sandbox; - let logErrorSpy; - let logWarnSpy; - beforeEach(function () { - sandbox = sinon.sandbox.create(); - logErrorSpy = sinon.spy(utils, 'logError'); - logWarnSpy = sinon.spy(utils, 'logWarn'); - }); - - afterEach(function () { - sandbox.restore(); - utils.logError.restore(); - utils.logWarn.restore(); - }); - const ua = navigator.userAgent; - const screenHeight = screen.height; - const screenWidth = screen.width; - const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; - const language = navigator.language ? 'language' : 'userLanguage'; - let lang = navigator[language].split('-')[0]; - if (lang.length != 2 && lang.length != 3) { - lang = ''; - } - const secure = window.location.protocol === 'https:' ? 1 : 0; - const mgid_ver = spec.VERSION; - const prebid_ver = $$PREBID_GLOBAL$$.version; - const utcOffset = (new Date()).getTimezoneOffset().toString(); - - describe('isBidRequestValid', function () { - let bid = { - 'adUnitCode': 'div', - 'bidder': 'mgid', - 'params': { - 'property': '10433394', - 'zone': 'zone' - }, - }; - - it('should not accept bid without required params', function () { - let isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - - it('should return false when params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '', placementId: ''}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.adUnitCode = ''; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - bid.params = {accountId: 2, placementId: 1}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when adUnitCode not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.adUnitCode = ''; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - bid.params = {accountId: 2, placementId: 1}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when valid params are passed as nums', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.adUnitCode = 'div'; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - bid.params = {accountId: 2, placementId: 1}; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when valid params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.mediaTypes = { - native: { - sizes: [[300, 250]] - } - }; - bid.params = {accountId: '0', placementId: '00'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid mediaTypes are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid mediaTypes.banner are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid mediaTypes.banner.sizes are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - sizes: [] - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when valid mediaTypes.banner.sizes are not valid', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - sizes: [300, 250] - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when valid params are passed as strings', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.adUnitCode = 'div'; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when valid mediaTypes.native is not object', function () { - let bid = Object.assign({}, bid); - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - native: [] - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when mediaTypes.native is empty object', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - native: {} - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when mediaTypes.native is invalid object', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; - bid.mediaTypes = { - native: { - image: { - sizes: [80, 80] - }, - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when mediaTypes.native has unsupported required asset', function () { - let bid = Object.assign({}, bid); - bid.params = {accountId: '2', placementId: '1'}; - bid.mediaTypes = { - native: { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, - }, - }; - bid.nativeParams = { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, - unsupported: {required: true}, - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when mediaTypes.native all assets needed', function () { - let bid = Object.assign({}, bid); - bid.adUnitCode = 'div'; - bid.params = {accountId: '2', placementId: '1'}; - bid.mediaTypes = { - native: { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, - }, - }; - bid.nativeParams = { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - - describe('override defaults', function () { - let bid = { - bidder: 'mgid', - params: { - accountId: '1', - placementId: '2', - }, - }; - it('should return object', function () { - let bid = Object.assign({}, bid); - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request).to.exist.and.to.be.a('object'); - }); - - it('should return overwrite default bidurl', function () { - let bid = Object.assign({}, bid); - bid.params = { - bidUrl: 'https://newbidurl.com/', - accountId: '1', - placementId: '2', - }; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request.url).to.include('https://newbidurl.com/1'); - }); - it('should return overwrite default bidFloor', function () { - let bid = Object.assign({}, bid); - bid.params = { - bidFloor: 1.1, - accountId: '1', - placementId: '2', - }; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request.data).to.be.a('string'); - const data = JSON.parse(request.data); - expect(data).to.be.a('object'); - expect(data.imp).to.be.a('array'); - expect(data.imp).to.have.lengthOf(1); - expect(data.imp[0].bidfloor).to.deep.equal(1.1); - }); - it('should return overwrite default currency', function () { - let bid = Object.assign({}, bid); - bid.params = { - cur: 'GBP', - accountId: '1', - placementId: '2', - }; - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request.data).to.be.a('string'); - const data = JSON.parse(request.data); - expect(data).to.be.a('object'); - expect(data.cur).to.deep.equal(['GBP']); - }); - }); - - describe('buildRequests', function () { - it('should return undefined if no validBidRequests passed', function () { - expect(spec.buildRequests([])).to.be.undefined; - }); - - let abid = { - adUnitCode: 'div', - bidder: 'mgid', - params: { - accountId: '1', - placementId: '2', - }, - }; - it('should return proper request url', function () { - localStorage.setItem('mgMuidn', 'xxx'); - let bid = Object.assign({}, abid); - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1?muid=xxx'); - localStorage.removeItem('mgMuidn') - }); - it('should proper handle gdpr', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'gdpr', gdprApplies: true}}); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.user).deep.equal({ext: {consent: 'gdpr'}}); - expect(data.regs).deep.equal({ext: {gdpr: 1}}); - }); - it('should return proper banner imp', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - banner: { - sizes: [[300, 250]] - } - }; - let bidRequests = [bid]; - const page = top.location.href; - const domain = utils.parseUrl(page).hostname; - const request = spec.buildRequests(bidRequests); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.site.domain).to.deep.equal(domain); - expect(data.site.page).to.deep.equal(page); - expect(data.cur).to.deep.equal(['USD']); - expect(data.device.ua).to.deep.equal(ua); - expect(data.device.dnt).equal(dnt); - expect(data.device.h).equal(screenHeight); - expect(data.device.w).equal(screenWidth); - expect(data.device.language).to.deep.equal(lang); - expect(data.imp[0].tagid).to.deep.equal('2/div'); - expect(data.imp[0].banner).to.deep.equal({w: 300, h: 250}); - expect(data.imp[0].secure).to.deep.equal(secure); - expect(request).to.deep.equal({ - 'method': 'POST', - 'url': 'https://prebid.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":250}}]}', - }); - }); - it('should not return native imp if minimum asset list not requested', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - native: '', - }; - bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - expect(request).to.be.undefined; - }); - it('should return proper native imp', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - native: '', - }; - bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, - sponsored: { }, - }; - - let bidRequests = [bid]; - const page = top.location.href; - const domain = utils.parseUrl(page).hostname; - const request = spec.buildRequests(bidRequests); - expect(request).to.be.a('object'); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.site.domain).to.deep.equal(domain); - expect(data.site.page).to.deep.equal(page); - expect(data.cur).to.deep.equal(['USD']); - expect(data.device.ua).to.deep.equal(ua); - expect(data.device.dnt).equal(dnt); - expect(data.device.h).equal(screenHeight); - expect(data.device.w).equal(screenWidth); - expect(data.device.language).to.deep.equal(lang); - expect(data.imp[0].tagid).to.deep.equal('2/div'); - expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 11, 'required': 0}], 'plcmtcnt': 1}}); - expect(data.imp[0].secure).to.deep.equal(secure); - expect(request).to.deep.equal({ - 'method': 'POST', - 'url': 'https://prebid.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":11,\"required\":0,\"data\":{\"type\":1}}]}}}]}', - }); - }); - it('should return proper native imp', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - native: '', - }; - bid.nativeParams = { - title: {required: true}, - image: {wmin: 50, hmin: 50, required: true}, - icon: {}, - sponsored: { }, - }; - - let bidRequests = [bid]; - const page = top.location.href; - const domain = utils.parseUrl(page).hostname; - const request = spec.buildRequests(bidRequests); - expect(request).to.be.a('object'); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.site.domain).to.deep.equal(domain); - expect(data.site.page).to.deep.equal(page); - expect(data.cur).to.deep.equal(['USD']); - expect(data.device.ua).to.deep.equal(ua); - expect(data.device.dnt).equal(dnt); - expect(data.device.h).equal(screenHeight); - expect(data.device.w).equal(screenWidth); - expect(data.device.language).to.deep.equal(lang); - expect(data.imp[0].tagid).to.deep.equal('2/div'); - expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 328, hmin: 50, 'type': 3, 'w': 492, wmin: 50}, 'required': 1}, {'id': 3, 'img': {'h': 50, 'type': 1, 'w': 50}, 'required': 0}, {'data': {'type': 1}, 'id': 11, 'required': 0}], 'plcmtcnt': 1}}); - expect(data.imp[0].secure).to.deep.equal(secure); - expect(request).to.deep.equal({ - 'method': 'POST', - 'url': 'https://prebid.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"w\":492,\"h\":328,\"wmin\":50,\"hmin\":50}},{\"id\":3,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":11,\"required\":0,\"data\":{\"type\":1}}]}}}]}', - }); - }); - it('should return proper native imp with sponsoredBy', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - native: '', - }; - bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, - sponsoredBy: { }, - }; - - let bidRequests = [bid]; - const page = top.location.href; - const domain = utils.parseUrl(page).hostname; - const request = spec.buildRequests(bidRequests); - expect(request).to.be.a('object'); - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.site.domain).to.deep.equal(domain); - expect(data.site.page).to.deep.equal(page); - expect(data.cur).to.deep.equal(['USD']); - expect(data.device.ua).to.deep.equal(ua); - expect(data.device.dnt).equal(dnt); - expect(data.device.h).equal(screenHeight); - expect(data.device.w).equal(screenWidth); - expect(data.device.language).to.deep.equal(lang); - expect(data.imp[0].tagid).to.deep.equal('2/div'); - expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 4, 'required': 0}], 'plcmtcnt': 1}}); - expect(data.imp[0].secure).to.deep.equal(secure); - expect(request).to.deep.equal({ - 'method': 'POST', - 'url': 'https://prebid.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":1}}]}}}]}', - }); - }); - it('should return proper banner request', function () { - let bid = Object.assign({}, abid); - bid.mediaTypes = { - banner: { - sizes: [[300, 600], [300, 250]], - } - }; - let bidRequests = [bid]; - const request = spec.buildRequests(bidRequests); - - const page = top.location.href; - const domain = utils.parseUrl(page).hostname; - expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); - expect(request.method).deep.equal('POST'); - const data = JSON.parse(request.data); - expect(data.site.domain).to.deep.equal(domain); - expect(data.site.page).to.deep.equal(page); - expect(data.cur).to.deep.equal(['USD']); - expect(data.device.ua).to.deep.equal(ua); - expect(data.device.dnt).equal(dnt); - expect(data.device.h).equal(screenHeight); - expect(data.device.w).equal(screenWidth); - expect(data.device.language).to.deep.equal(lang); - expect(data.imp[0].tagid).to.deep.equal('2/div'); - expect(data.imp[0].banner).to.deep.equal({w: 300, h: 600, format: [{w: 300, h: 600}, {w: 300, h: 250}]}); - expect(data.imp[0].secure).to.deep.equal(secure); - - expect(request).to.deep.equal({ - 'method': 'POST', - 'url': 'https://prebid.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\",\"page\":\"' + page + '\"},\"cur\":[\"USD\"],\"geo\":{\"utcoffset\":' + utcOffset + '},\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2/div\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":600,\"format\":[{\"w\":300,\"h\":600},{\"w\":300,\"h\":250}]}}]}', - }); - }); - }); - describe('interpretResponse banner', function () { - it('should not push bid response', function () { - let bids = spec.interpretResponse(); - expect(bids).to.be.undefined; - }); - it('should push proper banner bid response', function () { - let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': '', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'https nurl', 'burl': 'https burl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2']}], 'seat': '44082'}]} - }; - let bids = spec.interpretResponse(resp); - expect(bids).to.deep.equal([ - { - 'ad': 'html: adm', - 'cpm': 1.5, - 'creativeId': '2898532/2419121/2592854/2499195', - 'currency': 'USD', - 'dealId': '', - 'height': 600, - 'isBurl': true, - 'mediaType': 'banner', - 'netRevenue': true, - 'nurl': 'https nurl', - 'burl': 'https burl', - 'requestId': '61e40632c53fc2', - 'ttl': 300, - 'width': 300, - } - ]); - }); - }); - describe('interpretResponse native', function () { - it('should not push proper native bid response if adm is missing', function () { - let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'https nurl', 'burl': 'https burl', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} - }; - let bids = spec.interpretResponse(resp); - expect(bids).to.deep.equal([]) - }); - it('should not push proper native bid response if assets is empty', function () { - let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'https nurl', 'burl': 'https burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} - }; - let bids = spec.interpretResponse(resp); - expect(bids).to.deep.equal([]) - }); - it('should push proper native bid response', function () { - let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'https nurl', 'burl': 'https burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}], ext: {'muidn': 'userid'}} - }; - let bids = spec.interpretResponse(resp); - expect(bids).to.deep.equal([{ - 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', - 'burl': 'https burl', - 'cpm': 1.5, - 'creativeId': '2898532/2419121/2592854/2499195', - 'currency': 'GBP', - 'dealId': '', - 'height': 0, - 'isBurl': true, - 'mediaType': 'native', - 'native': { - 'clickTrackers': [], - 'clickUrl': 'link_url', - 'data': 'price1', - 'icon': { - 'height': 50, - 'url': 'icon_src', - 'width': 50 - }, - 'image': { - 'height': 80, - 'url': 'image_src', - 'width': 80 - }, - 'impressionTrackers': [ - 'imptrackers1' - ], - 'jstracker': [], - 'sponsoredBy': 'sponsored', - 'title': 'title1' - }, - 'netRevenue': true, - 'nurl': 'https nurl', - 'requestId': '61e40632c53fc2', - 'ttl': 300, - 'width': 0 - }]) - }); - it('should push proper native bid response', function () { - let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'https nurl', 'burl': 'https burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} - }; - let bids = spec.interpretResponse(resp); - expect(bids).to.deep.equal([ - { - 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', - 'cpm': 1.5, - 'creativeId': '2898532/2419121/2592854/2499195', - 'currency': 'GBP', - 'dealId': '', - 'height': 0, - 'isBurl': true, - 'mediaType': 'native', - 'netRevenue': true, - 'nurl': 'https nurl', - 'burl': 'https burl', - 'requestId': '61e40632c53fc2', - 'ttl': 300, - 'width': 0, - 'native': { - clickTrackers: [], - title: 'title1', - image: { - url: 'image_src', - width: 80, - height: 80, - }, - icon: { - url: 'icon_src', - width: 50, - height: 50, - }, - impressionTrackers: ['imptrackers1'], - jstracker: [], - clickUrl: 'link_url', - } - } - ]); - }); - }); - - describe('getUserSyncs', function () { - it('should do nothing on getUserSyncs', function () { - spec.getUserSyncs() - }); - }); - describe('on bidWon', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('should replace nurl and burl for native', function () { - const burl = 'burl&s=${' + 'AUCTION_PRICE}'; - const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; - const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'native', 'source': 'client', 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"LinkURL\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"TITLE\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"ImageURL\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"IconURL\"}},{\"id\":11,\"required\":0,\"data\":{\"type\":1,\"value\":\"sponsored\"}}],\"imptrackers\":[\"ImpTrackerURL\"]}}', 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'native': {'title': 'TITLE', 'image': {'url': 'ImageURL', 'height': 80, 'width': 80}, 'icon': {'url': 'IconURL', 'height': 50, 'width': 50}, 'sponsored': 'sponsored', 'clickUrl': 'LinkURL', 'clickTrackers': [], 'impressionTrackers': ['ImpTrackerURL'], 'jstracker': []}, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'native', 'hb_native_title': 'TITLE', 'hb_native_image': 'hb_native_image:3d0b6ff1dda89', 'hb_native_icon': 'IconURL', 'hb_native_linkurl': 'hb_native_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; - spec.onBidWon(bid); - expect(bid.nurl).to.deep.equal('nurl&s=0.66'); - expect(bid.burl).to.deep.equal('burl&s=0.66'); - }); - it('should replace nurl and burl for banner', function () { - const burl = 'burl&s=${' + 'AUCTION_PRICE}'; - const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; - const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'banner', 'source': 'client', 'ad': burl, 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'banner', 'hb_banner_title': 'TITLE', 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', 'hb_banner_icon': 'IconURL', 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; - spec.onBidWon(bid); - expect(bid.nurl).to.deep.equal('nurl&s=0.66'); - expect(bid.burl).to.deep.equal(burl); - expect(bid.ad).to.deep.equal('burl&s=0.66'); - }); - }); -}); diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js deleted file mode 100644 index 8298e2bd559..00000000000 --- a/test/spec/modules/microadBidAdapter_spec.js +++ /dev/null @@ -1,388 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/microadBidAdapter.js'; -import * as utils from 'src/utils.js'; - -describe('microadBidAdapter', () => { - const bidRequestTemplate = { - bidder: 'microad', - mediaTypes: { - banner: {} - }, - params: { - spot: 'spot-code' - }, - bidId: 'bid-id', - transactionId: 'transaction-id' - }; - - describe('isBidRequestValid', () => { - it('should return true when required parameters are set', () => { - const validBids = [ - bidRequestTemplate, - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - native: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - video: {} - } - }) - ]; - validBids.forEach(validBid => { - expect(spec.isBidRequestValid(validBid)).to.equal(true); - }); - }); - - it('should return false when required parameters are not set', () => { - const bidWithoutParams = utils.deepClone(bidRequestTemplate); - delete bidWithoutParams.params; - const bidWithoutSpot = utils.deepClone(bidRequestTemplate); - delete bidWithoutSpot.params.spot; - const bidWithoutMediaTypes = utils.deepClone(bidRequestTemplate); - delete bidWithoutMediaTypes.mediaTypes; - - const invalidBids = [ - {}, - bidWithoutParams, - bidWithoutSpot, - bidWithoutMediaTypes, - Object.assign({}, bidRequestTemplate, { - mediaTypes: {} - }) - ]; - invalidBids.forEach(invalidBid => { - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - }); - - describe('buildRequests', () => { - const bidderRequest = { - refererInfo: { - canonicalUrl: 'https://example.com/to', - referer: 'https://example.com/from' - } - }; - const expectedResultTemplate = { - spot: 'spot-code', - url: 'https://example.com/to', - referrer: 'https://example.com/from', - bid_id: 'bid-id', - transaction_id: 'transaction-id', - media_types: 1 - }; - - it('should generate valid media_types', () => { - const bidRequests = [ - bidRequestTemplate, - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - banner: {}, native: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - banner: {}, native: {}, video: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - native: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - native: {}, video: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - video: {} - } - }), - Object.assign({}, bidRequestTemplate, { - mediaTypes: { - banner: {}, video: {} - } - }) - ]; - - const results = bidRequests.map(bid => { - const requests = spec.buildRequests([bid], bidderRequest); - return requests[0].data.media_types; - }); - expect(results).to.deep.equal([ - 1, // BANNER - 3, // BANNER + NATIVE - 7, // BANNER + NATIVE + VIDEO - 2, // NATIVE - 6, // NATIVE + VIDEO - 4, // VIDEO - 5 // BANNER + VIDEO - ]); - }); - - it('should use window.location.href if there is no canonicalUrl', () => { - const bidderRequestWithoutCanonicalUrl = { - refererInfo: { - referer: 'https://example.com/from' - } - }; - const requests = spec.buildRequests([bidRequestTemplate], bidderRequestWithoutCanonicalUrl); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - url: window.location.href - }) - ); - }); - }); - - it('should generate valid request with no optional parameters', () => { - const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt - }) - ); - }); - }); - - it('should add url_macro parameter to response if request parameters contain url', () => { - const bidRequestWithUrl = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - url: '${COMPASS_EXT_URL}url-macro' - } - }); - const requests = spec.buildRequests([bidRequestWithUrl], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - url_macro: 'url-macro' - }) - ); - }); - }); - - it('should add referrer_macro parameter to response if request parameters contain referrer', () => { - const bidRequestWithReferrer = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - referrer: '${COMPASS_EXT_REF}referrer-macro' - } - }); - const requests = spec.buildRequests([bidRequestWithReferrer], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - referrer_macro: 'referrer-macro' - }) - ); - }); - }); - - it('should add ifa parameter to response if request parameters contain ifa', () => { - const bidRequestWithIfa = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - ifa: '${COMPASS_EXT_IFA}ifa' - } - }); - const requests = spec.buildRequests([bidRequestWithIfa], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - ifa: 'ifa' - }) - ); - }); - }); - - it('should add appid parameter to response if request parameters contain appid', () => { - const bidRequestWithAppid = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - appid: '${COMPASS_EXT_APPID}appid' - } - }); - const requests = spec.buildRequests([bidRequestWithAppid], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - appid: 'appid' - }) - ); - }); - }); - - it('should add geo parameter to response if request parameters contain geo', () => { - const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - geo: '${COMPASS_EXT_GEO}35.655275,139.693771' - } - }); - const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt, - geo: '35.655275,139.693771' - }) - ); - }); - }); - - it('should not add geo parameter to response if request parameters contain invalid geo', () => { - const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { - params: { - spot: 'spot-code', - geo: '${COMPASS_EXT_GEO}invalid format geo' - } - }); - const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); - requests.forEach(request => { - expect(request.data).to.deep.equal( - Object.assign({}, expectedResultTemplate, { - cbt: request.data.cbt - }) - ); - }); - }); - - it('should always use the HTTPS endpoint https://s-rtb-pb.send.microad.jp/prebid even if it is served via HTTP', () => { - const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); - requests.forEach(request => { - expect(request.url.lastIndexOf('https', 0) === 0).to.be.true; - }); - }); - }); - - describe('interpretResponse', () => { - const serverResponseTemplate = { - body: { - requestId: 'request-id', - cpm: 0.1, - width: 200, - height: 100, - ad: '
test
', - ttl: 10, - creativeId: 'creative-id', - netRevenue: true, - currency: 'JPY' - } - }; - const expectedBidResponseTemplate = { - requestId: 'request-id', - cpm: 0.1, - width: 200, - height: 100, - ad: '
test
', - ttl: 10, - creativeId: 'creative-id', - netRevenue: true, - currency: 'JPY' - }; - - it('should return nothing if server response body does not contain cpm', () => { - const emptyResponse = { - body: {} - }; - - expect(spec.interpretResponse(emptyResponse)).to.deep.equal([]); - }); - - it('should return nothing if returned cpm is zero', () => { - const serverResponse = { - body: { - cpm: 0 - } - }; - - expect(spec.interpretResponse(serverResponse)).to.deep.equal([]); - }); - - it('should return a valid bidResponse without deal id if serverResponse is valid, has a nonzero cpm and no deal id', () => { - expect(spec.interpretResponse(serverResponseTemplate)).to.deep.equal([expectedBidResponseTemplate]); - }); - - it('should return a valid bidResponse with deal id if serverResponse is valid, has a nonzero cpm and a deal id', () => { - const serverResponseWithDealId = Object.assign({}, utils.deepClone(serverResponseTemplate)); - serverResponseWithDealId.body['dealId'] = 10001; - const expectedBidResponse = Object.assign({}, expectedBidResponseTemplate, { - dealId: 10001 - }); - - expect(spec.interpretResponse(serverResponseWithDealId)).to.deep.equal([expectedBidResponse]); - }); - }); - - describe('getUserSyncs', () => { - const BOTH_ENABLED = { - iframeEnabled: true, pixelEnabled: true - }; - const IFRAME_ENABLED = { - iframeEnabled: true, pixelEnabled: false - }; - const PIXEL_ENABLED = { - iframeEnabled: false, pixelEnabled: true - }; - const BOTH_DISABLED = { - iframeEnabled: false, pixelEnabled: false - }; - const serverResponseTemplate = { - body: { - syncUrls: { - iframe: ['https://www.exmaple.com/iframe1', 'https://www.exmaple.com/iframe2'], - image: ['https://www.exmaple.com/image1', 'https://www.exmaple.com/image2'] - } - } - }; - const expectedIframeSyncs = [ - {type: 'iframe', url: 'https://www.exmaple.com/iframe1'}, - {type: 'iframe', url: 'https://www.exmaple.com/iframe2'} - ]; - const expectedImageSyncs = [ - {type: 'image', url: 'https://www.exmaple.com/image1'}, - {type: 'image', url: 'https://www.exmaple.com/image2'} - ]; - - it('should return nothing if no sync urls are set', () => { - const serverResponse = utils.deepClone(serverResponseTemplate); - serverResponse.body.syncUrls.iframe = []; - serverResponse.body.syncUrls.image = []; - - const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponse]); - expect(syncs).to.deep.equal([]); - }); - - it('should return nothing if sync is disabled', () => { - const syncs = spec.getUserSyncs(BOTH_DISABLED, [serverResponseTemplate]); - expect(syncs).to.deep.equal([]); - }); - - it('should register iframe and image sync urls if sync is enabled', () => { - const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponseTemplate]); - expect(syncs).to.deep.equal(expectedIframeSyncs.concat(expectedImageSyncs)); - }); - - it('should register iframe sync urls if iframe is enabled', () => { - const syncs = spec.getUserSyncs(IFRAME_ENABLED, [serverResponseTemplate]); - expect(syncs).to.deep.equal(expectedIframeSyncs); - }); - - it('should register image sync urls if image is enabled', () => { - const syncs = spec.getUserSyncs(PIXEL_ENABLED, [serverResponseTemplate]); - expect(syncs).to.deep.equal(expectedImageSyncs); - }); - }); -}); diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js deleted file mode 100644 index 026e79c6d5a..00000000000 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import { expect } from 'chai'; -import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('Missena Adapter', function () { - const adapter = newBidder(spec); - - const bidId = 'abc'; - - const bid = { - bidder: 'missena', - bidId: bidId, - sizes: [[1, 1]], - params: { - apiKey: 'PA-34745704', - }, - }; - - describe('codes', function () { - it('should return a bidder code of missena', function () { - expect(spec.code).to.equal('missena'); - }); - }); - - describe('isBidRequestValid', function () { - it('should return true if the apiKey param is present', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false if the apiKey is missing', function () { - expect( - spec.isBidRequestValid(Object.assign(bid, { params: {} })) - ).to.equal(false); - }); - - it('should return false if the apiKey is an empty string', function () { - expect( - spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })) - ).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const consentString = 'AAAAAAAAA=='; - - const bidderRequest = { - gdprConsent: { - consentString: consentString, - gdprApplies: true, - }, - refererInfo: { - referer: 'https://referer', - canonicalUrl: 'https://canonical', - }, - }; - - const requests = spec.buildRequests([bid, bid], bidderRequest); - const request = requests[0]; - const payload = JSON.parse(request.data); - - it('should return as many server requests as bidder requests', function () { - expect(requests.length).to.equal(2); - }); - - it('should have a post method', function () { - expect(request.method).to.equal('POST'); - }); - - it('should send the bidder id', function () { - expect(payload.request_id).to.equal(bidId); - }); - - it('should send referer information to the request', function () { - expect(payload.referer).to.equal('https://referer'); - expect(payload.referer_canonical).to.equal('https://canonical'); - }); - - it('should send gdpr consent information to the request', function () { - expect(payload.consent_string).to.equal(consentString); - expect(payload.consent_required).to.equal(true); - }); - }); - - describe('interpretResponse', function () { - const serverResponse = { - requestId: bidId, - cpm: 0.5, - currency: 'USD', - ad: '', - }; - - const serverTimeoutResponse = { - requestId: bidId, - timeout: true, - ad: '', - }; - - const serverEmptyAdResponse = { - requestId: bidId, - cpm: 0.5, - currency: 'USD', - ad: '', - }; - - it('should return a proper bid response', function () { - const result = spec.interpretResponse({ body: serverResponse }, bid); - - expect(result.length).to.equal(1); - - expect(Object.keys(result[0])).to.have.members( - Object.keys(serverResponse) - ); - }); - - it('should return an empty response when the server answers with a timeout', function () { - const result = spec.interpretResponse( - { body: serverTimeoutResponse }, - bid - ); - expect(result).to.deep.equal([]); - }); - - it('should return an empty response when the server answers with an empty ad', function () { - const result = spec.interpretResponse( - { body: serverEmptyAdResponse }, - bid - ); - expect(result).to.deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/mobfoxBidAdapter_spec.js b/test/spec/modules/mobfoxBidAdapter_spec.js deleted file mode 100644 index d3178d77a1a..00000000000 --- a/test/spec/modules/mobfoxBidAdapter_spec.js +++ /dev/null @@ -1,123 +0,0 @@ -describe('mobfox adapter tests', function () { - const expect = require('chai').expect; - const utils = require('src/utils'); - const adapter = require('modules/mobfoxBidAdapter'); - - const bidRequest = [{ - code: 'div-gpt-ad-1460505748561-0', - sizes: [[320, 480], [300, 250], [300, 600]], - // Replace this object to test a new Adapter! - bidder: 'mobfox', - bidId: '5t5t5t5', - params: { - s: '267d72ac3f77a3f447b32cf7ebf20673', // required - The hash of your inventory to identify which app is making the request, - imp_instl: 1 // optional - set to 1 if using interstitial otherwise delete or set to 0 - }, - placementCode: 'div-gpt-ad-1460505748561-0', - auctionId: 'c241c810-18d9-4aa4-a62f-8c1980d8d36b', - transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' - }]; - - describe('validRequests', function () { - let bidRequestInvalid1 = [{ - code: 'div-gpt-ad-1460505748561-0', - sizes: [[320, 480], [300, 250], [300, 600]], - // Replace this object to test a new Adapter! - bidder: 'mobfox', - bidId: '5t5t5t5', - params: { - imp_instl: 1 // optional - set to 1 if using interstitial otherwise delete or set to 0 - }, - placementCode: 'div-gpt-ad-1460505748561-0', - auctionId: 'c241c810-18d9-4aa4-a62f-8c1980d8d36b', - transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' - }]; - - it('test valid MF request success', function () { - let isValid = adapter.spec.isBidRequestValid(bidRequest[0]); - expect(isValid).to.equal(true); - }); - - it('test valid MF request failed1', function () { - let isValid = adapter.spec.isBidRequestValid(bidRequestInvalid1[0]); - expect(isValid).to.equal(false); - }); - }) - - describe('buildRequests', function () { - it('test build MF request', function () { - let request = adapter.spec.buildRequests(bidRequest); - let payload = request.data.split('&'); - expect(payload[0]).to.equal('rt=api-fetchip'); - expect(payload[1]).to.equal('r_type=banner'); - expect(payload[2]).to.equal('r_resp=json'); - expect(payload[3]).to.equal('s=267d72ac3f77a3f447b32cf7ebf20673'); - expect(payload[5]).to.equal('adspace_width=320'); - expect(payload[6]).to.equal('adspace_height=480'); - expect(payload[7]).to.equal('imp_instl=1'); - }); - - it('test build MF request', function () { - let request = adapter.spec.buildRequests(bidRequest); - let payload = request.data.split('&'); - expect(payload[0]).to.equal('rt=api-fetchip'); - expect(payload[1]).to.equal('r_type=banner'); - expect(payload[2]).to.equal('r_resp=json'); - expect(payload[3]).to.equal('s=267d72ac3f77a3f447b32cf7ebf20673'); - expect(payload[5]).to.equal('adspace_width=320'); - expect(payload[6]).to.equal('adspace_height=480'); - expect(payload[7]).to.equal('imp_instl=1'); - }); - }) - - describe('interceptResponse', function () { - let mockServerResponse = { - body: { - request: { - clicktype: 'safari', - clickurl: 'https://tokyo-my.mobfox.com/exchange.click.php?h=494ef76d5b0287a8b5ac8724855cb5e0', - cpmPrice: 50, - htmlString: 'test', - refresh: '30', - scale: 'no', - skippreflight: 'yes', - type: 'textAd', - urltype: 'link' - } - }, - headers: { - get: function (header) { - if (header === 'X-Pricing-CPM') { - return 50; - } - } - } - }; - it('test intercept response', function () { - let request = adapter.spec.buildRequests(bidRequest); - let bidResponses = adapter.spec.interpretResponse(mockServerResponse, request); - expect(bidResponses.length).to.equal(1); - expect(bidResponses[0].ad).to.equal('test'); - expect(bidResponses[0].cpm).to.equal(50); - expect(bidResponses[0].creativeId).to.equal('267d72ac3f77a3f447b32cf7ebf20673'); - expect(bidResponses[0].requestId).to.equal('5t5t5t5'); - expect(bidResponses[0].currency).to.equal('USD'); - expect(bidResponses[0].height).to.equal('480'); - expect(bidResponses[0].netRevenue).to.equal(true); - expect(bidResponses[0].referrer).to.equal('https://tokyo-my.mobfox.com/exchange.click.php?h=494ef76d5b0287a8b5ac8724855cb5e0'); - expect(bidResponses[0].ttl).to.equal(360); - expect(bidResponses[0].width).to.equal('320'); - }); - - it('test intercept response with empty server response', function () { - let request = adapter.spec.buildRequests(bidRequest); - let serverResponse = { - request: { - error: 'cannot get response' - } - }; - let bidResponses = adapter.spec.interpretResponse(serverResponse, request); - expect(bidResponses.length).to.equal(0); - }) - }) -}); diff --git a/test/spec/modules/mobfoxpbBidAdapter_spec.js b/test/spec/modules/mobfoxpbBidAdapter_spec.js deleted file mode 100644 index a02d580ab88..00000000000 --- a/test/spec/modules/mobfoxpbBidAdapter_spec.js +++ /dev/null @@ -1,304 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/mobfoxpbBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; - -describe('MobfoxHBBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'mobfoxpb', - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 783, - traffic: BANNER - } - }; - - 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://bes.mobfox.com/?c=o&m=multi'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'schain'); - expect(placement.placementId).to.equal(783); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - expect(placement.sizes).to.be.an('array'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - it('Returns valid data for mediatype native', function () { - const native = { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - }; - - bid.mediaTypes = {}; - bid.params.traffic = NATIVE; - bid.mediaTypes[NATIVE] = native; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'native', 'schain'); - expect(placement.traffic).to.equal(NATIVE); - expect(placement.native).to.equal(native); - }); - - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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; - }); - }); -}); diff --git a/test/spec/modules/mobsmartBidAdapter_spec.js b/test/spec/modules/mobsmartBidAdapter_spec.js deleted file mode 100644 index b48878adff6..00000000000 --- a/test/spec/modules/mobsmartBidAdapter_spec.js +++ /dev/null @@ -1,214 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/mobsmartBidAdapter.js'; - -describe('mobsmartBidAdapter', function () { - describe('isBidRequestValid', function () { - let bid; - beforeEach(function() { - bid = { - bidder: 'mobsmart', - params: { - floorPrice: 100, - currency: 'JPY' - }, - mediaTypes: { - banner: { - size: [[300, 250]] - } - } - }; - }); - - it('should return true when valid bid request is set', function() { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when bidder is not set to "mobsmart"', function() { - bid.bidder = 'bidder'; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when params are not set', function() { - delete bid.params.floorPrice; - delete bid.params.currency; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - - describe('buildRequests', function () { - let bidRequests; - beforeEach(function() { - bidRequests = [ - { - bidder: 'mobsmart', - adUnitCode: 'mobsmart-ad-code', - auctionId: 'auctionid-123', - bidId: 'bidid123', - bidRequestsCount: 1, - bidderRequestId: 'bidderrequestid123', - transactionId: 'transaction-id-123', - sizes: [[300, 250]], - requestId: 'requestid123', - params: { - floorPrice: 100, - currency: 'JPY' - }, - mediaTypes: { - banner: { - size: [[300, 250]] - } - }, - userId: { - pubcid: 'pubc-id-123' - } - }, { - bidder: 'mobsmart', - adUnitCode: 'mobsmart-ad-code2', - auctionId: 'auctionid-456', - bidId: 'bidid456', - bidRequestsCount: 1, - bidderRequestId: 'bidderrequestid456', - transactionId: 'transaction-id-456', - sizes: [[320, 50]], - requestId: 'requestid456', - params: { - floorPrice: 100, - currency: 'JPY' - }, - mediaTypes: { - banner: { - size: [[320, 50]] - } - }, - userId: { - pubcid: 'pubc-id-456' - } - } - ]; - }); - - let bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - - it('should not contain a sizes when sizes is not set', function() { - delete bidRequests[0].sizes; - delete bidRequests[1].sizes; - let requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].data.sizes).to.be.an('undefined'); - expect(requests[1].data.sizes).to.be.an('undefined'); - }); - - it('should not contain a userId when userId is not set', function() { - delete bidRequests[0].userId; - delete bidRequests[1].userId; - let requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].data.userId).to.be.an('undefined'); - expect(requests[1].data.userId).to.be.an('undefined'); - }); - - it('should have a post method', function() { - let requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].method).to.equal('POST'); - expect(requests[1].method).to.equal('POST'); - }); - - it('should contain a request id equals to the bid id', function() { - let requests = spec.buildRequests(bidRequests, bidderRequest); - expect(JSON.parse(requests[0].data).requestId).to.equal(bidRequests[0].bidId); - expect(JSON.parse(requests[1].data).requestId).to.equal(bidRequests[1].bidId); - }); - - it('should have an url that match the default endpoint', function() { - let requests = spec.buildRequests(bidRequests, bidderRequest); - expect(requests[0].url).to.equal('https://prebid.mobsmart.net/prebid/endpoint'); - expect(requests[1].url).to.equal('https://prebid.mobsmart.net/prebid/endpoint'); - }); - }); - - describe('interpretResponse', function () { - let serverResponse; - beforeEach(function() { - serverResponse = { - body: { - 'requestId': 'request-id', - 'cpm': 100, - 'width': 300, - 'height': 250, - 'ad': '
ad
', - 'ttl': 300, - 'creativeId': 'creative-id', - 'netRevenue': true, - 'currency': 'JPY' - } - }; - }); - - it('should return a valid response', () => { - var responses = spec.interpretResponse(serverResponse); - expect(responses).to.be.an('array').that.is.not.empty; - - let response = responses[0]; - expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency'); - expect(response.requestId).to.equal('request-id'); - expect(response.cpm).to.equal(100); - expect(response.width).to.equal(300); - expect(response.height).to.equal(250); - expect(response.ad).to.equal('
ad
'); - expect(response.ttl).to.equal(300); - expect(response.creativeId).to.equal('creative-id'); - expect(response.netRevenue).to.be.true; - expect(response.currency).to.equal('JPY'); - }); - - it('should return an empty array when serverResponse is empty', () => { - serverResponse = {}; - var responses = spec.interpretResponse(serverResponse); - expect(responses).to.deep.equal([]); - }); - }); - - describe('getUserSyncs', function () { - it('should return nothing when sync is disabled', function () { - const syncOptions = { - 'iframeEnabled': false, - 'pixelEnabled': false - } - let syncs = spec.getUserSyncs(syncOptions); - expect(syncs).to.deep.equal([]); - }); - - it('should register iframe sync when iframe is enabled', function () { - const syncOptions = { - 'iframeEnabled': true, - 'pixelEnabled': false - } - let syncs = spec.getUserSyncs(syncOptions); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://tags.mobsmart.net/tags/iframe'); - }); - - it('should register image sync when image is enabled', function () { - const syncOptions = { - 'iframeEnabled': false, - 'pixelEnabled': true - } - let syncs = spec.getUserSyncs(syncOptions); - expect(syncs[0].type).to.equal('image'); - expect(syncs[0].url).to.equal('https://tags.mobsmart.net/tags/image'); - }); - - it('should register iframe sync when iframe is enabled', function () { - const syncOptions = { - 'iframeEnabled': true, - 'pixelEnabled': true - } - let syncs = spec.getUserSyncs(syncOptions); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.equal('https://tags.mobsmart.net/tags/iframe'); - }); - }); -}); diff --git a/test/spec/modules/mytargetBidAdapter_spec.js b/test/spec/modules/mytargetBidAdapter_spec.js deleted file mode 100644 index ea998303fe3..00000000000 --- a/test/spec/modules/mytargetBidAdapter_spec.js +++ /dev/null @@ -1,229 +0,0 @@ -import { expect } from 'chai'; -import { config } from 'src/config.js'; -import { spec } from 'modules/mytargetBidAdapter.js'; - -describe('MyTarget Adapter', function() { - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - let validBid = { - bidder: 'mytarget', - params: { - placementId: '1' - } - }; - - expect(spec.isBidRequestValid(validBid)).to.equal(true); - }); - - it('should return false for when required params are not passed', function () { - let invalidBid = { - bidder: 'mytarget', - params: {} - }; - - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - bidId: 'bid1', - bidder: 'mytarget', - params: { - placementId: '1' - } - }, - { - bidId: 'bid2', - bidder: 'mytarget', - params: { - placementId: '2', - position: 1, - response: 1, - bidfloor: 10000 - } - } - ]; - let bidderRequest = { - refererInfo: { - referer: 'https://example.com?param=value' - } - }; - - let bidRequest = spec.buildRequests(bidRequests, bidderRequest); - - it('should build single POST request for multiple bids', function() { - expect(bidRequest.method).to.equal('POST'); - expect(bidRequest.url).to.equal('https://ad.mail.ru/hbid_prebid/'); - expect(bidRequest.data).to.be.an('object'); - expect(bidRequest.data.places).to.be.an('array'); - expect(bidRequest.data.places).to.have.lengthOf(2); - }); - - it('should pass bid parameters', function() { - let place1 = bidRequest.data.places[0]; - let place2 = bidRequest.data.places[1]; - - expect(place1.placementId).to.equal('1'); - expect(place2.placementId).to.equal('2'); - expect(place1.id).to.equal('bid1'); - expect(place2.id).to.equal('bid2'); - }); - - it('should pass default position and response type', function() { - let place = bidRequest.data.places[0]; - - expect(place.position).to.equal(0); - expect(place.response).to.equal(0); - }); - - it('should pass provided position and response type', function() { - let place = bidRequest.data.places[1]; - - expect(place.position).to.equal(1); - expect(place.response).to.equal(1); - }); - - it('should not pass default bidfloor', function() { - let place = bidRequest.data.places[0]; - - expect(place.bidfloor).not.to.exist; - }); - - it('should not pass provided bidfloor', function() { - let place = bidRequest.data.places[1]; - - expect(place.bidfloor).to.exist; - expect(place.bidfloor).to.equal(10000); - }); - - it('should pass site parameters', function() { - let site = bidRequest.data.site; - - expect(site).to.be.an('object'); - expect(site.sitename).to.equal('example.com'); - expect(site.page).to.equal('https://example.com?param=value'); - }); - - it('should pass settings', function() { - let settings = bidRequest.data.settings; - - expect(settings).to.be.an('object'); - expect(settings.currency).to.equal('RUB'); - expect(settings.windowSize).to.be.an('object'); - expect(settings.windowSize.width).to.equal(window.screen.width); - expect(settings.windowSize.height).to.equal(window.screen.height); - }); - - it('should pass currency from currency.adServerCurrency', function() { - const configStub = sinon.stub(config, 'getConfig').callsFake( - key => key === 'currency.adServerCurrency' ? 'USD' : ''); - - let bidRequest = spec.buildRequests(bidRequests, bidderRequest); - let settings = bidRequest.data.settings; - - expect(settings).to.be.an('object'); - expect(settings.currency).to.equal('USD'); - expect(settings.windowSize).to.be.an('object'); - expect(settings.windowSize.width).to.equal(window.screen.width); - expect(settings.windowSize.height).to.equal(window.screen.height); - - configStub.restore(); - }); - - it('should ignore currency other than "RUB" or "USD"', function() { - const configStub = sinon.stub(config, 'getConfig').callsFake( - key => key === 'currency.adServerCurrency' ? 'EUR' : ''); - - let bidRequest = spec.buildRequests(bidRequests, bidderRequest); - let settings = bidRequest.data.settings; - - expect(settings).to.be.an('object'); - expect(settings.currency).to.equal('RUB'); - - configStub.restore(); - }); - }); - - describe('interpretResponse', function () { - let serverResponse = { - body: { - 'bidder_status': - [ - { - 'bidder': 'mail.ru', - 'response_time_ms': 100, - 'num_bids': 2 - } - ], - 'bids': - [ - { - 'displayUrl': 'https://ad.mail.ru/hbid_imp/12345', - 'size': - { - 'height': '400', - 'width': '240' - }, - 'id': '1', - 'currency': 'RUB', - 'price': 100, - 'ttl': 360, - 'creativeId': '123456' - }, - { - 'adm': '

Ad

', - 'size': - { - 'height': '250', - 'width': '300' - }, - 'id': '2', - 'price': 200 - } - ] - } - }; - - let bids = spec.interpretResponse(serverResponse); - - it('should return empty array for response with no bids', function() { - let emptyBids = spec.interpretResponse({ body: {} }); - - expect(emptyBids).to.have.lengthOf(0); - }); - - it('should parse all bids from response', function() { - expect(bids).to.have.lengthOf(2); - }); - - it('should parse bid with ad url', function() { - expect(bids[0].requestId).to.equal('1'); - expect(bids[0].cpm).to.equal(100); - expect(bids[0].width).to.equal('240'); - expect(bids[0].height).to.equal('400'); - expect(bids[0].ttl).to.equal(360); - expect(bids[0].currency).to.equal('RUB'); - expect(bids[0]).to.have.property('creativeId'); - expect(bids[0].creativeId).to.equal('123456'); - expect(bids[0].netRevenue).to.equal(true); - expect(bids[0].adUrl).to.equal('https://ad.mail.ru/hbid_imp/12345'); - expect(bids[0]).to.not.have.property('ad'); - }); - - it('should parse bid with ad markup', function() { - expect(bids[1].requestId).to.equal('2'); - expect(bids[1].cpm).to.equal(200); - expect(bids[1].width).to.equal('300'); - expect(bids[1].height).to.equal('250'); - expect(bids[1].ttl).to.equal(180); - expect(bids[1].currency).to.equal('RUB'); - expect(bids[1]).to.have.property('creativeId'); - expect(bids[1].creativeId).not.to.equal('123456'); - expect(bids[1].netRevenue).to.equal(true); - expect(bids[1].ad).to.equal('

Ad

'); - expect(bids[1]).to.not.have.property('adUrl'); - }); - }); -}); diff --git a/test/spec/modules/nafdigitalBidAdapter_spec.js b/test/spec/modules/nafdigitalBidAdapter_spec.js deleted file mode 100644 index c8ffb9fbbaf..00000000000 --- a/test/spec/modules/nafdigitalBidAdapter_spec.js +++ /dev/null @@ -1,283 +0,0 @@ -import { expect } from 'chai'; -import * as utils from 'src/utils.js'; -import { spec } from 'modules/nafdigitalBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const URL = 'https://nafdigitalbidder.com/hb'; - -describe('nafdigitalBidAdapter', function() { - const adapter = newBidder(spec); - let element, win; - let bidRequests; - let sandbox; - - beforeEach(function() { - element = { - x: 0, - y: 0, - - width: 0, - height: 0, - - getBoundingClientRect: () => { - return { - width: element.width, - height: element.height, - - left: element.x, - top: element.y, - right: element.x + element.width, - bottom: element.y + element.height - }; - } - }; - win = { - document: { - visibilityState: 'visible' - }, - - innerWidth: 800, - innerHeight: 600 - }; - bidRequests = [{ - 'bidder': 'nafdigital', - 'params': { - 'publisherId': 1234567 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '5fb26ac22bde4', - 'bidderRequestId': '4bf93aeb730cb9', - 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e' - }]; - - sandbox = sinon.sandbox.create(); - sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); - sandbox.stub(utils, 'getWindowTop').returns(win); - sandbox.stub(utils, 'getWindowSelf').returns(win); - }); - - afterEach(function() { - sandbox.restore(); - }); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'nafdigital', - 'params': { - 'publisherId': 1234567 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '5fb26ac22bde4', - 'bidderRequestId': '4bf93aeb730cb9', - 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when tagid not passed correctly', function () { - bid.params.publisherId = undefined; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when require params are not passed', function () { - let bid = Object.assign({}, bid); - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - it('sends bid request to our endpoint via POST', function () { - const request = spec.buildRequests(bidRequests); - expect(request.method).to.equal('POST'); - }); - - it('request url should match our endpoint url', function () { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.equal(URL); - }); - - it('sets the proper banner object', function() { - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); - }); - - it('accepts a single array as a size', function() { - bidRequests[0].sizes = [300, 250]; - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); - }); - - it('sends bidfloor param if present', function () { - bidRequests[0].params.bidFloor = 0.05; - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].bidfloor).to.equal(0.05); - }); - - it('sends tagid', function () { - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].tagid).to.equal('adunit-code'); - }); - - it('sends publisher id', function () { - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.site.publisher.id).to.equal(1234567); - }); - - context('when element is fully in view', function() { - it('returns 100', function() { - Object.assign(element, { width: 600, height: 400 }); - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal(100); - }); - }); - - context('when element is out of view', function() { - it('returns 0', function() { - Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal(0); - }); - }); - - context('when element is partially in view', function() { - it('returns percentage', function() { - Object.assign(element, { width: 800, height: 800 }); - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal(75); - }); - }); - - context('when width or height of the element is zero', function() { - it('try to use alternative values', function() { - Object.assign(element, { width: 0, height: 0 }); - bidRequests[0].sizes = [[800, 2400]]; - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal(25); - }); - }); - - context('when nested iframes', function() { - it('returns \'na\'', function() { - Object.assign(element, { width: 600, height: 400 }); - - utils.getWindowTop.restore(); - utils.getWindowSelf.restore(); - sandbox.stub(utils, 'getWindowTop').returns(win); - sandbox.stub(utils, 'getWindowSelf').returns({}); - - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal('na'); - }); - }); - - context('when tab is inactive', function() { - it('returns 0', function() { - Object.assign(element, { width: 600, height: 400 }); - - utils.getWindowTop.restore(); - win.document.visibilityState = 'hidden'; - sandbox.stub(utils, 'getWindowTop').returns(win); - - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.imp[0].banner.ext.viewability).to.equal(0); - }); - }); - }); - - describe('interpretResponse', function () { - let response; - beforeEach(function () { - response = { - body: { - 'id': '37386aade21a71', - 'seatbid': [{ - 'bid': [{ - 'id': '376874781', - 'impid': '283a9f4cd2415d', - 'price': 0.35743275, - 'nurl': '', - 'adm': '', - 'w': 300, - 'h': 250 - }] - }] - } - }; - }); - - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': '283a9f4cd2415d', - 'cpm': 0.35743275, - 'width': 300, - 'height': 250, - 'creativeId': '376874781', - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': `
`, - 'ttl': 60 - }]; - - let result = spec.interpretResponse(response); - expect(result[0]).to.deep.equal(expectedResponse[0]); - }); - - it('crid should default to the bid id if not on the response', function () { - let expectedResponse = [{ - 'requestId': '283a9f4cd2415d', - 'cpm': 0.35743275, - 'width': 300, - 'height': 250, - 'creativeId': response.body.seatbid[0].bid[0].id, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': `
`, - 'ttl': 60 - }]; - - let result = spec.interpretResponse(response); - expect(result[0]).to.deep.equal(expectedResponse[0]); - }); - - it('handles empty bid response', function () { - let response = { - body: '' - }; - let result = spec.interpretResponse(response); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs ', () => { - let syncOptions = {iframeEnabled: true, pixelEnabled: true}; - - it('should not return', () => { - let returnStatement = spec.getUserSyncs(syncOptions, []); - expect(returnStatement).to.be.empty; - }); - }); -}); diff --git a/test/spec/modules/nanointeractiveBidAdapter_spec.js b/test/spec/modules/nanointeractiveBidAdapter_spec.js deleted file mode 100644 index 715a26a4597..00000000000 --- a/test/spec/modules/nanointeractiveBidAdapter_spec.js +++ /dev/null @@ -1,466 +0,0 @@ -import {expect} from 'chai'; -import * as utils from 'src/utils.js'; -import * as sinon from 'sinon'; - -import { - BIDDER_CODE, - CATEGORY, - CATEGORY_NAME, - SSP_PLACEMENT_ID, - END_POINT_URL, - NQ, - NQ_NAME, - REF, - spec, - SUB_ID -} from '../../../modules/nanointeractiveBidAdapter.js'; - -describe('nanointeractive adapter tests', function () { - const SIZES_PARAM = 'sizes'; - const BID_ID_PARAM = 'bidId'; - const BID_ID_VALUE = '24a1c9ec270973'; - const DATA_PARTNER_PIXEL_ID_VALUE = 'testPID'; - const NQ_VALUE = 'rumpelstiltskin'; - const NQ_NAME_QUERY_PARAM = 'nqName'; - const CATEGORY_VALUE = 'some category'; - const CATEGORY_NAME_QUERY_PARAM = 'catName'; - const SUB_ID_VALUE = '123'; - const REF_NO_VALUE = 'none'; - const REF_OTHER_VALUE = 'other'; - const WIDTH1 = 300; - const HEIGHT1 = 250; - const WIDTH2 = 468; - const HEIGHT2 = 60; - const SIZES_VALUE = [[WIDTH1, HEIGHT1], [WIDTH2, HEIGHT2]]; - const AD = ' '; - const CPM = 1; - - function getBidRequest(params) { - return { - bidder: BIDDER_CODE, - params: params, - placementCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'ee335735-ddd3-41f2-b6c6-e8aa99f81c0f', - [SIZES_PARAM]: SIZES_VALUE, - [BID_ID_PARAM]: BID_ID_VALUE, - bidderRequestId: '189135372acd55', - auctionId: 'ac15bb68-4ef0-477f-93f4-de91c47f00a9' - }; - } - - describe('NanoAdapter', function () { - let nanoBidAdapter = spec; - - describe('Methods', function () { - it('Test isBidRequestValid() with valid param(s): pid', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, category', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ, - [CATEGORY]: CATEGORY_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, categoryName', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ, - [CATEGORY_NAME_QUERY_PARAM]: CATEGORY_NAME_QUERY_PARAM, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nqName', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nqName, category', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - [CATEGORY]: CATEGORY_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nqName, categoryName', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - [CATEGORY_NAME_QUERY_PARAM]: CATEGORY_NAME_QUERY_PARAM, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nqName, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, category', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [CATEGORY]: CATEGORY_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, category, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nqName, categoryName, subId', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId, ref (value none)', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - [REF]: REF_NO_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with valid param(s): pid, nq, category, subId, ref (value other)', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - [REF]: REF_OTHER_VALUE, - }))).to.equal(true); - }); - it('Test isBidRequestValid() with invalid param(s): empty', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({}))).to.equal(false); - }); - it('Test isBidRequestValid() with invalid param(s): pid missing', function () { - expect(nanoBidAdapter.isBidRequestValid(getBidRequest({ - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }))).to.equal(false); - }); - - let sandbox; - - function getMocks() { - let mockOriginAddress = 'https://localhost'; - let mockRefAddress = 'https://some-ref.test'; - return { - 'windowLocationAddress': mockRefAddress, - 'originAddress': mockOriginAddress, - 'refAddress': '', - }; - } - - function setUpMocks() { - sinon.sandbox.restore(); - sandbox = sinon.sandbox.create(); - sandbox.stub(utils, 'getOrigin').callsFake(() => getMocks()['originAddress']); - sandbox.stub(utils, 'deepAccess').callsFake(() => getMocks()['windowLocationAddress']); - - sandbox.stub(utils, 'getParameterByName').callsFake((arg) => { - switch (arg) { - case CATEGORY_NAME_QUERY_PARAM: - return CATEGORY_VALUE; - case NQ_NAME_QUERY_PARAM: - return NQ_VALUE; - } - }); - } - - function assert( - request, - expectedPid, - expectedNq, - expectedCategory, - expectedSubId - ) { - const requestData = JSON.parse(request.data); - - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(END_POINT_URL + '/hb'); - expect(requestData[0].pid).to.equal(expectedPid); - expect(requestData[0].nq.toString()).to.equal(expectedNq.toString()); - expect(requestData[0].category.toString()).to.equal(expectedCategory.toString()); - expect(requestData[0].subId).to.equal(expectedSubId); - } - - function tearDownMocks() { - sandbox.restore(); - } - - it('Test buildRequest() - pid', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [null]; - let expectedCategory = [null]; - let expectedSubId = null; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nq', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [null]; - let expectedSubId = null; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nq, category', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = null; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nq, categoryName', function () { - setUpMocks(); - - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = null; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nq, subId', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [null]; - let expectedSubId = SUB_ID_VALUE; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, category', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [CATEGORY]: CATEGORY_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [null]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = null; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, category, subId', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [null]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = SUB_ID_VALUE; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, subId', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [null]; - let expectedCategory = [null]; - let expectedSubId = SUB_ID_VALUE; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nq, category, subId', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ]: NQ_VALUE, - [CATEGORY]: CATEGORY_VALUE, - [SUB_ID]: SUB_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = SUB_ID_VALUE; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test buildRequest() - pid, nqName, categoryName, subId', function () { - setUpMocks(); - let requestParams = { - [SSP_PLACEMENT_ID]: DATA_PARTNER_PIXEL_ID_VALUE, - [NQ_NAME]: NQ_NAME_QUERY_PARAM, - [CATEGORY_NAME]: CATEGORY_NAME_QUERY_PARAM, - [SUB_ID]: SUB_ID_VALUE, - }; - let expectedPid = DATA_PARTNER_PIXEL_ID_VALUE; - let expectedNq = [NQ_VALUE]; - let expectedCategory = [CATEGORY_VALUE]; - let expectedSubId = SUB_ID_VALUE; - - let request = nanoBidAdapter.buildRequests([getBidRequest(requestParams)]); - - assert(request, expectedPid, expectedNq, expectedCategory, expectedSubId); - tearDownMocks(); - }); - it('Test interpretResponse() length', function () { - let bids = nanoBidAdapter.interpretResponse({ - body: [ - // valid - { - id: '24a1c9ec270973', - cpm: CPM, - width: WIDTH1, - height: HEIGHT1, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - }, - // invalid - { - id: '24a1c9ec270973', - cpm: null, - width: WIDTH1, - height: HEIGHT1, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - } - ] - }); - expect(bids.length).to.equal(1); - }); - it('Test interpretResponse() bids', function () { - let bid = nanoBidAdapter.interpretResponse({ - body: [ - // valid - { - id: '24a1c9ec270973', - cpm: CPM, - width: WIDTH1, - height: HEIGHT1, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - }, - // invalid - { - id: '24a1c9ec270973', - cpm: null, - width: WIDTH1, - height: HEIGHT1, - ad: AD, - ttl: 360, - creativeId: 'TEST_ID', - netRevenue: false, - currency: 'EUR', - } - ] - })[0]; - expect(bid.requestId).to.equal('24a1c9ec270973'); - expect(bid.cpm).to.equal(CPM); - expect(bid.width).to.equal(WIDTH1); - expect(bid.height).to.equal(HEIGHT1); - expect(bid.ad).to.equal(AD); - expect(bid.ttl).to.equal(360); - expect(bid.creativeId).to.equal('TEST_ID'); - expect(bid.currency).to.equal('EUR'); - }); - }); - }); -}); diff --git a/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js b/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js deleted file mode 100644 index 4731b1a77d3..00000000000 --- a/test/spec/modules/nasmediaAdmixerBidAdapter_spec.js +++ /dev/null @@ -1,143 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/nasmediaAdmixerBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -describe('nasmediaAdmixerBidAdapter', 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 () { - const bid = { - 'bidder': 'nasmediaAdmixer', - 'params': { - 'media_key': 'media_key', - 'adunit_id': 'adunit_id', - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250]], - 'bidId': '3361d01e67dbd6', - 'bidderRequestId': '2b60dcd392628a', - 'auctionId': '124cb070528662', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - const bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'media_key': '', - 'adunit_id': '', - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - 'bidder': 'nasmediaAdmixer', - 'params': { - 'media_key': '19038695', - 'adunit_id': '24190632', - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250]], - 'bidId': '3361d01e67dbd6', - 'bidderRequestId': '2b60dcd392628a', - 'auctionId': '124cb070528662', - } - ]; - const bidderRequest = {refererInfo: {referer: 'https://example.com'}}; - - it('sends bid request to url via GET', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.method).to.equal('GET'); - expect(request.url).to.match(new RegExp(`https://adn.admixer.co.kr`)); - }); - }); - - describe('interpretResponse', function () { - const response = { - 'body': { - 'bidder': 'nasmediaAdmixer', - 'req_id': '861a8e7952c82c', - 'error_code': 0, - 'error_msg': 'OK', - 'body': [{ - 'ad_id': '20049', - 'width': 300, - 'height': 250, - 'currency': 'USD', - 'cpm': 1.769221, - 'ad': '' - }] - }, - 'headers': { - 'get': function () { - } - } - }; - - const bidRequest = { - 'bidder': 'nasmediaAdmixer', - 'params': { - 'media_key': '19038695', - 'adunit_id': '24190632', - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [320, 480]], - 'bidId': '31300c8b9697cd', - 'bidderRequestId': '2bf570adcf83fa', - 'auctionId': '169827a33f03cc', - }; - - it('should get correct bid response', function () { - const expectedResponse = [ - { - 'requestId': '861a8e7952c82c', - 'cpm': 1.769221, - 'currency': 'USD', - 'width': 300, - 'height': 250, - 'ad': '', - 'creativeId': '20049', - 'ttl': 360, - 'netRevenue': false - } - ]; - - const result = spec.interpretResponse(response, bidRequest); - expect(result).to.have.lengthOf(1); - let resultKeys = Object.keys(result[0]); - expect(resultKeys.sort()).to.deep.equal(Object.keys(expectedResponse[0]).sort()); - resultKeys.forEach(function (k) { - if (k === 'ad') { - expect(result[0][k]).to.match(/$/); - } else { - expect(result[0][k]).to.equal(expectedResponse[0][k]); - } - }); - }); - - it('handles nobid responses', function () { - response.body = { - 'bidder': 'nasmediaAdmixer', - 'req_id': '861a8e7952c82c', - 'error_code': 0, - 'error_msg': 'OK', - 'body': [] - }; - - const result = spec.interpretResponse(response, bidRequest); - expect(result).to.have.lengthOf(0); - }); - }); -}); diff --git a/test/spec/modules/newborntownWebBidAdapter_spec.js b/test/spec/modules/newborntownWebBidAdapter_spec.js deleted file mode 100644 index 3d3285328fe..00000000000 --- a/test/spec/modules/newborntownWebBidAdapter_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import { expect } from 'chai'; -import {spec} from 'modules/newborntownWebBidAdapter.js'; -describe('NewborntownWebAdapter', function() { - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'newborntownWeb', - 'params': { - 'publisher_id': '1238122', - 'slot_id': '123123', - 'bidfloor': 0.3 - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'sizes': [[300, 250]], - 'bidId': '2e9cf65f23dbd9', - 'bidderRequestId': '1f01d9d22ee657', - 'auctionId': '2bf455a4-a889-41d5-b48f-9b56b89fbec7', - } - it('should return true where required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }) - describe('buildRequests', function () { - let bidderRequest = { - 'bidderCode': 'newborntownWeb', - 'bidderRequestId': '1f5c279a4c5de3', - 'bids': [ - { - 'bidder': 'newborntownWeb', - 'params': { - 'publisher_id': '1238122', - 'slot_id': '123123', - 'bidfloor': 0.3 - }, - 'mediaTypes': { - 'banner': {'sizes': [[300, 250]]} - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': '9b954797-d6f4-4730-9cbe-5a1bc8480f52', - 'sizes': [[300, 250]], - 'bidId': '215f48d07eb8b8', - 'bidderRequestId': '1f5c279a4c5de3', - 'auctionId': '5ed4f607-e11c-45b0-aba9-f67768e1f9f4', - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'auctionStart': 1573123289380, - 'timeout': 9000, - 'start': 1573123289383 - } - it('Returns POST method', function () { - const request = spec.buildRequests(bidderRequest['bids'], bidderRequest); - expect(request[0].method).to.equal('POST'); - expect(request[0].url.indexOf('//us-west.solortb.com/adx/api/rtb?from=4') !== -1).to.equal(true); - expect(request[0].data).to.exist; - }); - it('request params multi size format object check', function () { - let bidderRequest = { - 'bidderCode': 'newborntownWeb', - 'bidderRequestId': '1f5c279a4c5de3', - 'bids': [ - { - 'bidder': 'newborntownWeb', - 'params': { - 'publisher_id': '1238122', - 'slot_id': '123123', - 'bidfloor': 0.3 - }, - 'mediaTypes': { - 'native': {'sizes': [[300, 250]]} - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': '9b954797-d6f4-4730-9cbe-5a1bc8480f52', - 'sizes': [300, 250], - 'bidId': '215f48d07eb8b8', - 'bidderRequestId': '1f5c279a4c5de3', - 'auctionId': '5ed4f607-e11c-45b0-aba9-f67768e1f9f4', - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'auctionStart': 1573123289380, - 'timeout': 9000, - 'start': 1573123289383 - } - let requstTest = spec.buildRequests(bidderRequest['bids'], bidderRequest) - expect(requstTest[0].data.imp[0].banner.w).to.equal(300); - expect(requstTest[0].data.imp[0].banner.h).to.equal(250); - }); - }) - describe('interpretResponse', function () { - let serverResponse; - let bidRequest = { - data: { - bidId: '2d359291dcf53b' - } - }; - beforeEach(function () { - serverResponse = { - 'body': { - 'id': '174548259807190369860081', - 'seatbid': [ - { - 'bid': [ - { - 'id': '1573540665390298996', - 'impid': '1', - 'price': 0.3001, - 'adid': '1573540665390299172', - 'nurl': 'https://us-west.solortb.com/winnotice?price=${AUCTION_PRICE}&ssp=4&req_unique_id=740016d1-175b-4c19-9744-58a59632dabe&unique_id=06b08e40-2489-439a-8f9e-6413f3dd0bc8&isbidder=1&up=bQyvVo7tgbBVW2dDXzTdBP95Mv35YqqEika0T_btI1h6xjqA8GSXQe51_2CCHQcfuwAEOgdwN8u3VgUHmCuqNPKiBmIPaYUOQBBKjJr05zeKtabKnGT7_JJKcurrXqQ5Sl804xJear_qf2-jOaKB4w', - 'adm': "
", - 'adomain': [ - 'newborntown.com' - ], - 'iurl': 'https://sdkvideo.s3.amazonaws.com/4aa1d9533c4ce71bb1cf750ed38e3a58.png', - 'cid': '345', - 'crid': '41_11113', - 'cat': [ - '' - ], - 'h': 250, - 'w': 300 - } - ], - 'seat': '1' - } - ], - 'bidid': 'bid1573540665390298585' - }, - 'headers': { - - } - } - }); - it('result is correct', function () { - const result = spec.interpretResponse(serverResponse, bidRequest); - if (result && result[0]) { - expect(result[0].requestId).to.equal('2d359291dcf53b'); - expect(result[0].cpm).to.equal(0.3001); - expect(result[0].width).to.equal(300); - expect(result[0].height).to.equal(250); - expect(result[0].creativeId).to.equal('345'); - } - }); - }) -}) diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js deleted file mode 100644 index 42032eb03ea..00000000000 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import { expect } from 'chai'; -import * as utils from '../../../src/utils.js'; -import { spec } from 'modules/nextMillenniumBidAdapter.js'; - -describe('nextMillenniumBidAdapterTests', function() { - let bidRequestData = { - bids: [ - { - bidId: 'transaction_1234', - bidder: 'nextMillennium', - params: { - placement_id: '12345' - }, - sizes: [[300, 250]] - } - ] - }; - - it('validate_pub_params', function() { - expect( - spec.isBidRequestValid({ - bidder: 'nextMillennium', - params: { - placement_id: '12345' - } - }) - ).to.equal(true); - }); - - it('validate_generated_params', function() { - let bidRequestData = [ - { - bidId: 'bid1234', - bidder: 'nextMillennium', - params: { placement_id: '-1' }, - sizes: [[300, 250]] - } - ]; - let request = spec.buildRequests(bidRequestData); - expect(request[0].bidId).to.equal('bid1234'); - }); - - it('validate_response_params', function() { - let 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.bids[0]); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - - expect(bid.creativeId).to.equal('96846035'); - expect(bid.ad).to.equal('Hello! It\'s a test ad!'); - expect(bid.cpm).to.equal(0.5); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.currency).to.equal('USD'); - }); - - it('validate_response_params_with passback', function() { - let serverResponse = { - body: [ - { - hash: '1e100887dd614b0909bf6c49ba7f69fdd1360437', - content: 'Ad html passback', - size: [300, 250], - is_passback: 1 - } - ] - }; - let bids = spec.interpretResponse(serverResponse); - - expect(bids).to.have.lengthOf(0); - }); -}); diff --git a/test/spec/modules/nextrollBidAdapter_spec.js b/test/spec/modules/nextrollBidAdapter_spec.js deleted file mode 100644 index 7722443e584..00000000000 --- a/test/spec/modules/nextrollBidAdapter_spec.js +++ /dev/null @@ -1,268 +0,0 @@ -import { expect } from 'chai'; -import { spec, tryGetPubtag, hasCCPAConsent } from 'modules/nextrollBidAdapter.js'; -import * as utils from 'src/utils.js'; - -describe('nextrollBidAdapter', function() { - let utilsMock; - beforeEach(function () { - utilsMock = sinon.mock(utils); - }); - - afterEach(function() { - global.NextRoll = undefined; - utilsMock.restore(); - }); - - let validBid = { - bidder: 'nextroll', - adUnitCode: 'adunit-code', - bidId: 'bid_id', - sizes: [[300, 200]], - params: { - bidfloor: 1, - zoneId: 'zone1', - publisherId: 'publisher_id' - } - }; - let bidWithoutValidId = { id: '' }; - let bidWithoutId = { params: { zoneId: 'zone1' } }; - - describe('nativeBidRequest', () => { - it('validates native spec', () => { - let nativeAdUnit = [{ - bidder: 'nextroll', - adUnitCode: 'adunit-code', - bidId: 'bid_id', - mediaTypes: { - native: { - title: {required: true, len: 80}, - image: {required: true, sizes: [728, 90]}, - sponsoredBy: {required: false, len: 20}, - clickUrl: {required: true}, - body: {required: true, len: 25}, - icon: {required: true, sizes: [50, 50], aspect_ratios: [{ratio_height: 3, ratio_width: 4}]}, - someRandomAsset: {required: false, len: 100} // This should be ignored - } - }, - params: { - bidfloor: 1, - zoneId: 'zone1', - publisherId: 'publisher_id' - } - }]; - - let request = spec.buildRequests(nativeAdUnit) - let assets = request[0].data.imp.native.request.native.assets - - let excptedAssets = [ - {id: 1, required: 1, title: {len: 80}}, - {id: 2, required: 1, img: {w: 728, h: 90, wmin: 1, hmin: 1, type: 3}}, - {id: 3, required: 1, img: {w: 50, h: 50, wmin: 4, hmin: 3, type: 1}}, - {id: 5, required: 0, data: {len: 20, type: 1}}, - {id: 6, required: 1, data: {len: 25, type: 2}} - ] - expect(assets).to.be.deep.equal(excptedAssets) - }) - }) - - describe('isBidRequestValid', function() { - it('validates the bids correctly when the bid has an id', function() { - expect(spec.isBidRequestValid(validBid)).to.be.true; - }); - - it('validates the bids correcly when the bid does not have an id', function() { - expect(spec.isBidRequestValid(bidWithoutValidId)).to.be.false; - expect(spec.isBidRequestValid(bidWithoutId)).to.be.false; - }); - }); - - describe('buildRequests', function() { - it('builds the same amount of requests as valid requests it takes', function() { - expect(spec.buildRequests([validBid, validBid], {})).to.be.lengthOf(2); - }); - - it('doest not build a request when there is no valid requests', function () { - expect(spec.buildRequests([], {})).to.be.lengthOf(0); - }); - - it('builds a request with POST method', function () { - expect(spec.buildRequests([validBid], {})[0].method).to.equal('POST'); - }); - - it('builds a request with cookies method', function () { - expect(spec.buildRequests([validBid], {})[0].options.withCredentials).to.be.true; - }); - - it('builds a request with id, url and imp object', function () { - const request = spec.buildRequests([validBid], {})[0]; - expect(request.data.id).to.be.an('string').that.is.not.empty; - expect(request.url).to.equal('https://d.adroll.com/bid/prebid/'); - expect(request.data.imp).to.exist.and.to.be.a('object'); - }); - - it('builds a request with site and device information', function () { - const request = spec.buildRequests([validBid], {})[0]; - - expect(request.data.site).to.exist.and.to.be.a('object'); - expect(request.data.device).to.exist.and.to.be.a('object'); - }); - - it('builds a request with a complete imp object', function () { - const request = spec.buildRequests([validBid], {})[0]; - - expect(request.data.imp.id).to.equal('bid_id'); - expect(request.data.imp.bidfloor).to.be.equal(1); - expect(request.data.imp.banner).to.exist.and.to.be.a('object'); - expect(request.data.imp.ext.zone.id).to.be.equal('zone1'); - }); - - it('includes the sizes into the request correctly', function () { - const bannerObject = spec.buildRequests([validBid], {})[0].data.imp.banner; - - expect(bannerObject.format).to.exist; - expect(bannerObject.format).to.be.lengthOf(1); - expect(bannerObject.format[0].w).to.be.equal(300); - expect(bannerObject.format[0].h).to.be.equal(200); - }); - - it('sets the CCPA consent string', function () { - const us_privacy = '1YYY'; - const request = spec.buildRequests([validBid], {'uspConsent': us_privacy})[0]; - - expect(request.data.regs.ext.us_privacy).to.be.equal(us_privacy); - }); - }); - - describe('interpretResponse', function () { - let responseBody = { - id: 'bidresponse_id', - dealId: 'deal_id', - seatbid: [ - { - bid: [ - { - price: 1.2, - w: 300, - h: 200, - crid: 'crid1', - adm: 'adm1' - } - ] - }, - { - bid: [ - { - price: 2.1, - w: 250, - h: 300, - crid: 'crid2', - adm: 'adm2' - } - ] - } - ] - }; - - it('returns an empty list when there is no response body', function () { - expect(spec.interpretResponse({}, {})).to.be.eql([]); - }); - - it('builds the same amount of responses as server responses it receives', function () { - expect(spec.interpretResponse({body: responseBody}, {})).to.be.lengthOf(2); - }); - - it('builds a response with the expected fields', function () { - const response = spec.interpretResponse({body: responseBody}, {})[0]; - - expect(response.requestId).to.be.equal('bidresponse_id'); - expect(response.cpm).to.be.equal(1.2); - expect(response.width).to.be.equal(300); - expect(response.height).to.be.equal(200); - expect(response.creativeId).to.be.equal('crid1'); - expect(response.dealId).to.be.equal('deal_id'); - expect(response.currency).to.be.equal('USD'); - expect(response.netRevenue).to.be.equal(true); - expect(response.ttl).to.be.equal(300); - expect(response.ad).to.be.equal('adm1'); - }); - }); - - describe('interpret native response', () => { - let clickUrl = 'https://clickurl.com/with/some/path' - let titleText = 'Some title' - let imgW = 300 - let imgH = 250 - let imgUrl = 'https://clickurl.com/img.png' - let brandText = 'Some Brand' - let impUrl = 'https://clickurl.com/imptracker' - - let responseBody = { - body: { - id: 'bidresponse_id', - seatbid: [{ - bid: [{ - price: 1.2, - crid: 'crid1', - adm: { - link: {url: clickUrl}, - assets: [ - {id: 1, title: {text: titleText}}, - {id: 2, img: {w: imgW, h: imgH, url: imgUrl}}, - {id: 5, data: {value: brandText}} - ], - imptrackers: [impUrl] - } - }] - }] - } - }; - - it('Should interpret response', () => { - let response = spec.interpretResponse(utils.deepClone(responseBody)) - let expectedResponse = { - clickUrl: clickUrl, - impressionTrackers: [impUrl], - privacyLink: 'https://info.evidon.com/pub_info/573', - privacyIcon: 'https://c.betrad.com/pub/icon1.png', - title: titleText, - image: {url: imgUrl, width: imgW, height: imgH}, - sponsoredBy: brandText, - clickTrackers: [], - jstracker: [] - } - - expect(response[0].native).to.be.deep.equal(expectedResponse) - }) - - it('Should interpret all assets', () => { - let allAssetsResponse = utils.deepClone(responseBody) - let iconUrl = imgUrl + '?icon=true', iconW = 10, iconH = 15 - let logoUrl = imgUrl + '?logo=true', logoW = 20, logoH = 25 - let bodyText = 'Some body text' - - allAssetsResponse.body.seatbid[0].bid[0].adm.assets.push(...[ - {id: 3, img: {w: iconW, h: iconH, url: iconUrl}}, - {id: 4, img: {w: logoW, h: logoH, url: logoUrl}}, - {id: 6, data: {value: bodyText}} - ]) - - let response = spec.interpretResponse(allAssetsResponse) - let expectedResponse = { - clickUrl: clickUrl, - impressionTrackers: [impUrl], - jstracker: [], - clickTrackers: [], - privacyLink: 'https://info.evidon.com/pub_info/573', - privacyIcon: 'https://c.betrad.com/pub/icon1.png', - title: titleText, - image: {url: imgUrl, width: imgW, height: imgH}, - icon: {url: iconUrl, width: iconW, height: iconH}, - logo: {url: logoUrl, width: logoW, height: logoH}, - body: bodyText, - sponsoredBy: brandText - } - - expect(response[0].native).to.be.deep.equal(expectedResponse) - }) - }) -}); diff --git a/test/spec/modules/open8BidAdapter_spec.js b/test/spec/modules/open8BidAdapter_spec.js deleted file mode 100644 index 506742bef9e..00000000000 --- a/test/spec/modules/open8BidAdapter_spec.js +++ /dev/null @@ -1,251 +0,0 @@ -import { spec } from 'modules/open8BidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://as.vt.open8.com/v1/control/prebid'; - -describe('Open8Adapter', function() { - const adapter = newBidder(spec); - - describe('isBidRequestValid', function() { - let bid = { - 'bidder': 'open8', - 'params': { - 'slotKey': 'slotkey1234' - }, - 'adUnitCode': 'adunit', - 'sizes': [[300, 250]], - 'bidId': 'bidid1234', - 'bidderRequestId': 'requestid1234', - 'auctionId': 'auctionid1234', - }; - - it('should return true when required params found', function() { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function() { - bid.params = { - ' slotKey': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function() { - let bidRequests = [ - { - 'bidder': 'open8', - 'params': { - 'slotKey': 'slotkey1234' - }, - 'adUnitCode': 'adunit', - 'sizes': [[300, 250]], - 'bidId': 'bidid1234', - 'bidderRequestId': 'requestid1234', - 'auctionId': 'auctionid1234', - } - ]; - - it('sends bid request to ENDPOINT via GET', function() { - const requests = spec.buildRequests(bidRequests); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('GET'); - }); - }); - describe('interpretResponse', function() { - const bannerResponse = { - slotKey: 'slotkey1234', - userId: 'userid1234', - impId: 'impid1234', - media: 'TEST_MEDIA', - nurl: 'https://example/win', - isAdReturn: true, - syncPixels: ['https://example/sync/pixel.gif'], - syncIFs: [], - ad: { - bidId: 'TEST_BID_ID', - price: 1234.56, - creativeId: 'creativeid1234', - dealId: 'TEST_DEAL_ID', - currency: 'JPY', - ds: 876, - spd: 1234, - fa: 5678, - pr: 'pr1234', - mr: 'mr1234', - nurl: 'https://example/win', - adType: 2, - banner: { - w: 300, - h: 250, - adm: '
', - imps: ['https://example.com/imp'] - } - } - }; - const videoResponse = { - slotKey: 'slotkey1234', - userId: 'userid1234', - impId: 'impid1234', - media: 'TEST_MEDIA', - isAdReturn: true, - syncPixels: ['https://example/sync/pixel.gif'], - syncIFs: [], - ad: { - bidId: 'TEST_BID_ID', - price: 1234.56, - creativeId: 'creativeid1234', - dealId: 'TEST_DEAL_ID', - currency: 'JPY', - ds: 876, - spd: 1234, - fa: 5678, - pr: 'pr1234', - mr: 'mr1234', - nurl: 'https://example/win', - adType: 1, - video: { - purl: 'https://playerexample.js', - vastXml: '', - w: 320, - h: 180 - }, - } - }; - - it('should get correct banner bid response', function() { - let expectedResponse = [{ - 'slotKey': 'slotkey1234', - 'userId': 'userid1234', - 'impId': 'impid1234', - 'media': 'TEST_MEDIA', - 'ds': 876, - 'spd': 1234, - 'fa': 5678, - 'pr': 'pr1234', - 'mr': 'mr1234', - 'nurl': 'https://example/win', - 'requestId': 'requestid1234', - 'cpm': 1234.56, - 'creativeId': 'creativeid1234', - 'dealId': 'TEST_DEAL_ID', - 'width': 300, - 'height': 250, - 'ad': "
", - 'mediaType': 'banner', - 'currency': 'JPY', - 'ttl': 360, - 'netRevenue': true - }]; - - let bidderRequest; - let result = spec.interpretResponse({ body: bannerResponse }, { bidderRequest }); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles video responses', function() { - let expectedResponse = [{ - 'slotKey': 'slotkey1234', - 'userId': 'userid1234', - 'impId': 'impid1234', - 'media': 'TEST_MEDIA', - 'ds': 876, - 'spd': 1234, - 'fa': 5678, - 'pr': 'pr1234', - 'mr': 'mr1234', - 'nurl': 'https://example/win', - 'requestId': 'requestid1234', - 'cpm': 1234.56, - 'creativeId': 'creativeid1234', - 'dealId': 'TEST_DEAL_ID', - 'width': 320, - 'height': 180, - 'vastXml': '', - 'mediaType': 'video', - 'renderer': {}, - 'adResponse': {}, - 'currency': 'JPY', - 'ttl': 360, - 'netRevenue': true - }]; - - let bidderRequest; - let result = spec.interpretResponse({ body: videoResponse }, { bidderRequest }); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function() { - let response = { - isAdReturn: false, - 'ad': {} - }; - - let bidderRequest; - let result = spec.interpretResponse({ body: response }, { bidderRequest }); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function() { - const imgResponse1 = { - body: { - 'isAdReturn': true, - 'ad': { /* ad body */ }, - 'syncPixels': [ - 'https://example.test/1' - ] - } - }; - - const imgResponse2 = { - body: { - 'isAdReturn': true, - 'ad': { /* ad body */ }, - 'syncPixels': [ - 'https://example.test/2' - ] - } - }; - - const ifResponse = { - body: { - 'isAdReturn': true, - 'ad': { /* ad body */ }, - 'syncIFs': [ - 'https://example.test/3' - ] - } - }; - - it('should use a sync img url from first response', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imgResponse1, imgResponse2, ifResponse]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://example.test/1' - } - ]); - }); - - it('handle ifs response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, [ifResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://example.test/3' - } - ]); - }); - - it('handle empty response (e.g. timeout)', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('returns empty syncs when not enabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imgResponse1]); - expect(syncs).to.deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 2a380277e55..112dd4768df 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1064,7 +1064,6 @@ describe('OpenxAdapter', function () { parrableId: { eid: 'eidVersion.encryptionKeyReference.encryptedValue' }, pubcid: '1111-pubcid', quantcastId: '1111-quantcastid', - sharedId: '1111-sharedid', tapadId: '111-tapadid', tdid: '1111-tdid', verizonMediaId: '1111-verizonmediaid', diff --git a/test/spec/modules/optimeraBidAdapter_spec.js b/test/spec/modules/optimeraBidAdapter_spec.js deleted file mode 100644 index ada07fe25c2..00000000000 --- a/test/spec/modules/optimeraBidAdapter_spec.js +++ /dev/null @@ -1,99 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/optimeraBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('OptimeraAdapter', 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': 'optimera', - 'params': { - 'clientID': '9999' - }, - 'adUnitCode': 'div-0', - '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); - }); - }) - - describe('buildRequests', function () { - let bid = [ - { - 'adUnitCode': 'div-0', - 'auctionId': '1ab30503e03994', - 'bidId': '313e0afede8cdb', - 'bidder': 'optimera', - 'bidderRequestId': '202be1ce3f6194', - 'params': { - 'clientID': '0' - } - } - ]; - it('buildRequests fires', function () { - let request = spec.buildRequests(bid); - expect(request).to.exist; - expect(request.method).to.equal('GET'); - expect(request.payload).to.exist; - }); - }) - - describe('interpretResponse', function () { - let serverResponse = {}; - serverResponse.body = JSON.parse('{"div-0":["RB_K","728x90K"], "timestamp":["RB_K","1507565666"], "device": { "de": { "div-0":["A1","728x90K"] }, "mo": { "div-0":["RB_K","728x90K"] }, "tb": { "div-0":["RB_K","728x90K"] } } }'); - var bidRequest = { - 'method': 'get', - 'payload': [ - { - 'bidder': 'optimera', - 'params': { - 'clientID': '0' - }, - 'adUnitCode': 'div-0', - 'bidId': '307440db8538ab' - } - ] - } - it('interpresResponse fires', function () { - let bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses[0].dealId[0]).to.equal('RB_K'); - expect(bidResponses[0].dealId[1]).to.equal('728x90K'); - }); - }); - - describe('interpretResponse with optional device param', function () { - let serverResponse = {}; - serverResponse.body = JSON.parse('{"div-0":["RB_K","728x90K"], "timestamp":["RB_K","1507565666"], "device": { "de": { "div-0":["A1","728x90K"] }, "mo": { "div-0":["RB_K","728x90K"] }, "tb": { "div-0":["RB_K","728x90K"] } } }'); - var bidRequest = { - 'method': 'get', - 'payload': [ - { - 'bidder': 'optimera', - 'params': { - 'clientID': '0', - 'device': 'de' - }, - 'adUnitCode': 'div-0', - 'bidId': '307440db8538ab' - } - ] - } - it('interpresResponse fires', function () { - let bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses[0].dealId[0]).to.equal('A1'); - expect(bidResponses[0].dealId[1]).to.equal('728x90K'); - }); - }); -}); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index 42cc25ae04f..c6dbb24614a 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -68,21 +68,6 @@ describe('orbidderBidAdapter', () => { delete bidRequest.params; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - - it('accepts optional bidfloor', () => { - const bidRequest = deepClone(defaultBidRequest); - bidRequest.params.bidfloor = 123; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - - bidRequest.params.bidfloor = 1.23; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('doesn\'t accept malformed bidfloor', () => { - const bidRequest = deepClone(defaultBidRequest); - bidRequest.params.bidfloor = 'another not usable string'; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); }); describe('buildRequests', () => { diff --git a/test/spec/modules/otmBidAdapter_spec.js b/test/spec/modules/otmBidAdapter_spec.js deleted file mode 100644 index 8ac01c1657e..00000000000 --- a/test/spec/modules/otmBidAdapter_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/otmBidAdapter.js'; - -describe('otmBidAdapterTests', function () { - it('validate_pub_params', function () { - expect(spec.isBidRequestValid({ - bidder: 'otm', - params: { - tid: '123', - bidfloor: 20 - } - })).to.equal(true); - }); - - it('validate_generated_params', function () { - let bidRequestData = [{ - bidId: 'bid1234', - bidder: 'otm', - params: { - tid: '123', - bidfloor: 20 - }, - sizes: [[240, 400]] - }]; - - let request = spec.buildRequests(bidRequestData); - let req_data = request[0].data; - - expect(req_data.bidid).to.equal('bid1234'); - }); - - it('validate_best_size_select', function () { - // when: - let bidRequestData = [{ - bidId: 'bid1234', - bidder: 'otm', - params: { - tid: '123', - bidfloor: 20 - }, - sizes: [[300, 500], [300, 600], [240, 400], [300, 50]] - }]; - - let request = spec.buildRequests(bidRequestData); - let req_data = request[0].data; - - // then: - expect(req_data.w).to.equal(240); - expect(req_data.h).to.equal(400); - - // when: - bidRequestData = [{ - bidId: 'bid1234', - bidder: 'otm', - params: { - tid: '123', - bidfloor: 20 - }, - sizes: [[200, 240], [400, 440]] - }]; - - request = spec.buildRequests(bidRequestData); - req_data = request[0].data; - - // then: - expect(req_data.w).to.equal(200); - expect(req_data.h).to.equal(240); - }); - - it('validate_response_params', function () { - let bidRequestData = { - data: { - bidId: 'bid1234' - } - }; - - let serverResponse = { - body: [ - { - 'auctionid': '3c6f8e22-541b-485c-9214-e974d9fb1b6f', - 'cpm': 847.097, - 'ad': 'test html', - 'w': 240, - 'h': 400, - 'currency': 'RUB', - 'ttl': 300, - 'creativeid': '1_7869053', - 'bidid': '101f211def7c99', - 'transactionid': 'transaction_id_1' - } - ] - }; - - let bids = spec.interpretResponse(serverResponse, bidRequestData); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - expect(bid.cpm).to.equal(847.097); - expect(bid.currency).to.equal('RUB'); - expect(bid.width).to.equal(240); - expect(bid.height).to.equal(400); - expect(bid.netRevenue).to.equal(true); - expect(bid.requestId).to.equal('101f211def7c99'); - expect(bid.ad).to.equal('test html'); - }); -}); diff --git a/test/spec/modules/outconBidAdapter_spec.js b/test/spec/modules/outconBidAdapter_spec.js deleted file mode 100644 index 81c3fdded62..00000000000 --- a/test/spec/modules/outconBidAdapter_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/outconBidAdapter.js'; - -describe('outconBidAdapter', function () { - describe('bidRequestValidity', function () { - it('Check the bidRequest with pod param', function () { - expect(spec.isBidRequestValid({ - bidder: 'outcon', - params: { - pod: '5d603538eba7192ae14e39a4', - env: 'test' - } - })).to.equal(true); - }); - it('Check the bidRequest with internalID and publisherID params', function () { - expect(spec.isBidRequestValid({ - bidder: 'outcon', - params: { - internalId: '12345678', - publisher: '5d5d66f2306ea4114a37c7c2', - env: 'test' - } - })).to.equal(true); - }); - }); - describe('buildRequests', function () { - it('Build requests with pod param', function () { - expect(spec.buildRequests([{ - bidder: 'outcon', - params: { - pod: '5d603538eba7192ae14e39a4', - env: 'test' - } - }])).to.have.keys('method', 'url', 'data'); - }); - it('Build requests with internalID and publisherID params', function () { - expect(spec.buildRequests([{ - bidder: 'outcon', - params: { - internalId: '12345678', - publisher: '5d5d66f2306ea4114a37c7c2', - env: 'test' - } - }])).to.have.keys('method', 'url', 'data'); - }); - }); - describe('interpretResponse', function () { - const bidRequest = { - method: 'GET', - url: 'https://test.outcondigital.com/ad/', - data: { - pod: '5d603538eba7192ae14e39a4', - env: 'test', - vast: 'true' - } - }; - const bidResponse = { - body: { - cpm: 0.10, - cur: 'USD', - exp: 10, - creatives: [ - { - url: 'https://test.outcondigital.com/uploads/5d42e7a7306ea4689b67c122/frutas.mp4', - size: 3, - width: 1920, - height: 1080, - codec: 'video/mp4' - } - ], - ad: '5d6e6aef22063e392bf7f564', - type: 'video', - campaign: '5d42e44b306ea469593c76a2', - trackingURL: 'https://test.outcondigital.com/ad/track?track=5d6e6aef22063e392bf7f564', - vastURL: 'https://test.outcondigital.com/outcon.xml?impression=5d6e6aef22063e392bf7f564&demo=true' - }, - }; - it('check all the keys that are needed to interpret the response', function () { - const result = spec.interpretResponse(bidResponse, bidRequest); - let requiredKeys = [ - 'requestId', - 'cpm', - 'width', - 'height', - 'creativeId', - 'currency', - 'netRevenue', - 'ttl', - 'ad', - 'vastImpUrl', - 'mediaType', - 'vastUrl' - ]; - let resultKeys = Object.keys(result[0]); - resultKeys.forEach(function(key) { - expect(requiredKeys.indexOf(key) !== -1).to.equal(true); - }); - }) - }); -}); diff --git a/test/spec/modules/papyrusBidAdapter_spec.js b/test/spec/modules/papyrusBidAdapter_spec.js deleted file mode 100644 index 20fcced2950..00000000000 --- a/test/spec/modules/papyrusBidAdapter_spec.js +++ /dev/null @@ -1,115 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/papyrusBidAdapter.js'; - -const ENDPOINT = 'https://prebid.papyrus.global'; -const BIDDER_CODE = 'papyrus'; - -describe('papyrus Adapter', function () { - describe('isBidRequestValid', function () { - let validBidReq = { - bidder: BIDDER_CODE, - params: { - address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', - placementId: 'b57e55fd18614b0591893e9fff41fbea' - } - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); - }); - - let invalidBidReq = { - bidder: BIDDER_CODE, - params: { - 'placementId': '' - } - }; - - it('should not validate incorrect bid request', function () { - expect(spec.isBidRequestValid(invalidBidReq)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - bidder: BIDDER_CODE, - params: { - address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', - placementId: 'b57e55fd18614b0591893e9fff41fbea' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('sends valid bids count', function () { - const request = spec.buildRequests(bidRequests); - expect(request.data.length).to.equal(1); - }); - - it('sends all bid parameters', function () { - const request = spec.buildRequests(bidRequests); - expect(request.data[0]).to.have.all.keys(['address', 'placementId', 'sizes', 'bidId', 'transactionId']); - }); - - it('sedns valid sizes parameter', function () { - const request = spec.buildRequests(bidRequests); - expect(request.data[0].sizes[0]).to.equal('300x250'); - }); - }); - - describe('interpretResponse', function () { - let bidRequests = [ - { - bidder: BIDDER_CODE, - params: { - address: '0xd7e2a771c5dcd5df7f789477356aecdaeee6c985', - placementId: 'b57e55fd18614b0591893e9fff41fbea' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - let bidResponse = { - bids: [ - { - id: '1036e9746c-d186-49ae-90cb-2796d0f9b223', - adm: 'test adm', - cpm: 100, - height: 250, - width: 300 - } - ] - }; - - it('should build bid array', function () { - const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - expect(result.length).to.equal(1); - }); - - it('should have all relevant fields', function () { - const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - const bid = result[0]; - - expect(bid.cpm).to.equal(bidResponse.bids[0].cpm); - expect(bid.width).to.equal(bidResponse.bids[0].width); - expect(bid.height).to.equal(bidResponse.bids[0].height); - }); - }); -}); diff --git a/test/spec/modules/performaxBidAdapter_spec.js b/test/spec/modules/performaxBidAdapter_spec.js deleted file mode 100644 index 43c256b9d13..00000000000 --- a/test/spec/modules/performaxBidAdapter_spec.js +++ /dev/null @@ -1,274 +0,0 @@ -import * as utils from 'src/utils.js'; -import { expect } from 'chai'; -import { spec } from 'modules/performaxBidAdapter'; - -describe('PerformaxAdapter', function () { - let bidRequests, bidderRequest; - let serverResponse, serverRequest; - - const URL = - 'https://dale.performax.cz/hb?slotId[]=3,2&client=hellboy:v0.0.1&auctionId=144b5079-8cbf-49a5-aca7-a68b3296cd6c'; - - bidRequests = [ - { - adUnitCode: 'postbid_iframe', - auctionId: '144b5079-8cbf-49a5-aca7-a68b3296cd6c', - bidId: '2a4332f6b2bc74', - bidRequestsCount: 1, - bidder: 'performax', - bidderRequestId: '1c7d8bf204f11e', - bidderRequestsCount: 1, - bidderWinsCount: 0, - mediaTypes: { - banner: { - sizes: [[300, 300]], - }, - }, - params: { - slotId: 3, - }, - sizes: [[300, 300]], - src: 'client', - transactionId: '14969d09-0068-4d5b-a34e-e35091561dee', - }, - { - adUnitCode: 'postbid_iframe2', - auctionId: '144b5079-8cbf-49a5-aca7-a68b3296cd6c', - bidId: '300bb0ac6a156a', - bidRequestsCount: 1, - bidder: 'performax', - bidderRequestId: '1c7d8bf204f11e', - bidderRequestsCount: 1, - bidderWinsCount: 0, - mediaTypes: { - banner: { - sizes: [[300, 300]], - }, - }, - params: { - slotId: 2, - }, - sizes: [[300, 300]], - src: 'client', - transactionId: '107cbebd-8c36-4456-b28c-91a19ba80151', - }, - ]; - - bidderRequest = { - auctionId: '144b5079-8cbf-49a5-aca7-a68b3296cd6c', - auctionStart: 1594281941845, - bidderCode: 'performax', - bidderRequestId: '1c7d8bf204f11e', - bids: bidRequests, - refererInfo: { - canonicalUrl: '', - numIframes: 0, - reachedTop: true, - referer: '', - }, - stack: [''], - start: 1594281941935, - timeout: 3600, - }; - - serverResponse = { - body: [ - { - ad: { - code: '$SYS_ID$ $VAR_NAME$ rest of the code', - data: { - SYS_ID: 1, - VAR_NAME: 'name', - }, - format_id: 2, - id: 11, - size: { - width: 300, - height: 300, - }, - tag_ids: [], - type: 'creative', - }, - cpm: 30, - creativeId: 'creative:11', - currency: 'CZK', - height: 300, - meta: { - agencyId: 1, - mediaType: 'banner', - }, - netRevenue: true, - requestId: '2a4332f6b2bc74', - ttl: 60, - width: 300, - }, - { - ad: { - code: '', - reason: 'Slot 2 does not allow HB requests', - type: 'empty', - }, - cpm: 0, - creativeId: null, - currency: 'CZK', - height: null, - meta: { - agencyId: null, - mediaType: 'banner', - }, - netRevenue: true, - requestId: '1c7d8bf204f11e', - ttl: 60, - width: 300, - }, - ], - }; - - serverRequest = { - data: { - bidderRequest: bidderRequest, - validBidRequests: bidRequests, - }, - method: 'POST', - options: { - contentType: 'application/json', - }, - url: URL, - }; - - describe('Bid validations', function () { - it('Valid bid', function () { - let validBid = { - bidder: 'performax', - params: { - slotId: 2, - }, - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(true); - }); - - it('Invalid bid: required param is missing', function () { - let invalidBid = { - bidder: 'performax', - params: { - invalidParam: 2, - }, - }, - isValid = spec.isBidRequestValid(invalidBid); - expect(isValid).to.equal(false); - }); - }); - - describe('Build Url', function () { - it('Should return url', function () { - let url = spec.buildUrl(bidRequests, bidderRequest); - expect(url).to.equal(URL); - }); - }); - - describe('Build Request', function () { - it('Should not modify bidRequests and bidder Requests', function () { - let originalBidRequests = utils.deepClone(bidRequests); - let originalBidderRequest = utils.deepClone(bidderRequest); - let request = spec.buildRequests(bidRequests, bidderRequest); - - expect(bidRequests).to.deep.equal(originalBidRequests); - expect(bidderRequest).to.deep.equal(originalBidderRequest); - }); - - it('Endpoint checking', function () { - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.equal(URL); - expect(request.method).to.equal('POST'); - expect(request.options).to.deep.equal({ - contentType: 'application/json', - }); - }); - - it('Request params checking', function () { - let request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.validBidRequests).to.deep.equal(bidRequests); - expect(request.data.bidderRequest).to.deep.equal(bidderRequest); - }); - }); - - describe('Build Html', function () { - it('Ad with data: should return build html', function () { - let validAd = { - code: '$SYS_ID$ $VAR_NAME$ rest of the code', - data: { - SYS_ID: 1, - VAR_NAME: 'name', - }, - format_id: 2, - id: 11, - size: { - width: 300, - height: 300, - }, - tag_ids: [], - type: 'creative', - }; - let html = spec.buildHtml(validAd); - expect(html).to.equal('1 name rest of the code'); - }); - - it('Ad with partial data: should return html without data change', function () { - let adWithPartialData = { - code: '$SYS_ID$ $VAR_NAME$ rest of the code', - data: { - VAR_NAME: 'name', - }, - format_id: 2, - id: 11, - size: { - width: 300, - height: 300, - }, - tag_ids: [], - type: 'creative', - }; - let html = spec.buildHtml(adWithPartialData); - expect(html).to.equal('$SYS_ID$ name rest of the code'); - }); - - it('Ad without data: should return html without data change', function () { - let adWithoutData = { - code: '$SYS_ID$ $VAR_NAME$ rest of the code', - format_id: 2, - id: 11, - size: { - width: 300, - height: 300, - }, - tag_ids: [], - type: 'creative', - }; - let html = spec.buildHtml(adWithoutData); - expect(html).to.equal('$SYS_ID$ $VAR_NAME$ rest of the code'); - }); - }); - - describe('Interpret Response', function () { - it('Ad without data: should return html without data change', function () { - let ads = spec.interpretResponse(serverResponse, serverRequest); - expect(ads).to.have.length(1); - expect(ads[0]).to.deep.equal({ - ad: '1 name rest of the code', - cpm: 30, - creativeId: 'creative:11', - currency: 'CZK', - height: 300, - meta: { - agencyId: 1, - mediaType: 'banner', - }, - netRevenue: true, - requestId: '2a4332f6b2bc74', - ttl: 60, - width: 300, - }); - }); - }); -}); diff --git a/test/spec/modules/piximediaBidAdapter_spec.js b/test/spec/modules/piximediaBidAdapter_spec.js deleted file mode 100644 index 88a6433b71d..00000000000 --- a/test/spec/modules/piximediaBidAdapter_spec.js +++ /dev/null @@ -1,103 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/piximediaBidAdapter.js'; - -describe('piximediaAdapterTest', function() { - describe('bidRequestValidity', function() { - it('bidRequest with site ID and placement ID param', function() { - expect(spec.isBidRequestValid({ - bidder: 'piximedia', - params: { - 'siteId': 'PIXIMEDIA_PREBID10', - 'placementId': 'RG' - }, - })).to.equal(true); - }); - - it('bidRequest with no required params', function() { - expect(spec.isBidRequestValid({ - bidder: 'piximedia', - params: { - }, - })).to.equal(false); - }); - }); - - describe('bidRequest', function() { - const bidRequests = [{ - 'bidder': 'piximedia', - 'params': { - 'siteId': 'PIXIMEDIA_PREBID10', - 'placementId': 'RG' - }, - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'sizes': [300, 250], - 'bidId': '51ef8751f9aead', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757' - }]; - - it('bidRequest HTTP method', function() { - const requests = spec.buildRequests(bidRequests); - requests.forEach(function(requestItem) { - expect(requestItem.method).to.equal('GET'); - }); - }); - - it('bidRequest data', function() { - const requests = spec.buildRequests(bidRequests); - expect(typeof requests[0].data.timestamp).to.equal('number'); - expect(requests[0].data.pbsizes).to.equal('["300x250"]'); - expect(requests[0].data.pver).to.equal('1.0'); - expect(requests[0].data.pbparams).to.equal(JSON.stringify(bidRequests[0].params)); - expect(requests[0].data.pbwidth).to.equal('300'); - expect(requests[0].data.pbheight).to.equal('250'); - expect(requests[0].data.pbbidid).to.equal('51ef8751f9aead'); - }); - }); - - describe('interpretResponse', function() { - const bidRequest = { - 'method': 'GET', - 'url': 'https://ad.piximedia.com/', - 'data': { - 'ver': 2, - 'hb': 1, - 'output': 'js', - 'pub': 267, - 'zone': 62546, - 'width': '300', - 'height': '250', - 'callback': 'json', - 'callback_uid': '51ef8751f9aead', - 'url': 'https://example.com', - 'cb': '', - } - }; - - const bidResponse = { - body: { - 'bidId': '51ef8751f9aead', - 'cpm': 4.2, - 'width': '300', - 'height': '250', - 'creative_id': '1234', - 'currency': 'EUR', - 'adm': '
', - }, - headers: {} - }; - - it('result is correct', function() { - const result = spec.interpretResponse(bidResponse, bidRequest); - expect(result[0].requestId).to.equal('51ef8751f9aead'); - expect(result[0].cpm).to.equal(4.2); - expect(result[0].width).to.equal('300'); - expect(result[0].height).to.equal('250'); - expect(result[0].creativeId).to.equal('1234'); - expect(result[0].currency).to.equal('EUR'); - expect(result[0].ttl).to.equal(300); - expect(result[0].ad).to.equal('
'); - }); - }); -}); diff --git a/test/spec/modules/platformioBidAdapter_spec.js b/test/spec/modules/platformioBidAdapter_spec.js deleted file mode 100644 index ee753be17a7..00000000000 --- a/test/spec/modules/platformioBidAdapter_spec.js +++ /dev/null @@ -1,348 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/platformioBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; - -describe('Platform.io Adapter Tests', function () { - const slotConfigs = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bidId: 'bid12345', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '123', - bidFloor: '0.001', - ifa: 'IFA', - latitude: '40.712775', - longitude: '-74.005973' - } - }, { - placementCode: '/DfpAccount2/slot2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - bidId: 'bid23456', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '1234', - bidFloor: '0.000001', - } - }]; - const nativeSlotConfig = [{ - placementCode: '/DfpAccount1/slot3', - bidId: 'bid12345', - mediaType: 'native', - nativeParams: { - title: { required: true, len: 200 }, - body: {}, - image: { wmin: 100 }, - sponsoredBy: { }, - icon: { } - }, - params: { - pubId: '29521', - placementId: '123', - siteId: '26047' - } - }]; - const videoSlotConfig = [{ - placementCode: '/DfpAccount1/slot4', - mediaTypes: { - video: { - playerSize: [[640, 480]] - } - }, - bidId: 'bid12345678', - mediaType: 'video', - video: { - skippable: true - }, - params: { - pubId: '29521', - placementId: '1234567', - siteId: '26047', - } - }]; - const appSlotConfig = [{ - placementCode: '/DfpAccount1/slot5', - bidId: 'bid12345', - params: { - pubId: '29521', - placementId: '1234', - app: { - id: '1111', - name: 'app name', - bundle: 'io.platform.apps', - storeUrl: 'https://platform.io/apps', - domain: 'platform.io' - } - } - }]; - - it('Verify build request', function () { - const request = spec.buildRequests(slotConfigs); - expect(request.url).to.equal('https://piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // site object - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.publisher).to.not.equal(null); - expect(ortbRequest.site.publisher.id).to.equal('29521'); - expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); - expect(ortbRequest.site.page).to.equal(window.location.href); - expect(ortbRequest.imp).to.have.lengthOf(2); - // device object - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); - expect(ortbRequest.device.ifa).to.equal('IFA'); - expect(ortbRequest.device.geo.lat).to.equal('40.712775'); - expect(ortbRequest.device.geo.lon).to.equal('-74.005973'); - // slot 1 - expect(ortbRequest.imp[0].tagid).to.equal('123'); - expect(ortbRequest.imp[0].banner).to.not.equal(null); - expect(ortbRequest.imp[0].banner.w).to.equal(300); - expect(ortbRequest.imp[0].banner.h).to.equal(250); - expect(ortbRequest.imp[0].bidfloor).to.equal('0.001'); - // slot 2 - expect(ortbRequest.imp[1].tagid).to.equal('1234'); - expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.w).to.equal(728); - expect(ortbRequest.imp[1].banner.h).to.equal(90); - expect(ortbRequest.imp[1].bidfloor).to.equal('0.000001'); - }); - - it('Verify parse response', function () { - const request = spec.buildRequests(slotConfigs); - const ortbRequest = JSON.parse(request.data); - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad', - w: 300, - h: 250 - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('This is an Ad'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.adId).to.equal('bid12345'); - expect(bid.creativeId).to.equal('bid12345'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verify full passback', function () { - const request = spec.buildRequests(slotConfigs); - const bids = spec.interpretResponse({ body: null }, request) - expect(bids).to.have.lengthOf(0); - }); - - it('Verify Native request', function () { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('https://piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('123'); - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.ver).to.equal('1.1'); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(5); - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[3].id).to.equal(4); - expect(nativeRequest.assets[4].id).to.equal(5); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.not.equal(null); - expect(nativeRequest.assets[0].title.len).to.equal(200); - expect(nativeRequest.assets[1].title).to.be.undefined; - expect(nativeRequest.assets[1].data).to.not.equal(null); - expect(nativeRequest.assets[1].data.type).to.equal(2); - expect(nativeRequest.assets[1].data.len).to.equal(200); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[3].img).to.not.equal(null); - expect(nativeRequest.assets[3].img.wmin).to.equal(50); - expect(nativeRequest.assets[3].img.hmin).to.equal(50); - expect(nativeRequest.assets[3].img.type).to.equal(1); - expect(nativeRequest.assets[4].img).to.not.equal(null); - expect(nativeRequest.assets[4].img.wmin).to.equal(100); - expect(nativeRequest.assets[4].img.hmin).to.equal(150); - expect(nativeRequest.assets[4].img.type).to.equal(3); - }); - - it('Verify Native response', function () { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('https://piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - const nativeResponse = { - 'native': { - assets: [ - { id: 1, title: { text: 'Ad Title' } }, - { id: 2, data: { value: 'Test description' } }, - { id: 3, data: { value: 'Brand' } }, - { id: 4, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_icon.png', w: 100, h: 100 } }, - { id: 5, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_image.png', w: 300, h: 300 } } - ], - link: { url: 'https://brand.com/' } - } - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - nurl: 'https://rtb.adx1.com/log', - adm: JSON.stringify(nativeResponse) - }] - }], - cur: 'USD', - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.adId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - const nativeBid = bid['native']; - expect(nativeBid).to.not.equal(null); - expect(nativeBid.title).to.equal('Ad Title'); - expect(nativeBid.sponsoredBy).to.equal('Brand'); - expect(nativeBid.icon.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_icon.png'); - expect(nativeBid.image.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_image.png'); - expect(nativeBid.image.width).to.equal(300); - expect(nativeBid.image.height).to.equal(300); - expect(nativeBid.icon.width).to.equal(100); - expect(nativeBid.icon.height).to.equal(100); - expect(nativeBid.clickUrl).to.equal(encodeURIComponent('https://brand.com/')); - expect(nativeBid.impressionTrackers).to.have.lengthOf(1); - expect(nativeBid.impressionTrackers[0]).to.equal('https://rtb.adx1.com/log'); - }); - - it('Verify Video request', function () { - const request = spec.buildRequests(videoSlotConfig); - expect(request.url).to.equal('https://piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const videoRequest = JSON.parse(request.data); - // site object - expect(videoRequest.site).to.not.equal(null); - expect(videoRequest.site.publisher.id).to.equal('29521'); - expect(videoRequest.site.ref).to.equal(window.top.document.referrer); - expect(videoRequest.site.page).to.equal(window.location.href); - // device object - expect(videoRequest.device).to.not.equal(null); - expect(videoRequest.device.ua).to.equal(navigator.userAgent); - // slot 1 - expect(videoRequest.imp[0].tagid).to.equal('1234567'); - expect(videoRequest.imp[0].video).to.not.equal(null); - expect(videoRequest.imp[0].video.w).to.equal(640); - expect(videoRequest.imp[0].video.h).to.equal(480); - expect(videoRequest.imp[0].banner).to.equal(null); - expect(videoRequest.imp[0].native).to.equal(null); - }); - - it('Verify parse video response', function () { - const request = spec.buildRequests(videoSlotConfig); - const videoRequest = JSON.parse(request.data); - const videoResponse = { - seatbid: [{ - bid: [{ - impid: videoRequest.imp[0].id, - price: 1.90, - adm: 'https://vid.example.com/9876', - crid: '510511_754567308' - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: videoResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.90); - expect(bid.vastUrl).to.equal('https://vid.example.com/9876'); - expect(bid.crid).to.equal('510511_754567308'); - expect(bid.width).to.equal(640); - expect(bid.height).to.equal(480); - expect(bid.adId).to.equal('bid12345678'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verifies bidder code', function () { - expect(spec.code).to.equal('platformio'); - }); - - it('Verifies supported media types', function () { - expect(spec.supportedMediaTypes).to.have.lengthOf(3); - expect(spec.supportedMediaTypes[0]).to.equal('banner'); - expect(spec.supportedMediaTypes[1]).to.equal('native'); - expect(spec.supportedMediaTypes[2]).to.equal('video'); - }); - - it('Verifies if bid request valid', function () { - expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); - expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); - expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); - expect(spec.isBidRequestValid(videoSlotConfig[0])).to.equal(true); - }); - - it('Verify app requests', function () { - const request = spec.buildRequests(appSlotConfig); - const ortbRequest = JSON.parse(request.data); - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.publisher).to.not.equal(null); - expect(ortbRequest.app.publisher.id).to.equal('29521'); - expect(ortbRequest.app.id).to.equal('1111'); - expect(ortbRequest.app.name).to.equal('app name'); - expect(ortbRequest.app.bundle).to.equal('io.platform.apps'); - expect(ortbRequest.app.storeurl).to.equal('https://platform.io/apps'); - expect(ortbRequest.app.domain).to.equal('platform.io'); - }); - - it('Verify GDPR', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'serialized_gpdr_data' - } - }; - const request = spec.buildRequests(slotConfigs, bidderRequest); - expect(request.url).to.equal('https://piohbdisp.hb.adx1.com/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - expect(ortbRequest.user).to.not.equal(null); - expect(ortbRequest.user.ext).to.not.equal(null); - expect(ortbRequest.user.ext.consent).to.equal('serialized_gpdr_data'); - expect(ortbRequest.regs).to.not.equal(null); - expect(ortbRequest.regs.ext).to.not.equal(null); - expect(ortbRequest.regs.ext.gdpr).to.equal(1); - }); -}); diff --git a/test/spec/modules/projectLimeLightBidAdapter_spec.js b/test/spec/modules/projectLimeLightBidAdapter_spec.js deleted file mode 100644 index 778d8eedf7b..00000000000 --- a/test/spec/modules/projectLimeLightBidAdapter_spec.js +++ /dev/null @@ -1,259 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/projectLimeLightBidAdapter.js'; - -describe('ProjectLimeLightAdapter', function () { - const bid1 = { - bidId: '2dd581a2b6281d', - bidder: 'project-limelight', - bidderRequestId: '145e1d6a7837c9', - params: { - host: 'ads.project-limelight.com', - adUnitId: 123, - adUnitType: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - } - const bid2 = { - bidId: '58ee9870c3164a', - bidder: 'project-limelight', - bidderRequestId: '209fdaf1c81649', - params: { - host: 'cpm.project-limelight.com', - adUnitId: 456, - adUnitType: 'banner' - }, - placementCode: 'placement_1', - auctionId: '482f88de-29ab-45c8-981a-d25e39454a34', - sizes: [[350, 200]], - transactionId: '068867d1-46ec-40bb-9fa0-e24611786fb4' - } - const bid3 = { - bidId: '019645c7d69460', - bidder: 'project-limelight', - bidderRequestId: 'f2b15f89e77ba6', - params: { - host: 'ads.project-limelight.com', - adUnitId: 789, - adUnitType: 'video' - }, - placementCode: 'placement_2', - auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', - sizes: [[800, 600]], - transactionId: '738d5915-6651-43b9-9b6b-d50517350917' - } - - describe('buildRequests', function () { - const serverRequests = spec.buildRequests([bid1, bid2, bid3]) - it('Creates two ServerRequests', function() { - expect(serverRequests).to.exist - expect(serverRequests).to.have.lengthOf(2) - }) - serverRequests.forEach(serverRequest => { - 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 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', 'secure', 'adUnits') - expect(data.deviceWidth).to.be.a('number') - expect(data.deviceHeight).to.be.a('number') - expect(data.secure).to.be.a('boolean') - data.adUnits.forEach(adUnit => { - expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId') - expect(adUnit.id).to.be.a('number') - expect(adUnit.bidId).to.be.a('string') - expect(adUnit.type).to.be.a('string') - expect(adUnit.transactionId).to.be.a('string') - expect(adUnit.sizes).to.be.an('array') - }) - }) - }) - it('Returns valid URL', function () { - expect(serverRequests[0].url).to.equal('https://ads.project-limelight.com/hb') - expect(serverRequests[1].url).to.equal('https://cpm.project-limelight.com/hb') - }) - it('Returns valid adUnits', function () { - validateAdUnit(serverRequests[0].data.adUnits[0], bid1) - validateAdUnit(serverRequests[1].data.adUnits[0], bid2) - validateAdUnit(serverRequests[0].data.adUnits[1], bid3) - }) - it('Returns empty data if no valid requests are passed', function () { - const serverRequests = spec.buildRequests([]) - expect(serverRequests).to.be.an('array').that.is.empty - }) - }) - describe('interpretBannerResponse', function () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - describe('interpretVideoResponse', function () { - let resObject = { - body: [ { - requestId: '123', - mediaType: 'video', - cpm: 0.3, - width: 320, - height: 50, - vastXml: '', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - } ] - }; - let serverResponses = spec.interpretResponse(resObject); - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.vastXml).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - } - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - describe('isBidRequestValid', function() { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'project-limelight', - bidderRequestId: '145e1d6a7837c9', - params: { - adUnitId: 123, - adUnitType: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - }; - - it('should return true when required params found', function() { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function() { - let bidFailed = { - bidder: 'project-limelight', - bidderRequestId: '145e1d6a7837c9', - params: { - adUnitId: 123, - adUnitType: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' - }; - expect(spec.isBidRequestValid(bidFailed)).to.equal(false); - }); - }); - describe('interpretResponse', function() { - let resObject = { - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD' - }; - it('should skip responses which do not contain required params', function() { - let bidResponses = { - body: [ { - mediaType: 'banner', - cpm: 0.3, - ttl: 1000, - currency: 'USD' - }, resObject ] - } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); - }); - it('should skip responses which do not contain expected mediaType', function() { - let bidResponses = { - body: [ { - requestId: '123', - mediaType: 'native', - cpm: 0.3, - creativeId: '123asd', - ttl: 1000, - currency: 'USD' - }, resObject ] - } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); - }); - }); -}); - -function validateAdUnit(adUnit, bid) { - expect(adUnit.id).to.equal(bid.params.adUnitId) - expect(adUnit.bidId).to.equal(bid.bidId) - expect(adUnit.type).to.equal(bid.params.adUnitType.toUpperCase()) - expect(adUnit.transactionId).to.equal(bid.transactionId) - expect(adUnit.sizes).to.deep.equal(bid.sizes.map(size => { - return { - width: size[0], - height: size[1] - } - })) -} diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js deleted file mode 100644 index bdcdca06183..00000000000 --- a/test/spec/modules/proxistoreBidAdapter_spec.js +++ /dev/null @@ -1,166 +0,0 @@ -import { expect } from 'chai'; -let { spec } = require('modules/proxistoreBidAdapter'); -const BIDDER_CODE = 'proxistore'; -describe('ProxistoreBidAdapter', function () { - const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const bidderRequest = { - bidderCode: BIDDER_CODE, - auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', - bidderRequestId: '10edf38ec1a719', - gdprConsent: { - apiVersion: 2, - gdprApplies: true, - consentString: consentString, - vendorData: { - vendor: { - consents: { - 418: true, - }, - }, - }, - }, - }; - let bid = { - sizes: [[300, 600]], - params: { - website: 'example.fr', - language: 'fr', - }, - ortb2: { - user: { ext: { data: { segments: [], contextual_categories: {} } } }, - }, - auctionId: 442133079, - bidId: 464646969, - transactionId: 511916005, - }; - describe('isBidRequestValid', function () { - it('it should be true if required params are presents and there is no info in the local storage', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function () { - const date = new Date(); - date.setMinutes(date.getMinutes() - 1); - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function () { - const date = new Date(); - date.setMinutes(date.getMinutes() - 10); - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - describe('buildRequests', function () { - const url = { - cookieBase: 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi', - cookieLess: - 'https://abs.cookieless-proxistore.com/fr/v3/rtb/prebid/multi', - }; - let request = spec.buildRequests([bid], bidderRequest); - it('should return a valid object', function () { - expect(request).to.be.an('object'); - expect(request.method).to.exist; - expect(request.url).to.exist; - expect(request.data).to.exist; - }); - it('request method should be POST', function () { - expect(request.method).to.equal('POST'); - }); - it('should have the value consentGiven to true bc we have 418 in the vendor list', function () { - const data = JSON.parse(request.data); - expect(data.gdpr.consentString).equal( - bidderRequest.gdprConsent.consentString - ); - expect(data.gdpr.applies).to.be.true; - expect(data.gdpr.consentGiven).to.be.true; - }); - it('should contain a valid url', function () { - // has gdpr consent - expect(request.url).equal(url.cookieBase); - // doens't have gpdr consent - bidderRequest.gdprConsent.vendorData = null; - - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieLess); - - // api v2 - bidderRequest.gdprConsent = { - gdprApplies: true, - allowAuctionWithoutConsent: true, - consentString: consentString, - vendorData: { - vendor: { - consents: { - '418': true - } - }, - }, - apiVersion: 2 - }; - // has gdpr consent - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieBase); - - bidderRequest.gdprConsent.vendorData.vendor = {}; - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieLess); - }); - it('should have a property a length of bids equal to one if there is only one bid', function () { - const data = JSON.parse(request.data); - expect(data.hasOwnProperty('bids')).to.be.true; - expect(data.bids).to.be.an('array'); - expect(data.bids.length).equal(1); - expect(data.bids[0].hasOwnProperty('id')).to.be.true; - expect(data.bids[0].sizes).to.be.an('array'); - }); - it('should correctly set bidfloor on imp when getfloor in scope', function () { - let data = JSON.parse(request.data); - expect(data.bids[0].floor).to.be.null; - - // make it respond with a non USD floor should not send it - bid.getFloor = function () { - return { currency: 'EUR', floor: 1.0 }; - }; - let req = spec.buildRequests([bid], bidderRequest); - data = JSON.parse(req.data); - expect(data.bids[0].floor).equal(1); - bid.getFloor = function () { - return { currency: 'USD', floor: 1.0 }; - }; - req = spec.buildRequests([bid], bidderRequest); - data = JSON.parse(req.data); - expect(data.bids[0].floor).to.be.null; - }); - }); - describe('interpretResponse', function () { - const emptyResponseParam = { body: [] }; - const fakeResponseParam = { - body: [ - { - ad: '', - cpm: 6.25, - creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', - currency: 'EUR', - dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', - height: 600, - netRevenue: true, - requestId: '3543724f2a033c9', - segments: [], - ttl: 10, - vastUrl: null, - vastXml: null, - width: 300, - }, - ], - }; - - it('should always return an array', function () { - let response = spec.interpretResponse(emptyResponseParam, bid); - expect(response).to.be.an('array'); - expect(response.length).equal(0); - response = spec.interpretResponse(fakeResponseParam, bid); - expect(response).to.be.an('array'); - expect(response.length).equal(1); - }); - }); -}); diff --git a/test/spec/modules/pubxBidAdapter_spec.js b/test/spec/modules/pubxBidAdapter_spec.js deleted file mode 100644 index 6cea8787845..00000000000 --- a/test/spec/modules/pubxBidAdapter_spec.js +++ /dev/null @@ -1,189 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/pubxBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -describe('pubxAdapter', function () { - const adapter = newBidder(spec); - const ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - const bid = { - bidder: 'pubx', - params: { - sid: '12345abc' - } - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - id: '26c1ee0038ac11', - params: { - sid: '12345abc' - } - } - ]; - - const data = { - banner: { - sid: '12345abc' - } - }; - - it('sends bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(bidRequests)[0]; - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('GET'); - }); - - it('should attach params to the banner request', function () { - const request = spec.buildRequests(bidRequests)[0]; - expect(request.data).to.deep.equal(data.banner); - }); - }); - - describe('getUserSyncs', function () { - const sandbox = sinon.sandbox.create(); - - const keywordsText = 'meta1,meta2,meta3,meta4,meta5'; - const descriptionText = 'description1description2description3description4description5description'; - - let documentStubMeta; - - beforeEach(function () { - documentStubMeta = sandbox.stub(document, 'getElementsByName'); - const metaElKeywords = document.createElement('meta'); - metaElKeywords.setAttribute('name', 'keywords'); - metaElKeywords.setAttribute('content', keywordsText); - documentStubMeta.withArgs('keywords').returns([metaElKeywords]); - - const metaElDescription = document.createElement('meta'); - metaElDescription.setAttribute('name', 'description'); - metaElDescription.setAttribute('content', descriptionText); - documentStubMeta.withArgs('description').returns([metaElDescription]); - }); - - afterEach(function () { - documentStubMeta.restore(); - }); - - let kwString = ''; - let kwEnc = ''; - let descContent = ''; - let descEnc = ''; - - it('returns empty sync array when iframe is not enabled', function () { - const syncOptions = {}; - expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]); - }); - - it('returns kwEnc when there is kwTag with more than 20 length', function () { - const kwArray = keywordsText.substr(0, 20).split(','); - kwArray.pop(); - kwString = kwArray.join(); - kwEnc = encodeURIComponent(kwString); - const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs[0].url).to.include(`pkw=${kwEnc}`); - }); - - it('returns kwEnc when there is kwTag with more than 60 length', function () { - descContent = descContent.substr(0, 60); - descEnc = encodeURIComponent(descContent); - const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs[0].url).to.include(`pkw=${descEnc}`); - }); - - it('returns titleEnc when there is titleContent with more than 30 length', function () { - let titleText = 'title1title2title3title4title5title'; - const documentStubTitle = sandbox.stub(document, 'title').value(titleText); - - if (titleText.length > 30) { - titleText = titleText.substr(0, 30); - } - - const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs[0].url).to.include(`pt=${encodeURIComponent(titleText)}`); - }); - }); - - describe('interpretResponse', function () { - const serverResponse = { - body: { - TTL: 300, - adm: '
some creative
', - cid: 'TKmB', - cpm: 500, - currency: 'JPY', - height: 250, - width: 300, - } - } - - const bidRequests = [ - { - id: '26c1ee0038ac11', - params: { - sid: '12345abc' - } - } - ]; - - const bidResponses = [ - { - requestId: '26c1ee0038ac11', - cpm: 500, - currency: 'JPY', - width: 300, - height: 250, - creativeId: 'TKmB', - netRevenue: true, - ttl: 300, - ad: '
some creative
' - } - ]; - it('should return empty array when required param is empty', function () { - const serverResponseWithCidEmpty = { - body: { - TTL: 300, - adm: '
some creative
', - cid: '', - cpm: '', - currency: 'JPY', - height: 250, - width: 300, - } - } - const result = spec.interpretResponse(serverResponseWithCidEmpty, bidRequests[0]); - expect(result).to.be.empty; - }); - it('handles banner responses', function () { - const result = spec.interpretResponse(serverResponse, bidRequests[0])[0]; - expect(result.requestId).to.equal(bidResponses[0].requestId); - expect(result.width).to.equal(bidResponses[0].width); - expect(result.height).to.equal(bidResponses[0].height); - expect(result.creativeId).to.equal(bidResponses[0].creativeId); - expect(result.currency).to.equal(bidResponses[0].currency); - expect(result.netRevenue).to.equal(bidResponses[0].netRevenue); - expect(result.ttl).to.equal(bidResponses[0].ttl); - expect(result.ad).to.equal(bidResponses[0].ad); - }); - }); -}); diff --git a/test/spec/modules/reklamstoreBidAdapter_spec.js b/test/spec/modules/reklamstoreBidAdapter_spec.js deleted file mode 100644 index 1dcd6c17ca4..00000000000 --- a/test/spec/modules/reklamstoreBidAdapter_spec.js +++ /dev/null @@ -1,85 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/reklamstoreBidAdapter.js'; - -describe('reklamstoreBidAdapterTests', function() { - let bidRequestData = { - bids: [ - { - bidder: 'reklamstore', - params: { - regionId: 532211 - }, - sizes: [[300, 250]] - } - ] - }; - let request = []; - - it('validate_params', function() { - expect( - spec.isBidRequestValid({ - bidder: 'reklamstore', - params: { - regionId: 532211 - } - }) - ).to.equal(true); - }); - - it('validate_generated_params', function() { - let bidderRequest = { - refererInfo: { - referer: 'https://reklamstore.com' - } - }; - request = spec.buildRequests(bidRequestData.bids, bidderRequest); - let req_data = request[0].data; - - expect(req_data.regionId).to.equal(532211); - }); - - const serverResponse = { - body: - { - cpm: 1.2, - ad: 'Ad html', - w: 300, - h: 250, - syncs: [{ - type: 'image', - url: 'https://link1' - }, - { - type: 'iframe', - url: 'https://link2' - } - ] - } - }; - - it('validate_response_params', function() { - let bids = spec.interpretResponse(serverResponse, bidRequestData.bids[0]); - expect(bids).to.have.lengthOf(1); - - let bid = bids[0]; - expect(bid.ad).to.equal('Ad html'); - expect(bid.cpm).to.equal(1.2); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.currency).to.equal('USD'); - }); - - it('should return no syncs when pixel syncing is disabled', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: false }, [serverResponse]); - expect(syncs).to.deep.equal([]); - }); - - it('should return user syncs', function () { - const syncs = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: true}, [serverResponse]); - const expected = [ - { type: 'image', url: 'https://link1' }, - { type: 'iframe', url: 'https://link2' }, - ]; - expect(syncs).to.deep.equal(expected); - }); -}); diff --git a/test/spec/modules/reloadBidAdapter_spec.js b/test/spec/modules/reloadBidAdapter_spec.js deleted file mode 100644 index b22dd9e7b92..00000000000 --- a/test/spec/modules/reloadBidAdapter_spec.js +++ /dev/null @@ -1,295 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/reloadBidAdapter.js'; - -let getParams = () => { - return JSON.parse(JSON.stringify({ - 'plcmID': 'placement_01', - 'partID': 'part00', - 'opdomID': 1, - 'bsrvID': 1, - 'type': 'pcm' - })); -}; - -let getBidderRequest = () => { - return JSON.parse(JSON.stringify({ - bidderCode: 'reload', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - bidderRequestId: '7101db09af0db2', - start: new Date().getTime(), - bids: [{ - bidder: 'reload', - bidId: '84ab500420319d', - bidderRequestId: '7101db09af0db2', - auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', - params: getParams() - }] - })); -}; - -let getValidBidRequests = () => { - return JSON.parse(JSON.stringify([ - { - 'bidder': 'reload', - 'params': getParams(), - 'mediaTypes': { - 'banner': { - 'sizes': [[160, 600]] - } - }, - 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', - 'transactionId': '8cbafa10-123d-4673-a1a5-04a1c7d62ded', - 'sizes': [[160, 600]], - 'bidId': '2236e11dc09931', - 'bidderRequestId': '1266bb886c2267', - 'auctionId': '4fb72c4d-94dc-4db1-8fac-3c2090ceeec0', - 'src': 'client', - 'bidRequestsCount': 1 - } - ])); -} - -let getExt1ServerResponse = () => { - return JSON.parse(JSON.stringify({ - 'pcmdata': { - 'thisVer': '100', - 'plcmSett': { - 'name': 'zz_test_mariano_adapter', - 'Version': '210', - 'lifeSpan': '100', - 'versionFolder': 'v4.14q', - 'versionFolderA': 'v4.14q', - 'versionFolderB': '', - 'stage': 'zz_test_mariano_adapter', - 'synchro': 1556916507000, - 'localCache': 'true', - 'testCase': 'A:00_B:100', - 'opdomain': '1', - 'checksum': '6378', - 'cpm': '0', - 'bstfct': '100', - 'totstop': 'false', - 'pcmurl': 'bidsrv01.reload.net' - }, - 'srvUrl': 'bidsrv01.reload.net', - 'instr': {'go': true, 'prc': 32, 'cur': 'USD'}, - 'statStr': 'eyN4aHYnQCk5OTotOC', - 'status': 'ok', - 'message': '', - 'log': '---- LOG ----' - }, - 'plcmID': 'zz_test_mariano_adapter', - 'partID': 'prx_part', - 'opdomID': '0', - 'bsrvID': 1, - 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', - 'banner': {'w': 300, 'h': 250} - })); -} - -let getExt2ServerResponse = () => { - return JSON.parse(JSON.stringify({ - 'pcmdata': { - 'thisVer': '100', - 'plcmSett': { - 'name': 'placement_01', - 'Version': '210', - 'lifeSpan': '100', - 'versionFolder': 'v4.14q', - 'versionFolderA': 'v4.14q', - 'versionFolderB': '', - 'stage': 'placement_01', - 'synchro': 1556574760000, - 'localCache': 'true', - 'testCase': 'A:00_B:100', - 'opdomain': '1', - 'checksum': '6378', - 'cpm': '0', - 'bstfct': '100', - 'totstop': 'false', - 'pcmurl': 'bidsrv00.reload.net' - }, - 'srvUrl': 'bidsrv00.reload.net', - 'log': 'incomp_input_obj_version', - 'message': 'incomp_input_obj_version', - 'status': 'error' - }, - 'plcmID': 'placement_01', - 'partID': 'prx_part', - 'opdomID': '0', - 'bsrvID': 1, - 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', - 'banner': {'w': 160, 'h': 600} - })); -} - -let getServerResponse = (pExt) => { - return JSON.parse(JSON.stringify({ - 'body': { - 'id': '2759340f70210d', - 'bidid': 'fbs-br-3mzdbycetjv8f8079', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'fbs-br-stbd-bd-3mzdbycetjv8f807b', - 'price': 0, - 'nurl': '', - 'adm': '', - 'ext': pExt - } - ], - 'seat': 'fbs-br-stbd-3mzdbycetjv8f807a', - 'group': 0 - } - ] - }, - 'headers': {} - })); -} - -describe('ReloadAdapter', function () { - describe('isBidRequestValid', function () { - var bid = { - 'bidder': 'reload', - 'params': { - 'plcmID': 'placement_01', - 'partID': 'part00', - 'opdomID': 1, - 'bsrvID': 23, - 'type': 'pcm' - }, - '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 bsrvID is not number', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'plcmID': 'placement_01', - 'partID': 'part00', - 'opdomID': 1, - 'bsrvID': 'abc' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when bsrvID > 99', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'plcmID': 'placement_01', - 'partID': 'part00', - 'opdomID': 1, - 'bsrvID': 230 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when bsrvID < 0', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'plcmID': 'placement_01', - 'partID': 'part00', - 'opdomID': 1, - 'bsrvID': -3, - 'type': 'pcm' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'plcmID': 'placement_01' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests()', function () { - let vRequests = spec.buildRequests(getValidBidRequests(), {}); - let vData = JSON.parse(vRequests[0].data); - - it('should send one requests', () => { - expect(vRequests.length).to.equal(1); - }); - - it('should send one requests, one impression', () => { - expect(vData.imp.length).to.equal(1); - }); - - it('should exists ext.type and ext.pcmdata', () => { - expect(vData.imp[0].banner).to.exist; - expect(vData.imp[0].banner.ext).to.exist; - expect(vData.imp[0].banner.ext.type).to.exist; - expect(vData.imp[0].banner.ext.pcmdata).to.exist; - expect(vData.imp[0].banner.ext.type).to.equal('pcm'); - }); - }); - - describe('interpretResponse()', function () { - it('Returns an empty array', () => { - let vData = spec.interpretResponse(getServerResponse(getExt2ServerResponse()), {}); - - expect(vData.length).to.equal(0); - }); - - it('Returns an array with one response', () => { - let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); - expect(vData.length).to.equal(1); - }); - - it('required fileds', () => { - let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); - expect(vData.length).to.equal(1); - expect(vData[0]).to.have.all.keys(['requestId', 'ad', 'cpm', 'width', 'height', 'creativeId', 'currency', 'ttl', 'netRevenue']); - }); - - it('CPM great than 0', () => { - let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); - expect(vData[0].cpm).to.greaterThan(0); - }); - - it('instruction empty', () => { - let vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); - vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = null; - let vData = spec.interpretResponse(vResponse, {}); - expect(vData.length).to.equal(0); - - vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); - vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = undefined; - vData = spec.interpretResponse(vResponse, {}); - expect(vData.length).to.equal(0); - - vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); - vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = undefined; - vData = spec.interpretResponse(vResponse, {}); - expect(vData.length).to.equal(0); - }); - - it('instruction with go = false', () => { - let vResponse = getServerResponse(getExt1ServerResponse()); - vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = false; - let vData = spec.interpretResponse(vResponse, {}); - expect(vData.length).to.equal(0); - }); - - it('incompatibility output object version (thisVer)', () => { - let vResponse = getServerResponse(getExt1ServerResponse()); - vResponse.body.seatbid[0].bid[0].ext.pcmdata.thisVer = '200'; - let vData = spec.interpretResponse(vResponse, {}); - expect(vData.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/resultsmediaBidAdapter_spec.js b/test/spec/modules/resultsmediaBidAdapter_spec.js deleted file mode 100644 index 0e2d4c0013a..00000000000 --- a/test/spec/modules/resultsmediaBidAdapter_spec.js +++ /dev/null @@ -1,574 +0,0 @@ -import {spec} from '../../../modules/resultsmediaBidAdapter.js'; -import * as utils from '../../../src/utils.js'; -import * as sinon from 'sinon'; - -var r1adapter = spec; - -describe('resultsmedia adapter tests', function () { - beforeEach(function() { - this.defaultBidderRequest = { - 'refererInfo': { - 'referer': 'Reference Page', - 'stack': [ - 'aodomain.dvl', - 'page.dvl' - ] - } - }; - }); - - describe('Verify 1.0 POST Banner Bid Request', function () { - it('buildRequests works', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - expect(bidRequest.url).to.have.string('https://bid306.rtbsrv.com/bidder/?bid=3mhdom&zoneId=9999&hbv='); - expect(bidRequest.method).to.equal('POST'); - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.site).to.not.equal(null); - expect(openrtbRequest.site.ref).to.equal('Reference Page'); - expect(openrtbRequest.device).to.not.equal(null); - expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); - expect(openrtbRequest.device.dnt).to.equal(0); - expect(openrtbRequest.imp[0].banner).to.not.equal(null); - expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); - expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(250); - expect(openrtbRequest.imp[0].ext.bidder.zoneId).to.equal(9999); - }); - - it('interpretResponse works', function() { - var bidList = { - 'body': [ - { - 'impid': 'div-gpt-ad-1438287399331-0', - 'w': 300, - 'h': 250, - 'adm': '
My Compelling Ad
', - 'price': 1, - 'crid': 'cr-cfy24' - } - ] - }; - - var bannerBids = r1adapter.interpretResponse(bidList); - - expect(bannerBids.length).to.equal(1); - const bid = bannerBids[0]; - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('cr-cfy24'); - expect(bid.currency).to.equal('USD'); - expect(bid.netRevenue).to.equal(true); - expect(bid.cpm).to.equal(1.0); - expect(bid.ttl).to.equal(350); - }); - }); - - describe('Verify POST Video Bid Request', function() { - it('buildRequests works', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'video': { - 'playerSize': [640, 480], - 'context': 'instream' - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', - 'sizes': [ - [300, 250] - ], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - expect(bidRequest.url).to.have.string('https://bid306.rtbsrv.com/bidder/?bid=3mhdom&zoneId=9999&hbv='); - expect(bidRequest.method).to.equal('POST'); - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.site).to.not.equal(null); - expect(openrtbRequest.device).to.not.equal(null); - expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); - expect(openrtbRequest.device).to.have.property('dnt'); - expect(openrtbRequest.imp[0].video).to.not.equal(null); - expect(openrtbRequest.imp[0].video.w).to.equal(640); - expect(openrtbRequest.imp[0].video.h).to.equal(480); - expect(openrtbRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); - expect(openrtbRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); - expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); - expect(openrtbRequest.imp[0].video.skip).to.equal(0); - expect(openrtbRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); - expect(openrtbRequest.imp[0].video.delivery[0]).to.equal(1); - expect(openrtbRequest.imp[0].video.api).to.eql([1, 2, 5]); - }); - - it('interpretResponse works', function() { - var bidList = { - 'body': [ - { - 'impid': 'div-gpt-ad-1438287399331-1', - 'price': 1, - 'adm': 'https://example.com/', - 'adomain': [ - 'test.com' - ], - 'cid': '467415', - 'crid': 'cr-vid', - 'w': 800, - 'h': 600 - } - ] - }; - - var videoBids = r1adapter.interpretResponse(bidList); - - expect(videoBids.length).to.equal(1); - const bid = videoBids[0]; - expect(bid.width).to.equal(800); - expect(bid.height).to.equal(600); - expect(bid.vastUrl).to.equal('https://example.com/'); - expect(bid.mediaType).to.equal('video'); - expect(bid.creativeId).to.equal('cr-vid'); - expect(bid.currency).to.equal('USD'); - expect(bid.netRevenue).to.equal(true); - expect(bid.cpm).to.equal(1.0); - expect(bid.ttl).to.equal(600); - }); - }); - - describe('misc buildRequests', function() { - it('should send GDPR Consent data to resultsmedia tag', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-3', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var consentString = 'testConsentString'; - var gdprBidderRequest = this.defaultBidderRequest; - gdprBidderRequest.gdprConsent = { - 'gdprApplies': true, - 'consentString': consentString - }; - - var bidRequest = r1adapter.buildRequests(bidRequestList, gdprBidderRequest); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.user.ext.consent).to.equal(consentString); - expect(openrtbRequest.regs.ext.gdpr).to.equal(true); - }); - - it('prefer 2.0 sizes', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); - expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(600); - }); - - it('does not return request for invalid banner size configuration', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - expect(bidRequest.method).to.be.undefined; - }); - - it('does not return request for missing banner size configuration', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': {} - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - expect(bidRequest.method).to.be.undefined; - }); - - it('reject bad sizes', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': {'sizes': [['400', '500'], ['4n0', '5g0']]} - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].banner.format.length).to.equal(1); - }); - - it('dnt is correctly set to 1', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 600]] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var dntStub = sinon.stub(utils, 'getDNT').returns(1); - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - dntStub.restore(); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.device.dnt).to.equal(1); - }); - - it('supports string video sizes', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': ['600', '300'] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].video.w).to.equal(600); - expect(openrtbRequest.imp[0].video.h).to.equal(300); - }); - - it('rejects bad video sizes', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': ['badWidth', 'badHeight'] - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].video.w).to.be.undefined; - expect(openrtbRequest.imp[0].video.h).to.be.undefined; - }); - - it('supports missing video size', function () { - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-1', - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].video.w).to.be.undefined; - expect(openrtbRequest.imp[0].video.h).to.be.undefined; - }); - - it('should return empty site data when refererInfo is missing', function() { - delete this.defaultBidderRequest.refererInfo; - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - const openrtbRequest = JSON.parse(bidRequest.data); - - expect(openrtbRequest.site.domain).to.equal(''); - expect(openrtbRequest.site.page).to.equal(''); - expect(openrtbRequest.site.ref).to.equal(''); - }); - }); - - it('should return empty site.domain and site.page when refererInfo.stack is empty', function() { - this.defaultBidderRequest.refererInfo.stack = []; - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - const openrtbRequest = JSON.parse(bidRequest.data); - - expect(openrtbRequest.site.domain).to.equal(''); - expect(openrtbRequest.site.page).to.equal(''); - expect(openrtbRequest.site.ref).to.equal('Reference Page'); - }); - - it('should secure correctly', function() { - this.defaultBidderRequest.refererInfo.stack[0] = ['https://securesite.dvl']; - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead' - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - const openrtbRequest = JSON.parse(bidRequest.data); - - expect(openrtbRequest.imp[0].secure).to.equal(1); - }); - - it('should pass schain', function() { - var schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [{ - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 1 - }] - }; - var bidRequestList = [ - { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]], - 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'bidderRequestId': '418b37f85e772c', - 'auctionId': '18fd8b8b0bd757', - 'bidRequestsCount': 1, - 'bidId': '51ef8751f9aead', - 'schain': schain - } - ]; - - var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - const openrtbRequest = JSON.parse(bidRequest.data); - - expect(openrtbRequest.source.ext.schain).to.deep.equal(schain); - }); - - describe('misc interpretResponse', function () { - it('No bid response', function() { - var noBidResponse = r1adapter.interpretResponse({ - 'body': '' - }); - expect(noBidResponse.length).to.equal(0); - }); - }); - - describe('isBidRequestValid', function () { - var bid = { - 'bidder': 'resultsmedia', - 'params': { - 'zoneId': 9999 - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250]] - } - }, - 'adUnitCode': 'bannerDiv' - }; - - it('should return true when required params found', function () { - expect(r1adapter.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when placementId missing', function () { - delete bid.params.zoneId; - expect(r1adapter.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('getUserSyncs', function () { - it('returns an empty string', function () { - expect(r1adapter.getUserSyncs()).to.deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/revcontentBidAdapter_spec.js b/test/spec/modules/revcontentBidAdapter_spec.js deleted file mode 100644 index 0a0263837c6..00000000000 --- a/test/spec/modules/revcontentBidAdapter_spec.js +++ /dev/null @@ -1,351 +0,0 @@ -// jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {spec} from 'modules/revcontentBidAdapter.js'; -import { NATIVE } from 'src/mediaTypes.js'; -import { config } from 'src/config.js'; -import * as utils from 'src/utils.js'; - -describe('revcontent adapter', function () { - let serverResponse, bidRequest, bidResponses; - let bids = []; - - describe('isBidRequestValid', function () { - let bid = { - bidder: 'revcontent', - nativeParams: {}, - params: { - size: {width: 300, height: 250}, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - domain: 'test.com', - endpoint: 'trends-s0.revcontent.com' - } - }; - - it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); - }); - - it('should return false when required params are missing', function () { - bid.params.apiKey = undefined; - assert.isFalse(spec.isBidRequestValid(bid)); - }); - }); - - describe('buildRequests', function () { - it('should send request with correct structure', function () { - let validBidRequests = [{ - bidder: 'revcontent', - nativeParams: {}, - params: { - size: {width: 300, height: 250}, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - widgetId: 33861, - endpoint: 'trends-s0.revcontent.com' - } - }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); - request = request[0]; - assert.equal(request.method, 'POST'); - assert.equal(request.url, 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673&widgetId=33861'); - assert.deepEqual(request.options, {contentType: 'application/json'}); - assert.ok(request.data); - }); - - it('should have default request structure', function () { - let keys = 'method,options,url,data,bid'.split(','); - let validBidRequests = [{ - bidder: 'revcontent', - nativeParams: {}, - params: { - size: {width: 300, height: 250}, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - domain: 'test.com', - endpoint: 'trends-s0.revcontent.com' - } - }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); - - request = request[0]; - let data = Object.keys(request); - - assert.deepEqual(keys, data); - }); - - it('should send info about device and unique bidfloor', function () { - let validBidRequests = [{ - bidder: 'revcontent', - nativeParams: {}, - params: { - size: {width: 300, height: 250}, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - domain: 'test.com', - endpoint: 'trends-s0.revcontent.com', - bidfloor: 0.05 - } - }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); - request = JSON.parse(request[0].data); - assert.equal(request.imp[0].bidfloor, 0.05); - assert.equal(request.device.ua, navigator.userAgent); - }); - - it('should send info about the site and default bidfloor', function () { - let validBidRequests = [{ - bidder: 'revcontent', - nativeParams: { - image: { - required: false - }, - 'title': { - required: false, - len: 140 - }, - clickUrl: { - required: false - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - } - }, - params: { - size: {width: 300, height: 250}, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - domain: 'test.com', - endpoint: 'trends-s0.revcontent.com' - } - }]; - let refererInfo = {referer: 'page'}; - let request = spec.buildRequests(validBidRequests, {refererInfo}); - - request = JSON.parse(request[0].data); - assert.equal(request.imp[0].bidfloor, 0.1); - assert.deepEqual(request.site, { - domain: 'test.com', - page: 'page', - cat: ['IAB17'], - publisher: {id: 673, domain: 'test.com'} - }); - }); - }); - - describe('interpretResponse', function () { - it('should return if no body in response', function () { - let serverResponse = {}; - let bidRequest = {}; - - assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); - }); - - const serverResponse = { - body: { - id: '5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58', - seatbid: [ - { - bid: [ - { - id: '6bbe3eed-f443-4e2b-a8da-57fd6327b37d', - impid: '1', - price: 0.1, - adid: '4162547', - nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', - adm: '{"ver":"1.1","assets":[{"id":3,"required":1,"img":{"url":"//img.revcontent.com/?url=https://revcontent-p0.s3.amazonaws.com/content/images/15761052960288727821.jpg&static=true"}},{"id":0,"required":1,"title":{"text":"Do You Eat Any of These Craving-trigger Foods?"}},{"id":5,"required":1,"data":{"value":""}}],"link":{"url":"https://trends-s0.revcontent.com/click.php?d=A7EVbNYBVyonty19Ak08zCr9J54qg%2Bmduq6p0Zyn5%2F%2Bapm4deUo9VAXmOGEIbUBf6i7m3%2F%2FWJm%2FzTha8SJ%2Br9MZL9jhhUxDeiKb6aRY1biLrvr6tFUd1phvtKqVmPd76l9VBLFMxS1brSzKjRCJlIGmyGJg7ueFvxpE9X%2BpHmdbE2uqUdRC49ENO3XZyHCCKMAZ8XD29fasX9Kli9mKpZTqw8vayFlXbVYSUwB8wfSwCt1sIUrt0aICYc0jcyWU3785GTS1xXzQj%2FIVszFYYrdTWd%2BDijjNZtFny0OomPHp8lRy5VcQVCuLpw0Fks4myvsE38XcNvs4wO3tWTNrI%2BMqcW1%2BD2OnMSq5nN5FCbmi2ly%2F1LbN9fibaFvW%2FQbzQhN9ZsAwmhm409UTtdmSA6hd96vDxDWLeUJhVO3UQyI0yq2TtVnB9tEICD8mZNWwYehOab%2BQ1EWmTerF6ZCDx8RyZus1UrsDfRwvTCyUjCmkZhmeo4QVJkpPy6QobCsngSaxkkKhH%2Fb7coZyBXXEt3ORoYBLUbfRO6nR8GdIt8413vrYr4gTAroh46VcWK0ls0gFNe2u3%2FqP%2By1yLKbzDVaR%2Fa02G%2Biiqbw86sCYfsy7qK9atyjNTm8RkH6JLESUzxc6IEazu4iwHKGnu5phTacmseXCi8y9Y5AdBZn8VnLP%2F2a%2FyAqq93xEH%2BIrkAdhGRY1tY39rBYAtvH%2FVyNFZcong%2FutUMYbp0WhDNyfl6iWxmpE28Cx9KDcqXss0NIwQm0AWeu8ogJCIG3faAkm5PdFsUdf2X9h3HuFDbnbvnXW27ml6z9GykEzv%2F8aSZlMZ"}}' - } - ] - } - ], - bidid: '7f729368-edb2-427a-bde7-a55b3bf8837c' - }, - headers: {} - }; - - const bidRequest = { - method: 'POST', - options: { - contentType: 'application/json' - }, - url: 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673', - data: '{"id":"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58","imp":[{"id":1,"bidderRequestId":"14e4dab7b5396e8","auctionId":"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58","transactionId":"69e69abf-a3ea-484d-a81c-d48dd0d5eaa3","native":{"request":{"ver":"1.1","context":2,"contextsubtype":21,"plcmttype":4,"plcmtcnt":4,"assets":[{"required":0,"id":3,"img":{"type":3}},{"required":0,"id":0,"title":{"len":140}},{"required":0,"id":5,"data":{"type":1}}]},"ver":"1.1","battr":[1,3,8,11,17]},"instl":0,"bidfloor":0.1,"secure":"1"}],"site":{"domain":"test.com","page":"https://feudfun.com/test22/revcontent_example.php","cat":["IAB17"],"publisher":{"id":673,"domain":"test.com"}},"device":{"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:71.0) Gecko/20100101 Firefox/71.0","language":"en"},"user":{"id":1},"at":2,"bcat":["IAB24","IAB25","IAB25-1","IAB25-2","IAB25-3","IAB25-4","IAB25-5","IAB25-6","IAB25-7","IAB26","IAB26-1","IAB26-2","IAB26-3","IAB26-4"]}', - bid: [ - { - bidder: 'revcontent', - params: { - size: { - width: 300, - height: 250 - }, - apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', - userId: 673, - domain: 'test.com', - endpoint: 'trends-s0.revcontent.com' - }, - crumbs: { - pubcid: '7a0b4adc-c109-49f0-aadc-4a4b62ebe269' - }, - nativeParams: { - image: { - required: false - }, - 'title': { - required: false, - len: 140 - }, - clickUrl: { - required: false - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - } - }, - mediaTypes: { - native: { - image: { - required: false - }, - title: { - required: false, - len: 140 - }, - clickUrl: { - required: false - }, - sponsoredBy: { - id: 5, - name: 'data', - type: 1 - } - } - }, - adUnitCode: '/19968336/header-bid-tag-1', - transactionId: '69e69abf-a3ea-484d-a81c-d48dd0d5eaa3', - sizes: [], - bidId: '294a7f446202848', - bidderRequestId: '14e4dab7b5396e8', - auctionId: '5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - } - ] - }; - - it('should set correct native params', function () { - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); - assert.equal(result.mediaType, 'native'); - assert.equal(result.requestId, '294a7f446202848'); - assert.equal(result.cpm, '0.1'); - assert.equal(result.creativeId, '4162547'); - }); - - it('validate template 728x90', function () { - bidRequest.bid[0].params.size.width = 728; - bidRequest.bid[0].params.size.height = 90; - - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); - assert.equal(result.mediaType, 'native'); - assert.equal(result.requestId, '294a7f446202848'); - assert.equal(result.cpm, '0.1'); - assert.equal(result.creativeId, '4162547'); - }); - - it('validate template 300x600', function () { - bidRequest.bid[0].params.size.width = 300; - bidRequest.bid[0].params.size.height = 600; - - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); - assert.equal(result.mediaType, 'native'); - assert.equal(result.requestId, '294a7f446202848'); - assert.equal(result.cpm, '0.1'); - assert.equal(result.creativeId, '4162547'); - }); - - it('validate template custom template', function () { - bidRequest.bid[0].params.template = '

{title}

SEE MORE
'; - - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); - assert.equal(result.mediaType, 'native'); - assert.equal(result.requestId, '294a7f446202848'); - assert.equal(result.cpm, '0.1'); - assert.equal(result.creativeId, '4162547'); - }); - - it('validate template custom invalid template', function () { - bidRequest.bid[0].params.size.width = 100; - bidRequest.bid[0].params.size.height = 200; - - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.equal(result.bidder, 'revcontent'); - assert.equal(result.bidderCode, 'revcontent'); - assert.equal(result.mediaType, 'native'); - assert.equal(result.requestId, '294a7f446202848'); - assert.equal(result.cpm, '0.1'); - assert.equal(result.creativeId, '4162547'); - }); - - it('should return empty when there is no bids in response', function () { - const serverResponse = { - body: { - id: null, - bidid: null, - seatbid: [{bid: []}], - cur: 'USD' - } - }; - let bidRequest = { - data: {}, - bids: [{bidId: 'bidId1'}] - }; - const result = spec.interpretResponse(serverResponse, bidRequest)[0]; - assert.ok(!result); - }); - }); - - describe('onBidWon', function () { - it('default bid won', function () { - const bid = { - nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', - cpm: '0.1' - }; - const result = spec.onBidWon(bid); - assert.ok(result); - }); - }); - - describe('onBidWon', function() { - const bid = { - nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', - cpm: '0.1' - }; - - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function() { - utils.triggerPixel.restore(); - }); - - it('make sure only 1 ajax call is happening', function() { - spec.onBidWon(bid); - expect(utils.triggerPixel.calledOnce).to.equal(true); - }); - }); -}); diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index 93735c019e1..e0e58fc89cd 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -444,7 +444,7 @@ describe('rhythmone adapter tests', function () { expect(openrtbRequest.device.dnt).to.equal(1); }); - it('sets floor', function () { + it('sets floor to zero', function () { var bidRequestList = [ { 'bidder': 'rhythmone', @@ -469,7 +469,7 @@ describe('rhythmone adapter tests', function () { var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.imp[0].bidfloor).to.equal(100.0); + expect(openrtbRequest.imp[0].bidfloor).to.equal(0); }); it('supports string video sizes', function () { diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js deleted file mode 100644 index b3257cbda9d..00000000000 --- a/test/spec/modules/riseBidAdapter_spec.js +++ /dev/null @@ -1,381 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/riseBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; -import { VIDEO } from '../../../src/mediaTypes.js'; - -const ENDPOINT = 'https://hb.yellowblue.io/hb'; -const TEST_ENDPOINT = 'https://hb.yellowblue.io/hb-test'; -const TTL = 360; - -describe('riseAdapter', 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 () { - const bid = { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [['640', '480']], - 'params': { - 'org': 'jdye8weeyirk00000001' - } - }; - - it('should return true when required params are passed', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not found', function () { - const newBid = Object.assign({}, bid); - delete newBid.params; - newBid.params = { - 'org': null - }; - expect(spec.isBidRequestValid(newBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'org': 'jdye8weeyirk00000001' - }, - 'bidId': '299ffc8cca0b87', - 'bidderRequestId': '1144f487e563f9', - 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', - } - ]; - - const testModeBidRequests = [ - { - 'bidder': spec.code, - 'adUnitCode': 'adunit-code', - 'sizes': [[640, 480]], - 'params': { - 'org': 'jdye8weeyirk00000001', - 'testMode': true - }, - 'bidId': '299ffc8cca0b87', - 'bidderRequestId': '1144f487e563f9', - 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', - } - ]; - - const bidderRequest = { - bidderCode: 'rise', - } - - const customSessionId = '12345678'; - - it('sends bid request to ENDPOINT via GET', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('GET'); - } - }); - - it('sends the is_wrapper query param', function () { - bidRequests[0].params.isWrapper = true; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.is_wrapper).to.equal(true); - } - }); - - it('sends the custom session id as a query param', function () { - bidRequests[0].params.sessionId = customSessionId; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.session_id).to.equal(customSessionId); - } - }); - - it('sends bid request to test ENDPOINT via GET', function () { - const requests = spec.buildRequests(testModeBidRequests, bidderRequest); - for (const request of requests) { - expect(request.url).to.equal(TEST_ENDPOINT); - expect(request.method).to.equal('GET'); - } - }); - - it('should send the correct bid Id', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data.bid_id).to.equal('299ffc8cca0b87'); - } - }); - - it('should send the correct width and height', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('width', 640); - expect(request.data).to.have.property('height', 480); - } - }); - - it('should respect syncEnabled option', function() { - config.setConfig({ - userSync: { - syncEnabled: false, - filterSettings: { - all: { - bidders: '*', - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('cs_method'); - } - }); - - it('should respect "iframe" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - iframe: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'iframe'); - } - }); - - it('should respect "all" filter settings', function () { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - all: { - bidders: [spec.code], - filter: 'include' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'iframe'); - } - }); - - it('should send the pixel user sync param if userSync is enabled and no "iframe" or "all" configs are present', function () { - config.setConfig({ - userSync: { - syncEnabled: true - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('cs_method', 'pixel'); - } - }); - - it('should respect total exclusion', function() { - config.setConfig({ - userSync: { - syncEnabled: true, - filterSettings: { - image: { - bidders: [spec.code], - filter: 'exclude' - }, - iframe: { - bidders: [spec.code], - filter: 'exclude' - } - } - } - }); - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('cs_method'); - } - }); - - it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithUSP); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('us_privacy', '1YNN'); - } - }); - - it('should have an empty us_privacy param if usPrivacy is missing in the bidRequest', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('us_privacy'); - } - }); - - it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.not.have.property('gdpr'); - expect(request.data).to.not.have.property('gdpr_consent'); - } - }); - - it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); - const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('gdpr', true); - expect(request.data).to.have.property('gdpr_consent', 'test-consent-string'); - } - }); - - it('should have schain param if it is available in the bidRequest', () => { - const schain = { - ver: '1.0', - complete: 1, - nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], - }; - bidRequests[0].schain = schain; - const requests = spec.buildRequests(bidRequests, bidderRequest); - for (const request of requests) { - expect(request.data).to.be.an('object'); - expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,,,,'); - } - }); - }); - - describe('interpretResponse', function () { - const response = { - cpm: 12.5, - vastXml: '', - width: 640, - height: 480, - requestId: '21e12606d47ba7', - netRevenue: true, - currency: 'USD' - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: '21e12606d47ba7', - cpm: 12.5, - width: 640, - height: 480, - creativeId: '21e12606d47ba7', - currency: 'USD', - netRevenue: true, - ttl: TTL, - vastXml: '', - mediaType: VIDEO - } - ]; - const result = spec.interpretResponse({ body: response }); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - }) - - describe('getUserSyncs', function() { - const imageSyncResponse = { - body: { - userSyncPixels: [ - 'https://image-sync-url.test/1', - 'https://image-sync-url.test/2', - 'https://image-sync-url.test/3' - ] - } - }; - - const iframeSyncResponse = { - body: { - userSyncURL: 'https://iframe-sync-url.test' - } - }; - - it('should register all img urls from the response', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should register the iframe url from the response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, [iframeSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - } - ]); - }); - - it('should register both image and iframe urls from the responses', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [iframeSyncResponse, imageSyncResponse]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://iframe-sync-url.test' - }, - { - type: 'image', - url: 'https://image-sync-url.test/1' - }, - { - type: 'image', - url: 'https://image-sync-url.test/2' - }, - { - type: 'image', - url: 'https://image-sync-url.test/3' - } - ]); - }); - - it('should handle an empty response', function() { - const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); - }); - - it('should handle when user syncs are disabled', function() { - const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imageSyncResponse]); - expect(syncs).to.deep.equal([]); - }); - }) -}); diff --git a/test/spec/modules/rtbdemandBidAdapter_spec.js b/test/spec/modules/rtbdemandBidAdapter_spec.js deleted file mode 100644 index be9872ec01b..00000000000 --- a/test/spec/modules/rtbdemandBidAdapter_spec.js +++ /dev/null @@ -1,174 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/rtbdemandBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('rtbdemandAdapter', 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': 'rtbdemand', - 'params': { - 'zoneid': '37', - 'floor': '0.05', - 'server': 'bidding.rtbdemand.com', - }, - '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 true when required params found', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'zoneid': '37', - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'zoneid': 0, - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidderRequest = { - bidderCode: 'rtbdemand', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - bidderRequestId: '178e34bad3658f', - bids: [ - { - bidder: 'rtbdemand', - params: { - zoneid: '37', - floor: '0.05', - server: 'bidding.rtbdemand.com', - }, - placementCode: '/19968336/header-bid-tag-0', - sizes: [[300, 250], [320, 50]], - bidId: '2ffb201a808da7', - bidderRequestId: '178e34bad3658f', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' - }, - { - bidder: 'rtbdemand', - params: { - zoneid: '37', - floor: '0.05', - server: 'bidding.rtbdemand.com', - }, - placementCode: '/19968336/header-bid-tag-0', - sizes: [[728, 90], [320, 50]], - bidId: '2ffb201a808da7', - bidderRequestId: '178e34bad3658f', - auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', - transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' - } - ], - start: 1472239426002, - auctionStart: 1472239426000, - timeout: 5000 - }; - - it('should add source and verison to the tag', function () { - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const payload = request.data; - expect(payload.from).to.exist; - expect(payload.v).to.exist; - expect(payload.request_id).to.exist; - expect(payload.imp_id).to.exist; - expect(payload.aff).to.exist; - expect(payload.bid_floor).to.exist; - expect(payload.charset).to.exist; - expect(payload.site_domain).to.exist; - expect(payload.site_page).to.exist; - expect(payload.subid).to.exist; - expect(payload.flashver).to.exist; - expect(payload.tmax).to.exist; - }); - - it('sends bid request to ENDPOINT via GET', function () { - const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.url).to.equal('https://bidding.rtbdemand.com/hb'); - expect(request.method).to.equal('GET'); - }); - }) - - describe('interpretResponse', function () { - let response = { - 'id': '543210', - 'seatbid': [ { - 'bid': [ { - 'id': '1111111', - 'impid': 'bidId-123456-1', - 'w': 728, - 'h': 90, - 'price': 0.09, - 'adm': '', - } ], - } ] - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - requestId: 'bidId-123456-1', - creativeId: 'bidId-123456-1', - cpm: 0.09, - width: 728, - height: 90, - ad: '', - netRevenue: true, - currency: 'USD', - ttl: 360, - } - ]; - - let result = spec.interpretResponse({ body: response }); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function () { - let response = { - 'id': '543210', - 'seatbid': [ ] - }; - - let result = spec.interpretResponse({ body: response }); - expect(result.length).to.equal(0); - }); - }); - - describe('user sync', function () { - const syncUrl = 'https://bidding.rtbdemand.com/delivery/matches.php?type=iframe'; - - it('should register the sync iframe', function () { - expect(spec.getUserSyncs({})).to.be.undefined; - expect(spec.getUserSyncs({iframeEnabled: false})).to.be.undefined; - const options = spec.getUserSyncs({iframeEnabled: true}); - expect(options).to.not.be.undefined; - expect(options).to.have.lengthOf(1); - expect(options[0].type).to.equal('iframe'); - expect(options[0].url).to.equal(syncUrl); - }); - }); -}); diff --git a/test/spec/modules/rtbsapeBidAdapter_spec.js b/test/spec/modules/rtbsapeBidAdapter_spec.js deleted file mode 100644 index 4c3814385b3..00000000000 --- a/test/spec/modules/rtbsapeBidAdapter_spec.js +++ /dev/null @@ -1,166 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/rtbsapeBidAdapter.js'; -import * as utils from 'src/utils.js'; -import {executeRenderer, Renderer} from 'src/Renderer.js'; - -describe('rtbsapeBidAdapterTests', function () { - describe('isBidRequestValid', function () { - it('valid', function () { - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {placeId: 4321}})).to.equal(true); - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {video: true}, params: {placeId: 4321}})).to.equal(true); - }); - - it('invalid', function () { - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {}})).to.equal(false); - expect(spec.isBidRequestValid({bidder: 'rtbsape', params: {placeId: 4321}})).to.equal(false); - }); - }); - - it('buildRequests', function () { - let bidRequestData = [{ - bidId: 'bid1234', - bidder: 'rtbsape', - params: {placeId: 4321}, - sizes: [[240, 400]] - }]; - let bidderRequest = { - auctionId: '2e208334-cafe-4c2c-b06b-f055ff876852', - bidderRequestId: '1392d0aa613366', - refererInfo: {} - }; - let request = spec.buildRequests(bidRequestData, bidderRequest); - expect(request.data.auctionId).to.equal('2e208334-cafe-4c2c-b06b-f055ff876852'); - expect(request.data.requestId).to.equal('1392d0aa613366'); - expect(request.data.bids[0].bidId).to.equal('bid1234'); - expect(request.data.timezone).to.not.equal(undefined); - }); - - describe('interpretResponse', function () { - it('banner', function () { - let serverResponse = { - body: { - bids: [{ - requestId: 'bid1234', - cpm: 2.21, - currency: 'RUB', - width: 240, - height: 400, - netRevenue: true, - ad: 'Ad html' - }] - } - }; - let bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - expect(bid.cpm).to.equal(2.21); - expect(bid.currency).to.equal('RUB'); - expect(bid.width).to.equal(240); - expect(bid.height).to.equal(400); - expect(bid.netRevenue).to.equal(true); - expect(bid.requestId).to.equal('bid1234'); - expect(bid.ad).to.equal('Ad html'); - }); - - describe('video (outstream)', function () { - let bid; - - before(() => { - let serverResponse = { - body: { - bids: [{ - requestId: 'bid1234', - adUnitCode: 'ad-bid1234', - cpm: 3.32, - currency: 'RUB', - width: 600, - height: 340, - netRevenue: true, - vastUrl: 'https://cdn-rtb.sape.ru/vast/4321.xml', - meta: { - mediaType: 'video' - } - }] - } - }; - let serverRequest = { - data: { - bids: [{ - bidId: 'bid1234', - adUnitCode: 'ad-bid1234', - mediaTypes: { - video: { - context: 'outstream' - } - }, - params: { - placeId: 4321, - video: { - playerMuted: false - } - } - }] - } - }; - let bids = spec.interpretResponse(serverResponse, serverRequest); - expect(bids).to.have.lengthOf(1); - bid = bids[0]; - }); - - it('should add renderer', () => { - expect(bid).to.have.own.property('renderer'); - expect(bid.renderer).to.be.instanceof(Renderer); - expect(bid.renderer.url).to.equal('https://cdn-rtb.sape.ru/js/player.js'); - expect(bid.playerMuted).to.equal(false); - }); - - it('should create player instance', () => { - let spy = false; - - window.sapeRtbPlayerHandler = function (id, w, h, m) { - const player = {addSlot: () => [id, w, h, m]} - expect(spy).to.equal(false); - spy = sinon.spy(player, 'addSlot'); - return player; - }; - - executeRenderer(bid.renderer, bid); - expect(spy).to.not.equal(false); - expect(spy.called).to.be.true; - - const spyCall = spy.getCall(0); - expect(spyCall.args[0].url).to.be.equal('https://cdn-rtb.sape.ru/vast/4321.xml'); - expect(spyCall.returnValue[0]).to.be.equal('ad-bid1234'); - expect(spyCall.returnValue[1]).to.be.equal(600); - expect(spyCall.returnValue[2]).to.be.equal(340); - expect(spyCall.returnValue[3]).to.be.equal(false); - }); - }); - }); - - it('getUserSyncs', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}); - expect(syncs).to.be.an('array').that.to.have.lengthOf(1); - expect(syncs[0]).to.deep.equal({type: 'iframe', url: 'https://www.acint.net/mc/?dp=141'}); - }); - - describe('onBidWon', function () { - beforeEach(function () { - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function () { - utils.triggerPixel.restore(); - }); - - it('called once', function () { - spec.onBidWon({cpm: '2.21', nurl: 'https://ssp-rtb.sape.ru/track?event=win'}); - expect(utils.triggerPixel.calledOnce).to.equal(true); - }); - - it('called false', function () { - spec.onBidWon({cpm: '2.21'}); - expect(utils.triggerPixel.called).to.equal(false); - }); - }); -}); diff --git a/test/spec/modules/rtbsolutionsBidAdapter_spec.js b/test/spec/modules/rtbsolutionsBidAdapter_spec.js deleted file mode 100644 index c47b086fe50..00000000000 --- a/test/spec/modules/rtbsolutionsBidAdapter_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/rtbsolutionsBidAdapter.js'; - -describe('rtbsolutionsBidAdapterTests', function () { - it('validate_pub_params_1', function () { - expect(spec.isBidRequestValid({ - bidder: 'rtbsolutions', - params: { - blockId: 777 - } - })).to.equal(true); - }); - it('validate_pub_params_2', function () { - expect(spec.isBidRequestValid({ - bidder: 'rtbsolutions', - params: { - s1: 'test' - } - })).to.equal(false); - }); - it('validate_generated_params', function () { - let bidderRequest = { - bids: [], - refererInfo: { - referer: '' - } - }; - bidderRequest.bids.push({ - bidId: 'bid1234', - bidder: 'rtbsolutions', - params: {blockId: 777}, - sizes: [[240, 400]] - }); - let request = spec.buildRequests(true, bidderRequest); - let req_data = request.data[0]; - expect(req_data.bid_id).to.equal('bid1234'); - }); - it('validate_response_params', function () { - let serverResponse = { - body: [{ - ad: 'Ad html', - bid_id: 'bid1234', - cpm: 1, - creative_id: 1, - height: 480, - nurl: 'http://test.test', - width: 640, - currency: 'USD', - }] - }; - let bids = spec.interpretResponse(serverResponse); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - expect(bid.cpm).to.equal(1); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(640); - expect(bid.height).to.equal(480); - expect(bid.netRevenue).to.equal(true); - expect(bid.requestId).to.equal('bid1234'); - expect(bid.ad).to.equal('Ad html'); - }); -}); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index a290a9bdb0a..4c0b27ca8be 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -229,7 +229,6 @@ describe('the rubicon adapter', function () { bid.userId = { lipb: {lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB']}, idl_env: '1111-2222-3333-4444', - sharedid: {id: '1111', third: '2222'}, tdid: '3000', pubcid: '4000', pubProvidedId: [{ @@ -511,6 +510,38 @@ describe('the rubicon adapter', function () { expect(data['p_pos']).to.equal(undefined); }); + it('should not send p_pos to AE if not mediaTypes.banner.pos is invalid', function () { + var bidRequest = utils.deepClone(bidderRequest); + bidRequest.bids[0].mediaTypes = { + banner: { + pos: 5 + } + }; + delete bidRequest.bids[0].params.position; + + let [request] = spec.buildRequests(bidRequest.bids, bidRequest); + let data = parseQuery(request.data); + + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal(undefined); + }); + + it('should send p_pos to AE if mediaTypes.banner.pos is valid', function () { + var bidRequest = utils.deepClone(bidderRequest); + bidRequest.bids[0].mediaTypes = { + banner: { + pos: 1 + } + }; + delete bidRequest.bids[0].params.position; + + let [request] = spec.buildRequests(bidRequest.bids, bidRequest); + let data = parseQuery(request.data); + + expect(data['site_id']).to.equal('70608'); + expect(data['p_pos']).to.equal('atf'); + }); + it('should not send p_pos to AE if not params.position is invalid', function () { var badposRequest = utils.deepClone(bidderRequest); badposRequest.bids[0].params.position = 'bad'; @@ -1226,23 +1257,6 @@ describe('the rubicon adapter', function () { }); }); - describe('SharedID support', function () { - it('should send sharedid when userIdAsEids contains sharedId', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userId = { - sharedid: { - id: '1111', - third: '2222' - } - }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); - - expect(data['eid_sharedid.org']).to.equal('1111^1^2222'); - }); - }); - describe('pubProvidedId support', function () { it('should send pubProvidedId when userIdAsEids contains pubProvidedId ids', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); @@ -1312,10 +1326,7 @@ describe('the rubicon adapter', function () { config.setConfig({user: {id: '123'}}); const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { - sharedid: { - id: '1111', - third: '2222' - } + pubcid: '1111' }; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1537,29 +1548,24 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); expect(post.user.ext.eids[1].uids[0].atype).to.equal(3); - // SharedId should exist - expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); - expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); - expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); // UnifiedId should exist - expect(post.user.ext.eids[3].source).to.equal('adserver.org'); - expect(post.user.ext.eids[3].uids[0].atype).to.equal(1); - expect(post.user.ext.eids[3].uids[0].id).to.equal('3000'); + expect(post.user.ext.eids[2].source).to.equal('adserver.org'); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[2].uids[0].id).to.equal('3000'); // PubCommonId should exist - expect(post.user.ext.eids[4].source).to.equal('pubcid.org'); - expect(post.user.ext.eids[4].uids[0].atype).to.equal(1); - expect(post.user.ext.eids[4].uids[0].id).to.equal('4000'); + expect(post.user.ext.eids[3].source).to.equal('pubcid.org'); + expect(post.user.ext.eids[3].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[3].uids[0].id).to.equal('4000'); // example should exist - expect(post.user.ext.eids[5].source).to.equal('example.com'); - expect(post.user.ext.eids[5].uids[0].id).to.equal('333333'); + expect(post.user.ext.eids[4].source).to.equal('example.com'); + expect(post.user.ext.eids[4].uids[0].id).to.equal('333333'); // id-partner.com - expect(post.user.ext.eids[6].source).to.equal('id-partner.com'); - expect(post.user.ext.eids[6].uids[0].id).to.equal('4444444'); + expect(post.user.ext.eids[5].source).to.equal('id-partner.com'); + expect(post.user.ext.eids[5].uids[0].id).to.equal('4444444'); // CriteoId should exist - expect(post.user.ext.eids[7].source).to.equal('criteo.com'); - expect(post.user.ext.eids[7].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[7].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[6].source).to.equal('criteo.com'); + expect(post.user.ext.eids[6].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[6].uids[0].atype).to.equal(1); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); diff --git a/test/spec/modules/saambaaBidAdapter_spec.js b/test/spec/modules/saambaaBidAdapter_spec.js deleted file mode 100755 index 80a85ae895d..00000000000 --- a/test/spec/modules/saambaaBidAdapter_spec.js +++ /dev/null @@ -1,139 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/saambaaBidAdapter.js'; -import { BANNER, VIDEO } from 'src/mediaTypes.js'; - -describe('saambaaBidAdapter', function () { - let bidRequests; - let bidRequestsVid; - - beforeEach(function () { - bidRequests = [{'bidder': 'saambaa', 'params': {'pubid': '121ab139faf7ac67428a23f1d0a9a71b', 'floor': 0.5, 'placement': 1234, size: '320x250'}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - - bidRequestsVid = [{'bidder': 'saambaa', 'params': {'pubid': '121ab139faf7ac67428a23f1d0a9a71b', 'floor': 1.0, 'placement': 1234, size: '320x480', 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - }); - - describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed for banner', function () { - const bidRequest = bidRequests[0]; - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - - it('should return true when the required params are passed for video', function () { - const bidRequests = bidRequestsVid[0]; - expect(spec.isBidRequestValid(bidRequests)).to.equal(true); - }); - - it('should return false when no pub id params are passed', function () { - const bidRequest = bidRequests[0]; - bidRequest.params.pubid = ''; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); - - it('should return false when no placement params are passed', function () { - const bidRequest = bidRequests[0]; - bidRequest.params.placement = ''; - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); - }); - - it('should return false when a bid request is not passed', function () { - expect(spec.isBidRequestValid()).to.equal(false); - expect(spec.isBidRequestValid({})).to.equal(false); - }); - }); - - describe('spec.buildRequests', function () { - it('should create a POST request for each bid', function () { - const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].method).to.equal('POST'); - }); - - it('should create a POST request for each bid in video request', function () { - const bidRequest = bidRequestsVid[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].method).to.equal('POST'); - }); - - it('should have domain in request', function () { - const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ]); - expect(requests[0].data.site.domain).length !== 0; - }); - }); - - describe('spec.interpretResponse', function () { - describe('for banner bids', function () { - it('should return no bids if the response is not valid', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); - - if (typeof bidResponse !== 'undefined') { - expect(bidResponse.length).to.equal(0); - } else { - expect(true).to.equal(true); - } - }); - - it('should return no bids if the response is empty', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); - if (typeof bidResponse !== 'undefined') { - expect(bidResponse.length).to.equal(0); - } else { expect(true).to.equal(true); } - }); - - it('should return valid video bid responses', function () { - let _mediaTypes = VIDEO; - const saambaabidreqVid = {'bidRequest': {'mediaTypes': {'video': {'w': 320, 'h': 480}}}}; - const serverResponseVid = {'cur': 'USD', 'id': '25c6ab92aa0e81', 'seatbid': [{'seat': '3', 'bid': [{'crid': '1855', 'h': 480, 'protocol': 2, 'nurl': 'http://nep.advangelists.com/xp/evt?pp=1MO1wiaMhhq7wLRzZZwwwPkJxxKpYEnM5k5MH4qSGm1HR8rp3Nl7vDocvzZzSAvE4pnREL9mQ1kf5PDjk6E8em6DOk7vVrYUH1TYQyqCucd58PFpJNN7h30RXKHHFg3XaLuQ3PKfMuI1qZATBJ6WHcu875y0hqRdiewn0J4JsCYF53M27uwmcV0HnQxARQZZ72mPqrW95U6wgkZljziwKrICM3aBV07TU6YK5R5AyzJRuD6mtrQ2xtHlQ3jXVYKE5bvWFiUQd90t0jOGhPtYBNoOfP7uQ4ZZj4pyucxbr96orHe9PSOn9UpCSWArdx7s8lOfDpwOvbMuyGxynbStDWm38sDgd4bMHnIt762m5VMDNJfiUyX0vWzp05OsufJDVEaWhAM62i40lQZo7mWP4ipoOWLkmlaAzFIMsTcNaHAHiKKqGEOZLkCEhFNM0SLcvgN2HFRULOOIZvusq7TydOKxuXgCS91dLUDxDDDFUK83BFKlMkTxnCzkLbIR1bd9GKcr1TRryOrulyvRWAKAIhEsUzsc5QWFUhmI2dZ1eqnBQJ0c89TaPcnoaP2WipF68UgyiOstf2CBy0M34858tC5PmuQwQYwXscg6zyqDwR0i9MzGH4FkTyU5yeOlPcsA0ht6UcoCdFpHpumDrLUwAaxwGk1Nj8S6YlYYT5wNuTifDGbg22QKXzZBkUARiyVvgPn9nRtXnrd7WmiMYq596rya9RQj7LC0auQW8bHVQLEe49shsZDnAwZTWr4QuYKqgRGZcXteG7RVJe0ryBZezOq11ha9C0Lv0siNVBahOXE35Wzoq4c4BDaGpqvhaKN7pjeWLGlQR04ufWekwxiMWAvjmfgAfexBJ7HfbYNZpq__', 'adid': '61_1855', 'adomain': ['chevrolet.com'], 'price': 2, 'w': 320, 'iurl': 'https://daf37cpxaja7f.cloudfront.net/c61/creative_url_14922301369663_1.png', 'cat': ['IAB2'], 'id': '7f570b40-aca1-4806-8ea8-818ea679c82b_0', 'attr': [], 'impid': '0', 'cid': '61'}]}], 'bidid': '7f570b40-aca1-4806-8ea8-818ea679c82b'} - const bidResponseVid = spec.interpretResponse({ body: serverResponseVid }, saambaabidreqVid); - delete bidResponseVid['vastUrl']; - delete bidResponseVid['ad']; - expect(bidResponseVid).to.deep.equal({ - requestId: bidRequestsVid[0].bidId, - bidderCode: 'saambaa', - creativeId: serverResponseVid.seatbid[0].bid[0].crid, - cpm: serverResponseVid.seatbid[0].bid[0].price, - width: serverResponseVid.seatbid[0].bid[0].w, - height: serverResponseVid.seatbid[0].bid[0].h, - mediaType: 'video', - meta: { advertiserDomains: serverResponseVid.seatbid[0].bid[0].adomain }, - currency: 'USD', - netRevenue: true, - ttl: 60 - }); - }); - - it('should return valid banner bid responses', function () { - const saambaabidreq = {bids: {}}; - bidRequests.forEach(bid => { - let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); - saambaabidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, - w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], - h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] - - }; - }); - const serverResponse = {'id': '2aa73f571eaf29', 'seatbid': [{'bid': [{'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
', 'adomain': ['advertiserdomain.com'], 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; - - const bidResponse = spec.interpretResponse({ body: serverResponse }, saambaabidreq); - expect(bidResponse).to.deep.equal({ - requestId: bidRequests[0].bidId, - ad: serverResponse.seatbid[0].bid[0].adm, - bidderCode: 'saambaa', - creativeId: serverResponse.seatbid[0].bid[0].crid, - cpm: serverResponse.seatbid[0].bid[0].price, - width: serverResponse.seatbid[0].bid[0].w, - height: serverResponse.seatbid[0].bid[0].h, - mediaType: 'banner', - meta: { advertiserDomains: serverResponse.seatbid[0].bid[0].adomain }, - currency: 'USD', - netRevenue: true, - ttl: 60 - }); - }); - }); - }); -}); diff --git a/test/spec/modules/seedingAllianceAdapter_spec.js b/test/spec/modules/seedingAllianceAdapter_spec.js deleted file mode 100755 index 81af9546ff0..00000000000 --- a/test/spec/modules/seedingAllianceAdapter_spec.js +++ /dev/null @@ -1,186 +0,0 @@ -// jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {spec} from 'modules/seedingAllianceBidAdapter.js'; -import { NATIVE } from 'src/mediaTypes.js'; -import { config } from 'src/config.js'; - -describe('SeedingAlliance adapter', function () { - let serverResponse, bidRequest, bidResponses; - let bid = { - 'bidder': 'seedingAlliance', - 'params': { - 'adUnitId': '1hq8' - } - }; - - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); - }); - - it('should return false when AdUnitId is not set', function () { - delete bid.params.adUnitId; - assert.isFalse(spec.isBidRequestValid(bid)); - }); - }); - - describe('buildRequests', function () { - it('should send request with correct structure', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {} - }]; - - let request = spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }); - - assert.equal(request.method, 'POST'); - assert.ok(request.data); - }); - - it('should have default request structure', function () { - let keys = 'site,device,cur,imp,user,regs'.split(','); - let validBidRequests = [{ - bidId: 'bidId', - params: {} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); - let data = Object.keys(request); - - assert.deepEqual(keys, data); - }); - - it('Verify the auction ID', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {}, - auctionId: 'auctionId' - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' }, auctionId: validBidRequests[0].auctionId }).data); - - assert.equal(request.id, validBidRequests[0].auctionId); - }); - - it('Verify the device', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {} - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); - - assert.equal(request.device.ua, navigator.userAgent); - }); - - it('Verify native asset ids', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: {}, - nativeParams: { - body: { - required: true, - len: 350 - }, - image: { - required: true - }, - title: { - required: true - }, - sponsoredBy: { - required: true - }, - cta: { - required: true - }, - icon: { - required: true - } - } - }]; - - let assets = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0].native.request.assets; - - assert.equal(assets[0].id, 1); - assert.equal(assets[1].id, 3); - assert.equal(assets[2].id, 0); - assert.equal(assets[3].id, 2); - assert.equal(assets[4].id, 4); - assert.equal(assets[5].id, 5); - }); - }); - - describe('interpretResponse', function () { - const goodResponse = { - body: { - cur: 'EUR', - id: '4b516b80-886e-4ec0-82ae-9209e6d625fb', - seatbid: [ - { - seat: 'seedingAlliance', - bid: [{ - adm: { - native: { - assets: [ - {id: 0, title: {text: 'this is a title'}} - ], - imptrackers: ['https://domain.for/imp/tracker?price=${AUCTION_PRICE}'], - link: { - clicktrackers: ['https://domain.for/imp/tracker?price=${AUCTION_PRICE}'], - url: 'https://domain.for/ad/' - } - } - }, - impid: 1, - price: 0.55 - }] - } - ] - } - }; - const badResponse = { body: { - cur: 'EUR', - id: '4b516b80-886e-4ec0-82ae-9209e6d625fb', - seatbid: [] - }}; - - const bidRequest = { - data: {}, - bids: [{ bidId: 'bidId1' }] - }; - - it('should return null if body is missing or empty', function () { - const result = spec.interpretResponse(badResponse, bidRequest); - assert.equal(result.length, 0); - - delete badResponse.body - - const result1 = spec.interpretResponse(badResponse, bidRequest); - assert.equal(result.length, 0); - }); - - it('should return the correct params', function () { - const result = spec.interpretResponse(goodResponse, bidRequest); - const bid = goodResponse.body.seatbid[0].bid[0]; - - assert.deepEqual(result[0].currency, goodResponse.body.cur); - assert.deepEqual(result[0].requestId, bidRequest.bids[0].bidId); - assert.deepEqual(result[0].cpm, bid.price); - assert.deepEqual(result[0].creativeId, bid.crid); - assert.deepEqual(result[0].mediaType, 'native'); - assert.deepEqual(result[0].bidderCode, 'seedingAlliance'); - }); - - it('should return the correct tracking links', function () { - const result = spec.interpretResponse(goodResponse, bidRequest); - const bid = goodResponse.body.seatbid[0].bid[0]; - const regExpPrice = new RegExp('price=' + bid.price); - - result[0].native.clickTrackers.forEach(function (clickTracker) { - assert.ok(clickTracker.search(regExpPrice) > -1); - }); - - result[0].native.impressionTrackers.forEach(function (impTracker) { - assert.ok(impTracker.search(regExpPrice) > -1); - }); - }); - }); -}); diff --git a/test/spec/modules/segmentoBidAdapter_spec.js b/test/spec/modules/segmentoBidAdapter_spec.js deleted file mode 100644 index 17ad424f73f..00000000000 --- a/test/spec/modules/segmentoBidAdapter_spec.js +++ /dev/null @@ -1,187 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/segmentoBidAdapter.js'; - -const BIDDER_CODE = 'segmento'; -const URL = 'https://prebid-bidder.rutarget.ru/bid'; -const SYNC_IFRAME_URL = 'https://tag.rutarget.ru/tag?event=otherPage&check=true&response=syncframe&synconly=true'; -const SYNC_IMAGE_URL = 'https://tag.rutarget.ru/tag?event=otherPage&check=true&synconly=true'; -const RUB = 'RUB'; -const TIME_TO_LIVE = 0; - -describe('SegmentoAdapter', function () { - describe('isBidRequestValid', function () { - const bid = { - bidder: BIDDER_CODE, - bidId: '51ef8751f9aead', - params: { - placementId: 34 - }, - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - sizes: [[320, 50], [300, 250], [300, 600]], - bidderRequestId: '418b37f85e772c', - auctionId: '18fd8b8b0bd757' - }; - - it('should return true if placementId is a number', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false if placementId is not a number', function () { - bid.params.placementId = 'placementId'; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false if no placementId param', function () { - delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bids = [{ - bidder: 'segmento', - bidId: '51ef8751f9aead', - params: { - placementId: 34 - }, - adUnitCode: 'div-gpt-ad-1460505748561-0', - transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - sizes: [[320, 50], [300, 250], [300, 600]], - bidderRequestId: '418b37f85e772c', - auctionId: '18fd8b8b0bd757' - }]; - - const bidderRequest = { - refererInfo: { - referer: 'https://comepage.com' - } - }; - - const request = spec.buildRequests(bids, bidderRequest); - it('should return POST method', function () { - expect(request.method).to.equal('POST'); - }); - - it('should return valid url', function () { - expect(request.url).to.equal(URL); - }); - - it('should return valid data', function () { - const data = request.data; - expect(data).to.have.all.keys('settings', 'places'); - expect(data.settings.currency).to.be.equal(RUB); - expect(data.settings.referrer).to.be.a('string'); - expect(data.settings.referrer).to.be.equal(bidderRequest.refererInfo.referer); - const places = data.places; - for (let i = 0; i < places.length; i++) { - const place = places[i]; - const bid = bids[i]; - expect(place).to.have.all.keys('id', 'placementId', 'sizes'); - expect(place.id).to.be.a('string'); - expect(place.id).to.be.equal(bid.bidId); - expect(place.placementId).to.be.a('number'); - expect(place.placementId).to.be.equal(bid.params.placementId); - expect(place.sizes).to.be.an('array'); - expect(place.sizes).to.deep.equal(bid.sizes); - } - }); - - it('should return empty places if no valid bids are passed', function () { - const request = spec.buildRequests([], {}); - expect(request.data.places).to.be.an('array').to.deep.equal([]); - }); - }); - - describe('interpretResponse', function() { - const serverResponse = { - body: { - bids: [{ - id: '51ef8751f9aead', - cpm: 0.23, - currency: RUB, - creativeId: 123, - displayUrl: 'displayUrl?t=123&p=456', - size: { - width: 300, - height: 250 - } - }] - } - }; - - const emptyServerResponse = { - body: { - bids: [] - } - }; - - it('should return valid data', function () { - const response = spec.interpretResponse(serverResponse); - expect(response).to.be.an('array'); - for (let i = 0; i < response.length; i++) { - const item = response[i]; - const bid = serverResponse.body.bids[i]; - expect(item).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'creativeId', - 'currency', 'netRevenue', 'ttl', 'adUrl'); - expect(item.requestId).to.be.a('string'); - expect(item.requestId).to.be.equal(bid.id); - expect(item.cpm).to.be.a('number'); - expect(item.cpm).to.be.equal(bid.cpm); - expect(item.width).to.be.a('number'); - expect(item.width).to.be.equal(bid.size.width); - expect(item.height).to.be.a('number'); - expect(item.height).to.be.equal(bid.size.height); - expect(item.creativeId).to.be.a('number'); - expect(item.creativeId).to.be.equal(bid.creativeId); - expect(item.currency).to.be.a('string'); - expect(item.currency).to.be.equal(bid.currency); - expect(item.netRevenue).to.be.a('boolean'); - expect(item.netRevenue).to.equal(true); - expect(item.ttl).to.be.a('number'); - expect(item.ttl).to.be.equal(TIME_TO_LIVE); - expect(item.adUrl).to.be.a('string'); - expect(item.adUrl).to.be.equal(bid.displayUrl); - } - }); - - it('should return empty array if no bids', function () { - const response = spec.interpretResponse(emptyServerResponse); - expect(response).to.be.an('array').to.deep.equal([]); - }); - - it('should return empty array if server response is invalid', function () { - const response = spec.interpretResponse({}); - expect(response).to.be.an('array').to.deep.equal([]); - }); - }); - - describe('getUserSyncs', function() { - it('should return iframe type if iframe enabled', function () { - const syncs = spec.getUserSyncs({ iframeEnabled: true }); - const sync = syncs[0]; - expect(syncs).to.be.an('array').with.lengthOf(1); - expect(sync).to.have.all.keys('type', 'url'); - expect(sync.type).to.be.a('string'); - expect(sync.type).to.be.equal('iframe'); - expect(sync.url).to.be.a('string'); - expect(sync.url).to.be.equal(SYNC_IFRAME_URL); - }); - - it('should return iframe type if iframe disabled, but image enable', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true }); - const sync = syncs[0]; - expect(syncs).to.be.an('array').with.lengthOf(1); - expect(sync).to.have.all.keys('type', 'url'); - expect(sync.type).to.be.a('string'); - expect(sync.type).to.be.equal('image'); - expect(sync.url).to.be.a('string'); - expect(sync.url).to.be.equal(SYNC_IMAGE_URL); - }); - - it('should return empty array if iframe and pixels disabled', function () { - const syncs = spec.getUserSyncs({}); - expect(syncs).to.be.an('array').to.deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/sekindoUMBidAdapter_spec.js b/test/spec/modules/sekindoUMBidAdapter_spec.js deleted file mode 100644 index 2c361c21303..00000000000 --- a/test/spec/modules/sekindoUMBidAdapter_spec.js +++ /dev/null @@ -1,168 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/sekindoUMBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('sekindoUMAdapter', function () { - const adapter = newBidder(spec); - - const bannerParams = { - 'spaceId': '14071' - }; - - const videoParams = { - 'spaceId': '14071', - 'video': { - playerWidth: 300, - playerHeight: 250, - vid_vastType: 2 // optional - } - }; - - var bidRequests = { - 'bidder': 'sekindoUM', - 'params': bannerParams, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'mediaType': 'banner' - }; - - 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 () { - bidRequests.mediaType = 'banner'; - bidRequests.params = bannerParams; - expect(spec.isBidRequestValid(bidRequests)).to.equal(true); - }); - - it('should return false when required video params are missing', function () { - bidRequests.mediaType = 'video'; - bidRequests.params = bannerParams; - expect(spec.isBidRequestValid(bidRequests)).to.equal(false); - }); - - it('should return true when required Video params found', function () { - bidRequests.mediaType = 'video'; - bidRequests.params = videoParams; - expect(spec.isBidRequestValid(bidRequests)).to.equal(true); - }); - }); - - describe('buildRequests', function () { - it('banner data should be a query string and method = GET', function () { - bidRequests.mediaType = 'banner'; - bidRequests.params = bannerParams; - const request = spec.buildRequests([bidRequests]); - expect(request[0].data).to.be.a('string'); - expect(request[0].method).to.equal('GET'); - }); - - it('with gdprConsent, banner data should be a query string and method = GET', function () { - bidRequests.mediaType = 'banner'; - bidRequests.params = bannerParams; - const request = spec.buildRequests([bidRequests], {'gdprConsent': {'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', 'vendorData': {}, 'gdprApplies': true}}); - expect(request[0].data).to.be.a('string'); - expect(request[0].method).to.equal('GET'); - }); - - it('video data should be a query string and method = GET', function () { - bidRequests.mediaType = 'video'; - bidRequests.params = videoParams; - const request = spec.buildRequests([bidRequests]); - expect(request[0].data).to.be.a('string'); - expect(request[0].method).to.equal('GET'); - }); - }); - - describe('interpretResponse', function () { - it('banner should get correct bid response', function () { - let response = { - 'headers': function(header) { - return 'dummy header'; - }, - 'body': {'id': '30b31c1838de1e', 'bidderCode': 'sekindoUM', 'cpm': 2.1951, 'width': 300, 'height': 250, 'ad': '

sekindo creative<\/h1>', 'ttl': 36000, 'creativeId': '323774', 'netRevenue': true, 'currency': 'USD'} - }; - - bidRequests.mediaType = 'banner'; - bidRequests.params = bannerParams; - let expectedResponse = [ - { - 'requestId': '30b31c1838de1e', - 'bidderCode': 'sekindoUM', - 'cpm': 2.1951, - 'width': 300, - 'height': 250, - 'creativeId': '323774', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 36000, - 'ad': '

sekindo creative

' - } - ]; - let result = spec.interpretResponse(response, bidRequests); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('vastXml video should get correct bid response', function () { - let response = { - 'headers': function(header) { - return 'dummy header'; - }, - 'body': {'id': '30b31c1838de1e', 'bidderCode': 'sekindoUM', 'cpm': 2.1951, 'width': 300, 'height': 250, 'vastXml': '', 'ttl': 36000, 'creativeId': '323774', 'netRevenue': true, 'currency': 'USD'} - }; - - bidRequests.mediaType = 'video'; - bidRequests.params = videoParams; - let expectedResponse = [ - { - 'requestId': '30b31c1838de1e', - 'bidderCode': 'sekindoUM', - 'cpm': 2.1951, - 'width': 300, - 'height': 250, - 'creativeId': '323774', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 36000, - 'vastXml': '' - } - ]; - let result = spec.interpretResponse(response, bidRequests); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('vastUrl video should get correct bid response', function () { - let response = { - 'headers': function(header) { - return 'dummy header'; - }, - 'body': {'id': '30b31c1838de1e', 'bidderCode': 'sekindoUM', 'cpm': 2.1951, 'width': 300, 'height': 250, 'vastUrl': 'https://vastUrl', 'ttl': 36000, 'creativeId': '323774', 'netRevenue': true, 'currency': 'USD'} - }; - bidRequests.mediaType = 'video'; - bidRequests.params = videoParams; - let expectedResponse = [ - { - 'requestId': '30b31c1838de1e', - 'bidderCode': 'sekindoUM', - 'cpm': 2.1951, - 'width': 300, - 'height': 250, - 'creativeId': '323774', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 36000, - 'vastUrl': 'https://vastUrl' - } - ]; - let result = spec.interpretResponse(response, bidRequests); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - }); -}); diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js index ad51fe81cde..d8056fcc616 100644 --- a/test/spec/modules/sharedIdSystem_spec.js +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -1,8 +1,9 @@ import { - sharedIdSubmodule, + sharedIdSystemSubmodule, } from 'modules/sharedIdSystem.js'; import { server } from 'test/mocks/xhr.js'; import {uspDataHandler} from 'src/adapterManager'; +import sinon from 'sinon'; let expect = require('chai').expect; @@ -22,7 +23,7 @@ describe('SharedId System', function() { }); it('should call shared id endpoint without consent data and handle a valid response', function () { - let submoduleCallback = sharedIdSubmodule.getId(undefined, undefined).callback; + let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, undefined).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -32,7 +33,7 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); }); it('should call shared id endpoint with consent data and handle a valid response', function () { @@ -41,7 +42,7 @@ describe('SharedId System', function() { consentString: 'abc12345234', }; - let submoduleCallback = sharedIdSubmodule.getId(undefined, consentData).callback; + let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, consentData).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -51,7 +52,7 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); }); it('should call shared id endpoint with usp consent data and handle a valid response', function () { @@ -61,7 +62,7 @@ describe('SharedId System', function() { consentString: 'abc12345234', }; - let submoduleCallback = sharedIdSubmodule.getId(undefined, consentData).callback; + let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, consentData).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -71,7 +72,7 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); }); }); }); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index b8d91249ec3..68d7b6210df 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -24,10 +24,6 @@ const bidRequests = [ linkType: 2 } }, - sharedid: { - id: 'fake-sharedid', - third: 'fake-sharedthird' - }, lipb: { lipbid: 'fake-lipbid' } @@ -375,12 +371,6 @@ describe('sharethrough adapter spec', function() { expect(bidRequest.data.id5uid.linkType).to.eq(2); }); - it('should add the shduid parameter if a bid request contains a value for Shared ID', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.shduid.id).to.eq('fake-sharedid'); - expect(bidRequest.data.shduid.third).to.eq('fake-sharedthird'); - }); - it('should add the liuid parameter if a bid request contains a value for LiveIntent ID', function() { const bidRequest = spec.buildRequests(bidRequests)[0]; expect(bidRequest.data.liuid).to.eq('fake-lipbid'); diff --git a/test/spec/modules/shinezBidAdapter_spec.js b/test/spec/modules/shinezBidAdapter_spec.js deleted file mode 100644 index cc3c2451c5d..00000000000 --- a/test/spec/modules/shinezBidAdapter_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import { expect } from 'chai'; -import sinon from 'sinon'; -import { spec, internal } from 'modules/shinezBidAdapter.js'; - -describe('shinezBidAdapter', () => { - let sandbox; - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - afterEach(() => { - sandbox.restore(); - }); - describe('isBidRequestValid', () => { - const cases = [ - [ - 'should return false when placementId is missing', - { - params: {}, - }, - false, - ], - [ - 'should return false when placementId has wrong type', - { - params: { - placementId: 123, - }, - }, - false, - ], - [ - 'should return false when unit has wrong type', - { - params: { - placementId: '00654321', - unit: 150, - }, - }, - false, - ], - [ - 'should return true when required params found and valid', - { - params: { - placementId: '00654321', - }, - }, - true, - ], - [ - 'should return true when all params found and valid', - { - params: { - placementId: '00654321', - unit: '__header-bid-1', - }, - }, - true, - ], - ]; - cases.map(([description, request, expected]) => { - it(description, () => { - const result = spec.isBidRequestValid(request); - expect(result).to.be.equal(expected); - }); - }); - }); - describe('buildRequests', () => { - it('should build server request correctly', () => { - const utcOffset = 300; - const validBidRequests = [ - { - params: { - placementId: '00654321', - unit: 'header-bid-tag-1-shinez', - }, - crumbs: { - pubcid: 'c8584a82-bec3-4347-8d3e-e7612438a161', - }, - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - adUnitCode: 'header-bid-tag-1', - transactionId: '665760dc-a249-4be7-ae86-91f417b2c65d', - }, - ]; - const bidderRequest = { - refererInfo: { - referer: 'http://site-with-ads.com', - }, - }; - sandbox.stub(Date.prototype, 'getTimezoneOffset').returns(utcOffset); - const result = spec.buildRequests(validBidRequests, bidderRequest); - const expectedData = [ - { - bidId: validBidRequests[0].bidId, - transactionId: validBidRequests[0].transactionId, - crumbs: validBidRequests[0].crumbs, - mediaTypes: validBidRequests[0].mediaTypes, - refererInfo: bidderRequest.refererInfo, - adUnitCode: validBidRequests[0].adUnitCode, - utcOffset: utcOffset, - placementId: validBidRequests[0].params.placementId, - unit: validBidRequests[0].params.unit, - }, - ]; - expect(result.method, "request should be POST'ed").equal('POST'); - expect(result.url, 'request should be send to correct url').equal( - internal.TARGET_URL - ); - expect(result.data, 'request should have correct payload').to.deep.equal( - expectedData - ); - }); - }); - describe('interpretResponse', () => { - it('should interpret bid responses correctly', () => { - const response = { - body: [ - { - bidId: '2ece6496f4d0c9', - cpm: 0.03, - currency: 'USD', - width: 300, - height: 250, - ad: `

The Ad

`, - ttl: 60, - creativeId: 'V8qlA6guwm', - netRevenue: true, - }, - ], - }; - const bids = [ - { - requestId: response.body[0].bidId, - cpm: response.body[0].cpm, - currency: response.body[0].currency, - width: response.body[0].width, - height: response.body[0].height, - ad: response.body[0].ad, - ttl: response.body[0].ttl, - creativeId: response.body[0].creativeId, - netRevenue: response.body[0].netRevenue, - }, - ]; - const result = spec.interpretResponse(response); - expect(result).to.deep.equal(bids); - }); - }); -}); diff --git a/test/spec/modules/slimcutBidAdapter_spec.js b/test/spec/modules/slimcutBidAdapter_spec.js deleted file mode 100644 index 44da1d87824..00000000000 --- a/test/spec/modules/slimcutBidAdapter_spec.js +++ /dev/null @@ -1,212 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/slimcutBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -const ENDPOINT = 'https://sb.freeskreen.com/pbr'; -const AD_SCRIPT = '"'; - -describe('slimcutBidAdapter', 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': 'slimcut', - 'params': { - 'placementId': 83 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '3c871ffa8ef14c', - 'bidderRequestId': 'b41642f1aee381', - 'auctionId': '4e156668c977d7' - }; - - it('should return true when required params found', function() { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when placementId is not valid (letters)', function() { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'placementId': 'ABCD' - }; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when placementId < 0', function() { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'placementId': -1 - }; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when required params are not passed', function() { - let bid = Object.assign({}, bid); - delete bid.params; - - bid.params = {}; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function() { - let bidRequests = [ - { - 'bidder': 'teads', - 'params': { - 'placementId': 10433394 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '3c871ffa8ef14c', - 'bidderRequestId': 'b41642f1aee381', - 'auctionId': '4e156668c977d7', - 'deviceWidth': 1680 - } - ]; - - let bidderResquestDefault = { - 'auctionId': '4e156668c977d7', - 'bidderRequestId': 'b41642f1aee381', - 'timeout': 3000 - }; - - it('sends bid request to ENDPOINT via POST', function() { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); - - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should send GDPR to endpoint', function() { - let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; - let bidderRequest = { - 'auctionId': '4e156668c977d7', - 'bidderRequestId': 'b41642f1aee381', - 'timeout': 3000, - 'gdprConsent': { - 'consentString': consentString, - 'gdprApplies': true, - 'vendorData': { - 'hasGlobalConsent': false - } - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.gdpr_iab).to.exist; - expect(payload.gdpr_iab.consent).to.equal(consentString); - }); - - it('should add referer info to payload', function () { - const bidRequest = Object.assign({}, bidRequests[0]) - const bidderRequest = { - refererInfo: { - referer: 'https://example.com/page.html', - reachedTop: true, - numIframes: 2 - } - } - const request = spec.buildRequests([bidRequest], bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.referrer).to.exist; - expect(payload.referrer).to.deep.equal('https://example.com/page.html') - }); - }); - - describe('getUserSyncs', () => { - let bids = { - 'body': { - 'responses': [{ - 'ad': AD_SCRIPT, - 'cpm': 0.5, - 'currency': 'USD', - 'height': 250, - 'netRevenue': true, - 'requestId': '3ede2a3fa0db94', - 'ttl': 360, - 'width': 300, - 'creativeId': 'er2ee', - 'transactionId': 'deadb33f', - 'winUrl': 'https://sb.freeskreen.com/win' - }] - } - }; - - it('should get the correct number of sync urls', () => { - let urls = spec.getUserSyncs({iframeEnabled: true}, bids); - expect(urls.length).to.equal(1); - expect(urls[0].url).to.equal('https://sb.freeskreen.com/async_usersync.html'); - }); - - it('should return no url if not iframe enabled', () => { - let urls = spec.getUserSyncs({iframeEnabled: false}, bids); - expect(urls.length).to.equal(0); - }); - }); - - describe('interpretResponse', function() { - let bids = { - 'body': { - 'responses': [{ - 'ad': AD_SCRIPT, - 'cpm': 0.5, - 'currency': 'USD', - 'height': 250, - 'netRevenue': true, - 'requestId': '3ede2a3fa0db94', - 'ttl': 360, - 'width': 300, - 'creativeId': 'er2ee', - 'transactionId': 'deadb33f', - 'winUrl': 'https://sb.freeskreen.com/win' - }] - } - }; - - it('should get correct bid response', function() { - let expectedResponse = [{ - 'cpm': 0.5, - 'width': 300, - 'height': 250, - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 360, - 'ad': AD_SCRIPT, - 'requestId': '3ede2a3fa0db94', - 'creativeId': 'er2ee', - 'transactionId': 'deadb33f', - 'winUrl': 'https://sb.freeskreen.com/win' - }]; - - let result = spec.interpretResponse(bids); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function() { - let bids = { - 'body': { - 'responses': [] - } - }; - - let result = spec.interpretResponse(bids); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/smarticoBidAdapter_spec.js b/test/spec/modules/smarticoBidAdapter_spec.js deleted file mode 100644 index e8ca5b0e127..00000000000 --- a/test/spec/modules/smarticoBidAdapter_spec.js +++ /dev/null @@ -1,118 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/smarticoBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -describe('smarticoBidAdapter', function () { - const adapter = newBidder(spec); - let bid = { - adUnitCode: 'adunit-code', - auctionId: '5kaj89l8-3456-2s56-c455-4g6h78jsdfgf', - bidRequestsCount: 1, - bidder: 'smartico', - bidderRequestId: '24081afs940568', - bidderRequestsCount: 1, - bidderWinsCount: 0, - bidId: '22499d052045', - mediaTypes: {banner: {sizes: [[300, 250]]}}, - params: { - token: 'FNVzUGZn9ebpIOoheh3kEJ2GQ6H6IyMH39sHXaya', - placementId: 'testPlacementId' - }, - sizes: [ - [300, 250] - ], - transactionId: '34562345-4dg7-46g7-4sg6-45gdsdj8fd56' - } - let bidderRequests = { - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - auctionStart: 1579746300522, - bidderCode: 'myBidderCode', - bidderRequestId: '15246a574e859f', - bids: [bid], - refererInfo: { - canonicalUrl: '', - numIframes: 0, - reachedTop: true - } - } - describe('isBidRequestValid', function () { - it('should return true where required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - describe('buildRequests', function () { - let bidRequests = [ bid ]; - let request = spec.buildRequests(bidRequests, bidderRequests); - it('sends bid request via POST', function () { - expect(request.method).to.equal('POST'); - }); - it('must contain token', function() { - expect(request.data.bidParams[0].token).to.equal('FNVzUGZn9ebpIOoheh3kEJ2GQ6H6IyMH39sHXaya'); - }); - it('must contain auctionId', function() { - expect(request.data.auctionId).to.exist.and.to.be.a('string') - }); - it('must contain valid width and height', function() { - expect(request.data.bidParams[0]['banner-format-width']).to.exist.and.to.be.a('number') - expect(request.data.bidParams[0]['banner-format-height']).to.exist.and.to.be.a('number') - }); - }); - - describe('interpretResponse', function () { - let bidRequest = { - method: 'POST', - url: 'https://trmads.eu/preBidRequest', - bids: [bid], - data: [{ - token: 'FNVzUGZn9ebpIOoheh3kEJ2GQ6H6IyMH39sHXaya', - bidId: '22499d052045', - 'banner-format-width': 300, - 'banner-format-height': 250, - placementId: 'testPlacementId', - }] - }; - let serverResponse = [{ - bidId: '22499d052045', - id: 987654, - cpm: 10, - ttl: 30, - bannerFormatWidth: 300, - bannerFormatHeight: 250, - bannerFormatAlias: 'medium_rectangle' - }]; - let expectedResponse = [{ - requestId: bid.bidId, - cpm: 10, - width: 300, - height: 250, - creativeId: 987654, - netRevenue: false, // gross - ttl: 30, - ad: '', - 'adomain': [''], - 'cid': '1', - 'crid': '700', - 'w': 300, - 'h': 250 - }]}], - 'bidid': 'bidid', - 'cur': 'USD' - }, - 'headers': {} - }; - it('required keys', function () { - const result = spec.interpretResponse(validServerResponse, validBidRequest); - - let requiredKeys = [ - 'requestId', - 'creativeId', - 'adId', - 'cpm', - 'width', - 'height', - 'currency', - 'netRevenue', - 'ttl', - 'ad' - ]; - - let resultKeys = Object.keys(result[0]); - requiredKeys.forEach(function(key) { - expect(resultKeys.indexOf(key) !== -1).to.equal(true); - }); - }) - }); - - describe('getUserSyncs', function () { - it('check empty response getUserSyncs', function () { - const result = spec.getUserSyncs('', ''); - expect(result).to.deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/topRTBBidAdapter_spec.js b/test/spec/modules/topRTBBidAdapter_spec.js deleted file mode 100644 index 9b97917a0b6..00000000000 --- a/test/spec/modules/topRTBBidAdapter_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/topRTBBidAdapter.js'; - -describe('topRTBBidAdapterTests', function () { - it('validate_pub_params', function () { - expect(spec.isBidRequestValid({ - bidder: 'topRTB', - params: { - adUnitId: 'c5c06f77430c4c33814a0577cb4cc978' - }, - adName: 'banner' - })); - }); - - it('validate_generated_params', function () { - let bidRequestData = [{ - bidId: 'bid12345', - bidder: 'topRTB', - adName: 'banner', - adType: '{"banner":{"sizes":[[]]}}', - params: { - adUnitId: 'c5c06f77430c4c33814a0577cb4cc978' - }, - sizes: [[728, 90]] - }]; - - let request = spec.buildRequests(bidRequestData); - const current_url = request.url; - const search_params = current_url.searchParams; - }); - - it('validate_response_params', function () { - let bidRequestData = { - data: { - bidId: 'bid12345' - } - }; - - let serverResponse = { - body: [{ - 'cpm': 1, - 'mediadata': "Banner 728x90", - 'width': 728, - 'currency': 'USD', - 'id': 'cd95dffec6b645afbc4e5aa9f68f2ff3', - 'type': 'RICHMEDIA', - 'ttl': 4000, - 'bidId': 'bid12345', - 'status': 'success', - 'height': 90}], - 'adName': 'banner', - 'vastXml': '', - 'mediaType': 'banner', - 'tracking': 'https://ssp.toprtb.com/ssp/tracking?F0cloTiKIw%2BjZ2UNDvlKGn5%2FWoAO9cnlAUDm6gFBM8bImY2fKo%2BMTvI0XvXzFTZSb5v8o4EUbPId9hckptTqA4QPaWvpVYCRKRZceXNa4kjtvfm4j2e%2FcRKgkns2goHXi7IZC0sBIbE77WWg%2BPBYv%2BCu84H%2FSH69mi%2FDaWcQlfaEOdkaJdstJEkaZtkgWnFnS7aagte%2BfdEbOqcTxq5hzj%2BZ4NZbwgReuWTQZbfrMWjkXFbn%2B35vZuI319o6XH9n9fKLS4xp8zstXfQT2oSgjw1NmrwqRKf1efB1UaWlS1TbkSqxZ7Kcy7nJvAZrDk0tzcSeIxe4VfHpwgPPs%2BueUeGwz3o7OCh7H1sCmogSrmJFB9JTeXudFjC13iANAtu4SvG9bGIbiJxS%2BNfkjy2mLFm8kSIcIobjNkMEcUAwmoqJNRndwb66a3Iovk2NTo0Ly%2FV7Y5ECPcS5%2FPBrIEOuQXS5SNUPRWKoklX5nexHtOc%3D', - 'impression': 'https://ssp.toprtb.com/ssp/impression?id=64f29f7b226249f19925a680a506b32d' - }; - - let bids = spec.interpretResponse(serverResponse, bidRequestData); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - expect(bid.cpm).to.equal(1); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(728); - expect(bid.height).to.equal(90); - expect(bid.requestId).to.equal('bid12345'); - }); -}); diff --git a/test/spec/modules/trendqubeBidAdapter_spec.js b/test/spec/modules/trendqubeBidAdapter_spec.js deleted file mode 100644 index f2ce95832ff..00000000000 --- a/test/spec/modules/trendqubeBidAdapter_spec.js +++ /dev/null @@ -1,270 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/trendqubeBidAdapter.js'; -import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; - -describe('TrendqubebBidAdapter', function () { - const bid = { - bidId: '23fhj33i987f', - bidder: 'trendqube', - params: { - placementId: 0, - traffic: BANNER - } - }; - - const bidderRequest = { - refererInfo: { - referer: 'test.com' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId 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://ads.trendqube.com/?c=o&m=multi'); - }); - 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'); - 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.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); - expect(placement.placementId).to.equal(0); - expect(placement.bidId).to.equal('23fhj33i987f'); - expect(placement.traffic).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - }); - - it('Returns valid data for mediatype video', function () { - const playerSize = [300, 300]; - bid.mediaTypes = {}; - bid.params.traffic = VIDEO; - bid.mediaTypes[VIDEO] = { - playerSize - }; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - let placement = data['placements'][0]; - expect(placement).to.be.an('object'); - expect(placement.traffic).to.equal(VIDEO); - expect(placement.wPlayer).to.equal(playerSize[0]); - expect(placement.hPlayer).to.equal(playerSize[1]); - }); - - 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: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - 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'); - expect(dataItem.requestId).to.equal('23fhj33i987f'); - 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'); - }); - 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' - }] - }; - 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'); - 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'); - }); - 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', - }] - }; - 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'); - 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'); - }); - 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; - }); - }); -}); diff --git a/test/spec/modules/tribeosBidAdapter_spec.js b/test/spec/modules/tribeosBidAdapter_spec.js deleted file mode 100644 index fd7f7087eb7..00000000000 --- a/test/spec/modules/tribeosBidAdapter_spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/tribeosBidAdapter.js'; - -describe('tribeosBidAdapter', function() { - describe('isBidRequestValid', function() { - it('should return true if all parameters are passed', function() { - expect(spec.isBidRequestValid({ - bidder: 'tribeos', - params: { - placementId: '12345' - } - })).to.equal(true); - }); - - it('should return false is placementId is missing', function() { - expect(spec.isBidRequestValid({ - bidder: 'tribeos', - params: {} - })).to.equal(false); - }); - }); - - it('validate bid request data from backend', function() { - let bidRequestData = [{ - bidId: 'bid12', - bidder: 'tribeos', - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ], - } - }, - params: { - placementId: 'test-bid' - } - }]; - - let request = spec.buildRequests(bidRequestData); - let payload = JSON.parse(request[0].data); - - expect(payload.bidId).to.equal('bid12'); - }); - - it('validate response parameters', function() { - let bidRequestData = { - data: { - bidId: '21f3e9c3ce92f2' - } - }; - - let serverResponse = { - body: { - 'id': '5e23a6c74314aa782328376f5954', - 'bidid': '5e23a6c74314aa782328376f5954', - 'seatbid': [{ - 'bid': [{ - 'id': '5e23a6c74314aa782328376f5954', - 'impid': '21f3e9c3ce92f2', - 'price': 1.1, - 'adid': '5e23a6c74314aa782328376f5954', - 'adm': '', - 'cid': '5e1eea895d37673aef2134825195rnd2', - 'crid': '5e0b71e6823bb66fcb6c9858', - 'h': 250, - 'w': 300 - }], - 'seats': '1' - }], - 'cur': 'USD' - } - }; - - let bids = spec.interpretResponse(serverResponse, bidRequestData); - expect(bids).to.have.lengthOf(1); - let bid = bids[0]; - - expect(bid.cpm).to.equal(1.1); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.netRevenue).to.equal(true); - expect(bid.requestId).to.equal('21f3e9c3ce92f2'); - expect(bid.ad).to.equal(''); - }); -}); diff --git a/test/spec/modules/truereachBidAdapter_spec.js b/test/spec/modules/truereachBidAdapter_spec.js index 36441722648..3c78c4b848d 100644 --- a/test/spec/modules/truereachBidAdapter_spec.js +++ b/test/spec/modules/truereachBidAdapter_spec.js @@ -40,7 +40,7 @@ describe('truereachBidAdapterTests', function () { expect(req_data.imp[0].id).to.equal('34ce3f3b15190a'); expect(req_data.imp[0].banner.w).to.equal(300); expect(req_data.imp[0].banner.h).to.equal(250); - expect(req_data.imp[0].bidfloor).to.equal(0.1); + expect(req_data.imp[0].bidfloor).to.equal(0); }); it('validate_response_params', function () { diff --git a/test/spec/modules/turktelekomBidAdapter_spec.js b/test/spec/modules/turktelekomBidAdapter_spec.js deleted file mode 100644 index c4e55178638..00000000000 --- a/test/spec/modules/turktelekomBidAdapter_spec.js +++ /dev/null @@ -1,749 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/turktelekomBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('TurkTelekomAdapter', 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': 'turktelekom', - 'params': { - 'uid': '17' - }, - '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 required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'uid': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - function parseRequest(url) { - const res = {}; - url.split('&').forEach((it) => { - const couple = it.split('='); - res[couple[0]] = decodeURIComponent(couple[1]); - }); - return res; - } - - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - const referrer = bidderRequest.refererInfo.referer; - - let bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '18' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '18' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90], [300, 250]], - 'bidId': '3150ccb55da321', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '20' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '42dbe3a7168a6a', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '18'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - }); - - it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '18,18,20'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('pt parameter must be "gross" if params.priceType === "gross"', function () { - bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'gross'); - expect(payload).to.have.property('auids', '18,18,20'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - delete bidRequests[1].params.priceType; - }); - - it('pt parameter must be "net" or "gross"', function () { - bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '18,18,20'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - delete bidRequests[1].params.priceType; - }); - - it('if gdprConsent is present payload must have gdpr params', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if gdprApplies is false gdpr_applies must be 0', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '0'); - }); - - it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - }); - - describe('interpretResponse', function () { - const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 18, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 17, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 19, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, - undefined, - {'bid': [], 'seat': '1'}, - {'seat': '1'}, - ]; - - it('should get correct bid response', function () { - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', - 'bidderRequestId': '5f2009617a7c0a', - 'auctionId': '1cbd2feafe5e8b', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '659423fff799cb', - 'cpm': 1.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('should get correct multi bid response', function () { - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71a5b', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '18' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4dff80cc4ee346', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '5703af74d0472a', - 'bidderRequestId': '2c2bb1972df9a', - 'auctionId': '1fa09aee5c8c99', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '300bfeb0d71a5b', - 'cpm': 1.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 18, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 2
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '5703af74d0472a', - 'cpm': 0.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 728, - 'height': 90, - 'ad': '
test content 3
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('handles wrong and nobid responses', function () { - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '19' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d7190gf', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '20' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71321', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '25' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '300bfeb0d7183bb', - 'bidderRequestId': '2c2bb1972d23af', - 'auctionId': '1fa09aee5c84d34', - } - ]; - const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); - expect(result.length).to.equal(0); - }); - - it('complicated case', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 18, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 17, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 17, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 18, 'h': 600, 'w': 350}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2164be6358b9', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '326bde7fbf69', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '18' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '4e111f1b66e4', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '26d6f897b516', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '44' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '1751cd90161', - 'bidderRequestId': '106efe3247', - 'auctionId': '32a1f276cb87cb8', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '2164be6358b9', - 'cpm': 1.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '4e111f1b66e4', - 'cpm': 0.5, - 'creativeId': 18, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 2
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '26d6f897b516', - 'cpm': 0.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 728, - 'height': 90, - 'ad': '
test content 3
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '326bde7fbf69', - 'cpm': 0.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'ad': '
test content 4
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('dublicate uids and sizes in one slot', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 17, 'h': 250, 'w': 300}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '5126e301f4be', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57b2ebe70e16', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '17' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '225fcd44b18c', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - } - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '5126e301f4be', - 'cpm': 1.15, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - }, - { - 'requestId': '57b2ebe70e16', - 'cpm': 0.5, - 'creativeId': 17, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 2
', - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'banner', - 'netRevenue': true, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - }); - - it('should get correct video bid response', function () { - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '25' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57dfefb80eca', - 'bidderRequestId': '20394420a762a2', - 'auctionId': '140132d07b031', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '26' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'e893c787c22dd', - 'bidderRequestId': '20394420a762a2', - 'auctionId': '140132d07b031', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - } - ]; - const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 25, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 26, content_type: 'video'}], 'seat': '2'} - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '57dfefb80eca', - 'cpm': 1.15, - 'creativeId': 25, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': response}}, request); - expect(result).to.deep.equal(expectedResponse); - }); - - it('should have right renderer in the bid response', function () { - const spySetRenderer = sinon.spy(); - const stubRenderer = { - setRender: spySetRenderer - }; - const spyRendererInstall = sinon.spy(function() { return stubRenderer; }); - const stubRendererConst = { - install: spyRendererInstall - }; - const bidRequests = [ - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '25' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'e6e65553fc8', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3', - 'mediaTypes': { - 'video': { - 'context': 'outstream' - } - } - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '26' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': 'c8fdcb3f269f', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3' - }, - { - 'bidder': 'turktelekom', - 'params': { - 'uid': '27' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '1de036c37685', - 'bidderRequestId': '1380f393215dc7', - 'auctionId': '10b8d2f3c697e3', - 'renderer': {} - } - ]; - const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 25, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 26, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, - {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 27, content_type: 'video', w: 300, h: 250}], 'seat': '2'} - ]; - const request = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': 'e6e65553fc8', - 'cpm': 1.15, - 'creativeId': 25, - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - }, - 'renderer': stubRenderer - }, - { - 'requestId': 'c8fdcb3f269f', - 'cpm': 1.00, - 'creativeId': 26, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - }, - 'renderer': stubRenderer - }, - { - 'requestId': '1de036c37685', - 'cpm': 1.20, - 'creativeId': 27, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'bidderCode': 'turktelekom', - 'currency': 'TRY', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': response}}, request, stubRendererConst); - - expect(spySetRenderer.calledTwice).to.equal(true); - expect(spySetRenderer.getCall(0).args[0]).to.be.a('function'); - expect(spySetRenderer.getCall(1).args[0]).to.be.a('function'); - - expect(spyRendererInstall.calledTwice).to.equal(true); - expect(spyRendererInstall.getCall(0).args[0]).to.deep.equal({ - id: 'e6e65553fc8', - url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', - loaded: false - }); - expect(spyRendererInstall.getCall(1).args[0]).to.deep.equal({ - id: 'c8fdcb3f269f', - url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js', - loaded: false - }); - - expect(result).to.deep.equal(expectedResponse); - }); -}); diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 5899554244b..ac788e537e2 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -14,7 +14,6 @@ const userId = { 'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', 'parrableId': {'eid': '01.1608624401.fe44bca9b96873084a0d4e9d0ac5729f13790ba8f8e58fa4707b6b3c096df91c6b5f254992bdad4ab1dd4a89919081e9b877d7a039ac3183709277665bac124f28e277d109f0ff965058'}, 'pubcid': 'd8aa10fa-d86c-451d-aad8-5f16162a9e64', - 'sharedid': {'id': '01ESHXW4HD29KMF387T63JQ9H5', 'third': '01ESHXW4HD29KMF387T63JQ9H5'}, 'tdid': 'D6885E90-2A7A-4E0F-87CB-7734ED1B99A3', 'haloId': {}, 'uid2': {'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888'}, diff --git a/test/spec/modules/unicornBidAdapter_spec.js b/test/spec/modules/unicornBidAdapter_spec.js deleted file mode 100644 index dcd446b2bb0..00000000000 --- a/test/spec/modules/unicornBidAdapter_spec.js +++ /dev/null @@ -1,490 +0,0 @@ -import { assert, expect } from 'chai'; -import { spec } from 'modules/unicornBidAdapter.js'; -import * as _ from 'lodash'; - -const bidRequests = [ - { - bidder: 'unicorn', - params: { - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250], [336, 280]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'ea0aa332-a6e1-4474-8180-83720e6b87bc', - sizes: [[300, 250], [336, 280]], - bidId: '226416e6e6bf41', - bidderRequestId: '1f41cbdcbe58d5', - auctionId: '77987c3a-9be9-4e43-985a-26fc91d84724', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - transactionId: 'cf801303-cf98-4b4a-9e0a-c27b93bce6d8', - sizes: [[300, 250]], - bidId: '37cdc0b5d0363b', - bidderRequestId: '1f41cbdcbe58d5', - auctionId: '77987c3a-9be9-4e43-985a-26fc91d84724', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - bidfloorCpm: 0 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: '/19968336/header-bid-tag-2', - transactionId: 'ba7f114c-3676-4a08-a26d-1ee293d521ed', - sizes: [[300, 250]], - bidId: '468569a6597a4', - bidderRequestId: '1f41cbdcbe58d5', - auctionId: '77987c3a-9be9-4e43-985a-26fc91d84724', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - } -]; - -const validBidRequests = [ - { - bidder: 'unicorn', - params: { - placementId: 'rectangle-ad-1', - bidfloorCpm: 0, - accountId: 12345, - publisherId: 99999, - mediaId: 'example' - }, - mediaTypes: { - banner: { - sizes: [[300, 250], [336, 280]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'fbf94ccf-f377-4201-a662-32c2feb8ab6d', - sizes: [[300, 250], [336, 280]], - bidId: '2fb90842443e24', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: '/19968336/header-bid-tag-1', - transactionId: '2d65e313-f8a6-4888-b9ab-50fb3ca744ea', - sizes: [[300, 250]], - bidId: '352f86f158d97a', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - placementId: 'rectangle-ad-2', - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: '/19968336/header-bid-tag-2', - transactionId: '82f445a8-44bc-40bc-9913-739b40375566', - sizes: [[300, 250]], - bidId: '4cde82cc90126b', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - } -]; - -const bidderRequest = { - bidderCode: 'unicorn', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - bidderRequestId: '123ae4cc3eeb7e', - bids: [ - { - bidder: 'unicorn', - params: { - placementId: 'rectangle-ad-1', - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250], [336, 280]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'fbf94ccf-f377-4201-a662-32c2feb8ab6d', - sizes: [[300, 250], [336, 280]], - bidId: '2fb90842443e24', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: '/19968336/header-bid-tag-1', - transactionId: '2d65e313-f8a6-4888-b9ab-50fb3ca744ea', - sizes: [[300, 250]], - bidId: '352f86f158d97a', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - }, - { - bidder: 'unicorn', - params: { - placementId: 'rectangle-ad-2', - bidfloorCpm: 0, - accountId: 12345 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - adUnitCode: '/19968336/header-bid-tag-2', - transactionId: '82f445a8-44bc-40bc-9913-739b40375566', - sizes: [[300, 250]], - bidId: '4cde82cc90126b', - bidderRequestId: '123ae4cc3eeb7e', - auctionId: 'c594a888-6744-46c6-8b0e-d188e40e83ef', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 - } - ], - auctionStart: 1581064124172, - timeout: 1000, - refererInfo: { - referer: 'https://uni-corn.net/', - reachedTop: true, - numIframes: 0, - stack: ['https://uni-corn.net/'] - }, - start: 1581064124177 -}; - -const openRTBRequest = { - id: '5ebea288-f13a-4754-be6d-4ade66c68877', - at: 1, - imp: [ - { - id: '216255f234b602', - banner: { - w: 300, - h: 250, - format: [ - { - w: 300, - h: 250 - }, - { - w: 336, - h: 280 - } - ] - }, - secure: 1, - bidfloor: 0, - tagid: 'rectangle-ad-1' - }, - { - id: '31e2b28ced2475', - banner: { - w: 300, - h: 250, - format: [ - { - w: 300, - h: 250 - } - ] - }, - secure: 1, - bidfloor: 0, - tagid: '/19968336/header-bid-tag-1' - }, - { - id: '40a333e047a9bd', - banner: { - w: 300, - h: 250, - format: [ - { - w: 300, - h: 250 - } - ] - }, - secure: 1, - bidfloor: 0, - tagid: 'rectangle-ad-2' - } - ], - cur: 'JPY', - ext: { - accountId: 12345 - }, - site: { - id: 'example', - publisher: { - id: 99999 - }, - domain: 'uni-corn.net', - page: 'https://uni-corn.net/', - ref: 'https://uni-corn.net/' - }, - device: { - language: 'ja', - ua: - 'Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A5000) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36' - }, - user: { - id: '69d9e1c2-801e-4901-a665-fad467550fec' - }, - bcat: [], - source: { - ext: { - stype: 'prebid_uncn', - bidder: 'unicorn', - prebid_version: '1.0' - } - } -}; - -const serverResponse = { - body: { - bidid: '04db8629-179d-4bcd-acce-e54722969006', - cur: 'JPY', - ext: {}, - id: '5ebea288-f13a-4754-be6d-4ade66c68877', - seatbid: [ - { - bid: [ - { - adid: 'uqgbp4y0_OoqM1QOt', - adm: '
test
', - adomain: ['test1.co.jp'], - attr: [], - bundle: 'com.test1.android', - cat: ['IAB9'], - cid: '2196', - crid: 'ABCDE', - ext: { - imptrackers: ['https://uncn.jp/pb/2/view/test1'] - }, - h: 250, - id: '1', - impid: '216255f234b602', - iurl: 'https://assets.ucontent.net/test1.jpg', - price: 1.0017, - w: 300 - }, - { - adid: 'uqgbp4y0_uqjrNT7h_25512', - adm: '
test
', - adomain: ['test1.co.jp'], - attr: ['6'], - bundle: 'com.test1.android', - cat: ['IAB9'], - cid: '2196', - crid: 'abcde', - ext: { - imptrackers: ['https://uncn.jp/pb/2/view/test1'] - }, - h: 250, - id: '2', - impid: '31e2b28ced2475', - iurl: 'https://assets.ucontent.net/test1.jpg', - price: 0.9513, - w: 300 - } - ], - group: 0, - seat: '65' - }, - { - bid: [ - { - adid: 'uoNYC6II_eoySuXNi', - adm: '
test
', - adomain: ['test2.co.jp'], - attr: [], - bundle: 'jp.co.test2', - cat: ['IAB9'], - cid: '7315', - crid: 'XYZXYZ', - ext: { - imptrackers: ['https://uncn.jp/pb/2/view/test2'] - }, - h: 250, - id: '3', - impid: '40a333e047a9bd', - iurl: 'https://assets.ucontent.net/test2.jpg', - price: 0.674, - w: 300 - } - ], - group: 0, - seat: '274' - } - ], - units: 0 - }, - headers: {} -}; - -const request = { - method: 'POST', - url: 'https://ds.uncn.jp/pb/0/bid.json', - data: - '{"id":"5ebea288-f13a-4754-be6d-4ade66c68877","at":1,"imp":[{"id":"216255f234b602","banner":{"w":300,"h":250},"format":[{"w":300,"h":250},{"w":336,"h":280}],"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-0"},{"id":"31e2b28ced2475","banner":{"w":"300","h":"250"},"format":[{"w":"300","h":"250"}],"secure":1,"bidfloor":0"tagid":"/19968336/header-bid-tag-1"},{"id":"40a333e047a9bd","banner":{"w":300,"h":250},"format":[{"w":300,"h":250}],"secure":1,"bidfloor":0,"tagid":"/19968336/header-bid-tag-2"}],"cur":"JPY","site":{"id":"uni-corn.net","publisher":{"id":12345},"domain":"uni-corn.net","page":"https://uni-corn.net/","ref":"https://uni-corn.net/"},"device":{"language":"ja","ua":"Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A5000) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36"},"user":{"id":"69d9e1c2-801e-4901-a665-fad467550fec"},"bcat":[],"source":{"ext":{"stype":"prebid_uncn","bidder":"unicorn","prebid_version":"1.0"}}}' -}; - -const interpretedBids = [ - { - requestId: '216255f234b602', - cpm: 1.0017, - width: 300, - height: 250, - ad: '
test
', - ttl: 1000, - creativeId: 'ABCDE', - netRevenue: false, - currency: 'JPY' - }, - { - requestId: '31e2b28ced2475', - cpm: 0.9513, - width: 300, - height: 250, - ad: '
test
', - ttl: 1000, - creativeId: 'abcde', - netRevenue: false, - currency: 'JPY' - }, - { - requestId: '40a333e047a9bd', - cpm: 0.674, - width: 300, - height: 250, - ad: '
test
', - ttl: 1000, - creativeId: 'XYZXYZ', - netRevenue: false, - currency: 'JPY' - } -]; - -describe('unicornBidAdapterTest', () => { - describe('isBidRequestValid', () => { - it('isBidRequestValid', () => { - expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); - expect(spec.isBidRequestValid(bidRequests[1])).to.equal(false); - expect(spec.isBidRequestValid(bidRequests[2])).to.equal(false); - }); - }); - - describe('buildBidRequest', () => { - it('buildBidRequest', () => { - const req = spec.buildRequests(validBidRequests, bidderRequest); - const removeUntestableAttrs = data => { - delete data['device']; - delete data['site']['domain']; - delete data['site']['page']; - delete data['id']; - data['imp'].forEach(imp => { - delete imp['id']; - }) - delete data['user']['id']; - return data; - }; - const uid = JSON.parse(req.data)['user']['id']; - const reqData = removeUntestableAttrs(JSON.parse(req.data)); - const openRTBRequestData = removeUntestableAttrs(openRTBRequest); - assert.deepStrictEqual(reqData, openRTBRequestData); - const req2 = spec.buildRequests(validBidRequests, bidderRequest); - const uid2 = JSON.parse(req2.data)['user']['id']; - assert.deepStrictEqual(uid, uid2); - }); - }); - - describe('interpretResponse', () => { - it('interpretResponse', () => { - const bids = spec.interpretResponse(serverResponse, request); - assert.deepStrictEqual(bids, interpretedBids); - }); - it('interpretResponseEmptyString', () => { - const bids = spec.interpretResponse('', request); - assert.deepStrictEqual(bids, []); - }); - it('interpretResponseEmptyArray', () => { - const bids = spec.interpretResponse([], request); - assert.deepStrictEqual(bids, []); - }); - }); -}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 420d2ddce91..9f48127b6f9 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -25,7 +25,6 @@ import { import {server} from 'test/mocks/xhr.js'; import find from 'core-js-pure/features/array/find.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; -import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem.js'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; import {id5IdSubmodule} from 'modules/id5IdSystem.js'; import {identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; @@ -36,7 +35,7 @@ import {netIdSubmodule} from 'modules/netIdSystem.js'; import {nextrollIdSubmodule} from 'modules/nextrollIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; -import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; +import {sharedIdSystemSubmodule} from 'modules/sharedIdSystem.js'; import {haloIdSubmodule} from 'modules/haloIdSystem.js'; import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js'; import {criteoIdSubmodule} from 'modules/criteoIdSystem.js'; @@ -137,7 +136,7 @@ describe('User ID', function () { let pubcid = coreStorage.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); @@ -171,7 +170,7 @@ describe('User ID', function () { let pubcid1; let pubcid2; - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { @@ -191,7 +190,7 @@ describe('User ID', function () { }); }); - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { @@ -218,7 +217,7 @@ describe('User ID', function () { let adUnits = [getAdUnitMock()]; let innerAdUnits; - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie'])); requestBidsHook((config) => { @@ -246,7 +245,7 @@ describe('User ID', function () { let customConfig = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); customConfig = addConfig(customConfig, 'params', {extend: true}); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule]); init(config); config.setConfig(customConfig); requestBidsHook((config) => { @@ -273,7 +272,7 @@ describe('User ID', function () { let customConfig = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); customConfig = addConfig(customConfig, 'params', {create: false}); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule]); init(config); config.setConfig(customConfig); requestBidsHook((config) => { @@ -290,7 +289,7 @@ describe('User ID', function () { }); it('pbjs.getUserIds', function () { - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig({ userSync: { @@ -305,7 +304,7 @@ describe('User ID', function () { }); it('pbjs.getUserIdsAsEids', function () { - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig({ userSync: { @@ -440,14 +439,14 @@ describe('User ID', function () { }); it('fails initialization if opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); 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'); }); it('initializes if no opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); @@ -466,7 +465,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -474,14 +473,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -492,7 +491,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -509,7 +508,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -531,849 +530,731 @@ describe('User ID', function () { }); it('config with 21 configurations should result in 21 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [{ - name: 'pubProvidedId' - }, { - name: 'pubCommonId', value: {'pubcid': '11111'} - }, { - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }, { - name: 'id5Id', - storage: {name: 'id5id', type: 'cookie'} - }, { - name: 'identityLink', - storage: {name: 'idl_env', type: 'cookie'} - }, { - name: 'liveIntentId', - storage: {name: '_li_pbid', type: 'cookie'} - }, { - name: 'britepoolId', - value: {'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'} - }, { - name: 'netId', - storage: {name: 'netId', type: 'cookie'} - }, { - name: 'nextrollId' - }, { - name: 'sharedId', - storage: {name: 'sharedid', type: 'cookie'} - }, { - name: 'intentIqId', - storage: {name: 'intentIqId', type: 'cookie'} - }, { - name: 'haloId', - storage: {name: 'haloId', type: 'cookie'} - }, { - name: 'zeotapIdPlus' - }, { - name: 'criteo' - }, { - name: 'mwOpenLinkId' - }, { - name: 'tapadId', - storage: {name: 'tapad_id', type: 'cookie'} - }, { - name: 'uid2' - }, { - name: 'admixerId', - storage: {name: 'admixerId', type: 'cookie'} - }, { - name: 'deepintentId', - storage: {name: 'deepintentId', type: 'cookie'} - }, { - name: 'flocId' - }, { - name: 'dmdId', - storage: {name: 'dmdId', type: 'cookie'} - }] - } + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + it('config with 14 configurations should result in 14 submodules add', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'pubProvidedId' + }, { + name: 'pubCommonId', value: {'pubcid': '11111'} + }, { + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }, { + name: 'id5Id', + storage: {name: 'id5id', type: 'cookie'} + }, { + name: 'identityLink', + storage: {name: 'idl_env', type: 'cookie'} + }, { + name: 'liveIntentId', + storage: {name: '_li_pbid', type: 'cookie'} + }, { + name: 'britepoolId', + value: {'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'} + }, { + name: 'netId', + storage: {name: 'netId', type: 'cookie'} + }, { + name: 'nextrollId' + }, { + name: 'intentIqId', + storage: {name: 'intentIqId', type: 'cookie'} + }, { + name: 'haloId', + storage: {name: 'haloId', type: 'cookie'} + }, { + name: 'zeotapIdPlus' + }, { + name: 'criteo' + }, { + name: 'mwOpenLinkId' + }, { + name: 'tapadId', + storage: {name: 'tapad_id', type: 'cookie'} + }, { + name: 'uid2' + }, { + name: 'admixerId', + storage: {name: 'admixerId', type: 'cookie'} + }, { + name: 'deepintentId', + storage: {name: 'deepintentId', type: 'cookie'} + }, { + name: 'flocId' + }, { + name: 'dmdId', + storage: {name: 'dmdId', type: 'cookie'} + }] + } + }); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 20 submodules'); }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 21 submodules'); - }); - it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + it('config syncDelay updates module correctly', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 99, - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } + init(config); + config.setConfig({ + userSync: { + syncDelay: 99, + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } + }); + expect(syncDelay).to.equal(99); }); - expect(syncDelay).to.equal(99); - }); - it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - auctionDelay: 100, - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } + it('config auctionDelay updates module correctly', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + auctionDelay: 100, + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } + }); + expect(auctionDelay).to.equal(100); }); - expect(auctionDelay).to.equal(100); - }); - it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - auctionDelay: '', - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } + it('config auctionDelay defaults to 0 if not a number', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + auctionDelay: '', + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } + }); + expect(auctionDelay).to.equal(0); }); - expect(auctionDelay).to.equal(0); }); - }); - describe('auction and user sync delays', function () { - let sandbox; - let adUnits; - let mockIdCallback; - let auctionSpy; + describe('auction and user sync delays', function () { + let sandbox; + let adUnits; + let mockIdCallback; + let auctionSpy; - beforeEach(function () { - sandbox = sinon.createSandbox(); - sandbox.stub(global, 'setTimeout').returns(2); - sandbox.stub(global, 'clearTimeout'); - sandbox.stub(events, 'on'); - sandbox.stub(coreStorage, 'getCookie'); + beforeEach(function () { + sandbox = sinon.createSandbox(); + sandbox.stub(global, 'setTimeout').returns(2); + sandbox.stub(global, 'clearTimeout'); + sandbox.stub(events, 'on'); + sandbox.stub(coreStorage, 'getCookie'); - // remove cookie - coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + // remove cookie + coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); - adUnits = [getAdUnitMock()]; + adUnits = [getAdUnitMock()]; - auctionSpy = sandbox.spy(); - mockIdCallback = sandbox.stub(); - const mockIdSystem = { - name: 'mockId', - decode: function (value) { - return { - 'mid': value['MOCKID'] - }; - }, - getId: function () { - const storedId = coreStorage.getCookie('MOCKID'); - if (storedId) { - return {id: {'MOCKID': storedId}}; + auctionSpy = sandbox.spy(); + mockIdCallback = sandbox.stub(); + const mockIdSystem = { + name: 'mockId', + decode: function (value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: function () { + const storedId = coreStorage.getCookie('MOCKID'); + if (storedId) { + return {id: {'MOCKID': storedId}}; + } + return {callback: mockIdCallback}; } - return {callback: mockIdCallback}; - } - }; - - init(config); + }; - attachIdSystem(mockIdSystem, true); - }); + init(config); - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - config.resetConfig(); - sandbox.restore(); - }); + attachIdSystem(mockIdSystem, true); + }); - it('delays auction if auctionDelay is set, timing out at auction delay', function () { - config.setConfig({ - userSync: { - auctionDelay: 33, - syncDelay: 77, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } + afterEach(function () { + $$PREBID_GLOBAL$$.requestBids.removeAll(); + config.resetConfig(); + sandbox.restore(); }); - requestBidsHook(auctionSpy, {adUnits}); + it('delays auction if auctionDelay is set, timing out at auction delay', function () { + config.setConfig({ + userSync: { + auctionDelay: 33, + syncDelay: 77, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); - // check auction was delayed - global.clearTimeout.calledOnce.should.equal(false); - global.setTimeout.calledOnce.should.equal(true); - global.setTimeout.calledWith(sinon.match.func, 33); - auctionSpy.calledOnce.should.equal(false); + requestBidsHook(auctionSpy, {adUnits}); - // check ids were fetched - mockIdCallback.calledOnce.should.equal(true); + // check auction was delayed + global.clearTimeout.calledOnce.should.equal(false); + global.setTimeout.calledOnce.should.equal(true); + global.setTimeout.calledWith(sinon.match.func, 33); + auctionSpy.calledOnce.should.equal(false); - // callback to continue auction if timed out - global.setTimeout.callArg(0); - auctionSpy.calledOnce.should.equal(true); + // check ids were fetched + mockIdCallback.calledOnce.should.equal(true); - // does not call auction again once ids are synced - mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); - auctionSpy.calledOnce.should.equal(true); + // callback to continue auction if timed out + global.setTimeout.callArg(0); + auctionSpy.calledOnce.should.equal(true); - // no sync after auction ends - events.on.called.should.equal(false); - }); + // does not call auction again once ids are synced + mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); + auctionSpy.calledOnce.should.equal(true); - it('delays auction if auctionDelay is set, continuing auction if ids are fetched before timing out', function (done) { - config.setConfig({ - userSync: { - auctionDelay: 33, - syncDelay: 77, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } + // no sync after auction ends + events.on.called.should.equal(false); }); - requestBidsHook(auctionSpy, {adUnits}); + it('delays auction if auctionDelay is set, continuing auction if ids are fetched before timing out', function (done) { + config.setConfig({ + userSync: { + auctionDelay: 33, + syncDelay: 77, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); - // check auction was delayed - // global.setTimeout.calledOnce.should.equal(true); - global.clearTimeout.calledOnce.should.equal(false); - global.setTimeout.calledWith(sinon.match.func, 33); - auctionSpy.calledOnce.should.equal(false); + requestBidsHook(auctionSpy, {adUnits}); - // check ids were fetched - mockIdCallback.calledOnce.should.equal(true); + // check auction was delayed + // global.setTimeout.calledOnce.should.equal(true); + global.clearTimeout.calledOnce.should.equal(false); + global.setTimeout.calledWith(sinon.match.func, 33); + auctionSpy.calledOnce.should.equal(false); - // if ids returned, should continue auction - mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); - auctionSpy.calledOnce.should.equal(true); + // check ids were fetched + mockIdCallback.calledOnce.should.equal(true); - // check ids were copied to bids - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('1234'); - expect(bid.userIdAsEids.length).to.equal(0);// "mid" is an un-known submodule for USER_IDS_CONFIG in eids.js - }); - done(); - }); + // if ids returned, should continue auction + mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); + auctionSpy.calledOnce.should.equal(true); - // no sync after auction ends - events.on.called.should.equal(false); - }); + // check ids were copied to bids + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.mid'); + expect(bid.userId.mid).to.equal('1234'); + expect(bid.userIdAsEids.length).to.equal(0);// "mid" is an un-known submodule for USER_IDS_CONFIG in eids.js + }); + done(); + }); - it('does not delay auction if not set, delays id fetch after auction ends with syncDelay', function () { - config.setConfig({ - userSync: { - syncDelay: 77, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } + // no sync after auction ends + events.on.called.should.equal(false); }); - // check config has been set correctly - expect(auctionDelay).to.equal(0); - expect(syncDelay).to.equal(77); + it('does not delay auction if not set, delays id fetch after auction ends with syncDelay', function () { + config.setConfig({ + userSync: { + syncDelay: 77, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); - requestBidsHook(auctionSpy, {adUnits}); + // check config has been set correctly + expect(auctionDelay).to.equal(0); + expect(syncDelay).to.equal(77); - // should not delay auction - global.setTimeout.calledOnce.should.equal(false); - auctionSpy.calledOnce.should.equal(true); + requestBidsHook(auctionSpy, {adUnits}); - // check user sync is delayed after auction is ended - mockIdCallback.calledOnce.should.equal(false); - events.on.calledOnce.should.equal(true); - events.on.calledWith(CONSTANTS.EVENTS.AUCTION_END, sinon.match.func); + // should not delay auction + global.setTimeout.calledOnce.should.equal(false); + auctionSpy.calledOnce.should.equal(true); - // once auction is ended, sync user ids after delay - events.on.callArg(1); - global.setTimeout.calledOnce.should.equal(true); - global.setTimeout.calledWith(sinon.match.func, 77); - mockIdCallback.calledOnce.should.equal(false); + // check user sync is delayed after auction is ended + mockIdCallback.calledOnce.should.equal(false); + events.on.calledOnce.should.equal(true); + events.on.calledWith(CONSTANTS.EVENTS.AUCTION_END, sinon.match.func); - // once sync delay is over, ids should be fetched - global.setTimeout.callArg(0); - mockIdCallback.calledOnce.should.equal(true); - }); + // once auction is ended, sync user ids after delay + events.on.callArg(1); + global.setTimeout.calledOnce.should.equal(true); + global.setTimeout.calledWith(sinon.match.func, 77); + mockIdCallback.calledOnce.should.equal(false); - it('does not delay user id sync after auction ends if set to 0', function () { - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } + // once sync delay is over, ids should be fetched + global.setTimeout.callArg(0); + mockIdCallback.calledOnce.should.equal(true); }); - expect(syncDelay).to.equal(0); + it('does not delay user id sync after auction ends if set to 0', function () { + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); - requestBidsHook(auctionSpy, {adUnits}); + expect(syncDelay).to.equal(0); - // auction should not be delayed - global.setTimeout.calledOnce.should.equal(false); - auctionSpy.calledOnce.should.equal(true); + requestBidsHook(auctionSpy, {adUnits}); - // sync delay after auction is ended - mockIdCallback.calledOnce.should.equal(false); - events.on.calledOnce.should.equal(true); - events.on.calledWith(CONSTANTS.EVENTS.AUCTION_END, sinon.match.func); + // auction should not be delayed + global.setTimeout.calledOnce.should.equal(false); + auctionSpy.calledOnce.should.equal(true); - // once auction is ended, if no sync delay, fetch ids - events.on.callArg(1); - global.setTimeout.calledOnce.should.equal(false); - mockIdCallback.calledOnce.should.equal(true); - }); + // sync delay after auction is ended + mockIdCallback.calledOnce.should.equal(false); + events.on.calledOnce.should.equal(true); + events.on.calledWith(CONSTANTS.EVENTS.AUCTION_END, sinon.match.func); - it('does not delay auction if there are no ids to fetch', function () { - coreStorage.getCookie.withArgs('MOCKID').returns('123456778'); - config.setConfig({ - userSync: { - auctionDelay: 33, - syncDelay: 77, - userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }] - } + // once auction is ended, if no sync delay, fetch ids + events.on.callArg(1); + global.setTimeout.calledOnce.should.equal(false); + mockIdCallback.calledOnce.should.equal(true); }); - requestBidsHook(auctionSpy, {adUnits}); + it('does not delay auction if there are no ids to fetch', function () { + coreStorage.getCookie.withArgs('MOCKID').returns('123456778'); + config.setConfig({ + userSync: { + auctionDelay: 33, + syncDelay: 77, + userIds: [{ + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }] + } + }); + + requestBidsHook(auctionSpy, {adUnits}); - global.setTimeout.calledOnce.should.equal(false); - auctionSpy.calledOnce.should.equal(true); - mockIdCallback.calledOnce.should.equal(false); + global.setTimeout.calledOnce.should.equal(false); + auctionSpy.calledOnce.should.equal(true); + mockIdCallback.calledOnce.should.equal(false); - // no sync after auction ends - events.on.called.should.equal(false); + // no sync after auction ends + events.on.called.should.equal(false); + }); }); - }); - describe('Request bids hook appends userId to bid objs in adapters', function () { - let adUnits; + describe('Request bids hook appends userId to bid objs in adapters', function () { + let adUnits; - beforeEach(function () { - adUnits = [getAdUnitMock()]; - }); + beforeEach(function () { + adUnits = [getAdUnitMock()]; + }); - it('test hook from pubcommonid cookie', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); + it('test hook from pubcommonid cookie', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'pubcid.org', + uids: [{id: 'testpubcid', atype: 1}] + }); }); - - // verify no sharedid was added - expect(bid.userId).to.not.have.property('sharedid'); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from pubcommonid html5', function (done) { + it('test hook from pubcommonid html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('pubcid', 'testpubcid'); - localStorage.setItem('pubcid_exp', new Date(Date.now() + 100000).toUTCString()); + localStorage.setItem('pubcid', 'testpubcid'); + localStorage.setItem('pubcid_exp', new Date(Date.now() + 100000).toUTCString()); - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'html5'])); + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'pubcid.org', + uids: [{id: 'testpubcid', atype: 1}] + }); }); - - // verify no sharedid was added - expect(bid.userId).to.not.have.property('sharedid'); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; }); - }); - localStorage.removeItem('pubcid'); - localStorage.removeItem('pubcid_exp'); - done(); - }, {adUnits}); - }); - - it('test hook from pubcommonid config value object', function (done) { - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(getConfigValueMock('pubCommonId', {'pubcidvalue': 'testpubcidvalue'})); + localStorage.removeItem('pubcid'); + localStorage.removeItem('pubcid_exp'); + done(); + }, {adUnits}); + }); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); - expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); - expect(bid.userIdAsEids.length).to.equal(0);// "pubcidvalue" is an un-known submodule for USER_IDS_CONFIG in eids.js + it('test hook from pubcommonid config value object', function (done) { + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + config.setConfig(getConfigValueMock('pubCommonId', {'pubcidvalue': 'testpubcidvalue'})); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); + expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); + expect(bid.userIdAsEids.length).to.equal(0);// "pubcidvalue" is an un-known submodule for USER_IDS_CONFIG in eids.js + }); }); - }); - done(); - }, {adUnits}); - }); + done(); + }, {adUnits}); + }); - it('test hook from UnifiedId html5', function (done) { + it('test hook from UnifiedId html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); - localStorage.setItem('unifiedid_alt_exp', ''); + localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); + localStorage.setItem('unifiedid_alt_exp', ''); - setSubmoduleRegistry([unifiedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['unifiedId', 'unifiedid_alt', 'html5'])); + setSubmoduleRegistry([unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['unifiedId', 'unifiedid_alt', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal('testunifiedid_alt'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'adserver.org', - uids: [{id: 'testunifiedid_alt', atype: 1, ext: {rtiPartner: 'TDID'}}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid_alt'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'adserver.org', + uids: [{id: 'testunifiedid_alt', atype: 1, ext: {rtiPartner: 'TDID'}}] + }); }); }); - }); - localStorage.removeItem('unifiedid_alt'); - localStorage.removeItem('unifiedid_alt_exp'); - done(); - }, {adUnits}); - }); + localStorage.removeItem('unifiedid_alt'); + localStorage.removeItem('unifiedid_alt_exp'); + done(); + }, {adUnits}); + }); - it('test hook from identityLink html5', function (done) { + it('test hook from identityLink html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); - localStorage.setItem('idl_env_exp', ''); + localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); + localStorage.setItem('idl_env_exp', ''); - setSubmoduleRegistry([identityLinkSubmodule]); - init(config); - config.setConfig(getConfigMock(['identityLink', 'idl_env', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.idl_env'); - expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveramp.com', - uids: [{id: 'AiGNC8Z5ONyZKSpIPf', atype: 3}] + setSubmoduleRegistry([identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['identityLink', 'idl_env', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveramp.com', + uids: [{id: 'AiGNC8Z5ONyZKSpIPf', atype: 3}] + }); }); }); - }); - localStorage.removeItem('idl_env'); - localStorage.removeItem('idl_env_exp'); - done(); - }, {adUnits}); - }); + localStorage.removeItem('idl_env'); + localStorage.removeItem('idl_env_exp'); + done(); + }, {adUnits}); + }); - it('test hook from identityLink cookie', function (done) { - coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); + it('test hook from identityLink cookie', function (done) { + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([identityLinkSubmodule]); - init(config); - config.setConfig(getConfigMock(['identityLink', 'idl_env', 'cookie'])); + setSubmoduleRegistry([identityLinkSubmodule]); + init(config); + config.setConfig(getConfigMock(['identityLink', 'idl_env', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.idl_env'); - expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveramp.com', - uids: [{id: 'AiGNC8Z5ONyZKSpIPf', atype: 3}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveramp.com', + uids: [{id: 'AiGNC8Z5ONyZKSpIPf', atype: 3}] + }); }); }); - }); - coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from criteoIdModule cookie', function (done) { - coreStorage.setCookie('storage_bidid', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 100000).toUTCString())); + it('test hook from criteoIdModule cookie', function (done) { + coreStorage.setCookie('storage_bidid', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([criteoIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['criteo', 'storage_bidid', 'cookie'])); + setSubmoduleRegistry([criteoIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['criteo', 'storage_bidid', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.criteoId'); - expect(bid.userId.criteoId).to.equal('test_bidid'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'criteo.com', - uids: [{id: 'test_bidid', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'criteo.com', + uids: [{id: 'test_bidid', atype: 1}] + }); }); }); - }); - coreStorage.setCookie('storage_bidid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('storage_bidid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from tapadIdModule cookie', function (done) { - coreStorage.setCookie('tapad_id', 'test-tapad-id', (new Date(Date.now() + 100000).toUTCString())); + it('test hook from tapadIdModule cookie', function (done) { + coreStorage.setCookie('tapad_id', 'test-tapad-id', (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([tapadIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['tapadId', 'tapad_id', 'cookie'])); + setSubmoduleRegistry([tapadIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['tapadId', 'tapad_id', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.tapadId'); - expect(bid.userId.tapadId).to.equal('test-tapad-id'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'tapad.com', - uids: [{id: 'test-tapad-id', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.tapadId'); + expect(bid.userId.tapadId).to.equal('test-tapad-id'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'tapad.com', + uids: [{id: 'test-tapad-id', atype: 1}] + }); }); - }); - }) - coreStorage.setCookie('tapad_id', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + }) + coreStorage.setCookie('tapad_id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from liveIntentId html5', function (done) { + it('test hook from liveIntentId html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier'})); - localStorage.setItem('_li_pbid_exp', ''); + localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier'})); + localStorage.setItem('_li_pbid_exp', ''); - setSubmoduleRegistry([liveIntentIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.lipb'); - expect(bid.userId.lipb.lipbid).to.equal('random-ls-identifier'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveintent.com', - uids: [{id: 'random-ls-identifier', atype: 3}] + setSubmoduleRegistry([liveIntentIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.lipb'); + expect(bid.userId.lipb.lipbid).to.equal('random-ls-identifier'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveintent.com', + uids: [{id: 'random-ls-identifier', atype: 3}] + }); }); }); - }); - localStorage.removeItem('_li_pbid'); - localStorage.removeItem('_li_pbid_exp'); - done(); - }, {adUnits}); - }); + localStorage.removeItem('_li_pbid'); + localStorage.removeItem('_li_pbid_exp'); + done(); + }, {adUnits}); + }); - it('test hook from liveIntentId cookie', function (done) { - coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); + it('test hook from liveIntentId cookie', function (done) { + coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([liveIntentIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); + setSubmoduleRegistry([liveIntentIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.lipb'); - expect(bid.userId.lipb.lipbid).to.equal('random-cookie-identifier'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveintent.com', - uids: [{id: 'random-cookie-identifier', atype: 3}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.lipb'); + expect(bid.userId.lipb.lipbid).to.equal('random-cookie-identifier'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveintent.com', + uids: [{id: 'random-cookie-identifier', atype: 3}] + }); }); }); - }); - coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from sharedId html5', function (done) { - // simulate existing browser local storage values - localStorage.setItem('sharedid', JSON.stringify({'id': 'test_sharedId', 'ts': 1590525289611})); - localStorage.setItem('sharedid_exp', ''); + it('eidPermissions fun with bidders', function (done) { + coreStorage.setCookie('pubcid', 'test222', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['sharedId', 'sharedid', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.have.deep.nested.property('id'); - expect(bid.userId.sharedid).to.have.deep.nested.property('third'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId', - third: 'test_sharedId' - }); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1, - ext: { - third: 'test_sharedId' + setSubmoduleRegistry([sharedIdSystemSubmodule]); + let eidPermissions; + getPrebidInternal().setEidPermissions = function (newEidPermissions) { + eidPermissions = newEidPermissions; + } + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [ + { + name: 'pubCommonId', + bidders: [ + 'sampleBidder' + ], + storage: { + type: 'cookie', + name: 'pubcid', + expires: 28 } - }] - }); - }); + } + ] + } }); - localStorage.removeItem('sharedid'); - localStorage.removeItem('sharedid_exp'); - done(); - }, {adUnits}); - }); - it('test hook from sharedId html5 (id not synced)', function (done) { - // simulate existing browser local storage values - localStorage.setItem('sharedid', JSON.stringify({'id': 'test_sharedId', 'ns': true, 'ts': 1590525289611})); - localStorage.setItem('sharedid_exp', ''); - - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['sharedId', 'sharedid', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.have.deep.nested.property('id'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId' - }); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1 - }] + requestBidsHook(function () { + expect(eidPermissions).to.deep.equal( + [ + { + bidders: [ + 'sampleBidder' + ], + source: 'pubcid.org' + } + ] + ); + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + if (bid.bidder === 'sampleBidder') { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('test222'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'pubcid.org', + uids: [ + { + id: 'test222', + atype: 1 + } + ] + }); + } + if (bid.bidder === 'anotherSampleBidder') { + expect(bid).to.not.have.deep.nested.property('userId.pubcid'); + expect(bid).to.not.have.property('userIdAsEids'); + } }); }); - }); - localStorage.removeItem('sharedid'); - localStorage.removeItem('sharedid_exp'); - done(); - }, {adUnits}); - }); - it('test hook from sharedId cookie', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test_sharedId', - 'ts': 1590525289611 - }), (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + getPrebidInternal().setEidPermissions = undefined; + done(); + }, {adUnits}); + }); - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); + it('eidPermissions fun without bidders', function (done) { + coreStorage.setCookie('pubcid', 'test222', new Date(Date.now() + 5000).toUTCString()); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.have.deep.nested.property('id'); - expect(bid.userId.sharedid).to.have.deep.nested.property('third'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId', - third: 'test_sharedId' - }); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1, - ext: { - third: 'test_sharedId' + setSubmoduleRegistry([sharedIdSystemSubmodule]); + let eidPermissions; + getPrebidInternal().setEidPermissions = function (newEidPermissions) { + eidPermissions = newEidPermissions; + } + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [ + { + name: 'pubCommonId', + storage: { + type: 'cookie', + name: 'pubcid', + expires: 28 } - }] - }); - }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - it('test hook from sharedId cookie (id not synced) ', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test_sharedId', - 'ns': true, - 'ts': 1590525289611 - }), (new Date(Date.now() + 100000).toUTCString())); - - setSubmoduleRegistry([sharedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.have.deep.nested.property('id'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId' - }); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [{ - id: 'test_sharedId', - atype: 1 - }] - }); - }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('eidPermissions fun with bidders', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test222', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([sharedIdSubmodule]); - let eidPermissions; - getPrebidInternal().setEidPermissions = function (newEidPermissions) { - eidPermissions = newEidPermissions; - } - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'sharedId', - bidders: [ - 'sampleBidder' - ], - storage: { - type: 'cookie', - name: 'sharedid', - expires: 28 } - } - ] - } - }); + ] + } + }); - requestBidsHook(function () { - expect(eidPermissions).to.deep.equal( - [ - { - bidders: [ - 'sampleBidder' - ], - source: 'sharedid.org' - } - ] - ); - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - if (bid.bidder === 'sampleBidder') { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid.id).to.equal('test222'); + requestBidsHook(function () { + expect(eidPermissions).to.deep.equal( + [] + ); + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('test222'); expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', + source: 'pubcid.org', uids: [ { id: 'test222', - atype: 1, - ext: { - third: 'test222' - } - } - ] + atype: 1 + }] }); - } - if (bid.bidder === 'anotherSampleBidder') { - expect(bid).to.not.have.deep.nested.property('userId.sharedid'); - expect(bid).to.not.have.property('userIdAsEids'); - } + }); }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - getPrebidInternal().setEidPermissions = undefined; - done(); - }, {adUnits}); - }); - - it('eidPermissions fun without bidders', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test222', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); + getPrebidInternal().setEidPermissions = undefined; + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - setSubmoduleRegistry([sharedIdSubmodule]); - let eidPermissions; - getPrebidInternal().setEidPermissions = function (newEidPermissions) { - eidPermissions = newEidPermissions; - } - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'sharedId', - storage: { - type: 'cookie', - name: 'sharedid', - expires: 28 + it('test hook from pubProvidedId config params', function (done) { + setSubmoduleRegistry([pubProvidedIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'pubProvidedId', + params: { + eids: [{ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }, { + source: 'id-partner.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'dmp' + } + }] + }], + eidsFunction: function () { + return [{ + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } + }] + }] + } } } - ] - } - }); - - requestBidsHook(function () { - expect(eidPermissions).to.deep.equal( - [] - ); - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid.id).to.equal('test222'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'sharedid.org', - uids: [ - { - id: 'test222', - atype: 1, - ext: { - third: 'test222' - } - }] - }); - }); + ] + } }); - getPrebidInternal().setEidPermissions = undefined; - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - it('test hook from pubProvidedId config params', function (done) { - setSubmoduleRegistry([pubProvidedIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [{ - name: 'pubProvidedId', - params: { - eids: [{ + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubProvidedId'); + expect(bid.userId.pubProvidedId).to.deep.equal([{ source: 'example.com', uids: [{ id: 'value read from cookie or local storage', @@ -1389,1537 +1270,1122 @@ describe('User ID', function () { stype: 'dmp' } }] - }], - eidsFunction: function () { - return [{ - source: 'provider.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'sha256email' - } - }] + }, { + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } }] - } - } - } - ] - } - }); + }]); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.pubProvidedId'); - expect(bid.userId.pubProvidedId).to.deep.equal([{ - source: 'example.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'ppuid' - } - }] - }, { - source: 'id-partner.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'dmp' - } - }] - }, { - source: 'provider.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'sha256email' - } - }] - }]); - - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'example.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'ppuid' - } - }] - }); - expect(bid.userIdAsEids[2]).to.deep.equal({ - source: 'provider.com', - uids: [{ - id: 'value read from cookie or local storage', - ext: { - stype: 'sha256email' - } - }] + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'example.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'ppuid' + } + }] + }); + expect(bid.userIdAsEids[2]).to.deep.equal({ + source: 'provider.com', + uids: [{ + id: 'value read from cookie or local storage', + ext: { + stype: 'sha256email' + } + }] + }); }); }); - }); - done(); - }, {adUnits}); - }); + done(); + }, {adUnits}); + }); - it('test hook from liveIntentId html5', function (done) { + it('test hook from liveIntentId html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier', 'segments': ['123']})); - localStorage.setItem('_li_pbid_exp', ''); + localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier', 'segments': ['123']})); + localStorage.setItem('_li_pbid_exp', ''); - setSubmoduleRegistry([liveIntentIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.lipb'); - expect(bid.userId.lipb.lipbid).to.equal('random-ls-identifier'); - expect(bid.userId.lipb.segments).to.include('123'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveintent.com', - uids: [{id: 'random-ls-identifier', atype: 3}], - ext: {segments: ['123']} + setSubmoduleRegistry([liveIntentIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.lipb'); + expect(bid.userId.lipb.lipbid).to.equal('random-ls-identifier'); + expect(bid.userId.lipb.segments).to.include('123'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveintent.com', + uids: [{id: 'random-ls-identifier', atype: 3}], + ext: {segments: ['123']} + }); }); }); - }); - localStorage.removeItem('_li_pbid'); - localStorage.removeItem('_li_pbid_exp'); - done(); - }, {adUnits}); - }); - - it('test hook from liveIntentId cookie', function (done) { - coreStorage.setCookie('_li_pbid', JSON.stringify({ - 'unifiedId': 'random-cookie-identifier', - 'segments': ['123'] - }), (new Date(Date.now() + 100000).toUTCString())); + localStorage.removeItem('_li_pbid'); + localStorage.removeItem('_li_pbid_exp'); + done(); + }, {adUnits}); + }); - setSubmoduleRegistry([liveIntentIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); + it('test hook from liveIntentId cookie', function (done) { + coreStorage.setCookie('_li_pbid', JSON.stringify({ + 'unifiedId': 'random-cookie-identifier', + 'segments': ['123'] + }), (new Date(Date.now() + 100000).toUTCString())); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.lipb'); - expect(bid.userId.lipb.lipbid).to.equal('random-cookie-identifier'); - expect(bid.userId.lipb.segments).to.include('123'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'liveintent.com', - uids: [{id: 'random-cookie-identifier', atype: 3}], - ext: {segments: ['123']} + setSubmoduleRegistry([liveIntentIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['liveIntentId', '_li_pbid', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.lipb'); + expect(bid.userId.lipb.lipbid).to.equal('random-cookie-identifier'); + expect(bid.userId.lipb.segments).to.include('123'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'liveintent.com', + uids: [{id: 'random-cookie-identifier', atype: 3}], + ext: {segments: ['123']} + }); }); }); - }); - coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from britepoolid cookies', function (done) { + it('test hook from britepoolid cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([britepoolIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['britepoolId', 'britepoolid', 'cookie'])); + setSubmoduleRegistry([britepoolIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['britepoolId', 'britepoolid', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.britepoolid'); - expect(bid.userId.britepoolid).to.equal('279c0161-5152-487f-809e-05d7f7e653fd'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'britepool.com', - uids: [{id: '279c0161-5152-487f-809e-05d7f7e653fd', atype: 3}] - }); - }); - }); - coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('test hook from dmdId cookies', function (done) { - // simulate existing browser local storage values - coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([dmdIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['dmdId', 'dmdId', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.dmdId'); - expect(bid.userId.dmdId).to.equal('testdmdId'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'hcn.health', - uids: [{id: 'testdmdId', atype: 3}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.britepoolid'); + expect(bid.userId.britepoolid).to.equal('279c0161-5152-487f-809e-05d7f7e653fd'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'britepool.com', + uids: [{id: '279c0161-5152-487f-809e-05d7f7e653fd', atype: 3}] + }); }); }); - }); - coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from netId cookies', function (done) { + it('test hook from dmdId cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([netIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['netId', 'netId', 'cookie'])); + setSubmoduleRegistry([dmdIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['dmdId', 'dmdId', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.netId'); - expect(bid.userId.netId).to.equal('fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'netid.de', - uids: [{id: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.dmdId'); + expect(bid.userId.dmdId).to.equal('testdmdId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'hcn.health', + uids: [{id: 'testdmdId', atype: 3}] + }); }); }); - }); - coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from intentIqId cookies', function (done) { + it('test hook from netId cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('intentIqId', 'abcdefghijk', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([intentIqIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['intentIqId', 'intentIqId', 'cookie'])); + setSubmoduleRegistry([netIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['netId', 'netId', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.intentIqId'); - expect(bid.userId.intentIqId).to.equal('abcdefghijk'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'intentiq.com', - uids: [{id: 'abcdefghijk', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.netId'); + expect(bid.userId.netId).to.equal('fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'netid.de', + uids: [{id: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', atype: 1}] + }); }); }); - }); - coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from haloId html5', function (done) { + it('test hook from intentIqId cookies', function (done) { // simulate existing browser local storage values - localStorage.setItem('haloId', JSON.stringify({'haloId': 'random-ls-identifier'})); - localStorage.setItem('haloId_exp', ''); + coreStorage.setCookie('intentIqId', 'abcdefghijk', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([haloIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['haloId', 'haloId', 'html5'])); + setSubmoduleRegistry([intentIqIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['intentIqId', 'intentIqId', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.haloId'); - expect(bid.userId.haloId).to.equal('random-ls-identifier'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'audigent.com', - uids: [{id: 'random-ls-identifier', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.intentIqId'); + expect(bid.userId.intentIqId).to.equal('abcdefghijk'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'intentiq.com', + uids: [{id: 'abcdefghijk', atype: 1}] + }); }); }); - }); - localStorage.removeItem('haloId'); - localStorage.removeItem('haloId_exp', ''); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from merkleId cookies', function (done) { + it('test hook from haloId html5', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('merkleId', JSON.stringify({'pam_id': {'id': 'testmerkleId', 'keyID': 1}}), (new Date(Date.now() + 5000).toUTCString())); + localStorage.setItem('haloId', JSON.stringify({'haloId': 'random-ls-identifier'})); + localStorage.setItem('haloId_exp', ''); - setSubmoduleRegistry([merkleIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['merkleId', 'merkleId', 'cookie'])); + setSubmoduleRegistry([haloIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['haloId', 'haloId', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.merkleId'); - expect(bid.userId.merkleId).to.deep.equal({'id': 'testmerkleId', 'keyID': 1}); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'merkleinc.com', - uids: [{id: 'testmerkleId', atype: 3, ext: {keyID: 1}}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('random-ls-identifier'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'audigent.com', + uids: [{id: 'random-ls-identifier', atype: 1}] + }); }); }); - }); - coreStorage.setCookie('merkleId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + localStorage.removeItem('haloId'); + localStorage.removeItem('haloId_exp', ''); + done(); + }, {adUnits}); + }); - it('test hook from zeotapIdPlus cookies', function (done) { + it('test hook from merkleId cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('IDP', btoa(JSON.stringify('abcdefghijk')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('merkleId', JSON.stringify({'pam_id': {'id': 'testmerkleId', 'keyID': 1}}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([zeotapIdPlusSubmodule]); - init(config); - config.setConfig(getConfigMock(['zeotapIdPlus', 'IDP', 'cookie'])); + setSubmoduleRegistry([merkleIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['merkleId', 'merkleId', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.IDP'); - expect(bid.userId.IDP).to.equal('abcdefghijk'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'zeotap.com', - uids: [{id: 'abcdefghijk', atype: 1}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.merkleId'); + expect(bid.userId.merkleId).to.deep.equal({'id': 'testmerkleId', 'keyID': 1}); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'merkleinc.com', + uids: [{id: 'testmerkleId', atype: 3, ext: {keyID: 1}}] + }); }); }); - }); - coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('test hook from mwOpenLinkId cookies', function (done) { - // simulate existing browser local storage values - coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([mwOpenLinkIdSubModule]); - init(config); - config.setConfig(getConfigMock(['mwOpenLinkId', 'mwol', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); - expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); - }); - }); - coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('merkleId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from admixerId html5', function (done) { + it('test hook from zeotapIdPlus cookies', function (done) { // simulate existing browser local storage values - localStorage.setItem('admixerId', 'testadmixerId'); - localStorage.setItem('admixerId_exp', ''); - - setSubmoduleRegistry([admixerIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['admixerId', 'admixerId', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.admixerId'); - expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'admixer.net', - uids: [{id: 'testadmixerId', atype: 3}] - }); - }); - }); - localStorage.removeItem('admixerId'); - done(); - }, {adUnits}); - }); - - it('test hook from admixerId cookie', function (done) { - coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('abcdefghijk')), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([admixerIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['admixerId', 'admixerId', 'cookie'])); + setSubmoduleRegistry([zeotapIdPlusSubmodule]); + init(config); + config.setConfig(getConfigMock(['zeotapIdPlus', 'IDP', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.admixerId'); - expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'admixer.net', - uids: [{id: 'testadmixerId', atype: 3}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('abcdefghijk'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'zeotap.com', + uids: [{id: 'abcdefghijk', atype: 1}] + }); }); }); - }); - coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from deepintentId cookies', function (done) { + it('test hook from mwOpenLinkId cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([deepintentDpesSubmodule]); - init(config); - config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'cookie'])); + setSubmoduleRegistry([mwOpenLinkIdSubModule]); + init(config); + config.setConfig(getConfigMock(['mwOpenLinkId', 'mwol', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.deepintentId'); - expect(bid.userId.deepintentId).to.deep.equal('testdeepintentId'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'deepintent.com', - uids: [{id: 'testdeepintentId', atype: 3}] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); }); }); - }); - coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - it('test hook from deepintentId html5', function (done) { + it('test hook from admixerId html5', function (done) { // simulate existing browser local storage values - localStorage.setItem('deepintentId', 'testdeepintentId'); - localStorage.setItem('deepintentId_exp', ''); - - setSubmoduleRegistry([deepintentDpesSubmodule]); - init(config); - config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'html5'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.deepintentId'); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'deepintent.com', - uids: [{id: 'testdeepintentId', atype: 3}] - }); - }); - }); - localStorage.removeItem('deepintentId'); - done(); - }, {adUnits}); - }); - - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, sharedId, netId, haloId, Criteo, UID 2.0, admixerId, dmdId and mwOpenLinkId have data to pass', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test_sharedId', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], - ['unifiedId', 'unifiedid', 'cookie'], - ['id5Id', 'id5id', 'cookie'], - ['identityLink', 'idl_env', 'cookie'], - ['britepoolId', 'britepoolid', 'cookie'], - ['dmdId', 'dmdId', 'cookie'], - ['netId', 'netId', 'cookie'], - ['sharedId', 'sharedid', 'cookie'], - ['intentIqId', 'intentIqId', 'cookie'], - ['zeotapIdPlus', 'IDP', 'cookie'], - ['haloId', 'haloId', 'cookie'], - ['criteo', 'storage_criteo', 'cookie'], - ['mwOpenLinkId', 'mwol', 'cookie'], - ['tapadId', 'tapad_id', 'cookie'], - ['uid2', 'uid2id', 'cookie'], - ['admixerId', 'admixerId', 'cookie'], - ['deepintentId', 'deepintentId', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - // also check that UnifiedId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal('testunifiedid'); - // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id.uid'); - expect(bid.userId.id5id.uid).to.equal('testid5id'); - // check that identityLink id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.idl_env'); - expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); - // also check that britepoolId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.britepoolid'); - expect(bid.userId.britepoolid).to.equal('testbritepoolid'); - // also check that dmdID id was copied to bid - expect(bid).to.have.deep.nested.property('userId.dmdId'); - expect(bid.userId.dmdId).to.equal('testdmdId'); - // also check that netId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.netId'); - expect(bid.userId.netId).to.equal('testnetId'); - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId', - third: 'test_sharedId' - }); - // also check that intentIqId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.intentIqId'); - expect(bid.userId.intentIqId).to.equal('testintentIqId'); - // also check that zeotapIdPlus id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.IDP'); - expect(bid.userId.IDP).to.equal('zeotapId'); - // also check that haloId id was copied to bid - expect(bid).to.have.deep.nested.property('userId.haloId'); - expect(bid.userId.haloId).to.equal('testHaloId'); - // also check that criteo id was copied to bid - expect(bid).to.have.deep.nested.property('userId.criteoId'); - expect(bid.userId.criteoId).to.equal('test_bidid'); - // also check that mwOpenLink id was copied to bid - expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); - expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); - expect(bid.userId.uid2).to.deep.equal({ - id: 'Sample_AD_Token' - }); - // also check that criteo id was copied to bid - expect(bid).to.have.deep.nested.property('userId.admixerId'); - expect(bid.userId.admixerId).to.equal('testadmixerId'); - - // also check that deepintentId was copied to bid - expect(bid).to.have.deep.nested.property('userId.deepintentId'); - expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - - expect(bid.userIdAsEids.length).to.equal(16); - }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('deepintentId', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, dmdId, intentIqId, zeotapIdPlus, sharedId, criteo, netId, haloId, UID 2.0, admixerId and mwOpenLinkId have their modules added before and after init', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test_sharedId', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([]); - - // attaching before init - attachIdSystem(pubCommonIdSubmodule); + localStorage.setItem('admixerId', 'testadmixerId'); + localStorage.setItem('admixerId_exp', ''); - init(config); - - // attaching after init - attachIdSystem(unifiedIdSubmodule); - attachIdSystem(id5IdSubmodule); - attachIdSystem(identityLinkSubmodule); - attachIdSystem(britepoolIdSubmodule); - attachIdSystem(netIdSubmodule); - attachIdSystem(sharedIdSubmodule); - attachIdSystem(intentIqIdSubmodule); - attachIdSystem(zeotapIdPlusSubmodule); - attachIdSystem(haloIdSubmodule); - attachIdSystem(dmdIdSubmodule); - attachIdSystem(criteoIdSubmodule); - attachIdSystem(mwOpenLinkIdSubModule); - attachIdSystem(tapadIdSubmodule); - attachIdSystem(uid2IdSubmodule); - attachIdSystem(admixerIdSubmodule); - attachIdSystem(deepintentDpesSubmodule); - - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], - ['unifiedId', 'unifiedid', 'cookie'], - ['id5Id', 'id5id', 'cookie'], - ['identityLink', 'idl_env', 'cookie'], - ['britepoolId', 'britepoolid', 'cookie'], - ['netId', 'netId', 'cookie'], - ['sharedId', 'sharedid', 'cookie'], - ['intentIqId', 'intentIqId', 'cookie'], - ['zeotapIdPlus', 'IDP', 'cookie'], - ['haloId', 'haloId', 'cookie'], - ['dmdId', 'dmdId', 'cookie'], - ['criteo', 'storage_criteo', 'cookie'], - ['mwOpenLinkId', 'mwol', 'cookie'], - ['tapadId', 'tapad_id', 'cookie'], - ['uid2', 'uid2id', 'cookie'], - ['admixerId', 'admixerId', 'cookie'], - ['deepintentId', 'deepintentId', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - // also check that UnifiedId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); - // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id.uid'); - expect(bid.userId.id5id.uid).to.equal('testid5id'); - // also check that identityLink id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.idl_env'); - expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); - // also check that britepoolId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.britepoolid'); - expect(bid.userId.britepoolid).to.equal('testbritepoolid'); - // also check that britepoolId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.netId'); - expect(bid.userId.netId).to.equal('testnetId'); - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId', - third: 'test_sharedId' - }); - // also check that intentIqId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.intentIqId'); - expect(bid.userId.intentIqId).to.equal('testintentIqId'); - - // also check that zeotapIdPlus id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.IDP'); - expect(bid.userId.IDP).to.equal('zeotapId'); - // also check that haloId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.haloId'); - expect(bid.userId.haloId).to.equal('testHaloId'); - // also check that dmdId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.dmdId'); - expect(bid.userId.dmdId).to.equal('testdmdId'); - - // also check that criteo id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.criteoId'); - expect(bid.userId.criteoId).to.equal('test_bidid'); - - // also check that mwOpenLink id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); - expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123') - expect(bid.userId.uid2).to.deep.equal({ - id: 'Sample_AD_Token' + setSubmoduleRegistry([admixerIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['admixerId', 'admixerId', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.admixerId'); + expect(bid.userId.admixerId).to.equal('testadmixerId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'admixer.net', + uids: [{id: 'testadmixerId', atype: 3}] + }); }); - - // also check that admixerId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.admixerId'); - expect(bid.userId.admixerId).to.equal('testadmixerId'); - // also check that deepintentId was copied to bid - expect(bid).to.have.deep.nested.property('userId.deepintentId'); - expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - - expect(bid.userIdAsEids.length).to.equal(16); - }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('test hook when sharedId(opted out) have their modules added before and after init', function (done) { - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': '00000000000000000000000000', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([]); - init(config); - - attachIdSystem(sharedIdSubmodule); - - config.setConfig(getConfigMock(['sharedId', 'sharedid', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.userIdAsEids).to.be.undefined; - }); - }); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - - it('test sharedid enabled via pubcid cookie', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - - const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - addConfig(customCfg, 'params', {enableSharedId: true}); - config.setConfig(customCfg); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( - {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} - ); - // verify that the sharedid was also copied to bid - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.deep.equal({id: 'testsharedid'}); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.deep.equal( - {'source': 'sharedid.org', 'uids': [{'id': 'testsharedid', 'atype': 1}]} - ); - }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - - done(); - }, {adUnits}); - }); - - it('test sharedid disabled via pubcid cookie', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - - // pubCommonId's support for sharedId is off by default - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( - {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} - ); - // verify that the sharedid was not added to bid - expect(bid.userId).to.not.have.property('sharedid'); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; - }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - - done(); - }, {adUnits}); - }); - - it('test sharedid enabled via pubcid html5', function (done) { - coreStorage.setDataInLocalStorage('pubcid', 'testpubcid'); - coreStorage.setDataInLocalStorage('pubcid_exp', new Date(Date.now() + 5000).toUTCString()); - coreStorage.setDataInLocalStorage('pubcid_sharedid', 'testsharedid'); - coreStorage.setDataInLocalStorage('pubcid_sharedid_exp', new Date(Date.now() + 5000).toUTCString()); - - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - - const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'html5']); - addConfig(customCfg, 'params', {enableSharedId: true}); - config.setConfig(customCfg); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( - {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} - ); - // verify that the sharedid was also copied to bid - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.deep.equal({id: 'testsharedid'}); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.deep.equal( - {'source': 'sharedid.org', 'uids': [{'id': 'testsharedid', 'atype': 1}]} - ); - }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - - coreStorage.removeDataFromLocalStorage('pubcid'); - coreStorage.removeDataFromLocalStorage('pubcid_exp'); - coreStorage.removeDataFromLocalStorage('pubcid_sharedid'); - coreStorage.removeDataFromLocalStorage('pubcid_sharedid_exp'); - done(); - }, {adUnits}); - }); - - it('test expired sharedid via pubcid html5', function (done) { - coreStorage.setDataInLocalStorage('pubcid', 'testpubcid'); - coreStorage.setDataInLocalStorage('pubcid_exp', new Date(Date.now() + 5000).toUTCString()); - - // set sharedid to expired already - coreStorage.setDataInLocalStorage('pubcid_sharedid', 'testsharedid'); - coreStorage.setDataInLocalStorage('pubcid_sharedid_exp', new Date(Date.now() - 100).toUTCString()); - - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - - const customCfg = getConfigMock(['pubCommonId', 'pubcid', 'html5']); - addConfig(customCfg, 'params', {enableSharedId: true}); - config.setConfig(customCfg); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( - {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} - ); - // verify that the sharedid was not added - expect(bid.userId).to.not.have.property('sharedid'); - expect(findEid(bid.userIdAsEids, 'sharedid.org')).to.be.undefined; - }); - }); - - coreStorage.removeDataFromLocalStorage('pubcid'); - coreStorage.removeDataFromLocalStorage('pubcid_exp'); - coreStorage.removeDataFromLocalStorage('pubcid_sharedid'); - coreStorage.removeDataFromLocalStorage('pubcid_sharedid_exp'); - done(); - }, {adUnits}); - }); - - it('test pubcid coexisting with sharedid', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('pubcid_sharedid', 'test111', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test222', - 'ts': 1590525289611 - }), (new Date(Date.now() + 5000).toUTCString())); - - // When both pubcommon and sharedid are configured, pubcommon are invoked first due to - // module loading order. This mean the output from the primary sharedid module will overwrite - // the one in pubcommon. - - setSubmoduleRegistry([pubCommonIdSubmodule, sharedIdSubmodule]); - init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], - ['sharedId', 'sharedid', 'cookie'], - )); - - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - expect(findEid(bid.userIdAsEids, 'pubcid.org')).to.deep.equal( - {'source': 'pubcid.org', 'uids': [{'id': 'testpubcid', 'atype': 1}]} - ); - // verify that the sharedid was also copied to bid - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid.id).to.equal('test222'); - expect(findEid(bid.userIdAsEids, 'sharedid.org').uids[0].id).to.equal('test222'); }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - - done(); - }, {adUnits}); - }); + localStorage.removeItem('admixerId'); + done(); + }, {adUnits}); + }); - it('test hook from UID2 cookie', function (done) { - coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + it('test hook from admixerId cookie', function (done) { + coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 100000).toUTCString())); - setSubmoduleRegistry([uid2IdSubmodule]); - init(config); - config.setConfig(getConfigMock(['uid2', 'uid2id', 'cookie'])); + setSubmoduleRegistry([admixerIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['admixerId', 'admixerId', 'cookie'])); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.uid2'); - expect(bid.userId.uid2).to.have.deep.nested.property('id'); - expect(bid.userId.uid2).to.deep.equal({ - id: 'Sample_AD_Token' - }); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'uidapi.com', - uids: [{ - id: 'Sample_AD_Token', - atype: 3, - }] + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.admixerId'); + expect(bid.userId.admixerId).to.equal('testadmixerId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'admixer.net', + uids: [{id: 'testadmixerId', atype: 3}] + }); }); }); - }); - coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - it('should add new id system ', function (done) { - coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('sharedid', JSON.stringify({ - 'id': 'test_sharedId', - 'ts': 1590525289611 - }), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); - coreStorage.setCookie('admixerId', 'testadmixerId', new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('deepintentId', 'testdeepintentId', new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); - coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); - init(config); - - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [{ - name: 'pubCommonId', storage: {name: 'pubcid', type: 'cookie'} - }, { - name: 'unifiedId', storage: {name: 'unifiedid', type: 'cookie'} - }, { - name: 'id5Id', storage: {name: 'id5id', type: 'cookie'} - }, { - name: 'identityLink', storage: {name: 'idl_env', type: 'cookie'} - }, { - name: 'britepoolId', storage: {name: 'britepoolid', type: 'cookie'} - }, { - name: 'dmdId', storage: {name: 'dmdId', type: 'cookie'} - }, { - name: 'netId', storage: {name: 'netId', type: 'cookie'} - }, { - name: 'sharedId', storage: {name: 'sharedid', type: 'cookie'} - }, { - name: 'intentIqId', storage: {name: 'intentIqId', type: 'cookie'} - }, { - name: 'zeotapIdPlus' - }, { - name: 'haloId', storage: {name: 'haloId', type: 'cookie'} - }, { - name: 'admixerId', storage: {name: 'admixerId', type: 'cookie'} - }, { - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} - }, { - name: 'uid2' - }, { - name: 'deepintentId', storage: {name: 'deepintentId', type: 'cookie'} - }] - } - }); - - // Add new submodule named 'mockId' - attachIdSystem({ - name: 'mockId', - decode: function (value) { - return { - 'mid': value['MOCKID'] - }; - }, - getId: function (config, storedId) { - if (storedId) return {}; - return {id: {'MOCKID': '1234'}}; - } + coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); }); - requestBidsHook(function () { - adUnits.forEach(unit => { - unit.bids.forEach(bid => { - // check PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal('testpubcid'); - // check UnifiedId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); - // also check that Id5Id id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.id5id.uid'); - expect(bid.userId.id5id.uid).to.equal('testid5id'); - // also check that identityLink id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.idl_env'); - expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); - // also check that britepoolId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.britepoolid'); - expect(bid.userId.britepoolid).to.equal('testbritepoolid'); - // also check that dmdId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.dmdId'); - expect(bid.userId.dmdId).to.equal('testdmdId'); - // check MockId data was copied to bid - expect(bid).to.have.deep.nested.property('userId.netId'); - expect(bid.userId.netId).to.equal('testnetId'); - // also check that sharedId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.sharedid'); - expect(bid.userId.sharedid).to.deep.equal({ - id: 'test_sharedId', - third: 'test_sharedId' - }); - // check MockId data was copied to bid - expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('1234'); - // also check that intentIqId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.intentIqId'); - expect(bid.userId.intentIqId).to.equal('testintentIqId'); - // also check that zeotapIdPlus id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.IDP'); - expect(bid.userId.IDP).to.equal('zeotapId'); - // also check that haloId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.haloId'); - expect(bid.userId.haloId).to.equal('testHaloId'); - expect(bid.userId.uid2).to.deep.equal({ - id: 'Sample_AD_Token' - }); - - // also check that admixerId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.admixerId'); - expect(bid.userId.admixerId).to.equal('testadmixerId'); + it('test hook from deepintentId cookies', function (done) { + // simulate existing browser local storage values + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); - // also check that deepintentId was copied to bid - expect(bid).to.have.deep.nested.property('userId.deepintentId'); - expect(bid.userId.deepintentId).to.equal('testdeepintentId'); + setSubmoduleRegistry([deepintentDpesSubmodule]); + init(config); + config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'cookie'])); - expect(bid.userIdAsEids.length).to.equal(14); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.deep.equal('testdeepintentId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'deepintent.com', + uids: [{id: 'testdeepintentId', atype: 3}] + }); + }); }); - }); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); - done(); - }, {adUnits}); - }); - }); - - describe('callbacks at the end of auction', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - sinon.stub(utils, 'triggerPixel'); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); - }); - - afterEach(function () { - events.getEvents.restore(); - utils.triggerPixel.restore(); - coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); - resetConsentData(); - delete window.__tcfapi; - }); - - it('pubcid callback with url', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); - - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - expect(utils.triggerPixel.called).to.be.false; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); - }); + it('test hook from deepintentId html5', function (done) { + // simulate existing browser local storage values + localStorage.setItem('deepintentId', 'testdeepintentId'); + localStorage.setItem('deepintentId_exp', ''); - it('unifiedid callback with url', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); - addConfig(customCfg, 'params', {url: '/any/unifiedid/url'}); + setSubmoduleRegistry([deepintentDpesSubmodule]); + init(config); + config.setConfig(getConfigMock(['deepintentId', 'deepintentId', 'html5'])); + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'deepintent.com', + uids: [{id: 'testdeepintentId', atype: 3}] + }); + }); + }); + localStorage.removeItem('deepintentId'); + done(); + }, {adUnits}); + }); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, dmdId and mwOpenLinkId have data to pass', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'], + ['identityLink', 'idl_env', 'cookie'], + ['britepoolId', 'britepoolid', 'cookie'], + ['dmdId', 'dmdId', 'cookie'], + ['netId', 'netId', 'cookie'], + ['intentIqId', 'intentIqId', 'cookie'], + ['zeotapIdPlus', 'IDP', 'cookie'], + ['haloId', 'haloId', 'cookie'], + ['criteo', 'storage_criteo', 'cookie'], + ['mwOpenLinkId', 'mwol', 'cookie'], + ['tapadId', 'tapad_id', 'cookie'], + ['uid2', 'uid2id', 'cookie'], + ['admixerId', 'admixerId', 'cookie'], + ['deepintentId', 'deepintentId', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); + // check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + // also check that britepoolId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.britepoolid'); + expect(bid.userId.britepoolid).to.equal('testbritepoolid'); + // also check that dmdID id was copied to bid + expect(bid).to.have.deep.nested.property('userId.dmdId'); + expect(bid.userId.dmdId).to.equal('testdmdId'); + // also check that netId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.netId'); + expect(bid.userId.netId).to.equal('testnetId'); + // also check that intentIqId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.intentIqId'); + expect(bid.userId.intentIqId).to.equal('testintentIqId'); + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + // also check that haloId id was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + // also check that criteo id was copied to bid + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + // also check that mwOpenLink id was copied to bid + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123'); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); + // also check that criteo id was copied to bid + expect(bid).to.have.deep.nested.property('userId.admixerId'); + expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(server.requests).to.be.empty; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(server.requests[0].url).to.equal('/any/unifiedid/url'); - }); + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - it('unifiedid callback with partner', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); - addConfig(customCfg, 'params', {partner: 'rubicon'}); + expect(bid.userIdAsEids.length).to.equal(15); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, dmdId, intentIqId, zeotapIdPlus, criteo, netId, haloId, UID 2.0, admixerId and mwOpenLinkId have their modules added before and after init', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('storage_criteo', JSON.stringify({'criteoId': 'test_bidid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([]); + + // attaching before init + attachIdSystem(sharedIdSystemSubmodule); - expect(server.requests).to.be.empty; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); - }); + init(config); - it('verify sharedid callback via pubcid when enabled', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); + // attaching after init + attachIdSystem(unifiedIdSubmodule); + attachIdSystem(id5IdSubmodule); + attachIdSystem(identityLinkSubmodule); + attachIdSystem(britepoolIdSubmodule); + attachIdSystem(netIdSubmodule); + attachIdSystem(intentIqIdSubmodule); + attachIdSystem(zeotapIdPlusSubmodule); + attachIdSystem(haloIdSubmodule); + attachIdSystem(dmdIdSubmodule); + attachIdSystem(criteoIdSubmodule); + attachIdSystem(mwOpenLinkIdSubModule); + attachIdSystem(tapadIdSubmodule); + attachIdSystem(uid2IdSubmodule); + attachIdSystem(admixerIdSubmodule); + attachIdSystem(deepintentDpesSubmodule); + + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'], + ['identityLink', 'idl_env', 'cookie'], + ['britepoolId', 'britepoolid', 'cookie'], + ['netId', 'netId', 'cookie'], + ['intentIqId', 'intentIqId', 'cookie'], + ['zeotapIdPlus', 'IDP', 'cookie'], + ['haloId', 'haloId', 'cookie'], + ['dmdId', 'dmdId', 'cookie'], + ['criteo', 'storage_criteo', 'cookie'], + ['mwOpenLinkId', 'mwol', 'cookie'], + ['tapadId', 'tapad_id', 'cookie'], + ['uid2', 'uid2id', 'cookie'], + ['admixerId', 'admixerId', 'cookie'], + ['deepintentId', 'deepintentId', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); + // also check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + // also check that britepoolId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.britepoolid'); + expect(bid.userId.britepoolid).to.equal('testbritepoolid'); + // also check that britepoolId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.netId'); + expect(bid.userId.netId).to.equal('testnetId'); + // also check that intentIqId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.intentIqId'); + expect(bid.userId.intentIqId).to.equal('testintentIqId'); + + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + // also check that haloId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + // also check that dmdId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.dmdId'); + expect(bid.userId.dmdId).to.equal('testdmdId'); + + // also check that criteo id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.criteoId'); + expect(bid.userId.criteoId).to.equal('test_bidid'); + + // also check that mwOpenLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.mwOpenLinkId'); + expect(bid.userId.mwOpenLinkId).to.equal('XX-YY-ZZ-123') + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); + + // also check that admixerId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.admixerId'); + expect(bid.userId.admixerId).to.equal('testadmixerId'); + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - server.respondWith('https://id.sharedid.org/id', function(xhr) { - xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); + expect(bid.userIdAsEids.length).to.equal(15); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('storage_criteo', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); }); - server.respondImmediately = true; - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('test hook from UID2 cookie', function (done) { + coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); - expect(utils.triggerPixel.called).to.be.false; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + setSubmoduleRegistry([uid2IdSubmodule]); + init(config); + config.setConfig(getConfigMock(['uid2', 'uid2id', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.uid2'); + expect(bid.userId.uid2).to.have.deep.nested.property('id'); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: 'Sample_AD_Token', + atype: 3, + }] + }); + }); + }); + coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + it('should add new id system ', function (done) { + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('intentIqId', 'testintentIqId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('IDP', btoa(JSON.stringify('zeotapId')), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('haloId', JSON.stringify({'haloId': 'testHaloId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('admixerId', 'testadmixerId', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('deepintentId', 'testdeepintentId', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); + init(config); - expect(server.requests[0].url).to.equal('https://id.sharedid.org/id'); - expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid'); - }); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'pubCommonId', storage: {name: 'pubcid', type: 'cookie'} + }, { + name: 'unifiedId', storage: {name: 'unifiedid', type: 'cookie'} + }, { + name: 'id5Id', storage: {name: 'id5id', type: 'cookie'} + }, { + name: 'identityLink', storage: {name: 'idl_env', type: 'cookie'} + }, { + name: 'britepoolId', storage: {name: 'britepoolid', type: 'cookie'} + }, { + name: 'dmdId', storage: {name: 'dmdId', type: 'cookie'} + }, { + name: 'netId', storage: {name: 'netId', type: 'cookie'} + }, { + name: 'intentIqId', storage: {name: 'intentIqId', type: 'cookie'} + }, { + name: 'zeotapIdPlus' + }, { + name: 'haloId', storage: {name: 'haloId', type: 'cookie'} + }, { + name: 'admixerId', storage: {name: 'admixerId', type: 'cookie'} + }, { + name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + }, { + name: 'uid2' + }, { + name: 'deepintentId', storage: {name: 'deepintentId', type: 'cookie'} + }] + } + }); - it('verify no sharedid callback via pubcid when disabled', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); + // Add new submodule named 'mockId' + attachIdSystem({ + name: 'mockId', + decode: function (value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: function (config, storedId) { + if (storedId) return {}; + return {id: {'MOCKID': '1234'}}; + } + }); - server.respondWith('https://id.sharedid.org/id', function(xhr) { - xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); - }); - server.respondImmediately = true; + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // check PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // check UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id.uid'); + expect(bid.userId.id5id.uid).to.equal('testid5id'); + // also check that identityLink id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.idl_env'); + expect(bid.userId.idl_env).to.equal('AiGNC8Z5ONyZKSpIPf'); + // also check that britepoolId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.britepoolid'); + expect(bid.userId.britepoolid).to.equal('testbritepoolid'); + // also check that dmdId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.dmdId'); + expect(bid.userId.dmdId).to.equal('testdmdId'); + // check MockId data was copied to bid + expect(bid).to.have.deep.nested.property('userId.netId'); + expect(bid.userId.netId).to.equal('testnetId'); + // check MockId data was copied to bid + expect(bid).to.have.deep.nested.property('userId.mid'); + expect(bid.userId.mid).to.equal('1234'); + // also check that intentIqId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.intentIqId'); + expect(bid.userId.intentIqId).to.equal('testintentIqId'); + // also check that zeotapIdPlus id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.IDP'); + expect(bid.userId.IDP).to.equal('zeotapId'); + // also check that haloId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.haloId'); + expect(bid.userId.haloId).to.equal('testHaloId'); + expect(bid.userId.uid2).to.deep.equal({ + id: 'Sample_AD_Token' + }); - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + // also check that admixerId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.admixerId'); + expect(bid.userId.admixerId).to.equal('testadmixerId'); - expect(utils.triggerPixel.called).to.be.false; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + // also check that deepintentId was copied to bid + expect(bid).to.have.deep.nested.property('userId.deepintentId'); + expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(server.requests).to.have.lengthOf(0); - expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null; + expect(bid.userIdAsEids.length).to.equal(13); + }); + }); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('intentIqId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('IDP', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('haloId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('dmdId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); }); - it('verify sharedid optout via pubcid when enabled', function () { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); - coreStorage.setCookie('pubcid_sharedid', 'testsharedid', (new Date(Date.now() + 5000).toUTCString())); + describe('callbacks at the end of auction', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + sinon.stub(utils, 'triggerPixel'); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); + }); - server.respondWith('https://id.sharedid.org/id', function(xhr) { - xhr.respond(200, {}, '{"sharedId":"00000000000000000000000000"}'); + afterEach(function () { + events.getEvents.restore(); + utils.triggerPixel.restore(); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); + resetConsentData(); + delete window.__tcfapi; }); - server.respondImmediately = true; - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(customCfg); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('pubcid callback with url', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); - expect(utils.triggerPixel.called).to.be.false; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - expect(server.requests[0].url).to.equal('https://id.sharedid.org/id'); - expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null; - }); + expect(utils.triggerPixel.called).to.be.false; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + }); - it('verify sharedid called with consent data when gdpr applies', function () { - let adUnits = [getAdUnitMock()]; - let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - let consentConfig = { - cmpApi: 'iab', - timeout: 7500, - allowAuctionWithoutConsent: false - }; - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true}); - - server.respondWith('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234', function(xhr) { - xhr.respond(200, {}, '{"sharedId":"testsharedid"}'); - }); - server.respondImmediately = true; - - let testConsentData = { - tcString: 'abc12345234', - gdprApplies: true, - purposeOneTreatment: false, - eventStatus: 'tcloaded', - vendor: {consents: {887: true}}, - purpose: { - consents: { - 1: true - } - } - }; + it('unifiedid callback with url', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); + addConfig(customCfg, 'params', {url: '/any/unifiedid/url'}); - window.__tcfapi = function () { }; - sinon.stub(window, '__tcfapi').callsFake((...args) => { - args[2](testConsentData, true); - }); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig(customCfg); - setConsentConfig(consentConfig); + expect(server.requests).to.be.empty; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(server.requests[0].url).to.equal('/any/unifiedid/url'); + }); - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - }, {adUnits}); + it('unifiedid callback with partner', function () { + let adUnits = [getAdUnitMock()]; + let innerAdUnits; + let customCfg = getConfigMock(['unifiedId', 'unifiedid', 'cookie']); + addConfig(customCfg, 'params', {partner: 'rubicon'}); - expect(utils.triggerPixel.called).to.be.false; - events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url'); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(customCfg); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - expect(server.requests[0].url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234'); - expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid'); + expect(server.requests).to.be.empty; + events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); + expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); + }); }); - }); - describe('Set cookie behavior', function () { - let coreStorageSpy; - beforeEach(function () { - coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - }); - afterEach(function () { - coreStorageSpy.restore(); - }); - it('should allow submodules to override the domain', function () { - const submodule = { - submodule: { - domainOverride: function () { - return 'foo.com' - } - }, - config: { - storage: { - type: 'cookie' + describe('Set cookie behavior', function () { + let coreStorageSpy; + beforeEach(function () { + coreStorageSpy = sinon.spy(coreStorage, 'setCookie'); + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + }); + afterEach(function () { + coreStorageSpy.restore(); + }); + it('should allow submodules to override the domain', function () { + const submodule = { + submodule: { + domainOverride: function () { + return 'foo.com' + } + }, + config: { + storage: { + type: 'cookie' + } } } - } - setStoredValue(submodule, 'bar'); - expect(coreStorage.setCookie.getCall(0).args[4]).to.equal('foo.com'); - }); + setStoredValue(submodule, 'bar'); + expect(coreStorage.setCookie.getCall(0).args[4]).to.equal('foo.com'); + }); - it('should pass null for domain if submodule does not override the domain', function () { - const submodule = { - submodule: {}, - config: { - storage: { - type: 'cookie' + it('should pass null for domain if submodule does not override the domain', function () { + const submodule = { + submodule: {}, + config: { + storage: { + type: 'cookie' + } } } - } - setStoredValue(submodule, 'bar'); - expect(coreStorage.setCookie.getCall(0).args[4]).to.equal(null); + setStoredValue(submodule, 'bar'); + expect(coreStorage.setCookie.getCall(0).args[4]).to.equal(null); + }); }); - }); - describe('Consent changes determine getId refreshes', function () { - let expStr; - let adUnits; - - const mockIdCookieName = 'MOCKID'; - let mockGetId = sinon.stub(); - let mockDecode = sinon.stub(); - let mockExtendId = sinon.stub(); - const mockIdSystem = { - name: 'mockId', - getId: mockGetId, - decode: mockDecode, - extendId: mockExtendId - }; - const userIdConfig = { - userSync: { - userIds: [{ - name: 'mockId', - storage: { - name: 'MOCKID', - type: 'cookie', - refreshInSeconds: 30 - } - }], - auctionDelay: 5 - } - }; + describe('Consent changes determine getId refreshes', function () { + let expStr; + let adUnits; - let cmpStub; - let testConsentData; - const consentConfig = { - cmpApi: 'iab', - timeout: 7500, - allowAuctionWithoutConsent: false - }; + const mockIdCookieName = 'MOCKID'; + let mockGetId = sinon.stub(); + let mockDecode = sinon.stub(); + let mockExtendId = sinon.stub(); + const mockIdSystem = { + name: 'mockId', + getId: mockGetId, + decode: mockDecode, + extendId: mockExtendId + }; + const userIdConfig = { + userSync: { + userIds: [{ + name: 'mockId', + storage: { + name: 'MOCKID', + type: 'cookie', + refreshInSeconds: 30 + } + }], + auctionDelay: 5 + } + }; - const sharedBeforeFunction = function () { - // clear cookies - expStr = (new Date(Date.now() + 25000).toUTCString()); - coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie(`${mockIdCookieName}_last`, '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); + let cmpStub; + let testConsentData; + const consentConfig = { + cmpApi: 'iab', + timeout: 7500, + allowAuctionWithoutConsent: false + }; - // init - adUnits = [getAdUnitMock()]; - init(config); + const sharedBeforeFunction = function () { + // clear cookies + expStr = (new Date(Date.now() + 25000).toUTCString()); + coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie(`${mockIdCookieName}_last`, '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); - // init id system - attachIdSystem(mockIdSystem); - config.setConfig(userIdConfig); - } - const sharedAfterFunction = function () { - config.resetConfig(); - mockGetId.reset(); - mockDecode.reset(); - mockExtendId.reset(); - cmpStub.restore(); - resetConsentData(); - delete window.__cmp; - delete window.__tcfapi; - }; + // init + adUnits = [getAdUnitMock()]; + init(config); - describe('TCF v1', function () { - testConsentData = { - gdprApplies: true, - consentData: 'xyz', - apiVersion: 1 + // init id system + attachIdSystem(mockIdSystem); + config.setConfig(userIdConfig); + } + const sharedAfterFunction = function () { + config.resetConfig(); + mockGetId.reset(); + mockDecode.reset(); + mockExtendId.reset(); + cmpStub.restore(); + resetConsentData(); + delete window.__cmp; + delete window.__tcfapi; }; - beforeEach(function () { - sharedBeforeFunction(); - - // init v1 consent management - window.__cmp = function () { + describe('TCF v1', function () { + testConsentData = { + gdprApplies: true, + consentData: 'xyz', + apiVersion: 1 }; - delete window.__tcfapi; - cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { - args[2](testConsentData); - }); - setConsentConfig(consentConfig); - }); - afterEach(function () { - sharedAfterFunction(); - }); + beforeEach(function () { + sharedBeforeFunction(); - it('calls getId if no stored consent data and refresh is not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + // init v1 consent management + window.__cmp = function () { + }; + delete window.__tcfapi; + cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { + args[2](testConsentData); + }); + setConsentConfig(consentConfig); + }); - let innerAdUnits; - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + afterEach(function () { + sharedAfterFunction(); + }); - sinon.assert.calledOnce(mockGetId); - sinon.assert.calledOnce(mockDecode); - sinon.assert.notCalled(mockExtendId); - }); + it('calls getId if no stored consent data and refresh is not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); - it('calls getId if no stored consent data but refresh is needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); + let innerAdUnits; + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - let innerAdUnits; - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); + }); - sinon.assert.calledOnce(mockGetId); - sinon.assert.calledOnce(mockDecode); - sinon.assert.notCalled(mockExtendId); - }); + it('calls getId if no stored consent data but refresh is needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); - it('calls getId if empty stored consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + let innerAdUnits; + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - setStoredConsentData(); + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); + }); - let innerAdUnits; - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('calls getId if empty stored consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); - sinon.assert.calledOnce(mockGetId); - sinon.assert.calledOnce(mockDecode); - sinon.assert.notCalled(mockExtendId); - }); + setStoredConsentData(); - it('calls getId if stored consent does not match current consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + let innerAdUnits; + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - setStoredConsentData({ - gdprApplies: testConsentData.gdprApplies, - consentString: 'abc', - apiVersion: testConsentData.apiVersion + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); }); - let innerAdUnits; - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('calls getId if stored consent does not match current consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); - sinon.assert.calledOnce(mockGetId); - sinon.assert.calledOnce(mockDecode); - sinon.assert.notCalled(mockExtendId); - }); + setStoredConsentData({ + gdprApplies: testConsentData.gdprApplies, + consentString: 'abc', + apiVersion: testConsentData.apiVersion + }); - it('does not call getId if stored consent matches current consent and refresh not needed', function () { - coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + let innerAdUnits; + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - setStoredConsentData({ - gdprApplies: testConsentData.gdprApplies, - consentString: testConsentData.consentData, - apiVersion: testConsentData.apiVersion + sinon.assert.calledOnce(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.notCalled(mockExtendId); }); - let innerAdUnits; - consentManagementRequestBidsHook(() => { - }, {}); - requestBidsHook((config) => { - innerAdUnits = config.adUnits - }, {adUnits}); + it('does not call getId if stored consent matches current consent and refresh not needed', function () { + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); - sinon.assert.notCalled(mockGetId); - sinon.assert.calledOnce(mockDecode); - sinon.assert.calledOnce(mockExtendId); - }); - }); + setStoredConsentData({ + gdprApplies: testConsentData.gdprApplies, + consentString: testConsentData.consentData, + apiVersion: testConsentData.apiVersion + }); - describe('findRootDomain', function () { - let sandbox; + let innerAdUnits; + consentManagementRequestBidsHook(() => { + }, {}); + requestBidsHook((config) => { + innerAdUnits = config.adUnits + }, {adUnits}); - beforeEach(function () { - setSubmoduleRegistry([pubCommonIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [ - { - name: 'pubCommonId', - value: { pubcid: '11111' }, - }, - ], - }, + sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockDecode); + sinon.assert.calledOnce(mockExtendId); }); - sandbox = sinon.createSandbox(); - sandbox - .stub(coreStorage, 'getCookie') - .onFirstCall() - .returns(null) // .co.uk - .onSecondCall() - .returns('writeable'); // realdomain.co.uk; }); - afterEach(function () { - sandbox.restore(); - }); + describe('findRootDomain', function () { + let sandbox; - it('should just find the root domain', function () { - var domain = findRootDomain('sub.realdomain.co.uk'); - expect(domain).to.be.eq('realdomain.co.uk'); - }); + beforeEach(function () { + setSubmoduleRegistry([sharedIdSystemSubmodule]); + init(config); + 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 find the full domain when no subdomain is present', function () { - var domain = findRootDomain('realdomain.co.uk'); - expect(domain).to.be.eq('realdomain.co.uk'); + 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'); + }); }); }); - }); + }) }); diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js deleted file mode 100644 index d78a5ce04f6..00000000000 --- a/test/spec/modules/vdoaiBidAdapter_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/vdoaiBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; - -describe('vdoaiBidAdapter', function () { - const adapter = newBidder(spec); - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'vdoai', - 'params': { - placementId: 'testPlacementId' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '1234asdf1234', - 'bidderRequestId': '1234asdf1234asdf', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' - }; - it('should return true where required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'vdoai', - 'params': { - placementId: 'testPlacementId' - }, - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - 'mediaTypes': 'banner' - } - ]; - - let bidderRequests = { - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'https://example.com', - 'stack': ['https://example.com'] - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequests); - it('sends bid request to our endpoint via POST', function () { - expect(request[0].method).to.equal('POST'); - }); - it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.equal(ENDPOINT_URL); - }); - }); - - describe('interpretResponse', function () { - let bidRequest = [ - { - 'method': 'POST', - 'url': ENDPOINT_URL, - 'data': { - 'placementId': 'testPlacementId', - 'width': '300', - 'height': '200', - 'bidId': 'bidId123', - 'referer': 'www.example.com' - } - - } - ]; - let serverResponse = { - body: { - 'vdoCreative': '

I am an ad

', - 'price': 4.2, - 'adid': '12345asdfg', - 'currency': 'EUR', - 'statusMessage': 'Bid available', - 'requestId': 'bidId123', - 'width': 300, - 'height': 250, - 'netRevenue': true - } - }; - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': 'bidId123', - 'cpm': 4.2, - 'width': 300, - 'height': 250, - 'creativeId': '12345asdfg', - 'currency': 'EUR', - 'netRevenue': true, - 'ttl': 3000, - 'ad': '

I am an ad

' - }]; - let result = spec.interpretResponse(serverResponse, bidRequest[0]); - expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); - }); - - it('handles instream video responses', function () { - let serverResponse = { - body: { - 'vdoCreative': '', - 'price': 4.2, - 'adid': '12345asdfg', - 'currency': 'EUR', - 'statusMessage': 'Bid available', - 'requestId': 'bidId123', - 'width': 300, - 'height': 250, - 'netRevenue': true, - 'mediaType': 'video' - } - }; - let bidRequest = [ - { - 'method': 'POST', - 'url': ENDPOINT_URL, - 'data': { - 'placementId': 'testPlacementId', - 'width': '300', - 'height': '200', - 'bidId': 'bidId123', - 'referer': 'www.example.com', - 'mediaType': 'video' - } - } - ]; - - let result = spec.interpretResponse(serverResponse, bidRequest[0]); - expect(result[0]).to.have.property('vastXml'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); - }); -}); diff --git a/test/spec/modules/viBidAdapter_spec.js b/test/spec/modules/viBidAdapter_spec.js deleted file mode 100644 index e1a88c004bb..00000000000 --- a/test/spec/modules/viBidAdapter_spec.js +++ /dev/null @@ -1,911 +0,0 @@ -import { - ratioToPercentageCeil, - merge, - getDocumentHeight, - getOffset, - getWindowParents, - getRectCuts, - getTopmostReachableWindow, - topDocumentIsReachable, - isInsideIframe, - isInsideSafeframe, - getIframeType, - getFrameElements, - getElementCuts, - getInViewRatio, - getMayBecomeVisible, - getInViewPercentage, - getInViewRatioInsideTopFrame, - getOffsetTopDocument, - getOffsetTopDocumentPercentage, - getOffsetToView, - getOffsetToViewPercentage, - area, - get, - getViewabilityDescription, - mergeArrays, - documentFocus -} from 'modules/viBidAdapter.js'; - -describe('ratioToPercentageCeil', () => { - it('1 converts to percentage', () => - expect(ratioToPercentageCeil(0.01)).to.equal(1)); - it('2 converts to percentage', () => - expect(ratioToPercentageCeil(0.00000000001)).to.equal(1)); - it('3 converts to percentage', () => - expect(ratioToPercentageCeil(0.5)).to.equal(50)); - it('4 converts to percentage', () => - expect(ratioToPercentageCeil(1)).to.equal(100)); - it('5 converts to percentage', () => - expect(ratioToPercentageCeil(0.99)).to.equal(99)); - it('6 converts to percentage', () => - expect(ratioToPercentageCeil(0.990000000000001)).to.equal(100)); -}); - -describe('merge', () => { - it('merges two objects', () => { - expect( - merge({ a: 1, b: 2, d: 0 }, { a: 2, b: 2, c: 3 }, (a, b) => a + b) - ).to.deep.equal({ a: 3, b: 4, c: 3, d: 0 }); - }); -}); - -describe('getDocumentHeight', () => { - [ - { - curDocument: { - body: { - clientHeight: 0, - offsetHeight: 0, - scrollHeight: 0 - }, - documentElement: { - clientHeight: 0, - offsetHeight: 0, - scrollHeight: 0 - } - }, - expected: 0 - }, - { - curDocument: { - body: { - clientHeight: 0, - offsetHeight: 13, - scrollHeight: 24 - }, - documentElement: { - clientHeight: 0, - offsetHeight: 0, - scrollHeight: 0 - } - }, - expected: 24 - }, - { - curDocument: { - body: { - clientHeight: 0, - offsetHeight: 13, - scrollHeight: 24 - }, - documentElement: { - clientHeight: 100, - offsetHeight: 50, - scrollHeight: 30 - } - }, - expected: 100 - } - ].forEach(({ curDocument, expected }) => - expect(getDocumentHeight(curDocument)).to.be.equal(expected) - ); -}); - -describe('getOffset', () => { - [ - { - element: { - ownerDocument: { - defaultView: { - pageXOffset: 0, - pageYOffset: 0 - } - }, - getBoundingClientRect: () => ({ - top: 0, - right: 0, - bottom: 0, - left: 0 - }) - }, - expected: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - } - ].forEach(({ description, element, expected }, i) => - it( - 'returns element offsets from the document edges (including scroll): ' + - i, - () => expect(getOffset(element)).to.be.deep.equal(expected) - ) - ); - it('Throws when there is no window', () => - expect( - getOffset.bind(null, { - ownerDocument: { - defaultView: null - }, - getBoundingClientRect: () => ({ - top: 0, - right: 0, - bottom: 0, - left: 0 - }) - }) - ).to.throw()); -}); - -describe('getWindowParents', () => { - const win = {}; - win.top = win; - win.parent = win; - const win1 = { top: win, parent: win }; - const win2 = { top: win, parent: win1 }; - const win3 = { top: win, parent: win2 }; - - it('get parents up to the top', () => - expect(getWindowParents(win3)).to.be.deep.equal([win2, win1, win])); -}); - -describe('getTopmostReachableWindow', () => { - const win = {}; - win.top = win; - win.parent = win; - const win1 = { top: win, parent: win }; - const win2 = { top: win, parent: win1 }; - const win3 = { top: win, parent: win2 }; - - it('get parents up to the top', () => - expect(getTopmostReachableWindow(win3)).to.be.equal(win)); -}); - -const topWindow = { document, frameElement: 0 }; -topWindow.top = topWindow; -topWindow.parent = topWindow; -const topFrameElement = { - ownerDocument: { - defaultView: topWindow - } -}; -const frameWindow1 = { - top: topWindow, - parent: topWindow, - frameElement: topFrameElement -}; -const frameElement1 = { - ownerDocument: { - defaultView: frameWindow1 - } -}; -const frameWindow2 = { - top: topWindow, - parent: frameWindow1, - frameElement: frameElement1 -}; -const frameElement2 = { - ownerDocument: { - defaultView: frameWindow2 - } -}; -const frameWindow3 = { - top: topWindow, - parent: frameWindow2, - frameElement: frameElement2 -}; - -describe('topDocumentIsReachable', () => { - it('returns true if it no inside iframe', () => - expect(topDocumentIsReachable(topWindow)).to.be.true); - it('returns true if it can access top document', () => - expect(topDocumentIsReachable(frameWindow3)).to.be.true); -}); - -describe('isInsideIframe', () => { - it('returns true if window !== window.top', () => - expect(isInsideIframe(topWindow)).to.be.false); - it('returns true if window !== window.top', () => - expect(isInsideIframe(frameWindow1)).to.be.true); -}); - -const safeframeWindow = { $sf: {} }; - -describe('isInsideSafeframe', () => { - it('returns true if top window is not reachable and window.$sf is defined', () => - expect(isInsideSafeframe(safeframeWindow)).to.be.true); -}); - -const hostileFrameWindow = {}; - -describe('getIframeType', () => { - it('returns undefined when is not inside iframe', () => - expect(getIframeType(topWindow)).to.be.undefined); - it("returns 'safeframe' when inside sf", () => - expect(getIframeType(safeframeWindow)).to.be.equal('safeframe')); - it("returns 'friendly' when inside friendly iframe and can reach top window", () => - expect(getIframeType(frameWindow3)).to.be.equal('friendly')); - it("returns 'nonfriendly' when cannot get top window", () => - expect(getIframeType(hostileFrameWindow)).to.be.equal('nonfriendly')); -}); - -describe('getFrameElements', () => { - it('it returns a list iframe elements up to the top, topmost goes first', () => { - expect(getFrameElements(frameWindow3)).to.be.deep.equal([ - topFrameElement, - frameElement1, - frameElement2 - ]); - }); -}); - -describe('area', () => { - it('calculates area', () => expect(area(10, 10)).to.be.equal(100)); - it('calculates area', () => - expect( - area(10, 10, { top: -2, left: -2, bottom: 0, right: 0 }) - ).to.be.equal(64)); -}); - -describe('getElementCuts', () => { - it('returns element cuts', () => - expect( - getElementCuts({ - getBoundingClientRect() { - return { - top: 0, - right: 200, - bottom: 200, - left: 0 - }; - }, - ownerDocument: { - defaultView: { - innerHeight: 1000, - innerWidth: 1000 - } - } - }) - ).to.be.deep.equal({ - top: 0, - right: 0, - bottom: 0, - left: 0 - })); -}); - -describe('getInViewRatio', () => { - it('returns inViewRatio', () => - expect( - getInViewRatio({ - ownerDocument: { - defaultView: { - innerHeight: 1000, - innerWidth: 1000 - } - }, - offsetWidth: 200, - offsetHeight: 200, - getBoundingClientRect() { - return { - top: 0, - right: 200, - bottom: 200, - left: 0 - }; - } - }) - ).to.be.deep.equal(1)); -}); - -describe('getMayBecomeVisible', () => { - it('returns true if not inside iframe of visible inside the iframe', () => - expect( - getMayBecomeVisible({ - ownerDocument: { - defaultView: { - innerHeight: 1000, - innerWidth: 1000 - } - }, - offsetWidth: 200, - offsetHeight: 200, - getBoundingClientRect() { - return { - top: 0, - right: 200, - bottom: 200, - left: 0 - }; - } - }) - ).to.be.true); -}); - -describe('getInViewPercentage', () => { - it('returns inViewRatioPercentage', () => - expect( - getInViewPercentage({ - ownerDocument: { - defaultView: { - innerHeight: 1000, - innerWidth: 1000 - } - }, - offsetWidth: 200, - offsetHeight: 200, - getBoundingClientRect() { - return { - top: 0, - right: 200, - bottom: 200, - left: 0 - }; - } - }) - ).to.be.deep.equal(100)); -}); - -describe('getInViewRatioInsideTopFrame', () => { - it('returns inViewRatio', () => - expect( - getInViewRatioInsideTopFrame({ - ownerDocument: { - defaultView: { - innerHeight: 1000, - innerWidth: 1000 - } - }, - offsetWidth: 200, - offsetHeight: 200, - getBoundingClientRect() { - return { - top: 0, - right: 200, - bottom: 200, - left: 0 - }; - } - }) - ).to.be.deep.equal(1)); -}); - -describe('getOffsetTopDocument', () => { - it('returns offset relative to the top document', () => - expect( - getOffsetTopDocument({ - ownerDocument: { - defaultView: { - pageXOffset: 0, - pageYOffset: 0 - } - }, - getBoundingClientRect: () => ({ - top: 0, - right: 0, - bottom: 0, - left: 0 - }) - }) - ).to.be.deep.equal({ - top: 0, - right: 0, - bottom: 0, - left: 0 - })); -}); - -describe('getOffsetTopDocumentPercentage', () => { - it('returns offset from the top as a percentage of the page length', () => { - const topWindow = { - pageXOffset: 0, - pageYOffset: 100, - document: { - body: { - clientHeight: 1000 - } - } - }; - topWindow.top = topWindow; - topWindow.parent = topWindow; - expect( - getOffsetTopDocumentPercentage({ - ownerDocument: { - defaultView: topWindow - }, - getBoundingClientRect: () => ({ - top: 100, - right: 0, - bottom: 0, - left: 0 - }) - }) - ).to.be.equal(20); - }); - it('throws when cannot get window', () => - expect(() => - getOffsetTopDocumentPercentage({ - ownerDocument: {} - }) - ).to.throw()); - it("throw when top document isn't reachable", () => { - const topWindow = { ...topWindow, document: null }; - expect(() => - getOffsetTopDocumentPercentage({ - ownerDocument: { - defaultView: { - top: topWindow - } - } - }) - ).to.throw(); - }); -}); - -describe('getOffsetToView', () => { - expect( - getOffsetToView({ - ownerDocument: { - defaultView: { - scrollY: 0, - pageXOffset: 0, - pageYOffset: 0 - } - }, - getBoundingClientRect: () => ({ - top: 0, - right: 0, - bottom: 0, - left: 0 - }) - }) - ).to.be.equal(0); -}); - -describe('getOffsetToView', () => { - expect( - getOffsetToViewPercentage({ - ownerDocument: { - defaultView: { - scrollY: 0, - pageXOffset: 0, - pageYOffset: 0, - document: { - body: { - clientHeight: 1000 - } - } - } - }, - getBoundingClientRect: () => ({ - top: 0, - right: 0, - bottom: 0, - left: 0 - }) - }) - ).to.be.equal(0); -}); - -describe('getCuts without vCuts', () => { - const cases = { - 'completely in view 1': { - top: 0, - bottom: 200, - right: 200, - left: 0, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - }, - 'completely in view 2': { - top: 100, - bottom: 200, - right: 200, - left: 0, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: 0, - left: 0 - } - }, - 'half cut from the top': { - top: -200, - bottom: 200, - right: 200, - left: 0, - vw: 300, - vh: 300, - expected: { - top: -200, - right: 0, - bottom: 0, - left: 0 - } - }, - 'half cut from the bottom': { - top: 0, - bottom: 600, - right: 200, - left: 0, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: -300, - left: 0 - } - }, - 'quarter cut from top and bottom': { - top: -25, - bottom: 75, - right: 200, - left: 0, - vw: 300, - vh: 50, - expected: { - top: -25, - right: 0, - bottom: -25, - left: 0 - } - }, - 'out of view top': { - top: -200, - bottom: -5, - right: 200, - left: 0, - vw: 300, - vh: 200, - expected: { - top: -200, - right: 0, - bottom: 0, - left: 0 - } - }, - 'out of view bottom': { - top: 250, - bottom: 500, - right: 200, - left: 0, - vw: 300, - vh: 200, - expected: { - top: 0, - right: 0, - bottom: -300, - left: 0 - } - }, - 'half cut from left': { - top: 0, - bottom: 200, - left: -200, - right: 200, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: 0, - left: -200 - } - }, - 'half cut from left and top': { - top: -100, - bottom: 100, - left: -200, - right: 200, - vw: 300, - vh: 300, - expected: { - top: -100, - right: 0, - bottom: 0, - left: -200 - } - }, - 'quarter cut from all sides': { - top: -100, - left: -100, - bottom: 300, - right: 300, - vw: 200, - vh: 200, - expected: { - top: -100, - right: -100, - bottom: -100, - left: -100 - } - } - }; - for (let descr in cases) { - it(descr, () => { - const { expected, vh, vw, ...rect } = cases[descr]; - expect(getRectCuts(rect, vh, vw)).to.deep.equal(expected); - }); - } -}); - -describe('getCuts with vCuts', () => { - const cases = { - 'completely in view 1, half-cut viewport from top': { - top: 0, - right: 200, - bottom: 200, - left: 0, - vw: 200, - vh: 200, - vCuts: { - top: -100, - right: 0, - bottom: 0, - left: 0 - }, - expected: { - top: -100, - right: 0, - bottom: 0, - left: 0 - } - }, - 'completely in view 2, half-cut viewport from bottom': { - top: 100, - bottom: 200, - right: 200, - left: 0, - vw: 300, - vh: 300, - vCuts: { - top: 0, - right: 0, - bottom: -150, - left: 0 - }, - expected: { - top: 0, - right: 0, - bottom: -50, - left: 0 - } - }, - 'half cut from the top, 1/3 viewport cut from the bottom': { - top: -200, - bottom: 200, - right: 200, - left: 0, - vw: 300, - vh: 300, - vCuts: { - top: 0, - right: 0, - bottom: -100, - left: 0 - }, - expected: { - top: -200, - right: 0, - bottom: 0, - left: 0 - } - }, - 'half cut from the bottom': { - top: 0, - bottom: 600, - right: 200, - left: 0, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: -300, - left: 0 - } - }, - 'quarter cut from top and bottom': { - top: -25, - bottom: 75, - right: 200, - left: 0, - vw: 300, - vh: 50, - expected: { - top: -25, - right: 0, - bottom: -25, - left: 0 - } - }, - 'out of view top': { - top: -200, - bottom: -5, - right: 200, - left: 0, - vw: 300, - vh: 200, - expected: { - top: -200, - right: 0, - bottom: 0, - left: 0 - } - }, - 'out of view bottom': { - top: 250, - bottom: 500, - right: 200, - left: 0, - vw: 300, - vh: 200, - expected: { - top: 0, - right: 0, - bottom: -300, - left: 0 - } - }, - 'half cut from left': { - top: 0, - bottom: 200, - left: -200, - right: 200, - vw: 300, - vh: 300, - expected: { - top: 0, - right: 0, - bottom: 0, - left: -200 - } - }, - 'half cut from left and top': { - top: -100, - bottom: 100, - left: -200, - right: 200, - vw: 300, - vh: 300, - expected: { - top: -100, - right: 0, - bottom: 0, - left: -200 - } - }, - 'quarter cut from all sides': { - top: -100, - left: -100, - bottom: 300, - right: 300, - vw: 200, - vh: 200, - expected: { - top: -100, - right: -100, - bottom: -100, - left: -100 - } - } - }; - for (let descr in cases) { - it(descr, () => { - const { expected, vh, vw, vCuts, ...rect } = cases[descr]; - expect(getRectCuts(rect, vh, vw, vCuts)).to.deep.equal(expected); - }); - } -}); - -describe('get', () => { - it('returns a property in a nested object 1', () => - expect(get(['a'], { a: 1 })).to.equal(1)); - it('returns a property in a nested object 2', () => - expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); - it('returns a property in a nested object 3', () => - expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); - it('returns undefined if property does not exist', () => - expect(get(['a', 'b'], { b: 1 })).to.equal(undefined)); - it('returns undefined if property does not exist', () => - expect(get(['a', 'b'], undefined)).to.equal(undefined)); - it('returns undefined if property does not exist', () => - expect(get(['a', 'b'], 1213)).to.equal(undefined)); - const DEFAULT = -5; - it('returns defaultValue if property does not exist', () => - expect(get(['a', 'b'], { b: 1 }, DEFAULT)).to.equal(DEFAULT)); - it('returns defaultValue if property does not exist', () => - expect(get(['a', 'b'], undefined, DEFAULT)).to.equal(DEFAULT)); - it('returns defaultValue if property does not exist', () => - expect(get(['a', 'b'], 1213, DEFAULT)).to.equal(DEFAULT)); - it('can work with arrays 1', () => expect(get([0, 1], [[1, 2]])).to.equal(2)); - it('can work with arrays 2', () => - expect(get([0, 'a'], [{ a: 42 }])).to.equal(42)); -}); - -describe('getViewabilityDescription', () => { - it('returns error when there is no element', () => { - expect(getViewabilityDescription(null)).to.deep.equal({ - error: 'no element' - }); - }); - it('returns only iframe type for nonfrienly iframe', () => { - expect( - getViewabilityDescription({ - ownerDocument: { - defaultView: {} - } - }) - ).to.deep.equal({ - iframeType: 'nonfriendly' - }); - }); - it('returns only iframe type for safeframe iframe', () => { - expect( - getViewabilityDescription({ - ownerDocument: { - defaultView: { - $sf: true - } - } - }) - ).to.deep.equal({ - iframeType: 'safeframe' - }); - }); -}); - -describe('mergeSizes', () => { - it('merges provides arrays of tuples, leaving only unique', () => { - expect( - mergeArrays(x => x.join(','), [[1, 2], [2, 4]], [[1, 2]]) - ).to.deep.equal([[1, 2], [2, 4]]); - }); - it('merges provides arrays of tuples, leaving only unique', () => { - expect( - mergeArrays( - x => x.join(','), - [[1, 2], [2, 4]], - [[1, 2]], - [[400, 500], [500, 600]] - ) - ).to.deep.equal([[1, 2], [2, 4], [400, 500], [500, 600]]); - }); -}); - -describe('documentFocus', () => { - it('calls hasFocus function if it present, converting boolean to an int 0/1 value, returns undefined otherwise', () => { - expect( - documentFocus({ - hasFocus: () => true - }) - ).to.equal(1); - expect( - documentFocus({ - hasFocus: () => false - }) - ).to.equal(0); - expect(documentFocus({})).to.be.undefined; - }); -}); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js deleted file mode 100644 index d7f20c434ca..00000000000 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ /dev/null @@ -1,372 +0,0 @@ -import { expect } from 'chai'; -import { - spec as adapter, - SUPPORTED_ID_SYSTEMS, - createDomain, - hashCode, - extractPID, - extractCID, - extractSubDomain, - getStorageItem, - setStorageItem, - tryParseJSON, - getUniqueDealId, - getNextDealId, - getVidazooSessionId, -} from 'modules/vidazooBidAdapter.js'; -import * as utils from 'src/utils.js'; -import { version } from 'package.json'; -import { useFakeTimers } from 'sinon'; - -const SUB_DOMAIN = 'openrtb'; - -const BID = { - 'bidId': '2d52001cabd527', - 'adUnitCode': 'div-gpt-ad-12345-0', - 'params': { - 'subDomain': SUB_DOMAIN, - 'cId': '59db6b3b4ffaa70004f45cdc', - 'pId': '59ac17c192832d0011283fe3', - 'bidFloor': 0.1, - 'ext': { - 'param1': 'loremipsum', - 'param2': 'dolorsitamet' - } - }, - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', - 'sizes': [[300, 250], [300, 600]], - 'bidderRequestId': '1fdb5ff1b6eaa7', - 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a' -}; - -const BIDDER_REQUEST = { - 'gdprConsent': { - 'consentString': 'consent_string', - 'gdprApplies': true - }, - 'uspConsent': 'consent_string', - 'refererInfo': { - 'referer': 'https://www.greatsite.com' - } -}; - -const SERVER_RESPONSE = { - body: { - results: [{ - 'ad': '', - 'price': 0.8, - 'creativeId': '12610997325162499419', - 'exp': 30, - 'width': 300, - 'height': 250, - 'cookies': [{ - 'src': 'https://sync.com', - 'type': 'iframe' - }, { - 'src': 'https://sync.com', - 'type': 'img' - }] - }] - } -}; - -const REQUEST = { - data: { - width: 300, - height: 250, - bidId: '2d52001cabd527' - } -}; - -describe('VidazooBidAdapter', 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 () { - sandbox = sinon.sandbox.create(); - sandbox.stub(Date, 'now').returns(1000); - }); - - it('should build request for each size', function () { - const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.referer); - 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', - cb: 1000, - bidFloor: 0.1, - bidId: '2d52001cabd527', - adUnitCode: 'div-gpt-ad-12345-0', - publisherId: '59ac17c192832d0011283fe3', - dealId: 1, - sessionId: '', - uniqueDealId: `${hashUrl}_${Date.now().toString()}`, - bidderVersion: adapter.version, - prebidVersion: version, - res: `${window.top.screen.width}x${window.top.screen.height}`, - 'ext.param1': 'loremipsum', - 'ext.param2': 'dolorsitamet', - } - }); - }); - - after(function () { - 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://prebid.cootlogix.com/api/sync/iframe/?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://prebid.cootlogix.com/api/sync/image/?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: '' - }); - }); - - 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 'digitrustid': return { data: { id: id } }; - 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('vidazoo session id', function () { - it('should get undefined vidazoo session id', function () { - const sessionId = getVidazooSessionId(); - expect(sessionId).to.be.empty; - }); - - it('should get vidazoo session id from storage', function () { - const vidSid = '1234-5678'; - window.localStorage.setItem('vidSid', vidSid); - const sessionId = getVidazooSessionId(); - expect(sessionId).to.be.equal(vidSid); - }); - }); - - describe('deal id', function () { - const key = 'myDealKey'; - - it('should get the next deal id', function () { - const dealId = getNextDealId(key); - const nextDealId = getNextDealId(key); - expect(dealId).to.be.equal(1); - expect(nextDealId).to.be.equal(2); - }); - - it('should get the first deal id on expiration', function (done) { - setTimeout(function () { - const dealId = getNextDealId(key, 100); - expect(dealId).to.be.equal(1); - done(); - }, 200); - }); - }); - - describe('unique deal id', function () { - const key = 'myKey'; - let uniqueDealId; - uniqueDealId = getUniqueDealId(key); - - 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 () { - const current = getUniqueDealId(key, 100); - expect(current).to.not.be.equal(uniqueDealId); - }); - }); - - describe('storage utils', function () { - 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); - }); - }); -}); diff --git a/test/spec/modules/videoNowBidAdapter_spec.js b/test/spec/modules/videoNowBidAdapter_spec.js deleted file mode 100644 index a419c73456b..00000000000 --- a/test/spec/modules/videoNowBidAdapter_spec.js +++ /dev/null @@ -1,599 +0,0 @@ -import { expect } from 'chai' -import { spec } from 'modules/videoNowBidAdapter.js' -import { replaceAuctionPrice } from 'src/utils.js' -import * as utils from 'src/utils.js'; - -// childNode.remove polyfill for ie11 -// suggested by: https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove - -// from:https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md -(function (arr) { - arr.forEach(function (item) { - if (item.hasOwnProperty('remove')) { - return; - } - Object.defineProperty(item, 'remove', { - configurable: true, - enumerable: true, - writable: true, - value: function remove() { - if (this.parentNode === null) { - return; - } - this.parentNode.removeChild(this); - } - }); - }); -})([Element.prototype, CharacterData.prototype, DocumentType.prototype]); - -const placementId = 'div-gpt-ad-1438287399331-1' -const LS_ITEM_NAME = 'videonow-config' - -const getValidServerResponse = () => { - const serverResponse = { - body: { - id: '111-111', - bidid: '2955a162-699e-4811-ce88-5c3ac973e73c', - cur: 'RUB', - seatbid: [ - { - bid: [ - { - id: 'e3bf2b82e3e9485113fad6c9b27f8768.1', - impid: '1', - price: 10.97, - nurl: 'https://localhost:8086/event/nurl', - netRevenue: false, - ttl: 800, - adm: '', - crid: 'e3bf2b82e3e9485113fad6c9b27f8768.1', - h: 640, - w: 480, - ext: { - init: 'https://localhost:8086/vn_init.js', - module: { - min: 'https://localhost:8086/vn_module.js', - log: 'https://localhost:8086/vn_module.js?log=1' - }, - format: { - name: 'flyRoll', - }, - }, - - }, - ], - group: 0, - }, - ], - price: 10, - ext: { - placementId, - pixels: [ - 'https://localhost:8086/event/pxlcookiematching?uiid=1', - 'https://localhost:8086/event/pxlcookiematching?uiid=2', - ], - iframes: [ - 'https://localhost:8086/event/ifrcookiematching?uiid=1', - 'https://localhost:8086/event/ifrcookiematching?uiid=2', - ], - }, - }, - headers: {}, - } - - return JSON.parse(JSON.stringify(serverResponse)) -} - -describe('videonowAdapterTests', function() { - describe('bidRequestValidity', function() { - it('bidRequest with pId', function() { - expect(spec.isBidRequestValid({ - bidder: 'videonow', - params: { - pId: '86858', - }, - })).to.equal(true) - }) - - it('bidRequest without pId', function() { - expect(spec.isBidRequestValid({ - bidder: 'videonow', - params: { - nomater: 86858, - }, - })).to.equal(false) - - it('bidRequest is empty', function() { - expect(spec.isBidRequestValid({})).to.equal(false) - }) - - it('bidRequest is undefned', function() { - expect(spec.isBidRequestValid(undefined)).to.equal(false) - }) - }) - - describe('bidRequest', function() { - const validBidRequests = [ - { - bidder: 'videonow', - params: { - pId: '1', - placementId, - url: 'https://localhost:8086/bid?p=exists', - bidFloor: 10, - cur: 'RUB' - }, - crumbs: { - pubcid: 'feded041-35dd-4b54-979a-6d7805abfa75', - }, - mediaTypes: { - banner: { - sizes: [[640, 480], [320, 200]] - }, - }, - adUnitCode: 'test-ad', - transactionId: '676403c7-09c9-4b56-be82-e7cae81f40b9', - sizes: [[640, 480], [320, 200]], - bidId: '268c309f46390d', - bidderRequestId: '1dfdd514c36ef6', - auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', - src: 'client', - bidRequestsCount: 1, - }, - ] - - const bidderRequest = { - bidderCode: 'videonow', - auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', - bidderRequestId: '1dfdd514c36ef6', - bids: [ - { - bidder: 'videonow', - params: { - pId: '1', - placementId, - url: 'https://localhost:8086/bid', - bidFloor: 10, - cur: 'RUB', - }, - crumbs: { - pubcid: 'feded041-35dd-4b54-979a-6d7805abfa75', - }, - mediaTypes: { - banner: { - sizes: [[640, 480], [320, 200]], - }, - }, - adUnitCode: 'test-ad', - transactionId: '676403c7-09c9-4b56-be82-e7cae81f40b9', - sizes: [[640, 480], [320, 200]], - bidId: '268c309f46390d', - bidderRequestId: '1dfdd514c36ef6', - auctionId: '4d523546-889a-4029-9a79-13d3c69f9922', - src: 'client', - bidRequestsCount: 1, - }, - ], - auctionStart: 1565794308584, - timeout: 3000, - refererInfo: { - referer: 'https://localhost:8086/page', - reachedTop: true, - numIframes: 0, - stack: [ - 'https://localhost:8086/page', - ], - }, - start: 1565794308589, - } - - const requests = spec.buildRequests(validBidRequests, bidderRequest) - const request = (requests && requests.length && requests[0]) || {} - - it('bidRequest count', function() { - expect(requests.length).to.equal(1) - }) - - it('bidRequest method', function() { - expect(request.method).to.equal('POST') - }) - - it('bidRequest url', function() { - expect(request.url).to.equal('https://localhost:8086/bid?p=exists&profile_id=1') - }) - - it('bidRequest data', function() { - const data = request.data - expect(data.aid).to.be.eql(validBidRequests[0].params.aid) - expect(data.id).to.be.eql(validBidRequests[0].bidId) - expect(data.sizes).to.be.eql(validBidRequests[0].sizes) - }) - - describe('bidRequest advanced', function() { - const bidderRequestEmptyParamsAndExtParams = { - bidder: 'videonow', - params: { - pId: '1', - }, - ext: { - p1: 'ext1', - p2: 'ext2', - }, - } - - it('bidRequest count', function() { - const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) - expect(requests.length).to.equal(1) - }) - - it('bidRequest default url', function() { - const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) - const request = (requests && requests.length && requests[0]) || {} - expect(request.url).to.equal('https://bidder.videonow.ru/prebid?profile_id=1') - }) - - it('bidRequest default currency', function() { - const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) - const request = (requests && requests.length && requests[0]) || {} - const data = (request && request.data) || {} - expect(data.cur).to.equal('RUB') - }) - - it('bidRequest ext parameters ', function() { - const requests = spec.buildRequests([bidderRequestEmptyParamsAndExtParams], bidderRequest) - const request = (requests && requests.length && requests[0]) || {} - const data = (request && request.data) || {} - expect(data['ext_p1']).to.equal('ext1') - expect(data['ext_p2']).to.equal('ext2') - }) - - it('bidRequest without params', function() { - const bidderReq = { - bidder: 'videonow', - } - const requests = spec.buildRequests([bidderReq], bidderRequest) - expect(requests.length).to.equal(1) - }) - }) - }) - - describe('onBidWon', function() { - const cpm = 10 - const nurl = 'https://fakedomain.nld?price=${AUCTION_PRICE}' - const imgSrc = replaceAuctionPrice(nurl, cpm) - - beforeEach(function() { - sinon.stub(utils, 'triggerPixel') - }) - - afterEach(function() { - utils.triggerPixel.restore() - }) - - it('Should not create nurl pixel if bid is undefined', function() { - spec.onBidWon() - expect(utils.triggerPixel.called).to.equal(false); - }) - - it('Should not create nurl pixel if bid does not contains nurl', function() { - spec.onBidWon({}) - expect(utils.triggerPixel.called).to.equal(false); - }) - - it('Should create nurl pixel if bid nurl', function() { - spec.onBidWon({ nurl, cpm }) - expect(utils.triggerPixel.calledWith(imgSrc)).to.equal(true); - }) - }) - - describe('getUserSyncs', function() { - it('Should return an empty array if not get serverResponses', function() { - expect(spec.getUserSyncs({}).length).to.equal(0) - }) - - it('Should return an empty array if get serverResponses as empty array', function() { - expect(spec.getUserSyncs({}, []).length).to.equal(0) - }) - - it('Should return an empty array if serverResponses has no body', function() { - const serverResp = getValidServerResponse() - delete serverResp.body - const syncs = spec.getUserSyncs({}, [serverResp]) - expect(syncs.length).to.equal(0) - }) - - it('Should return an empty array if serverResponses has no ext', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.ext - const syncs = spec.getUserSyncs({}, [serverResp]) - expect(syncs.length).to.equal(0) - }) - - it('Should return an array', function() { - const serverResp = getValidServerResponse() - const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [serverResp]) - expect(syncs.length).to.equal(4) - }) - - it('Should return pixels', function() { - const serverResp = getValidServerResponse() - const syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [serverResp]) - expect(syncs.length).to.equal(2) - expect(syncs[0].type).to.equal('image') - expect(syncs[1].type).to.equal('image') - }) - - it('Should return iframes', function() { - const serverResp = getValidServerResponse() - const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [serverResp]) - expect(syncs.length).to.equal(2) - expect(syncs[0].type).to.equal('iframe') - expect(syncs[1].type).to.equal('iframe') - }) - }) - - describe('interpretResponse', function() { - const bidRequest = { - method: 'POST', - url: 'https://localhost:8086/bid?profile_id=1', - data: { - id: '217b8ab59a18e8', - cpm: 10, - sizes: [[640, 480], [320, 200]], - cur: 'RUB', - placementId, - ref: 'https://localhost:8086/page', - }, - } - - it('Should have only one bid', function() { - const serverResponse = getValidServerResponse() - const result = spec.interpretResponse(serverResponse, bidRequest) - expect(result.length).to.equal(1) - }) - - it('Should have required keys', function() { - const serverResponse = getValidServerResponse() - const result = spec.interpretResponse(serverResponse, bidRequest) - const bid = serverResponse.body.seatbid[0].bid[0] - const res = result[0] - expect(res.requestId).to.be.eql(bidRequest.data.id) - expect(res.cpm).to.be.eql(bid.price) - expect(res.creativeId).to.be.eql(bid.crid) - expect(res.netRevenue).to.be.a('boolean') - expect(res.ttl).to.be.eql(bid.ttl) - expect(res.renderer).to.be.a('Object') - expect(res.renderer.render).to.be.a('function') - }) - - it('Should return an empty array if empty or no bids in response', function() { - expect(spec.interpretResponse({ body: '' }, {}).length).to.equal(0) - }) - - it('Should return an empty array if bidRequest\'s data is absent', function() { - const serverResponse = getValidServerResponse() - expect(spec.interpretResponse(serverResponse, undefined).length).to.equal(0) - }) - - it('Should return an empty array if bidRequest\'s data is not contains bidId ', function() { - const serverResponse = getValidServerResponse() - expect(spec.interpretResponse(serverResponse, { data: {} }).length).to.equal(0) - }) - - it('Should return an empty array if bidRequest\'s data bidId is undefined', function() { - const serverResponse = getValidServerResponse() - expect(spec.interpretResponse(serverResponse, { data: { id: null } }).length).to.equal(0) - }) - - it('Should return an empty array if serverResponse do not contains seatbid', function() { - expect(spec.interpretResponse({ body: {} }, bidRequest).length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s seatbid is empty', function() { - expect(spec.interpretResponse({ body: { seatbid: [] } }, bidRequest).length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s placementId is undefined', function() { - expect(spec.interpretResponse({ body: { seatbid: [1, 2] } }, bidRequest).length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s id in the bid is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].id - let res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s price in the bid is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].price - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s price in the bid is 0', function() { - const serverResp = getValidServerResponse() - serverResp.body.seatbid[0].bid[0].price = 0 - const res = spec.interpretResponse(serverResp, bidRequest) - - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s init in the bid\'s ext is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].ext.init - const res = spec.interpretResponse(serverResp, bidRequest) - - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s module in the bid\'s ext is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].ext.module - const res = spec.interpretResponse(serverResp, bidRequest) - - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s adm in the bid is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].adm - const res = spec.interpretResponse(serverResp, bidRequest) - - expect(res.length).to.equal(0) - }) - - it('Should return an empty array if serverResponse\'s the bid\'s ext is undefined', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].ext - const res = spec.interpretResponse(serverResp, bidRequest) - - expect(res.length).to.equal(0) - }) - - it('Default ttl is 300', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].ttl - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - expect(res[0].ttl).to.equal(300) - }) - - it('Default netRevenue is true', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].netRevenue - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - expect(res[0].netRevenue).to.be.true; - }) - - it('Default currency is RUB', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.cur - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - expect(res[0].currency).to.equal('RUB') - }) - - describe('different module paths', function() { - beforeEach(function() { - window.localStorage && localStorage.setItem(LS_ITEM_NAME, '{}') - }) - - afterEach(function() { - const serverResp = getValidServerResponse() - const { module: { log, min }, init } = serverResp.body.seatbid[0].bid[0].ext - remove(init) - remove(log) - remove(min) - - function remove(src) { - if (!src) return - const d = document.querySelectorAll(`script[src^="${src}"]`) - // using the Array.prototype.forEach as a workaround for IE11... - // see https://developer.mozilla.org/en-US/docs/Web/API/NodeList - d && d.length && Array.prototype.forEach.call(d, el => el && el.remove()) - } - }) - - it('should use prod module by default', function() { - const serverResp = getValidServerResponse() - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - - const renderer = res[0].renderer - expect(renderer).to.be.an('object') - expect(renderer.url).to.equal(serverResp.body.seatbid[0].bid[0].ext.module.min) - }) - - it('should use "log" module if "prod" is not exists', function() { - const serverResp = getValidServerResponse() - delete serverResp.body.seatbid[0].bid[0].ext.module.min - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - - const renderer = res[0].renderer - expect(renderer).to.be.an('object') - expect(renderer.url).to.equal(serverResp.body.seatbid[0].bid[0].ext.module.log) - }) - - it('should correct combine src for init', function() { - const serverResp = getValidServerResponse() - - const src = `${serverResp.body.seatbid[0].bid[0].ext.init}?profileId=1` - const placementElement = document.createElement('div') - placementElement.setAttribute('id', placementId) - - const resp = spec.interpretResponse(serverResp, bidRequest) - expect(resp.length).to.equal(1) - - const renderer = resp[0].renderer - expect(renderer).to.be.an('object') - - document.body.appendChild(placementElement) - - renderer.render() - - // const res = document.querySelectorAll(`script[src="${src}"]`) - // expect(res.length).to.equal(1) - }) - - it('should correct combine src for init if init url contains "?"', function() { - const serverResp = getValidServerResponse() - - serverResp.body.seatbid[0].bid[0].ext.init += '?div=1' - const src = `${serverResp.body.seatbid[0].bid[0].ext.init}&profileId=1` - - const placementElement = document.createElement('div') - placementElement.setAttribute('id', placementId) - - const resp = spec.interpretResponse(serverResp, bidRequest) - expect(resp.length).to.equal(1) - - const renderer = resp[0].renderer - expect(renderer).to.be.an('object') - - document.body.appendChild(placementElement) - - renderer.render() - - // const res = document.querySelectorAll(`script[src="${src}"]`) - // expect(res.length).to.equal(1) - }) - }) - - describe('renderer object', function() { - it('execute renderer.render() should create window.videonow object', function() { - const serverResp = getValidServerResponse() - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - - const renderer = res[0].renderer - expect(renderer).to.be.an('object') - expect(renderer.render).to.a('function') - - const doc = window.document - const placementElement = doc.createElement('div') - placementElement.setAttribute('id', placementId) - doc.body.appendChild(placementElement) - - renderer.render() - expect(window.videonow).to.an('object') - }) - }) - - it('execute renderer.render() should not create window.videonow object if placement element not found', function() { - const serverResp = getValidServerResponse() - const res = spec.interpretResponse(serverResp, bidRequest) - expect(res.length).to.equal(1) - - const renderer = res[0].renderer - expect(renderer).to.be.an('object') - expect(renderer.render).to.a('function') - - renderer.render() - expect(window.videonow).to.be.undefined - }) - }) - }) -}) diff --git a/test/spec/modules/videofyBidAdapter_spec.js b/test/spec/modules/videofyBidAdapter_spec.js deleted file mode 100644 index 270eefd1efc..00000000000 --- a/test/spec/modules/videofyBidAdapter_spec.js +++ /dev/null @@ -1,253 +0,0 @@ -import { spec } from 'modules/videofyBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as utils from '../../../src/utils.js'; - -const { expect } = require('chai'); - -describe('Videofy Bid Adapter Test', 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': 'videofy', - 'params': { - 'AV_PUBLISHERID': '123456', - 'AV_CHANNELID': '123456' - }, - 'adUnitCode': 'video1', - 'sizes': [[300, 250], [640, 480]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', - 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - something: 'is wrong' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bid2Requests = [ - { - 'bidder': 'videofy', - 'params': { - 'AV_PUBLISHERID': '123456', - 'AV_CHANNELID': '123456' - }, - 'adUnitCode': 'test1', - 'sizes': [[300, 250], [640, 480]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', - 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', - } - ]; - let bid1Request = [ - { - 'bidder': 'videofy', - 'params': { - 'AV_PUBLISHERID': '123456', - 'AV_CHANNELID': '123456' - }, - 'adUnitCode': 'test1', - 'sizes': [640, 480], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'requestId': 'a09c66c3-53e3-4428-b296-38fc08e7cd2a', - 'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d', - } - ]; - - it('Test 2 requests', function () { - const requests = spec.buildRequests(bid2Requests); - expect(requests.length).to.equal(2); - const r1 = requests[0]; - const d1 = requests[0].data; - expect(d1).to.have.property('AV_PUBLISHERID'); - expect(d1.AV_PUBLISHERID).to.equal('123456'); - expect(d1).to.have.property('AV_CHANNELID'); - expect(d1.AV_CHANNELID).to.equal('123456'); - expect(d1).to.have.property('AV_WIDTH'); - expect(d1.AV_WIDTH).to.equal(300); - expect(d1).to.have.property('AV_HEIGHT'); - expect(d1.AV_HEIGHT).to.equal(250); - expect(d1).to.have.property('AV_URL'); - expect(d1).to.have.property('cb'); - expect(d1).to.have.property('s2s'); - expect(d1.s2s).to.equal('1'); - expect(d1).to.have.property('pbjs'); - expect(d1.pbjs).to.equal(1); - expect(r1).to.have.property('url'); - expect(r1.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/'); - const r2 = requests[1]; - const d2 = requests[1].data; - expect(d2).to.have.property('AV_PUBLISHERID'); - expect(d2.AV_PUBLISHERID).to.equal('123456'); - expect(d2).to.have.property('AV_CHANNELID'); - expect(d2.AV_CHANNELID).to.equal('123456'); - expect(d2).to.have.property('AV_WIDTH'); - expect(d2.AV_WIDTH).to.equal(640); - expect(d2).to.have.property('AV_HEIGHT'); - expect(d2.AV_HEIGHT).to.equal(480); - expect(d2).to.have.property('AV_URL'); - expect(d2).to.have.property('cb'); - expect(d2).to.have.property('s2s'); - expect(d2.s2s).to.equal('1'); - expect(d2).to.have.property('pbjs'); - expect(d2.pbjs).to.equal(1); - expect(r2).to.have.property('url'); - expect(r2.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/'); - }); - - it('Test 1 request', function () { - const requests = spec.buildRequests(bid1Request); - expect(requests.length).to.equal(1); - const r = requests[0]; - const d = requests[0].data; - expect(d).to.have.property('AV_PUBLISHERID'); - expect(d.AV_PUBLISHERID).to.equal('123456'); - expect(d).to.have.property('AV_CHANNELID'); - expect(d.AV_CHANNELID).to.equal('123456'); - expect(d).to.have.property('AV_WIDTH'); - expect(d.AV_WIDTH).to.equal(640); - expect(d).to.have.property('AV_HEIGHT'); - expect(d.AV_HEIGHT).to.equal(480); - expect(d).to.have.property('AV_URL'); - expect(d).to.have.property('cb'); - expect(d).to.have.property('s2s'); - expect(d.s2s).to.equal('1'); - expect(d).to.have.property('pbjs'); - expect(d.pbjs).to.equal(1); - expect(r).to.have.property('url'); - expect(r.url).to.contain('https://servx.srv-mars.com/api/adserver/vast3/'); - }); - }); - - describe('interpretResponse', function () { - let bidRequest = { - 'url': 'https://servx.srv-mars.com/api/adserver/vast3/', - 'data': { - 'bidId': '253dcb69fb2577', - AV_PUBLISHERID: '55b78633181f4603178b4568', - AV_CHANNELID: '55b7904d181f46410f8b4568', - } - }; - let serverResponse = {}; - serverResponse.body = 'FORDFORD00:00:15'; - - it('Check bid interpretResponse', function () { - const BIDDER_CODE = 'videofy'; - let bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses.length).to.equal(1); - let bidResponse = bidResponses[0]; - expect(bidResponse.requestId).to.equal(bidRequest.data.bidId); - expect(bidResponse.bidderCode).to.equal(BIDDER_CODE); - expect(bidResponse.cpm).to.equal('2'); - expect(bidResponse.ttl).to.equal(600); - expect(bidResponse.currency).to.equal('USD'); - expect(bidResponse.netRevenue).to.equal(true); - expect(bidResponse.mediaType).to.equal('video'); - }); - - it('safely handles XML parsing failure from invalid bid response', function () { - let invalidServerResponse = {}; - invalidServerResponse.body = ''; - - let result = spec.interpretResponse(invalidServerResponse, bidRequest); - expect(result.length).to.equal(0); - }); - - it('handles nobid responses', function () { - let nobidResponse = {}; - nobidResponse.body = ''; - - let result = spec.interpretResponse(nobidResponse, bidRequest); - expect(result.length).to.equal(0); - }); - }); - - describe('getUserSyncs', function () { - it('Check get sync pixels from response', function () { - let pixelUrl = 'https://sync.pixel.url/sync'; - let pixelEvent = 'inventory'; - let pixelType = '3'; - let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; - let bidResponse = 'FORDFORD00:00:15'; - let serverResponse = [ - {body: bidResponse} - ]; - let syncPixels = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); - expect(syncPixels.length).to.equal(1); - let pixel = syncPixels[0]; - expect(pixel.url).to.equal(pixelUrl); - expect(pixel.type).to.equal('iframe'); - }); - }); - - describe('on bidWon', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onBidWon).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onBidWon({}); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(true); - }); - }); - - describe('on Timeout', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onTimeout({}); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(true); - }); - }); - - describe('on Set Targeting', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onSetTargeting).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onSetTargeting({}); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(true); - }); - }); -}); diff --git a/test/spec/modules/videoreachBidAdapter_spec.js b/test/spec/modules/videoreachBidAdapter_spec.js deleted file mode 100644 index e5c6b9b30ad..00000000000 --- a/test/spec/modules/videoreachBidAdapter_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/videoreachBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -const ENDPOINT_URL = 'https://a.videoreach.com/hb/'; - -describe('videoreachBidAdapter', function () { - describe('isBidRequestValid', function () { - let bid = { - 'params': { - 'TagId': 'ABCDE' - }, - 'bidId': '242d506d4e4f15', - 'bidderRequestId': '1893a2136a84a2', - 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', - 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622' - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'TagId': '' - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'videoreach', - 'params': { - 'TagId': 'ABCDE' - }, - 'adUnitCode': 'adzone', - 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', - 'sizes': [[1, 1]], - 'bidId': '242d506d4e4f15', - 'bidderRequestId': '1893a2136a84a2', - 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', - 'mediaTypes': { - 'banner': { - 'sizes': [1, 1] - }, - } - } - ]; - - it('send bid request to endpoint', function () { - const request = spec.buildRequests(bidRequests); - - expect(request.url).to.equal(ENDPOINT_URL); - expect(request.method).to.equal('POST'); - }); - - it('send bid request with GDPR to endpoint', function () { - let consentString = 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'; - - let bidderRequest = { - 'gdprConsent': { - 'consentString': consentString, - 'gdprApplies': true - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.gdpr.consent_required).to.exist; - expect(payload.gdpr.consent_string).to.equal(consentString); - }); - }); - - describe('interpretResponse', function () { - let serverResponse = - { - 'body': { - 'responses': [{ - 'bidId': '242d506d4e4f15', - 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', - 'cpm': 10.0, - 'width': '1', - 'height': '1', - 'ad': '', - 'ttl': 360, - 'creativeId': '5cb5dc9375c0e', - 'netRevenue': true, - 'currency': 'EUR', - 'sync': ['https:\/\/SYNC_URL'] - }] - } - }; - - it('should handle response', function() { - let expectedResponse = [ - { - cpm: 10.0, - width: '1', - height: '1', - currency: 'EUR', - netRevenue: true, - ttl: 360, - ad: '', - requestId: '242d506d4e4f15', - creativeId: '5cb5dc9375c0e' - } - ]; - - let result = spec.interpretResponse(serverResponse); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - }); - - it('should handles empty response', function() { - let serverResponse = { - 'body': { - 'responses': [] - } - }; - - let result = spec.interpretResponse(serverResponse); - expect(result.length).to.equal(0); - }); - - describe('getUserSyncs', () => { - it('should push user sync images if enabled', () => { - const syncOptions = { pixelEnabled: true }; - const syncs = spec.getUserSyncs(syncOptions, [serverResponse]); - - expect(syncs[0]).to.deep.equal({ - type: 'image', - url: 'https://SYNC_URL' - }); - }) - }); - }); -}); diff --git a/test/spec/modules/vmgBidAdapter_spec.js b/test/spec/modules/vmgBidAdapter_spec.js deleted file mode 100644 index 41aa077adc7..00000000000 --- a/test/spec/modules/vmgBidAdapter_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/vmgBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('VmgAdapter', 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 bidRequest = { - adUnitCode: 'div-0', - auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', - bidId: '280e2eb8ac3891', - bidRequestsCount: 1, - bidder: 'vmg', - bidderRequestId: '14690d27b056c8', - mediaTypes: { - banner: { - sizes: [ [ 970, 250 ] ] - } - }, - sizes: [ 970, 250 ], - src: 'client', - transactionId: 'af62f065-dfa7-4564-8cb2-d277dc6069f2' - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bidRequest)).to.equal(true); - }); - }) - - describe('buildRequests', function () { - let validBidRequests = [ - { - adUnitCode: 'div-0', - auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', - bidId: '280e2eb8ac3891', - bidRequestsCount: 1, - bidder: 'vmg', - bidderRequestId: '14690d27b056c8', - mediaTypes: { - banner: { - sizes: [ [ 970, 250 ] ] - } - }, - sizes: [ 970, 250 ], - src: 'client', - transactionId: 'af62f065-dfa7-4564-8cb2-d277dc6069f2' - } - ]; - - let bidderRequest = { - auctionId: 'd69cdd3f-75e3-42dc-b313-e54c0a99c757', - auctionStart: 1549316149227, - bidderCode: 'vmg', - bidderRequestId: '14690d27b056c8', - refererInfo: { - canonicalUrl: undefined, - numIframes: 0, - reachedTop: true, - referer: 'https://vmg.nyc/public_assets/adapt/prebid.html', - stack: [ 'https://vmg.nyc/public_assets/adapt/prebid.html' ] - }, - start: 1549316149229, - timeout: 1000 - }; - - it('buildRequests fires', function () { - let request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request).to.exist; - expect(request.method).to.equal('POST'); - expect(request.data).to.exist; - }); - }) - - describe('interpretResponse', function () { - let serverResponse = {}; - serverResponse.body = { - 'div-0': ['test'] - }; - - var bidRequest = { - data: '[{"adUnitCode":"div-0","referer":"https://vmg.nyc/public_assets/adapt/prebid.html","bidId":"280e2eb8ac3891"}]', - method: 'POST', - url: 'https://predict.vmg.nyc' - }; - - it('interpresResponse fires', function () { - let bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses[0].dealId[0]).to.equal('test'); - }); - }); -}); diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js deleted file mode 100644 index 729b0a723e4..00000000000 --- a/test/spec/modules/vrtcalBidAdapter_spec.js +++ /dev/null @@ -1,135 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/vrtcalBidAdapter.js'; - -describe('Vrtcal Adapter', function () { - let bid = { - bidId: 'bidID0001', - bidder: 'vrtcal', - bidderRequestId: 'brID0001', - auctionId: 'auID0001', - sizes: [[300, 250]], - transactionId: 'tid0001', - adUnitCode: 'vrtcal-test-adunit' - }; - - describe('isBidRequestValid', function () { - it('Should return true when base params as set', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false when bid.bidId is blank', function () { - bid.bidId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false when bid.auctionId is blank', function () { - bid.auctionId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequests = spec.buildRequests([bid]); - - let serverRequest = serverRequests[0]; - - 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://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804'); - }); - - it('Returns valid data if array of bids is valid', function () { - let data = JSON.parse(serverRequest.data); - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('prebidJS', 'prebidAdUnitCode', 'id', 'imp', 'site', 'device'); - expect(data.prebidJS).to.not.equal(''); - expect(data.prebidAdUnitCode).to.not.equal(''); - }); - - it('Sets width and height based on existence of bid.mediaTypes.banner', function () { - let data = JSON.parse(serverRequest.data); - if (typeof (bid.mediaTypes) !== 'undefined' && typeof (bid.mediaTypes.banner) !== 'undefined' && typeof (bid.mediaTypes.banner.sizes) !== 'undefined') { - expect(data.imp[0].banner.w).to.equal(bid.mediaTypes.banner.sizes[0][0]); - expect(data.imp[0].banner.h).to.equal(bid.mediaTypes.banner.sizes[0][1]); - } else { - expect(data.imp[0].banner.w).to.equal(bid.sizes[0][0]); - expect(data.imp[0].banner.h).to.equal(bid.sizes[0][1]); - } - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequests = spec.buildRequests([]); - expect(serverRequests).to.be.an('array').that.is.empty; - }); - }); - - describe('interpretResponse', function () { - let bid = { - bidId: 'bidID0001', - bidder: 'vrtcal', - bidderRequestId: 'brID0001', - auctionId: 'auID0001', - sizes: [[300, 250]], - transactionId: 'tid0001', - adUnitCode: 'vrtcal-test-adunit' - }; - - let serverRequests = spec.buildRequests([bid]); - - let resObject = {body: {id: 'vrtcal-test-id', width: 300, height: 250, seatbid: [{bid: [{price: 3.0, w: 300, h: 250, crid: 'testcrid', adm: 'testad', nurl: 'https://vrtcal.com/faketracker'}]}], currency: 'USD', netRevenue: true, ttl: 900}}; - - let serverResponses = spec.interpretResponse(resObject, serverRequests); - - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'nurl'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.nurl).to.be.a('string'); - } - - it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - }); - - describe('onBidWon', function () { - let bid = { - bidId: '2dd581a2b6281d', - bidder: 'vrtcal', - bidderRequestId: '145e1d6a7837c9', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', - adUnitCode: 'vrtcal-test-adunit' - }; - - let serverRequests = spec.buildRequests([bid]); - let resObject = {body: {id: 'vrtcal-test-id', width: 300, height: 250, seatbid: [{bid: [{price: 3.0, w: 300, h: 250, crid: 'testcrid', adm: 'testad', nurl: 'https://vrtcal.com/faketracker'}]}], currency: 'USD', netRevenue: true, ttl: 900}}; - let serverResponses = spec.interpretResponse(resObject, serverRequests); - let wonbid = serverResponses[0]; - - it('Returns true is nurl is good/not blank', function () { - expect(wonbid.nurl).to.not.equal(''); - expect(spec.onBidWon(wonbid)).to.be.true; - }); - }); -}); diff --git a/test/spec/modules/vubleBidAdapter_spec.js b/test/spec/modules/vubleBidAdapter_spec.js deleted file mode 100644 index 2f0d1c1e262..00000000000 --- a/test/spec/modules/vubleBidAdapter_spec.js +++ /dev/null @@ -1,412 +0,0 @@ -// import or require modules necessary for the test, e.g.: - -import {expect} from 'chai'; -import {spec as adapter} from 'modules/vubleBidAdapter.js'; -import * as utils from 'src/utils.js'; - -describe('VubleAdapter', function () { - describe('Check methods existance', 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'); - }); - }); - - describe('Check method isBidRequestValid return', function () { - let bid = { - bidder: 'vuble', - params: { - env: 'net', - pubId: '3', - zoneId: '12345', - floorPrice: 5.00 // optional - }, - sizes: [[640, 360]], - mediaTypes: { - video: { - context: 'instream' - } - }, - }; - let bid2 = { - bidder: 'vuble', - params: { - env: 'net', - pubId: '3', - zoneId: '12345', - floorPrice: 5.00 // optional - }, - mediaTypes: { - video: { - context: 'instream', - playerSize: [640, 360] - } - }, - }; - - it('should be true', function () { - expect(adapter.isBidRequestValid(bid)).to.be.true; - expect(adapter.isBidRequestValid(bid2)).to.be.true; - }); - - it('should be false because the sizes are missing or in the wrong format', function () { - let wrongBid = utils.deepClone(bid); - wrongBid.sizes = '640360'; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - - wrongBid = utils.deepClone(bid); - delete wrongBid.sizes; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - }); - - it('should be false because the mediaType is missing or wrong', function () { - let wrongBid = utils.deepClone(bid); - wrongBid.mediaTypes = {}; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - - wrongBid = utils.deepClone(bid); - delete wrongBid.mediaTypes; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - }); - - it('should be false because the env is missing or wrong', function () { - let wrongBid = utils.deepClone(bid); - wrongBid.params.env = 'us'; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - - wrongBid = utils.deepClone(bid); - delete wrongBid.params.env; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - }); - - it('should be false because params.pubId is missing', function () { - let wrongBid = utils.deepClone(bid); - delete wrongBid.params.pubId; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - }); - - it('should be false because params.zoneId is missing', function () { - let wrongBid = utils.deepClone(bid); - delete wrongBid.params.zoneId; - expect(adapter.isBidRequestValid(wrongBid)).to.be.false; - }); - }); - - describe('Check buildRequests method', function () { - // Bids to be formatted - let bid1 = { - bidder: 'vuble', - params: { - env: 'net', - pubId: '3', - zoneId: '12345', - floorPrice: 5.50 // optional - }, - sizes: [[640, 360]], - mediaTypes: { - video: { - context: 'instream' - } - }, - bidId: 'abdc', - adUnitCode: '' - }; - let bid2 = { - bidder: 'vuble', - params: { - env: 'com', - pubId: '8', - zoneId: '2468', - referrer: 'https://www.vuble.fr/' - }, - sizes: '640x360', - mediaTypes: { - video: { - context: 'outstream' - } - }, - bidId: 'efgh', - adUnitCode: 'code' - }; - let bid3 = { - bidder: 'vuble', - params: { - env: 'net', - pubId: '3', - zoneId: '3579', - }, - mediaTypes: { - video: { - context: 'instream', - playerSize: [640, 360] - } - }, - bidId: 'ijkl', - adUnitCode: '' - }; - - // Formatted requets - let request1 = { - method: 'POST', - url: 'https://player.mediabong.net/prebid/request', - data: { - width: '640', - height: '360', - pub_id: '3', - zone_id: '12345', - context: 'instream', - floor_price: 5.5, - url: '', - env: 'net', - bid_id: 'abdc', - adUnitCode: '' - } - }; - let request2 = { - method: 'POST', - url: 'https://player.mediabong.com/prebid/request', - data: { - width: '640', - height: '360', - pub_id: '8', - zone_id: '2468', - context: 'outstream', - floor_price: 0, - url: 'https://www.vuble.fr/', - env: 'com', - bid_id: 'efgh', - adUnitCode: 'code' - } - }; - let request3 = { - method: 'POST', - url: 'https://player.mediabong.net/prebid/request', - data: { - width: '640', - height: '360', - pub_id: '3', - zone_id: '3579', - context: 'instream', - floor_price: 0, - url: 'https://www.vuble.tv/', - env: 'net', - bid_id: 'ijkl', - adUnitCode: '' - } - }; - let request4 = { - method: 'POST', - url: 'https://player.mediabong.net/prebid/request', - data: { - width: '640', - height: '360', - pub_id: '3', - zone_id: '3579', - context: 'instream', - floor_price: 0, - url: '', - env: 'net', - bid_id: 'ijkl', - adUnitCode: '', - gdpr_consent: { - consent_string: 'test', - gdpr_applies: true - } - } - }; - let bidderRequest1 = { - refererInfo: { - referer: 'https://www.vuble.tv/', - reachedTop: true, - numIframes: 1, - stack: [ - 'http://example.com/page.html', - 'http://example.com/iframe1.html', - 'http://example.com/iframe2.html' - ] - } - }; - let bidderRequest2 = { - 'gdprConsent': { - consentString: 'test', - gdprApplies: true - } - }; - - it('must return the right formatted requests', function () { - expect(adapter.buildRequests([bid1, bid2])).to.deep.equal([request1, request2]); - expect(adapter.buildRequests([bid3], bidderRequest1)).to.deep.equal([request3]); - expect(adapter.buildRequests([bid3], bidderRequest2)).to.deep.equal([request4]); - }); - }); - - describe('Check interpretResponse method return', function () { - // Server's response - let response = { - body: { - status: 'ok', - cpm: 5.00, - creativeId: '2468', - url: 'https//player.mediabong.net/prebid/ad/a1b2c3d4', - dealId: 'MDB-TEST-1357' - } - }; - // bid Request - let bid = { - data: { - context: 'instream', - env: 'net', - width: '640', - height: '360', - pub_id: '3', - zone_id: '12345', - bid_id: 'abdc', - floor_price: 5.50, // optional - adUnitCode: 'code' - }, - method: 'POST', - url: 'https://player.mediabong.net/prebid/request' - }; - // Formatted reponse - let result = { - requestId: 'abdc', - cpm: 5.00, - width: '640', - height: '360', - ttl: 60, - creativeId: '2468', - dealId: 'MDB-TEST-1357', - netRevenue: true, - currency: 'USD', - vastUrl: 'https//player.mediabong.net/prebid/ad/a1b2c3d4', - mediaType: 'video' - }; - - it('should equal to the expected formatted result', function () { - expect(adapter.interpretResponse(response, bid)).to.deep.equal([result]); - }); - - it('should be empty because the status is missing or wrong', function () { - let wrongResponse = utils.deepClone(response); - wrongResponse.body.status = 'ko'; - expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; - - wrongResponse = utils.deepClone(response); - delete wrongResponse.body.status; - expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; - }); - - it('should be empty because the body is missing or wrong', function () { - let wrongResponse = utils.deepClone(response); - wrongResponse.body = [1, 2, 3]; - expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; - - wrongResponse = utils.deepClone(response); - delete wrongResponse.body; - expect(adapter.interpretResponse(wrongResponse, bid)).to.be.empty; - }); - - it('should equal to the expected formatted result', function () { - response.body.renderer_url = 'vuble_renderer.js'; - result.adUnitCode = 'code'; - let formattedResponses = adapter.interpretResponse(response, bid); - expect(formattedResponses[0].adUnitCode).to.equal(result.adUnitCode); - }); - }); - - describe('Check getUserSyncs method return', function () { - // Sync options - let syncOptions = { - iframeEnabled: false - }; - // Server's response - let response = { - body: { - status: 'ok', - cpm: 5.00, - creativeId: '2468', - url: 'https//player.mediabong.net/prebid/ad/a1b2c3d4' - } - }; - // Formatted reponse - let result = { - type: 'iframe', - url: 'https://player.mediabong.net/csifr?1234' - }; - - it('should return an empty array', function () { - expect(adapter.getUserSyncs({}, [])).to.be.empty; - expect(adapter.getUserSyncs({}, [])).to.be.empty; - expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; - expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; - syncOptions.iframeEnabled = true; - expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; - expect(adapter.getUserSyncs(syncOptions, [response])).to.be.empty; - }); - - it('should be equal to the expected result', function () { - response.body.iframeSync = 'https://player.mediabong.net/csifr?1234'; - expect(adapter.getUserSyncs(syncOptions, [response])).to.deep.equal([result]); - }) - }); - - describe('Check outstream scenario with renderer', function () { - // bid Request - let bid = { - data: { - context: 'outstream', - env: 'net', - width: '640', - height: '360', - pub_id: '3', - zone_id: '12345', - bid_id: 'abdc', - floor_price: 5.50, // optional - adUnitCode: 'code' - }, - method: 'POST', - url: 'https://player.mediabong.net/prebid/request' - }; - // Server's response - let response = { - body: { - status: 'ok', - cpm: 5.00, - creativeId: '2468', - url: 'https//player.mediabong.net/prebid/ad/a1b2c3d4', - dealId: 'MDB-TEST-1357', - renderer_id: 0, - renderer_url: 'vuble_renderer.js', - content: 'test' - } - }; - - let adResponse = { - ad: { - video: { - content: 'test' - } - } - }; - let adUnitCode = 'code'; - let rendererUrl = 'vuble_renderer.js'; - let rendererId = 0; - - let formattedResponses = adapter.interpretResponse(response, bid); - it('should equal to the expected format result', function () { - expect(formattedResponses[0].adResponse).to.deep.equal(adResponse); - expect(formattedResponses[0].adUnitCode).to.deep.equal(adUnitCode); - expect(formattedResponses[0].renderer.url).to.equal(rendererUrl); - expect(formattedResponses[0].renderer.id).to.equal(rendererId); - expect(formattedResponses[0].renderer.render).to.exist.and.to.be.a('function'); - }); - }); -}); diff --git a/test/spec/modules/waardexBidAdapter_spec.js b/test/spec/modules/waardexBidAdapter_spec.js index 73094dd72a0..0b2e971aafd 100644 --- a/test/spec/modules/waardexBidAdapter_spec.js +++ b/test/spec/modules/waardexBidAdapter_spec.js @@ -210,14 +210,14 @@ describe('waardexBidAdapter', () => { const { data: payload, url, - method, + method } = spec.buildRequests(validBidRequests, bidderRequest); const ENDPOINT = `https://hb.justbidit.xyz:8843/prebid?pubId=${validBidRequests[0].params.zoneId}`; expect(payload.bidRequests[0]).deep.equal({ bidId: validBidRequests[0].bidId, - bidfloor: validBidRequests[0].params.bidfloor, + bidfloor: 0, position: validBidRequests[0].params.position, instl: validBidRequests[0].params.instl, banner: { @@ -280,7 +280,6 @@ describe('waardexBidAdapter', () => { 'bidRequests': [ { 'bidId': 'unique-bid-id-1', - 'bidfloor': 0.1, 'position': 1, 'instl': 1, 'banner': { diff --git a/test/spec/modules/welectBidAdapter_spec.js b/test/spec/modules/welectBidAdapter_spec.js deleted file mode 100644 index 2f2af35eaec..00000000000 --- a/test/spec/modules/welectBidAdapter_spec.js +++ /dev/null @@ -1,211 +0,0 @@ -import { expect } from 'chai'; -import { spec as adapter } from 'modules/welectBidAdapter.js'; - -describe('WelectAdapter', function () { - describe('Check methods existance', 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'); - }); - }); - - describe('Check method isBidRequestValid return', function () { - let bid = { - bidder: 'welect', - params: { - placementId: 'exampleAlias', - domain: 'www.welect.de' - }, - sizes: [[640, 360]], - mediaTypes: { - video: { - context: 'instream' - } - }, - }; - let bid2 = { - bidder: 'welect', - params: { - domain: 'www.welect.de' - }, - mediaTypes: { - video: { - context: 'instream', - playerSize: [640, 360] - } - }, - }; - - it('should be true', function () { - expect(adapter.isBidRequestValid(bid)).to.be.true; - }); - - it('should be false because the placementId is missing', function () { - expect(adapter.isBidRequestValid(bid2)).to.be.false; - }); - }); - - describe('Check buildRequests method', function () { - // Bids to be formatted - let bid1 = { - bidder: 'welect', - params: { - placementId: 'exampleAlias' - }, - sizes: [[640, 360]], - mediaTypes: { - video: { - context: 'instream' - } - }, - bidId: 'abdc' - }; - let bid2 = { - bidder: 'welect', - params: { - placementId: 'exampleAlias', - domain: 'www.welect2.de' - }, - sizes: [[640, 360]], - mediaTypes: { - video: { - context: 'instream' - } - }, - bidId: 'abdc', - gdprConsent: { - gdprApplies: 1, - gdprConsent: 'some_string' - } - }; - - let data1 = { - bid_id: 'abdc', - width: 640, - height: 360 - } - - let data2 = { - bid_id: 'abdc', - width: 640, - height: 360, - gdpr_consent: { - gdprApplies: 1, - tcString: 'some_string' - } - } - - // Formatted requets - let request1 = { - method: 'POST', - url: 'https://www.welect.de/api/v2/preflight/exampleAlias', - data: data1, - options: { - contentType: 'application/json', - withCredentials: false, - crossOrigin: true, - } - }; - - let request2 = { - method: 'POST', - url: 'https://www.welect2.de/api/v2/preflight/exampleAlias', - data: data2, - options: { - contentType: 'application/json', - withCredentials: false, - crossOrigin: true, - } - } - - it('defaults to www.welect.de, without gdpr object', function () { - expect(adapter.buildRequests([bid1])).to.deep.equal([request1]); - }) - - it('must return the right formatted requests, with gdpr object', function () { - expect(adapter.buildRequests([bid2])).to.deep.equal([request2]); - }); - }); - - describe('Check interpretResponse method return', function () { - // invalid server response - let unavailableResponse = { - body: { - available: false - } - }; - - let availableResponse = { - body: { - available: true, - bidResponse: { - ad: { - video: 'some vast url' - }, - meta: { - advertiserDomains: [], - }, - cpm: 17, - creativeId: 'svmpreview', - currency: 'EUR', - netRevenue: true, - requestId: 'some bid id', - ttl: 120, - vastUrl: 'some vast url', - height: 640, - width: 320 - } - } - } - // bid Request - let bid = { - data: { - bid_id: 'some bid id', - width: 640, - height: 320, - gdpr_consent: { - gdprApplies: 1, - tcString: 'some_string' - } - }, - method: 'POST', - url: 'https://www.welect.de/api/v2/preflight/exampleAlias', - options: { - contentType: 'application/json', - withCredentials: false, - crossOrigin: true, - } - }; - // Formatted reponse - let result = { - ad: { - video: 'some vast url' - }, - meta: { - advertiserDomains: [] - }, - cpm: 17, - creativeId: 'svmpreview', - currency: 'EUR', - height: 640, - netRevenue: true, - requestId: 'some bid id', - ttl: 120, - vastUrl: 'some vast url', - width: 320 - } - - it('if response reflects unavailability, should be empty', function () { - expect(adapter.interpretResponse(unavailableResponse, bid)).to.deep.equal([]); - }); - - it('if response reflects availability, should equal result', function () { - expect(adapter.interpretResponse(availableResponse, bid)).to.deep.equal([result]) - }) - }); -}); diff --git a/test/spec/modules/windtalkerBidAdapter_spec.js b/test/spec/modules/windtalkerBidAdapter_spec.js deleted file mode 100644 index 222a7611b01..00000000000 --- a/test/spec/modules/windtalkerBidAdapter_spec.js +++ /dev/null @@ -1,348 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/windtalkerBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; - -describe('Windtalker Adapter Tests', function () { - const slotConfigs = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bidId: 'bid12345', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '123', - bidFloor: '0.001', - ifa: 'IFA', - latitude: '40.712775', - longitude: '-74.005973' - } - }, { - placementCode: '/DfpAccount2/slot2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - bidId: 'bid23456', - mediaType: 'banner', - params: { - pubId: '29521', - siteId: '26047', - placementId: '1234', - bidFloor: '0.000001', - } - }]; - const nativeSlotConfig = [{ - placementCode: '/DfpAccount1/slot3', - bidId: 'bid12345', - mediaType: 'native', - nativeParams: { - title: { required: true, len: 200 }, - body: {}, - image: { wmin: 100 }, - sponsoredBy: { }, - icon: { } - }, - params: { - pubId: '29521', - placementId: '123', - siteId: '26047' - } - }]; - const videoSlotConfig = [{ - placementCode: '/DfpAccount1/slot4', - mediaTypes: { - video: { - playerSize: [[640, 480]] - } - }, - bidId: 'bid12345678', - mediaType: 'video', - video: { - skippable: true - }, - params: { - pubId: '29521', - placementId: '1234567', - siteId: '26047', - } - }]; - const appSlotConfig = [{ - placementCode: '/DfpAccount1/slot5', - bidId: 'bid12345', - params: { - pubId: '29521', - placementId: '1234', - app: { - id: '1111', - name: 'app name', - bundle: 'io.windtalker.apps', - storeUrl: 'https://windtalker.io/apps', - domain: 'windtalker.io' - } - } - }]; - - it('Verify build request', function () { - const request = spec.buildRequests(slotConfigs); - expect(request.url).to.equal('https://windtalkerdisplay.hb.adp3.net/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // site object - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.publisher).to.not.equal(null); - expect(ortbRequest.site.publisher.id).to.equal('29521'); - expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); - expect(ortbRequest.site.page).to.equal(window.location.href); - expect(ortbRequest.imp).to.have.lengthOf(2); - // device object - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); - expect(ortbRequest.device.ifa).to.equal('IFA'); - expect(ortbRequest.device.geo.lat).to.equal('40.712775'); - expect(ortbRequest.device.geo.lon).to.equal('-74.005973'); - // slot 1 - expect(ortbRequest.imp[0].tagid).to.equal('123'); - expect(ortbRequest.imp[0].banner).to.not.equal(null); - expect(ortbRequest.imp[0].banner.w).to.equal(300); - expect(ortbRequest.imp[0].banner.h).to.equal(250); - expect(ortbRequest.imp[0].bidfloor).to.equal('0.001'); - // slot 2 - expect(ortbRequest.imp[1].tagid).to.equal('1234'); - expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.w).to.equal(728); - expect(ortbRequest.imp[1].banner.h).to.equal(90); - expect(ortbRequest.imp[1].bidfloor).to.equal('0.000001'); - }); - - it('Verify parse response', function () { - const request = spec.buildRequests(slotConfigs); - const ortbRequest = JSON.parse(request.data); - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad', - w: 300, - h: 250 - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('This is an Ad'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.adId).to.equal('bid12345'); - expect(bid.creativeId).to.equal('bid12345'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verify full passback', function () { - const request = spec.buildRequests(slotConfigs); - const bids = spec.interpretResponse({ body: null }, request) - expect(bids).to.have.lengthOf(0); - }); - - it('Verify Native request', function () { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('https://windtalkerdisplay.hb.adp3.net/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('123'); - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.ver).to.equal('1.1'); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(5); - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[3].id).to.equal(4); - expect(nativeRequest.assets[4].id).to.equal(5); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.not.equal(null); - expect(nativeRequest.assets[0].title.len).to.equal(200); - expect(nativeRequest.assets[1].title).to.be.undefined; - expect(nativeRequest.assets[1].data).to.not.equal(null); - expect(nativeRequest.assets[1].data.type).to.equal(2); - expect(nativeRequest.assets[1].data.len).to.equal(200); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[3].img).to.not.equal(null); - expect(nativeRequest.assets[3].img.wmin).to.equal(50); - expect(nativeRequest.assets[3].img.hmin).to.equal(50); - expect(nativeRequest.assets[3].img.type).to.equal(1); - expect(nativeRequest.assets[4].img).to.not.equal(null); - expect(nativeRequest.assets[4].img.wmin).to.equal(100); - expect(nativeRequest.assets[4].img.hmin).to.equal(150); - expect(nativeRequest.assets[4].img.type).to.equal(3); - }); - - it('Verify Native response', function () { - const request = spec.buildRequests(nativeSlotConfig); - expect(request.url).to.equal('https://windtalkerdisplay.hb.adp3.net/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - const nativeResponse = { - 'native': { - assets: [ - { id: 1, title: { text: 'Ad Title' } }, - { id: 2, data: { value: 'Test description' } }, - { id: 3, data: { value: 'Brand' } }, - { id: 4, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_icon.png', w: 100, h: 100 } }, - { id: 5, img: { url: 'https://adx1public.s3.amazonaws.com/creatives_image.png', w: 300, h: 300 } } - ], - link: { url: 'https://brand.com/' } - } - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - nurl: 'https://rtb.adx1.com/log', - adm: JSON.stringify(nativeResponse) - }] - }], - cur: 'USD', - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.adId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - const nativeBid = bid['native']; - expect(nativeBid).to.not.equal(null); - expect(nativeBid.title).to.equal('Ad Title'); - expect(nativeBid.sponsoredBy).to.equal('Brand'); - expect(nativeBid.icon.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_icon.png'); - expect(nativeBid.image.url).to.equal('https://adx1public.s3.amazonaws.com/creatives_image.png'); - expect(nativeBid.image.width).to.equal(300); - expect(nativeBid.image.height).to.equal(300); - expect(nativeBid.icon.width).to.equal(100); - expect(nativeBid.icon.height).to.equal(100); - expect(nativeBid.clickUrl).to.equal(encodeURIComponent('https://brand.com/')); - expect(nativeBid.impressionTrackers).to.have.lengthOf(1); - expect(nativeBid.impressionTrackers[0]).to.equal('https://rtb.adx1.com/log'); - }); - - it('Verify Video request', function () { - const request = spec.buildRequests(videoSlotConfig); - expect(request.url).to.equal('https://windtalkerdisplay.hb.adp3.net/'); - expect(request.method).to.equal('POST'); - const videoRequest = JSON.parse(request.data); - // site object - expect(videoRequest.site).to.not.equal(null); - expect(videoRequest.site.publisher.id).to.equal('29521'); - expect(videoRequest.site.ref).to.equal(window.top.document.referrer); - expect(videoRequest.site.page).to.equal(window.location.href); - // device object - expect(videoRequest.device).to.not.equal(null); - expect(videoRequest.device.ua).to.equal(navigator.userAgent); - // slot 1 - expect(videoRequest.imp[0].tagid).to.equal('1234567'); - expect(videoRequest.imp[0].video).to.not.equal(null); - expect(videoRequest.imp[0].video.w).to.equal(640); - expect(videoRequest.imp[0].video.h).to.equal(480); - expect(videoRequest.imp[0].banner).to.equal(null); - expect(videoRequest.imp[0].native).to.equal(null); - }); - - it('Verify parse video response', function () { - const request = spec.buildRequests(videoSlotConfig); - const videoRequest = JSON.parse(request.data); - const videoResponse = { - seatbid: [{ - bid: [{ - impid: videoRequest.imp[0].id, - price: 1.90, - adm: 'https://vid.example.com/9876', - crid: '510511_754567308' - }] - }], - cur: 'USD' - }; - const bids = spec.interpretResponse({ body: videoResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.90); - expect(bid.vastUrl).to.equal('https://vid.example.com/9876'); - expect(bid.crid).to.equal('510511_754567308'); - expect(bid.width).to.equal(640); - expect(bid.height).to.equal(480); - expect(bid.adId).to.equal('bid12345678'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('USD'); - expect(bid.ttl).to.equal(360); - }); - - it('Verifies bidder code', function () { - expect(spec.code).to.equal('windtalker'); - }); - - it('Verifies supported media types', function () { - expect(spec.supportedMediaTypes).to.have.lengthOf(3); - expect(spec.supportedMediaTypes[0]).to.equal('banner'); - expect(spec.supportedMediaTypes[1]).to.equal('native'); - expect(spec.supportedMediaTypes[2]).to.equal('video'); - }); - - it('Verifies if bid request valid', function () { - expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); - expect(spec.isBidRequestValid(slotConfigs[1])).to.equal(true); - expect(spec.isBidRequestValid(nativeSlotConfig[0])).to.equal(true); - expect(spec.isBidRequestValid(videoSlotConfig[0])).to.equal(true); - }); - - it('Verify app requests', function () { - const request = spec.buildRequests(appSlotConfig); - const ortbRequest = JSON.parse(request.data); - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.publisher).to.not.equal(null); - expect(ortbRequest.app.publisher.id).to.equal('29521'); - expect(ortbRequest.app.id).to.equal('1111'); - expect(ortbRequest.app.name).to.equal('app name'); - expect(ortbRequest.app.bundle).to.equal('io.windtalker.apps'); - expect(ortbRequest.app.storeurl).to.equal('https://windtalker.io/apps'); - expect(ortbRequest.app.domain).to.equal('windtalker.io'); - }); - - it('Verify GDPR', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'serialized_gpdr_data' - } - }; - const request = spec.buildRequests(slotConfigs, bidderRequest); - expect(request.url).to.equal('https://windtalkerdisplay.hb.adp3.net/'); - expect(request.method).to.equal('POST'); - const ortbRequest = JSON.parse(request.data); - expect(ortbRequest.user).to.not.equal(null); - expect(ortbRequest.user.ext).to.not.equal(null); - expect(ortbRequest.user.ext.consent).to.equal('serialized_gpdr_data'); - expect(ortbRequest.regs).to.not.equal(null); - expect(ortbRequest.regs.ext).to.not.equal(null); - expect(ortbRequest.regs.ext.gdpr).to.equal(1); - }); -}); diff --git a/test/spec/modules/wipesBidAdapter_spec.js b/test/spec/modules/wipesBidAdapter_spec.js deleted file mode 100644 index c453eca82c5..00000000000 --- a/test/spec/modules/wipesBidAdapter_spec.js +++ /dev/null @@ -1,150 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/wipesBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; - -const ENDPOINT_URL = 'https://adn-srv.reckoner-api.com/v1/prebid'; - -describe('wipesBidAdapter', function () { - const adapter = newBidder(spec); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'wipes', - 'params': { - asid: 'dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ' - }, - 'adUnitCode': 'adunit-code', - 'bidId': '51ef8751f9aead', - 'bidderRequestId': '15246a574e859f', - 'auctionId': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when asid not passed correctly', function () { - bid.params.asid = ''; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when require params are not passed', function () { - let bid = Object.assign({}, bid); - bid.params = {}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'wipes', - 'params': { - asid: 'dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ' - }, - 'adUnitCode': 'adunit-code', - 'bidId': '51ef8751f9aead', - 'bidderRequestId': '15246a574e859f', - 'auctionId': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - }, - { - 'bidder': 'wipes', - 'params': { - asid: 'dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ' - }, - 'adUnitCode': 'adunit-code2', - 'bidId': '51ef8751f9aead', - 'bidderRequestId': '15246a574e859f', - 'auctionId': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - } - ]; - - let bidderRequest = { - refererInfo: { - numIframes: 0, - reachedTop: true, - referer: 'http://example.com', - stack: ['http://example.com'] - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequest); - - it('sends bid request to our endpoint via GET', function () { - expect(request[0].method).to.equal('GET'); - expect(request[1].method).to.equal('GET'); - }); - - it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.equal(ENDPOINT_URL); - expect(request[1].url).to.equal(ENDPOINT_URL); - }); - - it('adUnitCode should be sent as uc parameters on any requests', function () { - expect(request[0].data.asid).to.equal('dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ'); - expect(request[1].data.asid).to.equal('dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ'); - }); - }); - - describe('interpretResponse', function () { - let bidRequestVideo = [ - { - 'method': 'GET', - 'url': ENDPOINT_URL, - 'data': { - 'asid': 'dWyPondh2EGB_bNlrVjzIXRZO9F0k1dpo0I8ZvQ', - 'bid_id': '23beaa6af6cdde', - } - } - ]; - - let serverResponseVideo = { - body: { - 'uuid': 'a42947f8-f8fd-4cf7-bb72-31a87ab1f6ff', - 'ad_tag': '', - 'height': 160, - 'width': 300, - 'cpm': 850, - 'status_message': '', - 'currency': 'JPY', - 'video_creative_id': 600004, - 'bid_id': '23beaa6af6cdde' - } - }; - - it('should get the correct bid response for video', function () { - let expectedResponse = [{ - 'requestId': '23beaa6af6cdde', - 'cpm': 850, - 'width': 300, - 'height': 160, - 'creativeId': '600004', - 'dealId': undefined, - 'currency': 'JPY', - 'netRevenue': true, - 'ttl': 3000, - 'referrer': '', - 'mediaType': 'banner', - 'ad': '' - }]; - let result = spec.interpretResponse(serverResponseVideo, bidRequestVideo[0]); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); - expect(result[0].mediaType).to.equal(expectedResponse[0].mediaType); - }); - - it('handles empty bid response', function () { - let response = { - body: { - 'uid': 'a42947f8-f8fd-4cf7-bb72-31a87ab1f6ff', - 'height': 0, - 'crid': '', - 'statusMessage': '', - 'width': 0, - 'cpm': 0 - } - }; - let result = spec.interpretResponse(response, bidRequestVideo[0]); - expect(result.length).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/xhbBidAdapter_spec.js b/test/spec/modules/xhbBidAdapter_spec.js deleted file mode 100644 index a12abc74c64..00000000000 --- a/test/spec/modules/xhbBidAdapter_spec.js +++ /dev/null @@ -1,495 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/xhbBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { deepClone } from 'src/utils.js'; - -const ENDPOINT = 'https://ib.adnxs.com/ut/v3/prebid'; - -describe('xhbAdapter', 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': 'xhb', - 'params': { - 'placementId': '10433394' - }, - '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 true when required params found', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'member': '1234', - 'invCode': 'ABCD' - }; - - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'placementId': 0 - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'xhb', - 'params': { - 'placementId': '10433394' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should parse out private sizes', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - privateSizes: [300, 250] - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].private_sizes).to.exist; - expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); - }); - - it('should add source and verison to the tag', function () { - const request = spec.buildRequests(bidRequests); - const payload = JSON.parse(request.data); - expect(payload.sdk).to.exist; - expect(payload.sdk).to.deep.equal({ - source: 'pbjs', - version: '$prebid.version$' - }); - }); - - it('should populate the ad_types array on all requests', function () { - ['banner', 'video', 'native'].forEach(type => { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes[type] = {}; - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].ad_types).to.deep.equal([type]); - }); - }); - - it('should populate the ad_types array on outstream requests', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes.video = {context: 'outstream'}; - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].ad_types).to.deep.equal(['video']); - }); - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should attach valid video params to the tag', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - video: { - id: 123, - minduration: 100, - foobar: 'invalid' - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - id: 123, - minduration: 100 - }); - }); - - it('should attach valid user params to the tag', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - user: { - external_uid: '123', - foobar: 'invalid' - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.user).to.exist; - expect(payload.user).to.deep.equal({ - external_uid: '123', - }); - }); - - it('should attach native params to the request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - title: {required: true}, - body: {required: true}, - image: {required: true, sizes: [{ width: 100, height: 100 }]}, - cta: {required: false}, - sponsoredBy: {required: true} - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - title: {required: true}, - description: {required: true}, - main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, - ctatext: {required: false}, - sponsored_by: {required: true} - }); - }); - - it('sets minimum native asset params when not provided on adunit', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - image: {required: true}, - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: {required: true, sizes: [{}]}, - }); - }); - - it('does not overwrite native ad unit params with mimimum params', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - image: { - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: { - required: true, - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - }, - }); - }); - - it('should convert keyword params to proper form and attaches to request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - keywords: { - single: 'val', - singleArr: ['val'], - singleArrNum: [5], - multiValMixed: ['value1', 2, 'value3'], - singleValNum: 123, - badValue: {'foo': 'bar'} // should be dropped - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['5'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }]); - }); - - it('should add payment rules to the request', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - usePaymentRule: true - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].use_pmt_rule).to.equal(true); - }); - - it('should add gdpr consent information to the request', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let bidderRequest = { - 'bidderCode': 'xhb', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - gdprApplies: true - } - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - 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; - }); - }); - - describe('interpretResponse', function () { - let response = { - 'version': '3.0.0', - 'tags': [ - { - 'uuid': '3db3773286ee59', - 'tag_id': 10433394, - 'auction_id': '4534722592064951574', - 'nobid': false, - 'no_ad_url': 'https://lax1-ib.adnxs.com/no-ad', - 'timeout_ms': 10000, - 'ad_profile_id': 27079, - 'ads': [ - { - 'content_source': 'rtb', - 'ad_type': 'banner', - 'buyer_member_id': 958, - 'creative_id': 29681110, - 'media_type_id': 1, - 'media_subtype_id': 1, - 'cpm': 0.5, - 'cpm_publisher_currency': 0.5, - 'publisher_currency_code': '$', - 'client_initiated_ad_counting': true, - 'rtb': { - 'banner': { - 'content': '', - 'width': 300, - 'height': 250 - }, - 'trackers': [ - { - 'impression_urls': [ - 'https://lax1-ib.adnxs.com/impression' - ], - 'video_events': {} - } - ] - } - } - ] - } - ] - }; - - it('should get correct bid response', function () { - let expectedResponse = [ - { - 'requestId': '3db3773286ee59', - 'cpm': 0.5, - 'creativeId': 29681110, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '', - 'mediaType': 'banner', - 'currency': 'USD', - 'ttl': 300, - 'netRevenue': true, - 'appnexus': { - 'buyerMemberId': 958 - } - } - ]; - let bidderRequest; - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); - }); - - it('handles nobid responses', function () { - let response = { - 'version': '0.0.1', - 'tags': [{ - 'uuid': '84ab500420319d', - 'tag_id': 5976557, - 'auction_id': '297492697822162468', - 'nobid': true - }] - }; - let bidderRequest; - - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result.length).to.equal(0); - }); - - it('handles non-banner media responses', function () { - let response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'content': '' - } - } - }] - }] - }; - let bidderRequest; - - let result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result[0]).to.have.property('vastUrl'); - expect(result[0]).to.have.property('vastImpUrl'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); - - it('handles native responses', function () { - let response1 = deepClone(response); - response1.tags[0].ads[0].ad_type = 'native'; - response1.tags[0].ads[0].rtb.native = { - 'title': 'Native Creative', - 'desc': 'Cool description great stuff', - 'ctatext': 'Do it', - 'sponsored': 'AppNexus', - 'icon': { - 'width': 0, - 'height': 0, - 'url': 'https://cdn.adnxs.com/icon.png' - }, - 'main_img': { - 'width': 2352, - 'height': 1516, - 'url': 'https://cdn.adnxs.com/img.png' - }, - 'link': { - 'url': 'https://www.appnexus.com', - 'fallback_url': '', - 'click_trackers': ['https://nym1-ib.adnxs.com/click'] - }, - 'impression_trackers': ['https://example.com'], - }; - let bidderRequest; - - let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); - expect(result[0].native.title).to.equal('Native Creative'); - 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'); - }); - - it('supports configuring outstream renderers', function () { - const outstreamResponse = deepClone(response); - outstreamResponse.tags[0].ads[0].rtb.video = {}; - outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; - - const bidderRequest = { - bids: [{ - renderer: { - options: { - adText: 'configured' - } - } - }] - }; - - const result = spec.interpretResponse({ body: outstreamResponse }, {bidderRequest}); - expect(result[0].renderer.config).to.deep.equal( - bidderRequest.bids[0].renderer.options - ); - }); - }); -}); diff --git a/test/spec/modules/zedoBidAdapter_spec.js b/test/spec/modules/zedoBidAdapter_spec.js deleted file mode 100644 index 8e5a789656e..00000000000 --- a/test/spec/modules/zedoBidAdapter_spec.js +++ /dev/null @@ -1,354 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/zedoBidAdapter'; - -describe('The ZEDO bidding adapter', function () { - describe('isBidRequestValid', function () { - it('should return false when given an invalid bid', function () { - const bid = { - bidder: 'zedo', - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(false); - }); - - it('should return true when given a channelcode bid', function () { - const bid = { - bidder: 'zedo', - params: { - channelCode: 20000000, - dimId: 9 - }, - }; - const isValid = spec.isBidRequestValid(bid); - expect(isValid).to.equal(true); - }); - }); - - describe('buildRequests', function () { - const bidderRequest = { - timeout: 3000, - }; - - it('should properly build a channelCode request for dim Id with type not defined', function () { - const bidRequests = [ - { - bidder: 'zedo', - adUnitCode: 'p12345', - transactionId: '12345667', - sizes: [[300, 200]], - params: { - channelCode: 20000000, - dimId: 10, - pubId: 1 - }, - }, - ]; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.match(/^https:\/\/saxp.zedo.com\/asw\/fmh.json/); - expect(request.method).to.equal('GET'); - const zedoRequest = request.data; - expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":1,"width":300,"height":200,"dimension":10,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"display"}]}]}'); - }); - - it('should properly build a channelCode request for video with type defined', function () { - const bidRequests = [ - { - bidder: 'zedo', - adUnitCode: 'p12345', - transactionId: '12345667', - sizes: [640, 480], - mediaTypes: { - video: { - context: 'instream', - }, - }, - params: { - channelCode: 20000000, - dimId: 85 - }, - }, - ]; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.match(/^https:\/\/saxp.zedo.com\/asw\/fmh.json/); - expect(request.method).to.equal('GET'); - const zedoRequest = request.data; - expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":0,"width":640,"height":480,"dimension":85,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"Inarticle"}]}]}'); - }); - - describe('buildGDPRRequests', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const bidderRequest = { - timeout: 3000, - gdprConsent: { - 'consentString': consentString, - 'gdprApplies': true - } - }; - - it('should properly build request with gdpr consent', function () { - const bidRequests = [ - { - bidder: 'zedo', - adUnitCode: 'p12345', - transactionId: '12345667', - sizes: [[300, 200]], - params: { - channelCode: 20000000, - dimId: 10 - }, - }, - ]; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.method).to.equal('GET'); - const zedoRequest = request.data; - expect(zedoRequest).to.equal('g={"placements":[{"network":20,"channel":0,"publisher":0,"width":300,"height":200,"dimension":10,"version":"$prebid.version$","keyword":"","transactionId":"12345667","renderers":[{"name":"display"}]}],"gdpr":1,"gdpr_consent":"BOJ8RZsOJ8RZsABAB8AAAAAZ+A=="}'); - }); - }); - }); - describe('interpretResponse', function () { - it('should return an empty array when there is bid response', function () { - const response = {}; - const request = { bidRequests: [] }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(0); - }); - - it('should properly parse a bid response with no valid creative', function () { - const response = { - body: { - ad: [ - { - 'slotId': 'ad1d762', - 'network': '2000', - 'creatives': [ - { - 'adId': '12345', - 'height': '600', - 'width': '160', - 'isFoc': true, - 'creativeDetails': { - 'type': 'StdBanner', - 'adContent': { - 'focImage': { - 'url': 'https://c13.zedo.com/OzoDB/0/0/0/blank.gif', - 'target': '_blank', - } - } - }, - 'cpm': '0' - } - ] - } - ] - } - }; - const request = { - bidRequests: [{ - bidder: 'zedo', - adUnitCode: 'p12345', - bidId: 'test-bidId', - params: { - channelCode: 2000000, - dimId: 9 - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(0); - }); - - it('should properly parse a bid response with valid display creative', function () { - const response = { - body: { - ad: [ - { - 'slotId': 'ad1d762', - 'network': '2000', - 'creatives': [ - { - 'adId': '12345', - 'height': '600', - 'width': '160', - 'isFoc': true, - 'creativeDetails': { - 'type': 'StdBanner', - 'adContent': '' - }, - 'bidCpm': '720000' - } - ] - } - ] - } - }; - const request = { - bidRequests: [{ - bidder: 'zedo', - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - params: { - channelCode: 2000000, - dimId: 9 - }, - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(1); - expect(bids[0].requestId).to.equal('ad1d762'); - expect(bids[0].cpm).to.equal(0.72); - expect(bids[0].width).to.equal('160'); - expect(bids[0].height).to.equal('600'); - }); - - it('should properly parse a bid response with valid video creative', function () { - const response = { - body: { - ad: [ - { - 'slotId': 'ad1d762', - 'network': '2000', - 'creatives': [ - { - 'adId': '12345', - 'height': '480', - 'width': '640', - 'isFoc': true, - 'creativeDetails': { - 'type': 'VAST', - 'adContent': '' - }, - 'bidCpm': '780000' - } - ] - } - ] - } - }; - const request = { - bidRequests: [{ - bidder: 'zedo', - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - params: { - channelCode: 2000000, - dimId: 85 - }, - }] - }; - - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(1); - expect(bids[0].requestId).to.equal('ad1d762'); - expect(bids[0].cpm).to.equal(0.78); - expect(bids[0].width).to.equal('640'); - expect(bids[0].height).to.equal('480'); - expect(bids[0].adType).to.equal('VAST'); - expect(bids[0].vastXml).to.not.equal(''); - expect(bids[0].ad).to.be.an('undefined'); - expect(bids[0].renderer).not.to.be.an('undefined'); - }); - }); - - describe('user sync', function () { - it('should register the iframe sync url', function () { - let syncs = spec.getUserSyncs({ - iframeEnabled: true - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - }); - - it('should pass gdpr params', function () { - let syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { - gdprApplies: false, consentString: 'test' - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.contains('gdpr=0'); - }); - }); - - describe('bid events', function () { - it('should trigger a win pixel', function () { - const bid = { - 'bidderCode': 'zedo', - 'width': '300', - 'height': '250', - 'statusMessage': 'Bid available', - 'adId': '148018fe5e', - 'cpm': 0.5, - 'ad': 'dummy data', - 'ad_id': '12345', - 'sizeId': '15', - 'adResponse': - { - 'creatives': [ - { - 'adId': '12345', - 'height': '480', - 'width': '640', - 'isFoc': true, - 'creativeDetails': { - 'type': 'VAST', - 'adContent': '' - }, - 'seeder': { - 'network': 1234, - 'servedChan': 1234567, - }, - 'cpm': '1200000', - 'servedChan': 1234, - }] - }, - 'params': [{ - 'channelCode': '123456', - 'dimId': '85' - }], - 'requestTimestamp': 1540401686, - 'responseTimestamp': 1540401687, - 'timeToRespond': 6253, - 'pbLg': '0.50', - 'pbMg': '0.50', - 'pbHg': '0.53', - 'adUnitCode': '/123456/header-bid-tag-0', - 'bidder': 'zedo', - 'size': '300x250', - 'adserverTargeting': { - 'hb_bidder': 'zedo', - 'hb_adid': '148018fe5e', - 'hb_pb': '10.00', - } - }; - spec.onBidWon(bid); - spec.onTimeout(bid); - }); - it('should trigger a timeout pixel', function () { - const bid = { - 'bidderCode': 'zedo', - 'width': '300', - 'height': '250', - 'statusMessage': 'Bid available', - 'adId': '148018fe5e', - 'cpm': 0.5, - 'ad': 'dummy data', - 'ad_id': '12345', - 'sizeId': '15', - 'params': [{ - 'channelCode': '123456', - 'dimId': '85' - }], - 'timeout': 1, - 'requestTimestamp': 1540401686, - 'responseTimestamp': 1540401687, - 'timeToRespond': 6253, - 'adUnitCode': '/123456/header-bid-tag-0', - 'bidder': 'zedo', - 'size': '300x250', - }; - spec.onBidWon(bid); - spec.onTimeout(bid); - }); - }); -}); diff --git a/test/spec/modules/zemantaBidAdapter_spec.js b/test/spec/modules/zemantaBidAdapter_spec.js deleted file mode 100644 index 523cdcd2eb3..00000000000 --- a/test/spec/modules/zemantaBidAdapter_spec.js +++ /dev/null @@ -1,558 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/zemantaBidAdapter.js'; -import {config} from 'src/config.js'; -import {server} from 'test/mocks/xhr'; - -describe('Zemanta Adapter', function () { - describe('Bid request and response', function () { - const commonBidRequest = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id' - }, - }, - bidId: '2d6815a92ba1ba', - auctionId: '12043683-3254-4f74-8934-f941b085579e', - } - const nativeBidRequestParams = { - nativeParams: { - image: { - required: true, - sizes: [ - 120, - 100 - ], - sendId: true - }, - title: { - required: true, - sendId: true - }, - sponsoredBy: { - required: false - } - }, - } - - const displayBidRequestParams = { - sizes: [ - [ - 300, - 250 - ] - ] - } - - describe('isBidRequestValid', function () { - before(() => { - config.setConfig({ - zemanta: { - bidderUrl: 'https://bidder-url.com', - } - } - ) - }) - after(() => { - config.resetConfig() - }) - - it('should fail when bid is invalid', function () { - const bid = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id', - } - }, - } - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) - it('should succeed when bid contains native params', function () { - const bid = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id', - } - }, - ...nativeBidRequestParams, - } - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) - it('should succeed when bid contains sizes', function () { - const bid = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id', - } - }, - ...displayBidRequestParams, - } - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) - it('should fail if publisher id is not set', function () { - const bid = { - bidder: 'zemanta', - ...nativeBidRequestParams, - } - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) - it('should succeed with outbrain config', function () { - const bid = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id', - } - }, - ...nativeBidRequestParams, - } - config.resetConfig() - config.setConfig({ - outbrain: { - bidderUrl: 'https://bidder-url.com', - } - }) - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) - it('should fail if bidder url is not set', function () { - const bid = { - bidder: 'zemanta', - params: { - publisher: { - id: 'publisher-id', - } - }, - ...nativeBidRequestParams, - } - config.resetConfig() - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) - }) - - describe('buildRequests', function () { - before(() => { - config.setConfig({ - zemanta: { - bidderUrl: 'https://bidder-url.com', - } - } - ) - }) - after(() => { - config.resetConfig() - }) - - const commonBidderRequest = { - refererInfo: { - referer: 'https://example.com/' - } - } - - it('should build native request', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - const expectedNativeAssets = { - assets: [ - { - required: 1, - id: 3, - img: { - type: 3, - w: 120, - h: 100 - } - }, - { - required: 1, - id: 0, - title: {} - }, - { - required: 0, - id: 5, - data: { - type: 1 - } - } - ] - } - const expectedData = { - site: { - page: 'https://example.com/', - publisher: { - id: 'publisher-id' - } - }, - device: { - ua: navigator.userAgent - }, - source: { - fd: 1 - }, - cur: [ - 'USD' - ], - imp: [ - { - id: '1', - native: { - request: JSON.stringify(expectedNativeAssets) - } - } - ] - } - const res = spec.buildRequests([bidRequest], commonBidderRequest) - expect(res.url).to.equal('https://bidder-url.com') - expect(res.data).to.deep.equal(JSON.stringify(expectedData)) - }); - - it('should build display request', function () { - const bidRequest = { - ...commonBidRequest, - ...displayBidRequestParams, - } - const expectedData = { - site: { - page: 'https://example.com/', - publisher: { - id: 'publisher-id' - } - }, - device: { - ua: navigator.userAgent - }, - source: { - fd: 1 - }, - cur: [ - 'USD' - ], - imp: [ - { - id: '1', - banner: { - format: [ - { - w: 300, - h: 250 - } - ] - } - } - ] - } - const res = spec.buildRequests([bidRequest], commonBidderRequest) - expect(res.url).to.equal('https://bidder-url.com') - expect(res.data).to.deep.equal(JSON.stringify(expectedData)) - }) - - it('should pass optional parameters in request', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - bidRequest.params.tagid = 'test-tag' - bidRequest.params.publisher.name = 'test-publisher' - bidRequest.params.publisher.domain = 'test-publisher.com' - bidRequest.params.bcat = ['bad-category'] - bidRequest.params.badv = ['bad-advertiser'] - - const res = spec.buildRequests([bidRequest], commonBidderRequest) - const resData = JSON.parse(res.data) - expect(resData.imp[0].tagid).to.equal('test-tag') - expect(resData.site.publisher.name).to.equal('test-publisher') - expect(resData.site.publisher.domain).to.equal('test-publisher.com') - expect(resData.bcat).to.deep.equal(['bad-category']) - expect(resData.badv).to.deep.equal(['bad-advertiser']) - }); - - it('should pass bidder timeout', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - const bidderRequest = { - ...commonBidderRequest, - timeout: 500 - } - const res = spec.buildRequests([bidRequest], bidderRequest) - const resData = JSON.parse(res.data) - expect(resData.tmax).to.equal(500) - }); - - it('should pass GDPR consent', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - const bidderRequest = { - ...commonBidderRequest, - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - } - } - const res = spec.buildRequests([bidRequest], bidderRequest) - const resData = JSON.parse(res.data) - expect(resData.user.ext.consent).to.equal('consentString') - expect(resData.regs.ext.gdpr).to.equal(1) - }); - - it('should pass us privacy consent', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - const bidderRequest = { - ...commonBidderRequest, - uspConsent: 'consentString' - } - const res = spec.buildRequests([bidRequest], bidderRequest) - const resData = JSON.parse(res.data) - expect(resData.regs.ext.us_privacy).to.equal('consentString') - }); - - it('should pass coppa consent', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - } - config.setConfig({coppa: true}) - - const res = spec.buildRequests([bidRequest], commonBidderRequest) - const resData = JSON.parse(res.data) - expect(resData.regs.coppa).to.equal(1) - - config.resetConfig() - }); - }) - - describe('interpretResponse', function () { - it('should return empty array if no valid bids', function () { - const res = spec.interpretResponse({}, []) - expect(res).to.be.an('array').that.is.empty - }); - - it('should interpret native response', function () { - const serverResponse = { - body: { - id: '0a73e68c-9967-4391-b01b-dda2d9fc54e4', - seatbid: [ - { - bid: [ - { - id: '82822cf5-259c-11eb-8a52-f29e5275aa57', - impid: '1', - price: 1.1, - nurl: 'http://example.com/win/${AUCTION_PRICE}', - adm: '{"ver":"1.2","assets":[{"id":3,"required":1,"img":{"url":"http://example.com/img/url","w":120,"h":100}},{"id":0,"required":1,"title":{"text":"Test title"}},{"id":5,"data":{"value":"Test sponsor"}}],"link":{"url":"http://example.com/click/url"},"eventtrackers":[{"event":1,"method":1,"url":"http://example.com/impression"}]}', - adomain: [ - 'example.com' - ], - cid: '3487171', - crid: '28023739', - cat: [ - 'IAB10-2' - ] - } - ], - seat: 'acc-5537' - } - ], - bidid: '82822cf5-259c-11eb-8a52-b48e7518c657', - cur: 'USD' - }, - } - const request = { - bids: [ - { - ...commonBidRequest, - ...nativeBidRequestParams, - } - ] - } - const expectedRes = [ - { - requestId: request.bids[0].bidId, - cpm: 1.1, - creativeId: '28023739', - ttl: 360, - netRevenue: false, - currency: 'USD', - mediaType: 'native', - nurl: 'http://example.com/win/${AUCTION_PRICE}', - meta: { - 'advertiserDomains': [ - 'example.com' - ] - }, - native: { - clickTrackers: undefined, - clickUrl: 'http://example.com/click/url', - image: { - url: 'http://example.com/img/url', - width: 120, - height: 100 - }, - title: 'Test title', - sponsoredBy: 'Test sponsor', - impressionTrackers: [ - 'http://example.com/impression', - ] - } - } - ] - - const res = spec.interpretResponse(serverResponse, request) - expect(res).to.deep.equal(expectedRes) - }); - - it('should interpret display response', function () { - const serverResponse = { - body: { - id: '6b2eedc8-8ff5-46ef-adcf-e701b508943e', - seatbid: [ - { - bid: [ - { - id: 'd90fe7fa-28d7-11eb-8ce4-462a842a7cf9', - impid: '1', - price: 1.1, - nurl: 'http://example.com/win/${AUCTION_PRICE}', - adm: '
ad
', - adomain: [ - 'example.com' - ], - cid: '3865084', - crid: '29998660', - cat: [ - 'IAB10-2' - ], - w: 300, - h: 250 - } - ], - seat: 'acc-6536' - } - ], - bidid: 'd90fe7fa-28d7-11eb-8ce4-13d94bfa26f9', - cur: 'USD' - } - } - const request = { - bids: [ - { - ...commonBidRequest, - ...displayBidRequestParams - } - ] - } - const expectedRes = [ - { - requestId: request.bids[0].bidId, - cpm: 1.1, - creativeId: '29998660', - ttl: 360, - netRevenue: false, - currency: 'USD', - mediaType: 'banner', - nurl: 'http://example.com/win/${AUCTION_PRICE}', - ad: '
ad
', - width: 300, - height: 250, - meta: { - 'advertiserDomains': [ - 'example.com' - ] - }, - } - ] - - const res = spec.interpretResponse(serverResponse, request) - expect(res).to.deep.equal(expectedRes) - }); - }) - }) - - describe('getUserSyncs', function () { - const usersyncUrl = 'https://usersync-url.com'; - beforeEach(() => { - config.setConfig({ - zemanta: { - usersyncUrl: usersyncUrl, - } - } - ) - }) - after(() => { - config.resetConfig() - }) - - it('should return user sync if pixel enabled', function () { - const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) - }) - it('should return user sync if pixel enabled with outbrain config', function () { - config.resetConfig() - config.setConfig({ - outbrain: { - usersyncUrl: 'https://usersync-url.com', - } - }) - const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) - }) - - it('should not return user sync if pixel disabled', function () { - const ret = spec.getUserSyncs({pixelEnabled: false}) - expect(ret).to.be.an('array').that.is.empty - }) - - it('should not return user sync if url is not set', function () { - config.resetConfig() - const ret = spec.getUserSyncs({pixelEnabled: true}) - expect(ret).to.be.an('array').that.is.empty - }) - - it('should pass GDPR consent', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=0&gdpr_consent=foo` - }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=` - }]); - }); - - it('should pass US consent', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&us_privacy=1NYN` - }]); - }); - - it('should pass GDPR and US consent', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN` - }]); - }); - }) - - describe('onBidWon', function () { - it('should make an ajax call with the original cpm', function () { - const bid = { - nurl: 'http://example.com/win/${AUCTION_PRICE}', - cpm: 2.1, - originalCpm: 1.1, - } - spec.onBidWon(bid) - expect(server.requests[0].url).to.equals('http://example.com/win/1.1') - }); - }) -}) diff --git a/test/spec/refererDetection_spec.js b/test/spec/refererDetection_spec.js index 46990ae841f..a404e4f883e 100644 --- a/test/spec/refererDetection_spec.js +++ b/test/spec/refererDetection_spec.js @@ -1,4 +1,5 @@ import { detectReferer } from 'src/refererDetection.js'; +import { config } from 'src/config.js'; import { expect } from 'chai'; /** @@ -91,6 +92,10 @@ function buildWindowTree(urls, topReferrer = '', canonicalUrl = null, ancestorOr describe('Referer detection', () => { describe('Non cross-origin scenarios', () => { describe('No iframes', () => { + afterEach(function () { + config.resetConfig(); + }); + it('Should return the current window location and no canonical URL', () => { const testWindow = buildWindowTree(['https://example.com/some/page'], 'https://othersite.com/'), result = detectReferer(testWindow)(); @@ -156,6 +161,26 @@ describe('Referer detection', () => { canonicalUrl: 'https://example.com/canonical/page' }); }); + + it('Should override canonical URL with config pageUrl', () => { + config.setConfig({'pageUrl': 'testUrl.com'}); + + const testWindow = buildWindowTree(['https://example.com/some/page', 'https://example.com/other/page', 'https://example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'), + result = detectReferer(testWindow)(); + + expect(result).to.deep.equal({ + referer: 'https://example.com/some/page', + reachedTop: true, + isAmp: false, + numIframes: 2, + stack: [ + 'https://example.com/some/page', + 'https://example.com/other/page', + 'https://example.com/third/page' + ], + canonicalUrl: 'testUrl.com' + }); + }); }); }); diff --git a/test/spec/sizeMapping_spec.js b/test/spec/sizeMapping_spec.js index 78dd9797c36..a3c39a52441 100644 --- a/test/spec/sizeMapping_spec.js +++ b/test/spec/sizeMapping_spec.js @@ -93,6 +93,15 @@ describe('sizeMapping', function () { expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); }); + it('should log a warning message when mediaQuery property is declared as an empty string', function() { + const errorConfig = deepClone(sizeConfig); + errorConfig[0].mediaQuery = ''; + + sandbox.stub(utils, 'logWarn'); + resolveStatus(undefined, testSizes, undefined, errorConfig); + expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); + }); + it('should allow deprecated adUnit.sizes', function() { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 0bd3380f737..199ce699dc8 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1783,6 +1783,42 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250]]); expect(auctionArgs.adUnits[0].mediaTypes.banner.sizes).to.deep.equal([[300, 250]]); }); + + it('should filter mediaType pos value if not integer', function () { + let adUnit = [{ + code: 'test5', + bids: [], + sizes: [300, 250], + mediaTypes: { + banner: { + sizes: [300, 250], + pos: 'foo' + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: adUnit + }); + expect(auctionArgs.adUnits[0].mediaTypes.banner.pos).to.be.undefined; + }); + + it('should pass mediaType pos value if integer', function () { + let adUnit = [{ + code: 'test5', + bids: [], + sizes: [300, 250], + mediaTypes: { + banner: { + sizes: [300, 250], + pos: 2 + } + } + }]; + $$PREBID_GLOBAL$$.requestBids({ + adUnits: adUnit + }); + expect(auctionArgs.adUnits[0].mediaTypes.banner.pos).to.equal(2); + }); }); describe('negative tests for validating adUnits', function() { From dfd28d99382f0f09fada37a28fa89888ab297f92 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 10 Jun 2021 10:51:15 -0700 Subject: [PATCH 1147/1476] Prebid 5.0.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41c61664145..a1c45f929d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.43.0", + "version": "5.0.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From fa09766c02e1c9071348bd75c510acc1d3955de2 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 10 Jun 2021 11:15:06 -0700 Subject: [PATCH 1148/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1c45f929d9..20357e5bf10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.0.0", + "version": "5.1.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From cea9a15bde377ada9706c727987ce73d1c71de40 Mon Sep 17 00:00:00 2001 From: Jonathan Mullins Date: Fri, 11 Jun 2021 06:47:48 +1000 Subject: [PATCH 1149/1476] add advertiserDomain to bid object (#6998) Co-authored-by: jgan91 --- modules/pxyzBidAdapter.js | 4 ++++ test/spec/modules/pxyzBidAdapter_spec.js | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/pxyzBidAdapter.js b/modules/pxyzBidAdapter.js index bd2189ccc39..9baff4d1533 100644 --- a/modules/pxyzBidAdapter.js +++ b/modules/pxyzBidAdapter.js @@ -133,6 +133,7 @@ export const spec = { } function newBid(bid, currency) { + const { adomain } = bid; return { requestId: bid.impid, mediaType: BANNER, @@ -144,6 +145,9 @@ function newBid(bid, currency) { ttl: 300, netRevenue: true, currency: currency, + meta: { + ...(adomain && adomain.length > 0 ? { advertiserDomains: adomain } : {}) + } }; } diff --git a/test/spec/modules/pxyzBidAdapter_spec.js b/test/spec/modules/pxyzBidAdapter_spec.js index 6d8c6056076..21dd252c909 100644 --- a/test/spec/modules/pxyzBidAdapter_spec.js +++ b/test/spec/modules/pxyzBidAdapter_spec.js @@ -191,11 +191,15 @@ describe('pxyzBidAdapter', function () { 'mediaType': 'banner', 'currency': 'AUD', 'ttl': 300, - 'netRevenue': true + 'netRevenue': true, + 'meta': { + advertiserDomains: ['pg.xyz'] + } } ]; let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); }); it('handles nobid response', function () { From 2bb442ab2449ccad483247032e9f6a1c3de844b5 Mon Sep 17 00:00:00 2001 From: llays Date: Fri, 11 Jun 2021 14:31:09 +0300 Subject: [PATCH 1150/1476] Invamia Bid Adapter: Add meta.adomain support (#7012) * Add meta.adomain support * Update test Co-authored-by: Andrew Lays --- modules/invamiaBidAdapter.js | 4 ++++ test/spec/modules/invamiaBidAdapter_spec.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js index 0cc63d34550..2d36fb77e16 100644 --- a/modules/invamiaBidAdapter.js +++ b/modules/invamiaBidAdapter.js @@ -74,6 +74,10 @@ export const spec = { netRevenue: response.hb.netRevenue, ttl: 600, ad: response.template.html, + mediaType: 'banner', + meta: { + advertiserDomains: response.hb.adomains || [], + }, width, height, }; diff --git a/test/spec/modules/invamiaBidAdapter_spec.js b/test/spec/modules/invamiaBidAdapter_spec.js index ed004b651bd..2f8f0612e44 100644 --- a/test/spec/modules/invamiaBidAdapter_spec.js +++ b/test/spec/modules/invamiaBidAdapter_spec.js @@ -104,6 +104,7 @@ describe('invamia bid adapter tests', function () { hb: { cpm: 0.5, netRevenue: false, + adomains: ['securepubads.g.doubleclick.net'], }, template: { html: '', @@ -128,6 +129,8 @@ describe('invamia bid adapter tests', function () { expect(bids[0].cpm).to.equal(0.5); expect(bids[0].netRevenue).to.equal(false); expect(bids[0].currency).to.equal('USD'); + expect(bids[0].meta.advertiserDomains).to.be.lengthOf(1); + expect(bids[0].meta.advertiserDomains[0]).to.equal('securepubads.g.doubleclick.net'); }); it('should return empty bid response', function () { From 792664d917aff7a37eaa85cdc8ac8449b23e97fd Mon Sep 17 00:00:00 2001 From: Fridoom007 Date: Mon, 14 Jun 2021 17:31:13 +0300 Subject: [PATCH 1151/1476] AdRiver Bid Adapter: add userId to request from sharedId, id5Id, uid2Id (#7005) * AdRiver Bid Adapter: initial prebid.js integration * Added AdRiver Bid Adapter * AdRiver Bid Adapter: update getting floor, via getFloor() * Added internal method _getFloor() * Update test for getFloor() * Remove old currency logic * AdRiver Bid Adapter: update adriverBidAdapter.md * Delete old test parameters * AdRiver Bid Adapter: add meta.advertiserDomains * Added parameter meta.advertiserDomains to interpretResponse * Update test for meta.advertiserDomains * AdRiver Bid Adapter: add userId parameter * Add module sharedIdSystem,id5IdSystem,uid2IdSystem,userId * Update test * Add timeout to request --- modules/adriverBidAdapter.js | 28 +- test/spec/modules/adriverBidAdapter_spec.js | 307 ++++++++++++++++++-- 2 files changed, 309 insertions(+), 26 deletions(-) diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index af0a401b355..d5a777f6111 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -20,7 +20,7 @@ export const spec = { return !!bid.params.siteid; }, - buildRequests: function (validBidRequests) { + buildRequests: function (validBidRequests, bidderRequest) { utils.logInfo('validBidRequests', validBidRequests); let win = utils.getWindowLocation(); @@ -29,9 +29,15 @@ export const spec = { let currency = utils.getBidIdParameter('currency', validBidRequests[0].params); currency = 'RUB'; + let timeout = null; + if (bidderRequest) { + timeout = bidderRequest.timeout + } + const payload = { 'at': 1, 'cur': [currency], + 'tmax': timeout, 'site': { 'name': win.origin, 'domain': win.hostname, @@ -40,7 +46,10 @@ export const spec = { }, 'id': customID, 'user': { - 'buyerid': 0 + 'buyerid': 0, + 'ext': { + 'eids': getUserIdAsEids(validBidRequests) + } }, 'device': { 'ip': '195.209.111.14', @@ -132,10 +141,23 @@ export const spec = { }); return bidResponses; } - }; registerBidder(spec); +/** + * get first userId from validBidRequests + * @param validBidRequests + * @returns {Array|*} userIdAsEids + */ +function getUserIdAsEids(validBidRequests) { + if (validBidRequests && validBidRequests.length > 0 && validBidRequests[0].userIdAsEids && + validBidRequests[0].userIdAsEids.length > 0) { + return validBidRequests[0].userIdAsEids; + } else { + return []; + } +} + /** * Gets bidfloor * @param {Object} bid diff --git a/test/spec/modules/adriverBidAdapter_spec.js b/test/spec/modules/adriverBidAdapter_spec.js index c16bc5df5cb..9d410090885 100644 --- a/test/spec/modules/adriverBidAdapter_spec.js +++ b/test/spec/modules/adriverBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec } from 'modules/adriverBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as bidderFactory from 'src/adapters/bidderFactory.js'; import { auctionManager } from 'src/auctionManager.js'; + const ENDPOINT = 'https://pb.adriver.ru/cgi-bin/bid.cgi'; describe('adriverAdapter', function () { @@ -15,17 +16,44 @@ describe('adriverAdapter', function () { }); describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'adriver', - 'params': { - 'placementId': '55:test_placement', - 'siteid': 'testSiteID' + const bid = { + bidder: 'adriver', + params: { + placementId: '55:test_placement', + siteid: 'testSiteID' }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600], [300, 250]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600], [300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [ + { + id: '', + atype: 1, + ext: { + linkType: 0, + abTestingControlGroup: true + } + } + ] + }, + { + source: 'sharedid.org', + uids: [ + { + id: '01F4W41TMN7NBXBA0PXJMPB7GF', + atype: 1, + ext: { + third: '01F4W41TMN7NBXBA0PXJMPB7GF' + } + } + ] + } + ] }; it('should return true when required params found', function () { @@ -37,27 +65,163 @@ describe('adriverAdapter', function () { let getAdUnitsStub; const floor = 3; - let bidRequests = [ + const bidRequests = [ { - 'bidder': 'adriver', - 'params': { - 'placementId': '55:test_placement', - 'siteid': 'testSiteID', - 'dealid': 'dealidTest' + bidder: 'adriver', + params: { + placementId: '55:test_placement', + siteid: 'testSiteID', + dealid: 'dealidTest' }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600], [300, 250]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '04f2659e-c005-4eb1-a57c-fa93145e3843' + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600], [300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843', + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [ + { + id: '', + atype: 1, + ext: { + linkType: 0, + abTestingControlGroup: true + } + } + ] + }, + { + source: 'sharedid.org', + uids: [ + { + id: '01F4W41TMN7NBXBA0PXJMPB7GF', + atype: 1, + ext: { + third: '01F4W41TMN7NBXBA0PXJMPB7GF' + } + } + ] + } + ] } ]; + const bidderRequest = { + 'bidderCode': 'adriver', + 'auctionId': '2cdbf766-c37e-464c-a924-d8cf2a2f7ed2', + 'bidderRequestId': '10415226a1f2ac', + 'bids': [ + { + 'bidder': 'adriver', + 'params': { + 'siteid': '216200', + 'bidfloor': 1.33, + 'placementId': 'test1' + }, + 'auctionId': '2cdbf766-c37e-464c-a924-d8cf2a2f7ed2', + 'floorData': { + 'skipped': false, + 'skipRate': 5, + 'modelVersion': 'BlackBerryZap', + 'location': 'setConfig' + }, + 'userId': { + 'id5id': { + 'uid': 'ID5-ZHMO7vyrzH4ggO1TVF8lZ31h77BjNP6pLgMwIrhvtw!ID5*wP-eG3RLeJjkl1O5yeOMcf3Ksrsq1OeqM5nQZLgPvOMAACaMv9QnPWzdhdbFYu3r', + 'ext': { + 'linkType': 2, + 'abTestingControlGroup': false + } + }, + 'sharedid': { + 'id': '01F4W41TMN7NBXBA0PXJMPB7GF', + 'third': '01F4W41TMN7NBXBA0PXJMPB7GF' + } + }, + 'userIdAsEids': [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5-ZHMO7vyrzH4ggO1TVF8lZ31h77BjNP6pLgMwIrhvtw!ID5*wP-eG3RLeJjkl1O5yeOMcf3Ksrsq1OeqM5nQZLgPvOMAACaMv9QnPWzdhdbFYu3r', + 'atype': 1, + 'ext': { + 'linkType': 2, + 'abTestingControlGroup': false + } + } + ] + }, + { + 'source': 'sharedid.org', + 'uids': [ + { + 'id': '01F4W41TMN7NBXBA0PXJMPB7GF', + 'atype': 1, + 'ext': { + 'third': '01F4W41TMN7NBXBA0PXJMPB7GF' + } + } + ] + } + ], + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 600, + 500 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-51545-0', + 'transactionId': '01dfccdf-70d0-461f-b284-9132877ebe02', + 'sizes': [ + [ + 300, + 250 + ], + [ + 600, + 500 + ] + ], + 'bidId': '2794d8415635b3', + 'bidderRequestId': '10415226a1f2ac', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1622465003758, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://localhost:9999/integrationExamples/gpt/adUnitFloors.html', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': [ + 'http://localhost:9999/integrationExamples/gpt/adUnitFloors.html' + ], + 'canonicalUrl': null + }, + 'start': 1622465003762 + }; + let floorTestData = { 'currency': 'USD', 'floor': floor }; + bidRequests[0].getFloor = _ => { return floorTestData; }; @@ -79,6 +243,14 @@ describe('adriverAdapter', function () { expect(payload.cur).to.exist; }); + it('should exist timeout', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.tmax).to.exist; + expect(payload.tmax).to.equal(1000); + }); + it('should exist at', function () { const request = spec.buildRequests(bidRequests); const payload = JSON.parse(request.data); @@ -225,7 +397,34 @@ describe('adriverAdapter', function () { bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', - transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843', + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [ + { + id: '', + atype: 1, + ext: { + linkType: 0, + abTestingControlGroup: true + } + } + ] + }, + { + source: 'sharedid.org', + uids: [ + { + id: '01F4W41TMN7NBXBA0PXJMPB7GF', + atype: 1, + ext: { + third: '01F4W41TMN7NBXBA0PXJMPB7GF' + } + } + ] + } + ] } ]; @@ -320,4 +519,66 @@ describe('adriverAdapter', function () { expect(payload.imp[0].bidfloorcur).to.equal('RUB'); }); }); + + describe('user ids', function () { + let bidRequests = [ + { + bidder: 'adriver', + params: { + placementId: '55:test_placement', + siteid: 'testSiteID', + dealid: 'dealidTest', + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600], [300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843', + userIdAsEids: [ + { + source: 'id5-sync.com', + uids: [ + { + id: '', + atype: 1, + ext: { + linkType: 0, + abTestingControlGroup: true + } + } + ] + }, + { + source: 'sharedid.org', + uids: [ + { + id: '01F4W41TMN7NBXBA0PXJMPB7GF', + atype: 1, + ext: { + third: '01F4W41TMN7NBXBA0PXJMPB7GF' + } + } + ] + } + ] + } + ]; + + it('user id id5-sync.com', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.user.ext.eids[0].source).to.equal('id5-sync.com'); + expect(payload.user.ext.eids[0].uids[0].id).to.equal(''); + }); + + it('user id sharedid.org', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + + expect(payload.user.ext.eids[1].source).to.equal('sharedid.org'); + expect(payload.user.ext.eids[1].uids[0].id).to.equal('01F4W41TMN7NBXBA0PXJMPB7GF'); + }); + }); }); From 64885cb4f19765993ce0e293e6c214787a5edf15 Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Mon, 14 Jun 2021 12:30:35 -0400 Subject: [PATCH 1152/1476] amxIdSystem: add new ID submodule (#6996) * adding amxIdSystem * add amxIdSystem to userId_spec, fix related issues * forgot .js extension (lint) * add to submodules.json * Added integration example, removed timeout parameter * amxIdSystem: fix documentation + integration example --- integrationExamples/gpt/userId_example.html | 9 + modules/.submodules.json | 3 +- modules/amxIdSystem.js | 153 +++++++++++ modules/amxIdSystem.md | 51 ++++ modules/userId/eids.js | 4 + test/spec/modules/amxIdSystem_spec.js | 202 ++++++++++++++ test/spec/modules/eids_spec.js | 16 ++ test/spec/modules/userId_spec.js | 275 ++++++++++++-------- 8 files changed, 600 insertions(+), 113 deletions(-) create mode 100644 modules/amxIdSystem.js create mode 100644 modules/amxIdSystem.md create mode 100644 test/spec/modules/amxIdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 4b45064bb73..5659a208103 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -76,6 +76,7 @@ "95": true, // lotamePanoramaId "301": true, // zeotapIdPlus "91": true, // criteo + "737": true, // amxId } } } @@ -226,6 +227,14 @@ { "name": "criteo" }, + { + "name": "amxId", + "storage": { + "type": "html5", + "name": "amxId", + "expires": 14 + } + }, { "name": "uid2" } diff --git a/modules/.submodules.json b/modules/.submodules.json index 9e8300687db..04c92a2041e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -28,7 +28,8 @@ "uid2IdSystem", "admixerIdSystem", "dmdIdSystem", - "flocIdSystem" + "flocIdSystem", + "amxIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/amxIdSystem.js b/modules/amxIdSystem.js new file mode 100644 index 00000000000..8df518e4259 --- /dev/null +++ b/modules/amxIdSystem.js @@ -0,0 +1,153 @@ +/** + * This module adds AMX to the User ID Module + * The {@link module:modules/userId} is required + * + * @module modules/amxIdSystem + * @requires module:modules/userId + */ +import { uspDataHandler } from '../src/adapterManager.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { deepAccess, getWindowTop, logError } from '../src/utils.js'; + +const NAME = 'amxId'; +const GVL_ID = 737; +const ID_KEY = NAME; +const version = '1.0'; +const SYNC_URL = 'https://translator.a-mo.net/sync'; +const AJAX_TIMEOUT = 150; + +function validateConfig(config) { + if (config == null || config.storage == null) { + logError(`${NAME}: config.storage is required.`); + return false; + } + + if (config.storage.type !== 'html5') { + logError( + `${NAME} only supports storage.type "html5". ${config.storage.type} was provided` + ); + return false; + } + + if ( + typeof config.storage.expires === 'number' && + config.storage.expires > 30 + ) { + logError( + `${NAME}: storage.expires must be <= 30. ${config.storage.expires} was provided` + ); + return false; + } + + return true; +} + +function handleSyncResponse(client, response, callback) { + if (response.id != null && response.id.length > 0) { + callback(response.id); + return; + } + + if (response.u == null || response.u.length === 0) { + callback(null); + return; + } + + client(response.u, { + error(e) { + logError(`${NAME} failed on ${response.u}`, e); + callback(null); + }, + success(complete) { + if (complete != null && complete.length > 0) { + const value = JSON.parse(complete); + if (value.id != null) { + callback(value.id); + return; + } + } + + logError(`${NAME} invalid value`, complete); + callback(null); + }, + }); +} + +export const amxIdSubmodule = { + /** + * @type {string} + */ + name: NAME, + + /** + * @type {string} + */ + version, + + /** + * IAB TCF Vendor ID + * @type {string} + */ + gvlid: GVL_ID, + + decode: (value) => + value != null && value.length > 0 + ? { [ID_KEY]: value } + : undefined, + + getId(config, consentData, _extant) { + if (!validateConfig(config)) { + return undefined; + } + + const consent = consentData || { gdprApplies: false, consentString: '' }; + const client = ajaxBuilder(AJAX_TIMEOUT); + const usp = uspDataHandler.getConsentData(); + const ref = getRefererInfo(); + + const params = { + tagId: deepAccess(config, 'params.tagId', ''), + ref: ref.referer, + u: ref.stack[0] || getWindowTop().location.href, + v: '$prebid.version$', + vg: '$$PREBID_GLOBAL$$', + us_privacy: usp, + gdpr: consent.gdprApplies ? 1 : 0, + gdpr_consent: consent.consentString, + }; + + const callback = (done) => + client( + SYNC_URL, + { + error(e) { + logError(`${NAME} failed to load`, e); + done(null); + }, + success(responseText) { + if (responseText != null && responseText.length > 0) { + try { + const parsed = JSON.parse(responseText); + handleSyncResponse(client, parsed, done); + return; + } catch (e) { + logError(`${NAME} invalid response`, responseText); + } + } + + done(null); + }, + }, + params, + { + method: 'GET' + } + ); + + return { callback }; + }, +}; + +submodule('userId', amxIdSubmodule); diff --git a/modules/amxIdSystem.md b/modules/amxIdSystem.md new file mode 100644 index 00000000000..9de93c761a1 --- /dev/null +++ b/modules/amxIdSystem.md @@ -0,0 +1,51 @@ +# AMX RTB ID + +For help adding this module, please contact [prebid@amxrtb.com](prebid@amxrtb.com). + +### Prebid Configuration + +You can configure this module in your `userSync.userIds[]` configuration: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [ + { + name: "amxId", + storage: { + name: "amxId", + type: "html5", + expires: 14, + }, + params: { + tagId: "cHJlYmlkLm9yZw", + }, + }, + ], + }, +}); +``` + +| Param under `userSync.userIds[]` | Scope | Type | Description | Example | +| -------------------------------- | -------- | ------ | --------------------------- | ----------------------------------------- | +| name | Required | string | ID for the amxId module | `"amxId"` | +| storage | Required | Object | Settings for amxId storage | See [storage settings](#storage-settings) | +| params | Optional | Object | Parameters for amxId module | See [params](#params) | + +### Storage Settings + +The following settings are available for the `storage` property in the `userSync.userIds[]` object: + +| Param under `storage` | Scope | Type | Description | Example | +| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- | +| name | Required | String | Where the ID will be stored | `"amxId"` | +| type | Required | String | This must be `"html5"` | `"html5"` | +| expires | Required | Number <= 30 | number of days until the stored ID expires. **Must be less than or equal to 30** | `14` | + +### Params + +The following options are available in the `params` property in `userSync.userIds[]`: + +| Param under `params` | Scope | Type | Description | Example | +| -------------------- | -------- | ------ | ------------------------------------------------------------------------- | ---------------- | +| tagId | Optional | String | Your AMX tagId (optional) | `cHJlYmlkLm9yZw` | diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 3dc7a96c858..11c47e3ff8b 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -207,6 +207,10 @@ const USER_IDS_CONFIG = { 'admixerId': { source: 'admixer.net', atype: 3 + }, + amxId: { + source: 'amxrtb.com', + atype: 1, } }; diff --git a/test/spec/modules/amxIdSystem_spec.js b/test/spec/modules/amxIdSystem_spec.js new file mode 100644 index 00000000000..dea79e87baa --- /dev/null +++ b/test/spec/modules/amxIdSystem_spec.js @@ -0,0 +1,202 @@ +import { amxIdSubmodule } from 'modules/amxIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; +import * as utils from 'src/utils.js'; + +const TEST_ID = '51b561e3-0d82-4aea-8487-093fffca4a3a'; +const ERROR_CODES = [404, 501, 500, 403]; + +const config = { + params: { + tagId: Math.floor(Math.random() * 9e9).toString(36), + }, + storage: { + type: 'html5', + }, +}; + +describe('amxid submodule', () => { + it('should expose a "name" property containing amxId', () => { + expect(amxIdSubmodule.name).to.equal('amxId'); + }); + + it('should expose a "gvlid" property containing the GVL ID 737', () => { + expect(amxIdSubmodule.gvlid).to.equal(737); + }); +}); + +describe('decode', () => { + it('should respond with an object with "amxId" key containing the value', () => { + expect(amxIdSubmodule.decode(TEST_ID)).to.deep.equal({ + amxId: TEST_ID + }); + }); + + it('should respond with undefined if the value is not a string', () => { + [1, null, undefined, NaN, [], {}].forEach((value) => { + expect(amxIdSubmodule.decode(value)).to.equal(undefined); + }); + }); +}); + +describe('validateConfig', () => { + let logErrorSpy; + + beforeEach(() => { + logErrorSpy = sinon.spy(utils, 'logError'); + }); + afterEach(() => { + logErrorSpy.restore(); + }); + + it('should return undefined if config.storage is not present', () => { + expect( + amxIdSubmodule.getId( + { + ...config, + storage: null, + }, + null, + null + ) + ).to.equal(undefined); + + expect(logErrorSpy.calledOnce).to.be.true; + expect(logErrorSpy.lastCall.lastArg).to.contain('storage is required'); + }); + + it('should return undefined if config.storage.type !== "html5"', () => { + expect( + amxIdSubmodule.getId( + { + ...config, + storage: { + type: 'cookie', + }, + }, + null, + null + ) + ).to.equal(undefined); + + expect(logErrorSpy.calledOnce).to.be.true; + expect(logErrorSpy.lastCall.lastArg).to.contain('cookie'); + }); + + it('should return undefined if expires > 30', () => { + const expires = Math.floor(Math.random() * 90) + 30.01; + expect( + amxIdSubmodule.getId( + { + ...config, + storage: { + type: 'html5', + expires, + }, + }, + null, + null + ) + ).to.equal(undefined); + + expect(logErrorSpy.calledOnce).to.be.true; + expect(logErrorSpy.lastCall.lastArg).to.contain(expires); + }); +}); + +describe('getId', () => { + const spy = sinon.spy(); + + beforeEach(() => { + spy.resetHistory(); + }); + + it('should call the sync endpoint and accept a valid response', () => { + const { callback } = amxIdSubmodule.getId(config, null, null); + callback(spy); + + const [request] = server.requests; + expect(request.method).to.equal('GET'); + + request.respond( + 200, + {}, + JSON.stringify({ + id: TEST_ID, + v: '1.0a', + }) + ); + + expect(spy.calledOnce).to.be.true; + expect(spy.lastCall.lastArg).to.equal(TEST_ID); + }); + + it('should return undefined if the server has an error status code', () => { + const { callback } = amxIdSubmodule.getId(config, null, null); + callback(spy); + + const [request] = server.requests; + const responseCode = + ERROR_CODES[Math.floor(Math.random() * ERROR_CODES.length)]; + request.respond(responseCode, {}, ''); + + expect(spy.calledOnce).to.be.true; + expect(spy.lastCall.lastArg).to.equal(undefined); + }); + + it('should return undefined if the response has invalid keys', () => { + const { callback } = amxIdSubmodule.getId(config, null, null); + callback(spy); + + const [request] = server.requests; + request.respond( + 200, + {}, + JSON.stringify({ + test: TEST_ID, + }) + ); + + expect(spy.calledOnce).to.be.true; + expect(spy.lastCall.lastArg).to.equal(undefined); + }); + + it('should returned undefined if the server JSON is invalid', () => { + const { callback } = amxIdSubmodule.getId(config, null, null); + callback(spy); + + const [request] = server.requests; + request.respond(200, {}, '{,,}'); + + expect(spy.calledOnce).to.be.true; + expect(spy.lastCall.lastArg).to.equal(undefined); + }); + + it('should use the intermediate value for the sync server', () => { + const { callback } = amxIdSubmodule.getId(config, null, null); + callback(spy); + + const [request] = server.requests; + const intermediateValue = 'https://example-publisher.com/api/sync'; + + request.respond( + 200, + {}, + JSON.stringify({ + u: intermediateValue, + }) + ); + + const [, secondRequest] = server.requests; + expect(secondRequest.url).to.be.equal(intermediateValue); + secondRequest.respond( + 200, + {}, + JSON.stringify({ + id: TEST_ID, + }) + ); + + expect(spy.calledOnce).to.be.true; + expect(spy.lastCall.lastArg).to.equal(TEST_ID); + }); +}); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index c343f4359f7..d22225d191e 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -318,6 +318,22 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + + it('amxId', () => { + const id = 'c4bcadb0-124f-4468-a91a-d3d44cf311c5' + const userId = { + amxId: id + }; + + const [eid] = createEidsArray(userId); + expect(eid).to.deep.equal({ + source: 'amxrtb.com', + uids: [{ + atype: 1, + id, + }] + }); + }); }); describe('Negative case', function() { it('eids array generation for UN-known sub-module', function() { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 9f48127b6f9..8df7ad2dc53 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -46,6 +46,7 @@ import {uid2IdSubmodule} from 'modules/uid2IdSystem.js'; import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {flocIdSubmodule} from 'modules/flocIdSystem.js' +import { amxIdSubmodule } from '../../../modules/amxIdSystem.js'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -465,7 +466,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -473,14 +474,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -491,7 +492,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -508,7 +509,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -530,116 +531,116 @@ describe('User ID', function () { }); it('config with 21 configurations should result in 21 submodules add', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - it('config with 14 configurations should result in 14 submodules add', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 0, - userIds: [{ - name: 'pubProvidedId' - }, { - name: 'pubCommonId', value: {'pubcid': '11111'} - }, { - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }, { - name: 'id5Id', - storage: {name: 'id5id', type: 'cookie'} - }, { - name: 'identityLink', - storage: {name: 'idl_env', type: 'cookie'} - }, { - name: 'liveIntentId', - storage: {name: '_li_pbid', type: 'cookie'} - }, { - name: 'britepoolId', - value: {'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'} - }, { - name: 'netId', - storage: {name: 'netId', type: 'cookie'} - }, { - name: 'nextrollId' - }, { - name: 'intentIqId', - storage: {name: 'intentIqId', type: 'cookie'} - }, { - name: 'haloId', - storage: {name: 'haloId', type: 'cookie'} - }, { - name: 'zeotapIdPlus' - }, { - name: 'criteo' - }, { - name: 'mwOpenLinkId' - }, { - name: 'tapadId', - storage: {name: 'tapad_id', type: 'cookie'} - }, { - name: 'uid2' - }, { - name: 'admixerId', - storage: {name: 'admixerId', type: 'cookie'} - }, { - name: 'deepintentId', - storage: {name: 'deepintentId', type: 'cookie'} - }, { - name: 'flocId' - }, { - name: 'dmdId', - storage: {name: 'dmdId', type: 'cookie'} - }] - } - }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 20 submodules'); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'pubProvidedId' + }, { + name: 'pubCommonId', value: {'pubcid': '11111'} + }, { + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }, { + name: 'id5Id', + storage: {name: 'id5id', type: 'cookie'} + }, { + name: 'identityLink', + storage: {name: 'idl_env', type: 'cookie'} + }, { + name: 'liveIntentId', + storage: {name: '_li_pbid', type: 'cookie'} + }, { + name: 'britepoolId', + value: {'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'} + }, { + name: 'netId', + storage: {name: 'netId', type: 'cookie'} + }, { + name: 'nextrollId' + }, { + name: 'intentIqId', + storage: {name: 'intentIqId', type: 'cookie'} + }, { + name: 'haloId', + storage: {name: 'haloId', type: 'cookie'} + }, { + name: 'zeotapIdPlus' + }, { + name: 'criteo' + }, { + name: 'mwOpenLinkId' + }, { + name: 'tapadId', + storage: {name: 'tapad_id', type: 'cookie'} + }, { + name: 'uid2' + }, { + name: 'admixerId', + storage: {name: 'admixerId', type: 'cookie'} + }, { + name: 'deepintentId', + storage: {name: 'deepintentId', type: 'cookie'} + }, { + name: 'flocId' + }, { + name: 'dmdId', + storage: {name: 'dmdId', type: 'cookie'} + }, { + name: 'amxId', + storage: {name: 'amxId', type: 'html5'} + }] + } }); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 21 submodules'); + }); - it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); + it('config syncDelay updates module correctly', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - syncDelay: 99, - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } - }); - expect(syncDelay).to.equal(99); + init(config); + config.setConfig({ + userSync: { + syncDelay: 99, + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } }); + expect(syncDelay).to.equal(99); + }); - it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - auctionDelay: 100, - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } - }); - expect(auctionDelay).to.equal(100); + it('config auctionDelay updates module correctly', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + auctionDelay: 100, + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } }); + expect(auctionDelay).to.equal(100); + }); - it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule]); - init(config); - config.setConfig({ - userSync: { - auctionDelay: '', - userIds: [{ - name: 'unifiedId', - storage: {name: 'unifiedid', type: 'cookie'} - }] - } - }); - expect(auctionDelay).to.equal(0); + it('config auctionDelay defaults to 0 if not a number', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + init(config); + config.setConfig({ + userSync: { + auctionDelay: '', + userIds: [{ + name: 'unifiedId', + storage: {name: 'unifiedid', type: 'cookie'} + }] + } }); + expect(auctionDelay).to.equal(0); }); describe('auction and user sync delays', function () { @@ -950,6 +951,37 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from amxId html5', (done) => { + // simulate existing localStorage values + localStorage.setItem('amxId', 'test_amxid_id'); + localStorage.setItem('amxId_exp', ''); + + setSubmoduleRegistry([amxIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['amxId', 'amxId', 'html5'])); + + requestBidsHook(() => { + adUnits.forEach((adUnit) => { + adUnit.bids.forEach((bid) => { + expect(bid).to.have.deep.nested.property('userId.amxId'); + expect(bid.userId.amxId).to.equal('test_amxid_id'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'amxrtb.com', + uids: [{ + id: 'test_amxid_id', + atype: 1, + }] + }); + }); + }); + + // clear LS + localStorage.removeItem('amxId'); + localStorage.removeItem('amxId_exp'); + done(); + }, {adUnits}); + }); + it('test hook from identityLink html5', function (done) { // simulate existing browser local storage values localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); @@ -1643,7 +1675,7 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, dmdId and mwOpenLinkId have data to pass', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, amxId, dmdId and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1660,7 +1692,11 @@ describe('User ID', function () { coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); + // amxId only supports localStorage + localStorage.setItem('amxId', 'test_amxid_id'); + localStorage.setItem('amxId_exp', (new Date(Date.now() + 5000)).toUTCString()); + + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1677,6 +1713,7 @@ describe('User ID', function () { ['tapadId', 'tapad_id', 'cookie'], ['uid2', 'uid2id', 'cookie'], ['admixerId', 'admixerId', 'cookie'], + ['amxId', 'amxId', 'html5'], ['deepintentId', 'deepintentId', 'cookie'])); requestBidsHook(function () { @@ -1721,6 +1758,9 @@ describe('User ID', function () { expect(bid.userId.uid2).to.deep.equal({ id: 'Sample_AD_Token' }); + expect(bid).to.have.deep.nested.property('userId.amxId'); + expect(bid.userId.amxId).to.equal('test_amxid_id'); + // also check that criteo id was copied to bid expect(bid).to.have.deep.nested.property('userId.admixerId'); expect(bid.userId.admixerId).to.equal('testadmixerId'); @@ -1729,7 +1769,7 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.deepintentId'); expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(bid.userIdAsEids.length).to.equal(15); + expect(bid.userIdAsEids.length).to.equal(16); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1747,6 +1787,8 @@ describe('User ID', function () { coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('deepintentId', EXPIRED_COOKIE_DATE); + localStorage.removeItem('amxId'); + localStorage.removeItem('amxId_exp'); done(); }, {adUnits}); }); @@ -1927,8 +1969,10 @@ describe('User ID', function () { coreStorage.setCookie('deepintentId', 'testdeepintentId', new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); + localStorage.setItem('amxId', 'test_amxid_id'); + localStorage.setItem('amxId_exp', new Date(Date.now() + 5000).toUTCString()) - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ @@ -1962,6 +2006,8 @@ describe('User ID', function () { name: 'uid2' }, { name: 'deepintentId', storage: {name: 'deepintentId', type: 'cookie'} + }, { + name: 'amxId', storage: {name: 'amxId', type: 'html5'} }] } }); @@ -2020,6 +2066,9 @@ describe('User ID', function () { id: 'Sample_AD_Token' }); + expect(bid).to.have.deep.nested.property('userId.amxId'); + expect(bid.userId.amxId).to.equal('test_amxid_id'); + // also check that admixerId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.admixerId'); expect(bid.userId.admixerId).to.equal('testadmixerId'); @@ -2028,7 +2077,7 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.deepintentId'); expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(bid.userIdAsEids.length).to.equal(13); + expect(bid.userIdAsEids.length).to.equal(14); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -2045,6 +2094,8 @@ describe('User ID', function () { coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); + localStorage.removeItem('amxId'); + localStorage.removeItem('amxId_exp'); done(); }, {adUnits}); }); From db127c73a283d315694ae0584e11962ea794c9e1 Mon Sep 17 00:00:00 2001 From: Igor Tchibirev Date: Mon, 14 Jun 2021 13:15:11 -0400 Subject: [PATCH 1153/1476] RealVu Analytics Adapter: remove "tablet" from device types (#7016) * Remove _ps in _f=conf request * Replace " * realvuAnalyticsAdapter_spec updated * Update realvuAnalyticsAdapter.js Co-authored-by: Igor Tchibirev --- modules/realvuAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 95e62397c2c..78fa61c9576 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -42,7 +42,7 @@ export let lib = { init: function () { let z = this; let u = navigator.userAgent; - z.device = u.match(/iPad|Tablet/gi) ? 'tablet' : u.match(/iPhone|iPod|Android|Opera Mini|IEMobile/gi) ? 'mobile' : 'desktop'; + z.device = u.match(/iPhone|iPod|Android|Opera Mini|IEMobile/gi) ? 'mobile' : 'desktop'; if (typeof (z.len) == 'undefined') z.len = 0; z.ie = navigator.appVersion.match(/MSIE/); z.saf = (u.match(/Safari/) && !u.match(/Chrome/)); From dc6be1b136d65f26d308d4ec2eb74fca77b704e0 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Mon, 14 Jun 2021 19:22:48 +0200 Subject: [PATCH 1154/1476] tappx Bid Adapter: fix wrong requests with undefined params (#7021) * tappxBidAdapter - remove unsuported user eids * tappxBidAdapter : make optional video params * tappxBidAdapter - fix undefined or null params * tappxBidAdapter - Update adapter version * tappxBidAdapter - Add EOL Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 25 ++++++++++++++----------- modules/tappxBidAdapter.md | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index ff7cffdb021..19ca0cf16c0 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10610'; +const TAPPX_BIDDER_VERSION = '0.1.10614'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream']; @@ -294,9 +294,11 @@ function buildOneRequest(validBidRequests, bidderRequest) { let video = {}; let videoParams = utils.deepAccess(validBidRequests, 'params.video'); - for (var key in VIDEO_CUSTOM_PARAMS) { - if (videoParams.hasOwnProperty(key)) { - video[key] = _checkParamDataType(key, videoParams[key], VIDEO_CUSTOM_PARAMS[key]); + if (typeof videoParams !== 'undefined') { + for (var key in VIDEO_CUSTOM_PARAMS) { + if (videoParams.hasOwnProperty(key)) { + video[key] = _checkParamDataType(key, videoParams[key], VIDEO_CUSTOM_PARAMS[key]); + } } } @@ -379,14 +381,15 @@ function buildOneRequest(validBidRequests, bidderRequest) { if (typeof eidsArr !== 'undefined') { eidsArr = eidsArr.filter( uuid => - uuid !== null && - (uuid.source !== undefined && uuid.source !== null && typeof uuid.uids[0].id == 'string') && - (uuid.uids[0].id !== undefined && uuid.uids[0].id !== null && typeof uuid.uids[0].id == 'string') + (typeof uuid !== 'undefined' && uuid !== null) && + (typeof uuid.source == 'string' && uuid.source !== null) && + (typeof uuid.uids[0].id == 'string' && uuid.uids[0].id !== null) ) - } - payload.user = { - ext: { - eids: eidsArr + + payload.user = { + ext: { + eids: eidsArr + } } }; diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index ca9b2b97035..3ec4a4a5829 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -63,7 +63,7 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 endpoint: "VZ12TESTCTV", bidfloor: 0.005, test: true, - video: { + video: { // optional skippable: true, // optional minduration: 5, // optional maxduration: 30, // optional From 49579bd7c9895e959296e15936390e1bc84c302e Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 15 Jun 2021 01:35:15 +0600 Subject: [PATCH 1155/1476] Zeta Ssp Bid Adapter: page and domain fields from config (#7019) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 4 ++-- test/spec/modules/zetaSspBidAdapter_spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index ecf3c2835d4..5b42935cd18 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -64,8 +64,8 @@ export const spec = { }; const rInfo = bidderRequest.refererInfo; payload.device.ua = navigator.userAgent; - payload.site.page = (rInfo && rInfo.referer) ? rInfo.referer.trim() : window.location.href; - payload.site.domain = getDomainFromURL(payload.site.page); + payload.site.page = config.getConfig('pageUrl') || ((rInfo && rInfo.referer) ? rInfo.referer.trim() : window.location.href); + payload.site.domain = config.getConfig('publisherDomain') || getDomainFromURL(payload.site.page); payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; if (params.test) { diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 2313fae3705..080c487a29e 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -72,7 +72,7 @@ describe('Zeta Ssp Bid Adapter', function() { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); expect(payload.site.page).to.eql('http://www.zetaglobal.com/page?param=value'); - expect(payload.site.domain).to.eql('zetaglobal.com'); + expect(payload.site.domain).to.eql(window.location.origin); // config.js -> DEFAULT_PUBLISHER_DOMAIN }); it('Test the request processing function', function () { From dc6ae76eaf2227677ced782ae945083495bae8ab Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Tue, 15 Jun 2021 01:54:40 -0700 Subject: [PATCH 1156/1476] add uid2 to OpenX Bid Adapter (#7015) --- modules/openxBidAdapter.js | 5 ++++- test/spec/modules/openxBidAdapter_spec.js | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index b49f7ef2c3c..ee4784350c4 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -33,7 +33,7 @@ export const USER_ID_CODE_TO_QUERY_ARG = { quantcastId: 'quantcastid', // Quantcast ID tapadId: 'tapadid', // Tapad Id tdid: 'ttduuid', // The Trade Desk Unified ID - verizonMediaId: 'verizonmediaid', // Verizon Media ConnectID + uid2: 'uid2', // Unified ID 2.0 }; export const spec = { @@ -291,6 +291,9 @@ function appendUserIdsToQueryParams(queryParams, userIds) { if (USER_ID_CODE_TO_QUERY_ARG.hasOwnProperty(userIdProviderKey)) { switch (userIdProviderKey) { + case 'uid2': + queryParams[key] = userIdObjectOrValue.id; + break; case 'lipb': queryParams[key] = userIdObjectOrValue.lipbid; break; diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 112dd4768df..532c1d40ee0 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1066,7 +1066,7 @@ describe('OpenxAdapter', function () { quantcastId: '1111-quantcastid', tapadId: '111-tapadid', tdid: '1111-tdid', - verizonMediaId: '1111-verizonmediaid', + uid2: {id: '1111-uid2'} }; // generates the same set of tests for each id provider @@ -1104,6 +1104,9 @@ describe('OpenxAdapter', function () { let userIdValue; // handle cases where userId key refers to an object switch (userIdProviderKey) { + case 'uid2': + userIdValue = EXAMPLE_DATA_BY_ATTR.uid2.id; + break; case 'lipb': userIdValue = EXAMPLE_DATA_BY_ATTR.lipb.lipbid; break; From d649149b181223ba42423fb19a8b569ebbdd97fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Tue, 15 Jun 2021 11:55:21 +0300 Subject: [PATCH 1157/1476] VidazooBidAdapter: support for response meta.advertiserDomains (v5.0) (#7022) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): support for response advertiserDomains (prebid v5.0) Co-authored-by: roman --- modules/vidazooBidAdapter.js | 280 +++++++++++++++ test/spec/modules/vidazooBidAdapter_spec.js | 376 ++++++++++++++++++++ 2 files changed, 656 insertions(+) create mode 100644 modules/vidazooBidAdapter.js create mode 100644 test/spec/modules/vidazooBidAdapter_spec.js diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js new file mode 100644 index 00000000000..36705bae199 --- /dev/null +++ b/modules/vidazooBidAdapter.js @@ -0,0 +1,280 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const GLVID = 744; +const DEFAULT_SUB_DOMAIN = 'prebid'; +const BIDDER_CODE = 'vidazoo'; +const BIDDER_VERSION = '1.0.0'; +const CURRENCY = 'USD'; +const TTL_SECONDS = 60 * 5; +const DEAL_ID_EXPIRY = 1000 * 60 * 15; +const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; +const SESSION_ID_KEY = 'vidSid'; +export const SUPPORTED_ID_SYSTEMS = { + 'britepoolid': 1, + 'criteoId': 1, + 'digitrustid': 1, + 'id5id': 1, + 'idl_env': 1, + 'lipb': 1, + 'netId': 1, + 'parrableId': 1, + 'pubcid': 1, + 'tdid': 1, +}; +const storage = getStorageManager(GLVID); + +export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { + return `https://${subDomain}.cootlogix.com`; +} + +export function extractCID(params) { + return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; +} + +export function extractPID(params) { + return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; +} + +export function extractSubDomain(params) { + return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; +} + +function isBidRequestValid(bid) { + const params = bid.params || {}; + return !!(extractCID(params) && extractPID(params)); +} + +function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { + const { params, bidId, userId, adUnitCode } = bid; + const { bidFloor, ext } = params; + const hashUrl = hashCode(topWindowUrl); + const dealId = getNextDealId(hashUrl); + const uniqueDealId = getUniqueDealId(hashUrl); + const sId = getVidazooSessionId(); + const cId = extractCID(params); + const pId = extractPID(params); + const subDomain = extractSubDomain(params); + + let data = { + url: encodeURIComponent(topWindowUrl), + cb: Date.now(), + bidFloor: bidFloor, + bidId: bidId, + adUnitCode: adUnitCode, + publisherId: pId, + sessionId: sId, + sizes: sizes, + dealId: dealId, + uniqueDealId: uniqueDealId, + bidderVersion: BIDDER_VERSION, + prebidVersion: '$prebid.version$', + res: `${screen.width}x${screen.height}` + }; + + appendUserIdsToRequestPayload(data, userId); + + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + data.gdprConsent = bidderRequest.gdprConsent.consentString; + } + if (bidderRequest.gdprConsent.gdprApplies !== undefined) { + data.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + } + if (bidderRequest.uspConsent) { + data.usPrivacy = bidderRequest.uspConsent + } + + const dto = { + method: 'POST', + url: `${createDomain(subDomain)}/prebid/multi/${cId}`, + data: data + }; + + utils._each(ext, (value, key) => { + dto.data['ext.' + key] = value; + }); + + return dto; +} + +function appendUserIdsToRequestPayload(payloadRef, userIds) { + let key; + utils._each(userIds, (userId, idSystemProviderName) => { + if (SUPPORTED_ID_SYSTEMS[idSystemProviderName]) { + key = `uid.${idSystemProviderName}`; + + switch (idSystemProviderName) { + case 'digitrustid': + payloadRef[key] = utils.deepAccess(userId, 'data.id'); + break; + case 'lipb': + payloadRef[key] = userId.lipbid; + break; + case 'parrableId': + payloadRef[key] = userId.eid; + break; + case 'id5id': + payloadRef[key] = userId.uid; + break; + default: + payloadRef[key] = userId; + } + } + }); +} + +function buildRequests(validBidRequests, bidderRequest) { + const topWindowUrl = bidderRequest.refererInfo.referer; + const requests = []; + validBidRequests.forEach(validBidRequest => { + const sizes = utils.parseSizesInput(validBidRequest.sizes); + const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest); + requests.push(request); + }); + return requests; +} + +function interpretResponse(serverResponse, request) { + if (!serverResponse || !serverResponse.body) { + return []; + } + const { bidId } = request.data; + const { results } = serverResponse.body; + + let output = []; + + try { + results.forEach(result => { + const { creativeId, ad, price, exp, width, height, currency, advertiserDomains } = result; + if (!ad || !price) { + return; + } + output.push({ + requestId: bidId, + cpm: price, + width: width, + height: height, + creativeId: creativeId, + currency: currency || CURRENCY, + netRevenue: true, + ttl: exp || TTL_SECONDS, + ad: ad, + meta: { + advertiserDomains: advertiserDomains || [] + } + }) + }); + return output; + } catch (e) { + return []; + } +} + +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 || '')}` + if (iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `https://prebid.cootlogix.com/api/sync/iframe/${params}` + }); + } + if (pixelEnabled) { + syncs.push({ + type: 'image', + url: `https://prebid.cootlogix.com/api/sync/image/${params}` + }); + } + return syncs; +} + +export function hashCode(s, prefix = '_') { + const l = s.length; + let h = 0 + let i = 0; + if (l > 0) { + while (i < l) { h = (h << 5) - h + s.charCodeAt(i++) | 0; } + } + return prefix + h; +} + +export function getNextDealId(key, expiry = DEAL_ID_EXPIRY) { + try { + const data = getStorageItem(key); + let currentValue = 0; + let timestamp; + + if (data && data.value && Date.now() - data.created < expiry) { + currentValue = data.value; + timestamp = data.created; + } + + const nextValue = currentValue + 1; + setStorageItem(key, nextValue, timestamp); + return nextValue; + } catch (e) { + return 0; + } +} + +export function getUniqueDealId(key, expiry = UNIQUE_DEAL_ID_EXPIRY) { + const storageKey = `u_${key}`; + const now = Date.now(); + const data = getStorageItem(storageKey); + let uniqueId; + + if (!data || !data.value || now - data.created > expiry) { + uniqueId = `${key}_${now.toString()}`; + setStorageItem(storageKey, uniqueId); + } else { + uniqueId = data.value; + } + + return uniqueId; +} + +export function getVidazooSessionId() { + return getStorageItem(SESSION_ID_KEY) || ''; +} + +export function getStorageItem(key) { + try { + return tryParseJSON(storage.getDataFromLocalStorage(key)); + } catch (e) { } + + return null; +} + +export function setStorageItem(key, value, timestamp) { + try { + const created = timestamp || Date.now(); + const data = JSON.stringify({ value, created }); + storage.setDataInLocalStorage(key, data); + } catch (e) { } +} + +export function tryParseJSON(value) { + try { + return JSON.parse(value); + } catch (e) { + return value; + } +} + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +}; + +registerBidder(spec); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js new file mode 100644 index 00000000000..35f510fd6ee --- /dev/null +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -0,0 +1,376 @@ +import { expect } from 'chai'; +import { + spec as adapter, + SUPPORTED_ID_SYSTEMS, + createDomain, + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, + getNextDealId, + getVidazooSessionId, +} from 'modules/vidazooBidAdapter.js'; +import * as utils from 'src/utils.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; + +const SUB_DOMAIN = 'openrtb'; + +const BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': 'div-gpt-ad-12345-0', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '59db6b3b4ffaa70004f45cdc', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1, + 'ext': { + 'param1': 'loremipsum', + 'param2': 'dolorsitamet' + } + }, + 'placementCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '1fdb5ff1b6eaa7', + 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a' +}; + +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string', + 'gdprApplies': true + }, + 'uspConsent': 'consent_string', + 'refererInfo': { + 'referer': 'https://www.greatsite.com' + } +}; + +const SERVER_RESPONSE = { + body: { + results: [{ + 'ad': '', + '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' + } +}; + +describe('VidazooBidAdapter', 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 () { + sandbox = sinon.sandbox.create(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.referer); + 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', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + dealId: 1, + sessionId: '', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + res: `${window.top.screen.width}x${window.top.screen.height}`, + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + } + }); + }); + + after(function () { + 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://prebid.cootlogix.com/api/sync/iframe/?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://prebid.cootlogix.com/api/sync/image/?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 'digitrustid': return { data: { id: id } }; + 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('vidazoo session id', function () { + it('should get undefined vidazoo session id', function () { + const sessionId = getVidazooSessionId(); + expect(sessionId).to.be.empty; + }); + + it('should get vidazoo session id from storage', function () { + const vidSid = '1234-5678'; + window.localStorage.setItem('vidSid', vidSid); + const sessionId = getVidazooSessionId(); + expect(sessionId).to.be.equal(vidSid); + }); + }); + + describe('deal id', function () { + const key = 'myDealKey'; + + it('should get the next deal id', function () { + const dealId = getNextDealId(key); + const nextDealId = getNextDealId(key); + expect(dealId).to.be.equal(1); + expect(nextDealId).to.be.equal(2); + }); + + it('should get the first deal id on expiration', function (done) { + setTimeout(function () { + const dealId = getNextDealId(key, 100); + expect(dealId).to.be.equal(1); + done(); + }, 200); + }); + }); + + describe('unique deal id', function () { + const key = 'myKey'; + let uniqueDealId; + uniqueDealId = getUniqueDealId(key); + + 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 () { + const current = getUniqueDealId(key, 100); + expect(current).to.not.be.equal(uniqueDealId); + }); + }); + + describe('storage utils', function () { + 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 9e87c74c9fbc8d546b41c39c84b29f629e7afa4b Mon Sep 17 00:00:00 2001 From: jsut Date: Tue, 15 Jun 2021 07:22:27 -0400 Subject: [PATCH 1158/1476] make sizes a variable as the code expects (#7024) --- integrationExamples/postbid/postbid_prebidServer_example.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integrationExamples/postbid/postbid_prebidServer_example.html b/integrationExamples/postbid/postbid_prebidServer_example.html index 3af7b010872..9f4944884a0 100644 --- a/integrationExamples/postbid/postbid_prebidServer_example.html +++ b/integrationExamples/postbid/postbid_prebidServer_example.html @@ -14,11 +14,12 @@ })(); pbjs.que.push(function() { + var sizes = [[300, 250]]; var adUnits = [{ code: 'postbid_iframe', mediaTypes: { banner: { - sizes: [[300, 250]] + sizes: sizes } }, bids: [ From d9babb3f46f76a5084a5ac1b4e3e2788ebcd2f2e Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Wed, 16 Jun 2021 00:22:02 +0600 Subject: [PATCH 1159/1476] Zeta Ssp Bid Adapter: provide devicetype (#7026) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 5b42935cd18..54c0657e9df 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -63,10 +63,12 @@ export const spec = { } }; const rInfo = bidderRequest.refererInfo; - payload.device.ua = navigator.userAgent; payload.site.page = config.getConfig('pageUrl') || ((rInfo && rInfo.referer) ? rInfo.referer.trim() : window.location.href); payload.site.domain = config.getConfig('publisherDomain') || getDomainFromURL(payload.site.page); - payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; + + payload.device.ua = navigator.userAgent; + payload.device.devicetype = isMobile() ? 1 : isConnectedTV() ? 3 : 2; + payload.site.mobile = payload.device.devicetype === 1 ? 1 : 0; if (params.test) { payload.test = params.test; @@ -190,4 +192,12 @@ function getDomainFromURL(url) { return hostname; } +function isMobile() { + return /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent); +} + +function isConnectedTV() { + return /(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i.test(navigator.userAgent); +} + registerBidder(spec); From acf66ae87ae27b04c853329f1a43af2f81d4c44d Mon Sep 17 00:00:00 2001 From: ghguo Date: Wed, 16 Jun 2021 06:46:30 -0400 Subject: [PATCH 1160/1476] Fixed FPD issue after for Prebid.js 5.0 (#7031) --- modules/adrelevantisBidAdapter.js | 2 +- test/spec/modules/adrelevantisBidAdapter_spec.js | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index b3efa8a303d..b6832cfeb0d 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -124,7 +124,7 @@ export const spec = { if (fpdcfg && fpdcfg.context) { let fdata = { keywords: fpdcfg.context.keywords || '', - category: utils.deepAccess(fpdcfg, 'context.data.category') || '' + category: fpdcfg.context.category || '' } payload.fpd = fdata; } diff --git a/test/spec/modules/adrelevantisBidAdapter_spec.js b/test/spec/modules/adrelevantisBidAdapter_spec.js index 596f3bade5d..b87f9d6b86c 100644 --- a/test/spec/modules/adrelevantisBidAdapter_spec.js +++ b/test/spec/modules/adrelevantisBidAdapter_spec.js @@ -228,11 +228,7 @@ describe('AdrelevantisAdapter', function () { .returns({ site: { keywords: 'US Open', - ext: { - data: { - category: 'sports/tennis' - } - } + category: 'sports/tennis' } }); From a6b18ae120f4a8ba74b255975c21ce9c730266a6 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Wed, 16 Jun 2021 13:53:56 +0300 Subject: [PATCH 1161/1476] Adf Bid Adapter: add adform alias (#7009) --- modules/adfBidAdapter.js | 5 ++++- test/spec/modules/adfBidAdapter_spec.js | 11 +++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 5893891eba6..7666e98b374 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -15,7 +15,10 @@ const { getConfig } = config; const BIDDER_CODE = 'adf'; const GVLID = 50; -const BIDDER_ALIAS = [ { code: 'adformOpenRTB', gvlid: GVLID } ]; +const BIDDER_ALIAS = [ + { code: 'adformOpenRTB', gvlid: GVLID }, + { code: 'adform', gvlid: GVLID } +]; const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; const NATIVE_PARAMS = { title: { diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index ae92af32a1e..25ad6987153 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -1,12 +1,10 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {spec} from 'modules/adfBidAdapter.js'; -import { NATIVE } from 'src/mediaTypes.js'; +import { assert } from 'chai'; +import { spec } from 'modules/adfBidAdapter.js'; import { config } from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; describe('Adf adapter', function () { - let serverResponse, bidRequest, bidResponses; let bids = []; describe('backwards-compatibility', function () { @@ -14,6 +12,11 @@ describe('Adf adapter', function () { assert.equal(spec.aliases[0].code, 'adformOpenRTB'); assert.equal(spec.aliases[0].gvlid, 50); }); + + it('should have adform alias defined', function () { + assert.equal(spec.aliases[1].code, 'adform'); + assert.equal(spec.aliases[1].gvlid, 50); + }); }); describe('isBidRequestValid', function () { From 2219cb37b8e74621b0a1ebb0d94e648516eec63b Mon Sep 17 00:00:00 2001 From: HiroyukiFujimura <31462500+HiroyukiFujimura@users.noreply.github.com> Date: Wed, 16 Jun 2021 20:43:10 +0900 Subject: [PATCH 1162/1476] Add microadBidAdapter (#7007) --- modules/microadBidAdapter.js | 152 +++++++++++ test/spec/microadBidAdapter_spec.js | 404 ++++++++++++++++++++++++++++ 2 files changed, 556 insertions(+) create mode 100644 modules/microadBidAdapter.js create mode 100644 test/spec/microadBidAdapter_spec.js diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js new file mode 100644 index 00000000000..1a2f6f28019 --- /dev/null +++ b/modules/microadBidAdapter.js @@ -0,0 +1,152 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'microad'; + +const ENDPOINT_URLS = { + 'production': 'https://s-rtb-pb.send.microad.jp/prebid', + 'test': 'https://rtbtest.send.microad.jp/prebid' +}; +export let ENVIRONMENT = 'production'; + +/* eslint-disable no-template-curly-in-string */ +const EXT_URL_STRING = '${COMPASS_EXT_URL}'; +const EXT_REF_STRING = '${COMPASS_EXT_REF}'; +const EXT_IFA_STRING = '${COMPASS_EXT_IFA}'; +const EXT_APPID_STRING = '${COMPASS_EXT_APPID}'; +const EXT_GEO_STRING = '${COMPASS_EXT_GEO}'; +/* eslint-enable no-template-curly-in-string */ + +const BANNER_CODE = 1; +const NATIVE_CODE = 2; +const VIDEO_CODE = 4; + +function createCBT() { + const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); + const date = new Date().getTime().toString(16); + return randomValue + date; +} + +function createBitSequenceFromMediaType(hi, code) { + return (hi ? -1 : 0) & code; +} + +function convertMediaTypes(bid) { + return createBitSequenceFromMediaType(bid.mediaTypes.banner, BANNER_CODE) | + createBitSequenceFromMediaType(bid.mediaTypes.native, NATIVE_CODE) | + createBitSequenceFromMediaType(bid.mediaTypes.video, VIDEO_CODE); +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.spot && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); + }, + buildRequests: function(validBidRequests, bidderRequest) { + const requests = []; + + validBidRequests.forEach(bid => { + const bidParams = bid.params; + const params = { + spot: bidParams.spot, + url: bidderRequest.refererInfo.canonicalUrl || window.location.href, + referrer: bidderRequest.refererInfo.referer, + bid_id: bid.bidId, + transaction_id: bid.transactionId, + media_types: convertMediaTypes(bid), + cbt: createCBT() + }; + + if (bidParams.url) { + params['url_macro'] = bidParams.url.replace(EXT_URL_STRING, ''); + } + + if (bidParams.referrer) { + params['referrer_macro'] = bidParams.referrer.replace(EXT_REF_STRING, ''); + } + + if (bidParams.ifa) { + params['ifa'] = bidParams.ifa.replace(EXT_IFA_STRING, ''); + } + + if (bidParams.appid) { + params['appid'] = bidParams.appid.replace(EXT_APPID_STRING, ''); + } + + if (bidParams.geo) { + const geo = bidParams.geo.replace(EXT_GEO_STRING, ''); + if (/^[0-9.\-]+,[0-9.\-]+$/.test(geo)) { + params['geo'] = geo; + } + } + + requests.push({ + method: 'GET', + url: ENDPOINT_URLS[ENVIRONMENT], + data: params, + options: { Accept: 'application/json' } + }); + }); + return requests; + }, + interpretResponse: function(serverResponse) { + const body = serverResponse.body; + const bidResponses = []; + + if (body.cpm && body.cpm > 0) { + const bidResponse = { + requestId: body.requestId, + cpm: body.cpm, + width: body.width, + height: body.height, + ad: body.ad, + ttl: body.ttl, + creativeId: body.creativeId, + netRevenue: body.netRevenue, + currency: body.currency, + meta: body.meta || { advertiserDomains: [] } + }; + + if (body.dealId) { + bidResponse['dealId'] = body.dealId; + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + + if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { + return syncs; + } + + serverResponses.forEach(resp => { + const syncIframeUrls = resp.body.syncUrls.iframe; + const syncImageUrls = resp.body.syncUrls.image; + if (syncOptions.iframeEnabled && syncIframeUrls) { + syncIframeUrls.forEach(syncIframeUrl => { + syncs.push({ + type: 'iframe', + url: syncIframeUrl + }); + }); + } + if (syncOptions.pixelEnabled && syncImageUrls) { + syncImageUrls.forEach(syncImageUrl => { + syncs.push({ + type: 'image', + url: syncImageUrl + }); + }); + } + }); + + return syncs; + } +}; + +registerBidder(spec); diff --git a/test/spec/microadBidAdapter_spec.js b/test/spec/microadBidAdapter_spec.js new file mode 100644 index 00000000000..21e65462234 --- /dev/null +++ b/test/spec/microadBidAdapter_spec.js @@ -0,0 +1,404 @@ +import { expect } from 'chai'; +import { spec } from 'modules/microadBidAdapter.js'; +import * as utils from 'src/utils.js'; + +describe('microadBidAdapter', () => { + const bidRequestTemplate = { + bidder: 'microad', + mediaTypes: { + banner: {} + }, + params: { + spot: 'spot-code' + }, + bidId: 'bid-id', + transactionId: 'transaction-id' + }; + + describe('isBidRequestValid', () => { + it('should return true when required parameters are set', () => { + const validBids = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }) + ]; + validBids.forEach(validBid => { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); + + it('should return false when required parameters are not set', () => { + const bidWithoutParams = utils.deepClone(bidRequestTemplate); + delete bidWithoutParams.params; + const bidWithoutSpot = utils.deepClone(bidRequestTemplate); + delete bidWithoutSpot.params.spot; + const bidWithoutMediaTypes = utils.deepClone(bidRequestTemplate); + delete bidWithoutMediaTypes.mediaTypes; + + const invalidBids = [ + {}, + bidWithoutParams, + bidWithoutSpot, + bidWithoutMediaTypes, + Object.assign({}, bidRequestTemplate, { + mediaTypes: {} + }) + ]; + invalidBids.forEach(invalidBid => { + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + }); + + describe('buildRequests', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'https://example.com/to', + referer: 'https://example.com/from' + } + }; + const expectedResultTemplate = { + spot: 'spot-code', + url: 'https://example.com/to', + referrer: 'https://example.com/from', + bid_id: 'bid-id', + transaction_id: 'transaction-id', + media_types: 1 + }; + + it('should generate valid media_types', () => { + const bidRequests = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, video: {} + } + }) + ]; + + const results = bidRequests.map(bid => { + const requests = spec.buildRequests([bid], bidderRequest); + return requests[0].data.media_types; + }); + expect(results).to.deep.equal([ + 1, // BANNER + 3, // BANNER + NATIVE + 7, // BANNER + NATIVE + VIDEO + 2, // NATIVE + 6, // NATIVE + VIDEO + 4, // VIDEO + 5 // BANNER + VIDEO + ]); + }); + + it('should use window.location.href if there is no canonicalUrl', () => { + const bidderRequestWithoutCanonicalUrl = { + refererInfo: { + referer: 'https://example.com/from' + } + }; + const requests = spec.buildRequests([bidRequestTemplate], bidderRequestWithoutCanonicalUrl); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url: window.location.href + }) + ); + }); + }); + + it('should generate valid request with no optional parameters', () => { + const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + + it('should add url_macro parameter to response if request parameters contain url', () => { + const bidRequestWithUrl = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + url: '${COMPASS_EXT_URL}url-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithUrl], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url_macro: 'url-macro' + }) + ); + }); + }); + + it('should add referrer_macro parameter to response if request parameters contain referrer', () => { + const bidRequestWithReferrer = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + referrer: '${COMPASS_EXT_REF}referrer-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithReferrer], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + referrer_macro: 'referrer-macro' + }) + ); + }); + }); + + it('should add ifa parameter to response if request parameters contain ifa', () => { + const bidRequestWithIfa = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + ifa: '${COMPASS_EXT_IFA}ifa' + } + }); + const requests = spec.buildRequests([bidRequestWithIfa], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + ifa: 'ifa' + }) + ); + }); + }); + + it('should add appid parameter to response if request parameters contain appid', () => { + const bidRequestWithAppid = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + appid: '${COMPASS_EXT_APPID}appid' + } + }); + const requests = spec.buildRequests([bidRequestWithAppid], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + appid: 'appid' + }) + ); + }); + }); + + it('should add geo parameter to response if request parameters contain geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}35.655275,139.693771' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + geo: '35.655275,139.693771' + }) + ); + }); + }); + + it('should not add geo parameter to response if request parameters contain invalid geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}invalid format geo' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + + it('should always use the HTTPS endpoint https://s-rtb-pb.send.microad.jp/prebid even if it is served via HTTP', () => { + const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); + requests.forEach(request => { + expect(request.url.lastIndexOf('https', 0) === 0).to.be.true; + }); + }); + }); + + describe('interpretResponse', () => { + const serverResponseTemplate = { + body: { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
test
', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY', + meta: { + advertiserDomains: ['foobar.com'] + } + } + }; + const expectedBidResponseTemplate = { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
test
', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY', + meta: { + advertiserDomains: ['foobar.com'] + } + }; + + it('should return nothing if server response body does not contain cpm', () => { + const emptyResponse = { + body: {} + }; + + expect(spec.interpretResponse(emptyResponse)).to.deep.equal([]); + }); + + it('should return nothing if returned cpm is zero', () => { + const serverResponse = { + body: { + cpm: 0 + } + }; + + expect(spec.interpretResponse(serverResponse)).to.deep.equal([]); + }); + + it('should return a valid bidResponse without deal id if serverResponse is valid, has a nonzero cpm and no deal id', () => { + expect(spec.interpretResponse(serverResponseTemplate)).to.deep.equal([expectedBidResponseTemplate]); + }); + + it('should return a valid bidResponse with deal id if serverResponse is valid, has a nonzero cpm and a deal id', () => { + const serverResponseWithDealId = Object.assign({}, utils.deepClone(serverResponseTemplate)); + serverResponseWithDealId.body['dealId'] = 10001; + const expectedBidResponse = Object.assign({}, expectedBidResponseTemplate, { + dealId: 10001 + }); + + expect(spec.interpretResponse(serverResponseWithDealId)).to.deep.equal([expectedBidResponse]); + }); + + it('should return a valid bidResponse without meta if serverResponse is valid, has a nonzero cpm and no deal id', () => { + const serverResponseWithoutMeta = Object.assign({}, utils.deepClone(serverResponseTemplate)); + delete serverResponseWithoutMeta.body.meta; + const expectedBidResponse = Object.assign({}, expectedBidResponseTemplate, { + meta: { advertiserDomains: [] } + }); + + expect(spec.interpretResponse(serverResponseWithoutMeta)).to.deep.equal([expectedBidResponse]); + }); + }); + + describe('getUserSyncs', () => { + const BOTH_ENABLED = { + iframeEnabled: true, pixelEnabled: true + }; + const IFRAME_ENABLED = { + iframeEnabled: true, pixelEnabled: false + }; + const PIXEL_ENABLED = { + iframeEnabled: false, pixelEnabled: true + }; + const BOTH_DISABLED = { + iframeEnabled: false, pixelEnabled: false + }; + const serverResponseTemplate = { + body: { + syncUrls: { + iframe: ['https://www.exmaple.com/iframe1', 'https://www.exmaple.com/iframe2'], + image: ['https://www.exmaple.com/image1', 'https://www.exmaple.com/image2'] + } + } + }; + const expectedIframeSyncs = [ + {type: 'iframe', url: 'https://www.exmaple.com/iframe1'}, + {type: 'iframe', url: 'https://www.exmaple.com/iframe2'} + ]; + const expectedImageSyncs = [ + {type: 'image', url: 'https://www.exmaple.com/image1'}, + {type: 'image', url: 'https://www.exmaple.com/image2'} + ]; + + it('should return nothing if no sync urls are set', () => { + const serverResponse = utils.deepClone(serverResponseTemplate); + serverResponse.body.syncUrls.iframe = []; + serverResponse.body.syncUrls.image = []; + + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponse]); + expect(syncs).to.deep.equal([]); + }); + + it('should return nothing if sync is disabled', () => { + const syncs = spec.getUserSyncs(BOTH_DISABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal([]); + }); + + it('should register iframe and image sync urls if sync is enabled', () => { + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs.concat(expectedImageSyncs)); + }); + + it('should register iframe sync urls if iframe is enabled', () => { + const syncs = spec.getUserSyncs(IFRAME_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs); + }); + + it('should register image sync urls if image is enabled', () => { + const syncs = spec.getUserSyncs(PIXEL_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedImageSyncs); + }); + }); +}); From 12f461093b15657e11bedacba91319db873201b0 Mon Sep 17 00:00:00 2001 From: htang555 Date: Wed, 16 Jun 2021 08:33:52 -0400 Subject: [PATCH 1163/1476] Datablocks Bid Adapter : update bid adapter and add 5.0 compliance (#6765) * update datablocks bid adapter * remove TODO and fix linting errors * updated readme and changed insights to ortb2 * fixed ortb2 change * kick off tests and fix linting error * add adomain to bid.meta * kick off circleci * try to kick off tests manually again * kick off tests * fix linting error * fix other linting error * modifed code to pass circleci tests * fix for IE to pass tests * to be or not to be * removed user agent bot check and IE URL parsing issue Co-authored-by: John Mayor Co-authored-by: Chris Huie <3444727+ChrisHuie@users.noreply.github.com> Co-authored-by: Chris Huie --- modules/datablocksBidAdapter.js | 857 ++++++++++++------ modules/datablocksBidAdapter.md | 32 +- .../spec/modules/datablocksBidAdapter_spec.js | 601 +++++++----- 3 files changed, 982 insertions(+), 508 deletions(-) diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 47d2eb2652f..bfbe7a16fc6 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,330 +1,627 @@ -import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -const NATIVE_MAP = { - 'body': 2, - 'body2': 10, - 'price': 6, - 'displayUrl': 11, - 'cta': 12 -}; -const NATIVE_IMAGE = [{ - id: 1, - required: 1, +import { config } from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +export const storage = getStorageManager(); + +const NATIVE_ID_MAP = {}; +const NATIVE_PARAMS = { title: { - len: 140 - } -}, { - id: 2, - required: 1, - img: { type: 3 } -}, { - id: 3, - required: 1, - data: { - type: 11 - } -}, { - id: 4, - required: 0, - data: { + id: 1, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + body: { + id: 4, + name: 'data', type: 2 + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + cta: { + id: 6, + type: 12, + name: 'data' + }, + body2: { + id: 7, + name: 'data', + type: 10 + }, + rating: { + id: 8, + name: 'data', + type: 3 + }, + likes: { + id: 9, + name: 'data', + type: 4 + }, + downloads: { + id: 10, + name: 'data', + type: 5 + }, + displayUrl: { + id: 11, + name: 'data', + type: 11 + }, + price: { + id: 12, + name: 'data', + type: 6 + }, + salePrice: { + id: 13, + name: 'data', + type: 7 + }, + address: { + id: 14, + name: 'data', + type: 9 + }, + phone: { + id: 15, + name: 'data', + type: 8 } -}, { - id: 5, - required: 0, - img: { type: 1 } -}, { - id: 6, - required: 0, - data: { - type: 12 - } -}]; +}; -const VIDEO_PARAMS = ['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']; +Object.keys(NATIVE_PARAMS).forEach((key) => { + NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key; +}); +// DEFINE THE PREBID BIDDER SPEC export const spec = { - supportedMediaTypes: [BANNER, NATIVE, VIDEO], + supportedMediaTypes: [BANNER, NATIVE], code: 'datablocks', + + // DATABLOCKS SCOPED OBJECT + db_obj: {metrics_host: 'prebid.datablocks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, + + // STORE THE DATABLOCKS BUYERID IN STORAGE + store_dbid: function(dbid) { + let stored = false; + + // CREATE 1 YEAR EXPIRY DATE + let d = new Date(); + d.setTime(Date.now() + (365 * 24 * 60 * 60 * 1000)); + + // TRY TO STORE IN COOKIE + if (storage.cookiesAreEnabled) { + storage.setCookie('_db_dbid', dbid, d.toUTCString(), 'None', null); + stored = true; + } + + // TRY TO STORE IN LOCAL STORAGE + if (storage.localStorageIsEnabled) { + storage.setDataInLocalStorage('_db_dbid', dbid); + stored = true; + } + + return stored; + }, + + // FETCH DATABLOCKS BUYERID FROM STORAGE + get_dbid: function() { + let dbId = ''; + if (storage.cookiesAreEnabled) { + dbId = storage.getCookie('_db_dbid') || ''; + } + + if (!dbId && storage.localStorageIsEnabled) { + dbId = storage.getDataFromLocalStorage('_db_dbid') || ''; + } + return dbId; + }, + + // STORE SYNCS IN STORAGE + store_syncs: function(syncs) { + if (storage.localStorageIsEnabled) { + let syncObj = {}; + syncs.forEach(sync => { + syncObj[sync.id] = sync.uid; + }); + + // FETCH EXISTING SYNCS AND MERGE NEW INTO STORAGE + let storedSyncs = this.get_syncs(); + storage.setDataInLocalStorage('_db_syncs', JSON.stringify(Object.assign(storedSyncs, syncObj))); + + return true; + } + }, + + // GET SYNCS FROM STORAGE + get_syncs: function() { + if (storage.localStorageIsEnabled) { + let syncData = storage.getDataFromLocalStorage('_db_syncs'); + if (syncData) { + return JSON.parse(syncData); + } else { + return {}; + } + } else { + return {}; + } + }, + + // ADD METRIC DATA TO THE METRICS RESPONSE QUEUE + queue_metric: function(metric) { + if (typeof metric === 'object') { + // PUT METRICS IN THE QUEUE + this.db_obj.metrics.push(metric); + + // RESET PREVIOUS TIMER + if (this.db_obj.metrics_timer) { + clearTimeout(this.db_obj.metrics_timer); + } + + // SETUP THE TIMER TO FIRE BACK THE DATA + let scope = this; + this.db_obj.metrics_timer = setTimeout(function() { + scope.send_metrics(); + }, this.db_obj.metrics_queue_time); + + return true; + } else { + return false; + } + }, + + // POST CONSOLIDATED METRICS BACK TO SERVER + send_metrics: function() { + // POST TO SERVER + ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), {method: 'POST', withCredentials: true}); + + // RESET THE QUEUE OF METRIC DATA + this.db_obj.metrics = []; + + return true; + }, + + // GET BASIC CLIENT INFORMATION + get_client_info: function () { + let botTest = new BotClientTests(); + let win = utils.getWindowTop(); + return { + 'wiw': win.innerWidth, + 'wih': win.innerHeight, + 'saw': screen ? screen.availWidth : null, + 'sah': screen ? screen.availHeight : null, + 'scd': screen ? screen.colorDepth : null, + 'sw': screen ? screen.width : null, + 'sh': screen ? screen.height : null, + 'whl': win.history.length, + 'wxo': win.pageXOffset, + 'wyo': win.pageYOffset, + 'wpr': win.devicePixelRatio, + 'is_bot': botTest.doTests(), + 'is_hid': win.document.hidden, + 'vs': win.document.visibilityState + }; + }, + + // LISTEN FOR GPT VIEWABILITY EVENTS + get_viewability: function(bid) { + // ONLY RUN ONCE IF PUBLISHER HAS OPTED IN + if (!this.db_obj.vis_optout && !this.db_obj.vis_run) { + this.db_obj.vis_run = true; + + // ADD GPT EVENT LISTENERS + let scope = this; + if (utils.isGptPubadsDefined()) { + if (typeof window['googletag'].pubads().addEventListener == 'function') { + window['googletag'].pubads().addEventListener('impressionViewable', function(event) { + scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + }); + window['googletag'].pubads().addEventListener('slotRenderEnded', function(event) { + scope.queue_metric({type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + }) + } + } + } + }, + + // VALIDATE THE BID REQUEST isBidRequestValid: function(bid) { - return !!(bid.params.host && bid.params.sourceId && - bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); + // SET GLOBAL VARS FROM BIDDER CONFIG + this.db_obj.source_id = bid.params.source_id; + if (bid.params.vis_optout) { + this.db_obj.vis_optout = true; + } + + return !!(bid.params.source_id && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native)); }, - buildRequests: function(validBidRequests, bidderRequest) { - if (!validBidRequests.length) { return []; } - let imps = {}; - let site = {}; - let device = {}; - let refurl = utils.parseUrl(bidderRequest.referrer); - let requests = []; + // GENERATE THE RTB REQUEST + buildRequests: function(validRequests, bidderRequest) { + // RETURN EMPTY IF THERE ARE NO VALID REQUESTS + if (!validRequests.length) { + return []; + } - validBidRequests.forEach(bidRequest => { + // CONVERT PREBID NATIVE REQUEST OBJ INTO RTB OBJ + function createNativeRequest(bid) { + const assets = []; + if (bid.nativeParams) { + Object.keys(bid.nativeParams).forEach((key) => { + if (NATIVE_PARAMS[key]) { + const {name, type, id} = NATIVE_PARAMS[key]; + const assetObj = type ? {type} : {}; + let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key]; + if (len) { + assetObj.len = len; + } + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + let wmin = aRatios.min_width || 0; + let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + assetObj.wmin = wmin; + assetObj.hmin = hmin; + } + if (sizes && sizes.length) { + sizes = [].concat(...sizes); + assetObj.w = sizes[0]; + assetObj.h = sizes[1]; + } + const asset = {required: required ? 1 : 0, id}; + asset[name] = assetObj; + assets.push(asset); + } + }); + } + return { + ver: '1.2', + request: { + assets: assets, + context: 1, + plcmttype: 1, + ver: '1.2' + } + } + } + let imps = []; + // ITERATE THE VALID REQUESTS AND GENERATE IMP OBJECT + validRequests.forEach(bidRequest => { + // BUILD THE IMP OBJECT let imp = { id: bidRequest.bidId, - tagid: bidRequest.adUnitCode, - secure: window.location.protocol == 'https:' - }; + tagid: bidRequest.params.tagid || bidRequest.adUnitCode, + placement_id: bidRequest.params.placement_id || 0, + secure: window.location.protocol == 'https:', + ortb2: utils.deepAccess(bidRequest, `ortb2Imp`) || {}, + floor: {} + } + // CHECK FOR FLOORS + if (typeof bidRequest.getFloor === 'function') { + imp.floor = bidRequest.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + } + + // BUILD THE SIZES if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { - let sizes = bidRequest.mediaTypes.banner.sizes; - if (sizes.length == 1) { + let sizes = utils.getAdUnitSizes(bidRequest); + if (sizes.length) { imp.banner = { w: sizes[0][0], - h: sizes[0][1] - }; - } else if (sizes.length > 1) { - imp.banner = { + h: sizes[0][1], format: sizes.map(size => ({ w: size[0], h: size[1] })) }; - } else { - return; - } - } else if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { - let nativeImp = bidRequest.mediaTypes.native; - - if (nativeImp.type) { - let nativeAssets = []; - switch (nativeImp.type) { - case 'image': - nativeAssets = NATIVE_IMAGE; - break; - default: - return; - } - imp.native = JSON.stringify({ assets: nativeAssets }); - } else { - let nativeAssets = []; - let nativeKeys = Object.keys(nativeImp); - nativeKeys.forEach((nativeKey, index) => { - let required = !!nativeImp[nativeKey].required; - let assetId = index + 1; - switch (nativeKey) { - case 'title': - nativeAssets.push({ - id: assetId, - required: required, - title: { - len: nativeImp[nativeKey].len || 140 - } - }); - break; - case 'body': // desc - case 'body2': // desc2 - case 'price': - case 'display_url': - let data = { - id: assetId, - required: required, - data: { - type: NATIVE_MAP[nativeKey] - } - } - if (nativeImp[nativeKey].data && nativeImp[nativeKey].data.len) { data.data.len = nativeImp[nativeKey].data.len; } - - nativeAssets.push(data); - break; - case 'image': - if (nativeImp[nativeKey].sizes && nativeImp[nativeKey].sizes.length) { - nativeAssets.push({ - id: assetId, - required: required, - image: { - type: 3, - w: nativeImp[nativeKey].sizes[0], - h: nativeImp[nativeKey].sizes[1] - } - }) - } - } - }); - imp.native = { - request: JSON.stringify({native: {assets: nativeAssets}}) - }; - } - } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { - let video = bidRequest.mediaTypes.video; - let sizes = video.playerSize || bidRequest.sizes || []; - if (sizes.length && Array.isArray(sizes[0])) { - imp.video = { - w: sizes[0][0], - h: sizes[0][1] - }; - } else if (sizes.length == 2 && !Array.isArray(sizes[0])) { - imp.video = { - w: sizes[0], - h: sizes[1] - }; - } else { - return; - } - - if (video.durationRangeSec) { - if (Array.isArray(video.durationRangeSec)) { - if (video.durationRangeSec.length == 1) { - imp.video.maxduration = video.durationRangeSec[0]; - } else if (video.durationRangeSec.length == 2) { - imp.video.minduration = video.durationRangeSec[0]; - imp.video.maxduration = video.durationRangeSec[1]; - } - } else { - imp.video.maxduration = video.durationRangeSec; - } - } - if (bidRequest.params.video) { - Object.keys(bidRequest.params.video).forEach(k => { - if (VIDEO_PARAMS.indexOf(k) > -1) { - imp.video[k] = bidRequest.params.video[k]; - } - }); + // ADD TO THE LIST OF IMP REQUESTS + imps.push(imp); } + } else if (utils.deepAccess(bidRequest, `mediaTypes.native`)) { + // ADD TO THE LIST OF IMP REQUESTS + imp.native = createNativeRequest(bidRequest); + imps.push(imp); } - let host = bidRequest.params.host; - let sourceId = bidRequest.params.sourceId; - imps[host] = imps[host] || {}; - let hostImp = imps[host][sourceId] = imps[host][sourceId] || { imps: [] }; - hostImp.imps.push(imp); - hostImp.subid = hostImp.imps.subid || bidRequest.params.subid || 'blank'; - hostImp.path = 'search'; - hostImp.idParam = 'sid'; - hostImp.protocol = '//'; }); - // Generate Site obj - site.domain = refurl.hostname; - site.page = refurl.protocol + '://' + refurl.hostname + refurl.pathname; + // RETURN EMPTY IF THERE WERE NO PROPER ADUNIT REQUESTS TO BE MADE + if (!imps.length) { + return []; + } + + // GENERATE SITE OBJECT + let site = { + domain: window.location.host, + page: bidderRequest.refererInfo.referer, + schain: validRequests[0].schain || {}, + ext: { + p_domain: config.getConfig('publisherDomain'), + rt: bidderRequest.refererInfo.reachedTop, + frames: bidderRequest.refererInfo.numIframes, + stack: bidderRequest.refererInfo.stack, + timeout: config.getConfig('bidderTimeout') + }, + } + + // ADD REF URL IF FOUND if (self === top && document.referrer) { site.ref = document.referrer; } + + // ADD META KEYWORDS IF FOUND let keywords = document.getElementsByTagName('meta')['keywords']; if (keywords && keywords.content) { site.keywords = keywords.content; } - // Generate Device obj. - device.ip = 'peer'; - device.ua = window.navigator.userAgent; - device.js = 1; - device.language = ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en'; - - RtbRequest(device, site, imps).forEach(formatted => { - requests.push({ - method: 'POST', - url: formatted.url, - data: formatted.body, - options: { - withCredentials: false + // GENERATE DEVICE OBJECT + let device = { + ip: 'peer', + ua: window.navigator.userAgent, + js: 1, + language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', + buyerid: this.get_dbid() || 0, + ext: { + pb_eids: validRequests[0].userIdAsEids || {}, + syncs: this.get_syncs() || {}, + coppa: config.getConfig('coppa') || 0, + gdpr: bidderRequest.gdprConsent || {}, + usp: bidderRequest.uspConsent || {}, + client_info: this.get_client_info(), + ortb2: config.getConfig('ortb2') || {} + } + }; + + let sourceId = validRequests[0].params.source_id || 0; + let host = validRequests[0].params.host || 'prebid.datablocks.net'; + + // RETURN WITH THE REQUEST AND PAYLOAD + return { + method: 'POST', + url: `https://${sourceId}.${host}/openrtb/?sid=${sourceId}`, + data: { + id: bidderRequest.auctionId, + imp: imps, + site: site, + device: device + }, + options: { + withCredentials: true + } + }; + }, + + // INITIATE USER SYNCING + getUserSyncs: function(options, rtbResponse, gdprConsent) { + const syncs = []; + let bidResponse = rtbResponse[0].body; + let scope = this; + + // LISTEN FOR SYNC DATA FROM IFRAME TYPE SYNC + window.addEventListener('message', function (event) { + if (event.data.sentinel && event.data.sentinel === 'dblks_syncData') { + // STORE FOUND SYNCS + if (event.data.syncs) { + scope.store_syncs(event.data.syncs); } - }) + } }); - return requests; - - function RtbRequest(device, site, imps) { - let collection = []; - Object.keys(imps).forEach(host => { - let sourceIds = imps[host]; - Object.keys(sourceIds).forEach(sourceId => { - let impObj = sourceIds[sourceId]; - collection.push({ - url: `https://${host}/${impObj.path}/?${impObj.idParam}=${sourceId}`, - body: { - id: bidderRequest.auctionId, - imp: impObj.imps, - site: Object.assign({ id: impObj.subid || 'blank' }, site), - device: Object.assign({}, device) - } - }) - }) + + // POPULATE GDPR INFORMATION + let gdprData = { + gdpr: 0, + gdprConsent: '' + } + if (typeof gdprConsent === 'object') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprData.gdpr = Number(gdprConsent.gdprApplies); + gdprData.gdprConsent = gdprConsent.consentString; + } else { + gdprData.gdprConsent = gdprConsent.consentString; + } + } + + // EXTRACT BUYERID COOKIE VALUE FROM BID RESPONSE AND PUT INTO STORAGE + let dbBuyerId = this.get_dbid() || ''; + if (bidResponse.ext && bidResponse.ext.buyerid) { + dbBuyerId = bidResponse.ext.buyerid; + this.store_dbid(dbBuyerId); + } + + // EXTRACT USERSYNCS FROM BID RESPONSE + if (bidResponse.ext && bidResponse.ext.syncs) { + bidResponse.ext.syncs.forEach(sync => { + if (checkValid(sync)) { + syncs.push(addParams(sync)); + } }) + } + + // APPEND PARAMS TO SYNC URL + function addParams(sync) { + // PARSE THE URL + try { + let url = new URL(sync.url); + let urlParams = {}; + for (const [key, value] of url.searchParams.entries()) { + urlParams[key] = value; + }; - return collection; + // APPLY EXTRA VARS + urlParams.gdpr = gdprData.gdpr; + urlParams.gdprConsent = gdprData.gdprConsent; + urlParams.bidid = bidResponse.bidid; + urlParams.id = bidResponse.id; + urlParams.uid = dbBuyerId; + + // REBUILD URL + sync.url = `${url.origin}${url.pathname}?${Object.keys(urlParams).map(key => key + '=' + encodeURIComponent(urlParams[key])).join('&')}`; + } catch (e) {}; + + // RETURN THE REBUILT URL + return sync; + } + + // ENSURE THAT THE SYNC TYPE IS VALID AND HAS PERMISSION + function checkValid(sync) { + if (!sync.type || !sync.url) { + return false; + } + switch (sync.type) { + case 'iframe': + return options.iframeEnabled; + case 'image': + return options.pixelEnabled; + default: + return false; + } } + return syncs; }, - interpretResponse: function(serverResponse, bidRequest) { - if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { - return []; + + // DATABLOCKS WON THE AUCTION - REPORT SUCCESS + onBidWon: function(bid) { + this.queue_metric({type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl}); + }, + + // TARGETING HAS BEEN SET + onSetTargeting: function(bid) { + // LISTEN FOR VIEWABILITY EVENTS + this.get_viewability(bid); + }, + + // PARSE THE RTB RESPONSE AND RETURN FINAL RESULTS + interpretResponse: function(rtbResponse, bidRequest) { + // CONVERT NATIVE RTB RESPONSE INTO PREBID RESPONSE + function parseNative(native) { + const {assets, link, imptrackers, jstracker} = native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || [], + impressionTrackers: imptrackers || [], + javascriptTrackers: jstracker ? [jstracker] : [] + }; + + (assets || []).forEach((asset) => { + const {id, img, data, title} = asset; + const key = NATIVE_ID_MAP[id]; + if (key) { + if (!utils.isEmpty(title)) { + result.title = title.text + } else if (!utils.isEmpty(img)) { + result[key] = { + url: img.url, + height: img.h, + width: img.w + } + } else if (!utils.isEmpty(data)) { + result[key] = data.value; + } + } + }); + + return result; } - let body = serverResponse.body; - - let bids = body.seatbid - .map(seatbid => seatbid.bid) - .reduce((memo, bid) => memo.concat(bid), []); - let req = bidRequest.data; - let reqImps = req.imp; - - return bids.map(rtbBid => { - let imp; - for (let i in reqImps) { - let testImp = reqImps[i] - if (testImp.id == rtbBid.impid) { - imp = testImp; + + let bids = []; + let resBids = utils.deepAccess(rtbResponse, 'body.seatbid') || []; + resBids.forEach(bid => { + let resultItem = {requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360, meta: {advertiserDomains: bid.adomain}}; + + let mediaType = utils.deepAccess(bid, 'ext.mtype') || ''; + switch (mediaType) { + case 'banner': + bids.push(Object.assign({}, resultItem, {mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm})); + break; + + case 'native': + let nativeResult = JSON.parse(bid.adm); + bids.push(Object.assign({}, resultItem, {mediaType: NATIVE, native: parseNative(nativeResult.native)})); + break; + + default: break; - } } - let br = { - requestId: rtbBid.impid, - cpm: rtbBid.price, - creativeId: rtbBid.crid, - currency: rtbBid.currency || 'USD', - netRevenue: true, - ttl: 360 - }; - if (!imp) { - return br; - } else if (imp.banner) { - br.mediaType = BANNER; - br.width = rtbBid.w; - br.height = rtbBid.h; - br.ad = rtbBid.adm; - } else if (imp.native) { - br.mediaType = NATIVE; - - let reverseNativeMap = {}; - let nativeKeys = Object.keys(NATIVE_MAP); - nativeKeys.forEach(k => { - reverseNativeMap[NATIVE_MAP[k]] = k; - }); + }) + + return bids; + } +}; + +// DETECT BOTS +export class BotClientTests { + constructor() { + this.tests = { + headless_chrome: function() { + if (self.navigator) { + if (self.navigator.webdriver) { + return true; + } + } - let idMap = {}; - let nativeReq = JSON.parse(imp.native.request); - if (nativeReq.native && nativeReq.native.assets) { - nativeReq.native.assets.forEach(asset => { - if (asset.data) { idMap[asset.id] = reverseNativeMap[asset.data.type]; } + return false; + }, + + selenium: function () { + let response = false; + + if (window && document) { + let results = [ + 'webdriver' in window, + '_Selenium_IDE_Recorder' in window, + 'callSelenium' in window, + '_selenium' in window, + '__webdriver_script_fn' in document, + '__driver_evaluate' in document, + '__webdriver_evaluate' in document, + '__selenium_evaluate' in document, + '__fxdriver_evaluate' in document, + '__driver_unwrapped' in document, + '__webdriver_unwrapped' in document, + '__selenium_unwrapped' in document, + '__fxdriver_unwrapped' in document, + '__webdriver_script_func' in document, + document.documentElement.getAttribute('selenium') !== null, + document.documentElement.getAttribute('webdriver') !== null, + document.documentElement.getAttribute('driver') !== null + ]; + + results.forEach(result => { + if (result === true) { + response = true; + } }) } - const nativeResponse = JSON.parse(rtbBid.adm); - const { assets, link, imptrackers, jstrackers } = nativeResponse.native; - const result = { - clickUrl: link.url, - clickTrackers: link.clicktrackers || undefined, - impressionTrackers: imptrackers || undefined, - javascriptTrackers: jstrackers ? [jstrackers] : undefined - }; - assets.forEach(asset => { - if (asset.title) { - result.title = asset.title.text; - } else if (asset.img) { - result.image = asset.img.url; - } else if (idMap[asset.id]) { - result[idMap[asset.id]] = asset.data.value; - } - }) - br.native = result; - } else if (imp.video) { - br.mediaType = VIDEO; - br.width = rtbBid.w; - br.height = rtbBid.h; - if (rtbBid.adm) { br.vastXml = rtbBid.adm; } else if (rtbBid.nurl) { br.vastUrl = rtbBid.nurl; } + return response; + }, + } + } + doTests() { + let response = false; + for (const i of Object.keys(this.tests)) { + if (this.tests[i]() === true) { + response = true; } - return br; - }); + } + return response; } +} -}; +// INIT OUR BIDDER WITH PREBID registerBidder(spec); diff --git a/modules/datablocksBidAdapter.md b/modules/datablocksBidAdapter.md index e30cd361974..ad2eb4afc53 100644 --- a/modules/datablocksBidAdapter.md +++ b/modules/datablocksBidAdapter.md @@ -8,8 +8,8 @@ Maintainer: support@datablocks.net # Description -Connects to Datablocks Version 5 Platform -Banner Native and Video +Connects to Datablocks Exchange +Banner and Native # Test Parameters @@ -27,12 +27,13 @@ Banner Native and Video { bidder: 'datablocks', params: { - sourceId: 12345, + source_id: 12345, host: 'prebid.datablocks.net' } } ] - }, { + }, + { code: 'native-div', mediaTypes : { native: { @@ -44,30 +45,11 @@ Banner Native and Video { bidder: 'datablocks', params: { - sourceId: 12345, + source_id: 12345, host: 'prebid.datablocks.net' } - }, { - code: 'video-div', - mediaTypes : { - video: { - playerSize:[500,400], - durationRangeSec:[15,30], - context: "linear" - } - }, - bids: [ - { - bidder: 'datablocks', - params: { - sourceId: 12345, - host: 'prebid.datablocks.net', - video: { - mimes:["video/flv"] - } - } } ] } ]; -``` \ No newline at end of file +``` diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 18b8aac7371..0ec12905430 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -1,12 +1,15 @@ import { expect } from 'chai'; import { spec } from '../../../modules/datablocksBidAdapter.js'; +import { BotClientTests } from '../../../modules/datablocksBidAdapter.js'; +import { getStorageManager } from '../../../src/storageManager.js'; +export let storage = getStorageManager(); -let bid = { +const bid = { bidId: '2dd581a2b6281d', bidder: 'datablocks', bidderRequestId: '145e1d6a7837c9', params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -24,12 +27,12 @@ let bid = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe5542e8e1' }; -let bid2 = { +const bid2 = { bidId: '2dd581a2b624324g', bidder: 'datablocks', bidderRequestId: '145e1d6a7837543', params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, adUnitCode: '/19968336/header-bid-tag-0', @@ -43,7 +46,7 @@ let bid2 = { transactionId: '1ccbee15-f6f6-46ce-8998-58fe55425432' }; -let nativeBid = { +const nativeBid = { adUnitCode: '/19968336/header-bid-tag-0', auctionId: '160c78a4-f808-410f-b682-d8728f3a79ee', bidId: '332045ee374a99', @@ -78,41 +81,18 @@ let nativeBid = { } }, params: { - sourceId: 7560, + source_id: 7560, host: 'v5demo.datablocks.net' }, transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f6' } -let videoBid = { - adUnitCode: '/19968336/header-bid-tag-0', - auctionId: '160c78a4-f808-410f-b682-d8728f3a79e1', - bidId: '332045ee374b99', - bidder: 'datablocks', - bidderRequestId: '15d9012765e36d', - mediaTypes: { - video: { - context: 'instream', - playerSize: [501, 400], - durationRangeSec: [15, 60] - } - }, - params: { - sourceId: 7560, - host: 'v5demo.datablocks.net', - video: { - minduration: 14 - } - }, - transactionId: '0a4e9788-4def-4b94-bc25-564d7cac99f7' -} - const bidderRequest = { auctionId: '8bfef1be-d3ac-4d18-8859-754c7b4cf017', auctionStart: Date.now(), biddeCode: 'datablocks', bidderRequestId: '10c47a5fc3c41', - bids: [bid, bid2, nativeBid, videoBid], + bids: [bid, bid2, nativeBid], refererInfo: { numIframes: 0, reachedTop: true, @@ -123,208 +103,423 @@ const bidderRequest = { timeout: 10000 }; -let resObject = { +const res_object = { body: { - id: '10c47a5fc3c41', - bidid: '166895245-28-11347-1', - seatbid: [{ - seat: '7560', - bid: [{ - id: '1090738570', - impid: '2966b257c81d27', - price: 24.000000, - adm: 'RON', - cid: '55', - adid: '177654', - crid: '177656', - cat: [], - api: [], - w: 300, - h: 250 - }, { - id: '1090738571', - impid: '2966b257c81d28', - price: 24.000000, - adm: 'RON', - cid: '55', - adid: '177654', - crid: '177656', - cat: [], - api: [], - w: 728, - h: 90 - }, { - id: '1090738570', - impid: '15d9012765e36c', - price: 24.000000, - adm: '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"Example Title"}},{"id":2,"required":1,"data":{"value":"Example Body"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', - cid: '132145', - adid: '154321', - crid: '177432', - cat: [], - api: [] - }, { - id: '1090738575', - impid: '15d9012765e36f', - price: 25.000000, - cid: '12345', - adid: '12345', - crid: '123456', - nurl: 'https://click.v5demo.datablocks.net/m//?fcid=435235435432', - cat: [], - api: [], - w: 500, - h: 400 - }] - }], - cur: 'USD', - ext: {} + 'id': '10c47a5fc3c41', + 'bidid': '217868445-30021-19053-0', + 'seatbid': [ + { + 'id': '22621593137287', + 'impid': '1', + 'adm': 'John is great', + 'adomain': ['medianet.com'], + 'price': 0.430000, + 'cid': '2524568', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'type': 'CPM', + 'mtype': 'banner' + } + }, + { + 'id': '22645215457415', + 'impid': '2', + 'adm': 'john is the best', + 'adomain': ['td.com'], + 'price': 0.580000, + 'cid': '2524574', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'w': 728, + 'h': 90, + 'ext': { + 'type': 'CPM', + 'mtype': 'banner' + } + }, + + { + 'id': '22645215457416', + 'impid': '3', + 'adm': '{"native":{"ver":"1.2","assets":[{"id":1,"required":1,"title":{"text":"John is amazing"}},{"id":5,"required":1,"data":{"value":"Sponsored by John"}},{"id":3,"required":1,"img":{"url":"https://example.image.com/", "h":"360", "w":"360"}}],"link":{"url":"https://click.example.com/c/264597/?fcid=29699699045816"},"imptrackers":["https://impression.example.com/i/264597/?fcid=29699699045816"]}}', + 'adomain': ['td.com'], + 'price': 10.00, + 'cid': '2524574', + 'adid': '0', + 'crid': '0', + 'cat': [], + 'ext': { + 'type': 'CPM', + 'mtype': 'native' + } + } + ], + 'cur': 'USD', + 'ext': { + 'version': '1.2.93', + 'buyerid': '1234567', + 'syncs': [ + { + 'type': 'iframe', + 'url': 'https://s.0cf.io' + }, + { + 'type': 'image', + 'url': 'https://us.dblks.net/set_uid/' + } + ] + } } -}; -let bidRequest = { +} + +let bid_request = { method: 'POST', - url: 'https://v5demo.datablocks.net/search/?sid=7560', + url: 'https://prebid.datablocks.net/openrtb/?sid=2523014', options: { - withCredentials: false + withCredentials: true }, data: { - device: { - ip: 'peer', - ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) Ap…ML, like Gecko) Chrome/73.0.3683.86 Safari/537.36', - js: 1, - language: 'en' - }, - id: '10c47a5fc3c41', - imp: [{ - banner: { w: 300, h: 250 }, - id: '2966b257c81d27', - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - banner: { w: 728, h: 90 }, - id: '2966b257c81d28', - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - id: '15d9012765e36c', - native: {request: '{"native":{"assets":[{"id":"1","required":true,"title":{"len":140}},{"id":"2","required":true,"data":{"type":2}},{"id":"3","img":{"w":728,"h":90,"type":3}}]}}'}, - secure: false, - tagid: '/19968336/header-bid-tag-0' - }, { - id: '15d9012765e36f', - video: {w: 500, h: 400, minduration: 15, maxduration: 60}, - secure: false, - tagid: '/19968336/header-bid-tag-0' - }], - site: { - domain: '', - id: 'blank', - page: 'https://v5demo.datablocks.net/test' - } + 'id': 'c09c6e47-8bdb-4884-a46d-93165322b368', + 'imp': [{ + 'id': '1', + 'tagid': '/19968336/header-bid-tag-0', + 'placement_id': 0, + 'secure': true, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{ + 'w': 300, + 'h': 250 + }, { + 'w': 300, + 'h': 600 + }] + } + }, { + 'id': '2', + 'tagid': '/19968336/header-bid-tag-1', + 'placement_id': 12345, + 'secure': true, + 'banner': { + 'w': 729, + 'h': 90, + 'format': [{ + 'w': 729, + 'h': 90 + }, { + 'w': 970, + 'h': 250 + }] + } + }, { + 'id': '3', + 'tagid': '/19968336/prebid_multiformat_test', + 'placement_id': 0, + 'secure': true, + 'native': { + 'ver': '1.2', + 'request': { + 'assets': [{ + 'required': 1, + 'id': 1, + 'title': {} + }, { + 'required': 1, + 'id': 3, + 'img': { + 'type': 3 + } + }, { + 'required': 1, + 'id': 5, + 'data': { + 'type': 1 + } + }], + 'context': 1, + 'plcmttype': 1, + 'ver': '1.2' + } + } + }], + 'site': { + 'domain': 'test.datablocks.net', + 'page': 'https://test.datablocks.net/index.html', + 'schain': {}, + 'ext': { + 'p_domain': 'https://test.datablocks.net', + 'rt': true, + 'frames': 0, + 'stack': ['https://test.datablocks.net/index.html'], + 'timeout': 3000 + }, + 'keywords': 'HTML, CSS, JavaScript' + }, + 'device': { + 'ip': 'peer', + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36', + 'js': 1, + 'language': 'en', + 'buyerid': '1234567', + 'ext': { + 'pb_eids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'test', + 'atype': 1 + }] + }], + 'syncs': { + '1000': 'db_4044853', + '1001': true + }, + 'coppa': 0, + 'gdpr': {}, + 'usp': {}, + 'client_info': { + 'wiw': 2560, + 'wih': 1281, + 'saw': 2560, + 'sah': 1417, + 'scd': 24, + 'sw': 2560, + 'sh': 1440, + 'whl': 4, + 'wxo': 0, + 'wyo': 0, + 'wpr': 2, + 'is_bot': false, + 'is_hid': false, + 'vs': 'hidden' + }, + 'fpd': {} + } + } } } describe('DatablocksAdapter', function() { + describe('All needed functions are available', function() { + it(`isBidRequestValid is present and type function`, function () { + expect(spec.isBidRequestValid).to.exist.and.to.be.a('function') + }); + + it(`buildRequests is present and type function`, function () { + expect(spec.buildRequests).to.exist.and.to.be.a('function') + }); + + it(`getUserSyncs is present and type function`, function () { + expect(spec.getUserSyncs).to.exist.and.to.be.a('function') + }); + + it(`onBidWon is present and type function`, function () { + expect(spec.onBidWon).to.exist.and.to.be.a('function') + }); + + it(`onSetTargeting is present and type function`, function () { + expect(spec.onSetTargeting).to.exist.and.to.be.a('function') + }); + + it(`interpretResponse is present and type function`, function () { + expect(spec.interpretResponse).to.exist.and.to.be.a('function') + }); + + it(`store_dbid is present and type function`, function () { + expect(spec.store_dbid).to.exist.and.to.be.a('function') + }); + + it(`get_dbid is present and type function`, function () { + expect(spec.get_dbid).to.exist.and.to.be.a('function') + }); + + it(`store_syncs is present and type function`, function () { + expect(spec.store_syncs).to.exist.and.to.be.a('function') + }); + + it(`get_syncs is present and type function`, function () { + expect(spec.get_syncs).to.exist.and.to.be.a('function') + }); + + it(`queue_metric is present and type function`, function () { + expect(spec.queue_metric).to.exist.and.to.be.a('function') + }); + + it(`send_metrics is present and type function`, function () { + expect(spec.send_metrics).to.exist.and.to.be.a('function') + }); + + it(`get_client_info is present and type function`, function () { + expect(spec.get_client_info).to.exist.and.to.be.a('function') + }); + + it(`get_viewability is present and type function`, function () { + expect(spec.get_viewability).to.exist.and.to.be.a('function') + }); + }); + + describe('get / store dbid', function() { + it('Should return true / undefined', function() { + expect(spec.store_dbid('12345')).to.be.true; + expect(spec.get_dbid()).to.be.a('string'); + }); + }) + + describe('get / store syncs', function() { + it('Should return true / array', function() { + expect(spec.store_syncs([{id: 1, uid: 'test'}])).to.be.true; + expect(spec.get_syncs()).to.be.a('object'); + }); + }) + + describe('queue / send metrics', function() { + it('Should return true', function() { + expect(spec.queue_metric({type: 'test'})).to.be.true; + expect(spec.queue_metric('string')).to.be.false; + expect(spec.send_metrics()).to.be.true; + }); + }) + + describe('get_viewability', function() { + it('Should return undefined', function() { + expect(spec.get_viewability()).to.equal(undefined); + }); + }) + + describe('get client info', function() { + it('Should return object', function() { + let client_info = spec.get_client_info() + expect(client_info).to.be.a('object'); + expect(client_info).to.have.all.keys('wiw', 'wih', 'saw', 'sah', 'scd', 'sw', 'sh', 'whl', 'wxo', 'wyo', 'wpr', 'is_bot', 'is_hid', 'vs'); + }); + + it('bot test should return boolean', function() { + let bot_test = new BotClientTests(); + expect(bot_test.doTests()).to.be.a('boolean'); + }); + }) + describe('isBidRequestValid', function() { - it('Should return true when sourceId and Host are set', function() { + it('Should return true when source_id and Host are set', function() { expect(spec.isBidRequestValid(bid)).to.be.true; }); - it('Should return false when host/sourceId is not set', function() { + it('Should return false when host/source_id is not set', function() { let moddedBid = Object.assign({}, bid); - delete moddedBid.params.sourceId; - delete moddedBid.params.host; - expect(spec.isBidRequestValid(bid)).to.be.false; + delete moddedBid.params.source_id; + expect(spec.isBidRequestValid(moddedBid)).to.be.false; + }); + + it('Should return true when viewability reporting is opted out', function() { + let moddedBid = Object.assign({}, bid); + moddedBid.params.vis_optout = true; + spec.isBidRequestValid(moddedBid); + expect(spec.db_obj.vis_optout).to.be.true; + }); + }) + + describe('getUserSyncs', function() { + it('Should return array of syncs', function() { + expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [res_object], {gdprApplies: true, gdpr: 1, gdpr_consent: 'consent_string'}, {})).to.be.an('array'); + }); + }); + + describe('onSetTargeting', function() { + it('Should return undefined', function() { + expect(spec.onSetTargeting()).to.equal(undefined); + }); + }); + + describe('onBidWon', function() { + it('Should return undefined', function() { + let won_bid = {params: [{source_id: 1}], requestId: 1, adUnitCode: 'unit', auctionId: 1, size: '300x250', cpm: 10, adserverTargeting: {hb_pb: 10}, timeToRespond: 10, ttl: 10}; + expect(spec.onBidWon(won_bid)).to.equal(undefined); }); }); describe('buildRequests', function() { - let requests = spec.buildRequests([bid, bid2, nativeBid, videoBid], bidderRequest); + let request = spec.buildRequests([bid, bid2, nativeBid], bidderRequest); + + expect(request).to.exist; + it('Returns POST method', function() { + expect(request.method).to.exist; + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function() { + expect(request.url).to.exist; + expect(request.url).to.equal('https://7560.v5demo.datablocks.net/openrtb/?sid=7560'); + }); + it('Creates an array of request objects', function() { - expect(requests).to.be.an('array').that.is.not.empty; + expect(request.data.imp).to.be.an('array').that.is.not.empty; }); - requests.forEach(request => { - expect(request).to.exist; - it('Returns POST method', function() { - expect(request.method).to.exist; - expect(request.method).to.equal('POST'); - }); - it('Returns valid URL', function() { - expect(request.url).to.exist; - expect(request.url).to.equal('https://v5demo.datablocks.net/search/?sid=7560'); - }); + it('Should be a valid openRTB request', function() { + let data = request.data; - it('Should be a valid openRTB request', function() { - let data = request.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); - expect(data.id).to.be.a('string'); - - let imps = data['imp']; - imps.forEach((imp, index) => { - let curBid = bidderRequest.bids[index]; - if (imp.banner) { - expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid'); - expect(imp.banner).to.be.a('object'); - } else if (imp.native) { - expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid'); - expect(imp.native).to.have.all.keys('request'); - expect(imp.native.request).to.be.a('string'); - let native = JSON.parse(imp.native.request); - expect(native).to.be.a('object'); - } else if (imp.video) { - expect(imp).to.have.all.keys('video', 'id', 'secure', 'tagid'); - expect(imp.video).to.have.all.keys('w', 'h', 'minduration', 'maxduration') - } else { - expect(true).to.equal(false); - } - - expect(imp.id).to.be.a('string'); - expect(imp.id).to.equal(curBid.bidId); - expect(imp.tagid).to.be.a('string'); - expect(imp.tagid).to.equal(curBid.adUnitCode); - expect(imp.secure).to.equal(false); - }) - - expect(data.device.ip).to.equal('peer'); - }); - }) + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('device', 'imp', 'site', 'id'); + expect(data.id).to.be.a('string'); + expect(data.imp).to.be.a('array'); + expect(data.device.ip).to.equal('peer'); + + let imps = data['imp']; + imps.forEach((imp, index) => { + let curBid = bidderRequest.bids[index]; + if (imp.banner) { + expect(imp.banner).to.be.a('object'); + expect(imp).to.have.all.keys('banner', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); + } else if (imp.native) { + expect(imp).to.have.all.keys('native', 'id', 'secure', 'tagid', 'placement_id', 'ortb2', 'floor'); + expect(imp.native).to.have.all.keys('request', 'ver'); + expect(imp.native.request).to.be.a('object'); + } else { + expect(true).to.equal(false); + } + + expect(imp.id).to.be.a('string'); + expect(imp.id).to.equal(curBid.bidId); + expect(imp.tagid).to.be.a('string'); + expect(imp.tagid).to.equal(curBid.adUnitCode); + expect(imp.secure).to.equal(false); + }) + }); it('Returns empty data if no valid requests are passed', function() { - let request = spec.buildRequests([]); - expect(request).to.be.an('array').that.is.empty; + let test_request = spec.buildRequests([]); + expect(test_request).to.be.an('array').that.is.empty; }); }); + describe('interpretResponse', function() { - let serverResponses = spec.interpretResponse(resObject, bidRequest); + let response = spec.interpretResponse(res_object, bid_request); + it('Returns an array of valid server responses if response object is valid', function() { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - expect(Object.keys(dataItem)).to.include('cpm', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'mediaType', 'requestId'); - expect(dataItem.requestId).to.be.a('string'); - expect(dataItem.cpm).to.be.a('number'); - expect(dataItem.ttl).to.be.a('number'); - expect(dataItem.creativeId).to.be.a('string'); - expect(dataItem.netRevenue).to.be.a('boolean'); - expect(dataItem.currency).to.be.a('string'); - expect(dataItem.mediaType).to.be.a('string'); - - if (dataItem.mediaType == 'banner') { - expect(dataItem.ad).to.be.a('string'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); - } else if (dataItem.mediaType == 'native') { - expect(dataItem.native.title).to.be.a('string'); - expect(dataItem.native.body).to.be.a('string'); - expect(dataItem.native.clickUrl).to.be.a('string'); - } else if (dataItem.mediaType == 'video') { - expect(dataItem.vastUrl).to.be.a('string'); - expect(dataItem.width).to.be.a('number'); - expect(dataItem.height).to.be.a('number'); + expect(response).to.be.an('array').that.is.not.empty; + + response.forEach(bid => { + expect(parseInt(bid.requestId)).to.be.a('number').greaterThan(0); + expect(bid.cpm).to.be.a('number'); + expect(bid.creativeId).to.be.a('string'); + expect(bid.currency).to.be.a('string'); + expect(bid.netRevenue).to.be.a('boolean'); + expect(bid.ttl).to.be.a('number'); + expect(bid.mediaType).to.be.a('string'); + + if (bid.mediaType == 'banner') { + expect(bid.width).to.be.a('number'); + expect(bid.height).to.be.a('number'); + expect(bid.ad).to.be.a('string'); + } else if (bid.mediaType == 'native') { + expect(bid.native).to.be.a('object'); } - } + }) + it('Returns an empty array if invalid response is passed', function() { serverResponses = spec.interpretResponse('invalid_response'); expect(serverResponses).to.be.an('array').that.is.empty; From dc926c53ad010b8525f14bb24988a5424c8674c6 Mon Sep 17 00:00:00 2001 From: GammaSSP <35954362+gammassp@users.noreply.github.com> Date: Wed, 16 Jun 2021 20:03:22 +0700 Subject: [PATCH 1164/1476] Gamma Bid Adapter: support adomain for Prebid 5.0 (#7033) * Support adomain for prebid 5 * Support adomain for prebid 5 * Expected indentation of 8 spaces but found 2 tabs indent --- modules/gammaBidAdapter.js | 104 +++++++++++++++++++++ test/spec/modules/gammaBidAdapter_spec.js | 106 ++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 modules/gammaBidAdapter.js create mode 100644 test/spec/modules/gammaBidAdapter_spec.js diff --git a/modules/gammaBidAdapter.js b/modules/gammaBidAdapter.js new file mode 100644 index 00000000000..3e1298b7e23 --- /dev/null +++ b/modules/gammaBidAdapter.js @@ -0,0 +1,104 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const ENDPOINT = 'https://hb.gammaplatform.com'; +const ENDPOINT_USERSYNC = 'https://cm-supply-web.gammaplatform.com'; +const BIDDER_CODE = 'gamma'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['gamma'], + supportedMediaTypes: ['banner', 'video'], + + /** + * 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.siteId || bid.params.zoneId); + }, + + /** + * 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(bidRequests, bidderRequest) { + const serverRequests = []; + const bidderRequestReferer = (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) || ''; + for (var i = 0, len = bidRequests.length; i < len; i++) { + const gaxObjParams = bidRequests[i]; + serverRequests.push({ + method: 'GET', + url: ENDPOINT + '/adx/request?wid=' + gaxObjParams.params.siteId + '&zid=' + gaxObjParams.params.zoneId + '&hb=pbjs&bidid=' + gaxObjParams.bidId + '&urf=' + encodeURIComponent(bidderRequestReferer) + }); + } + return serverRequests; + }, + + /** + * 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) { + serverResponse = serverResponse.body; + + const bids = []; + + if (serverResponse.id) { + const bid = newBid(serverResponse); + bids.push(bid); + } + + return bids; + }, + + getUserSyncs: function(syncOptions) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: ENDPOINT_USERSYNC + '/adx/usersync' + }]; + } + } +} + +/** + * Unpack the Server's Bid into a Prebid-compatible one. + * @param serverBid + * @return Bid + */ +function newBid(serverBid) { + const bid = { + ad: serverBid.seatbid[0].bid[0].adm, + cpm: serverBid.seatbid[0].bid[0].price, + creativeId: serverBid.seatbid[0].bid[0].adid, + currency: serverBid.cur, + dealId: serverBid.seatbid[0].bid[0].dealid, + width: serverBid.seatbid[0].bid[0].w, + height: serverBid.seatbid[0].bid[0].h, + mediaType: serverBid.type, + netRevenue: true, + requestId: serverBid.id, + ttl: serverBid.seatbid[0].bid[0].ttl || 300, + meta: { + advertiserDomains: serverBid.seatbid[0].bid[0].adomain && serverBid.seatbid[0].bid[0].adomain.length ? serverBid.seatbid[0].bid[0].adomain : [] + } + }; + + if (serverBid.type == 'video') { + Object.assign(bid, { + vastXml: serverBid.seatbid[0].bid[0].vastXml, + vastUrl: serverBid.seatbid[0].bid[0].vastUrl, + ttl: 3600 + }); + } + + return bid; +} + +registerBidder(spec); diff --git a/test/spec/modules/gammaBidAdapter_spec.js b/test/spec/modules/gammaBidAdapter_spec.js new file mode 100644 index 00000000000..35394df7d11 --- /dev/null +++ b/test/spec/modules/gammaBidAdapter_spec.js @@ -0,0 +1,106 @@ +import { expect } from 'chai'; +import { spec } from 'modules/gammaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('gammaBidAdapter', function() { + const adapter = newBidder(spec); + + let bid = { + 'bidder': 'gamma', + 'params': { + siteId: '1465446377', + zoneId: '1515999290' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + }; + let bidArray = [bid]; + + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when require params are not passed', () => { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when params not passed correctly', () => { + bid.params.siteId = ''; + bid.params.zoneId = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + it('should attempt to send bid requests to the endpoint via GET', () => { + const requests = spec.buildRequests(bidArray); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('GET'); + expect(requestItem.url).to.match(new RegExp(`hb.gammaplatform.com`)); + }); + }); + }); + + describe('interpretResponse', () => { + let serverResponse; + + beforeEach(() => { + serverResponse = { + body: { + 'id': '23beaa6af6cdde', + 'bid': '5611802021800040585', + 'type': 'banner', + 'cur': 'USD', + 'seatbid': [{ + 'seat': '5611802021800040585', + 'bid': [{ + 'id': '1515999070', + 'impid': '1', + 'price': 0.45, + 'adm': '', + 'adid': '1515999070', + 'dealid': 'gax-paj2qarjf2g', + 'h': 250, + 'w': 300, + 'adomain': ['testdomain.com'] + }] + }] + } + }; + }) + + it('should get the correct bid response', () => { + let expectedResponse = [{ + 'requestId': '23beaa6af6cdde', + 'cpm': 0.45, + 'width': 300, + 'height': 250, + 'creativeId': '1515999070', + 'dealId': 'gax-paj2qarjf2g', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'ad': '', + 'meta': {'advertiserDomains': ['testdomain.com']} + }]; + let result = spec.interpretResponse(serverResponse); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + + it('handles empty bid response', () => { + let response = { + body: {} + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); From 551d9430d378decf7149408abdfd5b3785e81985 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Wed, 16 Jun 2021 15:51:48 +0200 Subject: [PATCH 1165/1476] Update sspBC bid adapter (v5.0) (#7002) 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] Update to adapter version 5.0 - support for first party data and user agent client hints * [sspbc-adapter] remove WIP code for native ads * [sspbc-adapter] remove WIP code for native ads * [sspbc-adapter] remove old/duplicated test * [sspbc-adapter] add tests for imp.ext (pbsize, pbslot) * [sspbc-adapter] fix adSizesCalled increment Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 40 +++++++++++----- test/spec/modules/sspBCBidAdapter_spec.js | 58 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index d166a01a1da..15e0baa811f 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -9,10 +9,11 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.9'; +const BIDDER_VERSION = '5.0'; const W = window; const { navigator } = W; const oneCodeDetection = {}; +const adSizesCalled = {}; var consentApiVersion; /** @@ -69,7 +70,7 @@ const cookieSupport = () => { }; const applyClientHints = ortbRequest => { - const connection = navigator.connection || false; + const { connection = {}, deviceMemory, userAgentData = {} } = navigator; const viewport = W.visualViewport || false; const segments = []; const hints = { @@ -77,9 +78,11 @@ const applyClientHints = ortbRequest => { 'CH-Rtt': connection.rtt, 'CH-SaveData': connection.saveData, 'CH-Downlink': connection.downlink, - 'CH-DeviceMemory': navigator.deviceMemory, + 'CH-DeviceMemory': deviceMemory, 'CH-Dpr': W.devicePixelRatio, 'CH-ViewportWidth': viewport.width, + 'CH-BrowserBrands': JSON.stringify(userAgentData.brands), + 'CH-isMobile': userAgentData.mobile, }; Object.keys(hints).forEach(key => { @@ -153,22 +156,37 @@ const mapBanner = slot => { } const mapImpression = slot => { - const { adUnitCode, bidId, params } = slot; - const { id, siteId } = params || {}; + const { adUnitCode, bidId, params = {}, ortb2Imp = {} } = slot; + const { id, siteId } = params; + const { ext = {} } = ortb2Imp; + + /* + check max size for this imp, and check/store number this size was called (for current view) + send this info as ext.pbsize + */ + const slotSize = slot.sizes.length ? slot.sizes.reduce((prev, next) => prev[0] * prev[1] <= next[0] * next[1] ? next : prev).join('x') : '1x1'; + adSizesCalled[slotSize] = adSizesCalled[slotSize] ? adSizesCalled[slotSize] + 1 : 1; + ext.data = Object.assign({ pbsize: `${slotSize}_${adSizesCalled[slotSize]}` }, ext.data); + const imp = { id: id && siteId ? id : 'bidid-' + bidId, banner: mapBanner(slot), - /* native: mapNative(slot), */ + // native: mapNative(slot), tagid: adUnitCode, + ext, }; // Check floorprices for this imp if (typeof slot.getFloor === 'function') { - // sspBC adapter accepts only floor per imp - check for maximum value for requested ad sizes - imp.bidfloor = slot.sizes.reduce((prev, next) => { - const currentFloor = slot.getFloor({ mediaType: 'banner', size: next }).floor; - return prev > currentFloor ? prev : currentFloor; - }, 0); + let bannerFloor = 0; + // sspBC adapter accepts only floor per imp - check for maximum value for requested ad types and sizes + if (slot.sizes.length) { + bannerFloor = slot.sizes.reduce((prev, next) => { + const currentFloor = slot.getFloor({ mediaType: 'banner', size: next }).floor; + return prev > currentFloor ? prev : currentFloor; + }, 0); + } + imp.bidfloor = bannerFloor; } return imp; } diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index efbfa37f6ca..ae27cf8deea 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -40,6 +40,13 @@ describe('SSPBC adapter', function () { bidderRequestId, bidId: auctionId + '1', transactionId, + ortb2Imp: { + ext: { + data: { + pbadslot: 'test_wideboard' + } + } + } }, { adUnitCode: 'test_rectangle', @@ -62,6 +69,28 @@ describe('SSPBC adapter', function () { bidderRequestId, bidId: auctionId + '2', transactionId, + }, + { + adUnitCode: 'test_rectangle_2', + bidder: BIDDER_CODE, + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + sizes: [ + [300, 250] + ], + params: { + id: '011', + siteId: '8816', + }, + auctionId, + bidderRequestId, + bidId: auctionId + '3', + transactionId, } ]; const bid_OneCode = { @@ -235,6 +264,21 @@ describe('SSPBC adapter', function () { }], 'seat': 'dsp2', 'group': 0 + }, { + 'bid': [{ + 'id': '2d766853-ea07-4529-8299-5f0ebaddfaza', + 'impid': '011', + 'siteid': '8816', + 'slotid': '011', + 'price': 2, + 'adm': 'AD CODE 3', + 'cid': '57745', + 'crid': '858253', + 'w': 300, + 'h': 250 + }], + 'seat': 'dsp3', + 'group': 0 }], 'cur': 'PLN' } @@ -338,6 +382,9 @@ describe('SSPBC adapter', function () { const requestSingle = spec.buildRequests([bids[0]], bidRequestSingle); const payload = request ? JSON.parse(request.data) : { site: false, imp: false }; const payloadSingle = request ? JSON.parse(requestSingle.data) : { site: false, imp: false }; + const payloadExt0 = payload.imp ? payload.imp[0].ext : {}; + const payloadExt1 = payload.imp ? payload.imp[1].ext : {}; + const payloadExt2 = payload.imp ? payload.imp[2].ext : {}; it('should send bid request to endpoint via POST', function () { expect(request.url).to.contain(BIDDER_URL); @@ -354,6 +401,17 @@ describe('SSPBC adapter', function () { expect(payloadSingle.imp.length).to.equal(1); }); + it('should add first party data (pbslot)', function () { + expect(payloadExt0.data).to.be.an('object').and.to.have.property('pbadslot'); + }); + + it('should add maximum size of adunit, and how many times this size has been requested (pbsize)', function () { + expect(payloadExt0.data).to.be.an('object').and.to.have.property('pbsize'); + expect(payloadExt0.data.pbsize).to.equal('750x200_1'); + expect(payloadExt1.data.pbsize).to.equal('300x250_1'); + expect(payloadExt2.data.pbsize).to.equal('300x250_2'); + }); + it('should save bidder request data', function () { expect(request.bidderRequest).to.deep.equal(bidRequest); }); From baf906b8709739398cc12eba127ee12625247948 Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Wed, 16 Jun 2021 16:02:27 +0200 Subject: [PATCH 1166/1476] Ats analytics set sampling rate to 1 percent (#7010) * ATS-analytics-adapter - Change sampling rate to 100 * ATS-analytics-adapter - add unit tests --- modules/atsAnalyticsAdapter.js | 16 +++++++---- test/spec/modules/atsAnalyticsAdapter_spec.js | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index 31e46dead89..0cff7bbd68f 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -275,6 +275,7 @@ function sendDataToAnalytic () { // preflight request, to check did publisher have permission to send data to analytics endpoint function preflightRequest (envelopeSourceCookieValue) { + utils.logInfo('ATS Analytics - preflight request!'); ajax(preflightUrl + atsAnalyticsAdapter.context.pid, function (data) { let samplingRateObject = JSON.parse(data); utils.logInfo('ATS Analytics - Sampling Rate: ', samplingRateObject); @@ -322,7 +323,6 @@ let atsAnalyticsAdapter = Object.assign(adapter( if (eventType === CONSTANTS.EVENTS.AUCTION_END) { let envelopeSourceCookieValue = storage.getCookie('_lr_env_src_ats'); try { - utils.logInfo('ATS Analytics - preflight request!'); let samplingRateCookie = storage.getCookie('_lr_sampling_rate'); if (!samplingRateCookie) { preflightRequest(envelopeSourceCookieValue); @@ -341,11 +341,16 @@ let atsAnalyticsAdapter = Object.assign(adapter( // save the base class function atsAnalyticsAdapter.originEnableAnalytics = atsAnalyticsAdapter.enableAnalytics; -// add check to not fire request every time, but instead to send 1/10 events +// add check to not fire request every time, but instead to send 1/100 atsAnalyticsAdapter.shouldFireRequest = function (samplingRate) { - let shouldFireRequestValue = (Math.floor((Math.random() * samplingRate + 1)) === samplingRate); - utils.logInfo('ATS Analytics - Should Fire Request: ', shouldFireRequestValue); - return shouldFireRequestValue; + if (samplingRate !== 0) { + let shouldFireRequestValue = (Math.floor((Math.random() * 100 + 1)) === 100); + utils.logInfo('ATS Analytics - Should Fire Request: ', shouldFireRequestValue); + return shouldFireRequestValue; + } else { + utils.logInfo('ATS Analytics - Should Fire Request: ', false); + return false; + } }; atsAnalyticsAdapter.getUserAgent = function () { @@ -362,6 +367,7 @@ atsAnalyticsAdapter.enableAnalytics = function (config) { pid: config.options.pid }; let initOptions = config.options; + utils.logInfo('ATS Analytics - adapter enabled! '); atsAnalyticsAdapter.originEnableAnalytics(initOptions); // call the base class function }; diff --git a/test/spec/modules/atsAnalyticsAdapter_spec.js b/test/spec/modules/atsAnalyticsAdapter_spec.js index 59b9105925a..e2dd4747199 100644 --- a/test/spec/modules/atsAnalyticsAdapter_spec.js +++ b/test/spec/modules/atsAnalyticsAdapter_spec.js @@ -21,10 +21,12 @@ describe('ats analytics adapter', function () { events.getEvents.restore(); atsAnalyticsAdapter.getUserAgent.restore(); atsAnalyticsAdapter.disableAnalytics(); + Math.random.restore(); }); describe('track', function () { it('builds and sends request and response data', function () { + sinon.stub(Math, 'random').returns(0.99); sinon.stub(atsAnalyticsAdapter, 'shouldFireRequest').returns(true); sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); let now = new Date(); @@ -156,26 +158,52 @@ describe('ats analytics adapter', function () { // check that the publisher ID is configured via options expect(atsAnalyticsAdapter.context.pid).to.equal(initOptions.pid); + + atsAnalyticsAdapter.shouldFireRequest.restore(); }) it('check browser is safari', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); + sinon.stub(Math, 'random').returns(0.99); let browser = parseBrowser(); expect(browser).to.equal('Safari'); }) it('check browser is chrome', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/80.0.3987.95 Mobile/15E148 Safari/604.1'); + sinon.stub(Math, 'random').returns(0.99); let browser = parseBrowser(); expect(browser).to.equal('Chrome'); }) it('check browser is edge', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43'); + sinon.stub(Math, 'random').returns(0.99); let browser = parseBrowser(); expect(browser).to.equal('Microsoft Edge'); }) it('check browser is firefox', function () { sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (iPhone; CPU OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/23.0 Mobile/15E148 Safari/605.1.15'); + sinon.stub(Math, 'random').returns(0.99); let browser = parseBrowser(); expect(browser).to.equal('Firefox'); }) + it('should not fire analytics request if sampling rate is 0', function () { + sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); + sinon.stub(Math, 'random').returns(0.99); + let result = atsAnalyticsAdapter.shouldFireRequest(0); + expect(result).to.equal(false); + }) + it('should fire analytics request', function () { + sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); + sinon.stub(Math, 'random').returns(0.99); + // publisher can try to pass anything they want but we will set sampling rate to 100, which means we will have 1% of requests + let result = atsAnalyticsAdapter.shouldFireRequest(10); + expect(result).to.equal(true); + }) + it('should not fire analytics request if math random is something other then 0.99', function () { + sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25'); + sinon.stub(Math, 'random').returns(0.98); + // publisher can try to pass anything they want but we will set sampling rate to 100, which means we will have 1% of requests + let result = atsAnalyticsAdapter.shouldFireRequest(10); + expect(result).to.equal(false); + }) }) }) From 1bab7ca9a6a67ebad58787970eb25523eeb718c1 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 16 Jun 2021 12:25:03 -0400 Subject: [PATCH 1167/1476] changing PBS debug flag to boolean (#7035) --- modules/prebidServerBidAdapter/index.js | 2 +- test/spec/modules/prebidServerBidAdapter_spec.js | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index bbf301fb072..ffd251cfac1 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -751,7 +751,7 @@ const OPEN_RTB_PROTOCOL = { // set debug flag if in debug mode if (getConfig('debug')) { - request.ext.prebid = Object.assign(request.ext.prebid, {debug: 1}) + request.ext.prebid = Object.assign(request.ext.prebid, {debug: true}) } // s2sConfig video.ext.prebid is passed through openrtb to PBS diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 1b9ae7ad184..a02e3b0586c 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2730,5 +2730,16 @@ describe('S2S Adapter', function () { expect(requestBid.coopSync).to.be.undefined; }); + + it('adds debug flag', function () { + config.setConfig({debug: true}); + + let bidRequest = utils.deepClone(BID_REQUESTS); + + adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.ext.prebid.debug).is.equal(true); + }); }); }); From c3b96a94af379202dc352783250c891357b139ab Mon Sep 17 00:00:00 2001 From: punkiller Date: Wed, 16 Jun 2021 13:35:45 -0700 Subject: [PATCH 1168/1476] IX Bid Adapter : reading video `placement` property (#6994) * Reads in video placement * some feedback from other MR --- modules/ixBidAdapter.js | 13 ++++++++----- test/spec/modules/ixBidAdapter_spec.js | 25 ++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 7512619019a..dfa9d0ad768 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -3,6 +3,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -103,6 +104,7 @@ function bidToVideoImp(bid) { imp.video = videoParamRef ? utils.deepClone(bid.params.video) : {}; + // copy all video properties to imp object for (const adUnitProperty in videoAdUnitRef) { if (VIDEO_PARAMS_ALLOW_LIST.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty]; @@ -111,19 +113,20 @@ function bidToVideoImp(bid) { if (imp.video.minduration > imp.video.maxduration) { utils.logError('IX Bid Adapter: video minduration [' + imp.video.minduration + - '] cannot be greater than video maxduration [' + imp.video.maxduration + ']'); + '] cannot be greater than video maxduration [' + imp.video.maxduration + ']'); return {}; } const context = (videoParamRef && videoParamRef.context) || (videoAdUnitRef && videoAdUnitRef.context); - if (context) { - if (context === 'instream') { + // if placement not already defined, pick one based on `context` + if (context && !imp.video.hasOwnProperty('placement')) { + if (context === INSTREAM) { imp.video.placement = 1; - } else if (context === 'outstream') { + } else if (context === OUTSTREAM) { imp.video.placement = 4; } else { - utils.logWarn(`IX Bid Adapter: video context '${context}' is not supported`); + utils.logWarn(`IX Bid Adapter: Video context '${context}' is not supported`); } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index cf716fd594b..def1924d465 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1873,15 +1873,34 @@ describe('IndexexchangeAdapter', function () { expect(impression.ext.sid).to.equal(sidValue); // TODO undefined - 400x600 }); - it('should set correct placement if context is outstream', function () { + it('should not use default placement values when placement is defined at adUnit level', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes.video.context = 'outstream'; + bid.mediaTypes.video.placement = 2; + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video.placement).to.equal(2); + }); + + it('should set correct default placement, if context is instream', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes.video.context = 'instream'; + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video.placement).to.equal(1); + }); + + it('should set correct default placement, if context is outstream', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'outstream'; const request = spec.buildRequests([bid])[0]; const impression = JSON.parse(request.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); - expect(impression.video).to.exist; - expect(impression.video.placement).to.exist; expect(impression.video.placement).to.equal(4); }); From 75ba0160d7683d63c723b6484a01349dc85064b6 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 16 Jun 2021 16:42:37 -0400 Subject: [PATCH 1169/1476] Prebid.js 5.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20357e5bf10..375b5d25895 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.1.0-pre", + "version": "5.1.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b8cf54b44f664fea4136b1e1cdaeb9a9ff1580c1 Mon Sep 17 00:00:00 2001 From: Matt Kendall <1870166+mkendall07@users.noreply.github.com> Date: Wed, 16 Jun 2021 17:07:43 -0400 Subject: [PATCH 1170/1476] 5.2.0-pre --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 375b5d25895..d4dc3fcbfd2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.1.0", + "version": "5.2.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 09174318a00300796f4554e536011165a8416de4 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 16 Jun 2021 22:42:06 -0700 Subject: [PATCH 1171/1476] Dependencies/Testing: update dependencies, npm audit fixes, and bump Browserstack versions (#6828) * Dependencies: update core-js to v3.13 * update core-js-pure to latest v3.13 * fix npm dependency vulnerabilities * update wdio/cli to latest v7.5.2 * update wdio/concise-reporter to latest v7.5.2 * update dio/local-runner to latest v7.5.2 * update wdio/mocha-framework to latest v7.5.2 * update wdio/spec-reporter to latest v7.5.2 * update wdio/sync to latest v7.5.2 * update documentation to latest v13.2.5 * update eslint-plugin-node to latest v11.1.0 * update eslint-plugin-promise to latest v5.1.0 * update execa to latest v5.0.0 * update faker to latest v5.5.3 * fix faker depreciation warnings * update gulp-eslint to v5.0.0 * revert gulp-eslint to v4.0.0 * update gulp-header to latest v2.0.9 * update gulp-if to latest v3.0.0 * update gulp-shell to latest v.0.8.0 * update gulp-sourcemaps to latest v3.0.0 * update is-docker to latest v2.2.1 * update karma to latest v6.3,2 * add new browserstack * change os_version to big sur * update karma-babel-preprocessor to latest v8.0.1 * update karma-chrome-launcher to latest v3.1.0 * update karma-coverage-istanbul-reporter to latest * update karma-firefox-launcher to latest v2.1.0 * update karma-mocha to latest v2.0.1 * update karma-spec-reporter to latest * update opn to open and latest v8.2.0 * update opn to latest v6.0.0 * update through2 to latest v4.0.2 * update webdriverio to latest * update yargs to latest v17.0.1 * npm audit fix on vulnerabilitiees * update gulp-eslint to latest v6.0.0 * remove package-lock to fix errors with eslint * recreate clean package-lock * revert gulp-eslint to v4.0.0 * add base eslint package * forgot comma * add terser * update gulp file with terser * fix conflict * revert package-lock * npm audit fix * remove package-lock file * add back in package-lock file * fix typo in variable name * fix merge conflicts * change opn to open since package renamed * change opn to open on import --- browsers.json | 18 +- gulpfile.js | 6 +- package-lock.json | 52384 ++++++++++++++++------ package.json | 61 +- test/spec/integration/faker/fixtures.js | 4 +- 5 files changed, 38971 insertions(+), 13502 deletions(-) diff --git a/browsers.json b/browsers.json index f73bbbb8aac..4f2ea456f68 100644 --- a/browsers.json +++ b/browsers.json @@ -7,11 +7,11 @@ "device": null, "os": "Windows" }, - "bs_edge_18_windows_10": { + "bs_edge_90_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "edge", - "browser_version": "18.0", + "browser_version": "90.0", "device": null, "os": "Windows" }, @@ -23,11 +23,11 @@ "device": null, "os": "Windows" }, - "bs_chrome_89_windows_10": { + "bs_chrome_90_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "89.0", + "browser_version": "90.0", "device": null, "os": "Windows" }, @@ -39,11 +39,11 @@ "device": null, "os": "Windows" }, - "bs_firefox_73_windows_10": { + "bs_firefox_88_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "73.0", + "browser_version": "88.0", "device": null, "os": "Windows" }, @@ -55,11 +55,11 @@ "device": null, "os": "Windows" }, - "bs_safari_11_mac_catalina": { + "bs_safari_14_mac_bigsur": { "base": "BrowserStack", - "os_version": "Catalina", + "os_version": "Big Sur", "browser": "safari", - "browser_version": "13.0", + "browser_version": "14.0", "device": null, "os": "OS X" }, diff --git a/gulpfile.js b/gulpfile.js index e2bed2a660f..dfb5a51fdbf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,11 +8,11 @@ var gutil = require('gulp-util'); var connect = require('gulp-connect'); var webpack = require('webpack'); var webpackStream = require('webpack-stream'); -var uglify = require('gulp-uglify'); +var terser = require('gulp-terser'); var gulpClean = require('gulp-clean'); var KarmaServer = require('karma').Server; var karmaConfMaker = require('./karma.conf.maker'); -var opens = require('opn'); +var opens = require('open'); var webpackConfig = require('./webpack.conf'); var helpers = require('./gulpHelpers'); var concat = require('gulp-concat'); @@ -168,7 +168,7 @@ function makeWebpackPkg() { return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js')) .pipe(helpers.nameModules(externalModules)) .pipe(webpackStream(cloned, webpack)) - .pipe(uglify()) + .pipe(terser()) .pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules))) .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) .pipe(gulp.dest('build/dist')); diff --git a/package-lock.json b/package-lock.json index c0b01db952a..7908764b8e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,43 +1,142 @@ { "name": "prebid.js", - "version": "4.43.0-pre", - "lockfileVersion": 1, + "version": "5.1.0-pre", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "packages": { + "": { + "name": "prebid.js", + "version": "5.1.0-pre", + "license": "Apache-2.0", + "dependencies": { + "babel-plugin-transform-object-assign": "^6.22.0", + "core-js": "^3.13.0", + "core-js-pure": "^3.13.0", + "criteo-direct-rsa-validate": "^1.1.0", + "crypto-js": "^3.3.0", + "dlv": "1.1.3", + "dset": "2.0.1", + "express": "^4.15.4", + "fun-hooks": "^0.9.9", + "just-clone": "^1.0.2", + "live-connect-js": "2.0.0" + }, + "devDependencies": { + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "@jsdevtools/coverage-istanbul-loader": "^3.0.3", + "@wdio/browserstack-service": "^6.1.4", + "@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.5.2", + "@wdio/sync": "^7.5.2", + "ajv": "5.5.2", + "babel-loader": "^8.0.5", + "body-parser": "^1.19.0", + "chai": "^4.2.0", + "coveralls": "^3.1.0", + "deep-equal": "^2.0.3", + "documentation": "^13.2.5", + "es5-shim": "^4.5.14", + "eslint": "^7.27.0", + "eslint-config-standard": "^10.2.1", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prebid": "file:./plugins/eslint", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-standard": "^3.0.1", + "execa": "^5.0.0", + "faker": "^5.5.3", + "fs.extra": "^1.3.2", + "gulp": "^4.0.0", + "gulp-clean": "^0.3.2", + "gulp-concat": "^2.6.0", + "gulp-connect": "^5.7.0", + "gulp-eslint": "^4.0.0", + "gulp-footer": "^2.0.2", + "gulp-header": "^2.0.9", + "gulp-if": "^3.0.0", + "gulp-js-escape": "^1.0.1", + "gulp-replace": "^1.0.0", + "gulp-shell": "^0.8.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-terser": "^2.0.1", + "gulp-util": "^3.0.0", + "is-docker": "^2.2.1", + "istanbul": "^0.4.5", + "karma": "^6.3.2", + "karma-babel-preprocessor": "^8.0.1", + "karma-browserstack-launcher": "1.4.0", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-coverage": "^2.0.1", + "karma-coverage-istanbul-reporter": "^3.0.3", + "karma-es5-shim": "^0.0.4", + "karma-firefox-launcher": "^2.1.0", + "karma-ie-launcher": "^1.0.0", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-opera-launcher": "^1.0.0", + "karma-safari-launcher": "^1.0.0", + "karma-script-launcher": "^1.0.0", + "karma-sinon": "^1.0.5", + "karma-sourcemap-loader": "^0.3.7", + "karma-spec-reporter": "^0.0.32", + "karma-webpack": "^3.0.5", + "lodash": "^4.17.21", + "mocha": "^5.0.0", + "morgan": "^1.10.0", + "opn": "^6.0.0", + "resolve-from": "^5.0.0", + "sinon": "^4.1.3", + "through2": "^4.0.2", + "url-parse": "^1.0.5", + "webdriverio": "^7.6.1", + "webpack": "^3.0.0", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-stream": "^3.2.0", + "yargs": "^17.0.1" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, - "requires": { + "dependencies": { "@babel/highlight": "^7.10.4" } }, - "@babel/compat-data": { + "node_modules/@babel/compat-data": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.5.tgz", "integrity": "sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw==", "dev": true, - "requires": { + "dependencies": { "browserslist": "^4.12.0", "invariant": "^2.2.4", "semver": "^5.5.0" } }, - "@babel/core": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.5.tgz", - "integrity": "sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w==", + "node_modules/@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.5", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.10.5", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.5", - "@babel/types": "^7.10.5", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -47,60 +146,65 @@ "semver": "^5.4.1", "source-map": "^0.5.0" }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } + "ms": "^2.1.1" } }, - "@babel/generator": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", - "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", + "node_modules/@babel/core/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/@babel/generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", + "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", "dev": true, - "requires": { - "@babel/types": "^7.10.5", + "dependencies": { + "@babel/types": "^7.12.1", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, - "@babel/helper-annotate-as-pure": { + "node_modules/@babel/helper-annotate-as-pure": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.10.4" } }, - "@babel/helper-builder-binary-assignment-operator-visitor": { + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-explode-assignable-expression": "^7.10.4", "@babel/types": "^7.10.4" } }, - "@babel/helper-compilation-targets": { + "node_modules/@babel/helper-compilation-targets": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", "dev": true, - "requires": { + "dependencies": { "@babel/compat-data": "^7.10.4", "browserslist": "^4.12.0", "invariant": "^2.2.4", @@ -108,12 +212,12 @@ "semver": "^5.5.0" } }, - "@babel/helper-create-class-features-plugin": { + "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.10.4", "@babel/helper-member-expression-to-functions": "^7.10.5", "@babel/helper-optimise-call-expression": "^7.10.4", @@ -122,130 +226,152 @@ "@babel/helper-split-export-declaration": "^7.10.4" } }, - "@babel/helper-create-regexp-features-plugin": { + "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-regex": "^7.10.4", "regexpu-core": "^4.7.0" } }, - "@babel/helper-define-map": { + "node_modules/@babel/helper-define-map": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.10.4", "@babel/types": "^7.10.5", "lodash": "^4.17.19" } }, - "@babel/helper-explode-assignable-expression": { + "node_modules/@babel/helper-explode-assignable-expression": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", "dev": true, - "requires": { + "dependencies": { "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" } }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "node_modules/@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "node_modules/@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz", - "integrity": "sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", + "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", "dev": true, - "requires": { - "@babel/types": "^7.10.5" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "node_modules/@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-module-transforms": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz", - "integrity": "sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-plugin-utils": { + "node_modules/@babel/helper-plugin-utils": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true }, - "@babel/helper-regex": { + "node_modules/@babel/helper-regex": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", "dev": true, - "requires": { + "dependencies": { "lodash": "^4.17.19" } }, - "@babel/helper-remap-async-to-generator": { + "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", "@babel/template": "^7.10.4", @@ -253,329 +379,355 @@ "@babel/types": "^7.10.4" } }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "node_modules/@babel/helper-simple-access": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-wrap-function": { + "node_modules/@babel/helper-wrap-function": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.10.4", "@babel/template": "^7.10.4", "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" } }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "node_modules/@babel/helpers": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", + "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", - "dev": true + "node_modules/@babel/parser": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", + "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } }, - "@babel/plugin-proposal-async-generator-functions": { + "node_modules/@babel/plugin-proposal-async-generator-functions": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-remap-async-to-generator": "^7.10.4", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, - "@babel/plugin-proposal-class-properties": { + "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-class-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-proposal-dynamic-import": { + "node_modules/@babel/plugin-proposal-dynamic-import": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, - "@babel/plugin-proposal-json-strings": { + "node_modules/@babel/plugin-proposal-json-strings": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.0" } }, - "@babel/plugin-proposal-nullish-coalescing-operator": { + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, - "@babel/plugin-proposal-numeric-separator": { + "node_modules/@babel/plugin-proposal-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, - "@babel/plugin-proposal-object-rest-spread": { + "node_modules/@babel/plugin-proposal-object-rest-spread": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-transform-parameters": "^7.10.4" } }, - "@babel/plugin-proposal-optional-catch-binding": { + "node_modules/@babel/plugin-proposal-optional-catch-binding": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, - "@babel/plugin-proposal-optional-chaining": { + "node_modules/@babel/plugin-proposal-optional-chaining": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, - "@babel/plugin-proposal-private-methods": { + "node_modules/@babel/plugin-proposal-private-methods": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-class-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-proposal-unicode-property-regex": { + "node_modules/@babel/plugin-proposal-unicode-property-regex": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-syntax-async-generators": { + "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-class-properties": { + "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-syntax-dynamic-import": { + "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-json-strings": { + "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", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-numeric-separator": { + "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-syntax-object-rest-spread": { + "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-optional-catch-binding": { + "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-optional-chaining": { + "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-top-level-await": { + "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-arrow-functions": { + "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-async-to-generator": { + "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-remap-async-to-generator": "^7.10.4" } }, - "@babel/plugin-transform-block-scoped-functions": { + "node_modules/@babel/plugin-transform-block-scoped-functions": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-block-scoping": { + "node_modules/@babel/plugin-transform-block-scoping": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-classes": { + "node_modules/@babel/plugin-transform-classes": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-define-map": "^7.10.4", "@babel/helper-function-name": "^7.10.4", @@ -586,272 +738,272 @@ "globals": "^11.1.0" } }, - "@babel/plugin-transform-computed-properties": { + "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-destructuring": { + "node_modules/@babel/plugin-transform-destructuring": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-dotall-regex": { + "node_modules/@babel/plugin-transform-dotall-regex": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-duplicate-keys": { + "node_modules/@babel/plugin-transform-duplicate-keys": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-exponentiation-operator": { + "node_modules/@babel/plugin-transform-exponentiation-operator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-for-of": { + "node_modules/@babel/plugin-transform-for-of": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-function-name": { + "node_modules/@babel/plugin-transform-function-name": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-literals": { + "node_modules/@babel/plugin-transform-literals": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-member-expression-literals": { + "node_modules/@babel/plugin-transform-member-expression-literals": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-modules-amd": { + "node_modules/@babel/plugin-transform-modules-amd": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-transforms": "^7.10.5", "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, - "@babel/plugin-transform-modules-commonjs": { + "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-transforms": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-simple-access": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, - "@babel/plugin-transform-modules-systemjs": { + "node_modules/@babel/plugin-transform-modules-systemjs": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-hoist-variables": "^7.10.4", "@babel/helper-module-transforms": "^7.10.5", "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, - "@babel/plugin-transform-modules-umd": { + "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-transforms": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.10.4" } }, - "@babel/plugin-transform-new-target": { + "node_modules/@babel/plugin-transform-new-target": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-object-super": { + "node_modules/@babel/plugin-transform-object-super": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-replace-supers": "^7.10.4" } }, - "@babel/plugin-transform-parameters": { + "node_modules/@babel/plugin-transform-parameters": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-get-function-arity": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-property-literals": { + "node_modules/@babel/plugin-transform-property-literals": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-regenerator": { + "node_modules/@babel/plugin-transform-regenerator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", "dev": true, - "requires": { + "dependencies": { "regenerator-transform": "^0.14.2" } }, - "@babel/plugin-transform-reserved-words": { + "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-shorthand-properties": { + "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-spread": { + "node_modules/@babel/plugin-transform-spread": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-sticky-regex": { + "node_modules/@babel/plugin-transform-sticky-regex": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-regex": "^7.10.4" } }, - "@babel/plugin-transform-template-literals": { + "node_modules/@babel/plugin-transform-template-literals": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-typeof-symbol": { + "node_modules/@babel/plugin-transform-typeof-symbol": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-unicode-escapes": { + "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-transform-unicode-regex": { + "node_modules/@babel/plugin-transform-unicode-regex": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/preset-env": { + "node_modules/@babel/preset-env": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", "dev": true, - "requires": { + "dependencies": { "@babel/compat-data": "^7.10.4", "@babel/helper-compilation-targets": "^7.10.4", "@babel/helper-module-imports": "^7.10.4", @@ -918,12 +1070,12 @@ "semver": "^5.5.0" } }, - "@babel/preset-modules": { + "node_modules/@babel/preset-modules": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", "@babel/plugin-transform-dotall-regex": "^7.4.4", @@ -931,3828 +1083,3214 @@ "esutils": "^2.0.2" } }, - "@babel/runtime": { + "node_modules/@babel/runtime": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", "dev": true, - "requires": { + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz", + "integrity": "sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.14.0", "regenerator-runtime": "^0.13.4" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "node_modules/@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "node_modules/@babel/template/node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "dependencies": { + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/traverse": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", - "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.5", - "@babel/types": "^7.10.5", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", + "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" + "dependencies": { + "@babel/types": "^7.14.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@gulp-sourcemaps/identity-map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, - "requires": { - "acorn": "^5.0.3", - "css": "^2.2.1", - "normalize-path": "^2.1.1", - "source-map": "^0.6.0", - "through2": "^2.0.3" + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/traverse/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/@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "to-fast-properties": "^2.0.0" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "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 - } + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "@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=", + "node_modules/@eslint/eslintrc/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, - "requires": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" + "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/eslintrc/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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 }, - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "@jest/core": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", - "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.9.0", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-resolve-dependencies": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "jest-watcher": "^24.9.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "slash": "^2.0.0", - "strip-ansi": "^5.0.0" + "bin": { + "acorn": "bin/acorn" }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "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": "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "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" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "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" - } - } + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "node_modules/@gulp-sourcemaps/identity-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, - "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "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=", "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "dependencies": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" }, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "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" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "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" - } - } + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "@jest/reporters": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", - "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "node_modules/@gulp-sourcemaps/map-sources/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "requires": { - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.2.6", - "jest-haste-map": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.4.2", - "slash": "^2.0.0", - "source-map": "^0.6.0", - "string-length": "^2.0.0" - }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "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 - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "node_modules/@jest/types": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", + "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/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": { - "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 - } + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, - "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "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/@jest/types/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": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "@jest/test-sequencer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", - "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "node_modules/@jest/types/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/@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, - "requires": { - "@jest/test-result": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0" + "engines": { + "node": ">=8" } }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "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", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "dependencies": { + "has-flag": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "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": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "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" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "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 - }, - "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" - } - } + "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" } }, - "@jest/types": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.1.0.tgz", - "integrity": "sha512-GXigDDsp6ZlNMhXQDeuy/iYCDsRIHJabWtDzvnn36+aqFfG14JmFV0e/iXxY4SP9vbXSiPNOWdehU5MeqrYHBQ==", + "node_modules/@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "engines": { + "node": ">=10" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@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" + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "@sindresorhus/is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.0.0.tgz", - "integrity": "sha512-kqA5I6Yun7PBHk8WN9BBP1c7FfN2SrD05GuVSEYPqDb4nerv7HqYfgBfMIKmT/EuejURkJKLZuLyGKGs6WEG9w==", - "dev": true - }, - "@sinonjs/commons": { + "node_modules/@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", "dev": true, - "requires": { + "dependencies": { "type-detect": "4.0.8" } }, - "@sinonjs/formatio": { + "node_modules/@sinonjs/formatio": { "version": "2.0.0", "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, - "requires": { + "dependencies": { "samsam": "1.3.0" } }, - "@sinonjs/samsam": { + "node_modules/@sinonjs/samsam": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^1.3.0", "array-from": "^2.1.1", "lodash": "^4.17.15" } }, - "@sinonjs/text-encoding": { + "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==", "dev": true }, - "@szmarczak/http-timer": { + "node_modules/@szmarczak/http-timer": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", "dev": true, - "requires": { + "dependencies": { "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "@types/babel__core": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", - "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz", - "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } + "node_modules/@types/aria-query": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", + "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", + "dev": true }, - "@types/cacheable-request": { + "node_modules/@types/cacheable-request": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", "dev": true, - "requires": { + "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "*", "@types/node": "*", "@types/responselike": "*" } }, - "@types/color-name": { + "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@types/http-cache-semantics": { + "node_modules/@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "dev": true + }, + "node_modules/@types/easy-table": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.32.tgz", + "integrity": "sha512-zKh0f/ixYFnr3Ldf5ZJTi1ZpnRqAynTTtVyGvWDf/TT12asE8ac98t3/WGWfFdRPp/qsccxg82C/Kl3NPNhqEw==", + "dev": true + }, + "node_modules/@types/ejs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.6.tgz", + "integrity": "sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ==", + "dev": true + }, + "node_modules/@types/fibers": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.0.tgz", + "integrity": "sha512-1o3I9xtk2PZFxwaLCC6gTaBfBZ5rvw/DSZZPK89fwuwO6LNrzSbC6rEs1xI0bQ3fCRWmO+uNJQQeD2J56oTMDg==", + "dev": true + }, + "node_modules/@types/fs-extra": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", + "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", "dev": true }, - "@types/istanbul-lib-coverage": { + "node_modules/@types/inquirer": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz", + "integrity": "sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, - "@types/istanbul-lib-report": { + "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-coverage": "*" } }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", + "dependencies": { "@types/istanbul-lib-report": "*" } }, - "@types/json-schema": { + "node_modules/@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", "dev": true }, - "@types/json5": { + "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=", "dev": true }, - "@types/keyv": { + "node_modules/@types/keyv": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/node": { - "version": "14.0.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.24.tgz", - "integrity": "sha512-btt/oNOiDWcSuI721MdL8VQGnjsKjlTMdrKyTcLCKeQp/n4AAMFJ961wMbp+09y8WuGPClDEv07RIItdXKIXAA==", + "node_modules/@types/lodash": { + "version": "4.14.170", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", + "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", "dev": true }, - "@types/puppeteer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.1.tgz", - "integrity": "sha512-t03eNKCvWJXhQ8wkc5C6GYuSqMEdKLOX0GLMGtks25YZr38wKZlKTwGM/BoAPVtdysX7Bb9tdwrDS1+NrW3RRA==", + "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==", "dev": true, - "requires": { + "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==", + "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==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", + "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.17.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", + "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "node_modules/@types/puppeteer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", + "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", + "dev": true, + "dependencies": { "@types/node": "*" } }, - "@types/responselike": { + "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==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "node_modules/@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, - "@types/yargs": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", - "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "node_modules/@types/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", "dev": true, - "requires": { + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "node_modules/@types/which": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.2.tgz", + "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", + "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==", + "dev": true, + "dependencies": { "@types/yargs-parser": "*" } }, - "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "node_modules/@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "dev": true }, - "@types/yauzl": { + "node_modules/@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", "dev": true, "optional": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@wdio/browserstack-service": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.1.15.tgz", - "integrity": "sha512-q8qLa44wGSB3tIuZ0yquvAZqr2W7vEwupWiOd1ct0CSYgd4yX/nLd8oypqJCc8jU1ZwNAhu+V3/6hszvwx+HbA==", + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/@vue/compiler-core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.1.tgz", + "integrity": "sha512-Z1RO3T6AEtAUFf2EqqovFm3ohAeTvFzRtB0qUENW2nEerJfdlk13/LS1a0EgsqlzxmYfR/S/S/gW9PLbFZZxkA==", "dev": true, - "requires": { - "@wdio/logger": "6.0.16", - "browserstack-local": "^1.4.5", - "got": "^11.0.2" + "optional": true, + "dependencies": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.1.1", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" } }, - "@wdio/cli": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-6.3.4.tgz", - "integrity": "sha512-eXA4rR6DwhNtXx1Hxknwgl7jGt/q4ZErCB8aOX9rowEoPOxwPQStd6yJcqI2QE8+AC1S72PKC4w+0WImL+M6Bw==", + "node_modules/@vue/compiler-core/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, - "requires": { - "@wdio/config": "6.1.14", - "@wdio/logger": "6.0.16", - "@wdio/utils": "6.3.0", - "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": "^9.0.0", - "inquirer": "^7.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": "6.3.4", - "yargs": "^15.0.1", - "yarn-install": "^1.0.0" - }, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz", + "integrity": "sha512-nobRIo0t5ibzg+q8nC31m+aJhbq8FbWUoKvk6h3Vs1EqTDJaj6lBTcVTq5or8AYht7FbSpdAJ81isbJ1rWNX7A==", + "dev": true, + "optional": true, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - } + "@vue/compiler-core": "3.1.1", + "@vue/shared": "3.1.1" } }, - "@wdio/concise-reporter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-6.3.0.tgz", - "integrity": "sha512-H7yILps+dKK1k4XoVE5HOVMpTHN321SFmjjMgtq1zfiC6Dph7Unl4ODmnyVLD5Kk3ycQ31PfOBr0QPyKnLUFiQ==", + "node_modules/@vue/compiler-sfc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz", + "integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==", "dev": true, - "requires": { - "@wdio/reporter": "6.3.0", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" + "optional": true, + "dependencies": { + "@babel/parser": "^7.13.9", + "@babel/types": "^7.13.0", + "@vue/compiler-core": "3.1.1", + "@vue/compiler-dom": "3.1.1", + "@vue/compiler-ssr": "3.1.1", + "@vue/shared": "3.1.1", + "consolidate": "^0.16.0", + "estree-walker": "^2.0.1", + "hash-sum": "^2.0.0", + "lru-cache": "^5.1.1", + "magic-string": "^0.25.7", + "merge-source-map": "^1.1.0", + "postcss": "^8.1.10", + "postcss-modules": "^4.0.0", + "postcss-selector-parser": "^6.0.4", + "source-map": "^0.6.1" + }, + "peerDependencies": { + "vue": "3.1.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "dev": true, + "optional": true, + "bin": { + "parser": "bin/babel-parser.js" }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "optional": true, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "yallist": "^3.0.2" } }, - "@wdio/config": { - "version": "6.1.14", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-6.1.14.tgz", - "integrity": "sha512-MXHMHwtkAblfnIxONs9aW//T9Fq5XIw3oH+tztcBRvNTTAIXmwHd+4sOjAwjpCdBSGs0C4kM/aTpGfwDZVURvQ==", + "node_modules/@vue/compiler-sfc/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, - "requires": { - "@wdio/logger": "6.0.16", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "@wdio/local-runner": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-6.3.4.tgz", - "integrity": "sha512-rKEhFXiNH6H2G86JTgy2cgtEFoNBZ50gRy+P1LEhc7Ko/dAYqYMC+Sy8lnbsDzxz6IZVlbubgs+y7GRREayqoQ==", + "node_modules/@vue/compiler-sfc/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "requires": { + "optional": true + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz", + "integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==", + "dev": true, + "optional": true, + "dependencies": { + "@vue/compiler-dom": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.1.tgz", + "integrity": "sha512-DsH5woNVCcPK1M0RRYVgJEU1GJDU2ASOKpAqW3ppHk+XjoFLCbqc/26RTCgTpJYd9z8VN+79Q1u7/QqgQPbuLQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vue/shared": "3.1.1" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.1.tgz", + "integrity": "sha512-GboqR02txOtkd9F3Ysd8ltPL68vTCqIx2p/J52/gFtpgb5FG9hvOAPEwFUqxeEJRu7ResvQnmdOHiEycGPCLhQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vue/reactivity": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.1.tgz", + "integrity": "sha512-o57n/199e/BBAmLRMSXmD2r12Old/h/gf6BgL0RON1NT2pwm6MWaMY4Ul55eyq+FsDILz4jR/UgoPQ9vYB8xcw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vue/runtime-core": "3.1.1", + "@vue/shared": "3.1.1", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz", + "integrity": "sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==", + "dev": true, + "optional": true + }, + "node_modules/@wdio/browserstack-service": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.1.15.tgz", + "integrity": "sha512-q8qLa44wGSB3tIuZ0yquvAZqr2W7vEwupWiOd1ct0CSYgd4yX/nLd8oypqJCc8jU1ZwNAhu+V3/6hszvwx+HbA==", + "dev": true, + "dependencies": { "@wdio/logger": "6.0.16", - "@wdio/repl": "6.3.0", - "@wdio/runner": "6.3.4", + "browserstack-local": "^1.4.5", + "got": "^11.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@wdio/cli": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.3.tgz", + "integrity": "sha512-n7XvIoruXlGQGt2dl4dLm/J6he2Int7BOe3gnTxRTddjcqXZ8bv7qvYvNvfXzEg/vVzmUyMW2dQfzpNVoyx/dQ==", + "dev": true, + "dependencies": { + "@types/ejs": "^3.0.5", + "@types/fs-extra": "^9.0.4", + "@types/inquirer": "^7.3.1", + "@types/lodash.flattendeep": "^4.4.6", + "@types/lodash.pickby": "^4.6.6", + "@types/lodash.union": "^4.6.6", + "@types/recursive-readdir": "^2.2.0", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", "async-exit-hook": "^2.0.1", - "stream-buffers": "^3.0.2" + "chalk": "^4.0.0", + "chokidar": "^3.0.0", + "cli-spinners": "^2.1.0", + "ejs": "^3.0.1", + "fs-extra": "^10.0.0", + "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.7.3", + "yargs": "^17.0.0", + "yarn-install": "^1.0.0" + }, + "bin": { + "wdio": "bin/wdio.js" + }, + "engines": { + "node": ">=12.0.0" } }, - "@wdio/logger": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.0.16.tgz", - "integrity": "sha512-VbH5UnQIG/3sSMV+Y38+rOdwyK9mVA9vuL7iOngoTafHwUjL1MObfN/Cex84L4mGxIgfxCu6GV48iUmSuQ7sqA==", + "node_modules/@wdio/cli/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "loglevel": "^1.6.0", "loglevel-plugin-prefix": "^0.8.4", "strip-ansi": "^6.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=12.0.0" } }, - "@wdio/mocha-framework": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-6.3.0.tgz", - "integrity": "sha512-3lLvzhDYWwOYmiJAjr2fm/nENq6g6uUOtkIeEQFp1kDyBQkDsH1PXGdFklQbRiQT8mAqOPhx1kvXrCA/XpWl7g==", + "node_modules/@wdio/cli/node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, - "requires": { - "@wdio/logger": "6.0.16", - "@wdio/utils": "6.3.0", - "expect-webdriverio": "^1.1.5", - "mocha": "^8.0.1" - }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "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 - }, - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mocha": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", - "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.3.1", - "debug": "3.2.6", - "diff": "4.0.2", - "escape-string-regexp": "1.0.5", - "find-up": "4.1.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "object.assign": "4.1.0", - "promise.allsettled": "1.0.2", - "serialize-javascript": "3.0.0", - "strip-json-comments": "3.0.1", - "supports-color": "7.1.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.0", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "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 - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.7" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" } }, - "@wdio/protocols": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-6.3.0.tgz", - "integrity": "sha512-1GKzfyCTLW5WkFd3W7NLACih+zNWU7c8kFurbCQXDK1ko1obqJEs7ZjBr85q5XqMWburdks5rDjyml2iEB2LBg==", - "dev": true + "node_modules/@wdio/cli/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } }, - "@wdio/repl": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-6.3.0.tgz", - "integrity": "sha512-FT3flKOqNdZNG1uYl+QpOfdZIgKAWhLfoQ0s+wL0crLeDNIFvvM2qSDhRBRDYV7a0IFyBi8Z975WBn0dlH03Ig==", + "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", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "@wdio/utils": "6.3.0" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "@wdio/reporter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-6.3.0.tgz", - "integrity": "sha512-vbwjJvSKZUtsWtQMhuVqT7ZP6RIFAH4+ienjNwW30QPDi38OujZgxC2ZqRoZKsxck6cfTgkxrXfNaxHN0/LHKg==", + "node_modules/@wdio/cli/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/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, - "requires": { - "fs-extra": "^9.0.0" + "engines": { + "node": ">=8" } }, - "@wdio/runner": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-6.3.4.tgz", - "integrity": "sha512-+iOXfTODsSVf9LFBFKAEZqvPzfIClwFCKu7GGFZ7lrOF1svMNzT/0UY0ETsCBZe61Gr8xiI0wbCEly+0DbEh6w==", + "node_modules/@wdio/cli/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, - "requires": { - "@wdio/config": "6.1.14", - "@wdio/logger": "6.0.16", - "@wdio/utils": "6.3.0", - "deepmerge": "^4.0.0", - "gaze": "^1.1.2", - "webdriver": "6.3.0", - "webdriverio": "6.3.4" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "@wdio/spec-reporter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-6.3.0.tgz", - "integrity": "sha512-JGZAMcqiOloOw6xcIT5O8GORVaww6kslgH5kZGydVQyoNBj1ZKoLdEjqq2jklJsge1xsscdYdW9u9kMHwm25iA==", + "node_modules/@wdio/concise-reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.7.3.tgz", + "integrity": "sha512-2Ix20n48N+lvvU4NzqMP7z+daG748RRsmDqdstCoBrJgXV6frvu38HVHV90U5uKt5Vmp6/QQl05A4OliaNoO9w==", "dev": true, - "requires": { - "@wdio/reporter": "6.3.0", + "dependencies": { + "@wdio/reporter": "7.7.3", + "@wdio/types": "7.7.3", "chalk": "^4.0.0", - "easy-table": "^1.1.1", "pretty-ms": "^7.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@wdio/cli": "^7.0.0" } }, - "@wdio/sync": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-6.3.3.tgz", - "integrity": "sha512-WNq+hhkgk9LluKLP2nQ/9+EH8HNQnROFFHvYuznxb1aKj/zhZvqWuQPpmMWhPMBSTpkdbdLCYerZWKcamYOcJQ==", + "node_modules/@wdio/concise-reporter/node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, - "requires": { - "@types/puppeteer": "^3.0.1", - "@wdio/logger": "6.0.16", - "fibers": "^4.0.1" + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" } }, - "@wdio/utils": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-6.3.0.tgz", - "integrity": "sha512-PbeC5fpieamgSAHf7S58MAyraGU1qKxnHdfGMG+ZIWiIo73oo4j/57CcH6ZawQ3YC1wEc/5q+VXg7N5hvqhJOQ==", + "node_modules/@wdio/concise-reporter/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, - "requires": { - "@wdio/logger": "6.0.16" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" } }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/@wdio/concise-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, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", - "dev": true - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "node_modules/@wdio/concise-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 }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "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" } }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "node_modules/@wdio/concise-reporter/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, - "requires": { - "acorn": "^4.0.3" - }, "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "node_modules/@wdio/config": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.7.3.tgz", + "integrity": "sha512-I8gkb5BjXLe6/9NK7OCA9Mc+A6xeGUqbYTRd4PNKdObE6HomKOxw4plVZCYF0DlD2FCo4OGrvYGmalojFsCMdA==", "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" + "dependencies": { + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "deepmerge": "^4.0.0", + "glob": "^7.1.2" }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/config/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, "dependencies": { - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - } + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "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, - "requires": { - "acorn": "^3.0.4" + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true + "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" + } }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "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 }, - "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 + "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" + } }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "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, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - }, "dependencies": { - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "ajv-keywords": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", - "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "node_modules/@wdio/local-runner": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.3.tgz", + "integrity": "sha512-TM1Xd8ioc4TpZwmRStDdk7m6IVOPAEsoyKoqffuRN2pZmrj4jswmvj6qys06ErrVCGWA4skyTYZjhMZf0+V0Zg==", "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "dependencies": { + "@types/stream-buffers": "^3.0.3", + "@wdio/logger": "7.7.0", + "@wdio/repl": "7.7.3", + "@wdio/runner": "7.7.3", + "@wdio/types": "7.7.3", + "async-exit-hook": "^2.0.1", + "split2": "^3.2.2", + "stream-buffers": "^3.0.2" }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@wdio/cli": "^7.0.0" + } + }, + "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, "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" - } - } + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "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" + } }, - "ansi-colors": { + "node_modules/@wdio/local-runner/node_modules/chalk": { "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-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, - "requires": { - "type-fest": "^0.11.0" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "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, - "requires": { - "ansi-wrap": "0.1.0" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "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 }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "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, - "requires": { - "color-convert": "^1.9.0" + "engines": { + "node": ">=8" } }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "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, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "node_modules/@wdio/logger": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.0.16.tgz", + "integrity": "sha512-VbH5UnQIG/3sSMV+Y38+rOdwyK9mVA9vuL7iOngoTafHwUjL1MObfN/Cex84L4mGxIgfxCu6GV48iUmSuQ7sqA==", "dev": true, - "requires": { - "buffer-equal": "^1.0.0" + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" } }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "node_modules/@wdio/logger/node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, - "requires": { - "default-require-extensions": "^1.0.0" + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" } }, - "archiver": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-4.0.2.tgz", - "integrity": "sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ==", + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, - "requires": { - "archiver-utils": "^2.1.0", - "async": "^3.2.0", - "buffer-crc32": "^0.2.1", - "glob": "^7.1.6", - "readable-stream": "^3.6.0", - "tar-stream": "^2.1.2", - "zip-stream": "^3.0.1" - }, "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - } + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" } }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "node_modules/@wdio/logger/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, - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "node_modules/@wdio/logger/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 }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "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, - "requires": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">=8" } }, - "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 + "node_modules/@wdio/logger/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "node_modules/@wdio/mocha-framework": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.3.tgz", + "integrity": "sha512-0G9q3z6kuqFJxavm/pZNvO0bhRrZQuPbWf38vQGrbHEP15i8LNI1dDg1R73vb0y1jIbZDSIiuQsQQ6keGWND+w==", "dev": true, - "requires": { - "make-iterator": "^1.0.0" + "dependencies": { + "@types/mocha": "^8.0.0", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "expect-webdriverio": "^3.0.0", + "mocha": "^8.0.1" + }, + "engines": { + "node": ">=12.0.0" } }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "node_modules/@wdio/mocha-framework/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "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" + } }, - "arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "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", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "make-iterator": "^1.0.0" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "node_modules/@wdio/mocha-framework/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "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=", - "dev": true + "node_modules/@wdio/mocha-framework/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true + "node_modules/@wdio/mocha-framework/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" + } }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "node_modules/@wdio/mocha-framework/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 }, - "array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "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 + } + } }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "node_modules/@wdio/mocha-framework/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 }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "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" + } }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true + "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", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "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, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" + "engines": { + "node": ">=8" } }, - "array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "node_modules/@wdio/mocha-framework/node_modules/js-yaml": { + "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": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" - }, "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "node_modules/@wdio/mocha-framework/node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, - "requires": { - "is-number": "^4.0.0" - }, "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, - "array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "node_modules/@wdio/mocha-framework/node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "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.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "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" + } }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "node_modules/@wdio/mocha-framework/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 }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "node_modules/@wdio/mocha-framework/node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "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, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true + "node_modules/@wdio/mocha-framework/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "node_modules/@wdio/mocha-framework/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { - "safer-buffer": "~2.1.0" + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@wdio/mocha-framework/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "engines": { + "node": ">=10" + } + }, + "node_modules/@wdio/protocols": { + "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.7.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", + "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", + "dev": true, + "dependencies": { + "@wdio/utils": "7.7.3" }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", + "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", + "dev": true, "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } + "@types/node": "^14.14.31", + "@wdio/types": "7.7.3", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "node_modules/@wdio/runner": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.3.tgz", + "integrity": "sha512-Jetud2znIkY70lrYvHoyBQVRrIQCzNlfjLpCMMraTeNlCzW3eO82TgnOwpCoJ5cJEg78n8YLIDRcIeZ5yo4asA==", "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "dependencies": { + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "deepmerge": "^4.0.0", + "gaze": "^1.1.2", + "webdriver": "7.7.3", + "webdriverio": "7.7.3" }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/runner/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.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" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "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" + } }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "node_modules/@wdio/runner/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "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" + } }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "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 }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "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" + } }, - "async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "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, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true + "node_modules/@wdio/spec-reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", + "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", + "dev": true, + "dependencies": { + "@types/easy-table": "^0.0.32", + "@wdio/reporter": "7.7.3", + "@wdio/types": "7.7.3", + "chalk": "^4.0.0", + "easy-table": "^1.1.1", + "pretty-ms": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@wdio/cli": "^7.0.0" + } }, - "async-exit-hook": { + "node_modules/@wdio/spec-reporter/node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true + "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" + } }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "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 }, - "async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "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, - "requires": { - "async-done": "^1.2.2" + "engines": { + "node": ">=8" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "node_modules/@wdio/spec-reporter/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "node_modules/@wdio/sync": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.3.tgz", + "integrity": "sha512-LsI9rvxup6mlMuRCDBrjh674bQt4Rnpzf/xa2obhn3GZL97teSwF5ZaTTeF+cs+MPylqwbHiY7iK+roaubqECw==", + "dev": true, + "dependencies": { + "@types/fibers": "^3.1.0", + "@types/puppeteer": "^5.4.0", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "fibers": "^5.0.0", + "webdriverio": "7.7.3" + }, + "engines": { + "node": ">=12.0.0" + } }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "node_modules/@wdio/sync/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "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" + } }, - "available-typed-arrays": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", - "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "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==", "dev": true, - "requires": { - "array-filter": "^1.0.0" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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=", - "dev": true + "node_modules/@wdio/sync/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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 }, - "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=", + "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, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/types": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", + "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "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 - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "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=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "@types/node": "^14.14.31", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" } }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "node_modules/@wdio/utils": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", + "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, "dependencies": { - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - } + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3" + }, + "engines": { + "node": ">=12.0.0" } }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "node_modules/@wdio/utils/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "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, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "node_modules/@wdio/utils/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "babel-helper-builder-react-jsx": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", - "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "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, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "esutils": "^2.0.2" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "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, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "engines": { + "node": ">=8" } }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "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, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" } }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", "dev": true, - "requires": { - "babel-helper-bindify-decorators": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "node_modules/acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "dependencies": { + "acorn": "^4.0.3" } }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "node_modules/acorn-dynamic-import/node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "node_modules/acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "dependencies": { + "acorn": "^3.0.4" } }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "node_modules/acorn-jsx/node_modules/acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" } }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "engines": { + "node": ">=0.4.0" } }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", + "dev": true + }, + "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==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "engines": { + "node": ">= 6.0.0" } }, - "babel-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", - "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, - "requires": { - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.9.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, - "babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "node_modules/ajv-keywords": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", + "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", + "dev": true + }, + "node_modules/ajv/node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "node_modules/ajv/node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "requires": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "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" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "node_modules/align-text/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/align-text/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, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.4.2" } }, - "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==", + "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, - "requires": { - "object.assign": "^4.1.0" + "engines": { + "node": ">=6" } }, - "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "babel-plugin-jest-hoist": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", - "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", - "dev": true - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", - "dev": true - }, - "babel-plugin-syntax-do-expressions": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", - "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", - "dev": true - }, - "babel-plugin-syntax-function-bind": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", - "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=", - "dev": true - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-system-import-transformer": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-system-import-transformer/-/babel-plugin-system-import-transformer-3.1.0.tgz", - "integrity": "sha1-038Mro5h7zkGAggzHZMbXmMNfF8=", + "node_modules/ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0" + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" } }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=8" } }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", + "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=", "dev": true, - "requires": { - "babel-plugin-syntax-class-constructor-call": "^6.18.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", "dev": true, - "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-decorators-legacy": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.5.tgz", - "integrity": "sha512-jYHwjzRXRelYQ1uGm353zNzf3QmtdCfvJbuYTZ4gKveK7M9H1fs3a5AKdY1JUDl0z97E30ukORW1dzhWvsabtA==", + "node_modules/archiver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", + "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", "dev": true, - "requires": { - "babel-plugin-syntax-decorators": "^6.1.18", - "babel-runtime": "^6.2.0", - "babel-template": "^6.3.0" + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.0", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.0.0", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" } }, - "babel-plugin-transform-do-expressions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz", - "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=", + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, - "requires": { - "babel-plugin-syntax-do-expressions": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" } }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } + "node_modules/archiver/node_modules/async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "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, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" } }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "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=", "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "node_modules/array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "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=", + "dev": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" } }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "node_modules/array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "node_modules/array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "node_modules/array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "node_modules/array-sort/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "node_modules/array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } + "safer-buffer": "~2.1.0" } }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, - "requires": { - "babel-plugin-syntax-export-extensions": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" } }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, - "requires": { - "babel-plugin-syntax-flow": "^6.18.0", - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.8" } }, - "babel-plugin-transform-function-bind": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz", - "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=", + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, - "requires": { - "babel-plugin-syntax-function-bind": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "inherits": "2.0.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" + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" } }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "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=", "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-plugin-transform-react-display-name": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", - "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=8" } }, - "babel-plugin-transform-react-jsx": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", - "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "node_modules/async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, - "requires": { - "babel-helper-builder-react-jsx": "^6.24.1", - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "babel-plugin-transform-react-jsx-self": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", - "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "engines": { + "node": ">=0.12.0" } }, - "babel-plugin-transform-react-jsx-source": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", - "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "node_modules/async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" } }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" + "bin": { + "atob": "bin/atob.js" }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, "dependencies": { - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - } + "array-filter": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "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=", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "engines": { + "node": "*" } }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - }, + "node_modules/aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true + }, + "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=", + "dev": true, "dependencies": { - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" - } - } + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "babel-preset-flow": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", - "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "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=", "dev": true, - "requires": { - "babel-plugin-transform-flow-strip-types": "^6.22.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-preset-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", - "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "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=", "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" + "engines": { + "node": ">=0.10.0" } }, - "babel-preset-react": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", - "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "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=", "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "^6.3.13", - "babel-plugin-transform-react-display-name": "^6.23.0", - "babel-plugin-transform-react-jsx": "^6.24.1", - "babel-plugin-transform-react-jsx-self": "^6.22.0", - "babel-plugin-transform-react-jsx-source": "^6.22.0", - "babel-preset-flow": "^6.23.0" + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-preset-stage-0": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", - "integrity": "sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=", + "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=", + "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=", "dev": true, - "requires": { - "babel-plugin-transform-do-expressions": "^6.22.0", - "babel-plugin-transform-function-bind": "^6.22.0", - "babel-preset-stage-1": "^6.24.1" + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", + "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=", "dev": true, - "requires": { - "babel-plugin-transform-class-constructor-call": "^6.24.1", - "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-preset-stage-2": "^6.24.1" + "engines": { + "node": ">=0.8.0" } }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "node_modules/babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" + "dependencies": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 6.9" } }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "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, - "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "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, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - }, "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "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==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" } }, - "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=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, + "node_modules/babel-loader/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==", + "dev": true, "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - } + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "node_modules/babel-loader/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "engines": { + "node": ">=6" } }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "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==", "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, "dependencies": { - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - } + "object.assign": "^4.1.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" } }, - "babel-types": { + "node_modules/babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dependencies": { - "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=", - "dev": true - } + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, - "babelify": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-8.0.0.tgz", - "integrity": "sha512-xVr63fKEvMWUrrIbqlHYsMcc5Zdw4FSVesAHgkgajyCE1W8gbm9rbMakqavhxKvikGYMhEcqxTwB/gQmQ6lBtw==", - "dev": true + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true + "node_modules/babelify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", + "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "bach": { + "node_modules/bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", "dev": true, - "requires": { + "dependencies": { "arr-filter": "^1.1.1", "arr-flatten": "^1.0.1", "arr-map": "^2.0.0", @@ -4762,32 +4300,33 @@ "async-done": "^1.2.2", "async-settle": "^1.0.0", "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "bail": { + "node_modules/bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base": { + "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "requires": { + "dependencies": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", "component-emitter": "^1.2.1", @@ -4796,238 +4335,237 @@ "mixin-deep": "^1.2.0", "pascalcase": "^0.1.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "base64-js": { + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "dev": true }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } }, - "basic-auth": { + "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" } }, - "batch": { + "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "bcrypt-pbkdf": { + "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=", "dev": true, - "requires": { + "dependencies": { "tweetnacl": "^0.14.3" } }, - "beeper": { + "node_modules/beeper": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", - "dev": true - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", "dev": true, - "requires": { - "callsite": "1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "bfj": { + "node_modules/bfj": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", "dev": true, - "requires": { + "dependencies": { "bluebird": "^3.5.5", "check-types": "^8.0.3", "hoopy": "^0.1.4", "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 6.0.0" } }, - "big-integer": { + "node_modules/big-integer": { "version": "1.6.48", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "big.js": { + "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "binary": { + "node_modules/binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", "dev": true, - "requires": { + "dependencies": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" } }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "binaryextensions": { + "node_modules/binaryextensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "bindings": { + "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, "optional": true, - "requires": { + "dependencies": { "file-uri-to-path": "1.0.0" } }, - "bl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", - "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "requires": { + "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } } }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "node_modules/bl/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "bluebird": { + "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "bn.js": { + "node_modules/bn.js": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", "dev": true }, - "body": { + "node_modules/body": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", "dev": true, - "requires": { + "dependencies": { "continuable-cache": "^0.3.1", "error": "^7.0.0", "raw-body": "~1.1.0", "safe-json-parse": "~1.0.1" - }, - "dependencies": { - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "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=", - "dev": true, - "requires": { - "bytes": "1", - "string_decoder": "0.10" - } - }, - "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 - } } }, - "body-parser": { + "node_modules/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { + "dependencies": { "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", @@ -5038,68 +4576,91 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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=", + "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=", "dev": true, - "requires": { + "dependencies": { + "bytes": "1", + "string_decoder": "0.10" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "requires": { + "dependencies": { "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "brorand": { + "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browser-resolve": { + "node_modules/browser-resolve": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, - "requires": { - "resolve": "1.1.7" - }, "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "resolve": "1.1.7" } }, - "browser-stdout": { + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "browserify-aes": { + "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, - "requires": { + "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", @@ -5108,53 +4669,51 @@ "safe-buffer": "^5.0.1" } }, - "browserify-cipher": { + "node_modules/browserify-cipher": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, - "requires": { + "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, - "browserify-des": { + "node_modules/browserify-des": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, - "requires": { + "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, - "browserify-rsa": { + "node_modules/browserify-rsa": { "version": "4.0.1", "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, - "requires": { + "dependencies": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } } }, - "browserify-sign": { + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/browserify-sign": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", "dev": true, - "requires": { + "dependencies": { "bn.js": "^5.1.1", "browserify-rsa": "^4.0.1", "create-hash": "^1.2.0", @@ -5164,246 +4723,264 @@ "parse-asn1": "^5.1.5", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "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==", - "dev": true - } } }, - "browserify-zlib": { + "node_modules/browserify-sign/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/browserify-sign/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==", + "dev": true + }, + "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, - "requires": { + "dependencies": { "pako": "~1.0.5" } }, - "browserslist": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", - "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001093", - "electron-to-chromium": "^1.3.488", - "escalade": "^3.0.1", - "node-releases": "^1.1.58" + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" } }, - "browserstack": { + "node_modules/browserstack": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "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 - } + "https-proxy-agent": "^2.2.1" } }, - "browserstack-local": { + "node_modules/browserstack-local": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.5.tgz", "integrity": "sha512-0/VdSv2YVXmcnwBb64XThMvjM1HnZJnPdv7CUgQbC5y/N9Wsr0Fu+j1oknE9fC/VPx9CpoSC6CJ0kza42skMSA==", "dev": true, - "requires": { + "dependencies": { "https-proxy-agent": "^4.0.0", "is-running": "^2.1.0", "ps-tree": "=1.2.0", "temp-fs": "^0.9.9" } }, - "browserstacktunnel-wrapper": { + "node_modules/browserstack/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browserstack/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/browserstack/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/browserstack/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/browserstacktunnel-wrapper": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz", "integrity": "sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg==", "dev": true, - "requires": { + "dependencies": { "https-proxy-agent": "^2.2.1", "unzipper": "^0.9.3" }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "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 - } + "engines": { + "node": ">= 0.10.20" } }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/browserstacktunnel-wrapper/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, - "requires": { - "node-int64": "^0.4.0" + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" } }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "node_modules/browserstacktunnel-wrapper/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "dependencies": { + "ms": "^2.1.1" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "node_modules/browserstacktunnel-wrapper/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" } }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "node_modules/browserstacktunnel-wrapper/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 }, - "buffer-crc32": { + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "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=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "buffer-equal": { + "node_modules/buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "buffer-indexof-polyfill": { + "node_modules/buffer-indexof-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "buffer-shims": { + "node_modules/buffer-shims": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, - "buffer-xor": { + "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "buffers": { + "node_modules/buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.2.0" + } }, - "builtin-status-codes": { + "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, - "bytes": { + "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } }, - "cac": { + "node_modules/cac": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", "dev": true, - "requires": { + "dependencies": { "camelcase-keys": "^3.0.0", "chalk": "^1.1.3", "indent-string": "^3.0.0", @@ -5412,55 +4989,71 @@ "suffix": "^0.1.0", "text-table": "^0.2.0" }, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "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 - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cache-base": { + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, - "requires": { + "dependencies": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", "get-value": "^2.0.6", @@ -5470,20 +5063,26 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cacheable-lookup": { + "node_modules/cacheable-lookup": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "cacheable-request": { + "node_modules/cacheable-request": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", "dev": true, - "requires": { + "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", @@ -5491,1780 +5090,26670 @@ "lowercase-keys": "^2.0.0", "normalize-url": "^4.1.0", "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "caller-path": { + "node_modules/cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, - "requires": { + "dependencies": { "callsites": "^0.2.0" }, - "dependencies": { - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - } + "engines": { + "node": ">=0.10.0" } }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true + "node_modules/caller-path/node_modules/callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "camelcase-keys": { + "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, - "requires": { + "dependencies": { "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": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } + "engines": { + "node": ">=0.10.0" } }, - "caniuse-lite": { + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-lite": { "version": "1.0.30001235", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", "dev": true }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { + "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "ccount": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", - "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==", - "dev": true + "node_modules/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "center-align": { + "node_modules/center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, - "requires": { + "dependencies": { "align-text": "^0.1.3", "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" } }, - "chai": { + "node_modules/chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, - "requires": { + "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", "pathval": "^1.1.0", "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" } }, - "chainsaw": { + "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", "dev": true, - "requires": { + "dependencies": { "traverse": ">=0.3.0 <0.4" } }, - "chalk": { + "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "character-entities": { + "node_modules/character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "character-entities-html4": { + "node_modules/character-entities-html4": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "character-entities-legacy": { + "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "character-reference-invalid": { + "node_modules/character-reference-invalid": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "chardet": { + "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "check-error": { + "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "check-types": { + "node_modules/check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", "dev": true }, - "chokidar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", - "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", + "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, - "requires": { + "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" } }, - "chownr": { + "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, - "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==", + "node_modules/chrome-launcher": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.14.0.tgz", + "integrity": "sha512-W//HpflaW6qBGrmuskup7g+XJZN6w03ko9QSIe5CtcTal2u0up5SeReK3Ll1Why4Ey8dPkv8XSodZyHPnGbVHQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", - "escape-string-regexp": "^1.0.5", + "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^0.5.3", - "rimraf": "^3.0.2" + "lighthouse-logger": "^1.0.0" }, - "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==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "engines": { + "node": ">=12.13.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "cipher-base": { + "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, - "circular-json": { + "node_modules/circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", "dev": true }, - "class-utils": { + "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, - "requires": { + "dependencies": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", "static-extend": "^0.1.1" }, + "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=", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cli-cursor": { + "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "requires": { + "dependencies": { "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "cli-spinners": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz", - "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==", - "dev": true + "node_modules/cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "cli-width": { + "node_modules/cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 10" + } }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "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, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "wrap-ansi": "^7.0.0" } }, - "clone": { + "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "clone-buffer": { + "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=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "clone-response": { + "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=", "dev": true, - "requires": { + "dependencies": { "mimic-response": "^1.0.0" } }, - "clone-stats": { + "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=", "dev": true }, - "cloneable-readable": { + "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", "readable-stream": "^2.3.5" - }, + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "co": { + "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } }, - "code-point-at": { + "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=", - "dev": true - }, - "collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "collection-map": { + "node_modules/collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, - "requires": { + "dependencies": { "arr-map": "^2.0.2", "for-own": "^1.0.0", "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "collection-visit": { + "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, - "requires": { + "dependencies": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "color-convert": { + "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "color-support": { + "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, - "colors": { + "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.1.90" + } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "comma-separated-tokens": { + "node_modules/comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "dev": true + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "commander": { + "node_modules/commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, - "commondir": { + "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } }, - "component-emitter": { + "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compress-commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-3.0.0.tgz", - "integrity": "sha512-FyDqr8TKX5/X0qo+aVfaZ+PVmNJHJeckFBlq8jZGSJOgnynhfifoyl24qaqdUdDIBe0EVTHByN6NAkqYvE/2Xg==", + "node_modules/compress-commons": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, - "requires": { + "dependencies": { "buffer-crc32": "^0.2.13", - "crc32-stream": "^3.0.1", + "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", - "readable-stream": "^2.3.7" + "readable-stream": "^3.6.0" }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } + "engines": { + "node": ">= 10" } }, - "concat-map": { + "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=", "dev": true }, - "concat-stream": { + "node_modules/concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, - "requires": { + "engines": [ + "node >= 0.8" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" - }, + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "concat-with-sourcemaps": { + "node_modules/concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "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 - } + "source-map": "^0.6.1" } }, - "connect": { + "node_modules/concat-with-sourcemaps/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/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, - "requires": { + "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" } }, - "connect-livereload": { + "node_modules/connect-livereload": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "console-browserify": { + "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, - "constants-browserify": { + "node_modules/consolidate": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", + "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", + "dev": true, + "optional": true, + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, - "contains-path": { + "node_modules/contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "content-disposition": { + "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { + "dependencies": { "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } }, - "continuable-cache": { + "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=", "dev": true }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "node_modules/conventional-changelog": { + "version": "3.1.24", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", + "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "dependencies": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" + }, + "engines": { + "node": ">=10" } }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-props": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "node_modules/conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", "dev": true, - "requires": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" - }, - "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "node_modules/conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", "dev": true, - "requires": { - "browserslist": "^4.8.5", - "semver": "7.0.0" - }, "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "core-js-pure": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", - "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + "node_modules/conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", + "dev": true, + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } }, - "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=", + "node_modules/conventional-changelog-config-spec": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", + "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", "dev": true }, - "coveralls": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", - "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", + "node_modules/conventional-changelog-conventionalcommits": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", + "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", "dev": true, - "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "requires": { - "buffer": "^5.1.0" + "node_modules/conventional-changelog-core": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz", + "integrity": "sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^4.0.18", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "shelljs": "^0.8.3", + "through2": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "crc32-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", - "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", + "node_modules/conventional-changelog-core/node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true, - "requires": { - "crc": "^3.4.4", - "readable-stream": "^3.4.0" + "engines": { + "node": "*" } }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "node_modules/conventional-changelog-core/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - }, "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "create-hash": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/conventional-changelog-core/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "criteo-direct-rsa-validate": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/criteo-direct-rsa-validate/-/criteo-direct-rsa-validate-1.1.0.tgz", - "integrity": "sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q==" - }, - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "node_modules/conventional-changelog-core/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/conventional-changelog-core/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "crypto-js": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", - "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" - }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.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 - } + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" } }, - "css-value": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", - "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=", - "dev": true + "node_modules/conventional-changelog-core/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==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } }, - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } }, - "cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "node_modules/conventional-changelog-core/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=", "dev": true, - "requires": { - "cssom": "0.3.x" + "engines": { + "node": ">=4" } }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "node_modules/conventional-changelog-core/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, - "requires": { - "array-find-index": "^1.0.1" + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" } }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true + "node_modules/conventional-changelog-core/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=", + "dev": true, + "engines": { + "node": ">=4" + } }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "node_modules/conventional-changelog-core/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "node_modules/conventional-changelog-core/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "engines": { + "node": ">=4" } }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "node_modules/conventional-changelog-core/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, "dependencies": { - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "node_modules/conventional-changelog-core/node_modules/read-pkg/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 }, - "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "node_modules/conventional-changelog-core/node_modules/read-pkg/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, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" + "node_modules/conventional-changelog-core/node_modules/read-pkg/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" } }, - "debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "node_modules/conventional-changelog-core/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "requires": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" - }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "node_modules/conventional-changelog-core/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } }, - "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=", + "node_modules/conventional-changelog-core/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "node_modules/conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "node_modules/conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", "dev": true, - "requires": { - "type-detect": "^4.0.0" + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "deep-equal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", - "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "node_modules/conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", "dev": true, - "requires": { - "es-abstract": "^1.17.5", - "es-get-iterator": "^1.1.0", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.0.5", - "isarray": "^2.0.5", - "object-is": "^1.1.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - }, "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "node_modules/conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", "dev": true, - "requires": { - "kind-of": "^5.0.2" + "dependencies": { + "q": "^1.5.1" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "dev": true, "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" } }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "node_modules/conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", "dev": true, - "requires": { - "strip-bom": "^2.0.0" + "engines": { + "node": ">=10" } }, - "default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", - "dev": true + "node_modules/conventional-changelog-writer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", + "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "node_modules/conventional-changelog-writer/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "optional": true, - "requires": { - "clone": "^1.0.2" + "engines": { + "node": ">=6" } }, - "defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", - "dev": true + "node_modules/conventional-changelog-writer/node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "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==", + "node_modules/conventional-changelog-writer/node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true, - "requires": { - "object-keys": "^1.0.12" + "engines": { + "node": "*" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/conventional-changelog-writer/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, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true + "node_modules/conventional-changelog-writer/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "node_modules/conventional-changelog-writer/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "node_modules/conventional-changelog-writer/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" + } }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "node_modules/conventional-changelog-writer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/conventional-changelog-writer/node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "detab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.3.tgz", - "integrity": "sha512-Up8P0clUVwq0FnFjDclzZsy9PadzRn5FFxrr47tQQvMHqyiFYVbpH8oXDzWtF0Q7pYy3l+RPmtBl+BsFF6wH0A==", + "node_modules/conventional-changelog-writer/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, - "requires": { - "repeat-string": "^1.5.4" + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true + "node_modules/conventional-changelog-writer/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "node_modules/conventional-changelog-writer/node_modules/normalize-package-data/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "requires": { - "repeating": "^2.0.0" + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true + "node_modules/conventional-changelog-writer/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" + } }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true + "node_modules/conventional-changelog-writer/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "node_modules/conventional-changelog-writer/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true, - "requires": { - "acorn": "^5.2.1", - "defined": "^1.0.0" + "engines": { + "node": ">=8" } }, - "devtools": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-6.3.4.tgz", - "integrity": "sha512-dOcLdArp5/dJBzD8T5wcT2YgqkA22Mkqo0OS9cXz7JkHNgwOx1FI2Bq9GvP6o0TENHifYSYg3G0K/z0bacekqg==", + "node_modules/conventional-changelog-writer/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, - "requires": { - "@wdio/config": "6.1.14", - "@wdio/logger": "6.0.16", - "@wdio/protocols": "6.3.0", - "@wdio/utils": "6.3.0", - "chrome-launcher": "^0.13.1", - "puppeteer-core": "^5.1.0", - "ua-parser-js": "^0.7.21", - "uuid": "^8.0.0" + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" } }, - "devtools-protocol": { - "version": "0.0.781568", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.781568.tgz", - "integrity": "sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==", + "node_modules/conventional-changelog-writer/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-writer/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-writer/node_modules/read-pkg/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 }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "node_modules/conventional-changelog-writer/node_modules/read-pkg/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/conventional-changelog-writer/node_modules/read-pkg/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/conventional-changelog-writer/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-writer/node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/conventional-changelog-writer/node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/conventional-changelog-writer/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-writer/node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-changelog-writer/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-writer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "diff": { + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", + "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", + "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/conventional-commits-parser/node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-commits-parser/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/conventional-commits-parser/node_modules/hosted-git-info": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } }, - "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", - "dev": true + "node_modules/conventional-commits-parser/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/conventional-commits-parser/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, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "dependencies": { + "p-locate": "^4.1.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "disparity": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/disparity/-/disparity-2.0.0.tgz", - "integrity": "sha1-V92stHMkrl9Y0swNqIbbTOnutxg=", + "node_modules/conventional-commits-parser/node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", "dev": true, - "requires": { - "ansi-styles": "^2.0.1", - "diff": "^1.3.2" + "engines": { + "node": ">=8" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-commits-parser/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", - "dev": true - } + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "node_modules/conventional-commits-parser/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } }, - "doctrine": { - "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "node_modules/conventional-commits-parser/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, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "doctrine-temporary-fork": { - "version": "2.0.0-alpha-allowarrayindex", - "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.0.0-alpha-allowarrayindex.tgz", - "integrity": "sha1-QAFahn6yfnWybIKLcVJPE3+J+fA=", + "node_modules/conventional-commits-parser/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "documentation": { - "version": "5.5.0", - "resolved": "http://registry.npmjs.org/documentation/-/documentation-5.5.0.tgz", - "integrity": "sha512-Aod3HOI+8zMhwWztDlECRsDfJ8SFu4oADvipOLq3gnWKy4Cpg2oF5AWT+U6PcX85KuguDI6c+q+2YwYEx99B/A==", + "node_modules/conventional-commits-parser/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true, - "requires": { - "ansi-html": "^0.0.7", - "babel-core": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-plugin-system-import-transformer": "3.1.0", - "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-preset-env": "^1.6.1", - "babel-preset-react": "^6.24.1", - "babel-preset-stage-0": "^6.24.1", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babelify": "^8.0.0", - "babylon": "^6.18.0", - "chalk": "^2.3.0", - "chokidar": "^2.0.0", - "concat-stream": "^1.6.0", - "disparity": "^2.0.0", - "doctrine-temporary-fork": "2.0.0-alpha-allowarrayindex", - "get-port": "^3.2.0", - "git-url-parse": "^8.0.0", - "github-slugger": "1.2.0", - "glob": "^7.1.2", - "globals-docs": "^2.4.0", - "highlight.js": "^9.12.0", - "js-yaml": "^3.10.0", - "lodash": "^4.17.4", - "mdast-util-inject": "^1.1.0", - "micromatch": "^3.1.5", - "mime": "^1.4.1", - "module-deps-sortable": "4.0.6", - "parse-filepath": "^1.0.2", - "pify": "^3.0.0", - "read-pkg-up": "^3.0.0", - "remark": "^9.0.0", - "remark-html": "7.0.0", - "remark-reference-links": "^4.0.1", - "remark-toc": "^5.0.0", - "remote-origin-url": "0.4.0", - "shelljs": "^0.8.1", - "stream-array": "^1.1.2", - "strip-json-comments": "^2.0.1", - "tiny-lr": "^1.1.0", - "unist-builder": "^1.0.2", - "unist-util-visit": "^1.3.0", - "vfile": "^2.3.0", - "vfile-reporter": "^4.0.0", - "vfile-sort": "^2.1.0", - "vinyl": "^2.1.0", - "vinyl-fs": "^3.0.2", - "yargs": "^9.0.1" + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-commits-parser/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-commits-parser/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-commits-parser/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/conventional-recommended-bump": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", + "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", + "dev": true, + "dependencies": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.3.4", + "conventional-commits-filter": "^2.0.7", + "conventional-commits-parser": "^3.2.0", + "git-raw-commits": "^2.0.8", + "git-semver-tags": "^4.1.1", + "meow": "^8.0.0", + "q": "^1.5.1" + }, + "bin": { + "conventional-recommended-bump": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/conventional-recommended-bump/node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump/node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/conventional-recommended-bump/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-recommended-bump/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "dependencies": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "node_modules/core-js": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", + "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "dependencies": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + } + }, + "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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", + "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "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=", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/coveralls": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", + "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", + "dev": true, + "dependencies": { + "js-yaml": "^3.13.1", + "lcov-parse": "^1.0.0", + "log-driver": "^1.2.7", + "minimist": "^1.2.5", + "request": "^2.88.2" + }, + "bin": { + "coveralls": "bin/coveralls.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "dev": true, + "dependencies": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + }, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", + "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/criteo-direct-rsa-validate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/criteo-direct-rsa-validate/-/criteo-direct-rsa-validate-1.1.0.tgz", + "integrity": "sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q==" + }, + "node_modules/cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-shorthand-properties": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", + "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", + "dev": true + }, + "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=", + "dev": true + }, + "node_modules/css/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/css/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/css/node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "optional": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "2.6.17", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", + "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "bin": { + "dateformat": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true, + "optional": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "dependencies": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + } + }, + "node_modules/debug-fabulous/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/debug-fabulous/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/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-equal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", + "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "dev": true, + "dependencies": { + "es-abstract": "^1.17.5", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.0.5", + "isarray": "^2.0.5", + "object-is": "^1.1.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "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=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "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=", + "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=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/devtools": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.7.3.tgz", + "integrity": "sha512-MvLCrJqLJXPK+N1En01EdM8wpitQiKaXGlprSsWMZWJU5iy/ljOKF9KRYGf0eQ2ZT3FfjcBgJh4yyLom7wVYeg==", + "dev": true, + "dependencies": { + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "chrome-launcher": "^0.14.0", + "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/devtools-protocol": { + "version": "0.0.887710", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.887710.tgz", + "integrity": "sha512-ZN57GSHgIoz6opBE4XtUhZvCG4Mjy6n0WxUCcSv8fdHc1TDRlI8IglTzwNMXUKqehFSIEHVxKZcaAoACKWHFBQ==", + "dev": true + }, + "node_modules/devtools/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "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/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/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/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/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/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.1.tgz", + "integrity": "sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/doctrine": { + "version": "1.5.0", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "dependencies": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/doctrine-temporary-fork": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.1.0.tgz", + "integrity": "sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/documentation": { + "version": "13.2.5", + "resolved": "https://registry.npmjs.org/documentation/-/documentation-13.2.5.tgz", + "integrity": "sha512-d1TrfrHXYZR63xrOzkYwwe297vkSwBoEhyyMBOi20T+7Ohe1aX1dW4nqXncQmdmE5MxluSaxxa3BW1dCvbF5AQ==", + "dev": true, + "dependencies": { + "@babel/core": "7.12.3", + "@babel/generator": "7.12.1", + "@babel/parser": "7.12.3", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "ansi-html": "^0.0.7", + "babelify": "^10.0.0", + "chalk": "^2.3.0", + "chokidar": "^3.4.0", + "concat-stream": "^1.6.0", + "diff": "^4.0.1", + "doctrine-temporary-fork": "2.1.0", + "get-port": "^5.0.0", + "git-url-parse": "^11.1.2", + "github-slugger": "1.2.0", + "glob": "^7.1.2", + "globals-docs": "^2.4.0", + "highlight.js": "^10.7.2", + "ini": "^1.3.5", + "js-yaml": "^3.10.0", + "lodash": "^4.17.10", + "mdast-util-find-and-replace": "^1.1.1", + "mdast-util-inject": "^1.1.0", + "micromatch": "^3.1.5", + "mime": "^2.2.0", + "module-deps-sortable": "^5.0.3", + "parse-filepath": "^1.0.2", + "pify": "^5.0.0", + "read-pkg-up": "^4.0.0", + "remark": "^13.0.0", + "remark-gfm": "^1.0.0", + "remark-html": "^13.0.1", + "remark-reference-links": "^5.0.0", + "remark-toc": "^7.2.0", + "resolve": "^1.8.1", + "stream-array": "^1.1.2", + "strip-json-comments": "^2.0.1", + "tiny-lr": "^1.1.0", + "unist-builder": "^2.0.3", + "unist-util-visit": "^2.0.3", + "vfile": "^4.0.0", + "vfile-reporter": "^6.0.0", + "vfile-sort": "^2.1.0", + "vinyl": "^2.1.0", + "vinyl-fs": "^3.0.2", + "yargs": "^15.3.1" + }, + "bin": { + "documentation": "bin/documentation.js" + }, + "engines": { + "node": ">=10" + }, + "optionalDependencies": { + "@vue/compiler-sfc": "^3.0.11", + "vue-template-compiler": "^2.6.12" + } + }, + "node_modules/documentation/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/documentation/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/documentation/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/documentation/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/documentation/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/documentation/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/documentation/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/documentation/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/documentation/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/documentation/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/documentation/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/documentation/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotgitignore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", + "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotgitignore/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/dset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dset/-/dset-2.0.1.tgz", + "integrity": "sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/easy-table": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", + "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/easy-table/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/edge-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-2.2.1.tgz", + "integrity": "sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw==", + "dev": true, + "dependencies": { + "@types/which": "^1.3.2", + "which": "^2.0.2" + } + }, + "node_modules/edge-paths/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/ejs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", + "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", + "dev": true, + "dependencies": { + "jake": "^10.6.1" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", + "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/elliptic/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", + "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.0", + "ws": "~7.4.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "dev": true, + "dependencies": { + "base64-arraybuffer": "0.1.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/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/engine.io/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/enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "dependencies": { + "string-template": "~0.2.1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-abstract/node_modules/is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "dependencies": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es5-shim": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", + "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-set/node_modules/es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "dependencies": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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=", + "dev": true + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/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==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/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==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-prebid": { + "resolved": "plugins/eslint", + "link": true + }, + "node_modules/eslint-plugin-promise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", + "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", + "dev": true, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0" + } + }, + "node_modules/eslint-plugin-standard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", + "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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", + "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/eslint/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/eslint/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/eslint/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/eslint/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/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/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "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/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/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/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "optional": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "dev": true + }, + "node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "dependencies": { + "fill-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "dependencies": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/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/expand-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/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/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.2.tgz", + "integrity": "sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.2", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.0.1", + "jest-matcher-utils": "^27.0.2", + "jest-message-util": "^27.0.2", + "jest-regex-util": "^27.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect-webdriverio": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-3.1.0.tgz", + "integrity": "sha512-Kn4Rtu5vKbDo95WNcjZ9XVz/qTPGZzumP9w7VSV4OxY5z6BAqSI2jb85EsqPxpavs33P+9Qse4Z+d5ilDD/dQw==", + "dev": true, + "dependencies": { + "expect": "^27.0.2", + "jest-matcher-utils": "^27.0.2" + } + }, + "node_modules/expect/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "dependencies": { + "type": "^2.0.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "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=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/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/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/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/extract-zip/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/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", + "dev": true + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "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=", + "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=", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fibers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.0.tgz", + "integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "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/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "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=", + "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=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "dev": true + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "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=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "node_modules/fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "dependencies": { + "null-check": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "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=", + "dev": true, + "dependencies": { + "fs-extra": "~0.6.1", + "mkdirp": "~0.3.5", + "walk": "^2.3.9" + }, + "engines": { + "node": "*" + } + }, + "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=", + "dev": true, + "dependencies": { + "jsonfile": "~1.0.1", + "mkdirp": "0.3.x", + "ncp": "~0.4.2", + "rimraf": "~2.2.0" + } + }, + "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=", + "dev": true + }, + "node_modules/fs.extra/node_modules/mkdirp": { + "version": "0.3.5", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "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=", + "dev": true, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/fun-hooks": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.9.tgz", + "integrity": "sha512-821UhoYfO9Sg01wAl/QsDRB088BW0aeOqzC1PXLxSlB+kaUVbK+Vp6wMDHU5huZZopYxmMmv5jDkEYqDpK3hqg==", + "dependencies": { + "typescript-tuple": "^2.2.1" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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=", + "dev": true + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/generic-names": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", + "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", + "dev": true, + "optional": true, + "dependencies": { + "loader-utils": "^1.1.0" + } + }, + "node_modules/generic-names/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, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/generic-names/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, + "optional": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "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=", + "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==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" + }, + "bin": { + "get-pkg-repo": "cli.js" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", + "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/git-raw-commits/node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/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/git-raw-commits/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/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/git-raw-commits/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/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/git-raw-commits/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-raw-commits/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "dev": true, + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "dependencies": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/git-semver-tags/node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/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/git-semver-tags/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/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/git-semver-tags/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/node_modules/normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/normalize-package-data/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/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/git-semver-tags/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/git-semver-tags/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/git-semver-tags/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-semver-tags/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/git-up": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", + "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" + } + }, + "node_modules/git-url-parse": { + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz", + "integrity": "sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw==", + "dev": true, + "dependencies": { + "git-up": "^4.0.0" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", + "dev": true, + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/github-slugger": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", + "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", + "dev": true, + "dependencies": { + "emoji-regex": ">=6.0.0 <=6.1.1" + } + }, + "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": "*" + } + }, + "node_modules/glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "dependencies": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "dependencies": { + "is-glob": "^2.0.0" + } + }, + "node_modules/glob-base/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-watcher/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "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=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "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", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/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/glob-watcher/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/glob-watcher/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.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=", + "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/glob-watcher/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/glob-watcher/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/glob-watcher/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "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-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/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/glob-watcher/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/glob-watcher/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globals-docs": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.4.1.tgz", + "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==", + "dev": true + }, + "node_modules/globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-clean": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.3.2.tgz", + "integrity": "sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI=", + "dev": true, + "dependencies": { + "gulp-util": "^2.2.14", + "rimraf": "^2.2.8", + "through2": "^0.4.2" + }, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/gulp-clean/node_modules/ansi-regex": { + "version": "0.2.1", + "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/chalk": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "dependencies": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/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=", + "dev": true + }, + "node_modules/gulp-clean/node_modules/gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "dependencies": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulp-clean/node_modules/gulp-util/node_modules/through2": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "node_modules/gulp-clean/node_modules/has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "dependencies": { + "ansi-regex": "^0.2.0" + }, + "bin": { + "has-ansi": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/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/gulp-clean/node_modules/minimist": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", + "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", + "dev": true + }, + "node_modules/gulp-clean/node_modules/object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "node_modules/gulp-clean/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-clean/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/gulp-clean/node_modules/strip-ansi": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "dependencies": { + "ansi-regex": "^0.2.1" + }, + "bin": { + "strip-ansi": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true, + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/through2": { + "version": "0.4.2", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + } + }, + "node_modules/gulp-clean/node_modules/through2/node_modules/xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "dependencies": { + "object-keys": "~0.4.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/gulp-clean/node_modules/vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "dependencies": { + "clone-stats": "~0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulp-clean/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cli/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-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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/gulp-cli/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "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=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "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/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "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=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/gulp-cli/node_modules/yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/gulp-cli/node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "dependencies": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-concat/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-concat/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-connect": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", + "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", + "dev": true, + "dependencies": { + "ansi-colors": "^2.0.5", + "connect": "^3.6.6", + "connect-livereload": "^0.6.0", + "fancy-log": "^1.3.2", + "map-stream": "^0.0.7", + "send": "^0.16.2", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2", + "tiny-lr": "^1.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-connect/node_modules/ansi-colors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", + "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/gulp-connect/node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, + "node_modules/gulp-connect/node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true, + "bin": { + "mime": "cli.js" + } + }, + "node_modules/gulp-connect/node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/gulp-connect/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/gulp-connect/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/gulp-eslint": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-4.0.2.tgz", + "integrity": "sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg==", + "dev": true, + "dependencies": { + "eslint": "^4.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.0" + } + }, + "node_modules/gulp-eslint/node_modules/ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true, + "peerDependencies": { + "ajv": "^5.0.0" + } + }, + "node_modules/gulp-eslint/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "node_modules/gulp-eslint/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/gulp-eslint/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/gulp-eslint/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/gulp-eslint/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/eslint": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "dev": true, + "dependencies": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.4", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.0.1", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/gulp-eslint/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "dependencies": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/gulp-eslint/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "dependencies": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "dependencies": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "node_modules/gulp-eslint/node_modules/inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "node_modules/gulp-eslint/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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/gulp-eslint/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/gulp-eslint/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "node_modules/gulp-eslint/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/gulp-eslint/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/gulp-eslint/node_modules/slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/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/gulp-eslint/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-eslint/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-eslint/node_modules/table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "dependencies": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "node_modules/gulp-footer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-footer/-/gulp-footer-2.0.2.tgz", + "integrity": "sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g==", + "dev": true, + "dependencies": { + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.6.2", + "map-stream": "0.0.7" + } + }, + "node_modules/gulp-footer/node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "node_modules/gulp-footer/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-footer/node_modules/lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "dependencies": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "node_modules/gulp-footer/node_modules/lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "dependencies": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "node_modules/gulp-footer/node_modules/lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "node_modules/gulp-footer/node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, + "node_modules/gulp-header": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.9.tgz", + "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==", + "dev": true, + "dependencies": { + "concat-with-sourcemaps": "^1.1.0", + "lodash.template": "^4.5.0", + "map-stream": "0.0.7", + "through2": "^2.0.0" + } + }, + "node_modules/gulp-header/node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "node_modules/gulp-header/node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/gulp-header/node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/gulp-header/node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, + "node_modules/gulp-header/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-header/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "dev": true, + "dependencies": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/gulp-if/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/gulp-if/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "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=", + "dev": true, + "dependencies": { + "through2": "^0.6.3" + } + }, + "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=", + "dev": true + }, + "node_modules/gulp-js-escape/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-js-escape/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/gulp-js-escape/node_modules/through2": { + "version": "0.6.5", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.3" + } + }, + "node_modules/gulp-replace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", + "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "dev": true, + "dependencies": { + "istextorbinary": "2.2.1", + "readable-stream": "^2.0.1", + "replacestream": "^4.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/gulp-replace/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-shell": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", + "dev": true, + "dependencies": { + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" + }, + "engines": { + "node": ">=10.0.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", + "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/gulp-shell/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-shell/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/gulp-shell/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/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/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/gulp-shell/node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "node_modules/gulp-shell/node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/gulp-shell/node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "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", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-shell/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "dependencies": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-sourcemaps/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-sourcemaps/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/gulp-sourcemaps/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-terser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.0.1.tgz", + "integrity": "sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw==", + "dev": true, + "dependencies": { + "plugin-error": "^1.0.1", + "terser": "5.4.0", + "through2": "^4.0.2", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "dependencies": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true + }, + "node_modules/gulp-util/node_modules/dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/gulp-util/node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "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.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "dependencies": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^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", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "dependencies": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-util/node_modules/replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gulp-util/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "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=", + "dev": true, + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gzip-size/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + } + }, + "node_modules/handlebars/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/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "dependencies": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/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/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/hash-base/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==", + "dev": true + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "optional": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hast-util-is-element": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", + "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-sanitize": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz", + "integrity": "sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz", + "integrity": "sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw==", + "dev": true, + "dependencies": { + "ccount": "^1.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-is-element": "^1.0.0", + "hast-util-whitespace": "^1.0.0", + "html-void-elements": "^1.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0", + "stringify-entities": "^3.0.1", + "unist-util-is": "^4.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", + "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "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/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "agent-base": "5", + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/https-proxy-agent/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true, + "optional": true + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "optional": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "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": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "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/inquirer": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.1.tgz", + "integrity": "sha512-hUDjc3vBkh/uk1gPfMAD/7Z188Q8cvTGl0nxwaCdwSbzFh6ZKkZh+s2ozVxbE5G9ZNRyeY0+lgbAIOUFsFf98w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/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/inquirer/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/inquirer/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/inquirer/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/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/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/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/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-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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/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-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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "dependencies": { + "is-primitive": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "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=", + "dev": true + }, + "node_modules/is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, + "node_modules/is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", + "dev": true, + "dependencies": { + "protocols": "^1.1.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "node_modules/is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "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==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true + }, + "node_modules/istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "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-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "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/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "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", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "node_modules/istanbul/node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/istanbul/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "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=", + "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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "node_modules/istanbul/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=", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "dependencies": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "dev": true, + "dependencies": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-diff": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.2.tgz", + "integrity": "sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.1", + "jest-get-type": "^27.0.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/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/jest-diff/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/jest-diff/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/jest-diff/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/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", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-get-type": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.1.tgz", + "integrity": "sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz", + "integrity": "sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.0.2", + "jest-get-type": "^27.0.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-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/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/jest-matcher-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/jest-matcher-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/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", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.2.tgz", + "integrity": "sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.0.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.0.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/jest-message-util/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/jest-message-util/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/jest-message-util/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/jest-message-util/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/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/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/jest-message-util/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/jest-regex-util": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.1.tgz", + "integrity": "sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "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", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "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=", + "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=", + "dev": true + }, + "node_modules/json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/just-clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-1.0.2.tgz", + "integrity": "sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ=" + }, + "node_modules/just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, + "node_modules/just-extend": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", + "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "dev": true + }, + "node_modules/karma": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz", + "integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==", + "dev": true, + "dependencies": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "colors": "^1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.3.0", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^3.1.0", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.28", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-babel-preprocessor": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", + "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "@babel/core": "7" + } + }, + "node_modules/karma-browserstack-launcher": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.4.0.tgz", + "integrity": "sha512-bUQK84U+euDfOUfEjcF4IareySMOBNRLrrl9q6cttIe8f011Ir6olLITTYMOJDcGY58wiFIdhPHSPd9Pi6+NfQ==", + "dev": true, + "dependencies": { + "browserstack": "~1.5.1", + "browserstacktunnel-wrapper": "~2.0.2", + "q": "~1.5.0" + } + }, + "node_modules/karma-chai": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", + "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.2.tgz", + "integrity": "sha512-zge5qiGEIKDdzWciQwP4p0LSac4k/L6VfrBsERMUn5mpDvxhv1sPVOrSlpzpi70T7NhuEy4bgnpAKIYuumIMCw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.1", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + } + }, + "node_modules/karma-coverage/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/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/karma-coverage/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/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=", + "dev": true, + "dependencies": { + "es5-shim": "^4.0.5" + } + }, + "node_modules/karma-firefox-launcher": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.1.tgz", + "integrity": "sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q==", + "dev": true, + "dependencies": { + "is-wsl": "^2.2.0", + "which": "^2.0.1" + } + }, + "node_modules/karma-firefox-launcher/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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=", + "dev": true, + "dependencies": { + "lodash": "^4.6.1" + } + }, + "node_modules/karma-mocha": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.1.tgz", + "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.3" + } + }, + "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=", + "dev": true, + "dependencies": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "strip-ansi": "^4.0.0" + } + }, + "node_modules/karma-mocha-reporter/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/karma-mocha-reporter/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true + }, + "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=", + "dev": true + }, + "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=", + "dev": true + }, + "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=", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/karma-sourcemap-loader": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz", + "integrity": "sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2" + } + }, + "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=", + "dev": true, + "dependencies": { + "colors": "^1.1.2" + }, + "peerDependencies": { + "karma": ">=0.9" + } + }, + "node_modules/karma-webpack": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-3.0.5.tgz", + "integrity": "sha1-H/HjppD7c66V7pX5q1jzQc/HtA8=", + "dev": true, + "dependencies": { + "async": "^2.0.0", + "babel-runtime": "^6.0.0", + "loader-utils": "^1.0.0", + "lodash": "^4.0.0", + "source-map": "^0.5.6", + "webpack-dev-middleware": "^2.0.6" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/karma-webpack/node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/karma-webpack/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/karma-webpack/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/karma/node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/karma/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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/karma/node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/karma/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/karma/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/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/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", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/keyv": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", + "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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/konan": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/konan/-/konan-2.1.1.tgz", + "integrity": "sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.10.5", + "@babel/traverse": "^7.10.5" + } + }, + "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=", + "dev": true, + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "bin": { + "lcov-parse": "bin/cli.js" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "dependencies": { + "leven": "^3.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.2.0.tgz", + "integrity": "sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw==", + "dev": true, + "dependencies": { + "debug": "^2.6.8", + "marky": "^1.2.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "dev": true + }, + "node_modules/live-connect-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-2.0.0.tgz", + "integrity": "sha512-Xhrj1JU5LoLjJuujjTlvDfc/n3Shzk2hPlYmLdCx/lsltFFVuCFa9uM8u5mcHlmOUKP5pu9I54bAITxZBMHoXg==", + "dependencies": { + "tiny-hashes": "1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/livereload-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", + "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "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=", + "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=", + "dev": true + }, + "node_modules/lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "dependencies": { + "lodash._htmlescapes": "~2.4.1" + } + }, + "node_modules/lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "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=", + "dev": true + }, + "node_modules/lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "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=", + "dev": true + }, + "node_modules/lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "node_modules/lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "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=", + "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=", + "dev": true + }, + "node_modules/lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "node_modules/lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "dependencies": { + "lodash._htmlescapes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "node_modules/lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true, + "optional": 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=", + "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=", + "dev": true + }, + "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=", + "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=", + "dev": true + }, + "node_modules/lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "dependencies": { + "lodash._escapehtmlchar": "~2.4.1", + "lodash._reunescapedhtml": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "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=", + "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=", + "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=", + "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=", + "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=", + "dev": true + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "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=", + "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=", + "dev": true + }, + "node_modules/lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "dependencies": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "node_modules/lodash.keys/node_modules/lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.pickby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=", + "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=", + "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=", + "dev": true + }, + "node_modules/lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "dependencies": { + "lodash._escapestringchar": "~2.4.1", + "lodash._reinterpolate": "~2.4.1", + "lodash.defaults": "~2.4.1", + "lodash.escape": "~2.4.1", + "lodash.keys": "~2.4.1", + "lodash.templatesettings": "~2.4.1", + "lodash.values": "~2.4.1" + } + }, + "node_modules/lodash.template/node_modules/lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "dependencies": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "~2.4.1", + "lodash.escape": "~2.4.1" + } + }, + "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=", + "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=", + "dev": true + }, + "node_modules/lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "dependencies": { + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", + "dev": true + }, + "node_modules/log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true, + "engines": { + "node": ">=0.8.6" + } + }, + "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==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/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/log-symbols/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/log-symbols/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/log-symbols/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/log-symbols/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/log-symbols/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/log4js": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", + "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", + "dev": true, + "dependencies": { + "date-format": "^3.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.1", + "rfdc": "^1.1.4", + "streamroller": "^2.2.4" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/log4js/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/log4js/node_modules/flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "node_modules/log4js/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/loglevel": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", + "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true + }, + "node_modules/loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha1-NvxPWZbWZA9Tn/IDuoGWQWgNdaI=", + "dev": true, + "dependencies": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", + "dev": true + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "optional": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "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=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marky": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.2.tgz", + "integrity": "sha512-k1dB2HNeaNyORco8ulVEhctyEGkKHb2YWAhDsxeFlW2nROIirsctBYzKwwS3Vza+sKTS1zO4Z+n9/+9WbGLIxQ==", + "dev": true + }, + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "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=", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "dev": true, + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz", + "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^4.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz", + "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", + "dev": true, + "dependencies": { + "mdast-util-gfm-autolink-literal": "^0.1.0", + "mdast-util-gfm-strikethrough": "^0.2.0", + "mdast-util-gfm-table": "^0.1.0", + "mdast-util-gfm-task-list-item": "^0.1.0", + "mdast-util-to-markdown": "^0.6.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz", + "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", + "dev": true, + "dependencies": { + "ccount": "^1.0.0", + "mdast-util-find-and-replace": "^1.1.0", + "micromark": "^2.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz", + "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", + "dev": true, + "dependencies": { + "mdast-util-to-markdown": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz", + "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", + "dev": true, + "dependencies": { + "markdown-table": "^2.0.0", + "mdast-util-to-markdown": "~0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz", + "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", + "dev": true, + "dependencies": { + "mdast-util-to-markdown": "~0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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=", + "dev": true, + "dependencies": { + "mdast-util-to-string": "^1.0.0" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", + "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", + "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "dev": true + }, + "node_modules/mdast-util-toc": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-5.1.0.tgz", + "integrity": "sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.3", + "@types/unist": "^2.0.3", + "extend": "^3.0.2", + "github-slugger": "^1.2.1", + "mdast-util-to-string": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-toc/node_modules/github-slugger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", + "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", + "dev": true, + "dependencies": { + "emoji-regex": ">=6.0.0 <=6.1.1" + } + }, + "node_modules/mdast-util-toc/node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/memoizee": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.45", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.5" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=" + }, + "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", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz", + "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", + "dev": true, + "dependencies": { + "micromark": "~2.11.0", + "micromark-extension-gfm-autolink-literal": "~0.5.0", + "micromark-extension-gfm-strikethrough": "~0.6.5", + "micromark-extension-gfm-table": "~0.4.0", + "micromark-extension-gfm-tagfilter": "~0.3.0", + "micromark-extension-gfm-task-list-item": "~0.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz", + "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", + "dev": true, + "dependencies": { + "micromark": "~2.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz", + "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", + "dev": true, + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz", + "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", + "dev": true, + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz", + "integrity": "sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz", + "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", + "dev": true, + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark/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/micromark/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/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/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-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/micromatch/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/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/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "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_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/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/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "dependencies": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "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": "*" + } + }, + "node_modules/mocha/node_modules/he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/mocha/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "node_modules/mocha/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module-deps-sortable": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/module-deps-sortable/-/module-deps-sortable-5.0.3.tgz", + "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", + "dev": true, + "dependencies": { + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "konan": "^2.1.1", + "readable-stream": "^2.0.2", + "resolve": "^1.1.3", + "standard-version": "^9.0.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/module-deps-sortable/node_modules/concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "node_modules/module-deps-sortable/node_modules/concat-stream/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "node_modules/module-deps-sortable/node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/module-deps-sortable/node_modules/concat-stream/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/module-deps-sortable/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/module-deps-sortable/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/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, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "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": "http://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": "http://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", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true, + "optional": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true + }, + "node_modules/ncp": { + "version": "0.4.2", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", + "dev": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node_modules/nise": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", + "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "dev": true, + "dependencies": { + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^5.0.1", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/formatio": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "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=", + "dev": true + }, + "node_modules/nise/node_modules/lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/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/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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-keys": { + "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" + } + }, + "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=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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=", + "dev": true, + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "dependencies": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.omit/node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/opn": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", + "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", + "deprecated": "The package has been renamed to `open`", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "dependencies": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "node_modules/optimist/node_modules/minimist": { + "version": "0.0.10", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "node_modules/optimist/node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/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/ora/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/ora/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/ora/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/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/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/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=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "dependencies": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/os-locale/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "dependencies": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true + }, + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "dependencies": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-path": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/parse-path/node_modules/qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/parse-url": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.3.tgz", + "integrity": "sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A==", + "dev": true, + "dependencies": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.0.1", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "node_modules/parse-url/node_modules/normalize-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", + "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "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/path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=" + }, + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pbkdf2-compat": { + "version": "2.0.1", + "resolved": "http://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", + "integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=", + "dev": true + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "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=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "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-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/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==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.4.tgz", + "integrity": "sha512-/tZY0PXExXXnNhKv3TOvZAOUYRyuqcCbBm2c17YMDK0PlVII3K7/LKdt3ScHL+hhouddjUWi+1sKDf9xXW+8YA==", + "dev": true, + "optional": true, + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-modules": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz", + "integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==", + "dev": true, + "optional": true, + "dependencies": { + "generic-names": "^2.0.1", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "optional": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "optional": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "optional": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "optional": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "optional": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true, + "optional": true + }, + "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=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.2.tgz", + "integrity": "sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", + "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==", + "dev": true, + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", + "dev": true, + "bin": { + "printj": "bin/printj.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "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==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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/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/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/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/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/puppeteer-core/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz", + "integrity": "sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg==", + "dev": true + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "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=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/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=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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": ">= 6" + } + }, + "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==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "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.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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==", + "dev": true, + "dependencies": { + "minimatch": "3.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent/node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "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==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "dependencies": { + "is-equal-shallow": "^0.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remark": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", + "dev": true, + "dependencies": { + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz", + "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", + "dev": true, + "dependencies": { + "mdast-util-gfm": "^0.1.0", + "micromark-extension-gfm": "^0.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-html": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-13.0.1.tgz", + "integrity": "sha512-K5KQCXWVz+harnyC+UVM/J9eJWCgjYRqFeZoZf2NgP0iFbuuw/RgMZv3MA34b/OEpGnstl3oiOUtZzD3tJ+CBw==", + "dev": true, + "dependencies": { + "hast-util-sanitize": "^3.0.0", + "hast-util-to-html": "^7.0.0", + "mdast-util-to-hast": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reference-links": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-5.0.0.tgz", + "integrity": "sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ==", + "dev": true, + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", + "dev": true, + "dependencies": { + "mdast-util-to-markdown": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-toc": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-7.2.0.tgz", + "integrity": "sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.3", + "mdast-util-toc": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-buffer/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/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=", + "dev": true, + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-bom-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/remove-bom-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "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=", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/replacestream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "dependencies": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-uncached/node_modules/resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true + }, + "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=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "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==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/resq": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resq/-/resq-1.10.0.tgz", + "integrity": "sha512-hCUd0xMalqtPDz4jXIqs0M5Wnv/LZXN8h7unFOo4/nvExT9dDPbhwd3udRxLlp0HgBnHcV009UlduE9NZi7A6w==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "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=", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rgb2hex": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", + "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", + "dev": true + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "dev": true, + "dependencies": { + "glob": "^7.0.5" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "node_modules/rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "dependencies": { + "rx-lite": "*" + } + }, + "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/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==" + }, + "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=", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + } + }, + "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/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=", + "dev": true, + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "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=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "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", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/sinon": { + "version": "4.5.0", + "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", + "dev": true, + "dependencies": { + "@sinonjs/formatio": "^2.0.0", + "diff": "^3.1.0", + "lodash.get": "^4.4.2", + "lolex": "^2.2.0", + "nise": "^1.2.0", + "supports-color": "^5.1.0", + "type-detect": "^4.0.5" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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/slice-ansi/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/slice-ansi/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/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/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/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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", + "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.8", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.1", + "engine.io": "~4.1.0", + "socket.io-adapter": "~2.1.0", + "socket.io-parser": "~4.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", + "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", + "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==", + "dev": true, + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/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/socket.io-parser/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/socket.io/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/socket.io/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/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", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "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" + } + }, + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true, + "optional": true + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "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/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-version": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.3.0.tgz", + "integrity": "sha512-cYxxKXhYfI3S9+CA84HmrJa9B88H56V5FQ302iFF2TNwJukJCNoU8FgWt+11YtwKFXRkQQFpepC2QOF7aDq2Ow==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "conventional-changelog": "3.1.24", + "conventional-changelog-config-spec": "2.1.0", + "conventional-changelog-conventionalcommits": "4.5.0", + "conventional-recommended-bump": "6.1.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "dotgitignore": "^2.1.0", + "figures": "^3.1.0", + "find-up": "^5.0.0", + "fs-access": "^1.0.1", + "git-semver-tags": "^4.0.0", + "semver": "^7.1.1", + "stringify-package": "^1.0.1", + "yargs": "^16.0.0" + }, + "bin": { + "standard-version": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-version/node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-version/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-version/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-version/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/standard-version/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "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=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/stream-array/-/stream-array-1.1.2.tgz", + "integrity": "sha1-nl9zRfITfDDuO0mLkRToC1K7frU=", + "dev": true, + "dependencies": { + "readable-stream": "~2.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-array/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "node_modules/stream-array/node_modules/readable-stream": { + "version": "2.1.5", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "dev": true, + "dependencies": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-array/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-combiner2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "node_modules/streamroller": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", + "dev": true, + "dependencies": { + "date-format": "^2.1.0", + "debug": "^4.1.1", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/streamroller/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/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": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/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/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/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "dev": true, + "optional": 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=", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "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.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "node_modules/string.prototype.trimend/node_modules/es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimend/node_modules/is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimend/node_modules/is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimend/node_modules/object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "node_modules/string.prototype.trimstart/node_modules/es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimstart/node_modules/is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimstart/node_modules/is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimstart/node_modules/object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stringify-entities": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", + "integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-package": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", + "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", + "dev": true + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "dependencies": { + "minimist": "^1.1.0" + } + }, + "node_modules/suffix": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", + "integrity": "sha1-zFgjFkag7xEC95R47zqSSP2chC8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/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=", + "dev": true, + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "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=", + "dev": true, + "dependencies": { + "rimraf": "~2.5.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ternary-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", + "dev": true, + "dependencies": { + "duplexify": "^4.1.1", + "fork-stream": "^0.0.4", + "merge-stream": "^2.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/ternary-stream/node_modules/duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ternary-stream/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ternary-stream/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/terser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.4.0.tgz", + "integrity": "sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "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/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", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "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=", + "dev": true + }, + "node_modules/textextensions": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", + "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2-filter/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2-filter/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tiny-hashes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tiny-hashes/-/tiny-hashes-1.0.1.tgz", + "integrity": "sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==" + }, + "node_modules/tiny-lr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", + "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", + "dev": true, + "dependencies": { + "body": "^5.1.0", + "debug": "^3.1.0", + "faye-websocket": "~0.10.0", + "livereload-js": "^2.3.0", + "object-assign": "^4.1.0", + "qs": "^6.4.0" + } + }, + "node_modules/tiny-lr/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/tiny-lr/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/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/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=", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/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/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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.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=", + "dev": true, + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/to-through/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/to-through/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "node_modules/tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/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/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "dependencies": { + "typescript-logic": "^0.0.0" + } + }, + "node_modules/typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==" + }, + "node_modules/typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "dependencies": { + "typescript-compare": "^0.0.2" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", + "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/uglify-js": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", + "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "node_modules/uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "dependencies": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "dependencies": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", + "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", + "dev": true, + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "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=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unzipper": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.9.15.tgz", + "integrity": "sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, + "node_modules/url-parse": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.0.tgz", + "integrity": "sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "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=", + "engines": { + "node": ">= 0.4.0" + } + }, + "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/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-reporter": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.2.tgz", + "integrity": "sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA==", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.0", + "string-width": "^4.0.0", + "supports-color": "^6.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-sort": "^2.1.2", + "vfile-statistics": "^1.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-reporter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/vfile-sort": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.2.tgz", + "integrity": "sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA==", + "dev": true + }, + "node_modules/vfile-statistics": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.4.tgz", + "integrity": "sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/vinyl-fs/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "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=", + "dev": true, + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.1.tgz", + "integrity": "sha512-j9fj3PNPMxo2eqOKYjMuss9XBS8ZtmczLY3kPvjcp9d3DbhyNqLYbaMQH18+1pDIzzVvQCQBvIf774LsjjqSKA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.1.1", + "@vue/runtime-dom": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dev": true, + "optional": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/walk": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz", + "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", + "dev": true, + "dependencies": { + "foreachasync": "^3.0.0" + } + }, + "node_modules/watchpack": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", + "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", + "dev": true, + "dependencies": { + "chokidar": "^3.4.0", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "watchpack-chokidar2": "^2.0.0" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/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=", + "dev": true, + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/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, + "optional": 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/watchpack-chokidar2/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, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "node_modules/watchpack-chokidar2/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, + "optional": 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/watchpack-chokidar2/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, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/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=", + "dev": true, + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/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=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/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, + "optional": true + }, + "node_modules/watchpack-chokidar2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/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, + "optional": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/watchpack-chokidar2/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, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.7.3.tgz", + "integrity": "sha512-/NfeRPREZPkY7pWVvnlyE2E4cfvl+lQmu9j1vE2GDL+oBwCHn+C5Vxwag6bOiBsrKcBan05Ghhlcl/o2uw9ZUA==", + "dev": true, + "dependencies": { + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "got": "^11.0.2", + "lodash.merge": "^4.6.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriver/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "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/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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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.7.3", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.7.3.tgz", + "integrity": "sha512-3m18Ax0dKHBT7lMueUNEgYDHMRVodXokmAq/yAH+SqHFUbHdPrHOFK3d/snFZe41f6LHrLLzebVE7rvI4Zr1AA==", + "dev": true, + "dependencies": { + "@types/aria-query": "^4.2.1", + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.7.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.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.7.3", + "devtools-protocol": "^0.0.887710", + "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.7.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/webdriverio/node_modules/@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "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/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/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/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/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/webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "dev": true, + "dependencies": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz", + "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 6.14.4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/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/webpack-bundle-analyzer/node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "dependencies": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/webpack-core/node_modules/source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "node_modules/webpack-core/node_modules/source-map": { + "version": "0.4.4", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "2.0.6", + "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", + "dev": true, + "dependencies": { + "loud-rejection": "^1.6.0", + "memory-fs": "~0.4.1", + "mime": "^2.1.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "url-join": "^2.0.2", + "webpack-log": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha1-pLNM2msitRjbsKsy5WeWLVxypD0=", + "dev": true, + "dependencies": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-log/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-log/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/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-stream": { + "version": "3.2.0", + "resolved": "http://registry.npmjs.org/webpack-stream/-/webpack-stream-3.2.0.tgz", + "integrity": "sha1-Oh0WD7EdQXJ7fObzL3IkZPmLIYY=", + "dev": true, + "dependencies": { + "gulp-util": "^3.0.7", + "lodash.clone": "^4.3.2", + "lodash.some": "^4.2.2", + "memory-fs": "^0.3.0", + "through": "^2.3.8", + "vinyl": "^1.1.0", + "webpack": "^1.12.9" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/acorn": { + "version": "3.3.0", + "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-stream/node_modules/anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "dependencies": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "node_modules/webpack-stream/node_modules/arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/webpack-stream/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "dependencies": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/browserify-aes": { + "version": "0.4.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", + "integrity": "sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/webpack-stream/node_modules/browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "dependencies": { + "pako": "~0.2.0" + } + }, + "node_modules/webpack-stream/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/webpack-stream/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "dependencies": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + }, + "optionalDependencies": { + "fsevents": "^1.0.0" + } + }, + "node_modules/webpack-stream/node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/webpack-stream/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=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/crypto-browserify": { + "version": "3.3.0", + "resolved": "http://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", + "integrity": "sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw=", + "dev": true, + "dependencies": { + "browserify-aes": "0.4.0", + "pbkdf2-compat": "2.0.1", + "ripemd160": "0.2.0", + "sha.js": "2.2.6" + }, + "engines": { + "node": "*" + } + }, + "node_modules/webpack-stream/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-stream/node_modules/enhanced-resolve": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.2.0", + "tapable": "^0.1.8" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/webpack-stream/node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/webpack-stream/node_modules/expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "dependencies": { + "is-posix-bracket": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/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/webpack-stream/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/webpack-stream/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-stream/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "dependencies": { + "is-glob": "^2.0.0" + } + }, + "node_modules/webpack-stream/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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/interpret": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", + "integrity": "sha1-/s16GOfOXKar+5U+H4YhOknxYls=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/is-accessor-descriptor/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/webpack-stream/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=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/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/webpack-stream/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/is-data-descriptor/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/webpack-stream/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/is-descriptor/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/webpack-stream/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack-stream/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/webpack-stream/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "node_modules/webpack-stream/node_modules/memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/webpack-stream/node_modules/micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "dependencies": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/webpack-stream/node_modules/node-libs-browser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", + "integrity": "sha1-PicsCBnjCJNeJmdECNevDhSRuDs=", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.1.4", + "buffer": "^4.9.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "3.3.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "0.0.1", + "os-browserify": "^0.2.0", + "path-browserify": "0.0.0", + "process": "^0.11.0", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.0.5", + "stream-browserify": "^2.0.1", + "stream-http": "^2.3.1", + "string_decoder": "^0.10.25", + "timers-browserify": "^2.0.2", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + } + }, + "node_modules/webpack-stream/node_modules/node-libs-browser/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/webpack-stream/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=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/pako": { + "version": "0.2.9", + "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack-stream/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/webpack-stream/node_modules/ripemd160": { + "version": "0.2.0", + "resolved": "http://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", + "integrity": "sha1-K/GYveFnys+lHAqSjoS2i74XH84=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/sha.js": { + "version": "2.2.6", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", + "integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=", + "dev": true, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/webpack-stream/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack-stream/node_modules/tapable": { + "version": "0.1.10", + "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/webpack-stream/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-stream/node_modules/uglify-js": { + "version": "2.7.5", + "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", + "dev": true, + "dependencies": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack-stream/node_modules/uglify-js/node_modules/async": { + "version": "0.2.10", + "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/webpack-stream/node_modules/vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/webpack-stream/node_modules/vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "dependencies": { + "indexof": "0.0.1" + } + }, + "node_modules/webpack-stream/node_modules/watchpack": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", + "integrity": "sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws=", + "dev": true, + "dependencies": { + "async": "^0.9.0", + "chokidar": "^1.0.0", + "graceful-fs": "^4.1.2" + } + }, + "node_modules/webpack-stream/node_modules/watchpack/node_modules/async": { + "version": "0.9.2", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "node_modules/webpack-stream/node_modules/webpack": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.15.0.tgz", + "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=", + "dev": true, + "dependencies": { + "acorn": "^3.0.0", + "async": "^1.3.0", + "clone": "^1.0.2", + "enhanced-resolve": "~0.9.0", + "interpret": "^0.6.4", + "loader-utils": "^0.2.11", + "memory-fs": "~0.3.0", + "mkdirp": "~0.5.0", + "node-libs-browser": "^0.7.0", + "optimist": "~0.6.0", + "supports-color": "^3.1.0", + "tapable": "~0.1.8", + "uglify-js": "~2.7.3", + "watchpack": "^0.2.1", + "webpack-core": "~0.6.9" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/webpack-stream/node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-stream/node_modules/yargs": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + } + }, + "node_modules/webpack/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/webpack/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/webpack/node_modules/cliui/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/webpack/node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/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=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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/webpack/node_modules/loader-utils/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/webpack/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/webpack/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==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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/webpack/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/webpack/node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/string-width/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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "dependencies": { + "has-flag": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/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=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/webpack/node_modules/yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "node_modules/webpack/node_modules/yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "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/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "dependencies": { + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/write/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==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "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/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/yarn-install": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", + "integrity": "sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA=", + "dev": true, + "dependencies": { + "cac": "^3.0.3", + "chalk": "^1.1.3", + "cross-spawn": "^4.0.2" + }, + "bin": { + "yarn-install": "bin/yarn-install.js", + "yarn-remove": "bin/yarn-remove.js" + }, + "engines": { + "node": ">=6" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", + "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", + "dev": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "compress-commons": "^4.1.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "plugins/eslint": { + "name": "eslint-plugin-prebid", + "version": "1.0.0", + "dev": true, + "license": "Apache-2.0" + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.5.tgz", + "integrity": "sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + } + } + }, + "@babel/generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", + "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", + "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", + "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", + "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", + "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "dev": true, + "requires": { + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", + "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", + "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", + "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", + "dev": true, + "requires": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", + "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", + "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", + "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", + "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", + "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", + "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", + "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", + "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.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", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", + "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", + "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", + "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", + "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", + "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", + "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", + "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", + "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", + "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "@babel/helper-compilation-targets": "^7.10.4", + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-proposal-async-generator-functions": "^7.10.4", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-dynamic-import": "^7.10.4", + "@babel/plugin-proposal-json-strings": "^7.10.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-numeric-separator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.10.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.10.4", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.10.4", + "@babel/plugin-transform-arrow-functions": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.10.4", + "@babel/plugin-transform-block-scoped-functions": "^7.10.4", + "@babel/plugin-transform-block-scoping": "^7.10.4", + "@babel/plugin-transform-classes": "^7.10.4", + "@babel/plugin-transform-computed-properties": "^7.10.4", + "@babel/plugin-transform-destructuring": "^7.10.4", + "@babel/plugin-transform-dotall-regex": "^7.10.4", + "@babel/plugin-transform-duplicate-keys": "^7.10.4", + "@babel/plugin-transform-exponentiation-operator": "^7.10.4", + "@babel/plugin-transform-for-of": "^7.10.4", + "@babel/plugin-transform-function-name": "^7.10.4", + "@babel/plugin-transform-literals": "^7.10.4", + "@babel/plugin-transform-member-expression-literals": "^7.10.4", + "@babel/plugin-transform-modules-amd": "^7.10.4", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/plugin-transform-modules-systemjs": "^7.10.4", + "@babel/plugin-transform-modules-umd": "^7.10.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", + "@babel/plugin-transform-new-target": "^7.10.4", + "@babel/plugin-transform-object-super": "^7.10.4", + "@babel/plugin-transform-parameters": "^7.10.4", + "@babel/plugin-transform-property-literals": "^7.10.4", + "@babel/plugin-transform-regenerator": "^7.10.4", + "@babel/plugin-transform-reserved-words": "^7.10.4", + "@babel/plugin-transform-shorthand-properties": "^7.10.4", + "@babel/plugin-transform-spread": "^7.10.4", + "@babel/plugin-transform-sticky-regex": "^7.10.4", + "@babel/plugin-transform-template-literals": "^7.10.4", + "@babel/plugin-transform-typeof-symbol": "^7.10.4", + "@babel/plugin-transform-unicode-escapes": "^7.10.4", + "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.10.4", + "browserslist": "^4.12.0", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", + "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@babel/runtime-corejs3": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz", + "integrity": "sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q==", + "dev": true, + "requires": { + "core-js-pure": "^3.14.0", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", + "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + } + } + }, + "@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "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" + } + }, + "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" + } + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "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 + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, + "requires": { + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "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 + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } + } + }, + "@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=", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/types": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", + "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } + } + }, + "@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" + } + }, + "@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/formatio": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "dev": true, + "requires": { + "samsam": "1.3.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.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==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/aria-query": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", + "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", + "dev": true + }, + "@types/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", + "dev": true + }, + "@types/cors": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "dev": true + }, + "@types/easy-table": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.32.tgz", + "integrity": "sha512-zKh0f/ixYFnr3Ldf5ZJTi1ZpnRqAynTTtVyGvWDf/TT12asE8ac98t3/WGWfFdRPp/qsccxg82C/Kl3NPNhqEw==", + "dev": true + }, + "@types/ejs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.6.tgz", + "integrity": "sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ==", + "dev": true + }, + "@types/fibers": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.0.tgz", + "integrity": "sha512-1o3I9xtk2PZFxwaLCC6gTaBfBZ5rvw/DSZZPK89fwuwO6LNrzSbC6rEs1xI0bQ3fCRWmO+uNJQQeD2J56oTMDg==", + "dev": true + }, + "@types/fs-extra": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", + "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, + "@types/inquirer": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz", + "integrity": "sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "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=", + "dev": true + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.170", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", + "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", + "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==", + "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==", + "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==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", + "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true + }, + "@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "@types/node": { + "version": "14.17.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", + "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/puppeteer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", + "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", + "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==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "@types/which": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.2.tgz", + "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", + "dev": true + }, + "@types/yargs": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", + "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "@vue/compiler-core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.1.tgz", + "integrity": "sha512-Z1RO3T6AEtAUFf2EqqovFm3ohAeTvFzRtB0qUENW2nEerJfdlk13/LS1a0EgsqlzxmYfR/S/S/gW9PLbFZZxkA==", + "dev": true, + "optional": true, + "requires": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.1.1", + "estree-walker": "^2.0.1", + "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, + "optional": true + } + } + }, + "@vue/compiler-dom": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz", + "integrity": "sha512-nobRIo0t5ibzg+q8nC31m+aJhbq8FbWUoKvk6h3Vs1EqTDJaj6lBTcVTq5or8AYht7FbSpdAJ81isbJ1rWNX7A==", + "dev": true, + "optional": true, + "requires": { + "@vue/compiler-core": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "@vue/compiler-sfc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz", + "integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==", + "dev": true, + "optional": true, + "requires": { + "@babel/parser": "^7.13.9", + "@babel/types": "^7.13.0", + "@vue/compiler-core": "3.1.1", + "@vue/compiler-dom": "3.1.1", + "@vue/compiler-ssr": "3.1.1", + "@vue/shared": "3.1.1", + "consolidate": "^0.16.0", + "estree-walker": "^2.0.1", + "hash-sum": "^2.0.0", + "lru-cache": "^5.1.1", + "magic-string": "^0.25.7", + "merge-source-map": "^1.1.0", + "postcss": "^8.1.10", + "postcss-modules": "^4.0.0", + "postcss-selector-parser": "^6.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "@babel/parser": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", + "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "dev": true, + "optional": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "optional": true, + "requires": { + "yallist": "^3.0.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, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "optional": true + } + } + }, + "@vue/compiler-ssr": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz", + "integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==", + "dev": true, + "optional": true, + "requires": { + "@vue/compiler-dom": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "@vue/reactivity": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.1.tgz", + "integrity": "sha512-DsH5woNVCcPK1M0RRYVgJEU1GJDU2ASOKpAqW3ppHk+XjoFLCbqc/26RTCgTpJYd9z8VN+79Q1u7/QqgQPbuLQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@vue/shared": "3.1.1" + } + }, + "@vue/runtime-core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.1.tgz", + "integrity": "sha512-GboqR02txOtkd9F3Ysd8ltPL68vTCqIx2p/J52/gFtpgb5FG9hvOAPEwFUqxeEJRu7ResvQnmdOHiEycGPCLhQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@vue/reactivity": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "@vue/runtime-dom": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.1.tgz", + "integrity": "sha512-o57n/199e/BBAmLRMSXmD2r12Old/h/gf6BgL0RON1NT2pwm6MWaMY4Ul55eyq+FsDILz4jR/UgoPQ9vYB8xcw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@vue/runtime-core": "3.1.1", + "@vue/shared": "3.1.1", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz", + "integrity": "sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==", + "dev": true, + "optional": true + }, + "@wdio/browserstack-service": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.1.15.tgz", + "integrity": "sha512-q8qLa44wGSB3tIuZ0yquvAZqr2W7vEwupWiOd1ct0CSYgd4yX/nLd8oypqJCc8jU1ZwNAhu+V3/6hszvwx+HbA==", + "dev": true, + "requires": { + "@wdio/logger": "6.0.16", + "browserstack-local": "^1.4.5", + "got": "^11.0.2" + } + }, + "@wdio/cli": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.3.tgz", + "integrity": "sha512-n7XvIoruXlGQGt2dl4dLm/J6he2Int7BOe3gnTxRTddjcqXZ8bv7qvYvNvfXzEg/vVzmUyMW2dQfzpNVoyx/dQ==", + "dev": true, + "requires": { + "@types/ejs": "^3.0.5", + "@types/fs-extra": "^9.0.4", + "@types/inquirer": "^7.3.1", + "@types/lodash.flattendeep": "^4.4.6", + "@types/lodash.pickby": "^4.6.6", + "@types/lodash.union": "^4.6.6", + "@types/recursive-readdir": "^2.2.0", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.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.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.7.3", + "yargs": "^17.0.0", + "yarn-install": "^1.0.0" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@wdio/concise-reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.7.3.tgz", + "integrity": "sha512-2Ix20n48N+lvvU4NzqMP7z+daG748RRsmDqdstCoBrJgXV6frvu38HVHV90U5uKt5Vmp6/QQl05A4OliaNoO9w==", + "dev": true, + "requires": { + "@wdio/reporter": "7.7.3", + "@wdio/types": "7.7.3", + "chalk": "^4.0.0", + "pretty-ms": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@wdio/config": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.7.3.tgz", + "integrity": "sha512-I8gkb5BjXLe6/9NK7OCA9Mc+A6xeGUqbYTRd4PNKdObE6HomKOxw4plVZCYF0DlD2FCo4OGrvYGmalojFsCMdA==", + "dev": true, + "requires": { + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "deepmerge": "^4.0.0", + "glob": "^7.1.2" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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.7.3", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.3.tgz", + "integrity": "sha512-TM1Xd8ioc4TpZwmRStDdk7m6IVOPAEsoyKoqffuRN2pZmrj4jswmvj6qys06ErrVCGWA4skyTYZjhMZf0+V0Zg==", + "dev": true, + "requires": { + "@types/stream-buffers": "^3.0.3", + "@wdio/logger": "7.7.0", + "@wdio/repl": "7.7.3", + "@wdio/runner": "7.7.3", + "@wdio/types": "7.7.3", + "async-exit-hook": "^2.0.1", + "split2": "^3.2.2", + "stream-buffers": "^3.0.2" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.0.16.tgz", + "integrity": "sha512-VbH5UnQIG/3sSMV+Y38+rOdwyK9mVA9vuL7iOngoTafHwUjL1MObfN/Cex84L4mGxIgfxCu6GV48iUmSuQ7sqA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@wdio/mocha-framework": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.3.tgz", + "integrity": "sha512-0G9q3z6kuqFJxavm/pZNvO0bhRrZQuPbWf38vQGrbHEP15i8LNI1dDg1R73vb0y1jIbZDSIiuQsQQ6keGWND+w==", + "dev": true, + "requires": { + "@types/mocha": "^8.0.0", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "expect-webdriverio": "^3.0.0", + "mocha": "^8.0.1" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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 + }, + "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" + }, + "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", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "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 + }, + "js-yaml": { + "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" + } + }, + "log-symbols": { + "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.0.0" + } + }, + "mocha": { + "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.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.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==", + "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.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "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" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, + "@wdio/protocols": { + "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.7.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", + "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", + "dev": true, + "requires": { + "@wdio/utils": "7.7.3" + } + }, + "@wdio/reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", + "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", + "dev": true, + "requires": { + "@types/node": "^14.14.31", + "@wdio/types": "7.7.3", + "fs-extra": "^10.0.0" + } + }, + "@wdio/runner": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.3.tgz", + "integrity": "sha512-Jetud2znIkY70lrYvHoyBQVRrIQCzNlfjLpCMMraTeNlCzW3eO82TgnOwpCoJ5cJEg78n8YLIDRcIeZ5yo4asA==", + "dev": true, + "requires": { + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "deepmerge": "^4.0.0", + "gaze": "^1.1.2", + "webdriver": "7.7.3", + "webdriverio": "7.7.3" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/spec-reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", + "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", + "dev": true, + "requires": { + "@types/easy-table": "^0.0.32", + "@wdio/reporter": "7.7.3", + "@wdio/types": "7.7.3", + "chalk": "^4.0.0", + "easy-table": "^1.1.1", + "pretty-ms": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@wdio/sync": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.3.tgz", + "integrity": "sha512-LsI9rvxup6mlMuRCDBrjh674bQt4Rnpzf/xa2obhn3GZL97teSwF5ZaTTeF+cs+MPylqwbHiY7iK+roaubqECw==", + "dev": true, + "requires": { + "@types/fibers": "^3.1.0", + "@types/puppeteer": "^5.4.0", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "fibers": "^5.0.0", + "webdriverio": "7.7.3" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/types": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", + "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", + "dev": true, + "requires": { + "@types/node": "^14.14.31", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", + "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", + "dev": true, + "requires": { + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dev": true, + "requires": { + "acorn": "^4.0.3" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", + "dev": true + }, + "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 + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + }, + "dependencies": { + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", + "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.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" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": 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==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "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==", + "dev": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.0", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.0.0", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "dependencies": { + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "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" + } + }, + "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 + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "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=", + "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=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "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=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + } + }, + "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=", + "dev": true + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true + }, + "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=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "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" - } - } + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "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" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "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-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + } + } + }, + "babelify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", + "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", + "dev": true, + "requires": {} + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "is-descriptor": "^1.0.0" } }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } + "kind-of": "^6.0.0" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "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" - } - } + "kind-of": "^6.0.0" } }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "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=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "binaryextensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", + "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", + "dev": true + }, + "body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", + "dev": true, + "requires": { + "continuable-cache": "^0.3.1", + "error": "^7.0.0", + "raw-body": "~1.1.0", + "safe-json-parse": "~1.0.1" + }, + "dependencies": { + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "dev": true }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "raw-body": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", + "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", "dev": true, - "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "bytes": "1", + "string_decoder": "0.10" } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "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 - }, - "get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + } + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "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=", - "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==", + } + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", + "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "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=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "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": { - "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" - } - } - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "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" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "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==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "browserstack": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", + "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + }, + "dependencies": { + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "es6-promisify": "^5.0.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=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "ms": "^2.1.1" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "pify": "^3.0.0" + "agent-base": "^4.3.0", + "debug": "^3.1.0" } }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "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 - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + } + } + }, + "browserstack-local": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.5.tgz", + "integrity": "sha512-0/VdSv2YVXmcnwBb64XThMvjM1HnZJnPdv7CUgQbC5y/N9Wsr0Fu+j1oknE9fC/VPx9CpoSC6CJ0kza42skMSA==", + "dev": true, + "requires": { + "https-proxy-agent": "^4.0.0", + "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==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1", + "unzipper": "^0.9.3" + }, + "dependencies": { + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "es6-promisify": "^5.0.0" } }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" + "ms": "^2.1.1" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "agent-base": "^4.3.0", + "debug": "^3.1.0" } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } + "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 + } + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.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=", + "dev": true + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", + "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cac": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", + "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", + "dev": true, + "requires": { + "camelcase-keys": "^3.0.0", + "chalk": "^1.1.3", + "indent-string": "^3.0.0", + "minimist": "^1.2.0", + "read-pkg-up": "^1.0.1", + "suffix": "^0.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "strip-ansi": { @@ -7276,228 +31765,368 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", + "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + } + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + }, + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "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" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "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" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } } } }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "caniuse-lite": { + "version": "1.0.30001235", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", + "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" } }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", "dev": true }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "character-entities-html4": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", + "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "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": { - "webidl-conversions": "^4.0.2" + "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" } }, - "dset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dset/-/dset-2.0.1.tgz", - "integrity": "sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ==" + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true }, - "duplexer": { - "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "chrome-launcher": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.14.0.tgz", + "integrity": "sha512-W//HpflaW6qBGrmuskup7g+XJZN6w03ko9QSIe5CtcTal2u0up5SeReK3Ll1Why4Ey8dPkv8XSodZyHPnGbVHQ==", + "dev": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "is-descriptor": "^0.1.0" } } } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", "dev": true }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "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==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "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=", + "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=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "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=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "requires": { - "end-of-stream": "^1.0.0", "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" }, "dependencies": { "readable-stream": { @@ -7517,1413 +32146,1878 @@ } } }, - "each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, - "easy-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", - "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "ansi-regex": "^3.0.0", - "wcwidth": ">=1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "color-name": "1.1.3" } }, - "editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true }, - "ejs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", - "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", - "dev": true, - "requires": { - "jake": "^10.6.1" - } + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true }, - "electron-to-chromium": { - "version": "1.3.505", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.505.tgz", - "integrity": "sha512-Aunrp3HWtmdiJLIl+IPSFtEvJ/4Q9a3eKaxmzCthaZF1gbTbpHUTCU2zOVnFPH7r/AD7zQXyuFidYXzSHXBdsw==", + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } + "delayed-stream": "~1.0.0" } }, - "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==", + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", "dev": true }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "requires": { - "once": "^1.4.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compress-commons": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, "requires": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" }, "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } } } }, - "engine.io-client": { - "version": "3.2.1", - "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" + "source-map": "^0.6.1" }, "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "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 - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } } } }, - "engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" } }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "connect-livereload": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", + "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", + "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", "dev": true, + "optional": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" + "bluebird": "^3.7.2" } }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "requires": { - "prr": "~1.0.1" + "safe-buffer": "5.1.2" } }, - "error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "requires": { - "string-template": "~0.2.1" - } + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } + "continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "dev": true }, - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "conventional-changelog": { + "version": "3.1.24", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", + "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", "dev": true, "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "dependencies": { - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - } + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" } }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", "dev": true, "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "compare-func": "^2.0.0", + "q": "^1.5.1" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "q": "^1.5.1" } }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "q": "^1.5.1" } }, - "es5-shim": { - "version": "4.5.14", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", - "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", + "conventional-changelog-config-spec": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", + "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", "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=", + "conventional-changelog-conventionalcommits": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", + "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz", + "integrity": "sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^4.0.18", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "shelljs": "^0.8.3", + "through2": "^4.0.0" + }, + "dependencies": { + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "q": "^1.5.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", "dev": true, "requires": { - "es6-promise": "^4.0.3" + "q": "^1.5.1" } }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - }, - "dependencies": { - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - } + "q": "^1.5.1" } }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", "dev": true, "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "q": "^1.5.1" } }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "compare-func": "^2.0.0", + "q": "^1.5.1" } }, - "escalade": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "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=", + "conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", "dev": true }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "conventional-changelog-writer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", + "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", "dev": true, "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.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==", + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, - "optional": true - } - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint": { - "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true }, - "ansi-regex": { + "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" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "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" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.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" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "min-indent": "^1.0.0" } }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", + "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", + "dev": true, + "requires": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" } }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "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": { - "ms": "^2.1.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", "dev": true, "requires": { - "esutils": "^2.0.2" + "lru-cache": "^6.0.0" } }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "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": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" + "p-locate": "^4.1.0" } }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "yallist": "^4.0.0" } }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" } }, - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true + "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" + } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { - "minimist": "^1.2.5" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "lru-cache": "^6.0.0" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "min-indent": "^1.0.0" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, - "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=", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "conventional-recommended-bump": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", + "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", "dev": true, "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.3.4", + "conventional-commits-filter": "^2.0.7", + "conventional-commits-parser": "^3.2.0", + "git-raw-commits": "^2.0.8", + "git-semver-tags": "^4.1.1", + "meow": "^8.0.0", + "q": "^1.5.1" }, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" } }, - "locate-path": { + "concat-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "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-try": "^1.0.0" + "p-locate": "^4.1.0" } }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "yallist": "^4.0.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=", - "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=", + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", "dev": true }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "requires": { - "find-up": "^2.1.0" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" } - } - } - }, - "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "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": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "p-limit": "^2.2.0" } }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "requires": { - "p-try": "^1.0.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } } }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { + "redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "requires": { - "pify": "^2.0.0" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "lru-cache": "^6.0.0" } }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "min-indent": "^1.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, - "eslint-plugin-node": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz", - "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==", + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { - "ignore": "^3.3.6", - "minimatch": "^3.0.4", - "resolve": "^1.3.3", - "semver": "5.3.0" + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-js": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", + "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==" + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" }, "dependencies": { "semver": { - "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-js-pure": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", + "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==" + }, + "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=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "coveralls": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", + "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", + "dev": true, + "requires": { + "js-yaml": "^3.13.1", + "lcov-parse": "^1.0.0", + "log-driver": "^1.2.7", + "minimist": "^1.2.5", + "request": "^2.88.2" + } + }, + "crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "dev": true, + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, + "crc32-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", + "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true } } }, - "eslint-plugin-prebid": { - "version": "file:plugins/eslint", - "dev": true + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } }, - "eslint-plugin-promise": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", - "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", - "dev": true + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } }, - "eslint-plugin-standard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", - "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true + "criteo-direct-rsa-validate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/criteo-direct-rsa-validate/-/criteo-direct-rsa-validate-1.1.0.tgz", + "integrity": "sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q==" }, - "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "lru-cache": "^4.0.1", + "which": "^1.2.9" } }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "3.5.4", - "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", "dev": true, "requires": { - "estraverse": "^5.1.0" + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" }, "dependencies": { - "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } } } }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "css-shorthand-properties": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", + "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", "dev": true }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=", "dev": true }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "optional": true }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "csstype": { + "version": "2.6.17", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", + "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==", "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } + "optional": true, + "peer": true }, - "event-stream": { - "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "array-find-index": "^1.0.1" } }, - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", - "dev": true - }, - "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "dev": true, "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "es5-ext": "^0.10.50", + "type": "^1.0.1" } }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } + "assert-plus": "^1.0.0" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "date-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", "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=", + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "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" - } - } + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" } }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", "dev": true, "requires": { - "fill-range": "^2.1.0" + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" }, "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "ms": "^2.1.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==", + "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 - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "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" - } } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" } }, - "expect": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.1.0.tgz", - "integrity": "sha512-QbH4LZXDsno9AACrN9eM0zfnby9G+OsdNgZUohjg/P0mLy1O+/bzTAJGT6VSIjVCe8yKM6SzEl/ckEOFBT7Vnw==", + "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=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "requires": { - "@jest/types": "^26.1.0", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.1.0", - "jest-message-util": "^26.1.0", - "jest-regex-util": "^26.0.0" + "mimic-response": "^3.1.0" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "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==", + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true } } }, - "expect-webdriverio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-1.2.0.tgz", - "integrity": "sha512-nis1EL4LJSKvhqES6ojx1QqAZYtWAUHaVtwilXBXJELN2YZhwOcrfBT0AvxDvJXYKzgDDTED9STc9MwcK8KbYg==", + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "expect": "^26.0.1", - "jest-matcher-utils": "^26.0.1" - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "type-detect": "^4.0.0" } }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "deep-equal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", + "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", "dev": true, "requires": { - "type": "^2.0.0" + "es-abstract": "^1.17.5", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.0.5", + "isarray": "^2.0.5", + "object-is": "^1.1.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" }, "dependencies": { - "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "kind-of": "^5.0.2" }, "dependencies": { - "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": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "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==", "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "object-keys": "^1.0.12" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "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-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", @@ -8955,204 +34049,320 @@ } } }, - "extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, - "faker": { - "version": "3.1.0", - "resolved": "http://registry.npmjs.org/faker/-/faker-3.1.0.tgz", - "integrity": "sha1-D5CPr05uwCUk5UpX5DLFwBPgjJ8=", + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "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=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "fibers": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-4.0.3.tgz", - "integrity": "sha512-MW5VrDtTOLpKK7lzw4qD7Z9tXaAhdOmOED5RHzg3+HjUk+ibkjVW0Py2ERtdqgTXaerLkVkBy2AEmJiT6RMyzg==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3" - } + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "filelist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", - "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" + "devtools": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.7.3.tgz", + "integrity": "sha512-MvLCrJqLJXPK+N1En01EdM8wpitQiKaXGlprSsWMZWJU5iy/ljOKF9KRYGf0eQ2ZT3FfjcBgJh4yyLom7wVYeg==", + "dev": true, + "requires": { + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "chrome-launcher": "^0.14.0", + "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" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "devtools-protocol": { + "version": "0.0.887710", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.887710.tgz", + "integrity": "sha512-ZN57GSHgIoz6opBE4XtUhZvCG4Mjy6n0WxUCcSv8fdHc1TDRlI8IglTzwNMXUKqehFSIEHVxKZcaAoACKWHFBQ==", "dev": true }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.1.tgz", + "integrity": "sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", - "dev": true + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "doctrine": { + "version": "1.5.0", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "to-regex-range": "^5.0.1" + "esutils": "^2.0.2", + "isarray": "^1.0.0" } }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "doctrine-temporary-fork": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.1.0.tgz", + "integrity": "sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA==", + "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" + "esutils": "^2.0.2" } }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "documentation": { + "version": "13.2.5", + "resolved": "https://registry.npmjs.org/documentation/-/documentation-13.2.5.tgz", + "integrity": "sha512-d1TrfrHXYZR63xrOzkYwwe297vkSwBoEhyyMBOi20T+7Ohe1aX1dW4nqXncQmdmE5MxluSaxxa3BW1dCvbF5AQ==", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "@babel/core": "7.12.3", + "@babel/generator": "7.12.1", + "@babel/parser": "7.12.3", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "@vue/compiler-sfc": "^3.0.11", + "ansi-html": "^0.0.7", + "babelify": "^10.0.0", + "chalk": "^2.3.0", + "chokidar": "^3.4.0", + "concat-stream": "^1.6.0", + "diff": "^4.0.1", + "doctrine-temporary-fork": "2.1.0", + "get-port": "^5.0.0", + "git-url-parse": "^11.1.2", + "github-slugger": "1.2.0", + "glob": "^7.1.2", + "globals-docs": "^2.4.0", + "highlight.js": "^10.7.2", + "ini": "^1.3.5", + "js-yaml": "^3.10.0", + "lodash": "^4.17.10", + "mdast-util-find-and-replace": "^1.1.1", + "mdast-util-inject": "^1.1.0", + "micromatch": "^3.1.5", + "mime": "^2.2.0", + "module-deps-sortable": "^5.0.3", + "parse-filepath": "^1.0.2", + "pify": "^5.0.0", + "read-pkg-up": "^4.0.0", + "remark": "^13.0.0", + "remark-gfm": "^1.0.0", + "remark-html": "^13.0.1", + "remark-reference-links": "^5.0.0", + "remark-toc": "^7.2.0", + "resolve": "^1.8.1", + "stream-array": "^1.1.2", + "strip-json-comments": "^2.0.1", + "tiny-lr": "^1.1.0", + "unist-builder": "^2.0.3", + "unist-util-visit": "^2.0.3", + "vfile": "^4.0.0", + "vfile-reporter": "^6.0.0", + "vfile-sort": "^2.1.0", + "vinyl": "^2.1.0", + "vinyl-fs": "^3.0.2", + "vue-template-compiler": "^2.6.12", + "yargs": "^15.3.1" }, "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" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.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 + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -9162,6 +34372,26 @@ "locate-path": "^3.0.0" } }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -9172,6 +34402,12 @@ "path-exists": "^3.0.0" } }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -9181,221 +34417,253 @@ "p-limit": "^2.0.0" } }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, - "pkg-dir": { + "path-type": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "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" - } - }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "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" + "pify": "^3.0.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" - } + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true } } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "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" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.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==", + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "kind-of": "^3.0.2" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "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": { - "is-buffer": "^1.1.5" + "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-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" } + }, + "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 } } }, - "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": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" } }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { - "is-buffer": "~2.0.3" + "is-obj": "^2.0.0" } }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dotgitignore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", + "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "find-up": "^3.0.0", + "minimatch": "^3.0.4" }, "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "glob": "^7.1.3" + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true } } }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dset/-/dset-2.0.1.tgz", + "integrity": "sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ==" + }, + "duplexer": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "readable-stream": "^2.0.2" }, "dependencies": { "readable-stream": { @@ -9406,104 +34674,25 @@ "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "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=", - "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=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "fork-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", - "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, "requires": { + "end-of-stream": "^1.0.0", "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" }, "dependencies": { "readable-stream": { @@ -9523,1327 +34712,1827 @@ } } }, - "fs-access": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { - "null-check": "^1.0.0" + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "easy-table": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", + "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", "dev": true, "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "ansi-regex": "^3.0.0", + "wcwidth": ">=1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } } }, - "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=", + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "fs.extra": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", - "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", + "edge-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-2.2.1.tgz", + "integrity": "sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw==", "dev": true, "requires": { - "fs-extra": "~0.6.1", - "mkdirp": "~0.3.5", - "walk": "^2.3.9" + "@types/which": "^1.3.2", + "which": "^2.0.2" }, "dependencies": { - "fs-extra": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", - "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "jsonfile": "~1.0.1", - "mkdirp": "0.3.x", - "ncp": "~0.4.2", - "rimraf": "~2.2.0" + "isexe": "^2.0.0" } - }, - "jsonfile": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", - "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", + } + } + }, + "editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "ejs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", + "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", + "dev": true, + "requires": { + "jake": "^10.6.1" + } + }, + "electron-to-chromium": { + "version": "1.3.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", + "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true }, - "mkdirp": { - "version": "0.3.5", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", + "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.0", + "ws": "~7.4.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "dev": true }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "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" + } + }, + "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 } } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "dev": true, + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", "dev": true }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, - "optional": true + "requires": { + "prr": "~1.0.1" + } }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "requires": { + "string-template": "~0.2.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" }, "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==", + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { - "minimist": "^1.2.5" + "has-symbols": "^1.0.1" } } } }, - "fun-hooks": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.9.tgz", - "integrity": "sha512-821UhoYfO9Sg01wAl/QsDRB088BW0aeOqzC1PXLxSlB+kaUVbK+Vp6wMDHU5huZZopYxmMmv5jDkEYqDpK3hqg==", + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, "requires": { - "typescript-tuple": "^2.2.1" + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "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=", - "dev": true - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { - "globule": "^1.0.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "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=", - "dev": true + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } }, - "get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "es5-shim": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", + "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", "dev": true }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "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=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "pump": "^3.0.0" + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "es6-promisify": { + "version": "5.0.0", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "es6-promise": "^4.0.3" } }, - "git-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-2.1.0.tgz", - "integrity": "sha512-MJgwfcSd9qxgDyEYpRU/CDxNpUadrK80JHuEQDG4Urn0m7tpSOgCBrtiSIa9S9KH8Tbuo/TN8SSQmJBvsw1HkA==", + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^3.0.2" + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } } }, - "git-url-parse": { - "version": "8.3.1", - "resolved": "http://registry.npmjs.org/git-url-parse/-/git-url-parse-8.3.1.tgz", - "integrity": "sha512-r/FxXIdfgdSO+V2zl4ZK1JGYkHT9nqVRSzom5WsYPLg3XzeBeKPl3R/6X9E9ZJRx/sE/dXwXtfl+Zp7YL8ktWQ==", + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "git-up": "^2.0.0", - "parse-domain": "^2.0.0" + "d": "^1.0.1", + "ext": "^1.1.2" } }, - "github-slugger": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", - "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, "requires": { - "emoji-regex": ">=6.0.0 <=6.1.1" - }, - "dependencies": { - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - } + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" } }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "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" + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { - "glob-parent": { + "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", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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 + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "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" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "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 + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "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 + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "is-glob": "^2.0.0" + "shebang-regex": "^3.0.0" } }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "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": { - "is-extglob": "^1.0.0" + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "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=", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "debug": "^2.6.9", + "resolve": "^1.13.1" } }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", "dev": true, "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" }, "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "locate-path": "^2.0.0" } }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "p-try": "^1.0.0" } - } - } - }, - "glob-watcher": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", - "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" } }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "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" - } - } + "find-up": "^2.1.0" } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "locate-path": "^2.0.0" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "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" - } - } + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" } }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, - "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "p-try": "^1.0.0" } }, - "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=", + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "p-limit": "^1.1.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==", + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, - "is-number": { + "path-exists": { "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": { - "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" - } - } - } + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "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" + "pify": "^2.0.0" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" } }, - "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" - } + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true } } }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "eslint-plugin-prebid": { + "version": "file:plugins/eslint" + }, + "eslint-plugin-promise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", + "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", + "dev": true, + "requires": {} + }, + "eslint-plugin-standard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", + "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } }, - "globals-docs": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.4.1.tgz", - "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==", + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, - "globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "requires": {} + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, - "glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, - "got": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/got/-/got-11.5.1.tgz", - "integrity": "sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==", + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "@sindresorhus/is": "^3.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.0", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "optional": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", "dev": true }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", "dev": true }, - "gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.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==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "ansi-wrap": "^0.1.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "path-key": "^3.0.0" } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" + "shebang-regex": "^3.0.0" } }, - "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=", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "isexe": "^2.0.0" } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + } + } + }, + "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=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "lcid": "^1.0.0" + "is-descriptor": "^0.1.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=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "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": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "is-extendable": "^0.1.0" } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "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 }, - "wrap-ansi": { + "is-number": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "kind-of": "^3.0.2" } }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "5.0.0-security.0" + "isarray": "1.0.0" } }, - "yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "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": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" + "is-buffer": "^1.1.5" } } } }, - "gulp-clean": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.3.2.tgz", - "integrity": "sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI=", + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "gulp-util": "^2.2.14", - "rimraf": "^2.2.8", - "through2": "^0.4.2" + "homedir-polyfill": "^1.0.1" + } + }, + "expect": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.2.tgz", + "integrity": "sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w==", + "dev": true, + "requires": { + "@jest/types": "^27.0.2", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.0.1", + "jest-matcher-utils": "^27.0.2", + "jest-message-util": "^27.0.2", + "jest-regex-util": "^27.0.1" }, "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", - "dev": true - }, "ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true - }, - "chalk": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + } + } + }, + "expect-webdriverio": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-3.1.0.tgz", + "integrity": "sha512-Kn4Rtu5vKbDo95WNcjZ9XVz/qTPGZzumP9w7VSV4OxY5z6BAqSI2jb85EsqPxpavs33P+9Qse4Z+d5ilDD/dQw==", + "dev": true, + "requires": { + "expect": "^27.0.2", + "jest-matcher-utils": "^27.0.2" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "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": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" + "is-plain-object": "^2.0.4" } - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "gulp-util": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", - "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" - }, - "dependencies": { - "through2": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - } + "is-descriptor": "^1.0.0" } }, - "has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "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": { - "ansi-regex": "^0.2.0" + "is-extendable": "^0.1.0" } }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "minimist": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", - "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", - "dev": true - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "kind-of": "^6.0.0" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "strip-ansi": { - "version": "0.3.0", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "ansi-regex": "^0.2.1" + "kind-of": "^6.0.0" } }, - "supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", - "dev": true - }, - "through2": { - "version": "0.4.2", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - }, - "dependencies": { - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } - } + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } - }, - "vinyl": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", - "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "clone-stats": "~0.0.1" + "ms": "2.1.2" } }, - "xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "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 } } }, - "gulp-concat": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", - "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", + "dev": true + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "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=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fibers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.0.tgz", + "integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", "dev": true, "requires": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" + "minimatch": "^3.0.4" } }, - "gulp-connect": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "ansi-colors": "^2.0.5", - "connect": "^3.6.6", - "connect-livereload": "^0.6.0", - "fancy-log": "^1.3.2", - "map-stream": "^0.0.7", - "send": "^0.16.2", - "serve-index": "^1.9.1", - "serve-static": "^1.13.2", - "tiny-lr": "^1.1.1" - }, - "dependencies": { - "ansi-colors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", - "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=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } + "to-regex-range": "^5.0.1" } }, - "gulp-eslint": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-4.0.2.tgz", - "integrity": "sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg==", - "dev": true, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", + "dev": true + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "requires": { - "eslint": "^4.0.0", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.0" + "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" } }, - "gulp-footer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gulp-footer/-/gulp-footer-2.0.2.tgz", - "integrity": "sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g==", + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, "requires": { - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.6.2", - "map-stream": "0.0.7" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" }, "dependencies": { - "lodash._reinterpolate": { + "find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "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=", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "locate-path": "^3.0.0" } }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "p-limit": "^2.0.0" } }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "find-up": "^3.0.0" } - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true } } }, - "gulp-header": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", - "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "concat-with-sourcemaps": "*", - "lodash.template": "^4.4.0", - "through2": "^2.0.0" - }, - "dependencies": { - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - } + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "gulp-if": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-2.0.2.tgz", - "integrity": "sha1-pJe351cwBQQcqivIt92jyARE1ik=", + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { - "gulp-match": "^1.0.3", - "ternary-stream": "^2.0.1", - "through2": "^2.0.1" + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" } }, - "gulp-js-escape": { + "flagged-respawn": { "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=", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "through2": "^0.6.3" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" }, "dependencies": { - "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.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "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": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.6.5", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "glob": "^7.1.3" } } } }, - "gulp-match": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", - "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", - "dev": true, - "requires": { - "minimatch": "^3.0.3" - } + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true }, - "gulp-replace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", - "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "requires": { - "istextorbinary": "2.2.1", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" }, "dependencies": { "readable-stream": { @@ -10863,1886 +36552,2053 @@ } } }, - "gulp-shell": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.5.2.tgz", - "integrity": "sha1-pJWcoGUa0ce7/nCy0K27tOGuqY0=", + "follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "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=", "dev": true, "requires": { - "async": "^1.5.0", - "gulp-util": "^3.0.7", - "lodash": "^4.0.0", - "through2": "^2.0.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } + "for-in": "^1.0.1" } }, - "gulp-sourcemaps": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "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=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { - "@gulp-sourcemaps/identity-map": "1.X", - "@gulp-sourcemaps/map-sources": "1.X", - "acorn": "5.X", - "convert-source-map": "1.X", - "css": "2.X", - "debug-fabulous": "1.X", - "detect-newline": "2.X", - "graceful-fs": "4.X", - "source-map": "~0.6.0", - "strip-bom-string": "1.X", - "through2": "2.X" - }, - "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 - } + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" } }, - "gulp-uglify": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.2.tgz", - "integrity": "sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg==", + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "array-each": "^1.0.1", - "extend-shallow": "^3.0.2", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "isobject": "^3.0.1", - "make-error-cause": "^1.1.1", - "safe-buffer": "^5.1.2", - "through2": "^2.0.0", - "uglify-js": "^3.0.5", - "vinyl-sourcemaps-apply": "^0.2.0" + "map-cache": "^0.2.2" } }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "dev": true, - "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "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 - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", - "dev": true - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "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.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "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=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } - }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + } + } + }, + "fs.extra": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", + "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", + "dev": true, + "requires": { + "fs-extra": "~0.6.1", + "mkdirp": "~0.3.5", + "walk": "^2.3.9" + }, + "dependencies": { + "fs-extra": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", + "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "jsonfile": "~1.0.1", + "mkdirp": "0.3.x", + "ncp": "~0.4.2", + "rimraf": "~2.2.0" } }, - "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=", + "jsonfile": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", + "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", "dev": true }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "mkdirp": { + "version": "0.3.5", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", "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=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", "dev": true - }, - "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", - "dev": true, - "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - } } } }, - "gulplog": { + "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "requires": { - "glogg": "^1.0.0" - } + "optional": true }, - "gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" }, "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "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==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } } } }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, + "fun-hooks": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.9.tgz", + "integrity": "sha512-821UhoYfO9Sg01wAl/QsDRB088BW0aeOqzC1PXLxSlB+kaUVbK+Vp6wMDHU5huZZopYxmMmv5jDkEYqDpK3hqg==", "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.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 - } + "typescript-tuple": "^2.2.1" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "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=", + "dev": true + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "globule": "^1.0.0" + } + }, + "generic-names": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", + "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", + "dev": true, + "optional": true, + "requires": { + "loader-utils": "^1.1.0" }, "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": 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, + "optional": 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" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" } } } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true }, - "has-ansi": { + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "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==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" } }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", "dev": true, "requires": { - "isarray": "2.0.1" + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" }, "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } } } }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", "dev": true }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, - "has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "pump": "^3.0.0" } }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "git-raw-commits": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", + "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", "dev": true, "requires": { - "has-symbol-support-x": "^1.4.1" + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "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" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "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" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "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" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" } }, - "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=", + "git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "meow": "^8.0.0", + "semver": "^6.0.0" }, "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==", + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "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" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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": { - "kind-of": "^3.0.2" + "p-locate": "^4.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "lru-cache": "^6.0.0" } } } }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "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": { - "is-buffer": "^1.1.5" + "p-limit": "^2.2.0" } - } - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, - "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==", + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hast-util-is-element": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.4.tgz", - "integrity": "sha512-NFR6ljJRvDcyPP5SbV7MyPBgF47X3BsskLnmw1U34yL+X6YC0MoBx9EyMg8Jtx4FzGH95jw8+c1VPLHaRA0wDQ==", - "dev": true - }, - "hast-util-sanitize": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.3.1.tgz", - "integrity": "sha512-AIeKHuHx0Wk45nSkGVa2/ujQYTksnDl8gmmKo/mwQi7ag7IBZ8cM3nJ2G86SajbjGP/HRpud6kMkPtcM2i0Tlw==", - "dev": true, - "requires": { - "xtend": "^4.0.1" - } - }, - "hast-util-to-html": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-3.1.0.tgz", - "integrity": "sha1-iCyZhJ5AEw6ZHAQuRW1FPZXDbP8=", - "dev": true, - "requires": { - "ccount": "^1.0.0", - "comma-separated-tokens": "^1.0.1", - "hast-util-is-element": "^1.0.0", - "hast-util-whitespace": "^1.0.0", - "html-void-elements": "^1.0.0", - "kebab-case": "^1.0.0", - "property-information": "^3.1.0", - "space-separated-tokens": "^1.0.0", - "stringify-entities": "^1.0.1", - "unist-util-is": "^2.0.0", - "xtend": "^4.0.1" - }, - "dependencies": { - "unist-util-is": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz", - "integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==", + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, - "hast-util-whitespace": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", - "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "highlight.js": { - "version": "9.18.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", - "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "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=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "git-up": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", + "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" } }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "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 - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "git-url-parse": { + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz", + "integrity": "sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "html-void-elements": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", - "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "git-up": "^4.0.0" } }, - "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", "dev": true, "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "ini": "^1.3.2" } }, - "http-signature": { + "github-slugger": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", + "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "emoji-regex": ">=6.0.0 <=6.1.1" } }, - "http2-wrapper": { - "version": "1.0.0-beta.5.2", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", - "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "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" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "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==", + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "agent-base": "5", - "debug": "4" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "ms": "^2.1.1" + "is-glob": "^2.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } } } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "is-glob": "^4.0.1" } }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "p-limit": "^2.0.0" + "is-extglob": "^2.1.0" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "find-up": "^3.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } } } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "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": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "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" + } + } } }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.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==", + "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": { - "color-name": "~1.1.4" + "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" + } + } } }, - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, + "optional": true, "requires": { - "has-flag": "^4.0.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" } - } - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "into-stream": { - "version": "3.1.0", - "resolved": "http://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", - "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", - "dev": true, - "requires": { - "from2": "^2.1.1", - "p-is-promise": "^1.1.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "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=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "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=", - "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=", + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } - } - } - }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", - "dev": true - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "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=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { + }, + "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=", + "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 }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "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": { - "is-buffer": "^1.1.5" + "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-1.1.0.tgz", - "integrity": "sha1-8EN01O7lMQ6ajhE78UlUEeRhdqE=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.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 - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true - }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, - "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=", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true - }, - "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 - }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "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" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "requires": { - "is-unc-path": "^1.0.0" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, - "is-running": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", - "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.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": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } }, - "is-ssh": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz", - "integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==", + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "protocols": "^1.1.0" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "globals-docs": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.4.1.tgz", + "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==", "dev": true }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" } }, - "is-typed-array": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", - "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.0", - "es-abstract": "^1.17.4", - "foreach": "^2.0.5", - "has-symbols": "^1.0.1" + "sparkles": "^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=", - "dev": true - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" } }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "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=", - "dev": true - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, - "is-whitespace-character": { + "grapheme-splitter": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", - "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "is-word-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", - "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", - "dev": true + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + } }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "gulp-clean": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.3.2.tgz", + "integrity": "sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI=", "dev": true, "requires": { - "is-docker": "^2.0.0" + "gulp-util": "^2.2.14", + "rimraf": "^2.2.8", + "through2": "^0.4.2" }, "dependencies": { - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "ansi-regex": { + "version": "0.2.1", + "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "minimist": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", + "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "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": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "through2": { + "version": "0.4.2", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + }, + "dependencies": { + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "~0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", "dev": true } } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "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=", - "dev": true - }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" }, "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "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": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "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=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^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=", "dev": true }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, - "has-flag": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "which-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "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==", + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "minimist": "^1.2.5" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" } }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "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=", + "yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + } + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "optional": true, "requires": { - "amdefine": ">=0.0.4" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } } } }, - "istanbul-api": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", - "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", - "dev": true, - "requires": { - "async": "^2.1.4", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.1", - "istanbul-lib-hook": "^1.2.2", - "istanbul-lib-instrument": "^1.10.2", - "istanbul-lib-report": "^1.1.5", - "istanbul-lib-source-maps": "^1.2.6", - "istanbul-reports": "^1.5.1", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" + "gulp-connect": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", + "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", + "dev": true, + "requires": { + "ansi-colors": "^2.0.5", + "connect": "^3.6.6", + "connect-livereload": "^0.6.0", + "fancy-log": "^1.3.2", + "map-stream": "^0.0.7", + "send": "^0.16.2", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2", + "tiny-lr": "^1.1.1" }, "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "ansi-colors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", + "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", + "dev": true }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "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=", "dev": true, "requires": { - "ms": "^2.1.1" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" } }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, - "istanbul-lib-coverage": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", - "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "dev": true }, - "istanbul-lib-instrument": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", - "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.1", - "semver": "^5.3.0" + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" } }, - "istanbul-lib-report": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", - "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" - } + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true }, - "istanbul-lib-source-maps": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", - "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "gulp-eslint": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-4.0.2.tgz", + "integrity": "sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg==", + "dev": true, + "requires": { + "eslint": "^4.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.0" + }, + "dependencies": { + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true, - "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" - } + "requires": {} }, - "istanbul-reports": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", - "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", - "dev": true, - "requires": { - "handlebars": "^4.0.3" - } + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "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==", + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "minimist": "^1.2.5" + "restore-cursor": "^2.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "glob": "^7.1.3" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "ms": "^2.1.1" } - } - } - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", - "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", - "dev": true, - "requires": { - "append-transform": "^0.4.0" - } - }, - "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" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "esutils": "^2.0.2" } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + }, + "eslint": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.4", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.0.1", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" } }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "glob": "^7.1.3" + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" } }, - "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 - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0" - } - }, - "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", - "dev": true, - "requires": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" - } - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "iterate-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", - "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, - "jake": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", - "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", - "dev": true, - "requires": { - "async": "0.9.x", - "chalk": "^2.4.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - } - }, - "jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", - "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", - "dev": true, - "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "@types/yargs-parser": "*" + "escape-string-regexp": "^1.0.5" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" } }, "is-fullwidth-code-point": { @@ -12751,358 +38607,399 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "jest-cli": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", - "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "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==", "dev": true, "requires": { - "@jest/core": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^13.3.0" + "minimist": "^1.2.5" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "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 + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "mimic-fn": "^1.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "p-limit": "^2.0.0" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^3.0.0" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" } + } + } + }, + "gulp-footer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-footer/-/gulp-footer-2.0.2.tgz", + "integrity": "sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g==", + "dev": true, + "requires": { + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.6.2", + "map-stream": "0.0.7" + }, + "dependencies": { + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "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": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "lodash._root": "^3.0.0" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" } - } - } - }, - "jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "@types/yargs-parser": "*" + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" } + }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true } } }, - "jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "gulp-header": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.9.tgz", + "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" + "concat-with-sourcemaps": "^1.1.0", + "lodash.template": "^4.5.0", + "map-stream": "0.0.7", + "through2": "^2.0.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "lodash._reinterpolate": "^3.0.0" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "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" - } - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "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" - } - } + "readable-stream": "~2.3.6", + "xtend": "~4.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==", + } + } + }, + "gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "dev": true, + "requires": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + } + } + }, + "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=", + "dev": true, + "requires": { + "through2": "^0.6.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "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" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" } - }, - "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=", + } + } + }, + "gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.3" + } + }, + "gulp-replace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", + "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "dev": true, + "requires": { + "istextorbinary": "2.2.1", + "readable-stream": "^2.0.1", + "replacestream": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } } } }, - "jest-diff": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.1.0.tgz", - "integrity": "sha512-GZpIcom339y0OXznsEKjtkfKxNdg7bVbEofK8Q6MnevTIiR1jNhDWKhRX6X0SDXJlwn3dy59nZ1z55fLkAqPWg==", + "gulp-shell": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.1.0" + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -13130,465 +39027,461 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" } - } - } - }, - "jest-docblock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", - "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "lodash._reinterpolate": "^3.0.0" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "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": { - "@types/yargs-parser": "*" + "has-flag": "^4.0.0" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } } } }, - "jest-environment-jsdom": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", - "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0", - "jsdom": "^11.5.1" + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "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 + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } } } }, - "jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "gulp-terser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.0.1.tgz", + "integrity": "sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } + "plugin-error": "^1.0.1", + "terser": "5.4.0", + "through2": "^4.0.2", + "vinyl-sourcemaps-apply": "^0.2.1" } }, - "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", - "dev": true - }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "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" - } - } + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "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==", + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", "dev": true }, - "is-number": { + "lodash._reinterpolate": { "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": { - "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" - } - } - } + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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": { - "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" + "lodash._root": "^3.0.0" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" } }, - "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=", + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" } - } - } - }, - "jest-jasmine2": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", - "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.9.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "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=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", "dev": true }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "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" - } - } + "ansi-regex": "^2.0.0" } }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, - "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "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" - } - } + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" } - }, + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.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 + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + } + } + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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 + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "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=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -13615,1408 +39508,1401 @@ } } }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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==", + "dev": true + } + } + }, + "hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "optional": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hast-util-is-element": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", + "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", + "dev": true + }, + "hast-util-sanitize": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz", + "integrity": "sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA==", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "hast-util-to-html": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz", + "integrity": "sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-is-element": "^1.0.0", + "hast-util-whitespace": "^1.0.0", + "html-void-elements": "^1.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0", + "stringify-entities": "^3.0.1", + "unist-util-is": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "hast-util-whitespace": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", + "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "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 + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "agent-base": "5", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "ms": "^2.1.1" } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "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 + } + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true, + "optional": true + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "optional": true, + "requires": {} + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true - }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "inquirer": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.1.tgz", + "integrity": "sha512-hUDjc3vBkh/uk1gPfMAD/7Z188Q8cvTGl0nxwaCdwSbzFh6ZKkZh+s2ozVxbE5G9ZNRyeY0+lgbAIOUFsFf98w==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "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": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "color-convert": "^2.0.1" } }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "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" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "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": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "color-name": "~1.1.4" } }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "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 }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "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 }, - "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=", + "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": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "has-flag": "^4.0.0" } } } }, - "jest-leak-detector": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", - "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "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=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "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=", "dev": true, "requires": { - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "kind-of": "^3.0.2" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "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 }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "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": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "is-buffer": "^1.1.5" } } } }, - "jest-matcher-utils": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.1.0.tgz", - "integrity": "sha512-PW9JtItbYvES/xLn5mYxjMd+Rk+/kIt88EfH3N7w9KeOrHWaHrdYPnVHndGbsFGRJ2d5gKtwggCvkqbFDoouQA==", + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", + "dev": true + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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=", "dev": true, "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.1.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.1.0" + "kind-of": "^3.0.2" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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==", + "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 }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "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": { - "has-flag": "^4.0.0" + "is-buffer": "^1.1.5" } } } }, - "jest-message-util": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.1.0.tgz", - "integrity": "sha512-dY0+UlldiAJwNDJ08SF0HdF32g9PkbF2NRK/+2iMPU40O6q+iSn1lgog/u0UH8ksWoPv0+gNq8cjhYO2MFtT0g==", + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.1.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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==", + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.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 + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, + "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=", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, + "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 + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "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" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "@jest/types": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } + "is-unc-path": "^1.0.0" } }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, - "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==", + "is-running": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", + "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", "dev": true }, - "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, + "is-ssh": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", + "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } + "protocols": "^1.1.0" } }, - "jest-resolve-dependencies": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", - "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - } + "has-symbols": "^1.0.1" } }, - "jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-leak-detector": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "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" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "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.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "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" - } - } + "text-extensions": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "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=", + "dev": true + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" } }, - "jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "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==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "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=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^13.3.0" + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "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": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "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" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.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-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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "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": { - "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" - } - } - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "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" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, "requires": { - "p-limit": "^2.0.0" + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "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=", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "minimist": "^1.2.5" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", "dev": true, + "optional": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "amdefine": ">=0.0.4" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "has-flag": "^1.0.0" } } } }, - "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, - "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "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/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "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": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "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 }, - "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "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" - } - } + "semver": "^6.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==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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": { - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "has-flag": "^4.0.0" } - }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "ms": "2.1.2" } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - } + "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 }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "glob": "^7.1.3" } }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "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 - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "requires": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + } + }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "dev": true, + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "jest-diff": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.2.tgz", + "integrity": "sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.1", + "jest-get-type": "^27.0.1", + "pretty-format": "^27.0.2" + }, + "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": { - "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" + "color-convert": "^2.0.1" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "minimist": "^1.2.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "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": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "color-name": "~1.1.4" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "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 }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "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 }, - "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=", + "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": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "has-flag": "^4.0.0" } } } }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "jest-get-type": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.1.tgz", + "integrity": "sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg==", + "dev": true + }, + "jest-matcher-utils": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz", + "integrity": "sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "chalk": "^4.0.0", + "jest-diff": "^27.0.2", + "jest-get-type": "^27.0.1", + "pretty-format": "^27.0.2" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "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": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "color-convert": "^2.0.1" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "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": { - "minimist": "^1.2.5" + "color-name": "~1.1.4" } }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "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 }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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" + } } } }, - "jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "jest-message-util": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.2.tgz", + "integrity": "sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "leven": "^3.1.0", - "pretty-format": "^24.9.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.0.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.0.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@babel/highlight": "^7.14.5" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "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": { - "@types/yargs-parser": "*" + "color-convert": "^2.0.1" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } - } - } - }, - "jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", - "dev": true, - "requires": { - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.9.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + }, + "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": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "color-name": "~1.1.4" } }, - "@types/yargs": { - "version": "13.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz", - "integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==", + "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 + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - } - } - }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "dependencies": { "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "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": "^3.0.0" + "has-flag": "^4.0.0" } } } }, + "jest-regex-util": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.1.tgz", + "integrity": "sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -15039,51 +40925,6 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -15108,6 +40949,12 @@ "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", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -15142,13 +40989,13 @@ } }, "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" + "universalify": "^2.0.0" } }, "jsonparse": { @@ -15157,6 +41004,16 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -15187,70 +41044,128 @@ "dev": true }, "karma": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", - "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz", + "integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==", "dev": true, "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", + "body-parser": "^1.19.0", "braces": "^3.0.2", - "chokidar": "^3.0.0", - "colors": "^1.1.0", - "connect": "^3.6.0", + "chokidar": "^3.5.1", + "colors": "^1.4.0", + "connect": "^3.7.0", "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.14", - "log4js": "^4.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", - "socket.io": "2.1.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.3.0", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^3.1.0", "source-map": "^0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.28", + "yargs": "^16.1.1" }, "dependencies": { + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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" + } + }, "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } - }, - "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": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "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", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } } } }, "karma-babel-preprocessor": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-6.0.1.tgz", - "integrity": "sha1-euHT5klQ2+EfQht0BAqwj7WmbCE=", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", + "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", "dev": true, - "requires": { - "babel-core": "^6.0.0" - } + "requires": {} }, "karma-browserstack-launcher": { "version": "1.4.0", @@ -15270,12 +41185,11 @@ "dev": true }, "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "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==", "dev": true, "requires": { - "fs-access": "^1.0.0", "which": "^1.2.1" } }, @@ -15302,23 +41216,6 @@ "ms": "^2.1.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 - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, "istanbul-lib-source-maps": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", @@ -15330,61 +41227,30 @@ "source-map": "^0.6.1" } }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, "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 }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "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 - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, "karma-coverage-istanbul-reporter": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.3.tgz", - "integrity": "sha1-O13/RmT6W41RlrmInj9hwforgNk=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", "dev": true, "requires": { - "istanbul-api": "^1.3.1", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", "minimatch": "^3.0.4" } }, @@ -15398,12 +41264,24 @@ } }, "karma-firefox-launcher": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.3.0.tgz", - "integrity": "sha512-Fi7xPhwrRgr+94BnHX0F5dCl1miIW4RHnzjIGxF8GaIEp7rNqX7LSi7ok63VXs3PS/5MQaQMhGxw+bvD+pibBQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.1.tgz", + "integrity": "sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q==", "dev": true, "requires": { - "is-wsl": "^2.1.0" + "is-wsl": "^2.2.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "karma-ie-launcher": { @@ -15416,20 +41294,12 @@ } }, "karma-mocha": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz", - "integrity": "sha1-7qrH/8DiAetjxGdEDStpx883eL8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.1.tgz", + "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", "dev": true, "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "minimist": "^1.2.3" } }, "karma-mocha-reporter": { @@ -15503,9 +41373,9 @@ } }, "karma-spec-reporter": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.31.tgz", - "integrity": "sha1-SDDccUihVcfXoYbmMjOaDYD63sM=", + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", + "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", "dev": true, "requires": { "colors": "^1.1.2" @@ -15556,12 +41426,6 @@ } } }, - "kebab-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", - "integrity": "sha1-P55JkK3K0MaGwOcB92RYaPdfkes=", - "dev": true - }, "keyv": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", @@ -15577,11 +41441,15 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "konan": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/konan/-/konan-2.1.1.tgz", + "integrity": "sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg==", + "dev": true, + "requires": { + "@babel/parser": "^7.10.5", + "@babel/traverse": "^7.10.5" + } }, "last-run": { "version": "1.1.1", @@ -15649,12 +41517,6 @@ "flush-write-stream": "^1.0.2" } }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -15706,6 +41568,12 @@ "marky": "^1.2.0" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -15757,12 +41625,12 @@ } }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -15877,6 +41745,13 @@ "lodash._objecttypes": "~2.4.1" } }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true, + "optional": true + }, "lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", @@ -15942,6 +41817,12 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "dev": true + }, "lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", @@ -16000,12 +41881,6 @@ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", "dev": true }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, "lodash.template": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", @@ -16043,6 +41918,12 @@ "lodash.escape": "~2.4.1" } }, + "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=", + "dev": true + }, "lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", @@ -16071,36 +41952,94 @@ "dev": true }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^2.4.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } } }, "log4js": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", - "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", + "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", "dev": true, "requires": { - "date-format": "^2.0.0", + "date-format": "^3.0.0", "debug": "^4.1.1", - "flatted": "^2.0.0", + "flatted": "^2.0.1", "rfdc": "^1.1.4", - "streamroller": "^1.0.6" + "streamroller": "^2.2.4" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16193,6 +42132,16 @@ "es5-ext": "~0.10.2" } }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "optional": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -16211,21 +42160,6 @@ } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-error-cause": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", - "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", - "dev": true, - "requires": { - "make-error": "^1.2.0" - } - }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -16235,15 +42169,6 @@ "kind-of": "^6.0.2" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -16271,22 +42196,19 @@ "object-visit": "^1.0.0" } }, - "markdown-escapes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", - "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", - "dev": true - }, "markdown-table": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", - "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "requires": { + "repeat-string": "^1.0.0" + } }, "marky": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.1.tgz", - "integrity": "sha512-md9k+Gxa3qLH6sUKpeC2CNkJK/Ld+bEz5X96nYwloqphQE0CKCVEKco/6jxEZixinqNdz5RFi/KaCyfbMDMAXQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.2.tgz", + "integrity": "sha512-k1dB2HNeaNyORco8ulVEhctyEGkKHb2YWAhDsxeFlW2nROIirsctBYzKwwS3Vza+sKTS1zO4Z+n9/+9WbGLIxQ==", "dev": true }, "matchdep": { @@ -16296,63 +42218,11 @@ "dev": true, "requires": { "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" - }, - "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" - } - } - } - }, + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { "findup-sync": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", @@ -16365,12 +42235,6 @@ "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", @@ -16379,57 +42243,6 @@ "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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "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" - } - }, - "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" - } } } }, @@ -16450,22 +42263,105 @@ "safe-buffer": "^5.1.2" } }, - "mdast-util-compact": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz", - "integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==", + "mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", "dev": true, "requires": { - "unist-util-visit": "^1.1.0" + "unist-util-visit": "^2.0.0" } }, - "mdast-util-definitions": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz", - "integrity": "sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA==", + "mdast-util-find-and-replace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz", + "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", + "dev": true, + "requires": { + "escape-string-regexp": "^4.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, + "mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dev": true, "requires": { - "unist-util-visit": "^1.0.0" + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "dependencies": { + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true + } + } + }, + "mdast-util-gfm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz", + "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", + "dev": true, + "requires": { + "mdast-util-gfm-autolink-literal": "^0.1.0", + "mdast-util-gfm-strikethrough": "^0.2.0", + "mdast-util-gfm-table": "^0.1.0", + "mdast-util-gfm-task-list-item": "^0.1.0", + "mdast-util-to-markdown": "^0.6.1" + } + }, + "mdast-util-gfm-autolink-literal": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz", + "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "mdast-util-find-and-replace": "^1.1.0", + "micromark": "^2.11.3" + } + }, + "mdast-util-gfm-strikethrough": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz", + "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", + "dev": true, + "requires": { + "mdast-util-to-markdown": "^0.6.0" + } + }, + "mdast-util-gfm-table": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz", + "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", + "dev": true, + "requires": { + "markdown-table": "^2.0.0", + "mdast-util-to-markdown": "~0.6.0" + } + }, + "mdast-util-gfm-task-list-item": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz", + "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", + "dev": true, + "requires": { + "mdast-util-to-markdown": "~0.6.0" } }, "mdast-util-inject": { @@ -16478,22 +42374,41 @@ } }, "mdast-util-to-hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-3.0.4.tgz", - "integrity": "sha512-/eIbly2YmyVgpJNo+bFLLMCI1XgolO/Ffowhf+pHDq3X4/V6FntC9sGQCDLM147eTS+uSXv5dRzJyFn+o0tazA==", - "dev": true, - "requires": { - "collapse-white-space": "^1.0.0", - "detab": "^2.0.0", - "mdast-util-definitions": "^1.2.0", - "mdurl": "^1.0.1", - "trim": "0.0.1", - "trim-lines": "^1.0.0", - "unist-builder": "^1.0.1", - "unist-util-generated": "^1.1.0", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", + "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", "unist-util-position": "^3.0.0", - "unist-util-visit": "^1.1.0", - "xtend": "^4.0.1" + "unist-util-visit": "^2.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + }, + "dependencies": { + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true + } } }, "mdast-util-to-string": { @@ -16503,23 +42418,20 @@ "dev": true }, "mdast-util-toc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-3.1.0.tgz", - "integrity": "sha512-Za0hqL1PqWrvxGtA/3NH9D5nhGAUS9grMM4obEAz5+zsk1RIw/vWUchkaoDLNdrwk05A0CSC5eEXng36/1qE5w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-5.1.0.tgz", + "integrity": "sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g==", "dev": true, "requires": { + "@types/mdast": "^3.0.3", + "@types/unist": "^2.0.3", + "extend": "^3.0.2", "github-slugger": "^1.2.1", - "mdast-util-to-string": "^1.0.5", - "unist-util-is": "^2.1.2", - "unist-util-visit": "^1.1.0" + "mdast-util-to-string": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit": "^2.0.0" }, "dependencies": { - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - }, "github-slugger": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", @@ -16529,10 +42441,10 @@ "emoji-regex": ">=6.0.0 <=6.1.1" } }, - "unist-util-is": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz", - "integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==", + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", "dev": true } } @@ -16608,12 +42520,6 @@ } } }, - "memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", - "dev": true - }, "meow": { "version": "3.7.0", "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -16638,61 +42544,245 @@ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "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", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "requires": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + }, + "dependencies": { + "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" + } + }, + "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 + } + } + }, + "micromark-extension-gfm": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz", + "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", + "dev": true, + "requires": { + "micromark": "~2.11.0", + "micromark-extension-gfm-autolink-literal": "~0.5.0", + "micromark-extension-gfm-strikethrough": "~0.6.5", + "micromark-extension-gfm-table": "~0.4.0", + "micromark-extension-gfm-tagfilter": "~0.3.0", + "micromark-extension-gfm-task-list-item": "~0.3.0" + } + }, + "micromark-extension-gfm-autolink-literal": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz", + "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", + "dev": true, + "requires": { + "micromark": "~2.11.3" + } + }, + "micromark-extension-gfm-strikethrough": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz", + "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", + "dev": true, + "requires": { + "micromark": "~2.11.0" + } + }, + "micromark-extension-gfm-table": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz", + "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", + "dev": true, + "requires": { + "micromark": "~2.11.0" + } + }, + "micromark-extension-gfm-tagfilter": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz", + "integrity": "sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==", + "dev": true + }, + "micromark-extension-gfm-task-list-item": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz", + "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", + "dev": true, + "requires": { + "micromark": "~2.11.0" + } + }, + "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": { + "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "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" } } } }, - "merge-descriptors": { - "version": "1.0.1", - "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", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -16741,6 +42831,12 @@ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -16768,6 +42864,25 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + } + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -16804,7 +42919,7 @@ "mocha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha1-bYrlCPWRZ/lA8rWzxKYSrlDJCuY=", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "dev": true, "requires": { "browser-stdout": "1.3.1", @@ -16881,22 +42996,30 @@ } } }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, "module-deps-sortable": { - "version": "4.0.6", - "resolved": "http://registry.npmjs.org/module-deps-sortable/-/module-deps-sortable-4.0.6.tgz", - "integrity": "sha1-ElGkuixEqS32mJvQKdoSGk8hCbA=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/module-deps-sortable/-/module-deps-sortable-5.0.3.tgz", + "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", "concat-stream": "~1.5.0", "defined": "^1.0.0", - "detective": "^4.0.0", + "detective": "^5.2.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", - "parents": "^1.0.0", + "JSONStream": "^1.0.3", + "konan": "^2.1.1", "readable-stream": "^2.0.2", "resolve": "^1.1.3", + "standard-version": "^9.0.0", "stream-combiner2": "^1.1.1", "subarg": "^1.0.0", "through2": "^2.0.0", @@ -16914,9 +43037,15 @@ "typedarray": "~0.0.5" }, "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, "readable-stream": { "version": "2.0.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "requires": { @@ -16927,15 +43056,15 @@ "string_decoder": "~0.10.x", "util-deprecate": "~1.0.1" } + }, + "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 } } }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -16949,30 +43078,17 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" - }, - "dependencies": { - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } } } }, @@ -17065,6 +43181,13 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -17113,12 +43236,6 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "nise": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", @@ -17168,10 +43285,10 @@ } } }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-libs-browser": { @@ -17239,37 +43356,10 @@ } } }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - }, - "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - } - } - }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", "dev": true }, "nopt": { @@ -17300,104 +43390,18 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true - }, - "now-and-later": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", - "dev": true, - "requires": { - "once": "^1.3.2" - } - }, - "npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } + }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "requires": { + "once": "^1.3.2" } }, "npm-run-path": { @@ -17421,12 +43425,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -17439,12 +43437,6 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -17483,9 +43475,9 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", "dev": true }, "object-is": { @@ -17537,16 +43529,6 @@ "isobject": "^3.0.0" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -17633,9 +43615,9 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -17648,9 +43630,9 @@ "dev": true }, "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", + "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", "dev": true, "requires": { "is-wsl": "^1.1.0" @@ -17702,6 +43684,74 @@ "word-wrap": "~1.2.3" } }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } + } + }, "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -17734,12 +43784,6 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, - "os-homedir": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", @@ -17774,299 +43818,103 @@ "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", - "dev": true - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", - "dev": true - }, - "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-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, - "p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", - "dev": true, - "requires": { - "p-finally": "^1.0.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 - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, - "requires": { - "path-platform": "~0.11.15" - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-domain": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-2.3.4.tgz", - "integrity": "sha512-LlFJJVTry4DD3Xa76CsVNP6MIu3JZ8GXd5HEEp38KSDGBCVsnccagAJ5YLy7uEEabvwtauQEQPcvXWgUGkJbMA==", - "dev": true, - "requires": { - "got": "^8.3.2", - "jest": "^24.9.0", - "mkdirp": "^0.5.1", - "npm-run-all": "^4.1.5" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", - "dev": true - }, - "cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", - "dev": true, - "requires": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", - "dev": true - } - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "got": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" - } - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "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==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - } - }, - "p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" + "strip-eof": "^1.0.0" } }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + } + } + }, + "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=", + "dev": true + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "is-plain-obj": "^1.0.0" + "yocto-queue": "^0.1.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 + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, "parse-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", - "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dev": true, "requires": { "character-entities": "^1.0.0", @@ -18088,14 +43936,11 @@ "path-root": "^0.1.1" } }, - "parse-git-config": { - "version": "0.2.0", - "resolved": "http://registry.npmjs.org/parse-git-config/-/parse-git-config-0.2.0.tgz", - "integrity": "sha1-Jygz/dFf6hRvt10zbSNrljtv9wY=", - "dev": true, - "requires": { - "ini": "^1.3.3" - } + "parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true }, "parse-glob": { "version": "3.0.4", @@ -18154,65 +43999,48 @@ "dev": true }, "parse-path": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-3.0.4.tgz", - "integrity": "sha512-wP70vtwv2DyrM2YoA7ZHVv4zIXa4P7dGgHlj+VwyXNDduLLVJ7NMY1zsFxjUUJ3DAwJLupGb1H5gMDDiNlJaxw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", + "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", "dev": true, "requires": { "is-ssh": "^1.3.0", - "protocols": "^1.4.0" + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + }, + "dependencies": { + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + } } }, "parse-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-3.0.2.tgz", - "integrity": "sha1-YCeHpwY6eV1yuGcxl1BecvYGEL4=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.3.tgz", + "integrity": "sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A==", "dev": true, "requires": { "is-ssh": "^1.3.0", - "normalize-url": "^1.9.1", - "parse-path": "^3.0.1", + "normalize-url": "^6.0.1", + "parse-path": "^4.0.0", "protocols": "^1.4.0" }, "dependencies": { "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", + "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==", + "dev": true } } }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -18266,12 +44094,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true - }, "path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", @@ -18350,15 +44172,9 @@ "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "pify": { @@ -18382,15 +44198,6 @@ "pinkie": "^2.0.0" } }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -18398,6 +44205,36 @@ "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-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" + } + } } }, "plugin-error": { @@ -18429,30 +44266,105 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, "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=", "dev": true }, + "postcss": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.4.tgz", + "integrity": "sha512-/tZY0PXExXXnNhKv3TOvZAOUYRyuqcCbBm2c17YMDK0PlVII3K7/LKdt3ScHL+hhouddjUWi+1sKDf9xXW+8YA==", + "dev": true, + "optional": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + } + }, + "postcss-modules": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz", + "integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==", + "dev": true, + "optional": true, + "requires": { + "generic-names": "^2.0.1", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "optional": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "optional": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "optional": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "optional": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "optional": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true, + "optional": true + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", @@ -18460,40 +44372,21 @@ "dev": true }, "pretty-format": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.1.0.tgz", - "integrity": "sha512-GmeO1PEYdM+non4BKCj+XsPJjFOJIPnsLewqhDVoqY1xo0yNmDas7tC2XwpMrRAHR3MaE2hPo37deX5OisJ2Wg==", + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.2.tgz", + "integrity": "sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==", "dev": true, "requires": { - "@jest/types": "^26.1.0", + "@jest/types": "^27.0.2", "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "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==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true } } @@ -18513,10 +44406,10 @@ "parse-ms": "^2.1.0" } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", "dev": true }, "process": { @@ -18537,39 +44430,19 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "promise.allsettled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", - "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", - "dev": true, - "requires": { - "array.prototype.map": "^1.0.1", - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "iterate-value": "^1.0.0" - } - }, - "prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dev": true, "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.4" + "xtend": "^4.0.0" } }, - "property-information": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-3.2.0.tgz", - "integrity": "sha1-/RSDyPusYYCPX+NZ52k6H0ilgzE=", - "dev": true - }, "protocols": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz", - "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", "dev": true }, "proxy-addr": { @@ -18676,40 +44549,59 @@ "dev": true }, "puppeteer-core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-5.2.1.tgz", - "integrity": "sha512-gLjEOrzwgcnwRH+sm4hS1TBqe2/DN248nRb2hYB7+lZ9kCuLuACNvuzlXILlPAznU3Ob+mEvVEBDcLuFa0zq3g==", + "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.781568", + "devtools-protocol": "0.0.869402", "extract-zip": "^2.0.0", - "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", "pkg-dir": "^4.2.0", "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", + "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": { + "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" + } + }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "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 }, + "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" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -18744,14 +44636,22 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "query-selector-shadow-dom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz", + "integrity": "sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg==", + "dev": true + }, "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "dev": true, "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" } }, "querystring": { @@ -18833,9 +44733,9 @@ } }, "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, "read-pkg": { @@ -18891,22 +44791,22 @@ "util-deprecate": "^1.0.1" } }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "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==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "minimatch": "^3.0.4" } }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "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": { - "util.promisify": "^1.0.0" + "picomatch": "^2.2.1" } }, "rechoir": { @@ -19007,9 +44907,9 @@ } }, "regexpp": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "regexpu-core": { @@ -19050,110 +44950,72 @@ } }, "remark": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", - "integrity": "sha512-amw8rGdD5lHbMEakiEsllmkdBP+/KpjW/PRK6NSGPZKCQowh0BT4IWXDAkRMyG3SB9dKPXWMviFjNusXzXNn3A==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", "dev": true, "requires": { - "remark-parse": "^5.0.0", - "remark-stringify": "^5.0.0", - "unified": "^6.0.0" + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" } }, - "remark-html": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-7.0.0.tgz", - "integrity": "sha512-jqRzkZXCkM12gIY2ibMLTW41m7rfanliMTVQCFTezHJFsbH00YaTox/BX4gU+f/zCdzfhFJONtebFByvpMv37w==", + "remark-gfm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz", + "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", "dev": true, "requires": { - "hast-util-sanitize": "^1.0.0", - "hast-util-to-html": "^3.0.0", - "mdast-util-to-hast": "^3.0.0", - "xtend": "^4.0.1" + "mdast-util-gfm": "^0.1.0", + "micromark-extension-gfm": "^0.3.0" } }, - "remark-parse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "remark-html": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-13.0.1.tgz", + "integrity": "sha512-K5KQCXWVz+harnyC+UVM/J9eJWCgjYRqFeZoZf2NgP0iFbuuw/RgMZv3MA34b/OEpGnstl3oiOUtZzD3tJ+CBw==", "dev": true, "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" + "hast-util-sanitize": "^3.0.0", + "hast-util-to-html": "^7.0.0", + "mdast-util-to-hast": "^10.0.0" } }, - "remark-reference-links": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-4.0.4.tgz", - "integrity": "sha512-+2X8hwSQqxG4tvjYZNrTcEC+bXp8shQvwRGG6J/rnFTvBoU4G0BBviZoqKGZizLh/DG+0gSYhiDDWCqyxXW1iQ==", + "remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "dev": true, "requires": { - "unist-util-visit": "^1.0.0" + "mdast-util-from-markdown": "^0.8.0" } }, - "remark-slug": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.1.2.tgz", - "integrity": "sha512-DWX+Kd9iKycqyD+/B+gEFO3jjnt7Yg1O05lygYSNTe5i5PIxxxPjp5qPBDxPIzp5wreF7+1ROCwRgjEcqmzr3A==", + "remark-reference-links": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-5.0.0.tgz", + "integrity": "sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ==", "dev": true, "requires": { - "github-slugger": "^1.0.0", - "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^1.0.0" + "unist-util-visit": "^2.0.0" } }, "remark-stringify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-5.0.0.tgz", - "integrity": "sha512-Ws5MdA69ftqQ/yhRF9XhVV29mhxbfGhbz0Rx5bQH+oJcNhhSM6nCu1EpLod+DjrFGrU0BMPs+czVmJZU7xiS7w==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", "dev": true, "requires": { - "ccount": "^1.0.0", - "is-alphanumeric": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "longest-streak": "^2.0.1", - "markdown-escapes": "^1.0.0", - "markdown-table": "^1.1.0", - "mdast-util-compact": "^1.0.0", - "parse-entities": "^1.0.2", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "stringify-entities": "^1.0.1", - "unherit": "^1.0.4", - "xtend": "^4.0.1" + "mdast-util-to-markdown": "^0.6.0" } }, "remark-toc": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-5.1.1.tgz", - "integrity": "sha512-vCPW4YOsm2CfyuScdktM9KDnJXVHJsd/ZeRtst+dnBU3B3KKvt8bc+bs5syJjyptAHfqo7H+5Uhz+2blWBfwow==", - "dev": true, - "requires": { - "mdast-util-toc": "^3.0.0", - "remark-slug": "^5.0.0" - } - }, - "remote-origin-url": { - "version": "0.4.0", - "resolved": "http://registry.npmjs.org/remote-origin-url/-/remote-origin-url-0.4.0.tgz", - "integrity": "sha1-TT4pAvNOLTfRwmPYdxC3frQIajA=", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-7.2.0.tgz", + "integrity": "sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg==", "dev": true, "requires": { - "parse-git-config": "^0.2.0" + "@types/unist": "^2.0.3", + "mdast-util-toc": "^5.0.0" } }, "remove-bom-buffer": { @@ -19183,6 +45045,33 @@ "remove-bom-buffer": "^3.0.0", "safe-buffer": "^5.1.0", "through2": "^2.0.3" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } } }, "remove-trailing-separator": { @@ -19299,32 +45188,18 @@ } } }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "dev": true, - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -19333,7 +45208,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -19356,11 +45231,12 @@ "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -19370,23 +45246,6 @@ "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", "dev": true }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -19428,9 +45287,9 @@ } }, "resq": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resq/-/resq-1.7.1.tgz", - "integrity": "sha512-09u9Q5SAuJfAW5UoVAmvRtLvCOMaKP+djiixTXsZvPaojGKhuvc0Nfvp84U1rIfopJWEOXi5ywpCFwCk7mj8Xw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resq/-/resq-1.10.0.tgz", + "integrity": "sha512-hCUd0xMalqtPDz4jXIqs0M5Wnv/LZXN8h7unFOo4/nvExT9dDPbhwd3udRxLlp0HgBnHcV009UlduE9NZi7A6w==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1" @@ -19461,15 +45320,15 @@ "dev": true }, "rfdc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "rgb2hex": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.0.tgz", - "integrity": "sha512-cHdNTwmTMPu/TpP1bJfdApd6MbD+Kzi4GNnM6h35mdFChhQPSi9cAI8J7DMn5kQDKX8NuBaQXAyo360Oa7tOEA==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", + "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", "dev": true }, "right-align": { @@ -19500,12 +45359,6 @@ "inherits": "^2.0.1" } }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -19521,203 +45374,50 @@ "rx-lite-aggregates": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "*" - } - }, - "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "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==" - }, - "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=", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "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" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "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" - } - } + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "*" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "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" + } + }, + "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==" + }, + "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=", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", "dev": true }, "schema-utils": { @@ -19788,27 +45488,30 @@ } }, "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "dev": true, "requires": { - "type-fest": "^0.13.1" + "type-fest": "^0.20.2" }, "dependencies": { "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } }, "serialize-javascript": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", - "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", - "dev": true + "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" + } }, "serve-index": { "version": "1.9.1", @@ -19921,12 +45624,6 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", - "dev": true - }, "shelljs": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", @@ -19938,20 +45635,15 @@ "rechoir": "^0.6.2" } }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, "side-channel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", - "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "requires": { - "es-abstract": "^1.17.0-next.1", - "object-inspect": "^1.7.0" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" } }, "signal-exit": { @@ -19983,12 +45675,6 @@ } } }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -19996,18 +45682,38 @@ "dev": true }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "dependencies": { - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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 } } @@ -20126,118 +45832,73 @@ } }, "socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", + "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", "dev": true, "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.8", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.1", + "engine.io": "~4.1.0", + "socket.io-adapter": "~2.1.0", + "socket.io-parser": "~4.0.3" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } + }, + "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 } } }, "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", + "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", "dev": true }, - "socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, "socket.io-parser": { - "version": "3.2.0", - "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "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==", "dev": true, "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" }, "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "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 } } }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -20250,6 +45911,13 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "optional": true + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -20264,12 +45932,21 @@ } }, "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==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { - "source-map": "^0.5.6" + "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-url": { @@ -20278,6 +45955,13 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true, + "optional": true + }, "space-separated-tokens": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", @@ -20331,6 +46015,12 @@ "through": "2" } }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -20340,6 +46030,15 @@ "extend-shallow": "^3.0.0" } }, + "split2": { + "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" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -20370,9 +46069,9 @@ "dev": true }, "stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -20386,11 +46085,75 @@ } } }, - "state-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", - "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", - "dev": true + "standard-version": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.3.0.tgz", + "integrity": "sha512-cYxxKXhYfI3S9+CA84HmrJa9B88H56V5FQ302iFF2TNwJukJCNoU8FgWt+11YtwKFXRkQQFpepC2QOF7aDq2Ow==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "conventional-changelog": "3.1.24", + "conventional-changelog-config-spec": "2.1.0", + "conventional-changelog-conventionalcommits": "4.5.0", + "conventional-recommended-bump": "6.1.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "dotgitignore": "^2.1.0", + "figures": "^3.1.0", + "find-up": "^5.0.0", + "fs-access": "^1.0.1", + "git-semver-tags": "^4.0.0", + "semver": "^7.1.1", + "stringify-package": "^1.0.1", + "yargs": "^16.0.0" + }, + "dependencies": { + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } }, "static-extend": { "version": "0.1.2", @@ -20418,12 +46181,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, "stream-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/stream-array/-/stream-array-1.1.2.tgz", @@ -20574,43 +46331,38 @@ "dev": true }, "streamroller": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", - "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", "dev": true, "requires": { - "async": "^2.6.2", - "date-format": "^2.0.0", - "debug": "^3.2.6", - "fs-extra": "^7.0.1", - "lodash": "^4.17.14" + "date-format": "^2.1.0", + "debug": "^4.1.1", + "fs-extra": "^8.1.0" }, "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "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.1.2", + "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } @@ -20639,38 +46391,27 @@ } }, "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "safe-buffer": "~5.1.0" } }, + "string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "dev": true, + "optional": true + }, "string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", @@ -20678,24 +46419,22 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" - } - }, - "string.prototype.padend": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", - "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.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": { @@ -20838,27 +46577,23 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "stringify-entities": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", - "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", + "integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==", "dev": true, "requires": { "character-entities-html4": "^1.0.0", "character-entities-legacy": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "xtend": "^4.0.0" } }, + "stringify-package": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", + "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", + "dev": true + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -20889,6 +46624,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", @@ -20899,9 +46640,9 @@ } }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "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 }, "subarg": { @@ -20938,62 +46679,37 @@ "es6-symbol": "^3.1.1" } }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true } } }, @@ -21004,24 +46720,24 @@ "dev": true }, "tar-fs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", - "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "requires": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.0.0" + "tar-stream": "^2.1.4" } }, "tar-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", - "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "requires": { - "bl": "^4.0.1", + "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", @@ -21038,155 +46754,78 @@ } }, "ternary-stream": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-2.1.1.tgz", - "integrity": "sha512-j6ei9hxSoyGlqTmoMjOm+QNvUKDOIY6bNl4Uh1lhBvl6yjPW2iLqxDUYyfDPZknQ4KdRziFl+ec99iT4l7g0cw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", "dev": true, "requires": { - "duplexify": "^3.5.0", + "duplexify": "^4.1.1", "fork-stream": "^0.0.4", - "merge-stream": "^1.0.0", - "through2": "^2.0.1" + "merge-stream": "^2.0.0", + "through2": "^3.0.1" }, "dependencies": { - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } } } }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "terser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.4.0.tgz", + "integrity": "sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==", "dev": true, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "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 }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "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 } } }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -21199,12 +46838,6 @@ "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", "dev": true }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -21212,13 +46845,22 @@ "dev": true }, "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "3" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" }, "dependencies": { "readable-stream": { @@ -21235,31 +46877,25 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } } } }, - "through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", - "dev": true, - "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - } - }, "time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, "timers-browserify": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", @@ -21324,12 +46960,6 @@ "os-tmpdir": "~1.0.2" } }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -21338,13 +46968,7 @@ "requires": { "is-absolute": "^1.0.0", "is-negated-glob": "^1.0.0" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true + } }, "to-arraybuffer": { "version": "1.0.1", @@ -21412,6 +47036,33 @@ "dev": true, "requires": { "through2": "^2.0.3" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } } }, "toidentifier": { @@ -21429,49 +47080,22 @@ "punycode": "^2.1.1" } }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", "dev": true }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, - "trim-lines": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.3.tgz", - "integrity": "sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==", - "dev": true - }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, - "trim-right": { + "trim-off-newlines": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "trim-trailing-lines": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", - "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, "trough": { @@ -21516,9 +47140,9 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tty-browserify": { @@ -21564,9 +47188,9 @@ "dev": true }, "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "type-is": { @@ -21606,9 +47230,9 @@ } }, "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", + "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", "dev": true }, "uglify-js": { @@ -21682,12 +47306,6 @@ } } }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, "unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -21727,16 +47345,6 @@ "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", "dev": true }, - "unherit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", - "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", - "dev": true, - "requires": { - "inherits": "^2.0.0", - "xtend": "^4.0.0" - } - }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -21766,17 +47374,17 @@ "dev": true }, "unified": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", + "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", "dev": true, "requires": { "bail": "^1.0.0", "extend": "^3.0.0", - "is-plain-obj": "^1.1.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", "trough": "^1.0.0", - "vfile": "^2.0.0", - "x-is-string": "^0.1.0" + "vfile": "^4.0.0" } }, "union-value": { @@ -21802,24 +47410,21 @@ } }, "unist-builder": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.4.tgz", - "integrity": "sha512-v6xbUPP7ILrT15fHGrNyHc1Xda8H3xVhP7/HAIotHOhVPjH5dCXA097C3Rry1Q2O+HbOLCao4hfPB+EYEjHgVg==", - "dev": true, - "requires": { - "object-assign": "^4.1.0" - } + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "dev": true }, "unist-util-generated": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz", - "integrity": "sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", "dev": true }, "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", "dev": true }, "unist-util-position": { @@ -21828,43 +47433,40 @@ "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", "dev": true }, - "unist-util-remove-position": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", - "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", "dev": true, "requires": { - "unist-util-visit": "^1.1.0" + "@types/unist": "^2.0.2" } }, - "unist-util-stringify-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", - "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", - "dev": true - }, "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "dev": true, "requires": { - "unist-util-visit-parents": "^2.0.0" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" } }, "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", "dev": true, "requires": { - "unist-util-is": "^3.0.0" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" } }, "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "unpipe": { @@ -22007,45 +47609,12 @@ "requires-port": "^1.0.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - } - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", - "dev": true, - "requires": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - } - }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -22061,27 +47630,21 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", - "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "v8flags": { @@ -22126,101 +47689,48 @@ } }, "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dev": true, "requires": { - "is-buffer": "^1.1.4", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-message": "^1.0.0" - }, - "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 - } + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" } }, - "vfile-location": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", - "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", - "dev": true - }, "vfile-message": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", - "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", "dev": true, "requires": { - "unist-util-stringify-position": "^1.1.1" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" } }, "vfile-reporter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-4.0.0.tgz", - "integrity": "sha1-6m8K4TQvSEFXOYXgX5QXNvJ96do=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.2.tgz", + "integrity": "sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA==", "dev": true, "requires": { "repeat-string": "^1.5.0", - "string-width": "^1.0.0", - "supports-color": "^4.1.0", - "unist-util-stringify-position": "^1.0.0", + "string-width": "^4.0.0", + "supports-color": "^6.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-sort": "^2.1.2", "vfile-statistics": "^1.1.0" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "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=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } } } @@ -22298,6 +47808,16 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } } } }, @@ -22348,13 +47868,28 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", "dev": true }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "vue": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.1.tgz", + "integrity": "sha512-j9fj3PNPMxo2eqOKYjMuss9XBS8ZtmczLY3kPvjcp9d3DbhyNqLYbaMQH18+1pDIzzVvQCQBvIf774LsjjqSKA==", "dev": true, + "optional": true, + "peer": true, + "requires": { + "@vue/compiler-dom": "3.1.1", + "@vue/runtime-dom": "3.1.1", + "@vue/shared": "3.1.1" + } + }, + "vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dev": true, + "optional": true, "requires": { - "browser-process-hrtime": "^1.0.0" + "de-indent": "^1.0.2", + "he": "^1.1.0" } }, "walk": { @@ -22366,15 +47901,6 @@ "foreachasync": "^3.0.0" } }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, "watchpack": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", @@ -22388,9 +47914,9 @@ } }, "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", "dev": true, "optional": true, "requires": { @@ -22577,28 +48103,6 @@ } } }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": 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" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -22627,58 +48131,129 @@ "readable-stream": "^2.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": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "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, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webdriver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.7.3.tgz", + "integrity": "sha512-/NfeRPREZPkY7pWVvnlyE2E4cfvl+lQmu9j1vE2GDL+oBwCHn+C5Vxwag6bOiBsrKcBan05Ghhlcl/o2uw9ZUA==", + "dev": true, + "requires": { + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "got": "^11.0.2", + "lodash.merge": "^4.6.1" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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, - "optional": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "has-flag": "^4.0.0" } } } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "optional": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webdriver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-6.3.0.tgz", - "integrity": "sha512-osHp5DX8eQ76Sy6/UYoECmDnUXwLhy5tlBeJh86er7S04FLAlmEqCvYXgxTSb8YtDjh9Xt9gP768RGhxR7ik5A==", - "dev": true, - "requires": { - "@wdio/config": "6.1.14", - "@wdio/logger": "6.0.16", - "@wdio/protocols": "6.3.0", - "@wdio/utils": "6.3.0", - "got": "^11.0.2", - "lodash.merge": "^4.6.1" - } - }, "webdriverio": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-6.3.4.tgz", - "integrity": "sha512-/53xQEituEFTaJtZMgg5Uz3GXY1Otqyry0LA8dYLYUNkTK0yCa26DL4ycDnWE0i9wEYNFX6YHCgiqTJjHEjKAg==", - "dev": true, - "requires": { - "@types/puppeteer": "^3.0.1", - "@wdio/config": "6.1.14", - "@wdio/logger": "6.0.16", - "@wdio/repl": "6.3.0", - "@wdio/utils": "6.3.0", - "archiver": "^4.0.1", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.7.3.tgz", + "integrity": "sha512-3m18Ax0dKHBT7lMueUNEgYDHMRVodXokmAq/yAH+SqHFUbHdPrHOFK3d/snFZe41f6LHrLLzebVE7rvI4Zr1AA==", + "dev": true, + "requires": { + "@types/aria-query": "^4.2.1", + "@types/node": "^14.14.31", + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/protocols": "7.5.3", + "@wdio/repl": "7.7.3", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.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": "6.3.4", + "devtools": "7.7.3", + "devtools-protocol": "^0.0.887710", + "fs-extra": "^10.0.0", "get-port": "^5.1.1", "grapheme-splitter": "^1.0.2", "lodash.clonedeep": "^4.5.0", @@ -22686,19 +48261,77 @@ "lodash.isplainobject": "^4.0.6", "lodash.zip": "^4.2.0", "minimatch": "^3.0.4", - "puppeteer-core": "^5.1.0", - "resq": "^1.6.0", - "rgb2hex": "^0.2.0", - "serialize-error": "^7.0.0", - "webdriver": "6.3.0" + "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.7.3" + }, + "dependencies": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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" + } + } } }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, "webpack": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", @@ -23022,9 +48655,9 @@ } }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, "yargs": { @@ -23086,12 +48719,6 @@ "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -23114,9 +48741,9 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dev": true, "requires": { "async-limiter": "~1.0.0" @@ -24113,32 +49740,6 @@ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -24254,15 +49855,15 @@ "dev": true }, "workerpool": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", - "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -24271,12 +49872,11 @@ }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -24323,40 +49923,12 @@ } } }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", - "dev": true - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "requires": {} }, "xtend": { "version": "4.0.2", @@ -24365,9 +49937,9 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -24377,153 +49949,43 @@ "dev": true }, "yargs": { - "version": "1.3.3", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", - "integrity": "sha1-BU3oth8i7v23IHBZ6u+da4P7kxo=", - "dev": true - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "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 - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -24590,22 +50052,28 @@ "fd-slicer": "~1.1.0" } }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true }, "zip-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-3.0.1.tgz", - "integrity": "sha512-r+JdDipt93ttDjsOVPU5zaq5bAyY+3H19bDrThkvuVxC0xMQzU1PJcS6D+KrP3u96gH9XLomcHPb+2skoDjulQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", + "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "compress-commons": "^3.0.0", + "compress-commons": "^4.1.0", "readable-stream": "^3.6.0" } + }, + "zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true } } } diff --git a/package.json b/package.json index d4dc3fcbfd2..8ddffff9b46 100644 --- a/package.json +++ b/package.json @@ -28,28 +28,29 @@ "@babel/preset-env": "^7.8.4", "@jsdevtools/coverage-istanbul-loader": "^3.0.3", "@wdio/browserstack-service": "^6.1.4", - "@wdio/cli": "^6.1.5", - "@wdio/concise-reporter": "^6.1.5", - "@wdio/local-runner": "^6.1.7", - "@wdio/mocha-framework": "^6.1.6", - "@wdio/spec-reporter": "^6.1.5", - "@wdio/sync": "^6.1.5", + "@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.5.2", + "@wdio/sync": "^7.5.2", "ajv": "5.5.2", "babel-loader": "^8.0.5", "body-parser": "^1.19.0", "chai": "^4.2.0", "coveralls": "^3.1.0", "deep-equal": "^2.0.3", - "documentation": "^5.2.2", + "documentation": "^13.2.5", "es5-shim": "^4.5.14", + "eslint": "^7.27.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.20.2", - "eslint-plugin-node": "^5.1.0", + "eslint-plugin-node": "^11.1.0", "eslint-plugin-prebid": "file:./plugins/eslint", - "eslint-plugin-promise": "^3.5.0", + "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^3.0.1", - "execa": "^1.0.0", - "faker": "^3.1.0", + "execa": "^5.0.0", + "faker": "^5.5.3", "fs.extra": "^1.3.2", "gulp": "^4.0.0", "gulp-clean": "^0.3.2", @@ -57,53 +58,53 @@ "gulp-connect": "^5.7.0", "gulp-eslint": "^4.0.0", "gulp-footer": "^2.0.2", - "gulp-header": "^1.7.1", - "gulp-if": "^2.0.2", + "gulp-header": "^2.0.9", + "gulp-if": "^3.0.0", "gulp-js-escape": "^1.0.1", "gulp-replace": "^1.0.0", - "gulp-shell": "^0.5.2", - "gulp-sourcemaps": "^2.6.0", - "gulp-uglify": "^3.0.0", + "gulp-shell": "^0.8.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-terser": "^2.0.1", "gulp-util": "^3.0.0", - "is-docker": "^1.1.0", + "is-docker": "^2.2.1", "istanbul": "^0.4.5", - "karma": "^4.0.0", - "karma-babel-preprocessor": "^6.0.1", + "karma": "^6.3.2", + "karma-babel-preprocessor": "^8.0.1", "karma-browserstack-launcher": "1.4.0", "karma-chai": "^0.1.0", - "karma-chrome-launcher": "^2.2.0", + "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.1", - "karma-coverage-istanbul-reporter": "^1.3.0", + "karma-coverage-istanbul-reporter": "^3.0.3", "karma-es5-shim": "^0.0.4", - "karma-firefox-launcher": "^1.3.0", + "karma-firefox-launcher": "^2.1.0", "karma-ie-launcher": "^1.0.0", - "karma-mocha": "^1.3.0", + "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", "karma-opera-launcher": "^1.0.0", "karma-safari-launcher": "^1.0.0", "karma-script-launcher": "^1.0.0", "karma-sinon": "^1.0.5", "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "^0.0.31", + "karma-spec-reporter": "^0.0.32", "karma-webpack": "^3.0.5", "lodash": "^4.17.21", "mocha": "^5.0.0", "morgan": "^1.10.0", - "opn": "^5.4.0", + "open": "^8.2.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", - "through2": "^2.0.3", + "through2": "^4.0.2", "url-parse": "^1.0.5", - "webdriverio": "^6.1.5", + "webdriverio": "^7.6.1", "webpack": "^3.0.0", "webpack-bundle-analyzer": "^3.8.0", "webpack-stream": "^3.2.0", - "yargs": "^1.3.1" + "yargs": "^17.0.1" }, "dependencies": { "babel-plugin-transform-object-assign": "^6.22.0", - "core-js": "^3.0.0", - "core-js-pure": "^3.6.5", + "core-js": "^3.13.0", + "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", "crypto-js": "^3.3.0", "dlv": "1.1.3", diff --git a/test/spec/integration/faker/fixtures.js b/test/spec/integration/faker/fixtures.js index a11bd126d61..6a65da37d37 100644 --- a/test/spec/integration/faker/fixtures.js +++ b/test/spec/integration/faker/fixtures.js @@ -23,7 +23,7 @@ export function makeBidder(overrides = {}) { bidder: `${faker.company.bsBuzz()}Media`, params: { abc: faker.random.alphaNumeric(10), - xyz: faker.random.number({ max: 10, precision: 2 }) + xyz: faker.datatype.number({ max: 10, precision: 2 }) }, callBids: sinon.spy() }, overrides); @@ -39,4 +39,4 @@ export function makeRequest(overrides = {}) { }, overrides); } -export function randomFive() { return faker.random.number({ min: 10000, max: 99999 }); } +export function randomFive() { return faker.datatype.number({ min: 10000, max: 99999 }); } From 2e08a8aec7e22c5d287d8ea9277c04f15503522c Mon Sep 17 00:00:00 2001 From: Rob Date: Thu, 17 Jun 2021 16:10:07 +0200 Subject: [PATCH 1172/1476] Opt Out Bid Adapter: add new bid adapter (#7029) * added opt out advertising bid adapter * send entire currency object instead of just the adServerCurrency string * removed debug line * added usersync and gdpr * tweaks/cleanup * some more minor changes * try to kickoff circleci tests Co-authored-by: Chris Huie --- modules/optoutBidAdapter.js | 85 +++++++++++++++ modules/optoutBidAdapter.md | 27 +++++ test/spec/modules/optoutBidAdapter_spec.js | 115 +++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 modules/optoutBidAdapter.js create mode 100644 modules/optoutBidAdapter.md create mode 100644 test/spec/modules/optoutBidAdapter_spec.js diff --git a/modules/optoutBidAdapter.js b/modules/optoutBidAdapter.js new file mode 100644 index 00000000000..3a2e672013c --- /dev/null +++ b/modules/optoutBidAdapter.js @@ -0,0 +1,85 @@ +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'optout'; + +function getDomain(bidderRequest) { + return utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || utils.deepAccess(window, 'location.href'); +} + +function getCurrency() { + let cur = config.getConfig('currency'); + if (cur === undefined) { + cur = { + adServerCurrency: 'EUR', + granularityMultiplier: 1 + }; + } + return cur; +} + +function hasPurpose1Consent(bidderRequest) { + let result = false; + if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.apiVersion === 2) { + result = !!(utils.deepAccess(bidderRequest.gdprConsent, 'vendorData.purpose.consents.1') === true); + } + } + return result; +} + +export const spec = { + code: BIDDER_CODE, + + isBidRequestValid: function(bid) { + return !!bid.params.publisher && !!bid.params.adslot; + }, + + buildRequests: function(validBidRequests) { + return validBidRequests.map(bidRequest => { + let endPoint = 'https://adscience-nocookie.nl/prebid/display'; + let consentString = ''; + let gdpr = 0; + if (bidRequest.gdprConsent) { + gdpr = (typeof bidRequest.gdprConsent.gdprApplies === 'boolean') ? Number(bidRequest.gdprConsent.gdprApplies) : 0; + consentString = bidRequest.gdprConsent.consentString; + if (!gdpr || hasPurpose1Consent(bidRequest)) { + endPoint = 'https://prebid.adscience.nl/prebid/display'; + } + } + return { + method: 'POST', + url: endPoint, + data: { + requestId: bidRequest.bidId, + publisher: bidRequest.params.publisher, + adSlot: bidRequest.params.adslot, + cur: getCurrency(), + url: getDomain(bidRequest), + ortb2: config.getConfig('ortb2'), + consent: consentString, + gdpr: gdpr + + }, + }; + }); + }, + + interpretResponse: function (serverResponse, bidRequest) { + return serverResponse.body; + }, + + getUserSyncs: function (syncOptions, responses, gdprConsent) { + if (gdprConsent) { + let gdpr = (typeof gdprConsent.gdprApplies === 'boolean') ? Number(gdprConsent.gdprApplies) : 0; + if (syncOptions.iframeEnabled && (!gdprConsent.gdprApplies || hasPurpose1Consent({gdprConsent}))) { + return [{ + type: 'iframe', + url: 'https://umframe.adscience.nl/matching/iframe?gdpr=' + gdpr + '&gdpr_consent=' + gdprConsent.consentString + }]; + } + } + }, +}; +registerBidder(spec); diff --git a/modules/optoutBidAdapter.md b/modules/optoutBidAdapter.md new file mode 100644 index 00000000000..de70f3e3569 --- /dev/null +++ b/modules/optoutBidAdapter.md @@ -0,0 +1,27 @@ +# Overview +Module Name: Opt Out Advertising Bidder Adapter Module +Type: Bidder Adapter +Maintainer: rob@optoutadvertising.com + +# Description +Opt Out Advertising Bidder Adapter for Prebid.js. + +# Test Parameters +``` +var adUnits = [ +{ + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: 'optout', + params: { + publisher: '8', + adslot: 'prebid_demo', + } + } + ] +} +]; +``` + diff --git a/test/spec/modules/optoutBidAdapter_spec.js b/test/spec/modules/optoutBidAdapter_spec.js new file mode 100644 index 00000000000..4d7c25d12bc --- /dev/null +++ b/test/spec/modules/optoutBidAdapter_spec.js @@ -0,0 +1,115 @@ +import { expect } from 'chai'; +import { spec } from 'modules/optoutBidAdapter.js'; +import {config} from 'src/config.js'; + +describe('optoutAdapterTest', function () { + describe('bidRequestValidity', function () { + it('bidRequest with adslot param', function () { + expect(spec.isBidRequestValid({ + bidder: 'optout', + params: { + 'adslot': 'prebid_demo', + 'publisher': '8' + } + })).to.equal(true); + }); + + it('bidRequest with no adslot param', function () { + expect(spec.isBidRequestValid({ + bidder: 'optout', + params: { + 'publisher': '8' + } + })).to.equal(false); + }); + + it('bidRequest with no publisher param', function () { + expect(spec.isBidRequestValid({ + bidder: 'optout', + params: { + 'adslot': 'prebid_demo' + } + })).to.equal(false); + }); + + it('bidRequest without params', function () { + expect(spec.isBidRequestValid({ + bidder: 'optout', + params: { } + })).to.equal(false); + }); + }); + + describe('bidRequest', function () { + const bidRequests = [{ + 'bidder': 'optout', + 'params': { + 'adslot': 'prebid_demo', + 'publisher': '8' + }, + 'adUnitCode': 'aaa', + 'transactionId': '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', + 'bidId': '9304jr394ddfj', + 'bidderRequestId': '70deaff71c281d', + 'auctionId': '5c66da22-426a-4bac-b153-77360bef5337' + }, + { + 'bidder': 'optout', + 'params': { + 'adslot': 'testslot2', + 'publisher': '2' + }, + 'adUnitCode': 'bbb', + 'transactionId': '193995b4-7122-4739-959b-2463282a138b', + 'bidId': '893j4f94e8jei', + 'bidderRequestId': '70deaff71c281d', + 'gdprConsent': { + consentString: '', + gdprApplies: true, + apiVersion: 2 + }, + 'auctionId': 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' + }]; + + it('bidRequest HTTP method', function () { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('POST'); + }); + }); + + it('bidRequest url without consent', function () { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function(requestItem) { + expect(requestItem.url).to.match(new RegExp('adscience-nocookie\\.nl/prebid/display')); + }); + }); + + it('bidRequest id', function () { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.requestId).to.equal('9304jr394ddfj'); + expect(requests[1].data.requestId).to.equal('893j4f94e8jei'); + }); + + it('bidRequest with config for currency', function () { + config.setConfig({ + currency: { + adServerCurrency: 'USD', + granularityMultiplier: 1 + } + }) + + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.cur.adServerCurrency).to.equal('USD'); + expect(requests[1].data.cur.adServerCurrency).to.equal('USD'); + }); + + it('bidRequest without config for currency', function () { + config.resetConfig(); + + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.cur.adServerCurrency).to.equal('EUR'); + expect(requests[1].data.cur.adServerCurrency).to.equal('EUR'); + }); + }); +}); From 389476735967668c1383b04b8efbc2c7bb68b6a1 Mon Sep 17 00:00:00 2001 From: punkiller Date: Thu, 17 Jun 2021 08:55:03 -0700 Subject: [PATCH 1173/1476] Prioritizing PBJS priceFloors module floors over IX bidder floors (#6997) --- modules/ixBidAdapter.js | 21 +--------------- test/spec/modules/ixBidAdapter_spec.js | 33 ++++++++++++-------------- 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index dfa9d0ad768..eb6c396f742 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -210,24 +210,7 @@ function _applyFloor(bid, imp, mediaType) { } } - if (adapterFloor && moduleFloor) { - if (adapterFloor.currency !== moduleFloor.currency) { - utils.logWarn('The bid floor currency mismatch between IX params and priceFloors module config'); - return; - } - - if (adapterFloor.floor > moduleFloor.floor) { - imp.bidfloor = adapterFloor.floor; - imp.bidfloorcur = adapterFloor.currency; - imp.ext.fl = FLOOR_SOURCE.IX; - } else { - imp.bidfloor = moduleFloor.floor; - imp.bidfloorcur = moduleFloor.currency; - imp.ext.fl = FLOOR_SOURCE.PBJS; - } - return; - } - + // Prioritize module floor over bidder.param floor if (moduleFloor) { imp.bidfloor = moduleFloor.floor; imp.bidfloorcur = moduleFloor.currency; @@ -236,8 +219,6 @@ function _applyFloor(bid, imp, mediaType) { imp.bidfloor = adapterFloor.floor; imp.bidfloorcur = adapterFloor.currency; imp.ext.fl = FLOOR_SOURCE.IX; - } else { - utils.logInfo('IX Bid Adapter: No floors available, no floors applied'); } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index def1924d465..b021b56024c 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1336,7 +1336,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.ext.sid).to.equal(sidValue); }); - it('video impression has #priceFloors floors', function () { + it('video impression should contain floors from priceFloors module', function () { const bid = utils.deepClone(ONE_VIDEO[0]); const flr = 5.5 const floorInfo = {floor: flr, currency: 'USD'}; @@ -1352,7 +1352,7 @@ describe('IndexexchangeAdapter', function () { expect(imp1.ext.fl).to.equal('p'); }); - it('banner imp has floors from #priceFloors module', function () { + it('banner impression should contain floors from priceFloors module', function () { const floor300x250 = 3.25 const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]) @@ -1370,16 +1370,13 @@ describe('IndexexchangeAdapter', function () { expect(imp1.ext.fl).to.equal('p'); }); - it('ix adapter floors chosen over #priceFloors ', function () { + it('should default to ix floors when priceFloors Module is not implemented', function () { const bid = utils.deepClone(ONE_BANNER[0]); - const floorhi = 4.5 - const floorlow = 3.5 - - bid.params.bidFloor = floorhi - bid.params.bidFloorCur = 'USD' + bid.params.bidFloor = 4.5; + bid.params.bidFloorCur = 'USD'; - const floorInfo = { floor: floorlow, currency: 'USD' }; + const floorInfo = null; bid.getFloor = function () { return floorInfo; }; @@ -1387,21 +1384,21 @@ describe('IndexexchangeAdapter', function () { // check if floors are in imp const requestBidFloor = spec.buildRequests([bid])[0]; const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(floorhi); + expect(imp1.bidfloor).to.equal(bid.params.bidFloor); expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); expect(imp1.ext.fl).to.equal('x'); }); - it(' #priceFloors floors chosen over ix adapter floors', function () { + it('should prioritize Floors Module over IX param floors', function () { const bid = utils.deepClone(ONE_BANNER[0]); - const floorhi = 4.5 - const floorlow = 3.5 + const floor1 = 4.5 + const floor2 = 3.5 - bid.params.bidFloor = floorlow + bid.params.bidFloor = floor2 bid.params.bidFloorCur = 'USD' - const floorInfo = { floor: floorhi, currency: 'USD' }; + const floorInfo = { floor: floor1, currency: 'USD' }; bid.getFloor = function () { return floorInfo; }; @@ -1409,8 +1406,8 @@ describe('IndexexchangeAdapter', function () { // check if floors are in imp const requestBidFloor = spec.buildRequests([bid])[0]; const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(floorhi); - expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(imp1.bidfloor).to.equal(floorInfo.floor); + expect(imp1.bidfloorcur).to.equal(floorInfo.currency); expect(imp1.ext.fl).to.equal('p'); }); @@ -1426,7 +1423,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.ext.fl).to.equal('x'); }); - it('missing sizes #priceFloors ', function () { + it('missing sizes impressions should contain floors from priceFloors module ', function () { const bid = utils.deepClone(ONE_BANNER[0]); bid.mediaTypes.banner.sizes.push([500, 400]) From 98bed8494ceb61ab777fe4546f64db924e341e0e Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Thu, 17 Jun 2021 23:42:25 +0300 Subject: [PATCH 1174/1476] OneVideo Bid Adapter: bugfix for validation conflict (#7048) * dap validation fix * removing display=1 validation conflict with multi-format * unitest fix * version update * package-lock fix --- modules/oneVideoBidAdapter.js | 8 +------ test/spec/modules/oneVideoBidAdapter_spec.js | 24 ++++++++++++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index dfedbd156a9..880f4992614 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.1.1', + VERSION: '3.1.2', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -54,12 +54,6 @@ export const spec = { }; }; - // DAP Validation - if (bid.mediaTypes.banner && bid.params.video && !bid.params.video.display) { - utils.logError('Failed validation: If you are trying to use Dynamic Ad Placement you must pass params.video.display=1'); - return false; - }; - // Publisher Id (Exchange) validation if (typeof bid.params.pubId === 'undefined') { utils.logError('Failed validation: Adapter cannot send requests without bid.params.pubId'); diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 5289203fd5b..d6dacb44529 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -237,11 +237,27 @@ describe('OneVideoBidAdapter', function () { }, video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'] } + }, + bidder: 'oneVideo', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + protocols: [2, 5], + api: [2] + }, + site: { + page: 'https://news.yahoo.com/portfolios', + referrer: 'http://www.yahoo.com' + }, + pubId: 'brxd' } - } - expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }) }); @@ -264,7 +280,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.1.1'; + const VERSION = '3.1.2'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); From fe8b043a5916413210d8b7f5de057692efdf293b Mon Sep 17 00:00:00 2001 From: guiann Date: Fri, 18 Jun 2021 09:08:11 +0200 Subject: [PATCH 1175/1476] AdYouLike Bidder: improve robustness on Native case by fixing img retreival (#7042) * add required clickurl in every native adrequest * allows the native response to be given as is to prebid if possible * add unit tests on new Native case * Handle meta object in bid response with default addomains array * fix icon retrieval in Native case * Update priorities in case of multiple mediatypes given * improve robustness and fix associated unit test on picture urls --- modules/adyoulikeBidAdapter.js | 92 ++++++++++--------- test/spec/modules/adyoulikeBidAdapter_spec.js | 1 - 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 74ce62950f8..5cec76e1571 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -170,16 +170,15 @@ function getCanonicalUrl() { /* Get mediatype from bidRequest */ function getMediatype(bidRequest) { - var type = BANNER; - - if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { - type = NATIVE; - } else if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { - type = VIDEO; + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { + return VIDEO; + } else if (utils.deepAccess(bidRequest, 'mediaTypes.banner')) { + return BANNER; + } else if (utils.deepAccess(bidRequest, 'mediaTypes.native')) { + return NATIVE; } - - return type; } + /* Get Floor price information */ function getFloor(bidRequest, size, mediaType) { const bidFloors = bidRequest.getFloor({ @@ -281,30 +280,31 @@ function getInternalImgUrl(uid) { function getImageUrl(config, resource, width, height) { let url = ''; + if (resource && resource.Kind) { + switch (resource.Kind) { + case 'INTERNAL': + url = getInternalImgUrl(resource.Data.Internal.BlobReference.Uid); - switch (resource.Kind) { - case 'INTERNAL': - url = getInternalImgUrl(resource.Data.Internal.BlobReference.Uid); - - break; - - case 'EXTERNAL': - const dynPrefix = config.DynamicPrefix; - let extUrl = resource.Data.External.Url; - extUrl = extUrl.replace(/\[height\]/i, '' + height); - extUrl = extUrl.replace(/\[width\]/i, '' + width); + break; - if (extUrl.indexOf(dynPrefix) >= 0) { - const urlmatch = (/.*url=([^&]*)/gm).exec(extUrl); - url = urlmatch ? urlmatch[1] : ''; - if (!url) { - url = getInternalImgUrl((/.*key=([^&]*)/gm).exec(extUrl)[1]); + case 'EXTERNAL': + const dynPrefix = config.DynamicPrefix; + let extUrl = resource.Data.External.Url; + extUrl = extUrl.replace(/\[height\]/i, '' + height); + extUrl = extUrl.replace(/\[width\]/i, '' + width); + + if (extUrl.indexOf(dynPrefix) >= 0) { + const urlmatch = (/.*url=([^&]*)/gm).exec(extUrl); + url = urlmatch ? urlmatch[1] : ''; + if (!url) { + url = getInternalImgUrl((/.*key=([^&]*)/gm).exec(extUrl)[1]); + } + } else { + url = extUrl; } - } else { - url = extUrl; - } - break; + break; + } } return url; @@ -390,33 +390,39 @@ function getNativeAssets(response, nativeConfig) { imgSize[1] = response.Height || 250; } - native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), - width: imgSize[0], - height: imgSize[1] - }; + const url = getImageUrl(adJson, utils.deepAccess(adJson, 'Content.Preview.Thumbnail.Image'), imgSize[0], imgSize[1]); + if (url) { + native[key] = { + url, + width: imgSize[0], + height: imgSize[1] + }; + } + break; case 'icon': - if (adJson.HasSponsorImage) { - // icon requested size - const iconSize = nativeConfig.icon.sizes || []; - if (!iconSize.length) { - iconSize[0] = 50; - iconSize[1] = 50; - } + // icon requested size + const iconSize = nativeConfig.icon.sizes || []; + if (!iconSize.length) { + iconSize[0] = 50; + iconSize[1] = 50; + } + + const icurl = getImageUrl(adJson, utils.deepAccess(adJson, 'Content.Preview.Sponsor.Logo.Resource'), iconSize[0], iconSize[1]); + if (url) { native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), + url: icurl, width: iconSize[0], height: iconSize[1] }; } break; case 'privacyIcon': - native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); + native[key] = getImageUrl(adJson, utils.deepAccess(adJson, 'Content.Preview.Credit.Logo.Resource'), 25, 25); break; case 'privacyLink': - native[key] = adJson.Content.Preview.Credit.Url; + native[key] = utils.deepAccess(adJson, 'Content.Preview.Credit.Url'); break; } }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index e6e95ea5423..ad772ee3961 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -302,7 +302,6 @@ describe('Adyoulike Adapter', function () { 'secondaryCatIds': ['IAB-222', 'IAB-333'], 'mediaType': 'banner' }; - const admSample = "\u003cscript id=\"ayl-prebid-a11a121205932e75e622af275681965d\"\u003e\n(function(){\n\twindow.isPrebid = true\n\tvar prebidResults = /*PREBID*/{\"OnEvents\":{\"CLICK\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelCLICK.com/fake\"}],\"IMPRESSION\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelIMP.com/fake\"},{\"Kind\":\"JAVASCRIPT_URL\",\"Url\":\"https://testJsIMP.com/fake.js\"}]},\"Disabled\":false,\"Attempt\":\"a11a121205932e75e622af275681965d\",\"ApiPrefix\":\"https://fo-api.omnitagjs.com/fo-api\",\"TrackingPrefix\":\"https://tracking.omnitagjs.com/tracking\",\"DynamicPrefix\":\"https://tag-dyn.omnitagjs.com/fo-dyn\",\"StaticPrefix\":\"https://fo-static.omnitagjs.com/fo-static\",\"BlobPrefix\":\"https://fo-api.omnitagjs.com/fo-api/blobs\",\"SspPrefix\":\"https://fo-ssp.omnitagjs.com/fo-ssp\",\"VisitorPrefix\":\"https://visitor.omnitagjs.com/visitor\",\"Trusted\":true,\"Placement\":\"e622af275681965d3095808561a1e510\",\"PlacementAccess\":\"ALL\",\"Site\":\"6e2df7a92203c3c7a25561ed63f25a27\",\"Lang\":\"EN\",\"SiteLogo\":null,\"HasSponsorImage\":false,\"ResizeIframe\":true,\"IntegrationConfig\":{\"Kind\":\"WIDGET\",\"Widget\":{\"ExtraStyleSheet\":\"\",\"Placeholders\":{\"Body\":{\"Color\":{\"R\":77,\"G\":21,\"B\":82,\"A\":100},\"BackgroundColor\":{\"R\":255,\"G\":255,\"B\":255,\"A\":100},\"FontFamily\":\"Lato\",\"Width\":\"100%\",\"Align\":\"\",\"BoxShadow\":true},\"CallToAction\":{\"Color\":{\"R\":26,\"G\":157,\"B\":212,\"A\":100}},\"Description\":{\"Length\":130},\"Image\":{\"Width\":600,\"Height\":600,\"Lowres\":false,\"Raw\":false},\"Size\":{\"Height\":\"250px\",\"Width\":\"300px\"},\"Sponsor\":{\"Color\":{\"R\":35,\"G\":35,\"B\":35,\"A\":100},\"Label\":true,\"WithoutLogo\":false},\"Title\":{\"Color\":{\"R\":219,\"G\":181,\"B\":255,\"A\":100}}},\"Selector\":{\"Kind\":\"CSS\",\"Css\":\"#ayl-prebid-a11a121205932e75e622af275681965d\"},\"Insertion\":\"AFTER\",\"ClickFormat\":true,\"Creative20\":true,\"WidgetKind\":\"CREATIVE_TEMPLATE_4\"}},\"Legal\":\"Sponsored\",\"ForcedCampaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"ForcedTrack\":\"\",\"ForcedCreative\":\"\",\"ForcedSource\":\"\",\"DisplayMode\":\"DEFAULT\",\"Campaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"CampaignAccess\":\"ALL\",\"CampaignKind\":\"AD_TRAFFIC\",\"DataSource\":\"LOCAL\",\"DataSourceUrl\":\"\",\"DataSourceOnEventsIsolated\":false,\"DataSourceWithoutCookie\":false,\"Content\":{\"Preview\":{\"Thumbnail\":{\"Image\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://tag-dyn.omnitagjs.com/fo-dyn/native/preview/image?key=fd4362d35bb174d6f1c80d4bb5643c22\\u0026kind=INTERNAL\\u0026ztop=0.000000\\u0026zleft=0.000000\\u0026zwidth=0.333333\\u0026zheight=1.000000\\u0026width=[width]\\u0026height=[height]\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Text\":{\"CALLTOACTION\":\"Click here to learn more\",\"DESCRIPTION\":\"Considérant l'extrémité conjoncturelle, il serait bon d'anticiper toutes les voies de bon sens.\",\"SPONSOR\":\"Tested by\",\"TITLE\":\"Adserver Traffic Redirect Internal\"},\"Sponsor\":{\"Name\":\"QA Team\"},\"Credit\":{\"Logo\":{\"Resource\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Url\":\"https://blobs.omnitagjs.com/adchoice/\"}},\"Landing\":{\"Url\":\"https://www.w3.org/People/mimasa/test/xhtml/entities/entities-11.xhtml#lat1\",\"LegacyTracking\":false},\"ViewButtons\":{\"Close\":{\"Skip\":6000}},\"InternalContentFields\":{\"AnimatedImage\":false}},\"AdDomain\":\"adyoulike.com\",\"Opener\":\"REDIRECT\",\"PerformUITriggers\":[\"CLICK\"],\"RedirectionTarget\":\"TAB\"}/*PREBID*/;\n\tvar insertAds = function insertAds() {\insertAds();\n\t}\n})();\n\u003c/script\u003e"; const responseWithSinglePlacement = [ { From de43c7c2d382ced2980df9354280b0b46f149781 Mon Sep 17 00:00:00 2001 From: Anthony Lauzon Date: Sun, 20 Jun 2021 05:08:21 -0500 Subject: [PATCH 1176/1476] Halo RTD Module: Enable Per-Bidder Data (#7060) * allow per-bidder logic in halo rtd provider * update halo rtd doc * add plain object check * use 'keys' instead of 'entries' syntax * syntax nit --- modules/haloRtdProvider.js | 27 +++- modules/haloRtdProvider.md | 12 +- test/spec/modules/haloRtdProvider_spec.js | 184 +++++++++++++++++++++- 3 files changed, 210 insertions(+), 13 deletions(-) diff --git a/modules/haloRtdProvider.js b/modules/haloRtdProvider.js index b57787aab14..39e13863ca8 100644 --- a/modules/haloRtdProvider.js +++ b/modules/haloRtdProvider.js @@ -69,12 +69,31 @@ function paramOrDefault(param, defaultVal, arg) { * @param {Object} rtdConfig */ export function addRealTimeData(bidConfig, rtd, rtdConfig) { - let ortb2 = config.getConfig('ortb2') || {}; - if (rtdConfig.params && rtdConfig.params.handleRtd) { rtdConfig.params.handleRtd(bidConfig, rtd, rtdConfig, config); - } else if (rtd.ortb2) { - config.setConfig({ortb2: mergeLazy(ortb2, rtd.ortb2)}); + } else { + if (isPlainObject(rtd.ortb2)) { + let ortb2 = config.getConfig('ortb2') || {}; + config.setConfig({ortb2: mergeLazy(ortb2, rtd.ortb2)}); + } + + if (isPlainObject(rtd.ortb2b)) { + let bidderConfig = config.getBidderConfig(); + + Object.keys(rtd.ortb2b).forEach(bidder => { + let rtdOptions = rtd.ortb2b[bidder] || {}; + + let bidderOptions = {}; + if (isPlainObject(bidderConfig[bidder])) { + bidderOptions = bidderConfig[bidder]; + } + + config.setBidderConfig({ + bidders: [bidder], + config: mergeLazy(bidderOptions, rtdOptions) + }); + }); + } } } diff --git a/modules/haloRtdProvider.md b/modules/haloRtdProvider.md index 4307618bb60..45097e48129 100644 --- a/modules/haloRtdProvider.md +++ b/modules/haloRtdProvider.md @@ -4,13 +4,9 @@ Audigent is a next-generation data management platform and a first-of-a-kind "data agency" containing some of the most exclusive content-consuming audiences across desktop, mobile and social platforms. -This real-time data module provides quality segmentation that can be -provided to bid request objects destined for different SSPs in order to optimize -targeting. Audigent maintains a large database of first-party Tradedesk Unified -ID, Audigent Halo ID and other id provider mappings to various third-party -segment types that are utilizable across different SSPs. With this module, -these segments and other data can be retrieved and supplied to your pages -and the bidstream in real-time during the bid request cycle. +This real-time data module provides quality first-party data, contextual data, +site-level data and more that can be injected into bid request objects destined +for different bidders in order to optimize targeting. ### Publisher Usage @@ -29,7 +25,7 @@ and segment configurations. pbjs.setConfig( ... realTimeData: { - auctionDelay: auctionDelay, + auctionDelay: 5000, dataProviders: [ { name: "halo", diff --git a/test/spec/modules/haloRtdProvider_spec.js b/test/spec/modules/haloRtdProvider_spec.js index 3052441a00d..f8bf2dd3dbb 100644 --- a/test/spec/modules/haloRtdProvider_spec.js +++ b/test/spec/modules/haloRtdProvider_spec.js @@ -114,7 +114,6 @@ describe('haloRtdProvider', function() { } }; - let pbConfig = config.getConfig(); addRealTimeData(bidConfig, rtd, rtdConfig); let ortb2Config = config.getConfig().ortb2; @@ -123,6 +122,189 @@ describe('haloRtdProvider', function() { expect(ortb2Config.site.content.data).to.deep.include.members([setConfigSiteObj1, rtdSiteObj1]); }); + it('merges bidder-specific ortb2 data', function() { + let rtdConfig = {}; + let bidConfig = {}; + + const configUserObj1 = { + name: 'www.dataprovider1.com', + ext: { segtax: 3 }, + segment: [{ + id: '1776' + }] + }; + + const configUserObj2 = { + name: 'www.dataprovider2.com', + ext: { segtax: 3 }, + segment: [{ + id: '1914' + }] + }; + + const configUserObj3 = { + name: 'www.dataprovider1.com', + ext: { segtax: 3 }, + segment: [{ + id: '2003' + }] + }; + + const configSiteObj1 = { + name: 'www.dataprovider3.com', + ext: { + segtax: 1 + }, + segment: [ + { + id: '1812' + }, + { + id: '1955' + } + ] + }; + + const configSiteObj2 = { + name: 'www.dataprovider3.com', + ext: { + segtax: 1 + }, + segment: [ + { + id: '1812' + } + ] + }; + + config.setBidderConfig({ + bidders: ['adbuzz'], + config: { + ortb2: { + user: { + data: [configUserObj1, configUserObj2] + }, + site: { + content: { + data: [configSiteObj1] + } + } + } + } + }); + + config.setBidderConfig({ + bidders: ['pubvisage'], + config: { + ortb2: { + user: { + data: [configUserObj3] + }, + site: { + content: { + data: [configSiteObj2] + } + } + } + } + }); + + const rtdUserObj1 = { + name: 'www.dataprovider4.com', + ext: { + segtax: 501 + }, + segment: [ + { + id: '1918' + }, + { + id: '1939' + } + ] + }; + + const rtdUserObj2 = { + name: 'www.dataprovider2.com', + ext: { + segtax: 502 + }, + segment: [ + { + id: '1939' + } + ] + }; + + const rtdSiteObj1 = { + name: 'www.dataprovider5.com', + ext: { + segtax: 1 + }, + segment: [ + { + id: '441' + }, + { + id: '442' + } + ] + }; + + const rtdSiteObj2 = { + name: 'www.dataprovider6.com', + ext: { + segtax: 2 + }, + segment: [ + { + id: '676' + } + ] + }; + + const rtd = { + ortb2b: { + adbuzz: { + ortb2: { + user: { + data: [rtdUserObj1] + }, + site: { + content: { + data: [rtdSiteObj1] + } + } + } + }, + pubvisage: { + ortb2: { + user: { + data: [rtdUserObj2] + }, + site: { + content: { + data: [rtdSiteObj2] + } + } + } + } + } + }; + + addRealTimeData(bidConfig, rtd, rtdConfig); + + let ortb2Config = config.getBidderConfig().adbuzz.ortb2; + + expect(ortb2Config.user.data).to.deep.include.members([configUserObj1, configUserObj2, rtdUserObj1]); + expect(ortb2Config.site.content.data).to.deep.include.members([configSiteObj1, rtdSiteObj1]); + + ortb2Config = config.getBidderConfig().pubvisage.ortb2; + + expect(ortb2Config.user.data).to.deep.include.members([configUserObj3, rtdUserObj2]); + expect(ortb2Config.site.content.data).to.deep.include.members([configSiteObj2, rtdSiteObj2]); + }); + it('allows publisher defined rtd ortb2 logic', function() { const rtdConfig = { params: { From 30a8c9c13500d88a7960c66f12ce2408dba93541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Mon, 21 Jun 2021 11:07:21 +0300 Subject: [PATCH 1177/1476] Vidazoo Bid Adapter: update documentation (#7061) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * docs(config): update vidazoo docs Co-authored-by: roman --- modules/vidazooBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vidazooBidAdapter.md b/modules/vidazooBidAdapter.md index df7700cb05a..c8fd5956d2a 100644 --- a/modules/vidazooBidAdapter.md +++ b/modules/vidazooBidAdapter.md @@ -20,7 +20,7 @@ var adUnits = [ { bidder: 'vidazoo', params: { - cId: '5a1c419d95fce900044c334e', + cId: '562524b21b1c1f08117fc7f9', pId: '59ac17c192832d0011283fe3', bidFloor: 0.0001, ext: { From 69daaf48db379d5ab52f27419ae5c18f79347ead Mon Sep 17 00:00:00 2001 From: notmani Date: Mon, 21 Jun 2021 14:38:01 +0200 Subject: [PATCH 1178/1476] madvertiseBidAdapter: support for response meta.advertiserDomains (v5.0) (#7053) * - [ ] support for response meta.advertiserDomains (v5.0) - [ ] use same parameter as Prebid-server * fix test --- modules/madvertiseBidAdapter.js | 100 +++++++++ modules/madvertiseBidAdapter.md | 2 +- .../spec/modules/madvertiseBidAdapter_spec.js | 205 ++++++++++++++++++ 3 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 modules/madvertiseBidAdapter.js create mode 100644 test/spec/modules/madvertiseBidAdapter_spec.js diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js new file mode 100644 index 00000000000..219295f0d50 --- /dev/null +++ b/modules/madvertiseBidAdapter.js @@ -0,0 +1,100 @@ +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; + +// use protocol relative urls for http or https +const MADVERTISE_ENDPOINT = 'https://mobile.mng-ads.com/'; + +export const spec = { + code: 'madvertise', + /** + * @param {object} bid + * @return boolean + */ + isBidRequestValid: function (bid) { + if (typeof bid.params !== 'object') { + return false; + } + let sizes = utils.parseSizesInput(bid.sizes); + if (!sizes || sizes.length === 0) { + return false; + } + if (sizes.length > 0 && sizes[0] === undefined) { + return false; + } + if (typeof bid.params.floor == 'undefined' || parseFloat(bid.params.floor) < 0.01) { + bid.params.floor = 0.01; + } + + return typeof bid.params.s != 'undefined'; + }, + /** + * @param {BidRequest[]} bidRequests + * @param bidderRequest + * @return ServerRequest[] + */ + buildRequests: function (bidRequests, bidderRequest) { + return bidRequests.map(bidRequest => { + bidRequest.startTime = new Date().getTime(); + + // non-video request builder + var src = '?rt=bid_request&v=1.0'; + + for (var i = 0; i < bidRequest.sizes.length; i++) { + if (Array.isArray(bidRequest.sizes[i]) && bidRequest.sizes[i].length == 2) { + src = src + '&sizes[' + i + ']=' + bidRequest.sizes[i][0] + 'x' + bidRequest.sizes[i][1]; + } + } + + utils._each(bidRequest.params, (item, key) => src = src + '&' + key + '=' + item); + + if (typeof bidRequest.params.u == 'undefined') { + src = src + '&u=' + navigator.userAgent; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + src = src + '&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? '1' : '0') + '&consent[0][format]=' + config.getConfig('consentManagement.cmpApi') + '&consent[0][value]=' + bidderRequest.gdprConsent.consentString; + } + + return { + method: 'GET', + url: MADVERTISE_ENDPOINT + src, + options: {withCredentials: false}, + bidId: bidRequest.bidId + }; + }); + }, + /** + * @param {*} responseObj + * @param {BidRequest} bidRequest + * @return {Bid[]} An array of bids which + */ + interpretResponse: function (responseObj, bidRequest) { + responseObj = responseObj.body; + // check overall response + if (responseObj == null || typeof responseObj !== 'object' || !responseObj.hasOwnProperty('ad')) { + return []; + } + + let bid = { + requestId: bidRequest.bidId, + cpm: responseObj.cpm, + width: responseObj.Width, + height: responseObj.height, + ad: responseObj.ad, + ttl: responseObj.ttl, + creativeId: responseObj.creativeId, + netRevenue: responseObj.netRevenue, + currency: responseObj.currency, + dealId: responseObj.dealId, + meta: { + advertiserDomains: Array.isArray(responseObj.adomain) ? responseObj.adomain : [] + } + + }; + return [bid]; + }, + getUserSyncs: function (syncOptions) { + } +}; +registerBidder(spec); diff --git a/modules/madvertiseBidAdapter.md b/modules/madvertiseBidAdapter.md index 4576e955cbd..b57925f2dc1 100644 --- a/modules/madvertiseBidAdapter.md +++ b/modules/madvertiseBidAdapter.md @@ -30,7 +30,7 @@ support@madvertise.com for more information. { bidder: "madvertise", params: { - s: "/4543756/prebidadaptor/madvertiseHB" + zoneId: "/4543756/prebidadaptor/madvertiseHB" } } ] diff --git a/test/spec/modules/madvertiseBidAdapter_spec.js b/test/spec/modules/madvertiseBidAdapter_spec.js new file mode 100644 index 00000000000..466d30acdd3 --- /dev/null +++ b/test/spec/modules/madvertiseBidAdapter_spec.js @@ -0,0 +1,205 @@ +import {expect} from 'chai'; +import {config} from 'src/config'; +import * as utils from 'src/utils'; +import {spec} from 'modules/madvertiseBidAdapter'; + +describe('madvertise adapater', () => { + describe('Test validate req', () => { + it('should accept minimum valid bid', () => { + let bid = { + bidder: 'madvertise', + sizes: [[728, 90]], + params: { + zoneId: 'test' + } + }; + const isValid = spec.isBidRequestValid(bid); + + expect(isValid).to.equal(false); + }); + it('should reject no sizes', () => { + let bid = { + bidder: 'madvertise', + params: { + zoneId: 'test' + } + }; + const isValid = spec.isBidRequestValid(bid); + + expect(isValid).to.equal(false); + }); + it('should reject empty sizes', () => { + let bid = { + bidder: 'madvertise', + sizes: [], + params: { + zoneId: 'test' + } + }; + const isValid = spec.isBidRequestValid(bid); + + expect(isValid).to.equal(false); + }); + it('should reject wrong format sizes', () => { + let bid = { + bidder: 'madvertise', + sizes: [['728x90']], + params: { + zoneId: 'test' + } + }; + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }); + it('should reject no params', () => { + let bid = { + bidder: 'madvertise', + sizes: [[728, 90]] + }; + const isValid = spec.isBidRequestValid(bid); + + expect(isValid).to.equal(false); + }); + it('should reject missing s', () => { + let bid = { + bidder: 'madvertise', + params: {} + }; + const isValid = spec.isBidRequestValid(bid); + + expect(isValid).to.equal(false); + }); + }); + + describe('Test build request', () => { + beforeEach(function () { + let mockConfig = { + consentManagement: { + cmpApi: 'IAB', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + afterEach(function () { + config.getConfig.restore(); + }); + let bid = [{ + bidder: 'madvertise', + sizes: [[728, 90], [300, 100]], + bidId: '51ef8751f9aead', + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + params: { + zoneId: 'test', + } + }]; + it('minimum request with gdpr consent', () => { + let bidderRequest = { + gdprConsent: { + consentString: 'CO_5mtSPHOmEIAsAkBFRBOCsAP_AAH_AAAqIHQgB7SrERyNAYWB5gusAKYlfQAQCA2AABAYdASgJQQBAMJYEkGAIuAnAACAKAAAEIHQAAAAlCCmABAEAAIABBSGMAQgABZAAIiAEEAATAABACAABGYCSCAIQjIAAAAEAgEKEAAoAQGBAAAEgBABAAAogACADAgXmACIKkQBAkBAYAkAYQAogAhAAAAAIAAAAAAAKAABAAAghAAQQAAAAAAAAAgAAAAABAAAAAAAAQAAAAAAAAABAAgAAAAAAAAAIAAAAAAAAAAAAAAAABAAAAAAAAAAAQCAKCgBgEQALgAqkJADAIgAXABVIaACAAERABAACKgAgABA', + vendorData: {}, + gdprApplies: true + } + }; + const req = spec.buildRequests(bid, bidderRequest); + + expect(req).to.exist.and.to.be.a('array'); + expect(req[0]).to.have.property('method'); + expect(req[0].method).to.equal('GET'); + expect(req[0]).to.have.property('url'); + expect(req[0].url).to.contain('//mobile.mng-ads.com/?rt=bid_request&v=1.0'); + expect(req[0].url).to.contain(`&zoneId=test`); + expect(req[0].url).to.contain(`&sizes[0]=728x90`); + expect(req[0].url).to.contain(`&gdpr=1`); + expect(req[0].url).to.contain(`&consent[0][format]=IAB`); + expect(req[0].url).to.contain(`&consent[0][value]=CO_5mtSPHOmEIAsAkBFRBOCsAP_AAH_AAAqIHQgB7SrERyNAYWB5gusAKYlfQAQCA2AABAYdASgJQQBAMJYEkGAIuAnAACAKAAAEIHQAAAAlCCmABAEAAIABBSGMAQgABZAAIiAEEAATAABACAABGYCSCAIQjIAAAAEAgEKEAAoAQGBAAAEgBABAAAogACADAgXmACIKkQBAkBAYAkAYQAogAhAAAAAIAAAAAAAKAABAAAghAAQQAAAAAAAAAgAAAAABAAAAAAAAQAAAAAAAAABAAgAAAAAAAAAIAAAAAAAAAAAAAAAABAAAAAAAAAAAQCAKCgBgEQALgAqkJADAIgAXABVIaACAAERABAACKgAgABA`) + }); + + it('minimum request without gdpr consent', () => { + let bidderRequest = {}; + const req = spec.buildRequests(bid, bidderRequest); + + expect(req).to.exist.and.to.be.a('array'); + expect(req[0]).to.have.property('method'); + expect(req[0].method).to.equal('GET'); + expect(req[0]).to.have.property('url'); + expect(req[0].url).to.contain('//mobile.mng-ads.com/?rt=bid_request&v=1.0'); + expect(req[0].url).to.contain(`&zoneId=test`); + expect(req[0].url).to.contain(`&sizes[0]=728x90`); + expect(req[0].url).not.to.contain(`&gdpr=1`); + expect(req[0].url).not.to.contain(`&consent[0][format]=`); + expect(req[0].url).not.to.contain(`&consent[0][value]=`) + }); + }); + + describe('Test interpret response', () => { + it('General banner response', () => { + let bid = { + bidder: 'madvertise', + sizes: [[728, 90]], + bidId: '51ef8751f9aead', + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + params: { + zoneId: 'test', + connection_type: 'WIFI', + age: 25, + } + }; + let resp = spec.interpretResponse({body: { + requestId: 'REQUEST_ID', + cpm: 1, + ad: '

I am an ad

', + Width: 320, + height: 50, + creativeId: 'CREATIVE_ID', + dealId: 'DEAL_ID', + ttl: 180, + currency: 'EUR', + netRevenue: true, + adomain: ['madvertise.com'] + }}, {bidId: bid.bidId}); + + expect(resp).to.exist.and.to.be.a('array'); + expect(resp[0]).to.have.property('requestId', bid.bidId); + expect(resp[0]).to.have.property('cpm', 1); + expect(resp[0]).to.have.property('width', 320); + expect(resp[0]).to.have.property('height', 50); + expect(resp[0]).to.have.property('ad', '

I am an ad

'); + expect(resp[0]).to.have.property('ttl', 180); + expect(resp[0]).to.have.property('creativeId', 'CREATIVE_ID'); + expect(resp[0]).to.have.property('netRevenue', true); + expect(resp[0]).to.have.property('currency', 'EUR'); + expect(resp[0]).to.have.property('dealId', 'DEAL_ID'); + // expect(resp[0].adomain).to.deep.equal(['madvertise.com']); + }); + it('No response', () => { + let bid = { + bidder: 'madvertise', + sizes: [[728, 90]], + bidId: '51ef8751f9aead', + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + params: { + zoneId: 'test', + connection_type: 'WIFI', + age: 25, + } + }; + let resp = spec.interpretResponse({body: null}, {bidId: bid.bidId}); + + expect(resp).to.exist.and.to.be.a('array').that.is.empty; + }); + }); +}); From e3fd49e5848232de7ace3b22534b0d038096ba93 Mon Sep 17 00:00:00 2001 From: PWyrembak Date: Mon, 21 Jun 2021 17:28:27 +0300 Subject: [PATCH 1179/1476] TrustX Bid Adapter: Support Permutive module (#7055) * 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 * TrustX Bid Adapter: added us_privacy parameter in bid request * TrustX Bid Adapter: fix us_privacy parameter in bid request * Fix alias error for TrustX Bid Adapter * TrustX Bid Adapter: added new request format * TrustX Bid adapter: fix new format endpoint * TrustX Bid Adapter: update md file to support useNewFormat parameter * TrustX Bid Adapter: added additional sync url * TrustX Bid Adapter: added check for enabled syncs number + added gdpr data to sync urls * TrustX Bid Adapter: added support of meta.advertiserDomains * TrustX Bid Adapter: added support rtd permutive and jwplayer for new and old request format --- modules/trustxBidAdapter.js | 95 ++++++++++++++++++---- test/spec/modules/trustxBidAdapter_spec.js | 61 ++++++++++++++ 2 files changed, 142 insertions(+), 14 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 247a1f6d048..66574e4fe57 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -285,6 +285,34 @@ function createBannerRequest(bid, mediaType) { return result; } +function addSegments(name, segName, segments, data, bidConfigName) { + if (segments && segments.length) { + data.push({ + name: name, + segment: segments.map((seg) => { + return {name: segName, value: seg}; + }) + }); + } else if (bidConfigName) { + const configData = config.getConfig('ortb2.user.data'); + let segData = null; + configData && configData.some(({name, segment}) => { + if (name === bidConfigName) { + segData = segment; + return true; + } + }); + if (segData && segData.length) { + data.push({ + name: name, + segment: segData.map((seg) => { + return {name: segName, value: seg}; + }) + }); + } + } +} + /** * Gets bidfloor * @param {Object} mediaTypes @@ -318,6 +346,7 @@ function newFormatRequest(validBidRequests, bidderRequest) { } let pageKeywords = null; let jwpseg = null; + let permutiveseg = null; let content = null; let schain = null; let userId = null; @@ -352,13 +381,19 @@ function newFormatRequest(validBidRequests, bidderRequest) { pageKeywords = utils.transformBidderParamKeywords(keywords); } const bidFloor = _getFloor(mediaTypes || {}, bid); - const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; - if (jwTargeting) { - if (!jwpseg && jwTargeting.segments) { - jwpseg = jwTargeting.segments; + if (rtd) { + const jwTargeting = rtd.jwplayer && rtd.jwplayer.targeting; + if (jwTargeting) { + if (!jwpseg && jwTargeting.segments) { + jwpseg = jwTargeting.segments; + } + if (!content && jwTargeting.content) { + content = jwTargeting.content; + } } - if (!content && jwTargeting.content) { - content = jwTargeting.content; + const permutiveTargeting = rtd.p_standard && rtd.p_standard.targeting; + if (!permutiveseg && permutiveTargeting && permutiveTargeting.segments) { + permutiveseg = permutiveTargeting.segments; } } let impObj = { @@ -420,14 +455,13 @@ function newFormatRequest(validBidRequests, bidderRequest) { request.site.content = content; } - if (jwpseg && jwpseg.length) { + const userData = []; + addSegments('iow_labs_pub_data', 'jwpseg', jwpseg, userData); + addSegments('permutive', 'p_standard', permutiveseg, userData, 'permutive.com'); + + if (userData.length) { user = { - data: [{ - name: 'iow_labs_pub_data', - segment: jwpseg.map((seg) => { - return {name: 'jwpseg', value: seg}; - }) - }] + data: userData }; } @@ -499,6 +533,8 @@ function oldFormatRequest(validBidRequests, bidderRequest) { const sizeMap = {}; const bids = validBidRequests || []; let priceType = 'net'; + let jwpseg = null; + let permutiveseg = null; let pageKeywords; let reqId; @@ -507,7 +543,7 @@ function oldFormatRequest(validBidRequests, bidderRequest) { priceType = 'gross'; } reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode} = bid; + const {params: {uid}, adUnitCode, rtd} = bid; auids.push(uid); const sizesId = utils.parseSizesInput(bid.sizes); @@ -520,6 +556,19 @@ function oldFormatRequest(validBidRequests, bidderRequest) { pageKeywords = keywords; } + if (rtd) { + const jwTargeting = rtd.jwplayer && rtd.jwplayer.targeting; + if (jwTargeting) { + if (!jwpseg && jwTargeting.segments) { + jwpseg = jwTargeting.segments; + } + } + const permutiveTargeting = rtd.p_standard && rtd.p_standard.targeting; + if (!permutiveseg && permutiveTargeting && permutiveTargeting.segments) { + permutiveseg = permutiveTargeting.segments; + } + } + if (!slotsMapByUid[uid]) { slotsMapByUid[uid] = {}; } @@ -546,6 +595,24 @@ function oldFormatRequest(validBidRequests, bidderRequest) { }); }); + const segmentsData = []; + addSegments('iow_labs_pub_data', 'jwpseg', jwpseg, segmentsData); + addSegments('permutive', 'p_standard', permutiveseg, segmentsData, 'permutive.com'); + + if (segmentsData.length) { + if (!pageKeywords) { + pageKeywords = []; + } + segmentsData.forEach(({segment}) => { + if (segment.length) { + pageKeywords.push({ + key: segment[0].name, + value: segment.map(({value}) => value) + }); + } + }); + } + const payload = { pt: priceType, auids: auids.join(','), diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index f53116f60a4..a8caed3ad0e 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -225,6 +225,41 @@ describe('TrustXAdapter', function () { 'key': 'emptyArr' }]); }); + + it('should attach rtd segments as keywords', function () { + const bidRequestWithKeywords = [].concat(bidRequests); + bidRequestWithKeywords[1] = Object.assign({}, + bidRequests[1], + { + rtd: { + p_standard: { + targeting: { + segments: ['perm_seg_1', 'perm_seg_2'] + } + }, + jwplayer: { + targeting: { + segments: ['jwp_seg_1', 'jwp_seg_2'] + } + } + } + } + ); + + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.keywords).to.be.an('string'); + payload.keywords = JSON.parse(payload.keywords); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'jwpseg', + 'value': ['jwp_seg_1', 'jwp_seg_2'] + }, { + 'key': 'p_standard', + 'value': ['perm_seg_1', 'perm_seg_2'] + }]); + }); }); describe('buildRequests with new format', function () { @@ -593,6 +628,32 @@ describe('TrustXAdapter', function () { expect(payload.site.content).to.deep.equal(jsContent); }); + it('if segment is present in permutive targeting, payload must have right params', function () { + const permSegments = ['test_perm_1', 'test_perm_2']; + const bidRequestsWithPermutiveTargeting = bidRequests.map((bid) => { + return Object.assign({ + rtd: { + p_standard: { + targeting: { + segments: permSegments + } + } + } + }, bid); + }); + const request = spec.buildRequests(bidRequestsWithPermutiveTargeting, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user.data).to.deep.equal([{ + name: 'permutive', + segment: [ + {name: 'p_standard', value: permSegments[0]}, + {name: 'p_standard', value: permSegments[1]} + ] + }]); + }); + it('should contain the keyword values if it present in ortb2.(site/user)', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); From 011fdd794f841d55de320444fb56373c36b03bd3 Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Mon, 21 Jun 2021 20:52:55 +0400 Subject: [PATCH 1180/1476] RadsBidAdapter: Add adomains/userIds support and change banner ad type priority (#6973) --- modules/radsBidAdapter.js | 153 +++++++++++++++++------ modules/radsBidAdapter.md | 1 + test/spec/modules/radsBidAdapter_spec.js | 39 ++++-- 3 files changed, 144 insertions(+), 49 deletions(-) diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index 8f3cbe02f23..2db42802067 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -7,10 +7,12 @@ const BIDDER_CODE = 'rads'; const ENDPOINT_URL = 'https://rads.recognified.net/md.request.php'; const ENDPOINT_URL_DEV = 'https://dcradn1.online-solution.biz/md.request.php'; const DEFAULT_VAST_FORMAT = 'vast2'; +const GVLID = 602; export const spec = { code: BIDDER_CODE, - aliases: ['rads'], + gvlid: GVLID, + aliases: [], supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { return !!(bid.params.placement); @@ -18,9 +20,6 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { const params = bidRequest.params; - const videoData = utils.deepAccess(bidRequest, 'mediaTypes.video') || {}; - const sizes = utils.parseSizesInput(videoData.playerSize || bidRequest.sizes)[0]; - const [width, height] = sizes.split('x'); const placementId = params.placement; const rnd = Math.floor(Math.random() * 99999999999); @@ -30,34 +29,37 @@ export const spec = { let endpoint = isDev ? ENDPOINT_URL_DEV : ENDPOINT_URL; - let payload = {}; - if (isVideoRequest(bidRequest)) { - let vastFormat = params.vastFormat || DEFAULT_VAST_FORMAT; - payload = { - rt: vastFormat, - _f: 'prebid_js', - _ps: placementId, - srw: width, - srh: height, - idt: 100, - rnd: rnd, - p: referrer, - bid_id: bidId, - }; + let payload = { + _f: 'prebid_js', + _ps: placementId, + idt: 100, + rnd: rnd, + p: referrer, + bid_id: bidId, + }; + + let sizes; + if (isBannerRequest(bidRequest)) { + sizes = getBannerSizes(bidRequest); + payload.rt = 'bid-response'; + payload.srw = sizes[0].width; + payload.srh = sizes[0].height; } else { - payload = { - rt: 'bid-response', - _f: 'prebid_js', - _ps: placementId, - srw: width, - srh: height, - idt: 100, - rnd: rnd, - p: referrer, - bid_id: bidId, - }; + let vastFormat = params.vastFormat || DEFAULT_VAST_FORMAT; + sizes = getVideoSizes(bidRequest); + payload.rt = vastFormat; + payload.srw = sizes[0].width; + payload.srh = sizes[0].height; } - prepareExtraParams(params, payload, bidderRequest); + + if (sizes.length > 1) { + payload.alt_ad_sizes = []; + for (let i = 1; i < sizes.length; i++) { + payload.alt_ad_sizes.push(sizes[i].width + 'x' + sizes[i].height); + } + } + + prepareExtraParams(params, payload, bidderRequest, bidRequest); return { method: 'GET', @@ -84,7 +86,10 @@ export const spec = { dealId: dealId, currency: currency, netRevenue: netRevenue, - ttl: config.getConfig('_bidderTimeout') + ttl: config.getConfig('_bidderTimeout'), + meta: { + advertiserDomains: response.adomain || [] + } }; if (response.vastXml) { @@ -151,18 +156,15 @@ function objectToQueryString(obj, prefix) { } return str.join('&'); } - /** - * Check if it's a video bid request + * Add extra params to server request * - * @param {BidRequest} bid - Bid request generated from ad slots - * @returns {boolean} True if it's a video bid + * @param params + * @param payload + * @param bidderRequest + * @param {BidRequest} bidRequest - Bid request generated from ad slots */ -function isVideoRequest(bid) { - return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); -} - -function prepareExtraParams(params, payload, bidderRequest) { +function prepareExtraParams(params, payload, bidderRequest, bidRequest) { if (params.pfilter !== undefined) { payload.pfilter = params.pfilter; } @@ -196,6 +198,77 @@ function prepareExtraParams(params, payload, bidderRequest) { if (params.ip !== undefined) { payload.i = params.ip; } + + if (bidRequest.userId && bidRequest.userId.netId) { + payload.did_netid = bidRequest.userId.netId; + } + if (bidRequest.userId && bidRequest.userId.uid2) { + payload.did_uid2 = bidRequest.userId.uid2; + } +} + +/** + * Check if it's a banner bid request + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {boolean} True if it's a banner bid + */ +function isBannerRequest(bid) { + return bid.mediaType === 'banner' || !!utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoRequest(bid); +} + +/** + * Check if it's a video bid request + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {boolean} True if it's a video bid + */ +function isVideoRequest(bid) { + return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); +} + +/** + * Get video sizes + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {object} True if it's a video bid + */ +function getVideoSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); +} + +/** + * Get banner sizes + * + * @param {BidRequest} bid - Bid request generated from ad slots + * @returns {object} True if it's a video bid + */ +function getBannerSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); +} + +/** + * Parse size + * @param sizes + * @returns {width: number, h: height} + */ +function parseSize(size) { + let sizeObj = {} + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + return sizeObj; +} + +/** + * Parse sizes + * @param sizes + * @returns {{width: number , height: number }[]} + */ +function parseSizes(sizes) { + if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) + return sizes.map(size => parseSize(size)); + } + return [parseSize(sizes)]; // or a single one ? (ie. [728,90]) } registerBidder(spec); diff --git a/modules/radsBidAdapter.md b/modules/radsBidAdapter.md index 6e970093154..a00b82e20cb 100644 --- a/modules/radsBidAdapter.md +++ b/modules/radsBidAdapter.md @@ -25,6 +25,7 @@ RADS Bidder Adapter for Prebid.js 1.x bidder: "rads", params: { placement: 3, // placement ID + vastFormat: "vast2", // vast2(default) or vast4 devMode: true // if true: library uses dev server for tests } } diff --git a/test/spec/modules/radsBidAdapter_spec.js b/test/spec/modules/radsBidAdapter_spec.js index c3c3b4b2746..019108fce9d 100644 --- a/test/spec/modules/radsBidAdapter_spec.js +++ b/test/spec/modules/radsBidAdapter_spec.js @@ -59,9 +59,24 @@ describe('radsAdapter', function () { 'sizes': [ [300, 250] ], + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + }, + 'banner': { + 'sizes': [ + [100, 100], [400, 400], [500, 500] + ] + } + }, 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' + 'auctionId': '1d1a030790a475', + 'userId': { + 'netId': '123', + 'uid2': '456' + } }, { 'bidder': 'rads', 'params': { @@ -78,7 +93,7 @@ describe('radsAdapter', function () { }, 'mediaTypes': { 'video': { - 'playerSize': [640, 480], + 'playerSize': [[640, 480], [500, 500], [600, 600]], 'context': 'instream' } }, @@ -110,13 +125,13 @@ describe('radsAdapter', function () { it('sends bid request to our endpoint via GET', function () { expect(request[0].method).to.equal('GET'); let data = request[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); - expect(data).to.equal('rt=bid-response&_f=prebid_js&_ps=6682&srw=300&srh=250&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1'); + expect(data).to.equal('_f=prebid_js&_ps=6682&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&rt=bid-response&srw=100&srh=100&alt_ad_sizes%5B0%5D=400x400&alt_ad_sizes%5B1%5D=500x500&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1&did_netid=123&did_uid2=456'); }); it('sends bid video request to our rads endpoint via GET', function () { expect(request[1].method).to.equal('GET'); let data = request[1].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); - expect(data).to.equal('rt=vast2&_f=prebid_js&_ps=6682&srw=640&srh=480&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&bcat=IAB2%2CIAB4&dvt=desktop'); + expect(data).to.equal('_f=prebid_js&_ps=6682&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&rt=vast2&srw=640&srh=480&alt_ad_sizes%5B0%5D=500x500&alt_ad_sizes%5B1%5D=600x600&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&bcat=IAB2%2CIAB4&dvt=desktop'); }); // with gdprConsent @@ -124,13 +139,13 @@ describe('radsAdapter', function () { it('sends bid request to our endpoint via GET', function () { expect(request2[0].method).to.equal('GET'); let data = request2[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); - expect(data).to.equal('rt=bid-response&_f=prebid_js&_ps=6682&srw=300&srh=250&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1'); + expect(data).to.equal('_f=prebid_js&_ps=6682&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&rt=bid-response&srw=100&srh=100&alt_ad_sizes%5B0%5D=400x400&alt_ad_sizes%5B1%5D=500x500&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1&did_netid=123&did_uid2=456'); }); it('sends bid video request to our rads endpoint via GET', function () { expect(request2[1].method).to.equal('GET'); let data = request2[1].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); - expect(data).to.equal('rt=vast2&_f=prebid_js&_ps=6682&srw=640&srh=480&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop'); + expect(data).to.equal('_f=prebid_js&_ps=6682&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&rt=vast2&srw=640&srh=480&alt_ad_sizes%5B0%5D=500x500&alt_ad_sizes%5B1%5D=600x600&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop'); }); }); @@ -146,7 +161,8 @@ describe('radsAdapter', function () { 'currency': 'EUR', 'ttl': 60, 'netRevenue': true, - 'zone': '6682' + 'zone': '6682', + 'adomain': ['bdomain'] } }; let serverVideoResponse = { @@ -174,7 +190,8 @@ describe('radsAdapter', function () { currency: 'EUR', netRevenue: true, ttl: 300, - ad: '' + ad: '', + meta: {advertiserDomains: ['bdomain']} }, { requestId: '23beaa6af6cdde', cpm: 0.5, @@ -186,7 +203,8 @@ describe('radsAdapter', function () { netRevenue: true, ttl: 300, vastXml: '{"reason":7001,"status":"accepted"}', - mediaType: 'video' + mediaType: 'video', + meta: {advertiserDomains: []} }]; it('should get the correct bid response by display ad', function () { @@ -202,6 +220,8 @@ describe('radsAdapter', function () { }]; let result = spec.interpretResponse(serverBannerResponse, bidRequest[0]); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].meta.advertiserDomains.length).to.equal(1); + expect(result[0].meta.advertiserDomains[0]).to.equal(expectedResponse[0].meta.advertiserDomains[0]); }); it('should get the correct rads video bid response by display ad', function () { @@ -220,6 +240,7 @@ describe('radsAdapter', function () { }]; let result = spec.interpretResponse(serverVideoResponse, bidRequest[0]); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[1])); + expect(result[0].meta.advertiserDomains.length).to.equal(0); }); it('handles empty bid response', function () { From 191655ad6b7b43f625118c2a9a431916d0f83dda Mon Sep 17 00:00:00 2001 From: Mirko Rean <3244291+mirkorean@users.noreply.github.com> Date: Mon, 21 Jun 2021 20:05:34 +0200 Subject: [PATCH 1181/1476] Yieldlab Adapter: Add pvid option for cookieless environments (#7008) * Add pvid in adtag/vasturls for Yieldlab Bidder In cookieless environments we will respond with a pvid, that needs to be appended to the adtag/vasturls. Force pvid with bidder params - customParams: { pvid: true } * Add test for pvid --- modules/yieldlabBidAdapter.js | 5 +-- test/spec/modules/yieldlabBidAdapter_spec.js | 33 +++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index d86f84e21b7..c05ecbc15fc 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -104,6 +104,7 @@ export const spec = { 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 bidResponse = { requestId: bidRequest.bidId, @@ -116,7 +117,7 @@ export const spec = { netRevenue: false, ttl: BID_RESPONSE_TTL_SEC, referrer: '', - ad: ``, + ad: ``, meta: { advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a' } @@ -129,7 +130,7 @@ export const spec = { bidResponse.height = playersize[1] } bidResponse.mediaType = VIDEO - bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}` + bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` if (isOutstream(bidRequest)) { const renderer = Renderer.install({ id: bidRequest.bidId, diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 3cc5971ce67..d150646851f 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -49,6 +49,14 @@ const REQUEST = { } } +const VIDEO_REQUEST = Object.assign({}, REQUEST, { + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } +}) + const RESPONSE = { advertiser: 'yieldlab', curl: 'https://www.yieldlab.de', @@ -64,6 +72,10 @@ const VIDEO_RESPONSE = Object.assign({}, RESPONSE, { 'adtype': 'VIDEO' }) +const PVID_RESPONSE = Object.assign({}, VIDEO_RESPONSE, { + 'pvid': '43513f11-55a0-4a83-94e5-0ebc08f54a2c' +}) + const REQPARAMS = { json: true, ts: 1234567890 @@ -217,13 +229,6 @@ describe('yieldlabBidAdapter', function () { }) it('should add vastUrl when type is video', function () { - const VIDEO_REQUEST = Object.assign({}, REQUEST, { - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }) const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS}) expect(result[0].requestId).to.equal('2d925f27f5079f') @@ -234,13 +239,6 @@ describe('yieldlabBidAdapter', function () { }) it('should append gdpr parameters to vastUrl', function () { - const VIDEO_REQUEST = Object.assign({}, REQUEST, { - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }) const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_GDPR}) expect(result[0].vastUrl).to.include('&gdpr=true') @@ -263,5 +261,12 @@ describe('yieldlabBidAdapter', function () { expect(result[0].width).to.equal(640) expect(result[0].height).to.equal(480) }) + + it('should add pvid to adtag urls when present', function () { + const result = spec.interpretResponse({body: [PVID_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS}) + + expect(result[0].ad).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c') + expect(result[0].vastUrl).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c') + }) }) }) From 0f6e314a9fc3a974d214f2c997ce67ff73add7a8 Mon Sep 17 00:00:00 2001 From: Newton <5769156+iamnewton@users.noreply.github.com> Date: Mon, 21 Jun 2021 13:39:27 -0700 Subject: [PATCH 1182/1476] chore: update spelling/grammar (#7067) --- PR_REVIEW.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 84131c177a3..1152e2942bf 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -1,4 +1,5 @@ ## Summary + We take PR review seriously. Please read https://medium.com/@mrjoelkemp/giving-better-code-reviews-16109e0fdd36#.xa8lc4i23 to understand how a PR review should be conducted. Be rational and strict in your review, make sure you understand exactly what the submitter's intent is. Anyone in the community can review a PR, but a Prebid Org member is also required. A Prebid Org member should take ownership of a PR and do the initial review. If the PR is for a standard bid adapter or a standard analytics adapter, just the one review from a core member is sufficient. The reviewer will check against [required conventions](http://prebid.org/dev-docs/bidder-adaptor.html#required-adapter-conventions) and may merge the PR after approving and confirming that the documentation PR against prebid.org is open and linked to the issue. @@ -11,14 +12,15 @@ General gulp commands include separate commands for serving the codebase on a bu - Run `gulp review-start`, adding the host parameter `gulp review-start --host=0.0.0.0` will bind to all IPs on the machine - A page will open which provides a hub for common reviewer tools. - - If you need to manually acceess the tools: + - If you need to manually access the tools: - Navigate to build/coverage/lcov-report/index.html to view coverage - Navigate to integrationExamples/gpt/hellow_world.html for basic integration testing - - The hello_world.html and other exampls can be edited and used as needed to verify functionality + - The hello_world.html and other examples can be edited and used as needed to verify functionality ### General PR review Process + - All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. -- Checkout the branch (these instructions are available on the github PR page as well). +- Checkout the branch (these instructions are available on the GitHub PR page as well). - Verify PR is a single change type. Example, refactor OR bugfix. If more than 1 type, ask submitter to break out requests. - Verify code under review has at least 80% unit test coverage. If legacy code doesn't have enough unit test coverage, require that additional unit tests to be included in the PR. - Verify tests are green in Travis-ci + local build by running `gulp serve` | `gulp test` @@ -29,13 +31,14 @@ General gulp commands include separate commands for serving the codebase on a bu - If the change results in needing updates to docs (such as public API change, module interface etc), add a label for "needs docs" and inform the submitter they must submit a docs PR to update the appropriate area of Prebid.org **before the PR can merge**. Help them with finding where the docs are located on prebid.org if needed. - If all above is good, add a `LGTM` comment and, if the change is in PBS-core or is an important module like the prebidServerBidAdapter, request 1 additional core member to review. - Once there are 2 `LGTM` on the PR, merge to master -- The [draft release](https://github.com/prebid/Prebid.js/releases) notes are managed by [release drafter](https://github.com/release-drafter/release-drafter). To get the PR added to the release notes do the steps below. A github action will use that information to build the release notes. +- The [draft release](https://github.com/prebid/Prebid.js/releases) notes are managed by [release drafter](https://github.com/release-drafter/release-drafter). To get the PR added to the release notes do the steps below. A GitHub action will use that information to build the release notes. - Adjust the PR Title to be appropriate for release notes - Add a label for `feature`, `maintenance`, `fix`, `bugfix` or `bug` to categorize the PR - - Add a semver label of `major`, `minor` or `patch` to indicate the scope of change + - Add a SemVer label of `major`, `minor` or `patch` to indicate the scope of change ### Reviewing a New or Updated Bid Adapter -Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/bidder-adaptor.html + +Documentation: https://docs.prebid.org/dev-docs/bidder-adaptor.html Follow steps above for general review process. In addition, please verify the following: - Verify the biddercode and aliases are valid: @@ -67,14 +70,16 @@ Follow steps above for general review process. In addition, please verify the fo - After a new adapter is approved, let the submitter know they may open a PR in the [headerbid-expert repository](https://github.com/prebid/headerbid-expert) to have their adapter recognized by the [Headerbid Expert extension](https://chrome.google.com/webstore/detail/headerbid-expert/cgfkddgbnfplidghapbbnngaogeldmop). The PR should be to the [bidder patterns file](https://github.com/prebid/headerbid-expert/blob/master/bidderPatterns.js), adding an entry with their adapter's name and the url the adapter uses to send and receive bid responses. ### Reviewing a New or Updated Analytics Adapter -Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html + +Documentation: https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html No additional steps above the general review process and making sure it conforms to the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html). Make sure there's a docs pull request ### Reviewing a New or Updated User ID Sub-Module -Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/modules/userId.html#id-providers + +Documentation: https://docs.prebid.org/dev-docs/modules/userId.html#id-providers Follow steps above for general review process. In addition: - Try running the new user ID module with a basic config and confirm it hits the endpoint and stores the results. @@ -102,9 +107,11 @@ Follow steps above for general review process. In addition: - make sure there's a docs pull request ### Reviewing a New or Updated Real-Time-Data Sub-Module -Documentation they're supposed to be following is https://docs.prebid.org/dev-docs/add-rtd-submodule.html + +Documentation: https://docs.prebid.org/dev-docs/add-rtd-submodule.html Follow steps above for general review process. In addition: + - The RTD Provider must include a `providerRtdProvider.md` file. This file must have example parameters and document a sense of what to expect: what should change in the bidrequest, or what targeting data should be added? - Try running the new sub-module and confirm the provided test parameters. - Confirm that the module @@ -118,9 +125,7 @@ Follow steps above for general review process. In addition: ## Ticket Coordinator -Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. Every Monday morning a reminder is -sent to the prebid-js slack channel with a link to the spreadsheet. If you're on rotation, please check that list each -Monday to see if you're on-duty. +Each week, Prebid Org assigns one person to keep an eye on incoming issues and PRs. Every Monday morning a reminder is sent to the prebid-js slack channel with a link to the spreadsheet. If you're on rotation, please check that list each Monday to see if you're on-duty. When on-duty: - Review issues and PRs at least once per weekday for new items. Encourage a 48 "SLA" on PRs/issues assigned. Aim for touchpoint once every 48/hours. From 659a9717505f438fd670ab329e3d83da02193d57 Mon Sep 17 00:00:00 2001 From: videobyte20 <85643547+videobyte20@users.noreply.github.com> Date: Tue, 22 Jun 2021 16:02:28 +0700 Subject: [PATCH 1183/1476] VideoByte Bid Adapter: add new bid adapter (#7036) * videobyte prebid adapter * videobyte - added user sync support --- modules/videobyteBidAdapter.js | 311 +++++++++ modules/videobyteBidAdapter.md | 78 +++ test/spec/modules/videobyteBidAdapter_spec.js | 626 ++++++++++++++++++ 3 files changed, 1015 insertions(+) create mode 100644 modules/videobyteBidAdapter.js create mode 100644 modules/videobyteBidAdapter.md create mode 100644 test/spec/modules/videobyteBidAdapter_spec.js diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js new file mode 100644 index 00000000000..076329d1c8b --- /dev/null +++ b/modules/videobyteBidAdapter.js @@ -0,0 +1,311 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import {VIDEO} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'videobyte'; +const DEFAULT_BID_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_NET_REVENUE = true; +const VIDEO_ORTB_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity' +]; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO], + VERSION: '1.0.0', + ENDPOINT: 'https://x.videobyte.com/ortb/', + + /** + * 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) { + return validateVideo(bidRequest); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param bidRequests - an array of bid requests + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (bidRequests, bidderRequest) { + if (!bidRequests) { + return; + } + return bidRequests.map(bidRequest => { + const {params} = bidRequest; + let pubId = params.pubId; + if (bidRequest.params.video && bidRequest.params.video.e2etest) { + utils.logMessage('E2E test mode enabled'); + pubId = 'e2etest' + } + return { + method: 'POST', + url: spec.ENDPOINT + pubId, + data: JSON.stringify(buildRequestData(bidRequest, bidderRequest)), + } + }); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse) { + const bidResponses = []; + const response = (serverResponse || {}).body; + // one seat with (optional) bids for each impression + if (response && response.seatbid && response.seatbid.length === 1 && response.seatbid[0].bid && response.seatbid[0].bid.length === 1) { + const bid = response.seatbid[0].bid[0] + if (bid.adm && bid.price) { + let bidResponse = { + requestId: response.id, + bidderCode: spec.code, + cpm: bid.price, + width: bid.w, + height: bid.h, + ttl: DEFAULT_BID_TTL, + creativeId: bid.crid, + netRevenue: DEFAULT_NET_REVENUE, + currency: DEFAULT_CURRENCY, + mediaType: 'video', + vastXml: bid.adm, + meta: { + advertiserDomains: bid.adomain + } + }; + bidResponses.push(bidResponse) + } + } else { + utils.logError('invalid server response received'); + } + 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) { + let syncs = []; + + if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { + return syncs; + } + + serverResponses.forEach(resp => { + const userSync = utils.deepAccess(resp, 'body.ext.usersync'); + if (userSync) { + let syncDetails = []; + Object.keys(userSync).forEach(key => { + const value = userSync[key]; + if (value.syncs && value.syncs.length) { + syncDetails = syncDetails.concat(value.syncs); + } + }); + syncDetails.forEach(syncDetails => { + syncs.push({ + type: syncDetails.type === 'iframe' ? 'iframe' : 'image', + url: syncDetails.url + }); + }); + + // if iframe is enabled return only iframe (videobyte) + // if iframe is disabled, we can proceed to pixels if any + if (syncOptions.iframeEnabled) { + syncs = syncs.filter(s => s.type === 'iframe') + } else if (syncOptions.pixelEnabled) { + syncs = syncs.filter(s => s.type === 'image') + } + } + }); + return syncs; + } + +} + +// BUILD REQUESTS: VIDEO +function buildRequestData(bidRequest, bidderRequest) { + const {params} = bidRequest; + + const videoAdUnit = utils.deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {}); + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams // Bidder Specific overrides + }; + + if (bidRequest.params.video && bidRequest.params.video.e2etest) { + videoParams.playerSize = [[640, 480]] + videoParams.conext = 'instream' + } + + const video = { + w: parseInt(videoParams.playerSize[0][0], 10), + h: parseInt(videoParams.playerSize[0][1], 10), + } + + // Obtain all ORTB params related video from Ad Unit + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; + } + }); + + // Placement Inference Rules: + // - If no placement is defined then default to 1 (In Stream) + video.placement = video.placement || 2; + + // - If product is instream (for instream context) then override placement to 1 + if (params.context === 'instream') { + video.startdelay = video.startdelay || 0; + video.placement = 1; + } + + // bid floor + const bidFloorRequest = { + currency: bidRequest.params.cur || 'USD', + mediaType: 'video', + size: '*' + }; + let floorData = bidRequest.params + if (utils.isFn(bidRequest.getFloor)) { + floorData = bidRequest.getFloor(bidFloorRequest); + } else { + if (params.bidfloor) { + floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; + } + } + + const openrtbRequest = { + id: bidRequest.bidId, + imp: [ + { + id: '1', + video: video, + secure: isSecure() ? 1 : 0, + bidfloor: floorData.floor, + bidfloorcur: floorData.currency + } + ], + site: { + domain: window.location.hostname, + page: window.location.href, + ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null + }, + ext: { + hb: 1, + prebidver: '$prebid.version$', + adapterver: spec.VERSION, + }, + }; + + // content + if (videoParams.content && utils.isPlainObject(videoParams.content)) { + openrtbRequest.site.content = {}; + const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; + const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; + const contentArrayKeys = ['cat']; + const contentObjectKeys = ['ext']; + for (const contentKey in videoBidderParams.content) { + if ( + (contentStringKeys.indexOf(contentKey) > -1 && utils.isStr(videoParams.content[contentKey])) || + (contentNumberkeys.indexOf(contentKey) > -1 && utils.isNumber(videoParams.content[contentKey])) || + (contentObjectKeys.indexOf(contentKey) > -1 && utils.isPlainObject(videoParams.content[contentKey])) || + (contentArrayKeys.indexOf(contentKey) > -1 && utils.isArray(videoParams.content[contentKey]) && + videoParams.content[contentKey].every(catStr => utils.isStr(catStr)))) { + openrtbRequest.site.content[contentKey] = videoParams.content[contentKey]; + } else { + utils.logMessage('videobyte bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); + } + } + } + + // adding schain object + if (bidRequest.schain) { + utils.deepSetValue(openrtbRequest, 'source.ext.schain', bidRequest.schain); + openrtbRequest.source.ext.schain.nodes[0].rid = openrtbRequest.id; + } + + // Attaching GDPR Consent Params + if (bidderRequest.gdprConsent) { + utils.deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // CCPA + if (bidderRequest.uspConsent) { + utils.deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + return openrtbRequest; +} + +function validateVideo(bidRequest) { + if (!bidRequest.params) { + return false; + } + + if (!bidRequest.params.pubId) { + utils.logError('failed validation: publisher id not declared'); + return false; + } + + const videoAdUnit = utils.deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {}); + + if (videoBidderParams && videoBidderParams.e2etest) { + return true; + } + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams // Bidder Specific overrides + }; + + if (!videoParams.context) { + utils.logError('failed validation: context id not declared'); + return false; + } + if (videoParams.context !== 'instream') { + utils.logError('failed validation: only context instream is supported '); + return false; + } + + if (typeof videoParams.playerSize === 'undefined' || !Array.isArray(videoParams.playerSize) || !Array.isArray(videoParams.playerSize[0])) { + utils.logError('failed validation: player size not declared or is not in format [[w,h]]'); + return false; + } + + return true; +} + +function isSecure() { + return document.location.protocol === 'https:'; +} + +registerBidder(spec); diff --git a/modules/videobyteBidAdapter.md b/modules/videobyteBidAdapter.md new file mode 100644 index 00000000000..fc2b0bce4b5 --- /dev/null +++ b/modules/videobyteBidAdapter.md @@ -0,0 +1,78 @@ +# Overview + +``` +Module Name: VideoByte Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@videobyte.com +``` + +# Description + +Module that connects to VideoByte's demand sources + +*Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. + +## Instream Video adUnit using mediaTypes.video +*Note:* By default, the adapter will read the mandatory parameters from mediaTypes.video. +*Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. +``` + var adUnits = [ + { + code: 'video1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], + } + }, + bids: [ + { + bidder: 'videobyte', + params: { + bidfloor: 0.5, + pubId: 'e2etest' + } + } + ] + } + ] +``` + +# End To End testing mode +By passing bid.params.video.e2etest = true you will be able to receive a test creative + +``` +var adUnits = [ + { + code: 'video-1', + mediaTypes: { + video: { + context: "instream", + playerSize: [[640, 480]], + mimes: ['video/mp4'], + } + }, + bids: [ + { + bidder: 'videobyte', + params: { + video: { + e2etest: true + } + } + } + ] + } +] +``` diff --git a/test/spec/modules/videobyteBidAdapter_spec.js b/test/spec/modules/videobyteBidAdapter_spec.js new file mode 100644 index 00000000000..b8e41829031 --- /dev/null +++ b/test/spec/modules/videobyteBidAdapter_spec.js @@ -0,0 +1,626 @@ +import { expect } from 'chai'; +import { spec } from 'modules/videobyteBidAdapter.js'; + +describe('VideoByteBidAdapter', function () { + let bidRequest; + let bidderRequest = { + 'bidderCode': 'videobyte', + 'auctionId': 'e158486f-8c7f-472f-94ce-b0cbfbb50ab4', + 'bidderRequestId': '1e498b84fffc39', + 'bids': bidRequest, + 'auctionStart': 1520001292880, + 'timeout': 3000, + 'start': 1520001292884, + 'doneCbCallCount': 0, + 'refererInfo': { + 'numIframes': 1, + 'reachedTop': true, + 'referer': 'test.com' + } + }; + let mockConfig; + + beforeEach(function () { + bidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]], + } + }, + bidder: 'videobyte', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + sid: 134, + rewarded: 1, + placement: 1, + hp: 1, + inventoryid: 123 + }, + site: { + id: 1, + page: 'https://test.com', + referrer: 'http://test.com' + }, + pubId: 'vb12345' + } + }; + }); + + describe('spec.isBidRequestValid', function () { + it('should return false when mediaTypes is empty', function () { + bidRequest.mediaTypes = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return true (skip validations) when e2etest = true', function () { + bidRequest.params.video = { + e2etest: true + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mediaTypes.video has all mandatory params', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSize: [[640, 480]], + mimes: ['video/mp4', 'application/javascript'], + } + bidRequest.params.video = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when params.video has all override params instead of mediaTypes.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream' + }; + bidRequest.params.video = { + playerSize: [[640, 480]], + mimes: ['video/mp4', 'application/javascript'] + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mimes is passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSize: [[640, 480]] + }; + bidRequest.video = { + mimes: ['video/mp4', 'application/javascript'] + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return false when both mediaTypes.video and params.video Objects are missing', function () { + bidRequest.mediaTypes = {}; + bidRequest.params = { + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when both mediaTypes.video and params.video are missing mimes and player size', function () { + bidRequest.mediaTypes = { + video: { + context: 'instream' + } + }; + bidRequest.params = { + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when the "pubId" param is missing', function () { + bidRequest.params = { + video: { + playerWidth: 480, + playerHeight: 640, + mimes: ['video/mp4', 'application/javascript'], + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + it('should return false when the "pubId" param is missing', function () { + bidRequest.params = { + video: { + playerWidth: 480, + playerHeight: 640, + mimes: ['video/mp4', 'application/javascript'], + } + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when no bid params are passed', function () { + bidRequest.params = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + }); + + describe('spec.buildRequests', function () { + it('should create a POST request for every bid', function () { + const requests = spec.buildRequests([bidRequest], bidderRequest); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(spec.ENDPOINT + bidRequest.params.pubId); + }); + + it('should attach request data', function () { + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + const [width, height] = bidRequest.sizes; + const VERSION = '1.0.0'; + expect(data.imp[0].video.w).to.equal(width); + expect(data.imp[0].video.h).to.equal(height); + expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); + expect(data.ext.prebidver).to.equal('$prebid.version$'); + expect(data.ext.adapterver).to.equal(spec.VERSION); + }); + + it('should set pubId to e2etest when bid.params.video.e2etest = true', function () { + bidRequest.params.video.e2etest = true; + const requests = spec.buildRequests([bidRequest], bidderRequest); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(spec.ENDPOINT + 'e2etest'); + }); + + it('should attach End 2 End test data', function () { + bidRequest.params.video.e2etest = true; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].bidfloor).to.not.exist; + expect(data.imp[0].video.w).to.equal(640); + expect(data.imp[0].video.h).to.equal(480); + }); + + it('should send Global schain', function () { + bidRequest.params.video.sid = null; + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'some-platform.com', + sid: '111111', + rid: bidRequest.id, + hp: 1 + }] + }; + bidRequest.schain = globalSchain; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + const schain = data.source.ext.schain; + expect(schain.nodes.length).to.equal(1); + expect(schain).to.deep.equal(globalSchain); + }); + + describe('content object validations', function () { + it('should not accept content object if value is Undefined ', function () { + bidRequest.params.video.content = null; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.undefined; + }); + it('should not accept content object if value is is Array ', function () { + bidRequest.params.video.content = []; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.undefined; + }); + it('should not accept content object if value is Number ', function () { + bidRequest.params.video.content = 123456; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.undefined; + }); + it('should not accept content object if value is String ', function () { + bidRequest.params.video.content = 'keyValuePairs'; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.undefined; + }); + it('should not accept content object if value is Boolean ', function () { + bidRequest.params.video.content = true; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.undefined; + }); + it('should accept content object if value is Object ', function () { + bidRequest.params.video.content = {}; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.a('object'); + }); + + it('should not append unsupported content object keys', function () { + bidRequest.params.video.content = { + fake: 'news', + unreal: 'param', + counterfit: 'data' + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.empty; + }); + + it('should not append content string parameters if value is not string ', function () { + bidRequest.params.video.content = { + id: 1234, + title: ['Title'], + series: ['Series'], + season: ['Season'], + genre: ['Genre'], + contentrating: {1: 'C-Rating'}, + language: {1: 'EN'} + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty + }); + it('should not append content Number parameters if value is not Number ', function () { + bidRequest.params.video.content = { + episode: '1', + context: 'context', + livestream: {0: 'stream'}, + len: [360], + prodq: [1], + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty + }); + it('should not append content Array parameters if value is not Array ', function () { + bidRequest.params.video.content = { + cat: 'categories', + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty + }); + it('should not append content ext if value is not Object ', function () { + bidRequest.params.video.content = { + ext: 'content.ext', + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.be.a('object'); + expect(data.site.content).to.be.empty + }); + it('should append supported parameters if value match validations ', function () { + bidRequest.params.video.content = { + id: '1234', + title: 'Title', + series: 'Series', + season: 'Season', + cat: [ + 'IAB1' + ], + genre: 'Genre', + contentrating: 'C-Rating', + language: 'EN', + episode: 1, + prodq: 1, + context: 1, + livestream: 0, + len: 360, + ext: {} + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.site.content).to.deep.equal(bidRequest.params.video.content); + }); + }); + }); + describe('price floor module validations', function () { + beforeEach(function () { + bidRequest.getFloor = (floorObj) => { + return { + floor: bidRequest.floors.values[floorObj.mediaType + '|640x480'], + currency: floorObj.currency, + mediaType: floorObj.mediaType + } + } + }); + + it('should get bidfloor from getFloor method', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.floors = { + currency: 'EUR', + values: { + 'video|640x480': 5.55 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + it('should get bidfloor from params method', function () { + bidRequest.params.bidfloor = 4.0; + bidRequest.params.currency = 'EUR'; + bidRequest.getFloor = null; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(4.0); + expect(data.imp[0].bidfloorcur).to.equal('EUR'); + }); + + it('should use adUnit/module currency & floor instead of bid.params.bidfloor', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.params.bidfloor = 3.33; + bidRequest.floors = { + currency: 'EUR', + values: { + 'video|640x480': 5.55 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + + it('should load video floor when multi-format adUnit is present', function () { + bidRequest.params.cur = 'EUR'; + bidRequest.mediaTypes.banner = { + sizes: [ + [640, 480] + ] + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|640x480': 2.22, + 'video|640x480': 9.99 + } + }; + const requests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(9.99); + }) + }) + + describe('spec.interpretResponse', function () { + it('should return no bids if the response is not valid', function () { + const bidResponse = spec.interpretResponse({ + body: null + }, { + bidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response "nurl" and "adm" are missing', function () { + const serverResponse = { + seatbid: [{ + bid: [{ + price: 6.01 + }] + }] + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + bidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response "price" is missing', function () { + const serverResponse = { + seatbid: [{ + bid: [{ + adm: '' + }] + }] + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + bidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return a valid video bid response with just "adm"', function () { + const serverResponse = { + id: '123', + seatbid: [{ + bid: [{ + id: 1, + adid: 123, + crid: 2, + price: 6.01, + adm: '', + adomain: [ + 'videobyte.com' + ], + w: 640, + h: 480 + }] + }], + cur: 'USD' + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + bidRequest + }); + let o = { + requestId: serverResponse.id, + bidderCode: spec.code, + cpm: serverResponse.seatbid[0].bid[0].price, + creativeId: serverResponse.seatbid[0].bid[0].crid, + vastXml: serverResponse.seatbid[0].bid[0].adm, + width: 640, + height: 480, + mediaType: 'video', + currency: 'USD', + ttl: 300, + netRevenue: true, + meta: { + advertiserDomains: ['videobyte.com'] + } + }; + expect(bidResponse[0]).to.deep.equal(o); + }); + + it('should default ttl to 300', function () { + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse[0].ttl).to.equal(300); + }); + it('should not allow ttl above 3601, default to 300', function () { + bidRequest.params.video.ttl = 3601; + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse[0].ttl).to.equal(300); + }); + it('should not allow ttl below 1, default to 300', function () { + bidRequest.params.video.ttl = 0; + const serverResponse = {seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse[0].ttl).to.equal(300); + }); + }); + + describe('when GDPR and uspConsent applies', function () { + beforeEach(function () { + bidderRequest = { + 'gdprConsent': { + 'consentString': 'test-gdpr-consent-string', + 'gdprApplies': true + }, + 'uspConsent': '1YN-', + 'bidderCode': 'videobyte', + 'auctionId': 'e158486f-8c7f-472f-94ce-b0cbfbb50ab4', + 'bidderRequestId': '1e498b84fffc39', + 'bids': bidRequest, + 'auctionStart': 1520001292880, + 'timeout': 3000, + 'start': 1520001292884, + 'doneCbCallCount': 0, + 'refererInfo': { + 'numIframes': 1, + 'reachedTop': true, + 'referer': 'test.com' + } + }; + + mockConfig = { + consentManagement: { + gdpr: { + cmpApi: 'iab', + timeout: 3000, + allowAuctionWithoutConsent: 'cancel' + }, + usp: { + cmpApi: 'iab', + timeout: 1000, + allowAuctionWithoutConsent: 'cancel' + } + } + }; + }); + + it('should send a signal to specify that GDPR applies to this request', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(request[0].data); + expect(data.regs.ext.gdpr).to.equal(1); + }); + + it('should send the consent string', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(request[0].data); + expect(data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); + }); + + it('should send the uspConsent string', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(request[0].data); + expect(data.regs.ext.us_privacy).to.equal(bidderRequest.uspConsent); + }); + + it('should send the uspConsent and GDPR ', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(request[0].data); + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.regs.ext.us_privacy).to.equal(bidderRequest.uspConsent); + }); + }); + + describe('getUserSyncs', function () { + const ortbResponse = { + 'body': { + 'ext': { + 'usersync': { + 'sovrn': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlsovrn', + 'type': 'iframe' + } + ] + }, + 'appnexus': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlappnexus', + 'type': 'pixel' + } + ] + } + } + } + } + }; + it('handles no parameters', function () { + let opts = spec.getUserSyncs({}); + expect(opts).to.be.an('array').that.is.empty; + }); + it('returns non if sync is not allowed', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + + expect(opts).to.be.an('array').that.is.empty; + }); + + it('iframe sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [ortbResponse]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal(ortbResponse.body.ext.usersync['sovrn'].syncs[0].url); + }); + + it('pixel sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [ortbResponse]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal(ortbResponse.body.ext.usersync['appnexus'].syncs[0].url); + }); + + it('all sync enabled should return only iframe result', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [ortbResponse]); + + expect(opts.length).to.equal(1); + }); + }); +}); From 11bc5de91ba6577c4476cfbbb551066ab01f0c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Udi=20Talias=20=E2=9A=9B=EF=B8=8F?= Date: Tue, 22 Jun 2021 12:56:13 +0300 Subject: [PATCH 1184/1476] VIdazoo Bid Adapter: Export gvlid on the spec object (#7072) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): export gvlid on spec Co-authored-by: roman --- modules/vidazooBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 36705bae199..3fb94870d3f 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -const GLVID = 744; +const GVLID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; const BIDDER_CODE = 'vidazoo'; const BIDDER_VERSION = '1.0.0'; @@ -24,7 +24,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'pubcid': 1, 'tdid': 1, }; -const storage = getStorageManager(GLVID); +const storage = getStorageManager(GVLID); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.cootlogix.com`; @@ -270,6 +270,7 @@ export function tryParseJSON(value) { export const spec = { code: BIDDER_CODE, version: BIDDER_VERSION, + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid, buildRequests, From 8d4c8b9119ccac4adeb21d2a821b08b04bd224d7 Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 22 Jun 2021 15:20:59 +0200 Subject: [PATCH 1185/1476] Adnuntius Bid Adapter: Get User Id from local storage (#7063) * Master merge issues * Adnuntius Bid Adapter: Added tests for gdpr and segments * Moved segments to read from ortb2 instead of a custom value. * Changed bidder to read segments from ortb2. * fixing lgtm alert * Read USI from meta-information in browser. --- modules/adnuntiusBidAdapter.js | 44 ++++++++++++++----- test/spec/modules/adnuntiusBidAdapter_spec.js | 19 +++++--- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 5e10219188d..ba9a8bdddb5 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -2,6 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://delivery.adnuntius.com/i?tzo='; @@ -25,6 +26,22 @@ const getSegmentsFromOrtb = function (ortb2) { return segments } +const handleMeta = function () { + const storage = getStorageManager(GVLID, 'adnuntius') + let adnMeta = null + if (storage.localStorageIsEnabled()) { + adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) + } + const meta = (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} + utils.logMessage('STORE', adnMeta, meta) + return meta +} + +const getUsi = function (meta, ortb2, bidderRequest) { + const usi = (meta !== null) ? meta.usi : false; + return usi +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -37,13 +54,18 @@ export const spec = { const networks = {}; const bidRequests = {}; const requests = []; + const request = []; const ortb2 = config.getConfig('ortb2'); + const adnMeta = handleMeta() + const usi = getUsi(adnMeta, ortb2, bidderRequest) const segments = getSegmentsFromOrtb(ortb2); const tzo = new Date().getTimezoneOffset(); const gdprApplies = utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); const consentString = utils.deepAccess(bidderRequest, 'gdprConsent.consentString'); - const reqConsent = (gdprApplies !== undefined) ? '&consentString=' + consentString : ''; - const reqSegments = (segments.length > 0) ? '&segments=' + segments.join(',') : ''; + 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); for (var i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] @@ -55,6 +77,8 @@ export const spec = { networks[network] = networks[network] || {}; networks[network].adUnits = networks[network].adUnits || []; + if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.referer; + if (adnMeta) networks[network].metaData = adnMeta; networks[network].adUnits.push({ ...targeting, auId: bid.params.auId, targetId: bid.bidId }); } @@ -63,7 +87,7 @@ export const spec = { const network = networkKeys[j]; requests.push({ method: 'POST', - url: ENDPOINT_URL + tzo + '&format=json' + reqSegments + reqConsent, + url: ENDPOINT_URL + tzo + '&' + request.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); @@ -76,19 +100,19 @@ export const spec = { const adUnits = serverResponse.body.adUnits; const bidResponsesById = adUnits.reduce((response, adUnit) => { if (adUnit.matchedAdCount >= 1) { - const bid = adUnit.ads[0]; - const effectiveCpm = (bid.cpc && bid.cpm) ? bid.bid.amount + bid.cpm.amount : (bid.cpc) ? bid.bid.amount : (bid.cpm) ? bid.cpm.amount : 0; + const ad = adUnit.ads[0]; + const effectiveCpm = (ad.bid) ? ad.bid.amount * 1000 : 0; return { ...response, [adUnit.targetId]: { requestId: adUnit.targetId, cpm: effectiveCpm, - width: Number(bid.creativeWidth), - height: Number(bid.creativeHeight), - creativeId: bid.creativeId, - currency: (bid.bid) ? bid.bid.currency : 'EUR', + width: Number(ad.creativeWidth), + height: Number(ad.creativeHeight), + creativeId: ad.creativeId, + currency: (ad.bid) ? ad.bid.currency : 'EUR', meta: { - advertiserDomains: (bid.destinationUrls.destination) ? [bid.destinationUrls.destination.split('/')[2]] : [] + advertiserDomains: (ad.destinationUrls.destination) ? [ad.destinationUrls.destination.split('/')[2]] : [] }, netRevenue: false, diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 9c9bf0e9914..f5f3846cace 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -3,16 +3,25 @@ import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' import { spec } from 'modules/adnuntiusBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; +import * as utils from 'src/utils.js'; +import { getStorageManager } from 'src/storageManager.js'; describe('adnuntiusBidAdapter', function () { + const URL = 'https://delivery.adnuntius.com/i?tzo='; + const GVLID = 855; + const usi = utils.generateUUID() + const meta = [{ key: 'usi', value: usi }] + const storage = getStorageManager(GVLID, 'adnuntius') + storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + afterEach(function () { config.resetConfig(); }); const tzo = new Date().getTimezoneOffset(); - const ENDPOINT_URL = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; - // const ENDPOINT_URL_SEGMENTS_ = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; - const ENDPOINT_URL_SEGMENTS = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&segments=segment1,segment2,segment3`; - const ENDPOINT_URL_CONSENT = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&consentString=consentString`; + const ENDPOINT_URL = `${URL}${tzo}&format=json&userId=${usi}`; + // const ENDPOINT_URL_SEGMENTS_ = `${URL}${tzo}&format=json`; + 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 = [ @@ -121,7 +130,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\"}]}'); + expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); }); it('should pass segments if available in config', function () { From afb4fc3de9366e2aa6b9ec4cd12b4b06f9a8ab1d Mon Sep 17 00:00:00 2001 From: hbanalytics <55453525+hbanalytics@users.noreply.github.com> Date: Tue, 22 Jun 2021 17:07:56 +0300 Subject: [PATCH 1186/1476] YIELDONE Bid Adapter: support adomains (#7071) * Added Y1 Analytics Adapter * rename y1AnalyticsAdapter in yieldoneAnalyticsAdapter * Yieldone Bid Adapter: fixes from lint check * Yieldone Analytics Adapter: fix endpoint protocol * Added spec file for yieldone Analytics Adapter * Add adUnitName to analytics data for Yieldone Analytics Adapter * Fix yieldone Analytics Adapter to log only id from adUnitPath * Fix bug with timeout event in Yieldone Analytics Adapter * Update yieldone analytics adapter to remove excess 'ad' field from data * Update yieldone analytics adapter * Yieldone Analytics Adapter: remove dispensable events from log * Platform One Analytics Adapter: fixes after review * Fix empty events in Yieldone Analytics Adapter * YIELDONE Bidder Adapter: support adomains --- modules/yieldoneBidAdapter.js | 5 ++++- test/spec/modules/yieldoneBidAdapter_spec.js | 13 ++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 574967db291..14a9b7dae48 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -82,7 +82,10 @@ export const spec = { currency: currency, netRevenue: netRevenue, ttl: config.getConfig('_bidderTimeout'), - referrer: referrer + referrer: referrer, + meta: { + advertiserDomains: response.adomain ? response.adomain : [] + }, }; if (response.adTag && renderId === 'ViewableRendering') { diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index 84065682297..91bbc142236 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -132,7 +132,10 @@ describe('yieldoneBidAdapter', function() { 'crid': '2494768', 'currency': 'JPY', 'statusMessage': 'Bid available', - 'dealId': 'P1-FIX-7800-DSP-MON' + 'dealId': 'P1-FIX-7800-DSP-MON', + 'admoain': [ + 'www.example.com' + ] } }; @@ -148,6 +151,11 @@ describe('yieldoneBidAdapter', function() { 'netRevenue': true, 'ttl': 3000, 'referrer': '', + 'meta': { + 'advertiserDomains': [ + 'www.example.com' + ] + }, 'mediaType': 'banner', 'ad': '' }]; @@ -199,6 +207,9 @@ describe('yieldoneBidAdapter', function() { 'netRevenue': true, 'ttl': 3000, 'referrer': '', + 'meta': { + 'advertiserDomains': [] + }, 'mediaType': 'video', 'vastXml': '', 'renderer': { From d8d47db8af0a49fd4d3e5479178d8f0d392897b8 Mon Sep 17 00:00:00 2001 From: Alexey Sukhikh Date: Tue, 22 Jun 2021 17:36:30 +0300 Subject: [PATCH 1187/1476] GrowAdvertising Bid Adapter: add new adapter (#7054) * Add growadvertising adapter + tests * Revert package-lock.json * Add growadvertising docs * Fix typo * Rename docs + add test example --- modules/growadvertisingBidAdapter.js | 117 +++++++++++++ modules/growadvertisingBidAdapter.md | 40 +++++ .../modules/growadvertisingBidAdapter_spec.js | 161 ++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 modules/growadvertisingBidAdapter.js create mode 100644 modules/growadvertisingBidAdapter.md create mode 100644 test/spec/modules/growadvertisingBidAdapter_spec.js diff --git a/modules/growadvertisingBidAdapter.js b/modules/growadvertisingBidAdapter.js new file mode 100644 index 00000000000..b5103a06fa8 --- /dev/null +++ b/modules/growadvertisingBidAdapter.js @@ -0,0 +1,117 @@ +'use strict'; + +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'growads'; + +export const spec = { + code: BIDDER_CODE, + + isBidRequestValid: function (bid) { + return bid.params && !!bid.params.zoneId; + }, + + buildRequests: function (validBidRequests) { + let zoneId; + let domain; + let requestURI; + let data = {}; + const zoneCounters = {}; + + return validBidRequests.map(bidRequest => { + zoneId = utils.getBidIdParameter('zoneId', bidRequest.params); + domain = utils.getBidIdParameter('domain', bidRequest.params); + + if (!(zoneId in zoneCounters)) { + zoneCounters[zoneId] = 0; + } + + if (typeof domain === 'undefined' || domain.length === 0) { + domain = 'portal.growadvertising.com'; + } + + requestURI = 'https://' + domain + '/adserve/bid'; + data = { + type: 'prebidjs', + zoneId: zoneId, + i: zoneCounters[zoneId] + }; + zoneCounters[zoneId]++; + + return { + method: 'GET', + url: requestURI, + data: data, + bidRequest: bidRequest + }; + }); + }, + + interpretResponse: function (serverResponse, bidRequest) { + const request = bidRequest.bidRequest; + let bidResponses = []; + let CPM; + let width; + let height; + let response; + let isCorrectSize = false; + let isCorrectCPM = true; + let minCPM; + let maxCPM; + + let body = serverResponse.body; + + try { + response = JSON.parse(body); + } catch (ex) { + response = body; + } + + if (response && response.status === 'success' && request) { + CPM = parseFloat(response.cpm); + width = parseInt(response.width); + height = parseInt(response.height); + + minCPM = utils.getBidIdParameter('minCPM', request.params); + maxCPM = utils.getBidIdParameter('maxCPM', request.params); + width = parseInt(response.width); + height = parseInt(response.height); + + // Ensure response CPM is within the given bounds + if (minCPM !== '' && CPM < parseFloat(minCPM)) { + isCorrectCPM = false; + } + if (maxCPM !== '' && CPM > parseFloat(maxCPM)) { + isCorrectCPM = false; + } + + // Ensure that response ad matches one of the placement sizes. + utils._each(utils.deepAccess(request, 'mediaTypes.banner.sizes', []), function (size) { + if (width === size[0] && height === size[1]) { + isCorrectSize = true; + } + }); + + if (isCorrectCPM && isCorrectSize) { + bidResponses.push({ + requestId: request.bidId, + bidderCode: request.bidder, + creativeId: response.creativeId, + cpm: CPM, + width: width, + height: height, + ad: response.ad, + currency: response.currency, + netRevenue: true, + ttl: response.ttl, + referrer: utils.deepAccess(request, 'refererInfo.referer') + }); + } + } + + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/growadvertisingBidAdapter.md b/modules/growadvertisingBidAdapter.md new file mode 100644 index 00000000000..f20b853a33f --- /dev/null +++ b/modules/growadvertisingBidAdapter.md @@ -0,0 +1,40 @@ +--- +layout: bidder +title: GrowAdvertising +description: Prebid GrowAdvertising Bidder Adapter +pbjs: true +biddercode: growads +media_types: banner +--- + +### Bid Params + +| Name | Scope | Description | Example | Type | +|----------|----------|-----------|--------------------|----------| +| `zoneId` | required | ZoneId ID | `'unique-zone-id'` | `string` | +| `domain` | optional | Domain | `'example.org'` | `string` | +| `minCPM` | optional | Minimum CPM | `1.5` | `float` | +| `maxCPM` | optional | Maximum CPM | `10.8` | `float` | + +# Test Parameters +``` +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: "growads", + params: { + zoneId: '6WG9JK8-RvKai86-yL980YC-kQFoqXZ', + domain: 'native-test.growadvertising.com' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/growadvertisingBidAdapter_spec.js b/test/spec/modules/growadvertisingBidAdapter_spec.js new file mode 100644 index 00000000000..30d9207d4c2 --- /dev/null +++ b/test/spec/modules/growadvertisingBidAdapter_spec.js @@ -0,0 +1,161 @@ +import { expect } from 'chai'; +import { spec } from 'modules/growadvertisingBidAdapter.js'; +import * as utils from '../../../src/utils.js'; + +describe('GrowAdvertising Adapter', function() { + const ZONE_ID = 'unique-zone-id'; + const serverResponse = { + body: { + status: 'success', + width: 300, + height: 250, + creativeId: 'ULqaukILu0RnMa0FyidOtkji4Po3qbgQ9ceRVGlhjLLKnrrLAATmGNCwtE99Ems8', + ad: '', + cpm: 1, + ttl: 180, + currency: 'USD', + } + }; + let bidRequests = []; + + beforeEach(function () { + bidRequests = [ + { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + maxCPM: 5, + minCPM: 1 + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + }, + }, + } + ]; + }); + + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { + let validBid = { + bidder: 'growads', + params: { + zoneId: ZONE_ID + } + }; + + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('should reject null zoneId bid', function () { + let zoneNullBid = { + bidder: 'growads', + params: { + zoneId: null + } + }; + + let isValid = spec.isBidRequestValid(zoneNullBid); + expect(isValid).to.equal(false); + }); + + it('should reject absent zoneId bid', function () { + let absentZoneBid = { + bidder: 'growads', + params: { + param: ZONE_ID + } + }; + + let isValid = spec.isBidRequestValid(absentZoneBid); + expect(isValid).to.equal(false); + }); + + it('should use custom domain', function () { + let validBid = { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + domain: 'test.subdomain.growadvertising.com', + }, + }; + + let requests = spec.buildRequests([validBid]); + expect(requests[0].url).to.have.string('test.subdomain.'); + }); + + it('should use default domain', function () { + let validBid = { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + }, + }; + + let requests = spec.buildRequests([validBid]); + expect(requests[0].url).to.have.string('portal.growadvertising.com'); + }); + + it('should increment zone index', function () { + let validBids = [ + { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + }, + }, + { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + }, + } + ]; + + let requests = spec.buildRequests(validBids); + expect(requests[0].data).to.include({i: 0}); + expect(requests[1].data).to.include({i: 1}); + }); + }); + + describe('bid responses', function () { + it('should return complete bid response', function () { + let bids = spec.interpretResponse(serverResponse, {bidRequest: bidRequests[0]}); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].bidderCode).to.equal('growads'); + expect(bids[0].cpm).to.equal(1); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].creativeId).to.have.length.above(1); + expect(bids[0].ad).to.have.length.above(1); + }); + + it('should return empty bid on incorrect size', function () { + let response = utils.mergeDeep(serverResponse, { + body: { + width: 150, + height: 150 + } + }); + + let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + expect([]).to.be.lengthOf(0); + }); + + it('should return empty bid on incorrect CPM', function () { + let response = utils.mergeDeep(serverResponse, { + body: { + cpm: 10 + } + }); + + let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + expect([]).to.be.lengthOf(0); + }); + }); + }); +}); From 444df0c8096b559a652b7cd4896e2bfe56d48915 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 22 Jun 2021 19:31:01 +0200 Subject: [PATCH 1188/1476] Adagio Bid Adapter: add support for Prebid Server (#7065) * Prebid Server Bid Adapter: add context params to `transformBidParams()` * AdagioBidAdapter: refactor code to handle s2s * Fix: avoid localstorage error in crossdomain iframe * Update Native Tests * Fix for IE11 --- modules/adagioBidAdapter.js | 821 ++++++++++----------- modules/adagioBidAdapter.md | 374 +++++----- modules/prebidServerBidAdapter/index.js | 2 +- test/spec/modules/adagioBidAdapter_spec.js | 582 ++++++--------- 4 files changed, 852 insertions(+), 927 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 5598dc5d224..27379f016af 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -12,7 +12,6 @@ import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; const BIDDER_CODE = 'adagio'; const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.11.0'; const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; @@ -58,7 +57,66 @@ export const ORTB_VIDEO_PARAMS = { let currentWindow; -const EXT_DATA = {} +export const GlobalExchange = (function() { + let features; + let exchangeData = {}; + + return { + clearFeatures: function() { + features = undefined; + }, + + clearExchangeData: function() { + exchangeData = {}; + }, + + getOrSetGlobalFeatures: function () { + if (!features) { + features = { + page_dimensions: getPageDimensions().toString(), + viewport_dimensions: getViewPortDimensions().toString(), + user_timestamp: getTimestampUTC().toString(), + dom_loading: getDomLoadingDuration().toString(), + } + } + return features; + }, + + prepareExchangeData(storageValue) { + const adagioStorage = JSON.parse(storageValue, function(name, value) { + if (name.charAt(0) !== '_' || name === '') { + return value; + } + }); + let random = utils.deepAccess(adagioStorage, 'session.rnd'); + let newSession = false; + + if (internal.isNewSession(adagioStorage)) { + newSession = true; + random = Math.random(); + } + + const data = { + session: { + new: newSession, + rnd: random + } + } + + utils.mergeDeep(exchangeData, adagioStorage, data); + + internal.enqueue({ + action: 'session', + ts: Date.now(), + data: exchangeData + }); + }, + + getExchangeData() { + return exchangeData + } + }; +})(); export function adagioScriptFromLocalStorageCb(ls) { try { @@ -99,13 +157,18 @@ export function getAdagioScript() { if (isValid) { loadExternalScript(ADAGIO_TAG_URL, BIDDER_CODE); } else { - // ensure adagio removing for next time. - // It's an antipattern regarding the TCF2 enforcement logic - // but it's the only way to respect the user choice update. - window.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); - // Extra data from external script. - // This key is removed only if localStorage is not accessible. - window.localStorage.removeItem('adagio'); + // Try-catch to avoid error when 3rd party cookies is disabled (e.g. in privacy mode) + try { + // ensure adagio removing for next time. + // It's an antipattern regarding the TCF2 enforcement logic + // but it's the only way to respect the user choice update. + window.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); + // Extra data from external script. + // This key is removed only if localStorage is not accessible. + window.localStorage.removeItem('adagio'); + } catch (e) { + utils.logInfo(`${LOG_PREFIX} unable to clear Adagio scripts from localstorage.`); + } } }); } @@ -129,37 +192,6 @@ function isSafeFrameWindow() { return !!(ws.$sf && ws.$sf.ext); } -// Get localStorage "adagio" data to be passed to the request -export function prepareExchange(storageValue) { - const adagioStorage = JSON.parse(storageValue, function(name, value) { - if (!name.startsWith('_') || name === '') { - return value; - } - }); - let random = utils.deepAccess(adagioStorage, 'session.rnd'); - let newSession = false; - - if (internal.isNewSession(adagioStorage)) { - newSession = true; - random = Math.random(); - } - - const data = { - session: { - new: newSession, - rnd: random - } - } - - utils.mergeDeep(EXT_DATA, adagioStorage, data); - - internal.enqueue({ - action: 'session', - ts: Date.now(), - data: EXT_DATA - }); -} - function initAdagio() { if (canAccessTopWindow()) { currentWindow = (canAccessTopWindow()) ? utils.getWindowTop() : utils.getWindowSelf(); @@ -172,12 +204,12 @@ function initAdagio() { w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits || []; w.ADAGIO.queue = w.ADAGIO.queue || []; w.ADAGIO.versions = w.ADAGIO.versions || {}; - w.ADAGIO.versions.adagioBidderAdapter = VERSION; + w.ADAGIO.versions.pbjs = '$prebid.version$'; w.ADAGIO.isSafeFrameWindow = isSafeFrameWindow(); storage.getDataFromLocalStorage('adagio', (storageData) => { try { - internal.prepareExchange(storageData); + GlobalExchange.prepareExchangeData(storageData); } catch (e) { utils.logError(LOG_PREFIX, e); } @@ -186,211 +218,6 @@ function initAdagio() { getAdagioScript(); } -export const _features = { - getPrintNumber(adUnitCode) { - const adagioAdUnit = internal.getOrAddAdagioAdUnit(adUnitCode); - return adagioAdUnit.printNumber || 1; - }, - - getPageDimensions() { - if (isSafeFrameWindow() || !canAccessTopWindow()) { - return ''; - } - - // the page dimension can be computed on window.top only. - const wt = utils.getWindowTop(); - const body = wt.document.querySelector('body'); - - if (!body) { - return ''; - } - const html = wt.document.documentElement; - const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); - const pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); - - return `${pageWidth}x${pageHeight}`; - }, - - getViewPortDimensions() { - if (!isSafeFrameWindow() && !canAccessTopWindow()) { - return ''; - } - - const viewportDims = { w: 0, h: 0 }; - - if (isSafeFrameWindow()) { - const ws = utils.getWindowSelf(); - - if (typeof ws.$sf.ext.geom !== 'function') { - utils.logWarn(`${LOG_PREFIX} cannot use the $sf.ext.geom() safeFrame API method`); - return ''; - } - - const sfGeom = ws.$sf.ext.geom().win; - viewportDims.w = Math.round(sfGeom.w); - viewportDims.h = Math.round(sfGeom.h); - } else { - // window.top based computing - const wt = utils.getWindowTop(); - - if (wt.innerWidth) { - viewportDims.w = wt.innerWidth; - viewportDims.h = wt.innerHeight; - } else { - const d = wt.document; - const body = d.querySelector('body'); - - if (!body) { - return ''; - } - - viewportDims.w = d.querySelector('body').clientWidth; - viewportDims.h = d.querySelector('body').clientHeight; - } - } - - return `${viewportDims.w}x${viewportDims.h}`; - }, - - /** - * domLoading feature is computed on window.top if reachable. - */ - getDomLoadingDuration() { - let domLoadingDuration = -1; - let performance; - - performance = (canAccessTopWindow()) ? utils.getWindowTop().performance : utils.getWindowSelf().performance; - - if (performance && performance.timing && performance.timing.navigationStart > 0) { - const val = performance.timing.domLoading - performance.timing.navigationStart; - if (val > 0) { - domLoadingDuration = val; - } - } - - return domLoadingDuration; - }, - - getSlotPosition(params) { - const { adUnitElementId, postBid } = params; - - if (!adUnitElementId) { - return ''; - } - - if (!isSafeFrameWindow() && !canAccessTopWindow()) { - return ''; - } - - const position = { x: 0, y: 0 }; - - if (isSafeFrameWindow()) { - const ws = utils.getWindowSelf(); - - if (typeof ws.$sf.ext.geom !== 'function') { - utils.logWarn(`${LOG_PREFIX} cannot use the $sf.ext.geom() safeFrame API method`); - return ''; - } - - const sfGeom = ws.$sf.ext.geom().self; - position.x = Math.round(sfGeom.t); - position.y = Math.round(sfGeom.l); - } else if (canAccessTopWindow()) { - // window.top based computing - const wt = utils.getWindowTop(); - const d = wt.document; - - let domElement; - - if (postBid === true) { - const ws = utils.getWindowSelf(); - const currentElement = ws.document.getElementById(adUnitElementId); - domElement = internal.getElementFromTopWindow(currentElement, ws); - } else { - domElement = wt.document.getElementById(adUnitElementId); - } - - 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 || {}; - domElement.style.display = 'block'; - box = domElement.getBoundingClientRect(); - domElement.style.display = elComputedDisplay; - } - position.x = Math.round(box.left + scrollLeft - clientLeft); - position.y = Math.round(box.top + scrollTop - clientTop); - } else { - return ''; - } - - return `${position.x}x${position.y}`; - }, - - getTimestampUTC() { - // timestamp returned in seconds - return Math.floor(new Date().getTime() / 1000) - new Date().getTimezoneOffset() * 60; - }, - - getDevice() { - const ws = utils.getWindowSelf(); - const ua = ws.navigator.userAgent; - - if ((/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i).test(ua)) { - return 5; // "tablet" - } - if ((/Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/).test(ua)) { - return 4; // "phone" - } - return 2; // personal computers - }, - - getBrowser() { - const ws = utils.getWindowSelf(); - const ua = ws.navigator.userAgent; - const uaLowerCase = ua.toLowerCase(); - return /Edge\/\d./i.test(ua) ? 'edge' : uaLowerCase.indexOf('chrome') > 0 ? 'chrome' : uaLowerCase.indexOf('firefox') > 0 ? 'firefox' : uaLowerCase.indexOf('safari') > 0 ? 'safari' : uaLowerCase.indexOf('opera') > 0 ? 'opera' : uaLowerCase.indexOf('msie') > 0 || ws.MSStream ? 'ie' : 'unknow'; - }, - - getOS() { - const ws = utils.getWindowSelf(); - const ua = ws.navigator.userAgent; - const uaLowerCase = ua.toLowerCase(); - return uaLowerCase.indexOf('linux') > 0 ? 'linux' : uaLowerCase.indexOf('mac') > 0 ? 'mac' : uaLowerCase.indexOf('win') > 0 ? 'windows' : ''; - }, - - getUrl(refererInfo) { - // top has not been reached, it means we are not sure - // to get the proper page url. - if (!refererInfo.reachedTop) { - return; - } - return refererInfo.referer; - }, - - getUrlFromParams(params) { - const { postBidOptions } = params; - if (postBidOptions && postBidOptions.url) { - return postBidOptions.url; - } - } -}; - function enqueue(ob) { const w = internal.getCurrentWindow(); @@ -399,18 +226,6 @@ function enqueue(ob) { w.ADAGIO.queue.push(ob); }; -function getOrAddAdagioAdUnit(adUnitCode) { - const w = internal.getCurrentWindow(); - - w.ADAGIO = w.ADAGIO || {}; - - if (w.ADAGIO.adUnits[adUnitCode]) { - return w.ADAGIO.adUnits[adUnitCode]; - } - - return w.ADAGIO.adUnits[adUnitCode] = {}; -}; - function getPageviewId() { const w = internal.getCurrentWindow(); @@ -420,28 +235,11 @@ function getPageviewId() { return w.ADAGIO.pageviewId; }; -function computePrintNumber(adUnitCode) { - let printNumber = 1; - const w = internal.getCurrentWindow(); - - if ( - w.ADAGIO && - w.ADAGIO.adUnits && w.ADAGIO.adUnits[adUnitCode] && - w.ADAGIO.adUnits[adUnitCode].pageviewId === internal.getPageviewId() && - w.ADAGIO.adUnits[adUnitCode].printNumber - ) { - printNumber = parseInt(w.ADAGIO.adUnits[adUnitCode].printNumber, 10) + 1; - } - - return printNumber; -}; - function getDevice() { const language = navigator.language ? 'language' : 'userLanguage'; return { userAgent: navigator.userAgent, language: navigator[language], - deviceType: _features.getDevice(), dnt: utils.getDNT() ? 1 : 0, geo: {}, js: 1 @@ -504,72 +302,12 @@ function getElementFromTopWindow(element, currentWindow) { } }; -function autoDetectAdUnitElementId(adUnitCode) { +function autoDetectAdUnitElementIdFromGpt(adUnitCode) { const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode); - let adUnitElementId = null; if (autoDetectedAdUnit && autoDetectedAdUnit.divId) { - adUnitElementId = autoDetectedAdUnit.divId; + return autoDetectedAdUnit.divId; } - - return adUnitElementId; -}; - -function autoDetectEnvironment() { - const device = _features.getDevice(); - const map = { 2: 'desktop', 4: 'mobile', 5: 'tablet' }; - return map[device] || 'unknown'; -}; - -function supportIObs() { - const currentWindow = internal.getCurrentWindow(); - return !!(currentWindow && currentWindow.IntersectionObserver && currentWindow.IntersectionObserverEntry && - currentWindow.IntersectionObserverEntry.prototype && 'intersectionRatio' in currentWindow.IntersectionObserverEntry.prototype); -} - -function getFeatures(bidRequest, bidderRequest) { - const { adUnitCode, params } = bidRequest; - const { adUnitElementId } = params; - const { refererInfo } = bidderRequest; - - if (!adUnitElementId) { - utils.logWarn(`${LOG_PREFIX} unable to get params.adUnitElementId. Continue without tiv.`); - } - - const features = { - print_number: _features.getPrintNumber(adUnitCode).toString(), - page_dimensions: _features.getPageDimensions().toString(), - viewport_dimensions: _features.getViewPortDimensions().toString(), - dom_loading: _features.getDomLoadingDuration().toString(), - // layout: features.getLayout().toString(), - adunit_position: _features.getSlotPosition(params).toString(), - user_timestamp: _features.getTimestampUTC().toString(), - device: _features.getDevice().toString(), - url: _features.getUrl(refererInfo) || _features.getUrlFromParams(params) || '', - browser: _features.getBrowser(), - os: _features.getOS() - }; - - Object.keys(features).forEach((prop) => { - if (features[prop] === '') { - delete features[prop]; - } - }); - - const adUnitFeature = {}; - - adUnitFeature[adUnitElementId] = { - features: features, - version: FEATURES_VERSION - }; - - internal.enqueue({ - action: 'features', - ts: Date.now(), - data: adUnitFeature - }); - - return features; }; function isRendererPreferredFromPublisher(bidRequest) { @@ -604,23 +342,16 @@ function isNewSession(adagioStorage) { export const internal = { enqueue, - getOrAddAdagioAdUnit, getPageviewId, - computePrintNumber, getDevice, getSite, getElementFromTopWindow, - autoDetectAdUnitElementId, - autoDetectEnvironment, - getFeatures, getRefererInfo, adagioScriptFromLocalStorageCb, getCurrentWindow, - supportIObs, canAccessTopWindow, isRendererPreferredFromPublisher, - isNewSession, - prepareExchange + isNewSession }; function _getGdprConsent(bidderRequest) { @@ -863,85 +594,288 @@ function _getFloors(bidRequest) { return floors; } +/** + * Try to find the value of `paramName` and set it to adUnit.params if + * it has not already been set. + * This function will check through: + * - bidderSettings object + * - ortb2.site.ext.data FPD… + * + * @param {*} bid + * @param {String} paramName + */ +export function setExtraParam(bid, paramName) { + bid.params = bid.params || {}; + + // eslint-disable-next-line + if (!!(bid.params[paramName])) { + return; + } + + const adgGlobalConf = config.getConfig('adagio') || {}; + const ortb2Conf = config.getConfig('ortb2'); + + const detected = adgGlobalConf[paramName] || utils.deepAccess(ortb2Conf, `site.ext.data.${paramName}`, null); + if (detected) { + bid.params[paramName] = detected; + } +} + +function autoFillParams(bid) { + // adUnitElementId … + const adgGlobalConf = config.getConfig('adagio') || {}; + + bid.params = bid.params || {}; + + // adgGlobalConf.siteId is a shortcut to facilitate the integration for publisher. + if (adgGlobalConf.siteId) { + bid.params.organizationId = adgGlobalConf.siteId.split(':')[0]; + bid.params.site = adgGlobalConf.siteId.split(':')[1]; + } + + // Edge case. Useful when Prebid Manager cannot handle properly params setting… + if (adgGlobalConf.useAdUnitCodeAsPlacement === true || bid.params.useAdUnitCodeAsPlacement === true) { + bid.params.placement = bid.adUnitCode; + } + + bid.params.adUnitElementId = utils.deepAccess(bid, 'ortb2Imp.ext.data.elementId', null) || bid.params.adUnitElementId; + + if (!bid.params.adUnitElementId) { + if (adgGlobalConf.useAdUnitCodeAsAdUnitElementId === true || bid.params.useAdUnitCodeAsAdUnitElementId === true) { + bid.params.adUnitElementId = bid.adUnitCode; + } else { + bid.params.adUnitElementId = autoDetectAdUnitElementIdFromGpt(bid.adUnitCode); + } + } + + // extra params + setExtraParam(bid, 'environment'); + setExtraParam(bid, 'pagetype'); + setExtraParam(bid, 'category'); + setExtraParam(bid, 'subcategory'); +} + +function getPageDimensions() { + if (isSafeFrameWindow() || !canAccessTopWindow()) { + return ''; + } + + // the page dimension can be computed on window.top only. + const wt = utils.getWindowTop(); + const body = wt.document.querySelector('body'); + + if (!body) { + return ''; + } + const html = wt.document.documentElement; + const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); + const pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); + + return `${pageWidth}x${pageHeight}`; +} + +/** +* @todo Move to prebid Core as Utils. +* @returns +*/ +function getViewPortDimensions() { + if (!isSafeFrameWindow() && !canAccessTopWindow()) { + return ''; + } + + const viewportDims = { w: 0, h: 0 }; + + if (isSafeFrameWindow()) { + const ws = utils.getWindowSelf(); + + if (typeof ws.$sf.ext.geom !== 'function') { + utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api.'); + return ''; + } + + const sfGeom = ws.$sf.ext.geom(); + + if (!sfGeom || !sfGeom.win) { + utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api. Missing `geom().win` property'); + return ''; + } + + viewportDims.w = Math.round(sfGeom.w); + viewportDims.h = Math.round(sfGeom.h); + } else { + // window.top based computing + const wt = utils.getWindowTop(); + viewportDims.w = wt.innerWidth; + viewportDims.h = wt.innerHeight; + } + + return `${viewportDims.w}x${viewportDims.h}`; +} + +function getSlotPosition(adUnitElementId) { + if (!adUnitElementId) { + return ''; + } + + if (!isSafeFrameWindow() && !canAccessTopWindow()) { + return ''; + } + + const position = { x: 0, y: 0 }; + + if (isSafeFrameWindow()) { + const ws = utils.getWindowSelf(); + + if (typeof ws.$sf.ext.geom !== 'function') { + utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api.'); + return ''; + } + + const sfGeom = ws.$sf.ext.geom(); + + if (!sfGeom || !sfGeom.self) { + utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api. Missing `geom().self` property'); + return ''; + } + + position.x = Math.round(sfGeom.t); + position.y = Math.round(sfGeom.l); + } else if (canAccessTopWindow()) { + // window.top based computing + const wt = utils.getWindowTop(); + const d = wt.document; + + let domElement; + + if (utils.inIframe() === true) { + const ws = utils.getWindowSelf(); + const currentElement = ws.document.getElementById(adUnitElementId); + domElement = internal.getElementFromTopWindow(currentElement, ws); + } else { + domElement = wt.document.getElementById(adUnitElementId); + } + + 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 || {}; + domElement.style.display = 'block'; + box = domElement.getBoundingClientRect(); + domElement.style.display = elComputedDisplay; + } + position.x = Math.round(box.left + scrollLeft - clientLeft); + position.y = Math.round(box.top + scrollTop - clientTop); + } else { + return ''; + } + + return `${position.x}x${position.y}`; +} + +function getTimestampUTC() { + // timestamp returned in seconds + return Math.floor(new Date().getTime() / 1000) - new Date().getTimezoneOffset() * 60; +} + +function getPrintNumber(adUnitCode, bidderRequest) { + if (!bidderRequest.bids || !bidderRequest.bids.length) { + return 1; + } + const adagioBid = find(bidderRequest.bids, bid => bid.adUnitCode === adUnitCode); + return adagioBid.bidRequestsCount || 1; +} + +/** + * domLoading feature is computed on window.top if reachable. + */ +function getDomLoadingDuration() { + let domLoadingDuration = -1; + let performance; + + performance = (canAccessTopWindow()) ? utils.getWindowTop().performance : utils.getWindowSelf().performance; + + if (performance && performance.timing && performance.timing.navigationStart > 0) { + const val = performance.timing.domLoading - performance.timing.navigationStart; + if (val > 0) { + domLoadingDuration = val; + } + } + + return domLoadingDuration; +} + +function storeRequestInAdagioNS(bidRequest) { + const w = getCurrentWindow(); + // Store adUnits config. + // If an adUnitCode has already been stored, it will be replaced. + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== bidRequest.adUnitCode); + + let printNumber + if (bidRequest.features && bidRequest.features.print_number) { + printNumber = bidRequest.features.print_number; + } else if (bidRequest.params.features && bidRequest.params.features.print_number) { + printNumber = bidRequest.params.features.print_number; + } + + w.ADAGIO.pbjsAdUnits.push({ + code: bidRequest.adUnitCode, + mediaTypes: bidRequest.mediaTypes || {}, + sizes: (bidRequest.mediaTypes && bidRequest.mediaTypes.banner && Array.isArray(bidRequest.mediaTypes.banner.sizes)) ? bidRequest.mediaTypes.banner.sizes : bidRequest.sizes, + bids: [{ + bidder: bidRequest.bidder, + params: bidRequest.params // use the updated bid.params object with auto-detected params + }], + auctionId: bidRequest.auctionId, + pageviewId: internal.getPageviewId(), + printNumber + }); + + // (legacy) Store internal adUnit information + w.ADAGIO.adUnits[bidRequest.adUnitCode] = { + auctionId: bidRequest.auctionId, + pageviewId: internal.getPageviewId(), + printNumber, + }; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, supportedMediaTypes: SUPPORTED_MEDIA_TYPES, isBidRequestValid(bid) { - const { adUnitCode, auctionId, sizes, bidder, params, mediaTypes } = bid; - if (!params) { - utils.logWarn(`${LOG_PREFIX} the "params" property is missing.`); - return false; - } - - const { organizationId, site } = params; - const adUnitElementId = (params.useAdUnitCodeAsAdUnitElementId === true) - ? adUnitCode - : params.adUnitElementId || internal.autoDetectAdUnitElementId(adUnitCode); - const placement = (params.useAdUnitCodeAsPlacement === true) ? adUnitCode : params.placement; - const environment = params.environment || internal.autoDetectEnvironment(); - const supportIObs = internal.supportIObs(); - - // insure auto-detected params are kept in `bid` object. - bid.params = { - ...params, - adUnitElementId, - environment, - placement, - supportIObs - }; - - const debugData = () => ({ - action: 'pb-dbg', - ts: Date.now(), - data: { - bid - } - }); + bid.params = bid.params || {}; - const refererInfo = internal.getRefererInfo(); + autoFillParams(bid); - if (!refererInfo.reachedTop) { + if (!internal.getRefererInfo().reachedTop) { utils.logWarn(`${LOG_PREFIX} the main page url is unreachabled.`); - internal.enqueue(debugData()); - + // internal.enqueue(debugData()); return false; - } else if (!(organizationId && site && placement)) { - utils.logWarn(`${LOG_PREFIX} at least one required param is missing.`); - internal.enqueue(debugData()); + } + if (!(bid.params.organizationId && bid.params.site && bid.params.placement)) { + utils.logWarn(`${LOG_PREFIX} at least one required param is missing.`); + // internal.enqueue(debugData()); return false; } - const w = internal.getCurrentWindow(); - const pageviewId = internal.getPageviewId(); - const printNumber = internal.computePrintNumber(adUnitCode); - - // Store adUnits config. - // If an adUnitCode has already been stored, it will be replaced. - w.ADAGIO = w.ADAGIO || {}; - w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode); - w.ADAGIO.pbjsAdUnits.push({ - code: adUnitCode, - mediaTypes: mediaTypes || {}, - sizes: (mediaTypes && mediaTypes.banner && Array.isArray(mediaTypes.banner.sizes)) ? mediaTypes.banner.sizes : sizes, - bids: [{ - bidder, - params: bid.params // use the updated bid.params object with auto-detected params - }], - auctionId, - pageviewId, - printNumber - }); - - // (legacy) Store internal adUnit information - w.ADAGIO.adUnits[adUnitCode] = { - auctionId, - pageviewId, - printNumber, - }; - return true; }, @@ -955,8 +889,32 @@ export const spec = { const coppa = _getCoppa(); const schain = _getSchain(validBidRequests[0]); const eids = _getEids(validBidRequests[0]) || []; + const adUnits = utils._map(validBidRequests, (bidRequest) => { - bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); + const globalFeatures = GlobalExchange.getOrSetGlobalFeatures(); + const features = { + ...globalFeatures, + print_number: getPrintNumber(bidRequest.adUnitCode, bidderRequest).toString(), + adunit_position: getSlotPosition(bidRequest.params.adUnitElementId) // adUnitElementId à déplacer ??? + }; + + Object.keys(features).forEach((prop) => { + if (features[prop] === '') { + delete features[prop]; + } + }); + + bidRequest.features = features; + + internal.enqueue({ + action: 'features', + ts: Date.now(), + data: { + features: bidRequest.features, + params: bidRequest.params, + adUnitCode: bidRequest.adUnitCode + } + }); // Handle priceFloors module bidRequest.floors = _getFloors(bidRequest); @@ -965,6 +923,8 @@ export const spec = { _buildVideoBidRequest(bidRequest); } + storeRequestInAdagioNS(bidRequest); + return bidRequest; }); @@ -975,6 +935,7 @@ export const spec = { // remove useless props delete adUnitCopy.floorData; + delete adUnitCopy.params.siteId; groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); @@ -995,7 +956,7 @@ export const spec = { site: site, pageviewId: pageviewId, adUnits: groupedAdUnits[organizationId], - data: EXT_DATA, + data: GlobalExchange.getExchangeData(), regs: { gdpr: gdprConsent, coppa: coppa, @@ -1006,7 +967,6 @@ export const spec = { eids: eids }, prebidVersion: '$prebid.version$', - adapterVersion: VERSION, featuresVersion: FEATURES_VERSION }, options: { @@ -1094,6 +1054,39 @@ export const spec = { return syncs; }, + + /** + * Handle custom logic in s2s context + * + * @param {*} params + * @param {boolean} isOrtb Is an s2s context + * @param {*} adUnit + * @param {*} bidRequests + * @returns {object} updated params + */ + transformBidParams(params, isOrtb, adUnit, bidRequests) { + const adagioBidderRequest = find(bidRequests, bidRequest => bidRequest.bidderCode === 'adagio'); + const adagioBid = find(adagioBidderRequest.bids, bid => bid.adUnitCode === adUnit.code); + + if (isOrtb) { + autoFillParams(adagioBid); + + const globalFeatures = GlobalExchange.getOrSetGlobalFeatures(); + adagioBid.params.features = { + ...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$'; + adagioBid.params.data = GlobalExchange.getExchangeData(); + + storeRequestInAdagioNS(adagioBid); + } + + return adagioBid.params; + } }; initAdagio(); diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index 46656d88d37..2779ced8cea 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -8,187 +8,227 @@ Maintainer: dev@adagio.io Connects to Adagio demand source to fetch bids. -## Test Parameters +## Configuration + +Adagio require several params. These params must be set at Prebid.js global config level or at adUnit level. + +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 +| pagetype | x | x +| environment | x | x +| category | x | x +| subcategory | x | x +| useAdUnitCodeAsAdUnitElementId | x | x +| useAdUnitCodeAsPlacement | x | x +| placement | | x +| adUnitElementId | | x +| debug | | x +| video | | x +| native | | x + +### 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. ```javascript - var adUnits = [ - { - code: 'dfp_banniere_atf', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } +pbjs.setConfig({ + debug: false, + // …, + adagio: { + siteId: '1002:adagio-io', // Required. Provided by Adagio + + // The following params are limited to 30 characters, + // and can only contain the following characters: + // - alphanumeric (A-Z+a-z+0-9, case-insensitive) + // - dashes `-` + // - 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 + }, +}); +``` + +### adUnit configuration + +```javascript +var adUnits = [ + { + code: 'dfp_banniere_atf', + bids: [{ + bidder: 'adagio', + params: { + placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + // Optional debug mode, used to get a bid response with expected cpm. + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 }, - bids: [{ - bidder: 'adagio', // Required - params: { - organizationId: '1002', // Required - Organization ID provided by Adagio. - site: 'adagio-io', // Required - Site Name provided by Adagio. - placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. - adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. - - // The following params are limited to 30 characters, - // and can only contain the following characters: - // - alphanumeric (A-Z+a-z+0-9, case-insensitive) - // - dashes `-` - // - 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. - postBid: false, // Optional. Use it in case of Post-bid integration only. - 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 - // Optional debug mode, used to get a bid response with expected cpm. - debug: { - enabled: true, - cpm: 3.00 // default to 1.00 - } + video: { + skip: 0 + // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. + }, + native: { + // Optional OpenRTB Native 1.2 request object. Only `context`, `plcmttype` fields are supported. + context: 1, + plcmttype: 2 + }, + } + }] + } +]; +``` + +## Test Parameters + +```javascript + + pbjs.setConfig({ + debug: true, + adagio: { + pagetype: 'article', + environment: 'mobile', + category: 'sport', + subcategory: 'handball', + useAdUnitCodeAsAdUnitElementId: false, + useAdUnitCodeAsPlacement: false, + } + }); + + var adUnits = [ + { + code: 'dfp_banniere_atf', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [{ + bidder: 'adagio', // Required + params: { + placement: 'in_article', + adUnitElementId: 'article_outstream', + debug: { + enabled: true, + cpm: 3.00 // default to 1.00 } - }] + } + }] + }, + { + code: 'article_outstream', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + mimes: ['video/mp4'], + skip: 1 + } }, - { - code: 'article_outstream', - mediaTypes: { + bids: [{ + bidder: 'adagio', + params: { + placement: 'in_article', + adUnitElementId: 'article_outstream', video: { - context: 'outstream', - playerSize: [640, 480], - mimes: ['video/mp4'], - skip: 1 - // Other OpenRTB 2.5 video options… + skip: 0 + }, + debug: { + enabled: true, + cpm: 3.00 } - }, - bids: [{ - bidder: 'adagio', // Required - params: { - organizationId: '1002', // Required - Organization ID provided by Adagio. - site: 'adagio-io', // Required - Site Name provided by Adagio. - placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. - adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. - - // The following params are limited to 30 characters, - // and can only contain the following characters: - // - alphanumeric (A-Z+a-z+0-9, case-insensitive) - // - dashes `-` - // - 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. - postBid: false, // Optional. Use it in case of Post-bid integration only. - 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 - video: { - skip: 0 - // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. - }, - // Optional debug mode, used to get a bid response with expected cpm. - debug: { - enabled: true, - cpm: 3.00 // default to 1.00 + } + }] + }, + { + code: 'article_native', + mediaTypes: { + native: { + // generic Prebid options + title: { + required: true, + len: 80 + }, + // … + // Custom Adagio data assets + ext: { + adagio_bvw: { + required: false } } - }] + } }, - { - code: 'article_native', - mediaTypes: { + bids: [{ + bidder: 'adagio', + params: { + placement: 'in_article', + adUnitElementId: 'article_native', native: { - // generic Prebid options - title: { - required: true, - len: 80 - }, - // … - // Custom Adagio data assets - ext: { - adagio_bvw: { - required: false - } - } + context: 1, + plcmttype: 2 + }, + debug: { + enabled: true, + cpm: 3.00 + } + } + }] + } + ]; + + pbjs.addAdUnits(adUnits); + + pbjs.bidderSettings = { + adagio: { + alwaysUseBid: true, + adserverTargeting: [ + { + key: "site", + val: function (bidResponse) { + return bidResponse.site; } }, - bids: [{ - bidder: 'adagio', // Required - params: { - organizationId: '1002', // Required - Organization ID provided by Adagio. - site: 'adagio-io', // Required - Site Name provided by Adagio. - placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. - adUnitElementId: 'article_native', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. - - // The following params are limited to 30 characters, - // and can only contain the following characters: - // - alphanumeric (A-Z+a-z+0-9, case-insensitive) - // - dashes `-` - // - 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. - postBid: false, // Optional. Use it in case of Post-bid integration only. - 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 - // Optional OpenRTB Native 1.2 request object. Only `context`, `plcmttype` fields are supported. - native: { - context: 1, - plcmttype: 2 - }, - // Optional debug mode, used to get a bid response with expected cpm. - debug: { - enabled: true, - cpm: 3.00 // default to 1.00 - } + { + key: "environment", + val: function (bidResponse) { + return bidResponse.environment; } - }] - } - ]; - - pbjs.addAdUnits(adUnits); - - pbjs.bidderSettings = { - adagio: { - alwaysUseBid: true, - adserverTargeting: [ - { - key: "site", - val: function (bidResponse) { - return bidResponse.site; - } - }, - { - key: "environment", - val: function (bidResponse) { - return bidResponse.environment; - } - }, - { - key: "placement", - val: function (bidResponse) { - return bidResponse.placement; - } - }, - { - key: "pagetype", - val: function (bidResponse) { - return bidResponse.pagetype; - } - }, - { - key: "category", - val: function (bidResponse) { - return bidResponse.category; - } - }, - { - key: "subcategory", - val: function (bidResponse) { - return bidResponse.subcategory; - } + }, + { + key: "placement", + val: function (bidResponse) { + return bidResponse.placement; } - ] - } + }, + { + key: "pagetype", + val: function (bidResponse) { + return bidResponse.pagetype; + } + }, + { + key: "category", + val: function (bidResponse) { + return bidResponse.category; + } + }, + { + key: "subcategory", + val: function (bidResponse) { + return bidResponse.subcategory; + } + } + ] } + } ``` diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index ffd251cfac1..8641c832121 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -654,7 +654,7 @@ const OPEN_RTB_PROTOCOL = { const ext = adUnit.bids.reduce((acc, bid) => { const adapter = adapterManager.bidderRegistry[bid.bidder]; if (adapter && adapter.getSpec().transformBidParams) { - bid.params = adapter.getSpec().transformBidParams(bid.params, true); + bid.params = adapter.getSpec().transformBidParams(bid.params, true, adUnit, bidRequests); } acc[bid.bidder] = (s2sConfig.adapterOptions && s2sConfig.adapterOptions[bid.bidder]) ? Object.assign({}, bid.params, s2sConfig.adapterOptions[bid.bidder]) : bid.params; return acc; diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 53d1be78703..edbfafc3ec0 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,20 +1,23 @@ -import find from 'core-js-pure/features/array/find.js'; -import { expect, util } from 'chai'; +import { expect } from 'chai'; import { _features, internal as adagio, adagioScriptFromLocalStorageCb, getAdagioScript, storage, + setExtraParam, spec, ENDPOINT, VERSION, - RENDERER_URL + RENDERER_URL, + GlobalExchange } from '../../../modules/adagioBidAdapter.js'; import { loadExternalScript } from '../../../src/adloader.js'; import * as utils from '../../../src/utils.js'; import { config } from '../../../src/config.js'; import { NATIVE } from '../../../src/mediaTypes.js'; +import * as prebidGlobal from 'src/prebidGlobal.js'; +import { executeRenderer } from '../../../src/Renderer.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -115,6 +118,9 @@ describe('Adagio bid adapter', () => { window.ADAGIO.versions.adagioBidderAdapter = VERSION; window.ADAGIO.pageviewId = 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a'; + GlobalExchange.clearFeatures(); + GlobalExchange.clearExchangeData(); + adagioMock = sinon.mock(adagio); utilsMock = sinon.mock(utils); @@ -130,6 +136,46 @@ describe('Adagio bid adapter', () => { sandbox.restore(); }); + describe('get and set params at adUnit level from global Prebid configuration', function() { + it('should set params get from ortb2 config or bidderSettings. Priority to bidderSetting', function() { + const bid = new BidRequestBuilder().build(); + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + adagio: { + pagetype: 'article' + }, + ortb2: { + site: { + ext: { + data: { + environment: 'desktop', + pagetype: 'abc' + } + } + } + } + }; + 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'); + }); + + it('should use the adUnit param unit if defined', function() { + const bid = new BidRequestBuilder({ params: { pagetype: 'article' } }).build(); + sandbox.stub(config, 'getConfig').withArgs('adagio').returns({ + pagetype: 'ignore-me' + }); + setExtraParam(bid, 'pagetype') + expect(bid.params.pagetype).to.equal('article'); + }); + }) + describe('isBidRequestValid()', function() { it('should return true when required params have been found', function() { const bid = new BidRequestBuilder().withParams().build(); @@ -137,6 +183,19 @@ describe('Adagio bid adapter', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); + it('should compute organizationId and site params from global BidderSettings config', function() { + sandbox.stub(adagio, 'getRefererInfo').returns({ reachedTop: true }); + sandbox.stub(config, 'getConfig').withArgs('adagio').returns({ + siteId: '1000:SITE-NAME' + }); + + const bid = new BidRequestBuilder({ + params: { placement: 'PAVE_ATF' } + }).build(); + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }) + it('should return false if bid.params is missing', function() { sandbox.spy(utils, 'logWarn'); const bid01 = new BidRequestBuilder().build(); @@ -156,7 +215,7 @@ describe('Adagio bid adapter', () => { expect(spec.isBidRequestValid(bid01)).to.equal(true); expect(bid01.params.adUnitElementId).to.equal('adunit-code'); expect(bid01.params.placement).to.equal('adunit-code'); - }) + }); it('should return false when a required param is missing', function() { const bid01 = new BidRequestBuilder({ params: { @@ -174,9 +233,16 @@ describe('Adagio bid adapter', () => { site: 'SITE-NAME' }}).build(); + sandbox.stub(config, 'getConfig').withArgs('adagio').returns({ + siteId: '1000' + }); + + const bid04 = new BidRequestBuilder({ params: { placement: 'PAVE_ATF' } }).build(); + expect(spec.isBidRequestValid(bid01)).to.equal(false); expect(spec.isBidRequestValid(bid02)).to.equal(false); expect(spec.isBidRequestValid(bid03)).to.equal(false); + expect(spec.isBidRequestValid(bid04)).to.equal(false); }); it('should return false when refererInfo.reachedTop is false', function() { @@ -188,134 +254,6 @@ describe('Adagio bid adapter', () => { sinon.assert.callCount(utils.logWarn, 1); sinon.assert.calledWith(utils.logWarn, 'Adagio: the main page url is unreachabled.'); }); - - it('should log warning and enqueue the bid object in ADAGIO.queue when isBidRequestValid is false', function() { - sandbox.stub(Date, 'now').returns(12345); - sandbox.spy(utils, 'logWarn'); - sandbox.spy(adagio, 'enqueue'); - - const bid = new BidRequestBuilder({'params': { - organizationId: '1000', - placement: 'PAVE_ATF' - }}).build(); - - const expectedEnqueued = { - action: 'pb-dbg', - ts: 12345, - data: { bid } - }; - - spec.isBidRequestValid(bid); - - sinon.assert.calledWith(adagio.enqueue, expectedEnqueued); - sinon.assert.callCount(utils.logWarn, 1); - }); - - describe('Store ADAGIO global in window.top or window.self depending on context', function() { - const bid01 = new BidRequestBuilder({ - adUnitCode: 'adunit-code-01', - sizes: [[300, 250], [300, 600]] - }).withParams().build(); - - const bid02 = new BidRequestBuilder({ - adUnitCode: 'adunit-code-02', - mediaTypes: { - banner: { sizes: [[300, 250]] } - }, - }).withParams().build(); - - const bid03 = new BidRequestBuilder({ - adUnitCode: 'adunit-code-02', - mediaTypes: { - banner: { sizes: [[300, 600]] } - }, - }).withParams().build(); - - const expected = [ - { - code: 'adunit-code-01', - sizes: [[300, 250], [300, 600]], - mediaTypes: {}, - bids: [{ - bidder: 'adagio', - params: { - organizationId: '1000', - placement: 'PAVE_ATF', - site: 'SITE-NAME', - adUnitElementId: 'gpt-adunit-code', - environment: 'desktop', - supportIObs: true - } - }], - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - pageviewId: 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a', - printNumber: 1, - }, - { - code: 'adunit-code-02', - sizes: [[300, 600]], - mediaTypes: { - banner: { sizes: [[300, 600]] } - }, - bids: [{ - bidder: 'adagio', - params: { - organizationId: '1000', - placement: 'PAVE_ATF', - site: 'SITE-NAME', - adUnitElementId: 'gpt-adunit-code', - environment: 'desktop', - supportIObs: true - } - }], - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - pageviewId: 'dda61753-4059-4f75-b0bf-3f60bd2c4d9a', - printNumber: 2, - } - ]; - - it('should store bids config once by bid in window.top if it accessible', function() { - sandbox.stub(adagio, 'getCurrentWindow').returns(window.top); - sandbox.stub(adagio, 'supportIObs').returns(true); - - // replace by the values defined in beforeEach - window.top.ADAGIO = { - ...window.ADAGIO - }; - - spec.isBidRequestValid(bid01); - spec.isBidRequestValid(bid02); - spec.isBidRequestValid(bid03); - - expect(find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01')).to.deep.eql(expected[0]); - expect(find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-02')).to.deep.eql(expected[1]); - }); - - it('should detect IntersectionObserver support', function() { - sandbox.stub(adagio, 'getCurrentWindow').returns(window.top); - sandbox.stub(adagio, 'supportIObs').returns(false); - - window.top.ADAGIO = { - ...window.ADAGIO - }; - - spec.isBidRequestValid(bid01); - const validBidReq = find(window.top.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01'); - expect(validBidReq.bids[0].params.supportIObs).to.equal(false); - }); - - it('should store bids config once by bid in current window', function() { - sandbox.stub(adagio, 'getCurrentWindow').returns(window.self); - sandbox.stub(adagio, 'supportIObs').returns(true); - - spec.isBidRequestValid(bid01); - spec.isBidRequestValid(bid02); - spec.isBidRequestValid(bid03); - - expect(find(window.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-01')).to.deep.eql(expected[0]); - expect(find(window.ADAGIO.pbjsAdUnits, aU => aU.code === 'adunit-code-02')).to.deep.eql(expected[1]); - }); - }); }); describe('buildRequests()', function() { @@ -331,7 +269,6 @@ describe('Adagio bid adapter', () => { 'user', 'schain', 'prebidVersion', - 'adapterVersion', 'featuresVersion', 'data' ]; @@ -360,7 +297,6 @@ describe('Adagio bid adapter', () => { sandbox.stub(adagio, 'getDevice').returns({ a: 'a' }); sandbox.stub(adagio, 'getSite').returns({ domain: 'adagio.io', 'page': 'https://adagio.io/hb' }); sandbox.stub(adagio, 'getPageviewId').returns('1234-567'); - sandbox.stub(adagio, 'getFeatures').returns({}); const bid01 = new BidRequestBuilder().withParams().build(); const bidderRequest = new BidderRequestBuilder().build(); @@ -377,24 +313,11 @@ describe('Adagio bid adapter', () => { it('should enqueue computed features for collect usage', function() { sandbox.stub(Date, 'now').returns(12345); - for (const prop in _features) { - sandbox.stub(_features, prop).returns(''); - } - - adagioMock.expects('enqueue').withExactArgs({ - action: 'features', - ts: 12345, - data: { - 'gpt-adunit-code': { - features: {}, - version: '1' - } - } - }).atLeast(1); - const bid01 = new BidRequestBuilder().withParams().build(); const bidderRequest = new BidderRequestBuilder().build(); + adagioMock.expects('enqueue').withArgs(sinon.match({ action: 'features' })).atLeast(1); + const requests = spec.buildRequests([bid01], bidderRequest); expect(requests[0].data).to.have.all.keys(expectedDataKeys); @@ -497,7 +420,7 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); expect(requests).to.have.lengthOf(1); expect(requests[0].data.adUnits[0].mediaTypes.video).to.deep.equal(expected); - sinon.assert.calledTwice(utils.logWarn); + sinon.assert.calledTwice(utils.logWarn.withArgs(sinon.match(new RegExp(/^Adagio: The OpenRTB/)))); }); }); @@ -997,6 +920,20 @@ describe('Adagio bid adapter', () => { expect(bidResponse.height).to.equal(250); expect(bidResponse.vastUrl).to.match(/^data:text\/xml;/) }); + + it('should execute Adagio outstreamPlayer if defined', function() { + window.ADAGIO.outstreamPlayer = sinon.stub(); + const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0]; + executeRenderer(bidResponse.renderer, bidResponse) + sinon.assert.calledOnce(window.ADAGIO.outstreamPlayer); + delete window.ADAGIO.outstreamPlayer; + }); + + it('should logError if Adagio outstreamPlayer is not defined', function() { + const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0]; + executeRenderer(bidResponse.renderer, bidResponse) + utilsMock.expects('logError').withExactArgs('Adagio: Adagio outstream player is not defined').once(); + }); }); describe('Response with native add', () => { @@ -1033,7 +970,6 @@ describe('Adagio bid adapter', () => { ], assets: [ { - required: 1, title: { text: 'My title' } @@ -1081,34 +1017,32 @@ describe('Adagio bid adapter', () => { }; const bidRequestNative = utils.deepClone(bidRequest) - bidRequestNative.mediaTypes = { - native: { - sendTargetingKeys: false, + bidRequestNative.nativeParams = { + sendTargetingKeys: false, - clickUrl: { - required: true, - }, - title: { - required: true, - }, - body: { - required: true, - }, - sponsoredBy: { - required: false - }, - image: { - required: true - }, - icon: { - required: true - }, - privacyLink: { - required: false - }, - ext: { - adagio_bvw: {} - } + clickUrl: { + required: true, + }, + title: { + required: true, + }, + body: { + required: true, + }, + sponsoredBy: { + required: false + }, + image: { + required: true + }, + icon: { + required: true + }, + privacyLink: { + required: false + }, + ext: { + adagio_bvw: {} } }; @@ -1199,12 +1133,52 @@ describe('Adagio bid adapter', () => { }); }); - describe('Adagio features', function() { + describe('transformBidParams', function() { + it('Compute additional params in s2s mode', function() { + GlobalExchange.prepareExchangeData('{}'); + + sandbox.stub(window.top.document, 'getElementById').returns( + fixtures.getElementById() + ); + sandbox.stub(window.top, 'getComputedStyle').returns({ display: 'block' }); + sandbox.stub(utils, 'inIframe').returns(false); + + const adUnit = { + code: 'adunit-code', + params: { + organizationId: '1000' + } + }; + const bid01 = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); + + const params = spec.transformBidParams({ organizationId: '1000' }, true, adUnit, [{ bidderCode: 'adagio', bids: [bid01] }]); + + expect(params.organizationId).to.exist; + expect(params.organizationId).to.exist; + expect(params.features).to.exist; + expect(params.features.page_dimensions).to.exist; + expect(params.features.adunit_position).to.exist; + expect(params.features.dom_loading).to.exist; + expect(params.features.print_number).to.exist; + expect(params.features.user_timestamp).to.exist; + expect(params.placement).to.exist; + expect(params.adUnitElementId).to.exist; + expect(params.site).to.exist; + expect(params.data.session).to.exist; + }); + }); + + describe('Adagio features when prebid in top.window', function() { it('should return all expected features when all expected bidder params are available', function() { sandbox.stub(window.top.document, 'getElementById').returns( fixtures.getElementById() ); sandbox.stub(window.top, 'getComputedStyle').returns({ display: 'block' }); + sandbox.stub(utils, 'inIframe').returns(false); const bidRequest = new BidRequestBuilder({ 'mediaTypes': { @@ -1214,7 +1188,8 @@ describe('Adagio bid adapter', () => { const bidderRequest = new BidderRequestBuilder().build(); - const result = adagio.getFeatures(bidRequest, bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; expect(result.adunit_position).to.match(/^[\d]+x[\d]+$/); expect(result.page_dimensions).to.match(/^[\d]+x[\d]+$/); @@ -1222,13 +1197,15 @@ describe('Adagio bid adapter', () => { expect(result.print_number).to.be.a('String'); expect(result.dom_loading).to.be.a('String'); expect(result.user_timestamp).to.be.a('String'); - expect(result.url).to.be.a('String'); - expect(result.device).to.be.a('String'); - expect(result.os).to.be.a('String'); - expect(result.browser).to.be.a('String'); + expect(result.url).to.not.exist; + expect(result.device).to.not.exist; + expect(result.os).to.not.exist; + expect(result.browser).to.not.exist; }); it('should return all expected features when `adUnitElementId` param is not available', function() { + sandbox.stub(utils, 'inIframe').returns(false); + const bidRequest = new BidRequestBuilder({ params: { organizationId: '1000', @@ -1242,7 +1219,8 @@ describe('Adagio bid adapter', () => { const bidderRequest = new BidderRequestBuilder().build(); - const result = adagio.getFeatures(bidRequest, bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; expect(result.adunit_position).to.not.exist; expect(result.page_dimensions).to.be.a('String'); @@ -1250,16 +1228,23 @@ describe('Adagio bid adapter', () => { expect(result.print_number).to.be.a('String'); expect(result.dom_loading).to.be.a('String'); expect(result.user_timestamp).to.be.a('String'); - expect(result.url).to.be.a('String'); - expect(result.device).to.be.a('String'); - expect(result.os).to.be.a('String'); - expect(result.browser).to.be.a('String'); }); + }); - it('should not return feature with an empty value', function() { - sandbox.stub(_features, 'getDomLoadingDuration').returns(''); - sandbox.stub(_features, 'getUrl').returns(''); - sandbox.stub(_features, 'getBrowser').returns(''); + describe('Adagio features when prebid in Safeframe', function() { + beforeEach(function () { + window.$sf = $sf; + }); + + afterEach(function () { + delete window.$sf; + }); + + it('should return all expected features when prebid is in safeframe iframe', function() { + sandbox.stub(window.$sf.ext, 'geom').returns({ + win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, + self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, + }); const bidRequest = new BidRequestBuilder({ 'mediaTypes': { @@ -1269,178 +1254,85 @@ describe('Adagio bid adapter', () => { const bidderRequest = new BidderRequestBuilder().build(); - const result = adagio.getFeatures(bidRequest, bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; - expect(result.adunit_position).to.not.exist; - expect(result.page_dimensions).to.exist; - expect(result.viewport_dimensions).to.exist; - expect(result.print_number).to.exist; - expect(result.dom_loading).to.not.exist; - expect(result.user_timestamp).to.exist; - expect(result.url).to.not.exist; - expect(result.device).to.exist; - expect(result.os).to.exist; - expect(result.browser).to.not.exist; + expect(result.page_dimensions).to.not.exist; + expect(result.viewport_dimensions).to.be.a('String'); + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.adunit_position).to.exist; }); - describe('getPageDimensions feature', function() { - afterEach(() => { - delete window.$sf; - }); - - it('should not compute the page dimensions in cross-origin iframe', function() { - sandbox.stub(utils, 'getWindowTop').throws(); - const result = _features.getPageDimensions(); - expect(result).to.eq(''); - }); + it('should return all expected features when prebid safeframe api not properly implemented', function() { + const bidRequest = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); - it('should not compute the page dimensions even with safeFrame api', function() { - window.$sf = $sf; - const result = _features.getPageDimensions(); - expect(result).to.eq(''); - }); + const bidderRequest = new BidderRequestBuilder().build(); - it('should not compute the page dimensions if is not in the DOM', function() { - sandbox.stub(window.top.document, 'querySelector').withArgs('body').returns(null); - const result = _features.getPageDimensions(); - expect(result).to.eq(''); - }); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; - it('should compute the page dimensions based on body and viewport dimensions', function() { - sandbox.stub(window.top.document, 'querySelector').withArgs('body').returns({ scrollWidth: 1360, offsetWidth: 1280, scrollHeight: 2000, offsetHeight: 1000 }); - const result = _features.getPageDimensions(); - expect(result).to.eq('1360x2000'); - }); + expect(result.page_dimensions).to.not.exist; + expect(result.viewport_dimensions).to.not.exist; + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.adunit_position).to.not.exist; }); - describe('getViewPortDimensions feature', function() { - afterEach(() => { - delete window.$sf; - }); - - it('should not compute the viewport dimensions in cross-origin iframe', function() { - sandbox.stub(utils, 'getWindowTop').throws(); - const result = _features.getViewPortDimensions(); - expect(result).to.eq(''); - }); - - it('should compute the viewport dimensions in cross-origin iframe w/ safeFrame api', function() { - window.$sf = $sf; - sandbox.stub(window.$sf.ext, 'geom').returns({ - win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, - self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, - }); - const result = _features.getViewPortDimensions(); - expect(result).to.eq('1920x1177'); - }); + it('should return all expected features when prebid safeframe api not properly implemented bis', function() { + window.$sf.ext.geom = undefined; - it('should not compute the viewport dimensions if safeFrame api is misimplemented', function() { - window.$sf = { - ext: { geom: 'nothing' } - }; - const result = _features.getViewPortDimensions(); - expect(result).to.eq(''); - }); + const bidRequest = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); - it('should not compute the viewport dimensions if is not in the DOM', function() { - const querySelectorSpy = sandbox.spy(() => null); - sandbox.stub(utils, 'getWindowTop').returns({ - location: { href: 'https://mytest.io' }, - document: { querySelector: querySelectorSpy } - }); - const result = _features.getViewPortDimensions(); - expect(result).to.eq(''); - }); + const bidderRequest = new BidderRequestBuilder().build(); - it('should compute the viewport dimensions based on window', function() { - sandbox.stub(utils, 'getWindowTop').returns({ - location: { href: 'https://mytest.io' }, - innerWidth: 960, - innerHeight: 3000 - }); - const result = _features.getViewPortDimensions(); - expect(result).to.eq('960x3000'); - }); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; - it('should compute the viewport dimensions based on body', function() { - const querySelectorSpy = sandbox.spy(() => ({ clientWidth: 1024, clientHeight: 2000 })); - sandbox.stub(utils, 'getWindowTop').returns({ - location: { href: 'https://mytest.io' }, - document: { querySelector: querySelectorSpy } - }); - const result = _features.getViewPortDimensions(); - expect(result).to.eq('1024x2000'); - }); + expect(result.page_dimensions).to.not.exist; + expect(result.viewport_dimensions).to.not.exist; + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.adunit_position).to.not.exist; }); + }); - describe('getSlotPosition feature', function() { - let getElementByIdStub; - let getComputedStyleStub; - - beforeEach(() => { - getElementByIdStub = sandbox.stub(window.top.document, 'getElementById'); - getElementByIdStub.returns(fixtures.getElementById()); - getComputedStyleStub = sandbox.stub(window.top, 'getComputedStyle'); - getComputedStyleStub.returns({ display: 'block' }); - }); - - afterEach(() => { - delete window.$sf; - getElementByIdStub.restore(); - getComputedStyleStub.restore(); - }); - - it('should not compute the slot position in cross-origin iframe', function() { - sandbox.stub(utils, 'getWindowTop').throws(); - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq(''); - }); - - it('should compute the slot position in cross-origin iframe w/ safeFrame api', function() { - window.$sf = $sf; - sandbox.stub(window.$sf.ext, 'geom').returns({ - win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, - self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, - }); - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq('210x859'); - }); - - it('should not compute the slot position if safeFrame api is misimplemented', function() { - window.$sf = { - ext: { geom: 'nothing' } - }; - utilsMock.expects('logWarn').once(); - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq(''); - utilsMock.verify(); - }); + describe('Adagio features when prebid in crossdomain iframe', function() { + it('should return all expected features', function() { + sandbox.stub(utils, 'getWindowTop').throws(); - it('should not compute the slot position due to unreachable adUnitElementId', function() { - getElementByIdStub.returns(null); - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq(''); - }); + const bidRequest = new BidRequestBuilder({ + 'mediaTypes': { + banner: { sizes: [[300, 250]] } + } + }).withParams().build(); - it('should use a quick switch to display slot and compute position', function() { - getComputedStyleStub.returns({ display: 'none' }); - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq('800x300'); - }); + const bidderRequest = new BidderRequestBuilder().build(); - it('should compute the slot position based on window.top w/o postBid param', function() { - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: false }); - expect(result).to.eq('800x300'); - }); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const result = requests[0].data.adUnits[0].features; - it.skip('should compute the slot position inside the parent window (window.top) when safeFrame is not available and postBid params is `true`', function() { - const result = _features.getSlotPosition({ adUnitElementId: 'gpt-adunit-code', postBid: true }); - // expect(result).to.eq('800x300'); - }); + expect(result.page_dimensions).to.not.exist; + expect(result.viewport_dimensions).to.not.exist; + expect(result.print_number).to.be.a('String'); + expect(result.dom_loading).to.be.a('String'); + expect(result.user_timestamp).to.be.a('String'); + expect(result.adunit_position).to.not.exist; }); }); - describe('optional params auto detection', function() { + describe.skip('optional params auto detection', function() { it('should auto detect environment', function() { const getDeviceStub = sandbox.stub(_features, 'getDevice'); @@ -1460,7 +1352,7 @@ describe('Adagio bid adapter', () => { }); }); - describe('print number handling', function() { + describe.skip('print number handling', function() { it('should return 1 if no adunit-code found. This means it is the first auction', function() { sandbox.stub(adagio, 'getPageviewId').returns('abc-def'); expect(adagio.computePrintNumber('adunit-code')).to.eql(1); From b7ede2633f2437a60ed4ff3d52a3485c01963453 Mon Sep 17 00:00:00 2001 From: ym-atsymuk <81176595+ym-atsymuk@users.noreply.github.com> Date: Wed, 23 Jun 2021 11:20:37 +0200 Subject: [PATCH 1189/1476] yieldmo adapter: add support of ATS envelope (#7082) --- modules/yieldmoBidAdapter.js | 6 +++++- modules/yieldmoBidAdapter.md | 13 ++++++++----- test/spec/modules/yieldmoBidAdapter_spec.js | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index fa1ab3a90b3..654e7236210 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -93,6 +93,9 @@ export const spec = { if (request.schain) { serverRequest.schain = JSON.stringify(request.schain); } + if (utils.deepAccess(request, 'params.lr_env')) { + serverRequest.ats_envelope = request.params.lr_env; + } }); serverRequest.p = '[' + serverRequest.p.toString() + ']'; @@ -322,7 +325,8 @@ function openRtbRequest(bidRequests, bidderRequest) { bcat: bidRequests[0].params.bcat || [], ext: { prebid: '$prebid.version$', - } + }, + ats_envelope: bidRequests[0].params.lr_env, }; populateOpenRtbGdpr(openRtbRequest, bidderRequest); diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 040fbbec486..c98e2ab5c74 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -29,8 +29,9 @@ var adUnits = [{ // Banner adUnit bids: [{ bidder: 'yieldmo', params: { - placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only) - bidFloor: .28 // optional param + placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only) + bidFloor: .28, // optional param + lr_env: '***' // Optional. Live Ramp ATS envelope } }] }]; @@ -64,7 +65,8 @@ var adUnits = [{ // Video adUnit playbackmethod: [2,6], // required, array of integers skippable: true, // optional, boolean skipafter: 10 // optional, integer - } + }, + lr_env: '***' // Optional. Live Ramp ATS envelope } }] }]; @@ -91,13 +93,14 @@ var videoAdUnit = [{ protocols: [2, 3], // required, array of integers api: [2, 3], // required, array of integers playbackmethod: [1,2] // required, array of integers - } + }, + lr_env: '***' // Optional. Live Ramp ATS envelope } }] }]; ``` -Please also note, that we support the following OpenRTB params: +Please also note, that we support the following OpenRTB params: 'mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable'. They can be specified in `mediaTypes.video` or in `bids[].params.video`. diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 77542480c6c..378c5d89113 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -431,6 +431,20 @@ describe('YieldmoAdapter', function () { const requests = build([mockVideoBid()]); expect(requests[0].data.imp[0].bidfloor).to.equal(0); }); + + it('should add ats_envelope to video bid request', function() { + const envelope = 'test_envelope'; + const requests = build([mockVideoBid({}, { lr_env: envelope })]); + + expect(requests[0].data.ats_envelope).to.equal(envelope); + }); + + it('should add ats_envelope to banner bid request', function() { + const envelope = 'test_envelope'; + const requests = build([mockBannerBid({}, { lr_env: envelope })]); + + expect(requests[0].data.ats_envelope).to.equal(envelope); + }); }); }); From 16b85d0c3c9a7fdb81f805d3aeadf18d45d765fe Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 23 Jun 2021 14:30:36 +0200 Subject: [PATCH 1190/1476] tappx Bid Adapter: refactor and fix gdpr user param (#7085) * tappxBidAdapter :: update adapter version * tappxBidAdapter :: refactor and fix user object Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 19ca0cf16c0..3c7274d1497 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10614'; +const TAPPX_BIDDER_VERSION = '0.1.10623'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream']; @@ -375,6 +375,8 @@ function buildOneRequest(validBidRequests, bidderRequest) { // < Params // > GDPR + let user = {}; + user.ext = {}; // Universal ID let eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); @@ -386,18 +388,14 @@ function buildOneRequest(validBidRequests, bidderRequest) { (typeof uuid.uids[0].id == 'string' && uuid.uids[0].id !== null) ) - payload.user = { - ext: { - eids: eidsArr - } - } + if (typeof user !== 'undefined') { user.ext.eids = eidsArr } }; let regs = {}; regs.gdpr = 0; if (!(bidderRequest.gdprConsent == null)) { if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { regs.gdpr = bidderRequest.gdprConsent.gdprApplies; } - if (regs.gdpr) { payload.user.ext.consent = bidderRequest.gdprConsent.consentString; } + if (regs.gdpr) { user.ext.consent = bidderRequest.gdprConsent.consentString; } } // CCPA @@ -419,6 +417,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.tmax = bidderRequest.timeout ? bidderRequest.timeout : 600; payload.bidder = BIDDER_CODE; payload.imp = [imp]; + payload.user = user; payload.device = device; payload.params = params; From ba5f404e246b556bbe5985d3b1f33f9330422ee2 Mon Sep 17 00:00:00 2001 From: Michael Callari Date: Wed, 23 Jun 2021 10:41:19 -0400 Subject: [PATCH 1191/1476] Fixing Optimera RTD targeting values. (7079) (#7080) --- modules/optimeraRtdProvider.js | 2 +- test/spec/modules/optimeraRtdProvider_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js index fadbd099368..2ed1bd78c42 100644 --- a/modules/optimeraRtdProvider.js +++ b/modules/optimeraRtdProvider.js @@ -98,7 +98,7 @@ export function returnTargetingData(adUnits, config) { adUnits.forEach(function(adUnit) { if (optimeraTargeting[adUnit]) { targeting[adUnit] = {}; - targeting[adUnit][optimeraKeyName] = optimeraTargeting[adUnit]; + targeting[adUnit][optimeraKeyName] = [optimeraTargeting[adUnit]]; } }); } catch (err) { diff --git a/test/spec/modules/optimeraRtdProvider_spec.js b/test/spec/modules/optimeraRtdProvider_spec.js index 1d2c0d99a0a..f653389310b 100644 --- a/test/spec/modules/optimeraRtdProvider_spec.js +++ b/test/spec/modules/optimeraRtdProvider_spec.js @@ -53,8 +53,8 @@ describe('Optimera RTD targeting object is properly formed', () => { const adDivs = ['div-0', 'div-1']; it('applyTargeting properly created the targeting object', () => { const targeting = optimeraRTD.returnTargetingData(adDivs); - expect(targeting).to.deep.include({'div-0': {'optimera': ['A5', 'A6']}}); - expect(targeting).to.deep.include({'div-1': {'optimera': ['A7', 'A8']}}); + expect(targeting).to.deep.include({'div-0': {'optimera': [['A5', 'A6']]}}); + expect(targeting).to.deep.include({'div-1': {'optimera': [['A7', 'A8']]}}); }); }); From 5ad0df061efe946686a4afc7a5959ba1932a1f16 Mon Sep 17 00:00:00 2001 From: Marco Cosentino <807030+cosenmarco@users.noreply.github.com> Date: Wed, 23 Jun 2021 17:15:41 +0200 Subject: [PATCH 1192/1476] Id5 ID System: handle A/B testing server side in prebid (#7086) * #22 Removing a/b logic and using result from server side * #22 Better docs * #22 Added a check in a test case * fix typo Co-authored-by: Marco Cosentino Co-authored-by: Scott Menzer --- modules/id5IdSystem.js | 77 ++++------- test/spec/modules/id5IdSystem_spec.js | 190 +++++--------------------- 2 files changed, 58 insertions(+), 209 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 8a8cd25479f..d2d64f52738 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -18,7 +18,6 @@ const NB_EXP_DAYS = 30; export const ID5_STORAGE_NAME = 'id5id'; export const ID5_PRIVACY_STORAGE_NAME = `${ID5_STORAGE_NAME}_privacy`; const LOCAL_STORAGE = 'html5'; -const ABTEST_RESOLUTION = 10000; const LOG_PREFIX = 'User ID - ID5 submodule: '; // order the legacy cookie names in reverse priority order so the last @@ -59,22 +58,6 @@ export const id5IdSubmodule = { return undefined; } - // check for A/B testing configuration and hide ID if in Control Group - const abConfig = getAbTestingConfig(config); - const controlGroup = isInControlGroup(universalUid, abConfig.controlGroupPct); - if (abConfig.enabled === true && typeof controlGroup === 'undefined') { - // A/B Testing is enabled, but configured improperly, so skip A/B testing - utils.logError(LOG_PREFIX + 'A/B Testing controlGroupPct must be a number >= 0 and <= 1! Skipping A/B Testing'); - } else if (abConfig.enabled === true && controlGroup === true) { - // A/B Testing is enabled and user is in the Control Group, so do not share the ID5 ID - utils.logInfo(LOG_PREFIX + 'A/B Testing Enabled - user is in the Control Group, so the ID5 ID is NOT exposed'); - universalUid = ''; - linkType = 0; - } else if (abConfig.enabled === true) { - // A/B Testing is enabled but user is not in the Control Group, so ID5 ID is shared - utils.logInfo(LOG_PREFIX + 'A/B Testing Enabled - user is NOT in the Control Group, so the ID5 ID is exposed'); - } - let responseObj = { id5id: { uid: universalUid, @@ -84,8 +67,22 @@ export const id5IdSubmodule = { } }; - if (abConfig.enabled === true) { - utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', (typeof controlGroup === 'undefined' ? false : controlGroup)); + const abTestingResult = utils.deepAccess(value, 'ab_testing.result'); + switch (abTestingResult) { + case 'control': + // A/B Testing is enabled and user is in the Control Group + utils.logInfo(LOG_PREFIX + 'A/B Testing - user is in the Control Group: ID5 ID is NOT exposed'); + utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', true); + break; + case 'error': + // A/B Testing is enabled, but configured improperly, so skip A/B testing + utils.logError(LOG_PREFIX + 'A/B Testing ERROR! controlGroupPct must be a number >= 0 and <= 1'); + break; + case 'normal': + // A/B Testing is enabled but user is not in the Control Group, so ID5 ID is shared + utils.logInfo(LOG_PREFIX + 'A/B Testing - user is NOT in the Control Group'); + utils.deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', false); + break; } utils.logInfo(LOG_PREFIX + 'Decoded ID', responseObj); @@ -139,9 +136,12 @@ export const id5IdSubmodule = { data.provider = config.params.provider; } - // pass in feature flags, if applicable - if (getAbTestingConfig(config).enabled === true) { - utils.deepSetValue(data, 'features.ab', 1); + 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) { @@ -178,7 +178,7 @@ export const id5IdSubmodule = { utils.logInfo(LOG_PREFIX + 'requesting an ID from the server', data); ajax(url, callbacks, JSON.stringify(data), { method: 'POST', withCredentials: true }); }; - return {callback: resp}; + return { callback: resp }; }, /** @@ -310,37 +310,10 @@ export function storeInLocalStorage(key, value, expDays) { * gets the existing abTesting config or generates a default config with abTesting off * * @param {SubmoduleConfig|undefined} config - * @returns {(Object|undefined)} + * @returns {Object} an object which always contains at least the property "enabled" */ function getAbTestingConfig(config) { - return (config && config.params && config.params.abTesting) || { enabled: false }; -} - -/** - * Return a consistant random number between 0 and ABTEST_RESOLUTION-1 for this user - * Falls back to plain random if no user provided - * @param {string} userId - * @returns {number} - */ -function abTestBucket(userId) { - if (userId) { - return ((utils.cyrb53Hash(userId) % ABTEST_RESOLUTION) + ABTEST_RESOLUTION) % ABTEST_RESOLUTION; - } else { - return Math.floor(Math.random() * ABTEST_RESOLUTION); - } -} - -/** - * Return a consistant boolean if this user is within the control group ratio provided - * @param {string} userId - * @param {number} controlGroupPct - Ratio [0,1] of users expected to be in the control group - * @returns {boolean} - */ -export function isInControlGroup(userId, controlGroupPct) { - if (!utils.isNumber(controlGroupPct) || controlGroupPct < 0 || controlGroupPct > 1) { - return undefined; - } - return abTestBucket(userId) < controlGroupPct * ABTEST_RESOLUTION; + return utils.deepAccess(config, 'params.abTesting', { enabled: false }); } submodule('userId', id5IdSubmodule); diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 9ba9aee9c63..debde20e4c0 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -74,9 +74,6 @@ describe('ID5 ID System', function() { } } } - function getFetchCookieConfig() { - return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'cookie')]); - } function getFetchLocalStorageConfig() { return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'html5')]); } @@ -239,35 +236,36 @@ describe('ID5 ID System', function() { expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); }); - it('should call the ID5 server with ab feature = 1 when abTesting is turned on', function () { + it('should call the ID5 server with ab_testing object when abTesting is turned on', function () { let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: true, controlGroupPct: 10 } + id5Config.params.abTesting = { enabled: true, controlGroupPct: 0.234 } 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.features.ab).to.eq(1); + expect(requestBody.ab_testing.enabled).to.eq(true); + expect(requestBody.ab_testing.control_group_pct).to.eq(0.234); request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); }); - it('should call the ID5 server without ab feature when abTesting is turned off', function () { + it('should call the ID5 server without ab_testing object when abTesting is turned off', function () { let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: false, controlGroupPct: 10 } + id5Config.params.abTesting = { enabled: false, controlGroupPct: 0.55 } 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.features).to.be.undefined; + expect(requestBody.ab_testing).to.be.undefined; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); }); - it('should call the ID5 server without ab feature when when abTesting is not set', function () { + it('should call the ID5 server without ab_testing when when abTesting is not set', function () { let id5Config = getId5FetchConfig(); let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; @@ -275,7 +273,7 @@ describe('ID5 ID System', function() { let request = server.requests[0]; let requestBody = JSON.parse(request.requestBody); - expect(requestBody.features).to.be.undefined; + expect(requestBody.ab_testing).to.be.undefined; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); }); @@ -450,105 +448,11 @@ describe('ID5 ID System', 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; + let testConfig, storedObject; beforeEach(function() { testConfig = getId5FetchConfig(); - }); - - describe('Configuration Validation', function() { - let logErrorSpy; - let logInfoSpy; - - beforeEach(function() { - logErrorSpy = sinon.spy(utils, 'logError'); - logInfoSpy = sinon.spy(utils, 'logInfo'); - }); - afterEach(function() { - logErrorSpy.restore(); - logInfoSpy.restore(); - }); - - // A/B Testing ON, but invalid config - let testInvalidAbTestingConfigsWithError = [ - { enabled: true }, - { enabled: true, controlGroupPct: 2 }, - { enabled: true, controlGroupPct: -1 }, - { enabled: true, controlGroupPct: 'a' }, - { enabled: true, controlGroupPct: true } - ]; - testInvalidAbTestingConfigsWithError.forEach((testAbTestingConfig) => { - it('should be undefined if ratio is invalid', () => { - expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.be.undefined; - }); - it('should error if config is invalid, and always return an ID', function () { - testConfig.params.abTesting = testAbTestingConfig; - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); - sinon.assert.calledOnce(logErrorSpy); - }); - }); - - // A/B Testing OFF, with invalid config (ignore) - let testInvalidAbTestingConfigsWithoutError = [ - { enabled: false, controlGroupPct: -1 }, - { enabled: false, controlGroupPct: 2 }, - { enabled: false, controlGroupPct: 'a' }, - { enabled: false, controlGroupPct: true } - ]; - testInvalidAbTestingConfigsWithoutError.forEach((testAbTestingConfig) => { - it('should be undefined if ratio is invalid', () => { - expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.be.undefined; - }); - it('should not error if config is invalid but A/B testing is off, and always return an ID', function () { - testConfig.params.abTesting = testAbTestingConfig; - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); - sinon.assert.notCalled(logErrorSpy); - }); - }); - - // A/B Testing ON, with valid config - let testValidConfigs = [ - { enabled: true, controlGroupPct: 0 }, - { enabled: true, controlGroupPct: 0.5 }, - { enabled: true, controlGroupPct: 1 } - ]; - testValidConfigs.forEach((testAbTestingConfig) => { - it('should not be undefined if ratio is valid', () => { - expect(isInControlGroup('userId', testAbTestingConfig.controlGroupPct)).to.not.be.undefined; - }); - it('should not error if config is valid', function () { - testConfig.params.abTesting = testAbTestingConfig; - id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - sinon.assert.notCalled(logErrorSpy); - }); - }); - }); - - describe('A/B Testing Config is not Set', function() { - let randStub; - - beforeEach(function() { - randStub = sinon.stub(Math, 'random').callsFake(function() { - return 0; - }); - }); - afterEach(function () { - randStub.restore(); - }); - - it('should expose ID when A/B config is not set', function () { - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); - }); - - it('should expose ID when A/B config is empty', function () { - testConfig.params.abTesting = { }; - - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); - }); + storedObject = utils.deepClone(ID5_STORED_OBJ); }); describe('A/B Testing Config is Set', function() { @@ -563,69 +467,41 @@ describe('ID5 ID System', function() { randStub.restore(); }); - describe('IsInControlGroup', function () { - it('Nobody is in a 0% control group', function () { - expect(isInControlGroup('dsdndskhsdks', 0)).to.be.false; - expect(isInControlGroup('3erfghyuijkm', 0)).to.be.false; - expect(isInControlGroup('', 0)).to.be.false; - expect(isInControlGroup(undefined, 0)).to.be.false; - }); - - it('Everybody is in a 100% control group', function () { - expect(isInControlGroup('dsdndskhsdks', 1)).to.be.true; - expect(isInControlGroup('3erfghyuijkm', 1)).to.be.true; - expect(isInControlGroup('', 1)).to.be.true; - expect(isInControlGroup(undefined, 1)).to.be.true; - }); + describe('Decode', function() { + let logErrorSpy; - it('Being in the control group must be consistant', function () { - const inControlGroup = isInControlGroup('dsdndskhsdks', 0.5); - expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; - expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; - expect(inControlGroup === isInControlGroup('dsdndskhsdks', 0.5)).to.be.true; + beforeEach(function() { + logErrorSpy = sinon.spy(utils, 'logError'); }); - - it('Control group ratio must be within a 10% error on a large sample', function () { - let nbInControlGroup = 0; - const sampleSize = 100; - for (let i = 0; i < sampleSize; i++) { - nbInControlGroup = nbInControlGroup + (isInControlGroup('R$*df' + i, 0.5) ? 1 : 0); - } - expect(nbInControlGroup).to.be.greaterThan(sampleSize / 2 - sampleSize / 10); - expect(nbInControlGroup).to.be.lessThan(sampleSize / 2 + sampleSize / 10); + afterEach(function() { + logErrorSpy.restore(); }); - }); - - describe('Decode', function() { - it('should expose ID when A/B testing is off', function () { - testConfig.params.abTesting = { - enabled: false, - controlGroupPct: 0.5 - }; - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + 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); }); - it('should expose ID when no one is in control group', function () { - testConfig.params.abTesting = { - enabled: true, - controlGroupPct: 0 - }; - - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + it('should set abTestingControlGroup to false when A/B testing is on but in normal group', function () { + storedObject.ab_testing = { result: 'normal' }; + let decoded = id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); }); it('should not expose ID when everyone is in control group', function () { - testConfig.params.abTesting = { - enabled: true, - controlGroupPct: 1 - }; - - let decoded = id5IdSubmodule.decode(ID5_STORED_OBJ, testConfig); + storedObject.ab_testing = { result: 'control' }; + storedObject.universal_uid = ''; + storedObject.link_type = 0; + let decoded = id5IdSubmodule.decode(storedObject, testConfig); expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); }); + + it('should log A/B testing errors', function () { + storedObject.ab_testing = { result: 'error' }; + let decoded = id5IdSubmodule.decode(storedObject, testConfig); + expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + sinon.assert.calledOnce(logErrorSpy); + }); }); }); }); From c0454c833a00ea26aa31f0224c004a6e61b12e5e Mon Sep 17 00:00:00 2001 From: winrdev <85925981+winrdev@users.noreply.github.com> Date: Thu, 24 Jun 2021 01:36:52 +0800 Subject: [PATCH 1193/1476] WINR Bid Adapter: add new bid adapter (#7050) * WINR Bid Adapter: add new bid adapter * Updated winrBidAdapter.md --- modules/winrBidAdapter.js | 614 ++++++++++++++++++ modules/winrBidAdapter.md | 546 ++++++++++++++++ test/spec/modules/winrBidAdapter_spec.js | 791 +++++++++++++++++++++++ 3 files changed, 1951 insertions(+) create mode 100644 modules/winrBidAdapter.js create mode 100644 modules/winrBidAdapter.md create mode 100644 test/spec/modules/winrBidAdapter_spec.js diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js new file mode 100644 index 00000000000..b73758e9717 --- /dev/null +++ b/modules/winrBidAdapter.js @@ -0,0 +1,614 @@ +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); + +const BIDDER_CODE = 'winr'; +const URL = 'https://ib.adnxs.com/ut/v3/prebid'; +const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; +const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; +const APP_DEVICE_PARAMS = ['geo', 'device_id']; // appid is collected separately +const SOURCE = 'pbjs'; +const DEFAULT_CURRENCY = 'USD'; +const GATE_COOKIE_NAME = 'wnr_gate'; + +function buildBid(bidData) { + const bid = bidData; + const position = { + domParent: bid.meta.domParent ? `'${bid.meta.domParent}'` : null, + child: bid.meta.child ? bid.meta.child : 4 + } + bid.ad = wrapAd(bid, position); + return bid; +} + +function getMediaTypeFromBid(bid) { + return bid.mediaTypes && Object.keys(bid.mediaTypes)[0]; +} + +function wrapAd(bid, position) { + return ` + + + + + + + + + + `; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['wnr'], + 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. + */ + isBidRequestValid: function (bid) { + // Return false for each bid request if the media type is not 'banner' + if (getMediaTypeFromBid(bid) !== BANNER) { + return false; + } + + // Return false for each bid request if the cookies disabled + if (!storage.cookiesAreEnabled()) { + return false; + } + + // Return false for each bid request if the gate cookie is set + if (storage.getCookie(GATE_COOKIE_NAME) !== null) { + return false; + } + + // Return false for each bid request if no placementId exists + if (!bid.params.placementId) { + return false; + } + + return true; + }, + + /** + * 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 (bidRequests, bidderRequest) { + const tags = bidRequests.map(bidToTag); + const userObjBid = find(bidRequests, hasUserInfo); + let userObj = {}; + if (config.getConfig('coppa') === true) { + userObj = { 'coppa': true }; + } + + if (userObjBid) { + Object.keys(userObjBid.params.user) + .filter((param) => includes(USER_PARAMS, param)) + .forEach((param) => { + let uparam = utils.convertCamelToUnderscore(param); + if ( + param === 'segments' && + utils.isArray(userObjBid.params.user[param]) + ) { + let segs = []; + userObjBid.params.user[param].forEach((val) => { + if (utils.isNumber(val)) { + segs.push({ id: val }); + } else if (utils.isPlainObject(val)) { + segs.push(val); + } + }); + userObj[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; + } + }); + } + + const appDeviceObjBid = find(bidRequests, hasAppDeviceInfo); + let appDeviceObj; + if (appDeviceObjBid && appDeviceObjBid.params && appDeviceObjBid.params.app) { + appDeviceObj = {}; + Object.keys(appDeviceObjBid.params.app) + .filter(param => includes(APP_DEVICE_PARAMS, param)) + .forEach(param => appDeviceObj[param] = appDeviceObjBid.params.app[param]); + } + + const appIdObjBid = find(bidRequests, hasAppId); + let appIdObj; + if (appIdObjBid && appIdObjBid.params && appDeviceObjBid.params.app && appDeviceObjBid.params.app.id) { + appIdObj = { + appid: appIdObjBid.params.app.id + }; + } + + const memberIdBid = find(bidRequests, hasMemberId); + const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; + const schain = bidRequests[0].schain; + + const payload = { + tags: [...tags], + user: userObj, + sdk: { + source: SOURCE, + version: '$prebid.version$', + }, + schain: schain + }; + + if (member > 0) { + payload.member_id = member; + } + + if (appDeviceObjBid) { + payload.device = appDeviceObj + } + if (appIdObjBid) { + payload.app = appIdObj; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + // note - objects for impbus use underscore instead of camelCase + payload.gdpr_consent = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies, + }; + } + + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + + if (bidderRequest && bidderRequest.refererInfo) { + let refererinfo = { + rd_ref: encodeURIComponent(bidderRequest.refererInfo.referer), + rd_top: bidderRequest.refererInfo.reachedTop, + rd_ifs: bidderRequest.refererInfo.numIframes, + rd_stk: bidderRequest.refererInfo.stack + .map((url) => encodeURIComponent(url)) + .join(','), + }; + payload.referrer_detection = refererinfo; + } + + if (bidRequests[0].userId) { + let eids = []; + + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.flocId.id`), 'chrome.com', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.criteoId`), 'criteo.com', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.netId`), 'netid.de', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.idl_env`), 'liveramp.com', null); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.tdid`), 'adserver.org', 'TDID'); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.uid2.id`), 'uidapi.com', 'UID2'); + + if (eids.length) { + payload.eids = eids; + } + } + + if (tags[0].publisher_id) { + payload.publisher_id = tags[0].publisher_id; + } + + const request = formatRequest(payload, bidderRequest); + return request; + }, + + /** + * 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, { bidderRequest }) { + serverResponse = serverResponse.body; + const bids = []; + if (!serverResponse || serverResponse.error) { + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; + if (serverResponse && serverResponse.error) { + errorMessage += `: ${serverResponse.error}`; + } + utils.logError(errorMessage); + return bids; + } + + if (serverResponse.tags) { + serverResponse.tags.forEach((serverBid) => { + const rtbBid = getRtbBid(serverBid); + if (rtbBid) { + if ( + rtbBid.cpm !== 0 && + includes(this.supportedMediaTypes, rtbBid.ad_type) + ) { + const bid = newBid(serverBid, rtbBid, bidderRequest); + bid.mediaType = parseMediaType(rtbBid); + bids.push(bid); + } + } + }); + } + + return bids.map(bid => buildBid(bid)); + }, + + getUserSyncs: function (syncOptions) { + if (syncOptions.iframeEnabled) { + return [ + { + type: 'iframe', + url: 'https://acdn.adnxs.com/dmp/async_usersync.html', + }, + ]; + } + }, + + transformBidParams: function (params, isOpenRtb) { + params = utils.convertTypes( + { + member: 'string', + invCode: 'string', + placementId: 'number', + keywords: utils.transformBidderParamKeywords, + publisherId: 'number', + }, + params + ); + + if (isOpenRtb) { + params.use_pmt_rule = + typeof params.usePaymentRule === 'boolean' + ? params.usePaymentRule + : false; + if (params.usePaymentRule) { + delete params.usePaymentRule; + } + + if (isPopulatedArray(params.keywords)) { + params.keywords.forEach(deleteValues); + } + + Object.keys(params).forEach((paramKey) => { + let convertedKey = utils.convertCamelToUnderscore(paramKey); + if (convertedKey !== paramKey) { + params[convertedKey] = params[paramKey]; + delete params[paramKey]; + } + }); + } + + return params; + }, +}; + +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + +function hasPurpose1Consent(bidderRequest) { + let result = true; + if (bidderRequest && bidderRequest.gdprConsent) { + if ( + bidderRequest.gdprConsent.gdprApplies && + bidderRequest.gdprConsent.apiVersion === 2 + ) { + result = !!( + utils.deepAccess( + bidderRequest.gdprConsent, + 'vendorData.purpose.consents.1' + ) === true + ); + } + } + return result; +} + +function formatRequest(payload, bidderRequest) { + let request = []; + let options = { + withCredentials: true + }; + + let endpointUrl = URL; + + if (!hasPurpose1Consent(bidderRequest)) { + endpointUrl = URL_SIMPLE; + } + + if ( + utils.getParameterByName('apn_test').toUpperCase() === 'TRUE' || + config.getConfig('apn_test') === true + ) { + options.customHeaders = { + 'X-Is-Test': 1, + }; + } + + const payloadString = JSON.stringify(payload); + request = { + method: 'POST', + url: endpointUrl, + data: payloadString, + bidderRequest, + options, + }; + + return request; +} + +/** + * Unpack the Server's Bid into a Prebid-compatible one. + * @param serverBid + * @param rtbBid + * @param bidderRequest + * @return Bid + */ +function newBid(serverBid, rtbBid, bidderRequest) { + const bidRequest = utils.getBidRequest(serverBid.uuid, [bidderRequest]); + const bid = { + adType: rtbBid.ad_type, + requestId: serverBid.uuid, + auctionId: bidRequest.auctionId, + cpm: rtbBid.cpm, + creativeId: rtbBid.creative_id, + brandCategoryId: rtbBid.brandCategoryId, + dealId: rtbBid.deal_id, + currency: DEFAULT_CURRENCY, + netRevenue: true, + ttl: 300, + source: rtbBid.content_source, + mediaSubtypeId: rtbBid.media_subtype_id, + mediaTypeId: rtbBid.media_type_id, + adUnitCode: bidRequest.adUnitCode, + buyerMemberId: rtbBid.buyer_member_id, + appnexus: { + buyerMemberId: rtbBid.buyer_member_id, + dealPriority: rtbBid.deal_priority, + dealCode: rtbBid.deal_code, + } + }; + + // WE DON'T FULLY SUPPORT THIS ATM - future spot for adomain code; creating a stub for 5.0 compliance + if (rtbBid.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [] }); + } + + if (rtbBid.advertiser_id) { + bid.meta = Object.assign({}, bid.meta, { + advertiserId: rtbBid.advertiser_id, + }); + } + + if (bidRequest.params) { + const { placementId, siteId, domParent, child } = bidRequest.params; + bid.meta = Object.assign({}, bid.meta, { + placementId: placementId, + siteId: siteId, + domParent: domParent, + child: child + }); + } + + Object.assign(bid, { + width: rtbBid.rtb.banner.width, + height: rtbBid.rtb.banner.height, + }); + + try { + if (rtbBid.rtb.banner && rtbBid.rtb.trackers) { + bid.banner = Object.assign({}, bid.banner, { + content: rtbBid.rtb.banner.content, + width: rtbBid.rtb.banner.width, + height: rtbBid.rtb.banner.height, + trackers: rtbBid.rtb.trackers, + }); + } + } catch (error) { + utils.logError('Error assigning ad', error); + } + return bid; +} + +function bidToTag(bid) { + const tag = {}; + tag.sizes = transformSizes(bid.sizes); + tag.primary_size = tag.sizes[0]; + tag.ad_types = []; + tag.uuid = bid.bidId; + if (bid.params.placementId) { + tag.id = parseInt(bid.params.placementId, 10); + } else { + tag.code = bid.params.invCode; + } + tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; + tag.use_pmt_rule = bid.params.usePaymentRule || false; + tag.prebid = true; + tag.disable_psa = true; + let bidFloor = getBidFloor(bid); + if (bidFloor) { + tag.reserve = bidFloor; + } + if (bid.params.trafficSourceCode) { + tag.traffic_source_code = bid.params.trafficSourceCode; + } + if (bid.params.privateSizes) { + tag.private_sizes = transformSizes(bid.params.privateSizes); + } + if (bid.params.pubClick) { + tag.pubclick = bid.params.pubClick; + } + if (bid.params.publisherId) { + tag.publisher_id = parseInt(bid.params.publisherId, 10); + } + if (bid.params.externalImpId) { + tag.external_imp_id = bid.params.externalImpId; + } + if (!utils.isEmpty(bid.params.keywords)) { + let keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + tag.keywords = keywords; + } + + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + tag.gpid = gpid; + } + + tag.hb_source = 1; + + if (tag.ad_types.length === 0) { + delete tag.ad_types; + } + + return tag; +} + +/* Turn bid request sizes into ut-compatible format */ +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; +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +function hasMemberId(bid) { + return !!parseInt(bid.params.member, 10); +} + +function hasAppDeviceInfo(bid) { + if (bid.params) { + return !!bid.params.app + } +} + +function hasAppId(bid) { + if (bid.params && bid.params.app) { + return !!bid.params.app.id + } + return !!bid.params.app +} + +function getRtbBid(tag) { + return tag && tag.ads && tag.ads.length && find(tag.ads, (ad) => ad.rtb); +} + +function parseMediaType(rtbBid) { + const adType = rtbBid.ad_type; + if (adType !== BANNER) { + return false; + } + return BANNER; +} + +function addUserId(eids, id, source, rti) { + if (id) { + if (rti) { + eids.push({ source, id, rti_partner: rti }); + } else { + eids.push({ source, id }); + } + } + return eids; +} + +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.reserve) ? bid.params.reserve : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + +registerBidder(spec); diff --git a/modules/winrBidAdapter.md b/modules/winrBidAdapter.md new file mode 100644 index 00000000000..f9a73a6c0fa --- /dev/null +++ b/modules/winrBidAdapter.md @@ -0,0 +1,546 @@ +# Overview + +``` +Module Name: WINR Corporation Bid Adapter +Module Type: Bidder Adapter +Maintainer: tech@winr.com.au +``` + +# Description + +WINR AdGate Bid Adaptor for Prebid.js. + +Connects to AppNexus exchange for bids. + +This bid adapter supports the Banner media type only. + +Please reach out to the WINR team before using this plugin to get `placementId`. + +`domParent` and `child` position settings are usually determined and remotely controlled for each publisher site by the WINR team. If you would prefer to have control over these settings, please get in touch. + +The code below returns a demo ad. + +# Test Parameters + +```js +var adUnits = [ + // Banner adUnit + { + code: "ad-unit", + mediaTypes: { + banner: { + sizes: [[1, 1]], + }, + }, + bids: [ + { + bidder: "winr", + params: { + placementId: 21764100, + domParent: ".blog-post", // optional + child: 4, // optional + }, + }, + ], + }, +]; +``` + +# Example page + +```html + + + + + + Prebid.js Banner Example + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+
+ +
+
+
+

Title of a featured blog post

+

+ Multiple lines of text that form the lede, informing new readers + quickly and efficiently about what’s most interesting in this post’s + contents. +

+

+ Continue reading... +

+
+
+ +
+
+

From the Firehose

+ + +
+

Sample blog post

+ + +

+ This blog post shows a few different types of content that’s + supported and styled with Bootstrap. Basic typography, images, and + code are all supported. +

+
+

+ Yeah, she dances to her own beat. Oh, no. You could've been the + greatest. 'Cause, baby, you're a firework. Maybe a + reason why all the doors are closed. Open up your heart and just + let it begin. So très chic, yeah, she's a classic. +

+
+

+ Bikinis, zucchinis, Martinis, no weenies. I know there will be + sacrifice but that's the price. + This is how we do it. I'm not sticking around + to watch you go down. You think you're so rock and roll, but + you're really just a joke. I know one spark will shock the + world, yeah yeah. Can't replace you with a million rings. +

+
+

+ Trying to connect the dots, don't know what to tell my boss. + Before you met me I was alright but things were kinda heavy. You + just gotta ignite the light and let it shine. Glitter all over the + room pink flamingos in the pool. +

+

Sub-heading

+

+ You got the finest architecture. Passport stamps, she's + cosmopolitan. Fine, fresh, fierce, we got it on lock. Never + planned that one day I'd be losing you. She eats your heart out. +

+
    +
  • Got a motel and built a fort out of sheets.
  • +
  • Your kiss is cosmic, every move is magic.
  • +
  • Suiting up for my crowning battle.
  • +
+

+ Takes you miles high, so high, 'cause she’s got that one + international smile. +

+
    +
  1. Scared to rock the boat and make a mess.
  2. +
  3. I could have rewrite your addiction.
  4. +
  5. I know you get me so I let my walls come down.
  6. +
+

After a hurricane comes a rainbow.

+ + +
+ +
+
+ +
+ +
+
+

About

+

+ Saw you downtown singing the Blues. Watch you circle the drain. + Why don't you let me stop by? Heavy is the head that + wears the crown. Yes, we make angels cry, raining down on + earth from up above. +

+
+ + +
+
+ +
+ + + + + +``` + +# Example page with GPT + +```html + + + + + + Prebid.js Banner Example + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+
+ +
+
+
+

Title of a featured blog post

+

+ Multiple lines of text that form the lede, informing new readers + quickly and efficiently about what’s most interesting in this post’s + contents. +

+

+ Continue reading... +

+
+
+ +
+
+

From the Firehose

+ + +
+

Sample blog post

+ + +

+ This blog post shows a few different types of content that’s + supported and styled with Bootstrap. Basic typography, images, and + code are all supported. +

+
+

+ Yeah, she dances to her own beat. Oh, no. You could've been the + greatest. 'Cause, baby, you're a firework. Maybe a + reason why all the doors are closed. Open up your heart and just + let it begin. So très chic, yeah, she's a classic. +

+
+

+ Bikinis, zucchinis, Martinis, no weenies. I know there will be + sacrifice but that's the price. + This is how we do it. I'm not sticking around + to watch you go down. You think you're so rock and roll, but + you're really just a joke. I know one spark will shock the + world, yeah yeah. Can't replace you with a million rings. +

+
+

+ Trying to connect the dots, don't know what to tell my boss. + Before you met me I was alright but things were kinda heavy. You + just gotta ignite the light and let it shine. Glitter all over the + room pink flamingos in the pool. +

+

Sub-heading

+

+ You got the finest architecture. Passport stamps, she's + cosmopolitan. Fine, fresh, fierce, we got it on lock. Never + planned that one day I'd be losing you. She eats your heart out. +

+
    +
  • Got a motel and built a fort out of sheets.
  • +
  • Your kiss is cosmic, every move is magic.
  • +
  • Suiting up for my crowning battle.
  • +
+

+ Takes you miles high, so high, 'cause she’s got that one + international smile. +

+
    +
  1. Scared to rock the boat and make a mess.
  2. +
  3. I could have rewrite your addiction.
  4. +
  5. I know you get me so I let my walls come down.
  6. +
+

After a hurricane comes a rainbow.

+ + +
+ +
+
+ +
+ +
+
+

About

+

+ Saw you downtown singing the Blues. Watch you circle the drain. + Why don't you let me stop by? Heavy is the head that + wears the crown. Yes, we make angels cry, raining down on + earth from up above. +

+
+ + +
+
+ +
+ + + + + +``` diff --git a/test/spec/modules/winrBidAdapter_spec.js b/test/spec/modules/winrBidAdapter_spec.js new file mode 100644 index 00000000000..03e441df727 --- /dev/null +++ b/test/spec/modules/winrBidAdapter_spec.js @@ -0,0 +1,791 @@ +import { expect } from 'chai'; +import { spec, storage } from 'modules/winrBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; + +const GATE_COOKIE_NAME = 'wnr_gate'; +const ENDPOINT = 'https://ib.adnxs.com/ut/v3/prebid'; + +function getMediaTypeFromBid(bid) { + return bid.mediaTypes && Object.keys(bid.mediaTypes)[0]; +} + +describe('WinrAdapter', 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 getCookieStub; + let cookiesAreEnabledStub; + + beforeEach(function() { + getCookieStub = sinon.stub(storage, 'getCookie'); + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + }); + + afterEach(function() { + getCookieStub.restore(); + cookiesAreEnabledStub.restore(); + }); + + let placementId = '21543013'; + let bid = { + 'bidder': 'winr', + 'params': { + 'placementId': placementId, + 'domParent': '.blog-post', + 'child': 4 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[1, 1]], + 'bidId': '2b21e2d141e6d4', + 'bidderRequestId': '1dfdc89563b81a', + 'auctionId': '0bc27fb0-ea39-4a5a-b1ba-5d83a5f28a69', + 'mediaTypes': { + 'banner': {} + }, + }; + + describe('- with cookies disabled', function () { + beforeEach(function() { + cookiesAreEnabledStub.returns(false); + }); + + it('should return false', function () { + expect(storage.cookiesAreEnabled()).to.equal(false); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('- with gate cookie set', function () { + beforeEach(function() { + getCookieStub.withArgs(GATE_COOKIE_NAME).returns('true'); + }); + + it('should return false', function () { + expect(storage.getCookie(GATE_COOKIE_NAME)).to.not.equal(null); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('- with cookies enabled and gate cookie not set', function () { + beforeEach(function() { + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs(GATE_COOKIE_NAME).returns(null); + }); + + it('should return true when required params found', function () { + expect(storage.cookiesAreEnabled()).to.equal(true); + expect(storage.getCookie(GATE_COOKIE_NAME)).to.equal(null); + expect(getMediaTypeFromBid(bid)).to.equal('banner'); + expect(bid).to.have.deep.nested.property('params.placementId'); + expect(bid.params.placementId).to.equal(placementId); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + it('should return false when mediaType is not banner', function () { + let bid = Object.assign({}, bid); + delete bid.mediaTypes; + bid.mediaTypes = { + 'video': {} + }; + expect(getMediaTypeFromBid(bid)).to.not.equal('banner'); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let getAdUnitsStub; + let bidRequests = [ + { + 'bidder': 'winr', + 'params': { + 'placementId': '21543013' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[1, 1]], + 'bidId': '2b21e2d141e6d4', + 'bidderRequestId': '1dfdc89563b81a', + 'auctionId': '0bc27fb0-ea39-4a5a-b1ba-5d83a5f28a69', + 'transactionId': '270e4b6e-0acc-41c4-b253-b935f966fa7d' + } + ]; + + beforeEach(function() { + getAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits').callsFake(function() { + return []; + }); + }); + + afterEach(function() { + getAdUnitsStub.restore(); + }); + + it('should parse out private sizes', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + privateSizes: [1, 1] + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].private_sizes).to.exist; + expect(payload.tags[0].private_sizes).to.deep.equal([{width: 1, height: 1}]); + }); + + it('should add publisher_id in request', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + publisherId: '1231234' + } + }); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].publisher_id).to.exist; + expect(payload.tags[0].publisher_id).to.deep.equal(1231234); + expect(payload.publisher_id).to.exist; + expect(payload.publisher_id).to.deep.equal(1231234); + }) + + it('should add source and version to the tag', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.sdk).to.exist; + expect(payload.sdk).to.deep.equal({ + source: 'pbjs', + version: '$prebid.version$' + }); + }); + + it('should not populate the ad_types array when adUnit.mediaTypes is undefined', function() { + const bidRequest = Object.assign({}, bidRequests[0]); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].ad_types).to.not.exist; + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should attach valid user params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + user: { + externalUid: '123', + // dnt: false, + segments: [123, { id: 987, value: 876 }], + foobar: 'invalid' + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.user).to.exist; + expect(payload.user).to.deep.equal({ + external_uid: '123', + // dnt: false + segments: [{id: 123}, {id: 987, value: 876}] + }); + }); + + it('should attach reserve param when either bid param or getFloor function exists', function () { + let getFloorResponse = { currency: 'USD', floor: 3 }; + let request, payload = null; + let bidRequest = deepClone(bidRequests[0]); + + // 1 -> reserve not defined, getFloor not defined > empty + request = spec.buildRequests([bidRequest]); + payload = JSON.parse(request.data); + + expect(payload.tags[0].reserve).to.not.exist; + + // 2 -> reserve is defined, getFloor not defined > reserve is used + bidRequest.params = { + 'placementId': '21543013', + 'reserve': 0.5 + }; + request = spec.buildRequests([bidRequest]); + payload = JSON.parse(request.data); + + expect(payload.tags[0].reserve).to.exist.and.to.equal(0.5); + + // 3 -> reserve is defined, getFloor is defined > getFloor is used + bidRequest.getFloor = () => getFloorResponse; + + request = spec.buildRequests([bidRequest]); + payload = JSON.parse(request.data); + + expect(payload.tags[0].reserve).to.exist.and.to.equal(3); + }); + + it('should contain hb_source value for other media', function() { + let bidRequest = Object.assign({}, + bidRequests[0], + { + mediaType: 'banner', + params: { + sizes: [[1, 1]], + placementId: 13144370 + } + } + ); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].hb_source).to.deep.equal(1); + }); + + it('should convert keyword params to proper form and attaches to request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + emptyStr: '', + emptyArr: [''], + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + }]); + }); + + it('should add payment rules to the request', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + usePaymentRule: true + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].use_pmt_rule).to.equal(true); + }); + + it('should add gpid to the request', function () { + let testGpid = '/12345/my-gpt-tag-0'; + let bidRequest = deepClone(bidRequests[0]); + bidRequest.ortb2Imp = { ext: { data: { pbadslot: testGpid } } }; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].gpid).to.exist.and.equal(testGpid) + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'winr', + 'auctionId': '0bc27fb0-ea39-4a5a-b1ba-5d83a5f28a69', + 'bidderRequestId': '1dfdc89563b81a', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, 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; + }); + + it('should add us privacy string to payload', function() { + let consentString = '1YA-'; + let bidderRequest = { + 'bidderCode': 'winr', + 'auctionId': '0bc27fb0-ea39-4a5a-b1ba-5d83a5f28a69', + 'bidderRequestId': '1dfdc89563b81a', + 'timeout': 3000, + 'uspConsent': consentString + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.us_privacy).to.exist; + expect(payload.us_privacy).to.exist.and.to.equal(consentString); + }); + + it('supports sending hybrid mobile app parameters', function () { + let appRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '21543013', + app: { + id: 'B1O2W3M4AN.com.prebid.webview', + geo: { + lat: 40.0964439, + lng: -75.3009142 + }, + device_id: { + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', // Apple advertising identifier + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', // Android advertising identifier + md5udid: '5756ae9022b2ea1e47d84fead75220c8', // MD5 hash of the ANDROID_ID + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', // SHA1 hash of the ANDROID_ID + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' // Windows advertising identifier + } + } + } + } + ); + const request = spec.buildRequests([appRequest]); + const payload = JSON.parse(request.data); + expect(payload.app).to.exist; + expect(payload.app).to.deep.equal({ + appid: 'B1O2W3M4AN.com.prebid.webview' + }); + expect(payload.device.device_id).to.exist; + expect(payload.device.device_id).to.deep.equal({ + aaid: '38400000-8cf0-11bd-b23e-10b96e40000d', + idfa: '4D12078D-3246-4DA4-AD5E-7610481E7AE', + md5udid: '5756ae9022b2ea1e47d84fead75220c8', + sha1udid: '4DFAA92388699AC6539885AEF1719293879985BF', + windowsadid: '750c6be243f1c4b5c9912b95a5742fc5' + }); + expect(payload.device.geo).to.exist; + expect(payload.device.geo).to.deep.equal({ + lat: 40.0964439, + lng: -75.3009142 + }); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'https://example.com/page.html', + reachedTop: true, + numIframes: 2, + stack: [ + 'https://example.com/page.html', + 'https://example.com/iframe1.html', + 'https://example.com/iframe2.html' + ] + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer_detection).to.exist; + expect(payload.referrer_detection).to.deep.equal({ + rd_ref: 'https%3A%2F%2Fexample.com%2Fpage.html', + rd_top: true, + rd_ifs: 2, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + }); + }); + + it('should populate member if available', function () { + const bidRequest = Object.assign({}, bidRequests[0], { + params: { + member: '11626' + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.member_id).to.deep.equal(11626); + }); + + it('should populate schain if available', function () { + const bidRequest = Object.assign({}, bidRequests[0], { + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.schain).to.deep.equal({ + ver: '1.0', + complete: 1, + nodes: [ + { + 'asi': 'blob.com', + 'sid': '001', + 'hp': 1 + } + ] + }); + }); + + it('should populate coppa if set in config', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon.stub(config, 'getConfig') + .withArgs('coppa') + .returns(true); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.user.coppa).to.equal(true); + + config.getConfig.restore(); + }); + + it('should set the X-Is-Test customHeader if test flag is enabled', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + sinon.stub(config, 'getConfig') + .withArgs('apn_test') + .returns(true); + + const request = spec.buildRequests([bidRequest]); + expect(request.options.customHeaders).to.deep.equal({'X-Is-Test': 1}); + + config.getConfig.restore(); + }); + + it('should always set withCredentials: true on the request.options', function () { + let bidRequest = Object.assign({}, bidRequests[0]); + const request = spec.buildRequests([bidRequest]); + expect(request.options.withCredentials).to.equal(true); + }); + + it('should set simple domain variant if purpose 1 consent is not given', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'winr', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true, + apiVersion: 2, + vendorData: { + purpose: { + consents: { + 1: false + } + } + } + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.equal('https://ib.adnxs-simple.com/ut/v3/prebid'); + }); + + it('should populate eids when supported userIds are available', function () { + const bidRequest = Object.assign({}, bidRequests[0], { + userId: { + tdid: 'sample-userid', + uid2: { id: 'sample-uid2-value' }, + criteoId: 'sample-criteo-userid', + netId: 'sample-netId-userid', + idl_env: 'sample-idl-userid', + flocId: { + id: 'sample-flocid-value', + version: 'chrome.1.0' + } + } + }); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.eids).to.deep.include({ + source: 'adserver.org', + id: 'sample-userid', + rti_partner: 'TDID' + }); + + expect(payload.eids).to.deep.include({ + source: 'criteo.com', + id: 'sample-criteo-userid', + }); + + expect(payload.eids).to.deep.include({ + source: 'chrome.com', + id: 'sample-flocid-value' + }); + + expect(payload.eids).to.deep.include({ + source: 'netid.de', + id: 'sample-netId-userid', + }); + + expect(payload.eids).to.deep.include({ + source: 'liveramp.com', + id: 'sample-idl-userid' + }); + + expect(payload.eids).to.deep.include({ + source: 'uidapi.com', + id: 'sample-uid2-value', + rti_partner: 'UID2' + }); + }); + }); + + describe('interpretResponse', function () { + let bfStub; + before(function() { + bfStub = sinon.stub(bidderFactory, 'getIabSubCategory'); + }); + + after(function() { + bfStub.restore(); + }); + + let response = { + 'version': '3.0.0', + 'tags': [ + { + 'uuid': '3db3773286ee59', + 'tag_id': 10433394, + 'auction_id': '4534722592064951574', + 'nobid': false, + 'no_ad_url': 'https://lax1-ib.adnxs.com/no-ad', + 'timeout_ms': 10000, + 'ad_profile_id': 27079, + 'ads': [ + { + 'content_source': 'rtb', + 'ad_type': 'banner', + 'advertiserId': 4849978, + 'buyer_member_id': 11626, + 'brand_category_id': 0, + 'creative_id': 29681110, + 'media_type_id': 1, + 'media_subtype_id': 1, + 'cpm': 0.5, + 'cpm_publisher_currency': 0.5, + 'publisher_currency_code': '$', + 'client_initiated_ad_counting': true, + 'viewability': { + 'config': '' + }, + 'rtb': { + 'banner': { + 'content': '', + 'width': 1, + 'height': 1 + }, + 'trackers': [ + { + 'impression_urls': [ + 'https://lax1-ib.adnxs.com/impression' + ], + 'video_events': {} + } + ] + } + } + ] + } + ] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'adType': 'banner', + 'requestId': '3db3773286ee59', + 'auctionId': '28e94b67-b521-47b1-a284-e3cccc0a2707', + 'cpm': 0.5, + 'creativeId': 29681110, + 'brandCategoryId': 0, + 'dealId': undefined, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 300, + 'source': 'rtb', + 'mediaSubtypeId': 1, + 'mediaTypeId': 1, + 'adUnitCode': 'code', + 'buyerMemberId': 11626, + 'appnexus': { + 'buyerMemberId': 11626 + }, + 'meta': { + 'advertiserId': 4849978, + 'placementId': 10433394, + 'domParent': '.blog-post', + 'child': 4 + }, + 'width': 1, + 'height': 1, + 'banner': { + 'content': '', + 'width': 1, + 'height': 1, + 'trackers': [ + { + 'impression_urls': [ + 'https://www.example.com' + ] + } + ] + }, + 'mediaType': 'banner', + 'ad': '' + } + ]; + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code', + params: { + 'placementId': 10433394, + 'domParent': '.blog-post', + 'child': 4 + } + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let response = { + 'version': '0.0.1', + 'tags': [{ + 'uuid': '84ab500420319d', + 'tag_id': 5976557, + 'auction_id': '297492697822162468', + 'nobid': true + }] + }; + let bidderRequest; + + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + + it('should add advertiser id', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); + }); + + it('should add advertiserDomains', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].adomain = ['123']; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); + expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + }); + + it('should add params', function() { + let responseParams = deepClone(response); + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code', + params: { + 'placementId': 10433394, + 'domParent': '.blog-post', + 'child': 4 + } + }] + } + let result = spec.interpretResponse({ body: responseParams }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['placementId', 'domParent', 'child']); + }); + }); +}); From 409ec8a5553300e1b4f7cdbf401d1a02dde1ae25 Mon Sep 17 00:00:00 2001 From: Ilya Medvedev Date: Thu, 24 Jun 2021 00:09:35 +0600 Subject: [PATCH 1194/1476] Limelight Digital Bid Adapter: add new bid adapter (#7013) * Rename project limelight bidder adapter * fix ie 11 test * fix small code style mistakes * fix behavior when adUnitId is zero * fix lint * change bidder code --- modules/limelightDigitalBidAdapter.js | 144 ++++++++ modules/limelightDigitalBidAdapter.md | 60 ++++ modules/projectLimeLightBidAdapter.md | 57 --- .../limelightDigitalBidAdapter_spec.js | 327 ++++++++++++++++++ 4 files changed, 531 insertions(+), 57 deletions(-) create mode 100644 modules/limelightDigitalBidAdapter.js create mode 100644 modules/limelightDigitalBidAdapter.md delete mode 100644 modules/projectLimeLightBidAdapter.md create mode 100644 test/spec/modules/limelightDigitalBidAdapter_spec.js diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js new file mode 100644 index 00000000000..6203e22d253 --- /dev/null +++ b/modules/limelightDigitalBidAdapter.js @@ -0,0 +1,144 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import {ajax} from '../src/ajax.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'limelightDigital'; + +/** + * Determines whether or not the given bid response is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency || !bid.meta.advertiserDomains) { + return false; + } + switch (bid.meta.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastXml || bid.vastUrl); + } + return false; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['pll'], + 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: (bid) => { + return Boolean(bid.bidId && bid.params && bid.params.host && bid.params.adUnitType && + (bid.params.adUnitId || bid.params.adUnitId === 0)); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: (validBidRequests, bidderRequest) => { + let winTop; + try { + winTop = window.top; + winTop.location.toString(); + } catch (e) { + utils.logMessage(e); + winTop = window; + } + const placements = utils.groupBy(validBidRequests.map(bidRequest => buildPlacement(bidRequest)), 'host') + return Object.keys(placements) + .map(host => buildRequest(winTop, host, placements[host].map(placement => placement.adUnit))); + }, + + /** + * 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: (bid) => { + const cpm = bid.pbMg; + if (bid.nurl !== '') { + bid.nurl = bid.nurl.replace( + /\$\{AUCTION_PRICE\}/, + cpm + ); + ajax(bid.nurl, null); + } + }, + + /** + * 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: (serverResponse, bidRequest) => { + const bidResponses = []; + const serverBody = serverResponse.body; + const len = serverBody.length; + for (let i = 0; i < len; i++) { + const bidResponse = serverBody[i]; + if (isBidResponseValid(bidResponse)) { + bidResponses.push(bidResponse); + } + } + return bidResponses; + }, +}; + +registerBidder(spec); + +function buildRequest(winTop, host, adUnits) { + return { + method: 'POST', + url: `https://${host}/hb`, + data: { + secure: (location.protocol === 'https:'), + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + adUnits: adUnits + } + } +} + +function buildPlacement(bidRequest) { + let sizes; + if (bidRequest.mediaTypes) { + switch (bidRequest.params.adUnitType) { + case BANNER: + if (bidRequest.mediaTypes.banner && bidRequest.mediaTypes.banner.sizes) { + sizes = bidRequest.mediaTypes.banner.sizes; + } + break; + case VIDEO: + if (bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.playerSize) { + sizes = [bidRequest.mediaTypes.video.playerSize]; + } + break; + } + } + sizes = (sizes || []).concat(bidRequest.sizes || []).filter(utils.uniques); + return { + host: bidRequest.params.host, + adUnit: { + id: bidRequest.params.adUnitId, + bidId: bidRequest.bidId, + transactionId: bidRequest.transactionId, + sizes: sizes.map(size => { + return { + width: size[0], + height: size[1] + } + }), + type: bidRequest.params.adUnitType.toUpperCase() + } + } +} diff --git a/modules/limelightDigitalBidAdapter.md b/modules/limelightDigitalBidAdapter.md new file mode 100644 index 00000000000..ab69ef8eaa4 --- /dev/null +++ b/modules/limelightDigitalBidAdapter.md @@ -0,0 +1,60 @@ +# Overview + +``` +Module Name: Limelight Digital Exchange Bidder Adapter +Module Type: Bidder Adapter +Maintainer: engineering@project-limelight.com +``` + +# Description + +Module that connects to Limelight Digital Exchange's demand sources + +# Test Parameters for banner +``` +var adUnits = [{ + code: 'placementCode', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.ortb.net', + adUnitId: 0, + adUnitType: 'banner' + } + }] +}]; +``` + +# Test Parameters for video +``` +var videoAdUnit = [{ + code: 'video1', + sizes: [[300, 250]], + bids: [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.ortb.net', + adUnitId: 0, + adUnitType: 'video' + } + }] +}]; +``` + +# Configuration + +The Limelight Digital Exchange Bidder Adapter expects Prebid Cache(for video) to be enabled so that we can store and retrieve a single vastXml. + +``` +pbjs.setConfig({ + usePrebidCache: true, + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } +}); +``` diff --git a/modules/projectLimeLightBidAdapter.md b/modules/projectLimeLightBidAdapter.md deleted file mode 100644 index 15aa170cd2e..00000000000 --- a/modules/projectLimeLightBidAdapter.md +++ /dev/null @@ -1,57 +0,0 @@ -# Overview - -``` -Module Name: Project LimeLight SSP Adapter -Module Type: Bidder Adapter -Maintainer: engineering@project-limelight.com -``` - -# Description - -Module that connects to Project Limelight SSP demand sources - -# Test Parameters for banner -``` - var adUnits = [{ - code: 'placementCode', - sizes: [[300, 250]], - bids: [{ - bidder: 'project-limelight', - params: { - host: 'ads.project-limelight.com', - adUnitId: 0, - adUnitType: 'banner' - } - }] - } - ]; -``` - -# Test Parameters for video -``` -var videoAdUnit = [{ - code: 'video1', - sizes: [[300, 250]], - bids: [{ - bidder: 'project-limelight', - params: { - host: 'ads.project-limelight.com', - adUnitId: 0, - adUnitType: 'video' - } - }] - }]; -``` - -# Configuration - -The Project Limelight Bid Adapter expects Prebid Cache(for video) to be enabled so that we can store and retrieve a single vastXml. - -``` -pbjs.setConfig({ - usePrebidCache: true, - cache: { - url: 'https://prebid.adnxs.com/pbc/v1/cache' - } -}); -``` diff --git a/test/spec/modules/limelightDigitalBidAdapter_spec.js b/test/spec/modules/limelightDigitalBidAdapter_spec.js new file mode 100644 index 00000000000..336f199eb4f --- /dev/null +++ b/test/spec/modules/limelightDigitalBidAdapter_spec.js @@ -0,0 +1,327 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/limelightDigitalBidAdapter.js'; + +describe('limelightDigitalAdapter', function () { + const bid1 = { + bidId: '2dd581a2b6281d', + bidder: 'limelightDigital', + bidderRequestId: '145e1d6a7837c9', + params: { + host: 'exchange.ortb.net', + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + } + const bid2 = { + bidId: '58ee9870c3164a', + bidder: 'limelightDigital', + bidderRequestId: '209fdaf1c81649', + params: { + host: 'ads.project-limelight.com', + adUnitId: 456, + adUnitType: 'banner' + }, + placementCode: 'placement_1', + auctionId: '482f88de-29ab-45c8-981a-d25e39454a34', + sizes: [[350, 200]], + transactionId: '068867d1-46ec-40bb-9fa0-e24611786fb4' + } + const bid3 = { + bidId: '019645c7d69460', + bidder: 'limelightDigital', + bidderRequestId: 'f2b15f89e77ba6', + params: { + host: 'exchange.ortb.net', + adUnitId: 789, + adUnitType: 'video' + }, + placementCode: 'placement_2', + auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', + sizes: [[800, 600]], + transactionId: '738d5915-6651-43b9-9b6b-d50517350917' + } + const bid4 = { + bidId: '019645c7d69460', + bidder: 'limelightDigital', + bidderRequestId: 'f2b15f89e77ba6', + params: { + host: 'exchange.ortb.net', + adUnitId: 789, + adUnitType: 'video' + }, + placementCode: 'placement_2', + auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', + video: { + playerSize: [800, 600] + }, + transactionId: '738d5915-6651-43b9-9b6b-d50517350917' + } + + describe('buildRequests', function () { + const serverRequests = spec.buildRequests([bid1, bid2, bid3, bid4]) + it('Creates two ServerRequests', function() { + expect(serverRequests).to.exist + expect(serverRequests).to.have.lengthOf(2) + }) + serverRequests.forEach(serverRequest => { + 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 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', 'secure', 'adUnits') + expect(data.deviceWidth).to.be.a('number') + expect(data.deviceHeight).to.be.a('number') + expect(data.secure).to.be.a('boolean') + data.adUnits.forEach(adUnit => { + expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId') + expect(adUnit.id).to.be.a('number') + expect(adUnit.bidId).to.be.a('string') + expect(adUnit.type).to.be.a('string') + expect(adUnit.transactionId).to.be.a('string') + expect(adUnit.sizes).to.be.an('array') + }) + }) + }) + it('Returns valid URL', function () { + expect(serverRequests[0].url).to.equal('https://exchange.ortb.net/hb') + expect(serverRequests[1].url).to.equal('https://ads.project-limelight.com/hb') + }) + it('Returns valid adUnits', function () { + validateAdUnit(serverRequests[0].data.adUnits[0], bid1) + validateAdUnit(serverRequests[1].data.adUnits[0], bid2) + validateAdUnit(serverRequests[0].data.adUnits[1], bid3) + }) + it('Returns empty data if no valid requests are passed', function () { + const serverRequests = spec.buildRequests([]) + expect(serverRequests).to.be.an('array').that.is.empty + }) + }) + describe('interpretBannerResponse', function () { + let resObject = { + body: [ { + requestId: '123', + cpm: 0.3, + width: 320, + height: 50, + ad: '

Hello ad

', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['example.com'], + mediaType: 'banner' + } + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'meta'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.meta.advertiserDomains).to.be.an('array'); + expect(dataItem.meta.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('interpretVideoResponse', function () { + let resObject = { + body: [ { + requestId: '123', + cpm: 0.3, + width: 320, + height: 50, + vastXml: '', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['example.com'], + mediaType: 'video' + } + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'meta'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.vastXml).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.meta.advertiserDomains).to.be.an('array'); + expect(dataItem.meta.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('isBidRequestValid', function() { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'limelightDigital', + bidderRequestId: '145e1d6a7837c9', + params: { + host: 'exchange.ortb.net', + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + it('should return true when required params found', function() { + [bid, bid1, bid2, bid3].forEach(bid => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + it('should return true when adUnitId is zero', function() { + bid.params.adUnitId = 0; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + let bidFailed = { + bidder: 'limelightDigital', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + expect(spec.isBidRequestValid(bidFailed)).to.equal(false); + }); + }); + describe('interpretResponse', function() { + let resObject = { + requestId: '123', + cpm: 0.3, + width: 320, + height: 50, + ad: '

Hello ad

', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['example.com'], + mediaType: 'banner' + } + }; + it('should skip responses which do not contain required params', function() { + let bidResponses = { + body: [ { + cpm: 0.3, + ttl: 1000, + currency: 'USD', + meta: { + advertiserDomains: ['example.com'], + mediaType: 'banner' + } + }, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + }); + it('should skip responses which do not contain advertiser domains', function() { + let resObjectWithoutAdvertiserDomains = Object.assign({}, resObject); + resObjectWithoutAdvertiserDomains.meta = Object.assign({}, resObject.meta); + delete resObjectWithoutAdvertiserDomains.meta.advertiserDomains; + let bidResponses = { + body: [ resObjectWithoutAdvertiserDomains, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + }); + it('should return responses which contain empty advertiser domains', function() { + let resObjectWithEmptyAdvertiserDomains = Object.assign({}, resObject); + resObjectWithEmptyAdvertiserDomains.meta = Object.assign({}, resObject.meta); + resObjectWithEmptyAdvertiserDomains.meta.advertiserDomains = []; + let bidResponses = { + body: [ resObjectWithEmptyAdvertiserDomains, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObjectWithEmptyAdvertiserDomains, resObject]); + }); + it('should skip responses which do not contain meta media type', function() { + let resObjectWithoutMetaMediaType = Object.assign({}, resObject); + resObjectWithoutMetaMediaType.meta = Object.assign({}, resObject.meta); + delete resObjectWithoutMetaMediaType.meta.mediaType; + let bidResponses = { + body: [ resObjectWithoutMetaMediaType, resObject ] + } + expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + }); + }); +}); + +function validateAdUnit(adUnit, bid) { + expect(adUnit.id).to.equal(bid.params.adUnitId) + expect(adUnit.bidId).to.equal(bid.bidId) + expect(adUnit.type).to.equal(bid.params.adUnitType.toUpperCase()) + expect(adUnit.transactionId).to.equal(bid.transactionId) + let bidSizes = []; + if (bid.mediaTypes) { + if (bid.mediaTypes.video && bid.mediaTypes.video.playerSize) { + bidSizes = bidSizes.concat([bid.mediaTypes.video.playerSize]); + } + if (bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) { + bidSizes = bidSizes.concat(bid.mediaTypes.banner.sizes); + } + } + if (bid.sizes) { + bidSizes = bidSizes.concat(bid.sizes || []); + } + expect(adUnit.sizes).to.deep.equal(bidSizes.map(size => { + return { + width: size[0], + height: size[1] + } + })) +} From 650322dcc48dc5270c891cca7e9b215f221b8ade Mon Sep 17 00:00:00 2001 From: Etarget <40423120+etargetse@users.noreply.github.com> Date: Wed, 23 Jun 2021 20:14:01 +0200 Subject: [PATCH 1195/1476] eTarget Bid Adapter: getMetaData feature moved into bidderRequest (#6937) * new feature getMetaData * metaData unit test * advertiserDomains * advertiserDomains * added ortb2 * getMetaData feature moved into bidderRequest object * getMetaData featured moved into bidderRequest object * getMetaData feature moved into bidderRequest object --- modules/etargetBidAdapter.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 8a1b25cec70..a7e79b2fd9b 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -42,13 +42,16 @@ export const spec = { request.unshift('https://' + lastCountry + '.search.etargetnet.com/hb/?hbget=1'); netRevenue = 'net'; - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - gdprObject = { - gdpr: bidderRequest.gdprConsent.gdprApplies, - gdpr_consent: bidderRequest.gdprConsent.consentString - }; - request.push('gdpr=' + gdprObject.gdpr); - request.push('gdpr_consent=' + gdprObject.gdpr_consent); + if (bidderRequest) { + if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + gdprObject = { + gdpr: bidderRequest.gdprConsent.gdprApplies, + gdpr_consent: bidderRequest.gdprConsent.consentString + }; + request.push('gdpr=' + gdprObject.gdpr); + request.push('gdpr_consent=' + gdprObject.gdpr_consent); + } + bidderRequest.metaData = getMetaData(); } return { @@ -57,7 +60,6 @@ export const spec = { data: bidderRequest, bids: validBidRequests, netRevenue: netRevenue, - metaData: getMetaData(), bidder: 'etarget', gdpr: gdprObject }; From 8cffa84d4ad4db0f8a3ba73cedf1c577cbbbc855 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 23 Jun 2021 15:30:42 -0400 Subject: [PATCH 1196/1476] Prebid 5.2.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ddffff9b46..614b3db7cb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.2.0-pre", + "version": "5.2.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5c571fa0bf72fda344a1ef7ddf11441c19f790b3 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Wed, 23 Jun 2021 16:03:20 -0400 Subject: [PATCH 1197/1476] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 614b3db7cb4..4a93196cb98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.2.0", + "version": "5.3.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e6433030c8dcc7a6dd4b80bd320cf3df9da411e1 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 23 Jun 2021 18:56:35 -0400 Subject: [PATCH 1198/1476] temporary dependency change and update a test eslintrc rule (#7094) * temporary dependency change * adding eslint update --- gulpfile.js | 2 +- package-lock.json | 45745 +++++++++----------------------------------- package.json | 6 +- test/.eslintrc.js | 1 + 4 files changed, 8991 insertions(+), 36763 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index dfb5a51fdbf..088e3785747 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,7 +12,7 @@ var terser = require('gulp-terser'); var gulpClean = require('gulp-clean'); var KarmaServer = require('karma').Server; var karmaConfMaker = require('./karma.conf.maker'); -var opens = require('open'); +var opens = require('opn'); var webpackConfig = require('./webpack.conf'); var helpers = require('./gulpHelpers'); var concat = require('gulp-concat'); diff --git a/package-lock.json b/package-lock.json index 7908764b8e1..95a53b37173 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,33309 +1,5007 @@ { "name": "prebid.js", - "version": "5.1.0-pre", - "lockfileVersion": 2, + "version": "5.3.0-pre", + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "prebid.js", - "version": "5.1.0-pre", - "license": "Apache-2.0", - "dependencies": { - "babel-plugin-transform-object-assign": "^6.22.0", - "core-js": "^3.13.0", - "core-js-pure": "^3.13.0", - "criteo-direct-rsa-validate": "^1.1.0", - "crypto-js": "^3.3.0", - "dlv": "1.1.3", - "dset": "2.0.1", - "express": "^4.15.4", - "fun-hooks": "^0.9.9", - "just-clone": "^1.0.2", - "live-connect-js": "2.0.0" - }, - "devDependencies": { - "@babel/core": "^7.8.4", - "@babel/preset-env": "^7.8.4", - "@jsdevtools/coverage-istanbul-loader": "^3.0.3", - "@wdio/browserstack-service": "^6.1.4", - "@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.5.2", - "@wdio/sync": "^7.5.2", - "ajv": "5.5.2", - "babel-loader": "^8.0.5", - "body-parser": "^1.19.0", - "chai": "^4.2.0", - "coveralls": "^3.1.0", - "deep-equal": "^2.0.3", - "documentation": "^13.2.5", - "es5-shim": "^4.5.14", - "eslint": "^7.27.0", - "eslint-config-standard": "^10.2.1", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prebid": "file:./plugins/eslint", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-standard": "^3.0.1", - "execa": "^5.0.0", - "faker": "^5.5.3", - "fs.extra": "^1.3.2", - "gulp": "^4.0.0", - "gulp-clean": "^0.3.2", - "gulp-concat": "^2.6.0", - "gulp-connect": "^5.7.0", - "gulp-eslint": "^4.0.0", - "gulp-footer": "^2.0.2", - "gulp-header": "^2.0.9", - "gulp-if": "^3.0.0", - "gulp-js-escape": "^1.0.1", - "gulp-replace": "^1.0.0", - "gulp-shell": "^0.8.0", - "gulp-sourcemaps": "^3.0.0", - "gulp-terser": "^2.0.1", - "gulp-util": "^3.0.0", - "is-docker": "^2.2.1", - "istanbul": "^0.4.5", - "karma": "^6.3.2", - "karma-babel-preprocessor": "^8.0.1", - "karma-browserstack-launcher": "1.4.0", - "karma-chai": "^0.1.0", - "karma-chrome-launcher": "^3.1.0", - "karma-coverage": "^2.0.1", - "karma-coverage-istanbul-reporter": "^3.0.3", - "karma-es5-shim": "^0.0.4", - "karma-firefox-launcher": "^2.1.0", - "karma-ie-launcher": "^1.0.0", - "karma-mocha": "^2.0.1", - "karma-mocha-reporter": "^2.2.5", - "karma-opera-launcher": "^1.0.0", - "karma-safari-launcher": "^1.0.0", - "karma-script-launcher": "^1.0.0", - "karma-sinon": "^1.0.5", - "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "^0.0.32", - "karma-webpack": "^3.0.5", - "lodash": "^4.17.21", - "mocha": "^5.0.0", - "morgan": "^1.10.0", - "opn": "^6.0.0", - "resolve-from": "^5.0.0", - "sinon": "^4.1.3", - "through2": "^4.0.2", - "url-parse": "^1.0.5", - "webdriverio": "^7.6.1", - "webpack": "^3.0.0", - "webpack-bundle-analyzer": "^3.8.0", - "webpack-stream": "^3.2.0", - "yargs": "^17.0.1" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "requires": { + "@babel/highlight": "7.14.5" } }, - "node_modules/@babel/compat-data": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.5.tgz", - "integrity": "sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw==", - "dev": true, - "dependencies": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - } + "@babel/compat-data": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", + "dev": true }, - "node_modules/@babel/core": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", - "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.1", - "@babel/parser": "^7.12.3", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/core": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz", + "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.14.5", + "@babel/generator": "7.14.5", + "@babel/helper-compilation-targets": "7.14.5", + "@babel/helper-module-transforms": "7.14.5", + "@babel/helpers": "7.14.6", + "@babel/parser": "7.14.7", + "@babel/template": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5", + "convert-source-map": "1.8.0", + "debug": "4.3.1", + "gensync": "1.0.0-beta.2", + "json5": "2.2.0", + "semver": "6.3.0", + "source-map": "0.5.7" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "dependencies": { - "ms": "^2.1.1" + "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" + } + }, + "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/@babel/core/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/@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "@babel/generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", "dev": true, - "dependencies": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "requires": { + "@babel/types": "7.14.5", + "jsesc": "2.5.2", + "source-map": "0.5.7" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", "dev": true, - "dependencies": { - "@babel/types": "^7.10.4" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz", + "integrity": "sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w==", "dev": true, - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "requires": { + "@babel/helper-explode-assignable-expression": "7.14.5", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", "dev": true, - "dependencies": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" + "requires": { + "@babel/compat-data": "7.14.7", + "@babel/helper-validator-option": "7.14.5", + "browserslist": "4.16.6", + "semver": "6.3.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "@babel/helper-create-class-features-plugin": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz", + "integrity": "sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg==", "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" + "requires": { + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/helper-function-name": "7.14.5", + "@babel/helper-member-expression-to-functions": "7.14.7", + "@babel/helper-optimise-call-expression": "7.14.5", + "@babel/helper-replace-supers": "7.14.5", + "@babel/helper-split-export-declaration": "7.14.5" } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "@babel/helper-create-regexp-features-plugin": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz", + "integrity": "sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" + "requires": { + "@babel/helper-annotate-as-pure": "7.14.5", + "regexpu-core": "4.7.1" } }, - "node_modules/@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "@babel/helper-define-polyfill-provider": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz", + "integrity": "sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==", "dev": true, + "requires": { + "@babel/helper-compilation-targets": "7.14.5", + "@babel/helper-module-imports": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/traverse": "7.14.7", + "debug": "4.3.1", + "lodash.debounce": "4.0.8", + "resolve": "1.20.0", + "semver": "6.3.0" + }, "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "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" + } + }, + "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/@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "@babel/helper-explode-assignable-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz", + "integrity": "sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ==", "dev": true, - "dependencies": { - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-function-name": { + "@babel/helper-function-name": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-get-function-arity": "7.14.5", + "@babel/template": "7.14.5", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-get-function-arity": { + "@babel/helper-get-function-arity": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-hoist-variables": { + "@babel/helper-hoist-variables": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", - "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", + "@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-module-imports": { + "@babel/helper-module-imports": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-module-transforms": { + "@babel/helper-module-transforms": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-module-imports": "7.14.5", + "@babel/helper-replace-supers": "7.14.5", + "@babel/helper-simple-access": "7.14.5", + "@babel/helper-split-export-declaration": "7.14.5", + "@babel/helper-validator-identifier": "7.14.5", + "@babel/template": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-optimise-call-expression": { + "@babel/helper-optimise-call-expression": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", "dev": true }, - "node_modules/@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.19" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "@babel/helper-remap-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz", + "integrity": "sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "requires": { + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/helper-wrap-function": "7.14.5", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-replace-supers": { + "@babel/helper-replace-supers": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-member-expression-to-functions": "7.14.7", + "@babel/helper-optimise-call-expression": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-simple-access": { + "@babel/helper-simple-access": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz", + "integrity": "sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ==", + "dev": true, + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-split-export-declaration": { + "@babel/helper-split-export-declaration": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helper-validator-identifier": { + "@babel/helper-validator-identifier": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } + "dev": true }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz", + "integrity": "sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ==", "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "requires": { + "@babel/helper-function-name": "7.14.5", + "@babel/template": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/helpers": { + "@babel/helpers": { "version": "7.14.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", "dev": true, - "dependencies": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/template": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5" } }, - "node_modules/@babel/highlight": { + "@babel/highlight": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-validator-identifier": "7.14.5", + "chalk": "2.4.2", + "js-tokens": "4.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } + "@babel/parser": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz", + "integrity": "sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA==", + "dev": true }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz", + "integrity": "sha512-ZoJS2XCKPBfTmL122iP6NM9dOg+d4lc9fFk3zxc8iDjvt8Pk4+TlsHSKhIPf6X+L5ORCdBzqMZDjL/WHj7WknQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "7.14.5", + "@babel/plugin-proposal-optional-chaining": "7.14.5" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-remap-async-to-generator": "7.14.5", + "@babel/plugin-syntax-async-generators": "7.8.4" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "@babel/plugin-proposal-class-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz", + "integrity": "sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" + "requires": { + "@babel/helper-create-class-features-plugin": "7.14.6", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "@babel/plugin-proposal-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.5.tgz", + "integrity": "sha512-KBAH5ksEnYHCegqseI5N9skTdxgJdmDoAOc0uXa+4QMYKeZD0w5IARh4FMlTNtaHhbB8v+KzMdTgxMMzsIy6Yg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" + "requires": { + "@babel/helper-create-class-features-plugin": "7.14.6", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-class-static-block": "7.14.5" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "@babel/plugin-proposal-dynamic-import": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz", + "integrity": "sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-dynamic-import": "7.8.3" } }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz", + "integrity": "sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-export-namespace-from": "7.8.3" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", - "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", + "@babel/plugin-proposal-json-strings": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz", + "integrity": "sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-json-strings": "7.8.3" } }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz", + "integrity": "sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-logical-assignment-operators": "7.10.4" } }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", - "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz", + "integrity": "sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "7.8.3" } }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "@babel/plugin-proposal-numeric-separator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz", + "integrity": "sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg==", "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-numeric-separator": "7.10.4" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz", + "integrity": "sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==", "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - }, - "engines": { - "node": ">=4" + "requires": { + "@babel/compat-data": "7.14.7", + "@babel/helper-compilation-targets": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@babel/plugin-transform-parameters": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz", + "integrity": "sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-optional-catch-binding": "7.8.3" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "@babel/plugin-proposal-optional-chaining": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz", + "integrity": "sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "7.14.5", + "@babel/plugin-syntax-optional-chaining": "7.8.3" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "@babel/plugin-proposal-private-methods": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz", + "integrity": "sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-create-class-features-plugin": "7.14.6", + "@babel/helper-plugin-utils": "7.14.5" } }, - "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", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-62EyfyA3WA0mZiF2e2IV9mc9Ghwxcg8YTu8BS4Wss4Y3PY725OmS9M0qLORbJwLqFtGh+jiE4wAmocK2CTUK2Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/helper-create-class-features-plugin": "7.14.6", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-syntax-private-property-in-object": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz", + "integrity": "sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-create-regexp-features-plugin": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { + "@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "@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", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { + "@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { + "@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", - "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "@babel/plugin-transform-arrow-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", + "integrity": "sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-module-imports": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-remap-async-to-generator": "7.14.5" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz", + "integrity": "sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "@babel/plugin-transform-block-scoping": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz", + "integrity": "sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw==", "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "@babel/plugin-transform-classes": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz", + "integrity": "sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/helper-function-name": "7.14.5", + "@babel/helper-optimise-call-expression": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-replace-supers": "7.14.5", + "@babel/helper-split-export-declaration": "7.14.5", + "globals": "11.12.0" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "@babel/plugin-transform-computed-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz", + "integrity": "sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "@babel/plugin-transform-destructuring": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "@babel/plugin-transform-dotall-regex": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz", + "integrity": "sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "requires": { + "@babel/helper-create-regexp-features-plugin": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "@babel/plugin-transform-duplicate-keys": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz", + "integrity": "sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A==", "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz", + "integrity": "sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "@babel/plugin-transform-for-of": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz", + "integrity": "sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA==", "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "@babel/plugin-transform-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz", + "integrity": "sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-function-name": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "@babel/plugin-transform-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz", + "integrity": "sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "@babel/plugin-transform-member-expression-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz", + "integrity": "sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q==", "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "@babel/plugin-transform-modules-amd": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz", + "integrity": "sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-module-transforms": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "babel-plugin-dynamic-import-node": "2.3.3" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "@babel/plugin-transform-modules-commonjs": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz", + "integrity": "sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A==", "dev": true, - "dependencies": { - "regenerator-transform": "^0.14.2" + "requires": { + "@babel/helper-module-transforms": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-simple-access": "7.14.5", + "babel-plugin-dynamic-import-node": "2.3.3" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "@babel/plugin-transform-modules-systemjs": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz", + "integrity": "sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-hoist-variables": "7.14.5", + "@babel/helper-module-transforms": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-validator-identifier": "7.14.5", + "babel-plugin-dynamic-import-node": "2.3.3" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "@babel/plugin-transform-modules-umd": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz", + "integrity": "sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-module-transforms": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", - "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz", + "integrity": "sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-create-regexp-features-plugin": "7.14.5" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "@babel/plugin-transform-new-target": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz", + "integrity": "sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "@babel/plugin-transform-object-super": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz", + "integrity": "sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-replace-supers": "7.14.5" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "@babel/plugin-transform-parameters": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz", + "integrity": "sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "@babel/plugin-transform-property-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz", + "integrity": "sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "@babel/plugin-transform-regenerator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz", + "integrity": "sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==", "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "requires": { + "regenerator-transform": "0.14.5" } }, - "node_modules/@babel/preset-env": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", - "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.10.4", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.10.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.10.4", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.10.4", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.10.4", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "@babel/plugin-transform-reserved-words": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz", + "integrity": "sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/runtime": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", - "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", + "@babel/plugin-transform-shorthand-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz", + "integrity": "sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==", "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/runtime-corejs3": { + "@babel/plugin-transform-spread": { "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz", - "integrity": "sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz", + "integrity": "sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag==", "dev": true, - "dependencies": { - "core-js-pure": "^3.14.0", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "7.14.5" } }, - "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "node_modules/@babel/template": { + "@babel/plugin-transform-sticky-regex": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz", + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/template/node_modules/@babel/code-frame": { + "@babel/plugin-transform-template-literals": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz", + "integrity": "sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==", "dev": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "@babel/plugin-transform-typeof-symbol": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz", + "integrity": "sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/traverse": { + "@babel/plugin-transform-unicode-escapes": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", - "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz", + "integrity": "sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.5", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "@babel/plugin-transform-unicode-regex": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz", + "integrity": "sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==", "dev": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" + "requires": { + "@babel/helper-create-regexp-features-plugin": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5" } }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", - "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" + "@babel/preset-env": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.7.tgz", + "integrity": "sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==", + "dev": true, + "requires": { + "@babel/compat-data": "7.14.7", + "@babel/helper-compilation-targets": "7.14.5", + "@babel/helper-plugin-utils": "7.14.5", + "@babel/helper-validator-option": "7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "7.14.5", + "@babel/plugin-proposal-async-generator-functions": "7.14.7", + "@babel/plugin-proposal-class-properties": "7.14.5", + "@babel/plugin-proposal-class-static-block": "7.14.5", + "@babel/plugin-proposal-dynamic-import": "7.14.5", + "@babel/plugin-proposal-export-namespace-from": "7.14.5", + "@babel/plugin-proposal-json-strings": "7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.14.5", + "@babel/plugin-proposal-numeric-separator": "7.14.5", + "@babel/plugin-proposal-object-rest-spread": "7.14.7", + "@babel/plugin-proposal-optional-catch-binding": "7.14.5", + "@babel/plugin-proposal-optional-chaining": "7.14.5", + "@babel/plugin-proposal-private-methods": "7.14.5", + "@babel/plugin-proposal-private-property-in-object": "7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "7.14.5", + "@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-json-strings": "7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "7.8.3", + "@babel/plugin-syntax-numeric-separator": "7.10.4", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "7.8.3", + "@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.14.5", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/plugin-transform-block-scoped-functions": "7.14.5", + "@babel/plugin-transform-block-scoping": "7.14.5", + "@babel/plugin-transform-classes": "7.14.5", + "@babel/plugin-transform-computed-properties": "7.14.5", + "@babel/plugin-transform-destructuring": "7.14.7", + "@babel/plugin-transform-dotall-regex": "7.14.5", + "@babel/plugin-transform-duplicate-keys": "7.14.5", + "@babel/plugin-transform-exponentiation-operator": "7.14.5", + "@babel/plugin-transform-for-of": "7.14.5", + "@babel/plugin-transform-function-name": "7.14.5", + "@babel/plugin-transform-literals": "7.14.5", + "@babel/plugin-transform-member-expression-literals": "7.14.5", + "@babel/plugin-transform-modules-amd": "7.14.5", + "@babel/plugin-transform-modules-commonjs": "7.14.5", + "@babel/plugin-transform-modules-systemjs": "7.14.5", + "@babel/plugin-transform-modules-umd": "7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "7.14.7", + "@babel/plugin-transform-new-target": "7.14.5", + "@babel/plugin-transform-object-super": "7.14.5", + "@babel/plugin-transform-parameters": "7.14.5", + "@babel/plugin-transform-property-literals": "7.14.5", + "@babel/plugin-transform-regenerator": "7.14.5", + "@babel/plugin-transform-reserved-words": "7.14.5", + "@babel/plugin-transform-shorthand-properties": "7.14.5", + "@babel/plugin-transform-spread": "7.14.6", + "@babel/plugin-transform-sticky-regex": "7.14.5", + "@babel/plugin-transform-template-literals": "7.14.5", + "@babel/plugin-transform-typeof-symbol": "7.14.5", + "@babel/plugin-transform-unicode-escapes": "7.14.5", + "@babel/plugin-transform-unicode-regex": "7.14.5", + "@babel/preset-modules": "0.1.4", + "@babel/types": "7.14.5", + "babel-plugin-polyfill-corejs2": "0.2.2", + "babel-plugin-polyfill-corejs3": "0.2.3", + "babel-plugin-polyfill-regenerator": "0.2.2", + "core-js-compat": "3.15.1", + "semver": "6.3.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" + "requires": { + "@babel/helper-plugin-utils": "7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "7.14.5", + "@babel/plugin-transform-dotall-regex": "7.14.5", + "@babel/types": "7.14.5", + "esutils": "2.0.3" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "@babel/runtime": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", + "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", "dev": true, + "requires": { + "regenerator-runtime": "0.13.7" + }, "dependencies": { - "ms": "^2.1.1" + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } } }, - "node_modules/@babel/traverse/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/@babel/types": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", - "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "@babel/runtime-corejs3": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", + "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "to-fast-properties": "^2.0.0" + "requires": { + "core-js-pure": "3.15.1", + "regenerator-runtime": "0.13.7" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", "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" + "requires": { + "@babel/code-frame": "7.14.5", + "@babel/parser": "7.14.7", + "@babel/types": "7.14.5" } }, - "node_modules/@eslint/eslintrc/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" + "@babel/traverse": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.7.tgz", + "integrity": "sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ==", + "dev": true, + "requires": { + "@babel/code-frame": "7.14.5", + "@babel/generator": "7.14.5", + "@babel/helper-function-name": "7.14.5", + "@babel/helper-hoist-variables": "7.14.5", + "@babel/helper-split-export-declaration": "7.14.5", + "@babel/parser": "7.14.7", + "@babel/types": "7.14.5", + "debug": "4.3.1", + "globals": "11.12.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true + "dependencies": { + "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" + } + }, + "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/@eslint/eslintrc/node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "@babel/helper-validator-identifier": "7.14.5", + "to-fast-properties": "2.0.0" } }, - "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "ajv": "6.12.6", + "debug": "4.3.1", + "espree": "7.3.1", + "globals": "13.9.0", + "ignore": "4.0.6", + "import-fresh": "3.3.0", + "js-yaml": "3.14.1", + "minimatch": "3.0.4", + "strip-json-comments": "3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "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.3", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.4.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "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" + } + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "requires": { + "type-fest": "0.20.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" + } + }, + "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 + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, - "node_modules/@gulp-sourcemaps/identity-map": { + "@gulp-sourcemaps/identity-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", "dev": true, - "dependencies": { - "acorn": "^6.4.1", - "normalize-path": "^3.0.0", - "postcss": "^7.0.16", - "source-map": "^0.6.0", - "through2": "^3.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/@gulp-sourcemaps/identity-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/@gulp-sourcemaps/identity-map/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" + "requires": { + "acorn": "6.4.2", + "normalize-path": "3.0.0", + "postcss": "7.0.36", + "source-map": "0.6.1", + "through2": "3.0.2" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "source-map": "0.6.1", + "supports-color": "6.1.0" + } + }, + "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 + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "2.0.4", + "readable-stream": "3.6.0" + } + } } }, - "node_modules/@gulp-sourcemaps/map-sources": { + "@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=", "dev": true, - "dependencies": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" + "requires": { + "normalize-path": "2.1.1", + "through2": "2.0.5" }, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "2.3.7", + "xtend": "4.0.2" + } + } } }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "@jest/types": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", + "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", - "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/types/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/@jest/types/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/@jest/types/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" + "requires": { + "@types/istanbul-lib-coverage": "2.0.3", + "@types/istanbul-reports": "3.0.1", + "@types/node": "15.12.4", + "@types/yargs": "16.0.3", + "chalk": "4.1.1" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/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/@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", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@jsdevtools/coverage-istanbul-loader": { + "@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" + "requires": { + "convert-source-map": "1.8.0", + "istanbul-lib-instrument": "4.0.3", + "loader-utils": "2.0.0", + "merge-source-map": "1.1.0", + "schema-utils": "2.7.1" } }, - "node_modules/@sindresorhus/is": { + "@sindresorhus/is": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } + "dev": true }, - "node_modules/@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, - "dependencies": { + "requires": { "type-detect": "4.0.8" } }, - "node_modules/@sinonjs/formatio": { + "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, - "dependencies": { + "requires": { "samsam": "1.3.0" } }, - "node_modules/@sinonjs/samsam": { + "@sinonjs/samsam": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "requires": { + "@sinonjs/commons": "1.8.3", + "array-from": "2.1.1", + "lodash": "4.17.21" } }, - "node_modules/@sinonjs/text-encoding": { + "@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==", "dev": true }, - "node_modules/@szmarczak/http-timer": { + "@szmarczak/http-timer": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" + "requires": { + "defer-to-connect": "2.0.1" } }, - "node_modules/@types/aria-query": { + "@types/aria-query": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", "dev": true }, - "node_modules/@types/cacheable-request": { + "@types/cacheable-request": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", "dev": true, - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" + "requires": { + "@types/http-cache-semantics": "4.0.0", + "@types/keyv": "3.1.1", + "@types/node": "15.12.4", + "@types/responselike": "1.0.0" } }, - "node_modules/@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "node_modules/@types/component-emitter": { + "@types/component-emitter": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", "dev": true }, - "node_modules/@types/cookie": { + "@types/cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", "dev": true }, - "node_modules/@types/cors": { + "@types/cors": { "version": "2.8.10", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", "dev": true }, - "node_modules/@types/easy-table": { + "@types/easy-table": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.32.tgz", "integrity": "sha512-zKh0f/ixYFnr3Ldf5ZJTi1ZpnRqAynTTtVyGvWDf/TT12asE8ac98t3/WGWfFdRPp/qsccxg82C/Kl3NPNhqEw==", "dev": true }, - "node_modules/@types/ejs": { + "@types/ejs": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.6.tgz", "integrity": "sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ==", "dev": true }, - "node_modules/@types/fibers": { + "@types/estree": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", + "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", + "dev": true, + "optional": true + }, + "@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "@types/fibers": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.0.tgz", "integrity": "sha512-1o3I9xtk2PZFxwaLCC6gTaBfBZ5rvw/DSZZPK89fwuwO6LNrzSbC6rEs1xI0bQ3fCRWmO+uNJQQeD2J56oTMDg==", "dev": true }, - "node_modules/@types/fs-extra": { + "@types/fs-extra": { "version": "9.0.11", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/http-cache-semantics": { + "@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", "dev": true }, - "node_modules/@types/inquirer": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz", - "integrity": "sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==", + "@types/inquirer": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.2.tgz", + "integrity": "sha512-EkeX/hU0SWinA2c7Qu/+6+7KbepFPYJcjankUgtA/VSY6BlVHybL0Cgyey9PDbXwhNXnNGBLU3t+MORp23RgAw==", "dev": true, - "dependencies": { - "@types/through": "*", - "rxjs": "^6.4.0" + "requires": { + "@types/through": "0.0.30", + "rxjs": "6.6.7" } }, - "node_modules/@types/istanbul-lib-coverage": { + "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, - "node_modules/@types/istanbul-lib-report": { + "@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" + "requires": { + "@types/istanbul-lib-coverage": "2.0.3" } }, - "node_modules/@types/istanbul-reports": { + "@types/istanbul-reports": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" + "requires": { + "@types/istanbul-lib-report": "3.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", - "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, - "node_modules/@types/json5": { + "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, - "node_modules/@types/keyv": { + "@types/keyv": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/lodash": { + "@types/lodash": { "version": "4.14.170", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", "dev": true }, - "node_modules/@types/lodash.flattendeep": { + "@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==", "dev": true, - "dependencies": { - "@types/lodash": "*" + "requires": { + "@types/lodash": "4.14.170" } }, - "node_modules/@types/lodash.pickby": { + "@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==", "dev": true, - "dependencies": { - "@types/lodash": "*" + "requires": { + "@types/lodash": "4.14.170" } }, - "node_modules/@types/lodash.union": { + "@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==", "dev": true, - "dependencies": { - "@types/lodash": "*" + "requires": { + "@types/lodash": "4.14.170" } }, - "node_modules/@types/mdast": { + "@types/mdast": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", "dev": true, - "dependencies": { - "@types/unist": "*" + "requires": { + "@types/unist": "2.0.3" } }, - "node_modules/@types/minimist": { + "@types/minimist": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, - "node_modules/@types/mocha": { + "@types/mocha": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", "dev": true }, - "node_modules/@types/node": { - "version": "14.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", - "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==", + "@types/node": { + "version": "15.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", + "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==", "dev": true }, - "node_modules/@types/normalize-package-data": { + "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, - "node_modules/@types/puppeteer": { + "@types/puppeteer": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/recursive-readdir": { + "@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==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/responselike": { + "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/stack-utils": { + "@types/stack-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, - "node_modules/@types/stream-buffers": { + "@types/stream-buffers": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/through": { + "@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", "dev": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@types/unist": { + "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", "dev": true }, - "node_modules/@types/which": { + "@types/vinyl": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.4.tgz", + "integrity": "sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ==", + "dev": true, + "requires": { + "@types/expect": "1.20.4", + "@types/node": "15.12.4" + } + }, + "@types/which": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.2.tgz", "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", "dev": true }, - "node_modules/@types/yargs": { + "@types/yargs": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==", "dev": true, - "dependencies": { - "@types/yargs-parser": "*" + "requires": { + "@types/yargs-parser": "20.2.0" } }, - "node_modules/@types/yargs-parser": { + "@types/yargs-parser": { "version": "20.2.0", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "dev": true }, - "node_modules/@types/yauzl": { + "@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", "dev": true, "optional": true, - "dependencies": { - "@types/node": "*" + "requires": { + "@types/node": "15.12.4" } }, - "node_modules/@ungap/promise-all-settled": { + "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "node_modules/@vue/compiler-core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.1.tgz", - "integrity": "sha512-Z1RO3T6AEtAUFf2EqqovFm3ohAeTvFzRtB0qUENW2nEerJfdlk13/LS1a0EgsqlzxmYfR/S/S/gW9PLbFZZxkA==", + "@vue/compiler-core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.2.tgz", + "integrity": "sha512-nHmq7vLjq/XM2IMbZUcKWoH5sPXa2uR/nIKZtjbK5F3TcbnYE/zKsrSUR9WZJ03unlwotNBX1OyxVt9HbWD7/Q==", "dev": true, - "optional": true, + "requires": { + "@babel/parser": "7.14.7", + "@babel/types": "7.14.5", + "@vue/shared": "3.1.2", + "estree-walker": "2.0.2", + "source-map": "0.6.1" + }, "dependencies": { - "@babel/parser": "^7.12.0", - "@babel/types": "^7.12.0", - "@vue/shared": "3.1.1", - "estree-walker": "^2.0.1", - "source-map": "^0.6.1" + "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 + } } }, - "node_modules/@vue/compiler-core/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==", + "@vue/compiler-dom": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.2.tgz", + "integrity": "sha512-k2+SWcWH0jL6WQAX7Or2ONqu5MbtTgTO0dJrvebQYzgqaKMXNI90RNeWeCxS4BnNFMDONpHBeFgbwbnDWIkmRg==", "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" + "requires": { + "@vue/compiler-core": "3.1.2", + "@vue/shared": "3.1.2" } }, - "node_modules/@vue/compiler-dom": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz", - "integrity": "sha512-nobRIo0t5ibzg+q8nC31m+aJhbq8FbWUoKvk6h3Vs1EqTDJaj6lBTcVTq5or8AYht7FbSpdAJ81isbJ1rWNX7A==", + "@vue/compiler-sfc": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.2.tgz", + "integrity": "sha512-SeG/2+DvwejQ7oAiSx8BrDh5qOdqCYHGClPiTvVIHTfSIHiS2JjMbCANdDCjHkTOh/O7WZzo2JhdKm98bRBxTw==", "dev": true, "optional": true, + "requires": { + "@babel/parser": "7.14.7", + "@babel/types": "7.14.5", + "@types/estree": "0.0.48", + "@vue/compiler-core": "3.1.2", + "@vue/compiler-dom": "3.1.2", + "@vue/compiler-ssr": "3.1.2", + "@vue/shared": "3.1.2", + "consolidate": "0.16.0", + "estree-walker": "2.0.2", + "hash-sum": "2.0.0", + "lru-cache": "5.1.1", + "magic-string": "0.25.7", + "merge-source-map": "1.1.0", + "postcss": "8.3.5", + "postcss-modules": "4.1.3", + "postcss-selector-parser": "6.0.6", + "source-map": "0.6.1" + }, "dependencies": { - "@vue/compiler-core": "3.1.1", - "@vue/shared": "3.1.1" + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "optional": true, + "requires": { + "yallist": "3.1.1" + } + }, + "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, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "optional": true + } } }, - "node_modules/@vue/compiler-sfc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz", - "integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==", + "@vue/compiler-ssr": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.2.tgz", + "integrity": "sha512-BwXo9LFk5OSWdMyZQ4bX1ELHX0Z/9F+ld/OaVnpUPzAZCHslBYLvyKUVDwv2C/lpLjRffpC2DOUEdl1+RP1aGg==", "dev": true, "optional": true, - "dependencies": { - "@babel/parser": "^7.13.9", - "@babel/types": "^7.13.0", - "@vue/compiler-core": "3.1.1", - "@vue/compiler-dom": "3.1.1", - "@vue/compiler-ssr": "3.1.1", - "@vue/shared": "3.1.1", - "consolidate": "^0.16.0", - "estree-walker": "^2.0.1", - "hash-sum": "^2.0.0", - "lru-cache": "^5.1.1", - "magic-string": "^0.25.7", - "merge-source-map": "^1.1.0", - "postcss": "^8.1.10", - "postcss-modules": "^4.0.0", - "postcss-selector-parser": "^6.0.4", - "source-map": "^0.6.1" - }, - "peerDependencies": { - "vue": "3.1.1" + "requires": { + "@vue/compiler-dom": "3.1.2", + "@vue/shared": "3.1.2" } }, - "node_modules/@vue/compiler-sfc/node_modules/@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", - "dev": true, - "optional": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } + "@vue/shared": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.2.tgz", + "integrity": "sha512-EmH/poaDWBPJaPILXNI/1fvUbArJQmmTyVCwvvyDYDFnkPoTclAbHRAtyIvqfez7jybTDn077HTNILpxlsoWhg==", + "dev": true }, - "node_modules/@vue/compiler-sfc/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "optional": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@vue/compiler-sfc/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, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz", - "integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==", - "dev": true, - "optional": true, - "dependencies": { - "@vue/compiler-dom": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.1.tgz", - "integrity": "sha512-DsH5woNVCcPK1M0RRYVgJEU1GJDU2ASOKpAqW3ppHk+XjoFLCbqc/26RTCgTpJYd9z8VN+79Q1u7/QqgQPbuLQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@vue/shared": "3.1.1" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.1.tgz", - "integrity": "sha512-GboqR02txOtkd9F3Ysd8ltPL68vTCqIx2p/J52/gFtpgb5FG9hvOAPEwFUqxeEJRu7ResvQnmdOHiEycGPCLhQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@vue/reactivity": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.1.tgz", - "integrity": "sha512-o57n/199e/BBAmLRMSXmD2r12Old/h/gf6BgL0RON1NT2pwm6MWaMY4Ul55eyq+FsDILz4jR/UgoPQ9vYB8xcw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@vue/runtime-core": "3.1.1", - "@vue/shared": "3.1.1", - "csstype": "^2.6.8" - } - }, - "node_modules/@vue/shared": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz", - "integrity": "sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==", - "dev": true, - "optional": true - }, - "node_modules/@wdio/browserstack-service": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.1.15.tgz", - "integrity": "sha512-q8qLa44wGSB3tIuZ0yquvAZqr2W7vEwupWiOd1ct0CSYgd4yX/nLd8oypqJCc8jU1ZwNAhu+V3/6hszvwx+HbA==", + "@wdio/browserstack-service": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.12.1.tgz", + "integrity": "sha512-B4zYlaE8q1Jxb6ctcuUPlKL3inwloETwks+cB9fFtVMDf/HH2Cau3Pi0CoIs8435EI+J4/1LxLHQV2uhzbBSlQ==", "dev": true, - "dependencies": { - "@wdio/logger": "6.0.16", - "browserstack-local": "^1.4.5", - "got": "^11.0.2" - }, - "engines": { - "node": ">=10.0.0" + "requires": { + "@wdio/logger": "6.10.10", + "browserstack-local": "1.4.8", + "got": "11.8.2" } }, - "node_modules/@wdio/cli": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.3.tgz", - "integrity": "sha512-n7XvIoruXlGQGt2dl4dLm/J6he2Int7BOe3gnTxRTddjcqXZ8bv7qvYvNvfXzEg/vVzmUyMW2dQfzpNVoyx/dQ==", - "dev": true, - "dependencies": { - "@types/ejs": "^3.0.5", - "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^7.3.1", - "@types/lodash.flattendeep": "^4.4.6", - "@types/lodash.pickby": "^4.6.6", - "@types/lodash.union": "^4.6.6", - "@types/recursive-readdir": "^2.2.0", + "@wdio/cli": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.4.tgz", + "integrity": "sha512-npgpaIpPoSpyef1Pv0ZKyB5FJAZnDJiVphgda0RysXd6J0S3mlwzXrcIoapl28mmeWI6NIvvv65u0sominhsyQ==", + "dev": true, + "requires": { + "@types/ejs": "3.0.6", + "@types/fs-extra": "9.0.11", + "@types/inquirer": "7.3.2", + "@types/lodash.flattendeep": "4.4.6", + "@types/lodash.pickby": "4.6.6", + "@types/lodash.union": "4.6.6", + "@types/recursive-readdir": "2.2.0", "@wdio/config": "7.7.3", "@wdio/logger": "7.7.0", "@wdio/types": "7.7.3", "@wdio/utils": "7.7.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.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.7.3", - "yargs": "^17.0.0", - "yarn-install": "^1.0.0" - }, - "bin": { - "wdio": "bin/wdio.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/cli/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/cli/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "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", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" + "async-exit-hook": "2.0.1", + "chalk": "4.1.1", + "chokidar": "3.5.2", + "cli-spinners": "2.6.0", + "ejs": "3.1.6", + "fs-extra": "10.0.0", + "inquirer": "8.1.1", + "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.7.4", + "yargs": "17.0.1", + "yarn-install": "1.0.0" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@wdio/cli/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/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/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + }, + "yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "requires": { + "cliui": "7.0.4", + "escalade": "3.1.1", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "string-width": "4.2.2", + "y18n": "5.0.8", + "yargs-parser": "20.2.9" + } + } } }, - "node_modules/@wdio/concise-reporter": { + "@wdio/concise-reporter": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.7.3.tgz", "integrity": "sha512-2Ix20n48N+lvvU4NzqMP7z+daG748RRsmDqdstCoBrJgXV6frvu38HVHV90U5uKt5Vmp6/QQl05A4OliaNoO9w==", "dev": true, - "dependencies": { + "requires": { "@wdio/reporter": "7.7.3", "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@wdio/cli": "^7.0.0" - } - }, - "node_modules/@wdio/concise-reporter/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/concise-reporter/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@wdio/concise-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" + "chalk": "4.1.1", + "pretty-ms": "7.0.1" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@wdio/concise-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/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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/config": { + "@wdio/config": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.7.3.tgz", "integrity": "sha512-I8gkb5BjXLe6/9NK7OCA9Mc+A6xeGUqbYTRd4PNKdObE6HomKOxw4plVZCYF0DlD2FCo4OGrvYGmalojFsCMdA==", "dev": true, - "dependencies": { + "requires": { "@wdio/logger": "7.7.0", "@wdio/types": "7.7.3", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/config/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "deepmerge": "4.2.2", + "glob": "7.1.7" }, - "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" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/local-runner": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.3.tgz", - "integrity": "sha512-TM1Xd8ioc4TpZwmRStDdk7m6IVOPAEsoyKoqffuRN2pZmrj4jswmvj6qys06ErrVCGWA4skyTYZjhMZf0+V0Zg==", + "@wdio/local-runner": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.4.tgz", + "integrity": "sha512-ubBr9+pDZuOg6i/EJdW8E71dXrE1A63+wsZH6lpdm1fFWqfRvjl+DTCdE2rtLhr44vNSmiHxIIQnCjvZXwjiFg==", "dev": true, - "dependencies": { - "@types/stream-buffers": "^3.0.3", + "requires": { + "@types/stream-buffers": "3.0.3", "@wdio/logger": "7.7.0", "@wdio/repl": "7.7.3", - "@wdio/runner": "7.7.3", + "@wdio/runner": "7.7.4", "@wdio/types": "7.7.3", - "async-exit-hook": "^2.0.1", - "split2": "^3.2.2", - "stream-buffers": "^3.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@wdio/cli": "^7.0.0" - } - }, - "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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": "6.0.16", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.0.16.tgz", - "integrity": "sha512-VbH5UnQIG/3sSMV+Y38+rOdwyK9mVA9vuL7iOngoTafHwUjL1MObfN/Cex84L4mGxIgfxCu6GV48iUmSuQ7sqA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "async-exit-hook": "2.0.1", + "split2": "3.2.2", + "stream-buffers": "3.0.2" }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@wdio/logger/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/logger/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/logger/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==", + "@wdio/logger": { + "version": "6.10.10", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.10.10.tgz", + "integrity": "sha512-2nh0hJz9HeZE0VIEMI+oPgjr/Q37ohrR9iqsl7f7GW5ik+PnKYCT9Eab5mR1GNMG60askwbskgGC1S9ygtvrSw==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@wdio/logger/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/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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/mocha-framework": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.3.tgz", - "integrity": "sha512-0G9q3z6kuqFJxavm/pZNvO0bhRrZQuPbWf38vQGrbHEP15i8LNI1dDg1R73vb0y1jIbZDSIiuQsQQ6keGWND+w==", + "@wdio/mocha-framework": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.4.tgz", + "integrity": "sha512-zLhMJBAp4HOP0qGffCNSA1UBdRystn9o5y7EEQXU6Gu+ktrSOV/RU+pvd+kqHo6RfOIcwShljZVStf3zh8cY6Q==", "dev": true, - "dependencies": { - "@types/mocha": "^8.0.0", + "requires": { + "@types/mocha": "8.2.2", "@wdio/logger": "7.7.0", "@wdio/types": "7.7.3", "@wdio/utils": "7.7.3", - "expect-webdriverio": "^3.0.0", - "mocha": "^8.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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" + "expect-webdriverio": "3.1.0", + "mocha": "9.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@wdio/mocha-framework/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/mocha-framework/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/mocha-framework/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/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": { + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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.2", + "braces": "3.0.2", + "fsevents": "2.3.2", + "glob-parent": "5.1.2", + "is-binary-path": "2.1.0", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "readdirp": "3.5.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 + }, + "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" + }, + "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 + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "6.0.0", + "path-exists": "4.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 + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "5.0.0" + } + }, + "mocha": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.1.tgz", + "integrity": "sha512-9zwsavlRO+5csZu6iRtl3GHImAbhERoDsZwdRkdJ/bE+eVplmoxNKE901ZJ9LdSchYBjSCPbjKc5XvcAri2ylw==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "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.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.23", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.4", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.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==", + "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 + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "3.1.0" + } + }, + "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.3.0" + } + }, "supports-color": { - "optional": true + "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" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "7.0.4", + "escalade": "3.1.1", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "string-width": "4.2.2", + "y18n": "5.0.8", + "yargs-parser": "20.2.4" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true } } }, - "node_modules/@wdio/mocha-framework/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==", + "@wdio/protocols": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.7.4.tgz", + "integrity": "sha512-gfGPOjvqUws3/dTnrXbCYP2keYE6O5BK5qHWnOEu6c7ubE4hebxV8W5c822L7ntabc1e38+diEbM+qFuIT890Q==", "dev": 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", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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.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" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "@wdio/repl": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", + "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", "dev": true, - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=10" + "requires": { + "@wdio/utils": "7.7.3" } }, - "node_modules/@wdio/mocha-framework/node_modules/mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "@wdio/reporter": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", + "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "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.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 10.12.0" + "requires": { + "@types/node": "14.17.4", + "@wdio/types": "7.7.3", + "fs-extra": "10.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "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/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/nanoid": { - "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" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true + } } }, - "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==", + "@wdio/runner": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.4.tgz", + "integrity": "sha512-Ahfrv3TM9y2KMjWI1xKc+tnLVO+X1/Gf5QPjprmLlRxf/rSQDfX+wMmQP/g0wsLtm4pXy0kR1K/76WWvZgzSkw==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "requires": { + "@wdio/config": "7.7.3", + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3", + "@wdio/utils": "7.7.3", + "deepmerge": "4.2.2", + "gaze": "1.1.3", + "webdriver": "7.7.4", + "webdriverio": "7.7.4" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@wdio/mocha-framework/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@wdio/protocols": { - "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.7.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", - "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", - "dev": true, - "dependencies": { - "@wdio/utils": "7.7.3" - }, - "engines": { - "node": ">=12.0.0" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/reporter": { + "@wdio/spec-reporter": { "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", - "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", + "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", "dev": true, - "dependencies": { - "@types/node": "^14.14.31", + "requires": { + "@types/easy-table": "0.0.32", + "@wdio/reporter": "7.7.3", "@wdio/types": "7.7.3", - "fs-extra": "^10.0.0" + "chalk": "4.1.1", + "easy-table": "1.1.1", + "pretty-ms": "7.0.1" }, - "engines": { - "node": ">=12.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" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/runner": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.3.tgz", - "integrity": "sha512-Jetud2znIkY70lrYvHoyBQVRrIQCzNlfjLpCMMraTeNlCzW3eO82TgnOwpCoJ5cJEg78n8YLIDRcIeZ5yo4asA==", + "@wdio/sync": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.4.tgz", + "integrity": "sha512-x0ZU78Je0yl05TfwiNtkKkJZ+90y6MndR4z5n/m6ADRzSGdFOazGJSFO0h2bN8MkPRusfqYsJwB6MKftCP0URA==", "dev": true, - "dependencies": { - "@wdio/config": "7.7.3", + "requires": { + "@types/fibers": "3.1.0", + "@types/puppeteer": "5.4.3", "@wdio/logger": "7.7.0", "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "deepmerge": "^4.0.0", - "gaze": "^1.1.2", - "webdriver": "7.7.3", - "webdriverio": "7.7.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/runner/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/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" + "fibers": "5.0.0", + "webdriverio": "7.7.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/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/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" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "node_modules/@wdio/spec-reporter": { + "@wdio/types": { "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", - "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", + "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", "dev": true, - "dependencies": { - "@types/easy-table": "^0.0.32", - "@wdio/reporter": "7.7.3", - "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "easy-table": "^1.1.1", - "pretty-ms": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" + "requires": { + "@types/node": "14.17.4", + "got": "11.8.2" }, - "peerDependencies": { - "@wdio/cli": "^7.0.0" - } - }, - "node_modules/@wdio/spec-reporter/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true + } } }, - "node_modules/@wdio/spec-reporter/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "@wdio/utils": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", + "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "requires": { + "@wdio/logger": "7.7.0", + "@wdio/types": "7.7.3" }, - "engines": { - "node": ">=10" - } - }, - "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": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@wdio/sync": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.3.tgz", - "integrity": "sha512-LsI9rvxup6mlMuRCDBrjh674bQt4Rnpzf/xa2obhn3GZL97teSwF5ZaTTeF+cs+MPylqwbHiY7iK+roaubqECw==", - "dev": true, - "dependencies": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "fibers": "^5.0.0", - "webdriverio": "7.7.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/sync/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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/sync/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/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==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "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/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/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/types": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", - "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", - "dev": true, - "dependencies": { - "@types/node": "^14.14.31", - "got": "^11.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/utils": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", - "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", - "dev": true, - "dependencies": { - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@wdio/utils/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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" + "@wdio/logger": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", + "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", + "dev": true, + "requires": { + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "4.3.0", + "supports-color": "7.2.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" + } + } } }, - "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==", + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" } }, - "node_modules/abbrev": { + "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", "dev": true }, - "node_modules/accepts": { + "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", + "requires": { + "mime-types": "2.1.31", "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true }, - "node_modules/acorn-dynamic-import": { + "acorn-dynamic-import": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "dev": true, - "dependencies": { - "acorn": "^4.0.3" - } - }, - "node_modules/acorn-dynamic-import/node_modules/acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "requires": { + "acorn": "4.0.13" }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, "dependencies": { - "acorn": "^3.0.4" + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } } }, - "node_modules/acorn-jsx/node_modules/acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true }, - "node_modules/acorn-node": { + "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "dev": true, - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-node/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "requires": { + "acorn": "7.4.1", + "acorn-walk": "7.2.0", + "xtend": "4.0.2" } }, - "node_modules/acorn-walk": { + "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "dev": true }, - "node_modules/add-stream": { + "add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", "dev": true }, - "node_modules/agent-base": { + "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, - "engines": { - "node": ">= 6.0.0" - } + "dev": true }, - "node_modules/ajv": { + "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.3.1" + }, "dependencies": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } } }, - "node_modules/ajv-keywords": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", - "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", - "dev": true - }, - "node_modules/ajv/node_modules/fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "node_modules/ajv/node_modules/json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "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 }, - "node_modules/align-text": { + "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "dependencies": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" }, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "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.6" + } + } } }, - "node_modules/align-text/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/align-text/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/amdefine": { + "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true, - "engines": { - "node": ">=0.4.2" - } + "dev": true }, - "node_modules/ansi-colors": { + "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" - } + "dev": true }, - "node_modules/ansi-escapes": { + "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "type-fest": "0.21.3" } }, - "node_modules/ansi-gray": { + "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", "dev": true, - "dependencies": { + "requires": { "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" } }, - "node_modules/ansi-html": { + "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } + "dev": true }, - "node_modules/ansi-regex": { + "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/ansi-styles": { + "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "requires": { + "color-convert": "1.9.3" } }, - "node_modules/ansi-wrap": { + "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/anymatch": { + "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "requires": { + "normalize-path": "3.0.0", + "picomatch": "2.3.0" } }, - "node_modules/append-buffer": { + "append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", "dev": true, - "dependencies": { - "buffer-equal": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "buffer-equal": "1.0.0" } }, - "node_modules/archiver": { + "archiver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz", "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==", "dev": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.0", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" + "requires": { + "archiver-utils": "2.1.0", + "async": "3.2.0", + "buffer-crc32": "0.2.13", + "readable-stream": "3.6.0", + "readdir-glob": "1.1.1", + "tar-stream": "2.2.0", + "zip-stream": "4.1.0" }, - "engines": { - "node": ">= 10" + "dependencies": { + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true + } } }, - "node_modules/archiver-utils": { + "archiver-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" + "requires": { + "glob": "7.1.7", + "graceful-fs": "4.2.6", + "lazystream": "1.0.0", + "lodash.defaults": "4.2.0", + "lodash.difference": "4.5.0", + "lodash.flatten": "4.4.0", + "lodash.isplainobject": "4.0.6", + "lodash.union": "4.6.0", + "normalize-path": "3.0.0", + "readable-stream": "2.3.7" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, - "node_modules/archiver/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - }, - "node_modules/archy": { + "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/aria-query": { + "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" + "requires": { + "@babel/runtime": "7.14.6", + "@babel/runtime-corejs3": "7.14.7" } }, - "node_modules/arr-diff": { + "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, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/arr-filter": { + "arr-filter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", "dev": true, - "dependencies": { - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "make-iterator": "1.0.1" } }, - "node_modules/arr-flatten": { + "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/arr-map": { + "arr-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", "dev": true, - "dependencies": { - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "make-iterator": "1.0.1" } }, - "node_modules/arr-union": { + "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array-differ": { + "array-differ": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array-each": { + "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", "dev": true }, - "node_modules/array-find-index": { + "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array-flatten": { + "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "node_modules/array-from": { + "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, - "node_modules/array-ify": { + "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, - "node_modules/array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - }, - "engines": { - "node": ">= 0.4" + "requires": { + "call-bind": "1.0.2", + "define-properties": "1.1.3", + "es-abstract": "1.18.3", + "get-intrinsic": "1.1.1", + "is-string": "1.0.6" } }, - "node_modules/array-initial": { + "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", "dev": true, - "dependencies": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" + "requires": { + "array-slice": "1.1.0", + "is-number": "4.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-initial/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } } }, - "node_modules/array-last": { + "array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", "dev": true, - "dependencies": { - "is-number": "^4.0.0" + "requires": { + "is-number": "4.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-last/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } } }, - "node_modules/array-slice": { + "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array-sort": { + "array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, - "dependencies": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" + "requires": { + "default-compare": "1.0.0", + "get-value": "2.0.6", + "kind-of": "5.1.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-sort/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } } }, - "node_modules/array-uniq": { + "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array-unique": { + "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.4" + "requires": { + "call-bind": "1.0.2", + "define-properties": "1.1.3", + "es-abstract": "1.18.3" } }, - "node_modules/arrify": { + "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/asn1": { + "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" + "requires": { + "safer-buffer": "2.1.2" } }, - "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dev": true, + "requires": { + "bn.js": "4.12.0", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "safer-buffer": "2.1.2" + }, "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } } }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/assert": { + "assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, - "dependencies": { - "object-assign": "^4.1.1", + "requires": { + "object-assign": "4.1.1", "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } } }, - "node_modules/assert-plus": { + "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", "dev": true }, - "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "dependencies": { - "inherits": "2.0.1" - } - }, - "node_modules/assertion-error": { + "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/assign-symbols": { + "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/astral-regex": { + "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "dev": true }, - "node_modules/async": { + "async": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, - "node_modules/async-done": { + "async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" + "requires": { + "end-of-stream": "1.4.4", + "once": "1.4.0", + "process-nextick-args": "2.0.1", + "stream-exhaust": "1.0.2" } }, - "node_modules/async-each": { + "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, - "node_modules/async-exit-hook": { + "async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } + "dev": true }, - "node_modules/async-limiter": { + "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, - "node_modules/async-settle": { + "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", "dev": true, - "dependencies": { - "async-done": "^1.2.2" - }, - "engines": { - "node": ">= 0.10" + "requires": { + "async-done": "1.3.2" } }, - "node_modules/asynckit": { + "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "node_modules/atob": { + "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } + "dev": true }, - "node_modules/available-typed-arrays": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", - "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", - "dev": true, - "dependencies": { - "array-filter": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } + "available-typed-arrays": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", + "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", + "dev": true }, - "node_modules/aws-sign2": { + "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=", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "node_modules/babel-code-frame": { + "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=", "dev": true, - "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.3", + "js-tokens": "3.0.2" }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "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=", - "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.8.0" + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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 + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "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=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, - "node_modules/babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", "dev": true, - "dependencies": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "schema-utils": "^2.6.5" + "requires": { + "find-cache-dir": "3.3.1", + "loader-utils": "1.4.0", + "make-dir": "3.1.0", + "schema-utils": "2.7.1" }, - "engines": { - "node": ">= 6.9" - } - }, - "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" + "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.5" + } + }, + "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" + } + } } }, - "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==", + "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==", "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" + "requires": { + "object.assign": "4.1.2" } }, - "node_modules/babel-loader/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==", + "babel-plugin-polyfill-corejs2": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", + "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "requires": { + "@babel/compat-data": "7.14.7", + "@babel/helper-define-polyfill-provider": "0.2.3", + "semver": "6.3.0" } }, - "node_modules/babel-loader/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "babel-plugin-polyfill-corejs3": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz", + "integrity": "sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g==", "dev": true, - "engines": { - "node": ">=6" + "requires": { + "@babel/helper-define-polyfill-provider": "0.2.3", + "core-js-compat": "3.15.1" } }, - "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==", + "babel-plugin-polyfill-regenerator": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", + "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", "dev": true, - "dependencies": { - "object.assign": "^4.1.0" + "requires": { + "@babel/helper-define-polyfill-provider": "0.2.3" } }, - "node_modules/babel-plugin-transform-object-assign": { + "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" + "requires": { + "babel-runtime": "6.26.0" } }, - "node_modules/babel-runtime": { + "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=", + "requires": { + "core-js": "2.6.12", + "regenerator-runtime": "0.11.1" + }, "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } } }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, - "node_modules/babelify": { + "babelify": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "dev": true }, - "node_modules/bach": { + "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", "dev": true, - "dependencies": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" + "requires": { + "arr-filter": "1.1.2", + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "array-each": "1.0.1", + "array-initial": "1.1.0", + "array-last": "1.3.0", + "async-done": "1.3.2", + "async-settle": "1.0.0", + "now-and-later": "2.0.1" } }, - "node_modules/bail": { + "bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base": { + "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.3.0", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.2", + "pascalcase": "0.1.1" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" + } + } } }, - "node_modules/base64-arraybuffer": { + "base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } + "dev": true }, - "node_modules/base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, - "node_modules/base64id": { + "base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } + "dev": true }, - "node_modules/basic-auth": { + "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, - "dependencies": { + "requires": { "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" } }, - "node_modules/batch": { + "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "node_modules/bcrypt-pbkdf": { + "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" + "requires": { + "tweetnacl": "0.14.5" } }, - "node_modules/beeper": { + "beeper": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/bfj": { + "bfj": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "check-types": "^8.0.3", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - }, - "engines": { - "node": ">= 6.0.0" + "requires": { + "bluebird": "3.7.2", + "check-types": "8.0.3", + "hoopy": "0.1.4", + "tryer": "1.0.1" } }, - "node_modules/big-integer": { + "big-integer": { "version": "1.6.48", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", - "dev": true, - "engines": { - "node": ">=0.6" - } + "dev": true }, - "node_modules/big.js": { + "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/binary": { + "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", "dev": true, - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" + "requires": { + "buffers": "0.1.1", + "chainsaw": "0.1.0" } }, - "node_modules/binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true }, - "node_modules/binaryextensions": { + "binaryextensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", - "dev": true, - "engines": { - "node": ">=0.8" - } + "dev": true }, - "node_modules/bindings": { + "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, "optional": true, - "dependencies": { + "requires": { "file-uri-to-path": "1.0.0" } }, - "node_modules/bl": { + "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "requires": { + "buffer": "5.7.1", + "inherits": "2.0.4", + "readable-stream": "3.6.0" + }, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } } }, - "node_modules/bl/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/bluebird": { + "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "node_modules/bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", "dev": true }, - "node_modules/body": { + "body": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", "dev": true, + "requires": { + "continuable-cache": "0.3.1", + "error": "7.2.1", + "raw-body": "1.1.7", + "safe-json-parse": "1.0.1" + }, "dependencies": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "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=", + "dev": true, + "requires": { + "bytes": "1.0.0", + "string_decoder": "0.10.31" + } + }, + "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/body-parser": { + "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { + "requires": { "bytes": "3.1.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "1.1.2", "http-errors": "1.7.2", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.7.0", "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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=", - "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=", - "dev": true, - "dependencies": { - "bytes": "1", - "string_decoder": "0.10" - }, - "engines": { - "node": ">= 0.8.0" + "type-is": "1.6.18" } }, - "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=", - "dev": true - }, - "node_modules/brace-expansion": { + "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", + "requires": { + "balanced-match": "1.0.2", "concat-map": "0.0.1" } }, - "node_modules/braces": { + "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "requires": { + "fill-range": "7.0.1" } }, - "node_modules/brorand": { + "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, - "node_modules/browser-resolve": { + "browser-resolve": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, - "dependencies": { + "requires": { "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } } }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "node_modules/browser-stdout": { + "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/browserify-aes": { + "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, - "node_modules/browserify-cipher": { + "browserify-cipher": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "requires": { + "browserify-aes": "1.2.0", + "browserify-des": "1.0.2", + "evp_bytestokey": "1.0.3" } }, - "node_modules/browserify-des": { + "browserify-des": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.1", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, - "node_modules/browserify-rsa": { - "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "requires": { + "bn.js": "5.2.0", + "randombytes": "2.1.0" } }, - "node_modules/browserify-rsa/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/browserify-sign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", - "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", "dev": true, + "requires": { + "bn.js": "5.2.0", + "browserify-rsa": "4.1.0", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.5.4", + "inherits": "2.0.4", + "parse-asn1": "5.1.6", + "readable-stream": "3.6.0", + "safe-buffer": "5.2.1" + }, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.2", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, - "node_modules/browserify-sign/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/browserify-sign/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==", - "dev": true + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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==", + "dev": true + } + } }, - "node_modules/browserify-zlib": { + "browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, - "dependencies": { - "pako": "~1.0.5" + "requires": { + "pako": "1.0.11" } }, - "node_modules/browserslist": { + "browserslist": { "version": "4.16.6", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "requires": { + "caniuse-lite": "1.0.30001239", + "colorette": "1.2.2", + "electron-to-chromium": "1.3.755", + "escalade": "3.1.1", + "node-releases": "1.1.73" } }, - "node_modules/browserstack": { + "browserstack": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, - "node_modules/browserstack-local": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.5.tgz", - "integrity": "sha512-0/VdSv2YVXmcnwBb64XThMvjM1HnZJnPdv7CUgQbC5y/N9Wsr0Fu+j1oknE9fC/VPx9CpoSC6CJ0kza42skMSA==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^4.0.0", - "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" - } - }, - "node_modules/browserstack/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" + "requires": { + "https-proxy-agent": "2.2.4" }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/browserstack/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, "dependencies": { - "ms": "^2.1.1" + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "2.1.3" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "4.3.0", + "debug": "3.2.7" + } + }, + "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/browserstack/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "browserstack-local": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.8.tgz", + "integrity": "sha512-s+mc3gTOJwELdLWi4qFVKtGwMbb5JWsR+JxKlMaJkRJxoZ0gg3WREgPxAN0bm6iU5+S4Bi0sz0oxBRZT8BiNsQ==", "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" + "requires": { + "https-proxy-agent": "4.0.0", + "is-running": "2.1.0", + "ps-tree": "1.2.0", + "temp-fs": "0.9.9" } }, - "node_modules/browserstack/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/browserstacktunnel-wrapper": { + "browserstacktunnel-wrapper": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz", "integrity": "sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg==", "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1", - "unzipper": "^0.9.3" + "requires": { + "https-proxy-agent": "2.2.4", + "unzipper": "0.9.15" }, - "engines": { - "node": ">= 0.10.20" - } - }, - "node_modules/browserstacktunnel-wrapper/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "2.1.3" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "4.3.0", + "debug": "3.2.7" + } + }, + "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/browserstacktunnel-wrapper/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "requires": { + "base64-js": "1.5.1", + "ieee754": "1.2.1" } }, - "node_modules/browserstacktunnel-wrapper/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/browserstacktunnel-wrapper/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/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { + "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/buffer-equal": { + "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "dev": true }, - "node_modules/buffer-from": { + "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", - "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", - "dev": true, - "engines": { - "node": ">=0.10" - } + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true }, - "node_modules/buffer-shims": { + "buffer-shims": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, - "node_modules/buffer-xor": { + "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "node_modules/buffers": { + "buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true, - "engines": { - "node": ">=0.2.0" - } + "dev": true }, - "node_modules/builtin-status-codes": { + "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, - "node_modules/bytes": { + "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "node_modules/cac": { + "cac": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", "dev": true, - "dependencies": { - "camelcase-keys": "^3.0.0", - "chalk": "^1.1.3", - "indent-string": "^3.0.0", - "minimist": "^1.2.0", - "read-pkg-up": "^1.0.1", - "suffix": "^0.1.0", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "requires": { + "camelcase-keys": "3.0.0", + "chalk": "1.1.3", + "indent-string": "3.2.0", + "minimist": "1.2.5", + "read-pkg-up": "1.0.1", + "suffix": "0.1.1", + "text-table": "0.2.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.8.0" + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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 + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, - "node_modules/cache-base": { + "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.3.0", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.1", + "to-object-path": "0.3.0", + "union-value": "1.0.1", + "unset-value": "1.0.0" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", - "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", - "dev": true, - "engines": { - "node": ">=10" - } + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true }, - "node_modules/cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" + "requires": { + "clone-response": "1.0.2", + "get-stream": "5.2.0", + "http-cache-semantics": "4.1.0", + "keyv": "4.0.3", + "lowercase-keys": "2.0.0", + "normalize-url": "6.1.0", + "responselike": "2.0.0" } }, - "node_modules/cached-path-relative": { + "cached-path-relative": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", "dev": true }, - "node_modules/call-bind": { + "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "function-bind": "1.1.1", + "get-intrinsic": "1.1.1" } }, - "node_modules/caller-path": { + "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, - "dependencies": { - "callsites": "^0.2.0" + "requires": { + "callsites": "0.2.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/caller-path/node_modules/callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } } }, - "node_modules/callsites": { + "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } + "dev": true }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true }, - "node_modules/camelcase-keys": { + "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/camelcase-keys/node_modules/camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "requires": { + "camelcase": "3.0.0", + "map-obj": "1.0.1" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001235", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", - "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", + "caniuse-lite": { + "version": "1.0.30001239", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", + "integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", "dev": true }, - "node_modules/caseless": { + "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "node_modules/ccount": { + "ccount": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/center-align": { + "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, - "dependencies": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" + "requires": { + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.1", + "type-detect": "4.0.8" } }, - "node_modules/chainsaw": { + "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", "dev": true, - "dependencies": { - "traverse": ">=0.3.0 <0.4" + "requires": { + "traverse": "0.3.9" } }, - "node_modules/chalk": { + "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, - "node_modules/character-entities": { + "character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/character-entities-html4": { + "character-entities-html4": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/character-entities-legacy": { + "character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/character-reference-invalid": { + "character-reference-invalid": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/chardet": { + "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "node_modules/check-error": { + "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/check-types": { + "check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", "dev": true }, - "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==", + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, - "dependencies": { - "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.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" + "requires": { + "anymatch": "3.1.2", + "braces": "3.0.2", + "fsevents": "2.3.2", + "glob-parent": "5.1.2", + "is-binary-path": "2.1.0", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "readdirp": "3.6.0" } }, - "node_modules/chownr": { + "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, - "node_modules/chrome-launcher": { + "chrome-launcher": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.14.0.tgz", "integrity": "sha512-W//HpflaW6qBGrmuskup7g+XJZN6w03ko9QSIe5CtcTal2u0up5SeReK3Ll1Why4Ey8dPkv8XSodZyHPnGbVHQ==", "dev": true, - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/chrome-launcher/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" + "requires": { + "@types/node": "15.12.4", + "escape-string-regexp": "4.0.0", + "is-wsl": "2.2.0", + "lighthouse-logger": "1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } } }, - "node_modules/cipher-base": { + "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, - "node_modules/circular-json": { + "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", "dev": true }, - "node_modules/class-utils": { + "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, - "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=", - "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } } }, - "node_modules/cli-cursor": { + "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" + "requires": { + "restore-cursor": "3.1.0" } }, - "node_modules/cli-spinners": { + "cli-spinners": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "dev": true }, - "node_modules/cli-width": { + "cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } + "dev": true }, - "node_modules/cliui": { + "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" + "requires": { + "string-width": "4.2.2", + "strip-ansi": "6.0.0", + "wrap-ansi": "7.0.0" } }, - "node_modules/clone": { + "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true, - "engines": { - "node": ">=0.8" - } + "dev": true }, - "node_modules/clone-buffer": { + "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true, - "engines": { - "node": ">= 0.10" - } + "dev": true }, - "node_modules/clone-response": { + "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" + "requires": { + "mimic-response": "1.0.1" } }, - "node_modules/clone-stats": { + "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=", "dev": true }, - "node_modules/cloneable-readable": { + "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, + "requires": { + "inherits": "2.0.3", + "process-nextick-args": "2.0.1", + "readable-stream": "2.3.7" + }, "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, - "node_modules/cloneable-readable/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, - "node_modules/co": { + "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } + "dev": true }, - "node_modules/code-point-at": { + "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/collection-map": { + "collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, - "dependencies": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "arr-map": "2.0.2", + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, - "node_modules/collection-visit": { + "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, - "node_modules/color-convert": { + "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "dependencies": { + "requires": { "color-name": "1.1.3" } }, - "node_modules/color-name": { + "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/color-support": { + "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } + "dev": true }, - "node_modules/colorette": { + "colorette": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, - "node_modules/colors": { + "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } + "dev": true }, - "node_modules/combined-stream": { + "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" + "requires": { + "delayed-stream": "1.0.0" } }, - "node_modules/comma-separated-tokens": { + "comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "dev": true }, - "node_modules/commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "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": { + "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "node_modules/compare-func": { + "compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "requires": { + "array-ify": "1.0.0", + "dot-prop": "5.3.0" } }, - "node_modules/component-emitter": { + "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, - "node_modules/compress-commons": { + "compress-commons": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" + "requires": { + "buffer-crc32": "0.2.13", + "crc32-stream": "4.0.2", + "normalize-path": "3.0.0", + "readable-stream": "3.6.0" } }, - "node_modules/concat-map": { + "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/concat-stream": { + "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "requires": { + "buffer-from": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.7", + "typedarray": "0.0.6" + }, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, - "node_modules/concat-with-sourcemaps": { + "concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, + "requires": { + "source-map": "0.6.1" + }, "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/concat-with-sourcemaps/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": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "node_modules/connect": { + "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, - "dependencies": { + "requires": { "debug": "2.6.9", "finalhandler": "1.1.2", - "parseurl": "~1.3.3", + "parseurl": "1.3.3", "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" } }, - "node_modules/connect-livereload": { + "connect-livereload": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", - "dev": true, - "engines": { - "node": "*" - } + "dev": true }, - "node_modules/console-browserify": { + "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, - "node_modules/consolidate": { + "consolidate": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", "dev": true, "optional": true, - "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">= 0.10.0" + "requires": { + "bluebird": "3.7.2" } }, - "node_modules/constants-browserify": { + "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/content-disposition": { + "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { + "requires": { "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" } }, - "node_modules/content-type": { + "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, - "node_modules/continuable-cache": { + "continuable-cache": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", "dev": true }, - "node_modules/conventional-changelog": { + "conventional-changelog": { "version": "3.1.24", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", "dev": true, - "dependencies": { - "conventional-changelog-angular": "^5.0.12", - "conventional-changelog-atom": "^2.0.8", - "conventional-changelog-codemirror": "^2.0.8", - "conventional-changelog-conventionalcommits": "^4.5.0", - "conventional-changelog-core": "^4.2.1", - "conventional-changelog-ember": "^2.0.9", - "conventional-changelog-eslint": "^3.0.9", - "conventional-changelog-express": "^2.0.6", - "conventional-changelog-jquery": "^3.0.11", - "conventional-changelog-jshint": "^2.0.9", - "conventional-changelog-preset-loader": "^2.3.4" - }, - "engines": { - "node": ">=10" + "requires": { + "conventional-changelog-angular": "5.0.12", + "conventional-changelog-atom": "2.0.8", + "conventional-changelog-codemirror": "2.0.8", + "conventional-changelog-conventionalcommits": "4.5.0", + "conventional-changelog-core": "4.2.2", + "conventional-changelog-ember": "2.0.9", + "conventional-changelog-eslint": "3.0.9", + "conventional-changelog-express": "2.0.6", + "conventional-changelog-jquery": "3.0.11", + "conventional-changelog-jshint": "2.0.9", + "conventional-changelog-preset-loader": "2.3.4" } }, - "node_modules/conventional-changelog-angular": { + "conventional-changelog-angular": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" + "requires": { + "compare-func": "2.0.0", + "q": "1.5.1" } }, - "node_modules/conventional-changelog-atom": { + "conventional-changelog-atom": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" + "requires": { + "q": "1.5.1" } }, - "node_modules/conventional-changelog-codemirror": { + "conventional-changelog-codemirror": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" + "requires": { + "q": "1.5.1" } }, - "node_modules/conventional-changelog-config-spec": { + "conventional-changelog-config-spec": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", "dev": true }, - "node_modules/conventional-changelog-conventionalcommits": { + "conventional-changelog-conventionalcommits": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" + "requires": { + "compare-func": "2.0.0", + "lodash": "4.17.21", + "q": "1.5.1" } }, - "node_modules/conventional-changelog-core": { + "conventional-changelog-core": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz", "integrity": "sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg==", "dev": true, - "dependencies": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^4.0.18", - "conventional-commits-parser": "^3.2.0", - "dateformat": "^3.0.0", - "get-pkg-repo": "^1.0.0", - "git-raw-commits": "^2.0.8", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.1.1", - "lodash": "^4.17.15", - "normalize-package-data": "^3.0.0", - "q": "^1.5.1", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0", - "shelljs": "^0.8.3", - "through2": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/conventional-changelog-core/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/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==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "requires": { + "add-stream": "1.0.0", + "conventional-changelog-writer": "4.1.0", + "conventional-commits-parser": "3.2.1", + "dateformat": "3.0.3", + "get-pkg-repo": "1.4.0", + "git-raw-commits": "2.0.10", + "git-remote-origin-url": "2.0.0", + "git-semver-tags": "4.1.1", + "lodash": "4.17.21", + "normalize-package-data": "3.0.2", + "q": "1.5.1", + "read-pkg": "3.0.0", + "read-pkg-up": "3.0.0", + "shelljs": "0.8.4", + "through2": "4.0.2" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/read-pkg/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/conventional-changelog-core/node_modules/read-pkg/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/conventional-changelog-core/node_modules/read-pkg/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/conventional-changelog-core/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-core/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/conventional-changelog-ember": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", - "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-eslint": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", - "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-express": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", - "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-jquery": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", - "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", - "dev": true, - "dependencies": { - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-jshint": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", - "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-preset-loader": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", - "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", - "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/conventional-changelog-writer/node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/conventional-changelog-writer/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/conventional-changelog-writer/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/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/conventional-changelog-writer/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/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/conventional-changelog-writer/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/read-pkg/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/conventional-changelog-writer/node_modules/read-pkg/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/conventional-changelog-writer/node_modules/read-pkg/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/conventional-changelog-writer/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/conventional-changelog-writer/node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/conventional-changelog-writer/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-changelog-writer/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-changelog-writer/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dev": true, - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", - "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/conventional-commits-parser/node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/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/conventional-commits-parser/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/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/conventional-commits-parser/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/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/conventional-commits-parser/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/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/conventional-commits-parser/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-commits-parser/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-parser/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/conventional-recommended-bump": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", - "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", - "dev": true, - "dependencies": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^2.3.4", - "conventional-commits-filter": "^2.0.7", - "conventional-commits-parser": "^3.2.0", - "git-raw-commits": "^2.0.8", - "git-semver-tags": "^4.1.1", - "meow": "^8.0.0", - "q": "^1.5.1" - }, - "bin": { - "conventional-recommended-bump": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/conventional-recommended-bump/node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/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/conventional-recommended-bump/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/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/conventional-recommended-bump/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-recommended-bump/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/conventional-recommended-bump/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-recommended-bump/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/copy-props": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", - "dev": true, - "dependencies": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" - } - }, - "node_modules/core-js": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", - "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", - "dev": true, - "dependencies": { - "browserslist": "^4.8.5", - "semver": "7.0.0" - } - }, - "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==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/core-js-pure": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", - "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "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=", - "dev": true - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/coveralls": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", - "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", - "dev": true, - "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - }, - "bin": { - "coveralls": "bin/coveralls.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/crc-32": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", - "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", - "dev": true, - "dependencies": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" - }, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", - "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", - "dev": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/criteo-direct-rsa-validate": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/criteo-direct-rsa-validate/-/criteo-direct-rsa-validate-1.1.0.tgz", - "integrity": "sha512-7gQ3zX+d+hS/vOxzLrZ4aRAceB7qNJ0VzaGNpcWjDCmtOpASB50USJDupTik/H2nHgiSAA3VNZ3SFuONs8LR9Q==" - }, - "node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/crypto-js": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", - "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" - }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "node_modules/css-shorthand-properties": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", - "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", - "dev": true - }, - "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=", - "dev": true - }, - "node_modules/css/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/css/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/css/node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "optional": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "2.6.17", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", - "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", - "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "dependencies": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - }, - "bin": { - "dateformat": "bin/cli.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true, - "optional": true - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", - "dev": true, - "dependencies": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" - } - }, - "node_modules/debug-fabulous/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/debug-fabulous/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/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-equal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", - "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", - "dev": true, - "dependencies": { - "es-abstract": "^1.17.5", - "es-get-iterator": "^1.1.0", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.0.5", - "isarray": "^2.0.5", - "object-is": "^1.1.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/deep-equal/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "dev": true, - "dependencies": { - "kind-of": "^5.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-compare/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", - "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=", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "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=", - "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=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detective": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", - "dev": true, - "dependencies": { - "acorn-node": "^1.6.1", - "defined": "^1.0.0", - "minimist": "^1.1.1" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/devtools": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.7.3.tgz", - "integrity": "sha512-MvLCrJqLJXPK+N1En01EdM8wpitQiKaXGlprSsWMZWJU5iy/ljOKF9KRYGf0eQ2ZT3FfjcBgJh4yyLom7wVYeg==", - "dev": true, - "dependencies": { - "@types/node": "^14.14.31", - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "chrome-launcher": "^0.14.0", - "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/devtools-protocol": { - "version": "0.0.887710", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.887710.tgz", - "integrity": "sha512-ZN57GSHgIoz6opBE4XtUhZvCG4Mjy6n0WxUCcSv8fdHc1TDRlI8IglTzwNMXUKqehFSIEHVxKZcaAoACKWHFBQ==", - "dev": true - }, - "node_modules/devtools/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/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/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/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.1.tgz", - "integrity": "sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "node_modules/doctrine": { - "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/doctrine-temporary-fork": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.1.0.tgz", - "integrity": "sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/documentation": { - "version": "13.2.5", - "resolved": "https://registry.npmjs.org/documentation/-/documentation-13.2.5.tgz", - "integrity": "sha512-d1TrfrHXYZR63xrOzkYwwe297vkSwBoEhyyMBOi20T+7Ohe1aX1dW4nqXncQmdmE5MxluSaxxa3BW1dCvbF5AQ==", - "dev": true, - "dependencies": { - "@babel/core": "7.12.3", - "@babel/generator": "7.12.1", - "@babel/parser": "7.12.3", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "ansi-html": "^0.0.7", - "babelify": "^10.0.0", - "chalk": "^2.3.0", - "chokidar": "^3.4.0", - "concat-stream": "^1.6.0", - "diff": "^4.0.1", - "doctrine-temporary-fork": "2.1.0", - "get-port": "^5.0.0", - "git-url-parse": "^11.1.2", - "github-slugger": "1.2.0", - "glob": "^7.1.2", - "globals-docs": "^2.4.0", - "highlight.js": "^10.7.2", - "ini": "^1.3.5", - "js-yaml": "^3.10.0", - "lodash": "^4.17.10", - "mdast-util-find-and-replace": "^1.1.1", - "mdast-util-inject": "^1.1.0", - "micromatch": "^3.1.5", - "mime": "^2.2.0", - "module-deps-sortable": "^5.0.3", - "parse-filepath": "^1.0.2", - "pify": "^5.0.0", - "read-pkg-up": "^4.0.0", - "remark": "^13.0.0", - "remark-gfm": "^1.0.0", - "remark-html": "^13.0.1", - "remark-reference-links": "^5.0.0", - "remark-toc": "^7.2.0", - "resolve": "^1.8.1", - "stream-array": "^1.1.2", - "strip-json-comments": "^2.0.1", - "tiny-lr": "^1.1.0", - "unist-builder": "^2.0.3", - "unist-util-visit": "^2.0.3", - "vfile": "^4.0.0", - "vfile-reporter": "^6.0.0", - "vfile-sort": "^2.1.0", - "vinyl": "^2.1.0", - "vinyl-fs": "^3.0.2", - "yargs": "^15.3.1" - }, - "bin": { - "documentation": "bin/documentation.js" - }, - "engines": { - "node": ">=10" - }, - "optionalDependencies": { - "@vue/compiler-sfc": "^3.0.11", - "vue-template-compiler": "^2.6.12" - } - }, - "node_modules/documentation/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/documentation/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/documentation/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/documentation/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/documentation/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/documentation/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/path-type/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/documentation/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/documentation/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/documentation/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/documentation/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/documentation/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/documentation/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/documentation/node_modules/yargs/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/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true, - "engines": { - "node": ">=0.4", - "npm": ">=1.2" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dotgitignore": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", - "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotgitignore/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/dset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dset/-/dset-2.0.1.tgz", - "integrity": "sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/duplexer": { - "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" - } - }, - "node_modules/easy-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", - "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "optionalDependencies": { - "wcwidth": ">=1.0.1" - } - }, - "node_modules/easy-table/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/edge-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-2.2.1.tgz", - "integrity": "sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw==", - "dev": true, - "dependencies": { - "@types/which": "^1.3.2", - "which": "^2.0.2" - } - }, - "node_modules/edge-paths/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/ejs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", - "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", - "dev": true, - "dependencies": { - "jake": "^10.6.1" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.3.752", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", - "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", - "dev": true - }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/elliptic/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", - "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", - "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", - "dev": true, - "dependencies": { - "base64-arraybuffer": "0.1.4" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/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/engine.io/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/enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "dependencies": { - "string-template": "~0.2.1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-abstract/node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "dependencies": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } - }, - "node_modules/es-get-iterator/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "node_modules/es5-shim": { - "version": "4.5.14", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", - "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - } - }, - "node_modules/es6-set/node_modules/es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "dependencies": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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=", - "dev": true - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/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==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/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==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-prebid": { - "resolved": "plugins/eslint", - "link": true - }, - "node_modules/eslint-plugin-promise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", - "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", - "dev": true, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0" - } - }, - "node_modules/eslint-plugin-standard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", - "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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", - "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/eslint/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/eslint/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/eslint/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/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/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/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "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/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/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/eslint/node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/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/eslint/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/espree/node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "optional": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", - "dev": true - }, - "node_modules/events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "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=", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "dependencies": { - "fill-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "dependencies": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/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/expand-range/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/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/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.2.tgz", - "integrity": "sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w==", - "dev": true, - "dependencies": { - "@jest/types": "^27.0.2", - "ansi-styles": "^5.0.0", - "jest-get-type": "^27.0.1", - "jest-matcher-utils": "^27.0.2", - "jest-message-util": "^27.0.2", - "jest-regex-util": "^27.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect-webdriverio": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-3.1.0.tgz", - "integrity": "sha512-Kn4Rtu5vKbDo95WNcjZ9XVz/qTPGZzumP9w7VSV4OxY5z6BAqSI2jb85EsqPxpavs33P+9Qse4Z+d5ilDD/dQw==", - "dev": true, - "dependencies": { - "expect": "^27.0.2", - "jest-matcher-utils": "^27.0.2" - } - }, - "node_modules/expect/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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dev": true, - "dependencies": { - "type": "^2.0.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "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=", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend-shallow/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/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/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/extract-zip/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/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", - "dev": true - }, - "node_modules/fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", - "dev": true, - "dependencies": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "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=", - "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=", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fibers": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.0.tgz", - "integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "node_modules/filelist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", - "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "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/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "node_modules/flush-write-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "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=", - "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=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/fork-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", - "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", - "dev": true - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "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=", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "node_modules/fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "dependencies": { - "null-check": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/fs-mkdirp-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/fs-mkdirp-stream/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "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=", - "dev": true, - "dependencies": { - "fs-extra": "~0.6.1", - "mkdirp": "~0.3.5", - "walk": "^2.3.9" - }, - "engines": { - "node": "*" - } - }, - "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=", - "dev": true, - "dependencies": { - "jsonfile": "~1.0.1", - "mkdirp": "0.3.x", - "ncp": "~0.4.2", - "rimraf": "~2.2.0" - } - }, - "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=", - "dev": true - }, - "node_modules/fs.extra/node_modules/mkdirp": { - "version": "0.3.5", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", - "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=", - "dev": true, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/fun-hooks": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.9.tgz", - "integrity": "sha512-821UhoYfO9Sg01wAl/QsDRB088BW0aeOqzC1PXLxSlB+kaUVbK+Vp6wMDHU5huZZopYxmMmv5jDkEYqDpK3hqg==", - "dependencies": { - "typescript-tuple": "^2.2.1" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "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=", - "dev": true - }, - "node_modules/gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "dependencies": { - "globule": "^1.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/generic-names": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", - "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", - "dev": true, - "optional": true, - "dependencies": { - "loader-utils": "^1.1.0" - } - }, - "node_modules/generic-names/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, - "optional": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/generic-names/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, - "optional": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "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=", - "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==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-pkg-repo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", - "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "meow": "^3.3.0", - "normalize-package-data": "^2.3.0", - "parse-github-repo-url": "^1.3.0", - "through2": "^2.0.0" - }, - "bin": { - "get-pkg-repo": "cli.js" - } - }, - "node_modules/get-pkg-repo/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/get-pkg-repo/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/git-raw-commits": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", - "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", - "dev": true, - "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/git-raw-commits/node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/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/git-raw-commits/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/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/git-raw-commits/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/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/git-raw-commits/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/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/git-raw-commits/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-raw-commits/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", - "dev": true, - "dependencies": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/git-semver-tags": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", - "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", - "dev": true, - "dependencies": { - "meow": "^8.0.0", - "semver": "^6.0.0" - }, - "bin": { - "git-semver-tags": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/git-semver-tags/node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/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/git-semver-tags/node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/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/git-semver-tags/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-semver-tags/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/git-semver-tags/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/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/git-semver-tags/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/git-semver-tags/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/git-semver-tags/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-semver-tags/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/git-up": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", - "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "parse-url": "^5.0.0" - } - }, - "node_modules/git-url-parse": { - "version": "11.4.4", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz", - "integrity": "sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw==", - "dev": true, - "dependencies": { - "git-up": "^4.0.0" - } - }, - "node_modules/gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", - "dev": true, - "dependencies": { - "ini": "^1.3.2" - } - }, - "node_modules/github-slugger": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", - "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", - "dev": true, - "dependencies": { - "emoji-regex": ">=6.0.0 <=6.1.1" - } - }, - "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": "*" - } - }, - "node_modules/glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "dependencies": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/glob-base/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "dev": true, - "dependencies": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/glob-stream/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/glob-watcher": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", - "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/glob-watcher/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "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=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "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", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/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/glob-watcher/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/glob-watcher/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.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=", - "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/glob-watcher/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/glob-watcher/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/glob-watcher/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "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-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/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/glob-watcher/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/glob-watcher/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globals-docs": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.4.1.tgz", - "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==", - "dev": true - }, - "node_modules/globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", - "dev": true, - "dependencies": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", - "dev": true, - "dependencies": { - "sparkles": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/got": { - "version": "11.8.2", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", - "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", - "dev": true, - "dependencies": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" - }, - "bin": { - "gulp": "bin/gulp.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-clean": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.3.2.tgz", - "integrity": "sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI=", - "dev": true, - "dependencies": { - "gulp-util": "^2.2.14", - "rimraf": "^2.2.8", - "through2": "^0.4.2" - }, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/gulp-clean/node_modules/ansi-regex": { - "version": "0.2.1", - "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/chalk": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", - "dev": true, - "dependencies": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/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=", - "dev": true - }, - "node_modules/gulp-clean/node_modules/gulp-util": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", - "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", - "dev": true, - "dependencies": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/gulp-clean/node_modules/gulp-util/node_modules/through2": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "dependencies": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - }, - "node_modules/gulp-clean/node_modules/has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", - "dev": true, - "dependencies": { - "ansi-regex": "^0.2.0" - }, - "bin": { - "has-ansi": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/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/gulp-clean/node_modules/minimist": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", - "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", - "dev": true - }, - "node_modules/gulp-clean/node_modules/object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "node_modules/gulp-clean/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/gulp-clean/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "node_modules/gulp-clean/node_modules/strip-ansi": { - "version": "0.3.0", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", - "dev": true, - "dependencies": { - "ansi-regex": "^0.2.1" - }, - "bin": { - "strip-ansi": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", - "dev": true, - "bin": { - "supports-color": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-clean/node_modules/through2": { - "version": "0.4.2", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", - "dev": true, - "dependencies": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - } - }, - "node_modules/gulp-clean/node_modules/through2/node_modules/xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "dependencies": { - "object-keys": "~0.4.0" - }, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/gulp-clean/node_modules/vinyl": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", - "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", - "dev": true, - "dependencies": { - "clone-stats": "~0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/gulp-clean/node_modules/xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", - "dev": true, - "dependencies": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" - }, - "bin": { - "gulp": "bin/gulp.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-cli/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-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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/gulp-cli/node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "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=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "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/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "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=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, - "node_modules/gulp-cli/node_modules/yargs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", - "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", - "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.1" - } - }, - "node_modules/gulp-cli/node_modules/yargs-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", - "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", - "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" - } - }, - "node_modules/gulp-concat": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", - "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", - "dev": true, - "dependencies": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-concat/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-concat/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/gulp-connect": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", - "dev": true, - "dependencies": { - "ansi-colors": "^2.0.5", - "connect": "^3.6.6", - "connect-livereload": "^0.6.0", - "fancy-log": "^1.3.2", - "map-stream": "^0.0.7", - "send": "^0.16.2", - "serve-index": "^1.9.1", - "serve-static": "^1.13.2", - "tiny-lr": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-connect/node_modules/ansi-colors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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=", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/gulp-connect/node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "node_modules/gulp-connect/node_modules/mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true, - "bin": { - "mime": "cli.js" - } - }, - "node_modules/gulp-connect/node_modules/send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/gulp-connect/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/gulp-connect/node_modules/statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/gulp-eslint": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-4.0.2.tgz", - "integrity": "sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg==", - "dev": true, - "dependencies": { - "eslint": "^4.0.0", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.0" - } - }, - "node_modules/gulp-eslint/node_modules/ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true, - "peerDependencies": { - "ajv": "^5.0.0" - } - }, - "node_modules/gulp-eslint/node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "node_modules/gulp-eslint/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "node_modules/gulp-eslint/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/gulp-eslint/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/gulp-eslint/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-eslint/node_modules/eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, - "dependencies": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/gulp-eslint/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "dependencies": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-eslint/node_modules/external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "dependencies": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/gulp-eslint/node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "dependencies": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-eslint/node_modules/flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "dependencies": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-eslint/node_modules/ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "node_modules/gulp-eslint/node_modules/inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "node_modules/gulp-eslint/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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/gulp-eslint/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/gulp-eslint/node_modules/mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "node_modules/gulp-eslint/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/gulp-eslint/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/gulp-eslint/node_modules/slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/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/gulp-eslint/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-eslint/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-eslint/node_modules/table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "dependencies": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "node_modules/gulp-footer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gulp-footer/-/gulp-footer-2.0.2.tgz", - "integrity": "sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g==", - "dev": true, - "dependencies": { - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.6.2", - "map-stream": "0.0.7" - } - }, - "node_modules/gulp-footer/node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "node_modules/gulp-footer/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-footer/node_modules/lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "dependencies": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "node_modules/gulp-footer/node_modules/lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "dependencies": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" - } - }, - "node_modules/gulp-footer/node_modules/lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" - } - }, - "node_modules/gulp-footer/node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "node_modules/gulp-header": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.9.tgz", - "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==", - "dev": true, - "dependencies": { - "concat-with-sourcemaps": "^1.1.0", - "lodash.template": "^4.5.0", - "map-stream": "0.0.7", - "through2": "^2.0.0" - } - }, - "node_modules/gulp-header/node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "node_modules/gulp-header/node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/gulp-header/node_modules/lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/gulp-header/node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, - "node_modules/gulp-header/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-header/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/gulp-if": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", - "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", - "dev": true, - "dependencies": { - "gulp-match": "^1.1.0", - "ternary-stream": "^3.0.0", - "through2": "^3.0.1" - } - }, - "node_modules/gulp-if/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/gulp-if/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "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=", - "dev": true, - "dependencies": { - "through2": "^0.6.3" - } - }, - "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=", - "dev": true - }, - "node_modules/gulp-js-escape/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/gulp-js-escape/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "node_modules/gulp-js-escape/node_modules/through2": { - "version": "0.6.5", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "dev": true, - "dependencies": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } - }, - "node_modules/gulp-match": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", - "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.3" - } - }, - "node_modules/gulp-replace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", - "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", - "dev": true, - "dependencies": { - "istextorbinary": "2.2.1", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/gulp-replace/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-shell": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", - "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", - "dev": true, - "dependencies": { - "chalk": "^3.0.0", - "fancy-log": "^1.3.3", - "lodash.template": "^4.5.0", - "plugin-error": "^1.0.1", - "through2": "^3.0.1", - "tslib": "^1.10.0" - }, - "engines": { - "node": ">=10.0.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", - "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/gulp-shell/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-shell/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/gulp-shell/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/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/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/gulp-shell/node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "node_modules/gulp-shell/node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/gulp-shell/node_modules/lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "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", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-shell/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "node_modules/gulp-sourcemaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", - "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", - "dev": true, - "dependencies": { - "@gulp-sourcemaps/identity-map": "^2.0.1", - "@gulp-sourcemaps/map-sources": "^1.0.0", - "acorn": "^6.4.1", - "convert-source-map": "^1.0.0", - "css": "^3.0.0", - "debug-fabulous": "^1.0.0", - "detect-newline": "^2.0.0", - "graceful-fs": "^4.0.0", - "source-map": "^0.6.0", - "strip-bom-string": "^1.0.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gulp-sourcemaps/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/gulp-sourcemaps/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-sourcemaps/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/gulp-sourcemaps/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/gulp-terser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.0.1.tgz", - "integrity": "sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw==", - "dev": true, - "dependencies": { - "plugin-error": "^1.0.1", - "terser": "5.4.0", - "through2": "^4.0.2", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "dev": true, - "dependencies": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true - }, - "node_modules/gulp-util/node_modules/dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/gulp-util/node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "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.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "dependencies": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^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", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "dependencies": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-util/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/gulp-util/node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/gulp-util/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "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=", - "dev": true, - "dependencies": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", - "dev": true, - "dependencies": { - "glogg": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/gzip-size/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - } - }, - "node_modules/handlebars/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/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "dependencies": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "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" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", - "dev": true, - "dependencies": { - "sparkles": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/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/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-base/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/hash-base/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==", - "dev": true - }, - "node_modules/hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true, - "optional": true - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hast-util-is-element": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", - "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-sanitize": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz", - "integrity": "sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA==", - "dev": true, - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz", - "integrity": "sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw==", - "dev": true, - "dependencies": { - "ccount": "^1.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-is-element": "^1.0.0", - "hast-util-whitespace": "^1.0.0", - "html-void-elements": "^1.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0", - "stringify-entities": "^3.0.1", - "unist-util-is": "^4.0.0", - "xtend": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", - "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "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/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/html-void-elements": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", - "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.0-beta.5.2", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", - "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/https-proxy-agent/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/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true, - "optional": true - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "optional": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "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": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "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/inquirer": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.1.tgz", - "integrity": "sha512-hUDjc3vBkh/uk1gPfMAD/7Z188Q8cvTGl0nxwaCdwSbzFh6ZKkZh+s2ozVxbE5G9ZNRyeY0+lgbAIOUFsFf98w==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.3.0", - "run-async": "^2.4.0", - "rxjs": "^6.6.6", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/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/inquirer/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/inquirer/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/inquirer/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/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/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/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor/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-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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-data-descriptor/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-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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "dependencies": { - "is-primitive": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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/is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "dependencies": { - "is-unc-path": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "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=", - "dev": true - }, - "node_modules/is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true - }, - "node_modules/is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", - "dev": true, - "dependencies": { - "protocols": "^1.1.0" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", - "dev": true, - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", - "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.0", - "es-abstract": "^1.17.4", - "foreach": "^2.0.5", - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "dependencies": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "node_modules/is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "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==", - "dev": true, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true - }, - "node_modules/istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "dependencies": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "istanbul": "lib/cli.js" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true, - "engines": { - "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-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "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/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "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", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul/node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "node_modules/istanbul/node_modules/escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/istanbul/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul/node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "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=", - "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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/istanbul/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "node_modules/istanbul/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=", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", - "dev": true, - "dependencies": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/jake": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", - "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", - "dev": true, - "dependencies": { - "async": "0.9.x", - "chalk": "^2.4.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-diff": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.2.tgz", - "integrity": "sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.0.1", - "jest-get-type": "^27.0.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff/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/jest-diff/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/jest-diff/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/jest-diff/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/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", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.1.tgz", - "integrity": "sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz", - "integrity": "sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.0.2", - "jest-get-type": "^27.0.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-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/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/jest-matcher-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/jest-matcher-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/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", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.2.tgz", - "integrity": "sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.0.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jest-message-util/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/jest-message-util/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/jest-message-util/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/jest-message-util/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/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/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-message-util/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/jest-regex-util": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.1.tgz", - "integrity": "sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "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", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "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=", - "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=", - "dev": true - }, - "node_modules/json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/just-clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-1.0.2.tgz", - "integrity": "sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ=" - }, - "node_modules/just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", - "dev": true - }, - "node_modules/just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", - "dev": true - }, - "node_modules/karma": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz", - "integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==", - "dev": true, - "dependencies": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "^1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.3.0", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^3.1.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.28", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-babel-preprocessor": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", - "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "@babel/core": "7" - } - }, - "node_modules/karma-browserstack-launcher": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.4.0.tgz", - "integrity": "sha512-bUQK84U+euDfOUfEjcF4IareySMOBNRLrrl9q6cttIe8f011Ir6olLITTYMOJDcGY58wiFIdhPHSPd9Pi6+NfQ==", - "dev": true, - "dependencies": { - "browserstack": "~1.5.1", - "browserstacktunnel-wrapper": "~2.0.2", - "q": "~1.5.0" - } - }, - "node_modules/karma-chai": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", - "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.2.tgz", - "integrity": "sha512-zge5qiGEIKDdzWciQwP4p0LSac4k/L6VfrBsERMUn5mpDvxhv1sPVOrSlpzpi70T7NhuEy4bgnpAKIYuumIMCw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.1", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", - "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/mattlewis92" - } - }, - "node_modules/karma-coverage/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/karma-coverage/node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/karma-coverage/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/karma-coverage/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/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=", - "dev": true, - "dependencies": { - "es5-shim": "^4.0.5" - } - }, - "node_modules/karma-firefox-launcher": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.1.tgz", - "integrity": "sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q==", - "dev": true, - "dependencies": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" - } - }, - "node_modules/karma-firefox-launcher/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "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=", - "dev": true, - "dependencies": { - "lodash": "^4.6.1" - } - }, - "node_modules/karma-mocha": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.1.tgz", - "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.3" - } - }, - "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=", - "dev": true, - "dependencies": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "strip-ansi": "^4.0.0" - } - }, - "node_modules/karma-mocha-reporter/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-mocha-reporter/node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true - }, - "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=", - "dev": true - }, - "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=", - "dev": true - }, - "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=", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/karma-sourcemap-loader": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz", - "integrity": "sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2" - } - }, - "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=", - "dev": true, - "dependencies": { - "colors": "^1.1.2" - }, - "peerDependencies": { - "karma": ">=0.9" - } - }, - "node_modules/karma-webpack": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-3.0.5.tgz", - "integrity": "sha1-H/HjppD7c66V7pX5q1jzQc/HtA8=", - "dev": true, - "dependencies": { - "async": "^2.0.0", - "babel-runtime": "^6.0.0", - "loader-utils": "^1.0.0", - "lodash": "^4.0.0", - "source-map": "^0.5.6", - "webpack-dev-middleware": "^2.0.6" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/karma-webpack/node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/karma-webpack/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/karma-webpack/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/karma/node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/karma/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "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/karma/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/karma/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/karma/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/karma/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/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", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/keyv": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", - "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "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/konan": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/konan/-/konan-2.1.1.tgz", - "integrity": "sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.10.5", - "@babel/traverse": "^7.10.5" - } - }, - "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=", - "dev": true, - "dependencies": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "bin": { - "lcov-parse": "bin/cli.js" - } - }, - "node_modules/lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", - "dev": true, - "dependencies": { - "flush-write-stream": "^1.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "dependencies": { - "leven": "^3.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/liftoff": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", - "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", - "dev": true, - "dependencies": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/lighthouse-logger": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.2.0.tgz", - "integrity": "sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw==", - "dev": true, - "dependencies": { - "debug": "^2.6.8", - "marky": "^1.2.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", - "dev": true - }, - "node_modules/live-connect-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-2.0.0.tgz", - "integrity": "sha512-Xhrj1JU5LoLjJuujjTlvDfc/n3Shzk2hPlYmLdCx/lsltFFVuCFa9uM8u5mcHlmOUKP5pu9I54bAITxZBMHoXg==", - "dependencies": { - "tiny-hashes": "1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "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": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "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=", - "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=", - "dev": true - }, - "node_modules/lodash._escapehtmlchar": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", - "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", - "dev": true, - "dependencies": { - "lodash._htmlescapes": "~2.4.1" - } - }, - "node_modules/lodash._escapestringchar": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", - "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", - "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=", - "dev": true - }, - "node_modules/lodash._htmlescapes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", - "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", - "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=", - "dev": true - }, - "node_modules/lodash._isnative": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", - "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", - "dev": true - }, - "node_modules/lodash._objecttypes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", - "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", - "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=", - "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=", - "dev": true - }, - "node_modules/lodash._reinterpolate": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", - "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", - "dev": true - }, - "node_modules/lodash._reunescapedhtml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", - "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", - "dev": true, - "dependencies": { - "lodash._htmlescapes": "~2.4.1", - "lodash.keys": "~2.4.1" - } - }, - "node_modules/lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", - "dev": true - }, - "node_modules/lodash._shimkeys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", - "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", - "dev": true, - "dependencies": { - "lodash._objecttypes": "~2.4.1" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true, - "optional": 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=", - "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=", - "dev": true - }, - "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=", - "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=", - "dev": true - }, - "node_modules/lodash.escape": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", - "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", - "dev": true, - "dependencies": { - "lodash._escapehtmlchar": "~2.4.1", - "lodash._reunescapedhtml": "~2.4.1", - "lodash.keys": "~2.4.1" - } - }, - "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=", - "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=", - "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=", - "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=", - "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=", - "dev": true - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", - "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=", - "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=", - "dev": true - }, - "node_modules/lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", - "dev": true, - "dependencies": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" - } - }, - "node_modules/lodash.keys/node_modules/lodash.isobject": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", - "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", - "dev": true, - "dependencies": { - "lodash._objecttypes": "~2.4.1" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=", - "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=", - "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=", - "dev": true - }, - "node_modules/lodash.template": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", - "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", - "dev": true, - "dependencies": { - "lodash._escapestringchar": "~2.4.1", - "lodash._reinterpolate": "~2.4.1", - "lodash.defaults": "~2.4.1", - "lodash.escape": "~2.4.1", - "lodash.keys": "~2.4.1", - "lodash.templatesettings": "~2.4.1", - "lodash.values": "~2.4.1" - } - }, - "node_modules/lodash.template/node_modules/lodash.defaults": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", - "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", - "dev": true, - "dependencies": { - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" - } - }, - "node_modules/lodash.templatesettings": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", - "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "~2.4.1", - "lodash.escape": "~2.4.1" - } - }, - "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=", - "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=", - "dev": true - }, - "node_modules/lodash.values": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", - "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", - "dev": true, - "dependencies": { - "lodash.keys": "~2.4.1" - } - }, - "node_modules/lodash.zip": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", - "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", - "dev": true - }, - "node_modules/log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true, - "engines": { - "node": ">=0.8.6" - } - }, - "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==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/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/log-symbols/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/log-symbols/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/log-symbols/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/log-symbols/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/log-symbols/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/log4js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", - "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", - "dev": true, - "dependencies": { - "date-format": "^3.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.1", - "rfdc": "^1.1.4", - "streamroller": "^2.2.4" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/log4js/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/log4js/node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/log4js/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/loglevel": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", - "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/loglevel-plugin-prefix": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", - "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", - "dev": true - }, - "node_modules/loglevelnext": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha1-NvxPWZbWZA9Tn/IDuoGWQWgNdaI=", - "dev": true, - "dependencies": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", - "dev": true - }, - "node_modules/longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/longest-streak": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", - "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "dependencies": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "dev": true, - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "optional": true, - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "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=", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "dev": true, - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/marky": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.2.tgz", - "integrity": "sha512-k1dB2HNeaNyORco8ulVEhctyEGkKHb2YWAhDsxeFlW2nROIirsctBYzKwwS3Vza+sKTS1zO4Z+n9/+9WbGLIxQ==", - "dev": true - }, - "node_modules/matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", - "dev": true, - "dependencies": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" - }, - "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=", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", - "dev": true, - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz", - "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^4.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz", - "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", - "dev": true, - "dependencies": { - "mdast-util-gfm-autolink-literal": "^0.1.0", - "mdast-util-gfm-strikethrough": "^0.2.0", - "mdast-util-gfm-table": "^0.1.0", - "mdast-util-gfm-task-list-item": "^0.1.0", - "mdast-util-to-markdown": "^0.6.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz", - "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", - "dev": true, - "dependencies": { - "ccount": "^1.0.0", - "mdast-util-find-and-replace": "^1.1.0", - "micromark": "^2.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz", - "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", - "dev": true, - "dependencies": { - "mdast-util-to-markdown": "^0.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz", - "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", - "dev": true, - "dependencies": { - "markdown-table": "^2.0.0", - "mdast-util-to-markdown": "~0.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz", - "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", - "dev": true, - "dependencies": { - "mdast-util-to-markdown": "~0.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "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=", - "dev": true, - "dependencies": { - "mdast-util-to-string": "^1.0.0" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", - "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", - "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "longest-streak": "^2.0.0", - "mdast-util-to-string": "^2.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.0.0", - "zwitch": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown/node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", - "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", - "dev": true - }, - "node_modules/mdast-util-toc": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-5.1.0.tgz", - "integrity": "sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.3", - "@types/unist": "^2.0.3", - "extend": "^3.0.2", - "github-slugger": "^1.2.1", - "mdast-util-to-string": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-toc/node_modules/github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", - "dev": true, - "dependencies": { - "emoji-regex": ">=6.0.0 <=6.1.1" - } - }, - "node_modules/mdast-util-toc/node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mem/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/memoizee": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.45", - "es6-weak-map": "^2.0.2", - "event-emitter": "^0.3.5", - "is-promise": "^2.1", - "lru-queue": "0.1", - "next-tick": "1", - "timers-ext": "^0.1.5" - } - }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/memory-fs/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/meow": { - "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "dependencies": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/meow/node_modules/camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/meow/node_modules/camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "dependencies": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=" - }, - "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", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz", - "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", - "dev": true, - "dependencies": { - "micromark": "~2.11.0", - "micromark-extension-gfm-autolink-literal": "~0.5.0", - "micromark-extension-gfm-strikethrough": "~0.6.5", - "micromark-extension-gfm-table": "~0.4.0", - "micromark-extension-gfm-tagfilter": "~0.3.0", - "micromark-extension-gfm-task-list-item": "~0.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz", - "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", - "dev": true, - "dependencies": { - "micromark": "~2.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz", - "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", - "dev": true, - "dependencies": { - "micromark": "~2.11.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz", - "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", - "dev": true, - "dependencies": { - "micromark": "~2.11.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz", - "integrity": "sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz", - "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", - "dev": true, - "dependencies": { - "micromark": "~2.11.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark/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/micromark/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/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/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-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/micromatch/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/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/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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dependencies": { - "mime-db": "1.44.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "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_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/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/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "dependencies": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/mocha/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "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": "*" - } - }, - "node_modules/mocha/node_modules/he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/mocha/node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "node_modules/mocha/node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/module-deps-sortable": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/module-deps-sortable/-/module-deps-sortable-5.0.3.tgz", - "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", - "dev": true, - "dependencies": { - "browser-resolve": "^1.7.0", - "cached-path-relative": "^1.0.0", - "concat-stream": "~1.5.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "konan": "^2.1.1", - "readable-stream": "^2.0.2", - "resolve": "^1.1.3", - "standard-version": "^9.0.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "module-deps": "bin/cmd.js" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/module-deps-sortable/node_modules/concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "~2.0.0", - "typedarray": "~0.0.5" - } - }, - "node_modules/module-deps-sortable/node_modules/concat-stream/node_modules/process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "node_modules/module-deps-sortable/node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/module-deps-sortable/node_modules/concat-stream/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/module-deps-sortable/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/module-deps-sortable/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dev": true, - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/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, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", - "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": "http://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": "http://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", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true, - "optional": true - }, - "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true, - "optional": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true - }, - "node_modules/ncp": { - "version": "0.4.2", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", - "dev": true, - "bin": { - "ncp": "bin/ncp" - } - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "node_modules/nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", - "dev": true, - "dependencies": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/nise/node_modules/@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - } - }, - "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=", - "dev": true - }, - "node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/node-libs-browser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "node_modules/node-libs-browser/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - }, - "node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "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/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/now-and-later": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", - "dev": true, - "dependencies": { - "once": "^1.3.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/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/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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", - "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-keys": { - "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" - } - }, - "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=", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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=", - "dev": true, - "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "dependencies": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.omit/node_modules/for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/opn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", - "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", - "deprecated": "The package has been renamed to `open`", - "dev": true, - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "dependencies": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "node_modules/optimist/node_modules/minimist": { - "version": "0.0.10", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "node_modules/optimist/node_modules/wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/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/ora/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/ora/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/ora/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/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/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/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=", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.1" - } - }, - "node_modules/ordered-read-streams/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "node_modules/os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "dependencies": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/os-locale/node_modules/execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "dependencies": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, - "dependencies": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dev": true, - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-github-repo-url": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", - "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", - "dev": true - }, - "node_modules/parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "dependencies": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-glob/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-glob/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-path": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz", - "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "node_modules/parse-path/node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/parse-url": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.3.tgz", - "integrity": "sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A==", - "dev": true, - "dependencies": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.0.1", - "parse-path": "^4.0.0", - "protocols": "^1.4.0" - } - }, - "node_modules/parse-url/node_modules/normalize-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", - "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "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/path-is-absolute": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=" - }, - "node_modules/path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/pbkdf2-compat": { - "version": "2.0.1", - "resolved": "http://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", - "integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=", - "dev": true - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "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=", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "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-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/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==", - "dev": true, - "dependencies": { - "ansi-wrap": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.4.tgz", - "integrity": "sha512-/tZY0PXExXXnNhKv3TOvZAOUYRyuqcCbBm2c17YMDK0PlVII3K7/LKdt3ScHL+hhouddjUWi+1sKDf9xXW+8YA==", - "dev": true, - "optional": true, - "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-modules": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz", - "integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==", - "dev": true, - "optional": true, - "dependencies": { - "generic-names": "^2.0.1", - "icss-replace-symbols": "^1.1.0", - "lodash.camelcase": "^4.3.0", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "string-hash": "^1.1.1" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "optional": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "optional": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "optional": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "optional": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dev": true, - "optional": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true, - "optional": true - }, - "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=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pretty-format": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.2.tgz", - "integrity": "sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==", - "dev": true, - "dependencies": { - "@jest/types": "^27.0.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pretty-ms": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", - "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==", - "dev": true, - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/printj": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", - "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", - "dev": true, - "bin": { - "printj": "bin/printj.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "dev": true, - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==", - "dev": true - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "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==", - "dev": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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/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/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/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/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/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/puppeteer-core/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/query-selector-shadow-dom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz", - "integrity": "sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg==", - "dev": true - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "dependencies": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/randomatic/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "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=", - "dev": true, - "dependencies": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "dependencies": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/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=", - "dev": true, - "dependencies": { - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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": ">= 6" - } - }, - "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==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "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.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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==", - "dev": true, - "dependencies": { - "minimatch": "3.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "dependencies": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/redent/node_modules/indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "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==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "dependencies": { - "is-equal-shallow": "^0.1.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "node_modules/regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/remark": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", - "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", - "dev": true, - "dependencies": { - "remark-parse": "^9.0.0", - "remark-stringify": "^9.0.0", - "unified": "^9.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz", - "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", - "dev": true, - "dependencies": { - "mdast-util-gfm": "^0.1.0", - "micromark-extension-gfm": "^0.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-html": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-13.0.1.tgz", - "integrity": "sha512-K5KQCXWVz+harnyC+UVM/J9eJWCgjYRqFeZoZf2NgP0iFbuuw/RgMZv3MA34b/OEpGnstl3oiOUtZzD3tJ+CBw==", - "dev": true, - "dependencies": { - "hast-util-sanitize": "^3.0.0", - "hast-util-to-html": "^7.0.0", - "mdast-util-to-hast": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", - "dev": true, - "dependencies": { - "mdast-util-from-markdown": "^0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-reference-links": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-5.0.0.tgz", - "integrity": "sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ==", - "dev": true, - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", - "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", - "dev": true, - "dependencies": { - "mdast-util-to-markdown": "^0.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-toc": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-7.2.0.tgz", - "integrity": "sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.3", - "mdast-util-toc": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/remove-bom-buffer/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/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=", - "dev": true, - "dependencies": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remove-bom-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/remove-bom-stream/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "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=", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/replacestream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", - "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/replacestream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "dependencies": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-uncached/node_modules/resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", - "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", - "dev": true - }, - "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=", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", - "dev": true, - "dependencies": { - "value-or-function": "^3.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "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==", - "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - } - }, - "node_modules/resq": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resq/-/resq-1.10.0.tgz", - "integrity": "sha512-hCUd0xMalqtPDz4jXIqs0M5Wnv/LZXN8h7unFOo4/nvExT9dDPbhwd3udRxLlp0HgBnHcV009UlduE9NZi7A6w==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^2.0.1" - } - }, - "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=", - "dev": true - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rgb2hex": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", - "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", - "dev": true - }, - "node_modules/right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "dependencies": { - "align-text": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, - "dependencies": { - "glob": "^7.0.5" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "node_modules/rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "dependencies": { - "rx-lite": "*" - } - }, - "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/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==" - }, - "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=", - "dev": true - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "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" - } - }, - "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/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=", - "dev": true, - "dependencies": { - "sver-compat": "^1.5.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serialize-error": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", - "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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=", - "dev": true - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "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=", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/side-channel": { - "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", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/sinon": { - "version": "4.5.0", - "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", - "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", - "dev": true, - "dependencies": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/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/slice-ansi/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/slice-ansi/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/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/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/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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/socket.io": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", - "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.8", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.1", - "engine.io": "~4.1.0", - "socket.io-adapter": "~2.1.0", - "socket.io-parser": "~4.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", - "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", - "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==", - "dev": true, - "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/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/socket.io-parser/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/socket.io/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/socket.io/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/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", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "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" - } - }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true, - "optional": true - }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split2": { - "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/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/standard-version": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.3.0.tgz", - "integrity": "sha512-cYxxKXhYfI3S9+CA84HmrJa9B88H56V5FQ302iFF2TNwJukJCNoU8FgWt+11YtwKFXRkQQFpepC2QOF7aDq2Ow==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "conventional-changelog": "3.1.24", - "conventional-changelog-config-spec": "2.1.0", - "conventional-changelog-conventionalcommits": "4.5.0", - "conventional-recommended-bump": "6.1.0", - "detect-indent": "^6.0.0", - "detect-newline": "^3.1.0", - "dotgitignore": "^2.1.0", - "figures": "^3.1.0", - "find-up": "^5.0.0", - "fs-access": "^1.0.1", - "git-semver-tags": "^4.0.0", - "semver": "^7.1.1", - "stringify-package": "^1.0.1", - "yargs": "^16.0.0" - }, - "bin": { - "standard-version": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/standard-version/node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/standard-version/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/standard-version/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/standard-version/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/standard-version/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "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=", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/stream-array/-/stream-array-1.1.2.tgz", - "integrity": "sha1-nl9zRfITfDDuO0mLkRToC1K7frU=", - "dev": true, - "dependencies": { - "readable-stream": "~2.1.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-array/node_modules/process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "node_modules/stream-array/node_modules/readable-stream": { - "version": "2.1.5", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", - "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", - "dev": true, - "dependencies": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-array/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "node_modules/stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-buffers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", - "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true - }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/stream-http/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "node_modules/streamroller": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", - "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", - "dev": true, - "dependencies": { - "date-format": "^2.1.0", - "debug": "^4.1.1", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/streamroller/node_modules/date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/streamroller/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/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": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/streamroller/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/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/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", - "dev": true, - "optional": 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=", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "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.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", - "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "node_modules/string.prototype.trimend/node_modules/es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend/node_modules/is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend/node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend/node_modules/object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", - "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "node_modules/string.prototype.trimstart/node_modules/es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimstart/node_modules/is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimstart/node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimstart/node_modules/object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/stringify-entities": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", - "integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==", - "dev": true, - "dependencies": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-package": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", - "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", - "dev": true - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "dependencies": { - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "dependencies": { - "get-stdin": "^4.0.1" - }, - "bin": { - "strip-indent": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "dependencies": { - "minimist": "^1.1.0" - } - }, - "node_modules/suffix": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz", - "integrity": "sha1-zFgjFkag7xEC95R47zqSSP2chC8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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/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=", - "dev": true, - "dependencies": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tapable": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", - "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "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=", - "dev": true, - "dependencies": { - "rimraf": "~2.5.2" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ternary-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", - "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", - "dev": true, - "dependencies": { - "duplexify": "^4.1.1", - "fork-stream": "^0.0.4", - "merge-stream": "^2.0.0", - "through2": "^3.0.1" - } - }, - "node_modules/ternary-stream/node_modules/duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "node_modules/ternary-stream/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ternary-stream/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "node_modules/terser": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.4.0.tgz", - "integrity": "sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==", - "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "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/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", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "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=", - "dev": true - }, - "node_modules/textextensions": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", - "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dev": true, - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", - "dev": true, - "dependencies": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - } - }, - "node_modules/through2-filter/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2-filter/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "dependencies": { - "setimmediate": "^1.0.4" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dev": true, - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "node_modules/tiny-hashes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tiny-hashes/-/tiny-hashes-1.0.1.tgz", - "integrity": "sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==" - }, - "node_modules/tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "dependencies": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - } - }, - "node_modules/tiny-lr/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/tiny-lr/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/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/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=", - "dev": true, - "dependencies": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/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/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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.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=", - "dev": true, - "dependencies": { - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/to-through/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/to-through/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "dev": true - }, - "node_modules/trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-off-newlines": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", - "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, - "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/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/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "node_modules/typescript-compare": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", - "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", - "dependencies": { - "typescript-logic": "^0.0.0" - } - }, - "node_modules/typescript-logic": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", - "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==" - }, - "node_modules/typescript-tuple": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", - "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", - "dependencies": { - "typescript-compare": "^0.0.2" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.28", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", - "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/uglify-js": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", - "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", - "dev": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true - }, - "node_modules/uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "dependencies": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/uglifyjs-webpack-plugin/node_modules/camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglifyjs-webpack-plugin/node_modules/cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "dependencies": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "node_modules/uglifyjs-webpack-plugin/node_modules/uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "dependencies": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uglifyjs-webpack-plugin/node_modules/wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/uglifyjs-webpack-plugin/node_modules/yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "dependencies": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undertaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", - "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", - "dev": true, - "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", - "dev": true, - "dependencies": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" - } - }, - "node_modules/unist-builder": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", - "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-generated": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", - "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "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=", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unzipper": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.9.15.tgz", - "integrity": "sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", - "dev": true - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", - "dev": true - }, - "node_modules/url-parse": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.0.tgz", - "integrity": "sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "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=", - "engines": { - "node": ">= 0.4.0" - } - }, - "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/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-reporter": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.2.tgz", - "integrity": "sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA==", - "dev": true, - "dependencies": { - "repeat-string": "^1.5.0", - "string-width": "^4.0.0", - "supports-color": "^6.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-sort": "^2.1.2", - "vfile-statistics": "^1.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-reporter/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vfile-sort": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.2.tgz", - "integrity": "sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA==", - "dev": true - }, - "node_modules/vfile-statistics": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.4.tgz", - "integrity": "sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-fs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", - "dev": true, - "dependencies": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-fs/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/vinyl-fs/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "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=", - "dev": true, - "dependencies": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "source-map": "^0.5.1" - } - }, - "node_modules/vinyl/node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vue": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.1.tgz", - "integrity": "sha512-j9fj3PNPMxo2eqOKYjMuss9XBS8ZtmczLY3kPvjcp9d3DbhyNqLYbaMQH18+1pDIzzVvQCQBvIf774LsjjqSKA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.1.1", - "@vue/runtime-dom": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "node_modules/vue-template-compiler": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", - "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", - "dev": true, - "optional": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.1.0" - } - }, - "node_modules/walk": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz", - "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", - "dev": true, - "dependencies": { - "foreachasync": "^3.0.0" - } - }, - "node_modules/watchpack": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", - "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", - "dev": true, - "dependencies": { - "chokidar": "^3.4.0", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "optionalDependencies": { - "watchpack-chokidar2": "^2.0.0" - } - }, - "node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", - "dev": true, - "optional": true, - "dependencies": { - "chokidar": "^2.1.8" - } - }, - "node_modules/watchpack-chokidar2/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/watchpack-chokidar2/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=", - "dev": true, - "optional": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/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, - "optional": 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/watchpack-chokidar2/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, - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "node_modules/watchpack-chokidar2/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, - "optional": 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/watchpack-chokidar2/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, - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/watchpack-chokidar2/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=", - "dev": true, - "optional": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/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=", - "dev": true, - "optional": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/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, - "optional": true - }, - "node_modules/watchpack-chokidar2/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/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, - "optional": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "optional": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/watchpack-chokidar2/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/watchpack-chokidar2/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, - "optional": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webdriver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.7.3.tgz", - "integrity": "sha512-/NfeRPREZPkY7pWVvnlyE2E4cfvl+lQmu9j1vE2GDL+oBwCHn+C5Vxwag6bOiBsrKcBan05Ghhlcl/o2uw9ZUA==", - "dev": true, - "dependencies": { - "@types/node": "^14.14.31", - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "got": "^11.0.2", - "lodash.merge": "^4.6.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/webdriver/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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.7.3", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.7.3.tgz", - "integrity": "sha512-3m18Ax0dKHBT7lMueUNEgYDHMRVodXokmAq/yAH+SqHFUbHdPrHOFK3d/snFZe41f6LHrLLzebVE7rvI4Zr1AA==", - "dev": true, - "dependencies": { - "@types/aria-query": "^4.2.1", - "@types/node": "^14.14.31", - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", - "@wdio/repl": "7.7.3", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.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.7.3", - "devtools-protocol": "^0.0.887710", - "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.7.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/webdriverio/node_modules/@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "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/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/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/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/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/webpack": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", - "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", - "dev": true, - "dependencies": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/webpack-bundle-analyzer": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz", - "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==", - "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1", - "bfj": "^6.1.1", - "chalk": "^2.4.1", - "commander": "^2.18.0", - "ejs": "^2.6.1", - "express": "^4.16.3", - "filesize": "^3.6.1", - "gzip-size": "^5.0.0", - "lodash": "^4.17.15", - "mkdirp": "^0.5.1", - "opener": "^1.5.1", - "ws": "^6.0.0" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 6.14.4" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack-bundle-analyzer/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/webpack-bundle-analyzer/node_modules/ejs": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", - "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-bundle-analyzer/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/webpack-core": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", - "dev": true, - "dependencies": { - "source-list-map": "~0.1.7", - "source-map": "~0.4.1" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/webpack-core/node_modules/source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", - "dev": true - }, - "node_modules/webpack-core/node_modules/source-map": { - "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "2.0.6", - "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", - "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", - "dev": true, - "dependencies": { - "loud-rejection": "^1.6.0", - "memory-fs": "~0.4.1", - "mime": "^2.1.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "url-join": "^2.0.2", - "webpack-log": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha1-pLNM2msitRjbsKsy5WeWLVxypD0=", - "dev": true, - "dependencies": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-log/node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-log/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack-sources/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-stream": { - "version": "3.2.0", - "resolved": "http://registry.npmjs.org/webpack-stream/-/webpack-stream-3.2.0.tgz", - "integrity": "sha1-Oh0WD7EdQXJ7fObzL3IkZPmLIYY=", - "dev": true, - "dependencies": { - "gulp-util": "^3.0.7", - "lodash.clone": "^4.3.2", - "lodash.some": "^4.2.2", - "memory-fs": "^0.3.0", - "through": "^2.3.8", - "vinyl": "^1.1.0", - "webpack": "^1.12.9" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/acorn": { - "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack-stream/node_modules/anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "dependencies": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "node_modules/webpack-stream/node_modules/arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/webpack-stream/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "dependencies": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/browserify-aes": { - "version": "0.4.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", - "integrity": "sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw=", - "dev": true, - "dependencies": { - "inherits": "^2.0.1" - } - }, - "node_modules/webpack-stream/node_modules/browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "dependencies": { - "pako": "~0.2.0" - } - }, - "node_modules/webpack-stream/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/webpack-stream/node_modules/camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "dependencies": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - }, - "optionalDependencies": { - "fsevents": "^1.0.0" - } - }, - "node_modules/webpack-stream/node_modules/cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "dependencies": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "node_modules/webpack-stream/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=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/crypto-browserify": { - "version": "3.3.0", - "resolved": "http://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", - "integrity": "sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw=", - "dev": true, - "dependencies": { - "browserify-aes": "0.4.0", - "pbkdf2-compat": "2.0.1", - "ripemd160": "0.2.0", - "sha.js": "2.2.6" - }, - "engines": { - "node": "*" - } - }, - "node_modules/webpack-stream/node_modules/emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/webpack-stream/node_modules/enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/webpack-stream/node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/webpack-stream/node_modules/expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "dependencies": { - "is-posix-bracket": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/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/webpack-stream/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/webpack-stream/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/webpack-stream/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/webpack-stream/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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/interpret": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", - "integrity": "sha1-/s16GOfOXKar+5U+H4YhOknxYls=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/is-accessor-descriptor/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/webpack-stream/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=", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/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/webpack-stream/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/is-data-descriptor/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/webpack-stream/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/is-descriptor/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/webpack-stream/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/webpack-stream/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/webpack-stream/node_modules/loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "dependencies": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - }, - "node_modules/webpack-stream/node_modules/memory-fs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", - "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/webpack-stream/node_modules/micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "dependencies": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack-stream/node_modules/node-libs-browser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", - "integrity": "sha1-PicsCBnjCJNeJmdECNevDhSRuDs=", - "dev": true, - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.1.4", - "buffer": "^4.9.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "3.3.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "0.0.1", - "os-browserify": "^0.2.0", - "path-browserify": "0.0.0", - "process": "^0.11.0", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.0.5", - "stream-browserify": "^2.0.1", - "stream-http": "^2.3.1", - "string_decoder": "^0.10.25", - "timers-browserify": "^2.0.2", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - } - }, - "node_modules/webpack-stream/node_modules/node-libs-browser/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/webpack-stream/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=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/pako": { - "version": "0.2.9", - "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/webpack-stream/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/readdirp/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/webpack-stream/node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/webpack-stream/node_modules/ripemd160": { - "version": "0.2.0", - "resolved": "http://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", - "integrity": "sha1-K/GYveFnys+lHAqSjoS2i74XH84=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/sha.js": { - "version": "2.2.6", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", - "integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=", - "dev": true, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/webpack-stream/node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack-stream/node_modules/tapable": { - "version": "0.1.10", - "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/webpack-stream/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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-stream/node_modules/uglify-js": { - "version": "2.7.5", - "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", - "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", - "dev": true, - "dependencies": { - "async": "~0.2.6", - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack-stream/node_modules/uglify-js/node_modules/async": { - "version": "0.2.10", - "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/webpack-stream/node_modules/vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "dependencies": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/webpack-stream/node_modules/vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "dependencies": { - "indexof": "0.0.1" - } - }, - "node_modules/webpack-stream/node_modules/watchpack": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", - "integrity": "sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws=", - "dev": true, - "dependencies": { - "async": "^0.9.0", - "chokidar": "^1.0.0", - "graceful-fs": "^4.1.2" - } - }, - "node_modules/webpack-stream/node_modules/watchpack/node_modules/async": { - "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - }, - "node_modules/webpack-stream/node_modules/webpack": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.15.0.tgz", - "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=", - "dev": true, - "dependencies": { - "acorn": "^3.0.0", - "async": "^1.3.0", - "clone": "^1.0.2", - "enhanced-resolve": "~0.9.0", - "interpret": "^0.6.4", - "loader-utils": "^0.2.11", - "memory-fs": "~0.3.0", - "mkdirp": "~0.5.0", - "node-libs-browser": "^0.7.0", - "optimist": "~0.6.0", - "supports-color": "^3.1.0", - "tapable": "~0.1.8", - "uglify-js": "~2.7.3", - "watchpack": "^0.2.1", - "webpack-core": "~0.6.9" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/webpack-stream/node_modules/wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack-stream/node_modules/yargs": { - "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "dependencies": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "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" - } - }, - "node_modules/webpack/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/webpack/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/webpack/node_modules/cliui/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "node_modules/webpack/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/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=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/webpack/node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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/webpack/node_modules/loader-utils/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/webpack/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack/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==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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/webpack/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/webpack/node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/string-width/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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "dependencies": { - "has-flag": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/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=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, - "node_modules/webpack/node_modules/yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "dependencies": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - } - }, - "node_modules/webpack/node_modules/yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "dependencies": { - "camelcase": "^4.1.0" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", - "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-symbol": "^1.0.2" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", - "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.2", - "es-abstract": "^1.17.5", - "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "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": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "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": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "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/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/write/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "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/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "node_modules/yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "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/yarn-install": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz", - "integrity": "sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA=", - "dev": true, - "dependencies": { - "cac": "^3.0.3", - "chalk": "^1.1.3", - "cross-spawn": "^4.0.2" - }, - "bin": { - "yarn-install": "bin/yarn-install.js", - "yarn-remove": "bin/yarn-remove.js" - }, - "engines": { - "node": ">=6" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zip-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", - "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", - "dev": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "plugins/eslint": { - "name": "eslint-plugin-prebid", - "version": "1.0.0", - "dev": true, - "license": "Apache-2.0" - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.5.tgz", - "integrity": "sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - } - }, - "@babel/core": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", - "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.1", - "@babel/parser": "^7.12.3", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", - "dev": true, - "requires": { - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", - "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", - "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", - "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", - "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helpers": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", - "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", - "dev": true, - "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", - "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", - "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.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", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", - "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", - "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.4.tgz", - "integrity": "sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.10.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.10.4", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.10.4", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.10.4", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", - "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } - } - }, - "@babel/runtime-corejs3": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz", - "integrity": "sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q==", - "dev": true, - "requires": { - "core-js-pure": "^3.14.0", - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } - } - }, - "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", - "dev": true - } - } - }, - "@babel/traverse": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", - "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.5", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/generator": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", - "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - } - } - }, - "@babel/types": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", - "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "to-fast-properties": "^2.0.0" - } - }, - "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "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" - } - }, - "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" - } - }, - "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "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 - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@gulp-sourcemaps/identity-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", - "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", - "dev": true, - "requires": { - "acorn": "^6.4.1", - "normalize-path": "^3.0.0", - "postcss": "^7.0.16", - "source-map": "^0.6.0", - "through2": "^3.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "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 - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } - } - }, - "@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=", - "dev": true, - "requires": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@jest/types": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz", - "integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.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" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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" - } - } - } - }, - "@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" - } - }, - "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.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==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", - "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@types/aria-query": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", - "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", - "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/component-emitter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", - "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", - "dev": true - }, - "@types/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", - "dev": true - }, - "@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", - "dev": true - }, - "@types/easy-table": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/easy-table/-/easy-table-0.0.32.tgz", - "integrity": "sha512-zKh0f/ixYFnr3Ldf5ZJTi1ZpnRqAynTTtVyGvWDf/TT12asE8ac98t3/WGWfFdRPp/qsccxg82C/Kl3NPNhqEw==", - "dev": true - }, - "@types/ejs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.6.tgz", - "integrity": "sha512-fj1hi+ZSW0xPLrJJD+YNwIh9GZbyaIepG26E/gXvp8nCa2pYokxUYO1sK9qjGxp2g8ryZYuon7wmjpwE2cyASQ==", - "dev": true - }, - "@types/fibers": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-3.1.0.tgz", - "integrity": "sha512-1o3I9xtk2PZFxwaLCC6gTaBfBZ5rvw/DSZZPK89fwuwO6LNrzSbC6rEs1xI0bQ3fCRWmO+uNJQQeD2J56oTMDg==", - "dev": true - }, - "@types/fs-extra": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", - "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true - }, - "@types/inquirer": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz", - "integrity": "sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==", - "dev": true, - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/json-schema": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", - "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", - "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=", - "dev": true - }, - "@types/keyv": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", - "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.14.170", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", - "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", - "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==", - "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==", - "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==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/mdast": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", - "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", - "dev": true, - "requires": { - "@types/unist": "*" - } - }, - "@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", - "dev": true - }, - "@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, - "@types/node": { - "version": "14.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz", - "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/puppeteer": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.3.tgz", - "integrity": "sha512-3nE8YgR9DIsgttLW+eJf6mnXxq8Ge+27m5SU3knWmrlfl6+KOG0Bf9f7Ua7K+C4BnaTMAh3/UpySqdAYvrsvjg==", - "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==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "@types/stream-buffers": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", - "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", - "dev": true - }, - "@types/which": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.2.tgz", - "integrity": "sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==", - "dev": true - }, - "@types/yargs": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz", - "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", - "dev": true - }, - "@types/yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "@vue/compiler-core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.1.tgz", - "integrity": "sha512-Z1RO3T6AEtAUFf2EqqovFm3ohAeTvFzRtB0qUENW2nEerJfdlk13/LS1a0EgsqlzxmYfR/S/S/gW9PLbFZZxkA==", - "dev": true, - "optional": true, - "requires": { - "@babel/parser": "^7.12.0", - "@babel/types": "^7.12.0", - "@vue/shared": "3.1.1", - "estree-walker": "^2.0.1", - "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, - "optional": true - } - } - }, - "@vue/compiler-dom": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz", - "integrity": "sha512-nobRIo0t5ibzg+q8nC31m+aJhbq8FbWUoKvk6h3Vs1EqTDJaj6lBTcVTq5or8AYht7FbSpdAJ81isbJ1rWNX7A==", - "dev": true, - "optional": true, - "requires": { - "@vue/compiler-core": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "@vue/compiler-sfc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz", - "integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==", - "dev": true, - "optional": true, - "requires": { - "@babel/parser": "^7.13.9", - "@babel/types": "^7.13.0", - "@vue/compiler-core": "3.1.1", - "@vue/compiler-dom": "3.1.1", - "@vue/compiler-ssr": "3.1.1", - "@vue/shared": "3.1.1", - "consolidate": "^0.16.0", - "estree-walker": "^2.0.1", - "hash-sum": "^2.0.0", - "lru-cache": "^5.1.1", - "magic-string": "^0.25.7", - "merge-source-map": "^1.1.0", - "postcss": "^8.1.10", - "postcss-modules": "^4.0.0", - "postcss-selector-parser": "^6.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "@babel/parser": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz", - "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==", - "dev": true, - "optional": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "optional": true, - "requires": { - "yallist": "^3.0.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, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - } - } - }, - "@vue/compiler-ssr": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz", - "integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==", - "dev": true, - "optional": true, - "requires": { - "@vue/compiler-dom": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "@vue/reactivity": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.1.tgz", - "integrity": "sha512-DsH5woNVCcPK1M0RRYVgJEU1GJDU2ASOKpAqW3ppHk+XjoFLCbqc/26RTCgTpJYd9z8VN+79Q1u7/QqgQPbuLQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@vue/shared": "3.1.1" - } - }, - "@vue/runtime-core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.1.1.tgz", - "integrity": "sha512-GboqR02txOtkd9F3Ysd8ltPL68vTCqIx2p/J52/gFtpgb5FG9hvOAPEwFUqxeEJRu7ResvQnmdOHiEycGPCLhQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@vue/reactivity": "3.1.1", - "@vue/shared": "3.1.1" - } - }, - "@vue/runtime-dom": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.1.1.tgz", - "integrity": "sha512-o57n/199e/BBAmLRMSXmD2r12Old/h/gf6BgL0RON1NT2pwm6MWaMY4Ul55eyq+FsDILz4jR/UgoPQ9vYB8xcw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@vue/runtime-core": "3.1.1", - "@vue/shared": "3.1.1", - "csstype": "^2.6.8" - } - }, - "@vue/shared": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz", - "integrity": "sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==", - "dev": true, - "optional": true - }, - "@wdio/browserstack-service": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/@wdio/browserstack-service/-/browserstack-service-6.1.15.tgz", - "integrity": "sha512-q8qLa44wGSB3tIuZ0yquvAZqr2W7vEwupWiOd1ct0CSYgd4yX/nLd8oypqJCc8jU1ZwNAhu+V3/6hszvwx+HbA==", - "dev": true, - "requires": { - "@wdio/logger": "6.0.16", - "browserstack-local": "^1.4.5", - "got": "^11.0.2" - } - }, - "@wdio/cli": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-7.7.3.tgz", - "integrity": "sha512-n7XvIoruXlGQGt2dl4dLm/J6he2Int7BOe3gnTxRTddjcqXZ8bv7qvYvNvfXzEg/vVzmUyMW2dQfzpNVoyx/dQ==", - "dev": true, - "requires": { - "@types/ejs": "^3.0.5", - "@types/fs-extra": "^9.0.4", - "@types/inquirer": "^7.3.1", - "@types/lodash.flattendeep": "^4.4.6", - "@types/lodash.pickby": "^4.6.6", - "@types/lodash.union": "^4.6.6", - "@types/recursive-readdir": "^2.2.0", - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.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.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.7.3", - "yargs": "^17.0.0", - "yarn-install": "^1.0.0" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/concise-reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/concise-reporter/-/concise-reporter-7.7.3.tgz", - "integrity": "sha512-2Ix20n48N+lvvU4NzqMP7z+daG748RRsmDqdstCoBrJgXV6frvu38HVHV90U5uKt5Vmp6/QQl05A4OliaNoO9w==", - "dev": true, - "requires": { - "@wdio/reporter": "7.7.3", - "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/config": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.7.3.tgz", - "integrity": "sha512-I8gkb5BjXLe6/9NK7OCA9Mc+A6xeGUqbYTRd4PNKdObE6HomKOxw4plVZCYF0DlD2FCo4OGrvYGmalojFsCMdA==", - "dev": true, - "requires": { - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "deepmerge": "^4.0.0", - "glob": "^7.1.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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.7.3", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-7.7.3.tgz", - "integrity": "sha512-TM1Xd8ioc4TpZwmRStDdk7m6IVOPAEsoyKoqffuRN2pZmrj4jswmvj6qys06ErrVCGWA4skyTYZjhMZf0+V0Zg==", - "dev": true, - "requires": { - "@types/stream-buffers": "^3.0.3", - "@wdio/logger": "7.7.0", - "@wdio/repl": "7.7.3", - "@wdio/runner": "7.7.3", - "@wdio/types": "7.7.3", - "async-exit-hook": "^2.0.1", - "split2": "^3.2.2", - "stream-buffers": "^3.0.2" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-6.0.16.tgz", - "integrity": "sha512-VbH5UnQIG/3sSMV+Y38+rOdwyK9mVA9vuL7iOngoTafHwUjL1MObfN/Cex84L4mGxIgfxCu6GV48iUmSuQ7sqA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/mocha-framework": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-7.7.3.tgz", - "integrity": "sha512-0G9q3z6kuqFJxavm/pZNvO0bhRrZQuPbWf38vQGrbHEP15i8LNI1dDg1R73vb0y1jIbZDSIiuQsQQ6keGWND+w==", - "dev": true, - "requires": { - "@types/mocha": "^8.0.0", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "expect-webdriverio": "^3.0.0", - "mocha": "^8.0.1" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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 - }, - "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" - }, - "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", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "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 - }, - "js-yaml": { - "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" - } - }, - "log-symbols": { - "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.0.0" - } - }, - "mocha": { - "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.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.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==", - "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.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "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" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "@wdio/protocols": { - "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.7.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.7.3.tgz", - "integrity": "sha512-7nhvUa3Zd5Ny9topJGRZwkomlveuO3RIv+jBUHgQ2jiDIGvG9MroHxKEniIbscVSsD32XFOOZY59kSpX1b50VQ==", - "dev": true, - "requires": { - "@wdio/utils": "7.7.3" - } - }, - "@wdio/reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-7.7.3.tgz", - "integrity": "sha512-zAUGgP/FZ3XF5s4RUcDGIAeum3WzkA9ll5lymytxhh/9Jj9/5c77o498ic3RGQlB8FTz+5SVmw08r7g3uekI8g==", - "dev": true, - "requires": { - "@types/node": "^14.14.31", - "@wdio/types": "7.7.3", - "fs-extra": "^10.0.0" - } - }, - "@wdio/runner": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-7.7.3.tgz", - "integrity": "sha512-Jetud2znIkY70lrYvHoyBQVRrIQCzNlfjLpCMMraTeNlCzW3eO82TgnOwpCoJ5cJEg78n8YLIDRcIeZ5yo4asA==", - "dev": true, - "requires": { - "@wdio/config": "7.7.3", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "@wdio/utils": "7.7.3", - "deepmerge": "^4.0.0", - "gaze": "^1.1.2", - "webdriver": "7.7.3", - "webdriverio": "7.7.3" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/spec-reporter": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.7.3.tgz", - "integrity": "sha512-5elsNfZd3kbBaKY5IK5ZmdZsWZNSOCqXnM2fYryAh2RBoXbcXkak4D5PbLehusZhp6CQ7UpXEKf4BDDYfd0ebw==", - "dev": true, - "requires": { - "@types/easy-table": "^0.0.32", - "@wdio/reporter": "7.7.3", - "@wdio/types": "7.7.3", - "chalk": "^4.0.0", - "easy-table": "^1.1.1", - "pretty-ms": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@wdio/sync": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/sync/-/sync-7.7.3.tgz", - "integrity": "sha512-LsI9rvxup6mlMuRCDBrjh674bQt4Rnpzf/xa2obhn3GZL97teSwF5ZaTTeF+cs+MPylqwbHiY7iK+roaubqECw==", - "dev": true, - "requires": { - "@types/fibers": "^3.1.0", - "@types/puppeteer": "^5.4.0", - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3", - "fibers": "^5.0.0", - "webdriverio": "7.7.3" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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/types": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.7.3.tgz", - "integrity": "sha512-ZZBQHCXKjZSQj9pf4df/QhfgQQj0vzm9hkK7YyNM+S+qnW0LExL8qQKLxTlGHDaYxk/+Jrd9pcZrJXRCoSnUaA==", - "dev": true, - "requires": { - "@types/node": "^14.14.31", - "got": "^11.8.1" - } - }, - "@wdio/utils": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.7.3.tgz", - "integrity": "sha512-bvOoE2gve8Z8HFguVw0RMp5BbSmJR4zSr8DwbwnA8RSL3NshKlRk33HWYLmKsxjkH+ZWI2ihFbpvLD4W4imXag==", - "dev": true, - "requires": { - "@wdio/logger": "7.7.0", - "@wdio/types": "7.7.3" - }, - "dependencies": { - "@wdio/logger": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", - "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "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" - } - } - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", - "dev": true - }, - "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 - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - }, - "dependencies": { - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", - "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.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" - } - } - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": 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==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "dev": true, - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", - "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==", - "dev": true, - "requires": { - "archiver-utils": "^2.1.0", - "async": "^3.2.0", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - } - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "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" - } - }, - "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 - }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", - "dev": true, - "requires": { - "make-iterator": "^1.0.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", - "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=", - "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=", - "dev": true - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true - }, - "array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", - "dev": true - }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - } - }, - "array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", - "dev": true, - "requires": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, - "array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", - "dev": true, - "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "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=", - "dev": true - }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - }, - "async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", - "dev": true, - "requires": { - "async-done": "^1.2.2" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "available-typed-arrays": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", - "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", - "dev": true, - "requires": { - "array-filter": "^1.0.0" - } - }, - "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=", - "dev": true - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", - "dev": true - }, - "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=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "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 - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "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=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "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" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } - } - }, - "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==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "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-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - } - } - }, - "babelify": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", - "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true, - "requires": {} - }, - "bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", - "dev": true, - "requires": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" - } - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "dev": true - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "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=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", - "dev": true - }, - "bfj": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", - "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "check-types": "^8.0.3", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - } - }, - "big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "dev": true, - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "binaryextensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", - "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", - "dev": true - }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - }, - "dependencies": { - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "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=", - "dev": true, - "requires": { - "bytes": "1", - "string_decoder": "0.10" - } - }, - "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 - } - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "browserify-sign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", - "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.2", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "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==", - "dev": true - } - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "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 - } - } - }, - "browserstack-local": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.5.tgz", - "integrity": "sha512-0/VdSv2YVXmcnwBb64XThMvjM1HnZJnPdv7CUgQbC5y/N9Wsr0Fu+j1oknE9fC/VPx9CpoSC6CJ0kza42skMSA==", - "dev": true, - "requires": { - "https-proxy-agent": "^4.0.0", - "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==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1", - "unzipper": "^0.9.3" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "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 - } - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.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=", - "dev": true - }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", - "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", - "dev": true - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cac": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz", - "integrity": "sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8=", - "dev": true, - "requires": { - "camelcase-keys": "^3.0.0", - "chalk": "^1.1.3", - "indent-string": "^3.0.0", - "minimist": "^1.2.0", - "read-pkg-up": "^1.0.1", - "suffix": "^0.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "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 - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-lookup": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", - "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", - "dev": true - }, - "cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^2.0.0" - } - }, - "cached-path-relative": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", - "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - }, - "dependencies": { - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "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" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "caniuse-lite": { - "version": "1.0.30001235", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz", - "integrity": "sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "ccount": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", - "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "dev": true, - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true - }, - "character-entities-html4": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", - "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "check-types": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", - "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", - "dev": true - }, - "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" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "chrome-launcher": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.14.0.tgz", - "integrity": "sha512-W//HpflaW6qBGrmuskup7g+XJZN6w03ko9QSIe5CtcTal2u0up5SeReK3Ll1Why4Ey8dPkv8XSodZyHPnGbVHQ==", - "dev": true, - "requires": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - } - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "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==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "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=", - "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=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "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=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "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=", - "dev": true - }, - "collection-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", - "dev": true, - "requires": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "dev": true - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "compress-commons": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", - "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", - "dev": true, - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "concat-with-sourcemaps": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", - "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 - } - } - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - } - }, - "connect-livereload": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", - "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", - "dev": true - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "consolidate": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", - "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", - "dev": true, - "optional": true, - "requires": { - "bluebird": "^3.7.2" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "conventional-changelog": { - "version": "3.1.24", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", - "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.12", - "conventional-changelog-atom": "^2.0.8", - "conventional-changelog-codemirror": "^2.0.8", - "conventional-changelog-conventionalcommits": "^4.5.0", - "conventional-changelog-core": "^4.2.1", - "conventional-changelog-ember": "^2.0.9", - "conventional-changelog-eslint": "^3.0.9", - "conventional-changelog-express": "^2.0.6", - "conventional-changelog-jquery": "^3.0.11", - "conventional-changelog-jshint": "^2.0.9", - "conventional-changelog-preset-loader": "^2.3.4" - } - }, - "conventional-changelog-angular": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", - "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } - }, - "conventional-changelog-atom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", - "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-codemirror": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", - "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-config-spec": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", - "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", - "dev": true - }, - "conventional-changelog-conventionalcommits": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", - "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - } - }, - "conventional-changelog-core": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz", - "integrity": "sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg==", - "dev": true, - "requires": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^4.0.18", - "conventional-commits-parser": "^3.2.0", - "dateformat": "^3.0.0", - "get-pkg-repo": "^1.0.0", - "git-raw-commits": "^2.0.8", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.1.1", - "lodash": "^4.17.15", - "normalize-package-data": "^3.0.0", - "q": "^1.5.1", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0", - "shelljs": "^0.8.3", - "through2": "^4.0.0" - }, - "dependencies": { - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.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=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "conventional-changelog-ember": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", - "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-eslint": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", - "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-express": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", - "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-jquery": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", - "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-jshint": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", - "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } - }, - "conventional-changelog-preset-loader": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", - "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", - "dev": true - }, - "conventional-changelog-writer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", - "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "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" - } - }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "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" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true - }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - } - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.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" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", - "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "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" - } - }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "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" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true - }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - } - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "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" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "conventional-recommended-bump": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", - "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", - "dev": true, - "requires": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^2.3.4", - "conventional-commits-filter": "^2.0.7", - "conventional-commits-parser": "^3.2.0", - "git-raw-commits": "^2.0.8", - "git-semver-tags": "^4.1.1", - "meow": "^8.0.0", - "q": "^1.5.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "2.0.0" } }, "hosted-git-info": { @@ -33312,22 +5010,29 @@ "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "lru-cache": "6.0.0" } }, - "indent-string": { + "load-json-file": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "4.2.6", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lru-cache": { @@ -33336,32 +5041,7 @@ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true - }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "yallist": "4.0.0" } }, "normalize-package-data": { @@ -33370,49 +5050,76 @@ "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", "dev": true, "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "4.0.2", + "resolve": "1.20.0", + "semver": "7.3.5", + "validate-npm-package-license": "3.0.4" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "1.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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "1.3.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=", + "dev": true + }, "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2" } }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "load-json-file": "4.0.0", + "normalize-package-data": "2.5.0", + "path-type": "3.0.0" }, "dependencies": { "hosted-git-info": { @@ -33427,10 +5134,10 @@ "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" + "hosted-git-info": "2.8.9", + "resolve": "1.20.0", + "semver": "5.7.1", + "validate-npm-package-license": "3.0.4" } }, "semver": { @@ -33438,42 +5145,17 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true } } }, "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "find-up": "2.1.0", + "read-pkg": "3.0.0" } }, "semver": { @@ -33482,28 +5164,13 @@ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "lru-cache": "6.0.0" } }, - "strip-indent": { + "strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "yallist": { @@ -33514,13 +5181,149 @@ } } }, + "conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", + "dev": true, + "requires": { + "q": "1.5.1" + } + }, + "conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "dev": true, + "requires": { + "compare-func": "2.0.0", + "q": "1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", + "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", + "dev": true, + "requires": { + "compare-func": "2.0.0", + "conventional-commits-filter": "2.0.7", + "dateformat": "3.0.3", + "handlebars": "4.7.7", + "json-stringify-safe": "5.0.1", + "lodash": "4.17.21", + "meow": "8.1.2", + "semver": "6.3.0", + "split": "1.0.1", + "through2": "4.0.2" + }, + "dependencies": { + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2.3.8" + } + } + } + }, + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "requires": { + "lodash.ismatch": "4.4.0", + "modify-values": "1.0.1" + } + }, + "conventional-commits-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", + "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", + "dev": true, + "requires": { + "JSONStream": "1.3.5", + "is-text-path": "1.0.1", + "lodash": "4.17.21", + "meow": "8.1.2", + "split2": "3.2.2", + "through2": "4.0.2", + "trim-off-newlines": "1.0.1" + } + }, + "conventional-recommended-bump": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", + "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", + "dev": true, + "requires": { + "concat-stream": "2.0.0", + "conventional-changelog-preset-loader": "2.3.4", + "conventional-commits-filter": "2.0.7", + "conventional-commits-parser": "3.2.1", + "git-raw-commits": "2.0.10", + "git-semver-tags": "4.1.1", + "meow": "8.1.2", + "q": "1.5.1" + }, + "dependencies": { + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "3.6.0", + "typedarray": "0.0.6" + } + } + } + }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "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==", "dev": true, "requires": { - "safe-buffer": "~5.1.1" + "safe-buffer": "5.1.2" } }, "cookie": { @@ -33540,27 +5343,35 @@ "dev": true }, "copy-props": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", "dev": true, "requires": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" + "each-props": "1.3.2", + "is-plain-object": "5.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + } } }, "core-js": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.14.0.tgz", - "integrity": "sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==" + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.1.tgz", + "integrity": "sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg==" }, "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", + "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", "dev": true, "requires": { - "browserslist": "^4.8.5", + "browserslist": "4.16.6", "semver": "7.0.0" }, "dependencies": { @@ -33573,9 +5384,9 @@ } }, "core-js-pure": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", - "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==" + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.1.tgz", + "integrity": "sha512-OZuWHDlYcIda8sJLY4Ec6nWq2hRjlyCqCZ+jCflyleMkVt3tPedDVErvHslyS2nbO+SlBFMSBJYvtLMwxnrzjA==" }, "core-util-is": { "version": "1.0.2", @@ -33589,8 +5400,8 @@ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, "requires": { - "object-assign": "^4", - "vary": "^1" + "object-assign": "4.1.1", + "vary": "1.1.2" } }, "coveralls": { @@ -33599,11 +5410,32 @@ "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", "dev": true, "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" + "js-yaml": "3.14.1", + "lcov-parse": "1.0.0", + "log-driver": "1.2.7", + "minimist": "1.2.5", + "request": "2.88.2" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" + } + } } }, "crc-32": { @@ -33612,8 +5444,8 @@ "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", "dev": true, "requires": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" + "exit-on-epipe": "1.0.1", + "printj": "1.1.2" } }, "crc32-stream": { @@ -33622,53 +5454,53 @@ "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", "dev": true, "requires": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" + "crc-32": "1.2.0", + "readable-stream": "3.6.0" } }, "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "bn.js": "4.12.0", + "elliptic": "6.5.4" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.5", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" } }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "criteo-direct-rsa-validate": { @@ -33682,8 +5514,19 @@ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.5", + "which": "1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + } } }, "crypto-browserify": { @@ -33692,17 +5535,17 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "1.0.1", + "browserify-sign": "4.2.1", + "create-ecdh": "4.0.4", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.3", + "pbkdf2": "3.1.2", + "public-encrypt": "4.0.3", + "randombytes": "2.1.0", + "randomfill": "1.0.4" } }, "crypto-js": { @@ -33716,9 +5559,9 @@ "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", "dev": true, "requires": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" + "inherits": "2.0.4", + "source-map": "0.6.1", + "source-map-resolve": "0.6.0" }, "dependencies": { "inherits": { @@ -33739,8 +5582,8 @@ "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", "dev": true, "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" + "atob": "2.1.2", + "decode-uri-component": "0.2.0" } } } @@ -33761,16 +5604,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "optional": true - }, - "csstype": { - "version": "2.6.17", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", - "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "currently-unhandled": { "version": "0.4.1", @@ -33778,7 +5612,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "custom-event": { @@ -33793,8 +5627,8 @@ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "dev": true, "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "es5-ext": "0.10.53", + "type": "1.2.0" } }, "dargs": { @@ -33809,7 +5643,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "date-format": { @@ -33819,14 +5653,10 @@ "dev": true }, "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - } + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true }, "de-indent": { "version": "1.0.2", @@ -33849,32 +5679,32 @@ "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", "dev": true, "requires": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" + "debug": "3.2.7", + "memoizee": "0.4.15", + "object-assign": "4.1.1" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, "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 }, "decamelize-keys": { @@ -33883,8 +5713,16 @@ "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", "dev": true, "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "decamelize": "1.2.0", + "map-obj": "1.0.1" + }, + "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + } } }, "decode-uri-component": { @@ -33899,7 +5737,7 @@ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "requires": { - "mimic-response": "^3.1.0" + "mimic-response": "3.1.0" }, "dependencies": { "mimic-response": { @@ -33916,29 +5754,30 @@ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "^4.0.0" + "type-detect": "4.0.8" } }, "deep-equal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", - "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", - "dev": true, - "requires": { - "es-abstract": "^1.17.5", - "es-get-iterator": "^1.1.0", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.0.5", - "isarray": "^2.0.5", - "object-is": "^1.1.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", + "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "dev": true, + "requires": { + "call-bind": "1.0.2", + "es-get-iterator": "1.1.2", + "get-intrinsic": "1.1.1", + "is-arguments": "1.1.0", + "is-date-object": "1.0.4", + "is-regex": "1.1.3", + "isarray": "2.0.5", + "object-is": "1.1.5", + "object-keys": "1.1.1", + "object.assign": "4.1.2", + "regexp.prototype.flags": "1.3.1", + "side-channel": "1.0.4", + "which-boxed-primitive": "1.0.2", + "which-collection": "1.0.1", + "which-typed-array": "1.1.4" }, "dependencies": { "isarray": { @@ -33967,7 +5806,7 @@ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { - "kind-of": "^5.0.2" + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -33990,13 +5829,13 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" } }, "defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, "define-properties": { @@ -34005,7 +5844,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "object-keys": "1.1.1" } }, "define-property": { @@ -34014,8 +5853,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -34024,7 +5863,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-data-descriptor": { @@ -34033,7 +5872,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-descriptor": { @@ -34042,9 +5881,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" } } } @@ -34072,8 +5911,8 @@ "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "destroy": { @@ -34100,9 +5939,9 @@ "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=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, "detective": { @@ -34111,41 +5950,47 @@ "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "dev": true, "requires": { - "acorn-node": "^1.6.1", - "defined": "^1.0.0", - "minimist": "^1.1.1" + "acorn-node": "1.8.2", + "defined": "1.0.0", + "minimist": "1.2.5" } }, "devtools": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.7.3.tgz", - "integrity": "sha512-MvLCrJqLJXPK+N1En01EdM8wpitQiKaXGlprSsWMZWJU5iy/ljOKF9KRYGf0eQ2ZT3FfjcBgJh4yyLom7wVYeg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.7.4.tgz", + "integrity": "sha512-rkO9k6yOA2XzFTph9y+gO/387653jou0La7QSLd57XTQiM3D/UODqLBt+fMVu8w3fdQzZHVAlIIvP4B8rkXY1Q==", "dev": true, "requires": { - "@types/node": "^14.14.31", + "@types/node": "14.17.4", "@wdio/config": "7.7.3", "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", + "@wdio/protocols": "7.7.4", "@wdio/types": "7.7.3", "@wdio/utils": "7.7.3", - "chrome-launcher": "^0.14.0", - "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" + "chrome-launcher": "0.14.0", + "edge-paths": "2.2.1", + "puppeteer-core": "9.1.1", + "query-selector-shadow-dom": "1.0.0", + "ua-parser-js": "0.7.28", + "uuid": "8.3.2" }, "dependencies": { + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true + }, "@wdio/logger": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" } }, "ansi-styles": { @@ -34154,7 +5999,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -34163,8 +6008,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -34173,7 +6018,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -34194,15 +6039,15 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } }, "devtools-protocol": { - "version": "0.0.887710", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.887710.tgz", - "integrity": "sha512-ZN57GSHgIoz6opBE4XtUhZvCG4Mjy6n0WxUCcSv8fdHc1TDRlI8IglTzwNMXUKqehFSIEHVxKZcaAoACKWHFBQ==", + "version": "0.0.892017", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.892017.tgz", + "integrity": "sha512-23yn1+zeMBlWiZTtrCViNQt+W+FRDw5rEetI19bMuyKIYeK11xo/dS+Hmuu8ifGJnJvXUU3Y79IoxSPWZWcVOA==", "dev": true }, "di": { @@ -34212,9 +6057,9 @@ "dev": true }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "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 }, "diff-sequences": { @@ -34225,19 +6070,19 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "bn.js": "4.12.0", + "miller-rabin": "4.0.1", + "randombytes": "2.1.0" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -34248,13 +6093,12 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "doctrine": { - "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.3" } }, "doctrine-temporary-fork": { @@ -34263,7 +6107,7 @@ "integrity": "sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA==", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.3" } }, "documentation": { @@ -34275,60 +6119,139 @@ "@babel/core": "7.12.3", "@babel/generator": "7.12.1", "@babel/parser": "7.12.3", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "@vue/compiler-sfc": "^3.0.11", - "ansi-html": "^0.0.7", - "babelify": "^10.0.0", - "chalk": "^2.3.0", - "chokidar": "^3.4.0", - "concat-stream": "^1.6.0", - "diff": "^4.0.1", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5", + "@vue/compiler-sfc": "3.1.2", + "ansi-html": "0.0.7", + "babelify": "10.0.0", + "chalk": "2.4.2", + "chokidar": "3.5.2", + "concat-stream": "1.6.2", + "diff": "4.0.2", "doctrine-temporary-fork": "2.1.0", - "get-port": "^5.0.0", - "git-url-parse": "^11.1.2", + "get-port": "5.1.1", + "git-url-parse": "11.4.4", "github-slugger": "1.2.0", - "glob": "^7.1.2", - "globals-docs": "^2.4.0", - "highlight.js": "^10.7.2", - "ini": "^1.3.5", - "js-yaml": "^3.10.0", - "lodash": "^4.17.10", - "mdast-util-find-and-replace": "^1.1.1", - "mdast-util-inject": "^1.1.0", - "micromatch": "^3.1.5", - "mime": "^2.2.0", - "module-deps-sortable": "^5.0.3", - "parse-filepath": "^1.0.2", - "pify": "^5.0.0", - "read-pkg-up": "^4.0.0", - "remark": "^13.0.0", - "remark-gfm": "^1.0.0", - "remark-html": "^13.0.1", - "remark-reference-links": "^5.0.0", - "remark-toc": "^7.2.0", - "resolve": "^1.8.1", - "stream-array": "^1.1.2", - "strip-json-comments": "^2.0.1", - "tiny-lr": "^1.1.0", - "unist-builder": "^2.0.3", - "unist-util-visit": "^2.0.3", - "vfile": "^4.0.0", - "vfile-reporter": "^6.0.0", - "vfile-sort": "^2.1.0", - "vinyl": "^2.1.0", - "vinyl-fs": "^3.0.2", - "vue-template-compiler": "^2.6.12", - "yargs": "^15.3.1" - }, - "dependencies": { + "glob": "7.1.7", + "globals-docs": "2.4.1", + "highlight.js": "10.7.3", + "ini": "1.3.8", + "js-yaml": "3.14.1", + "lodash": "4.17.21", + "mdast-util-find-and-replace": "1.1.1", + "mdast-util-inject": "1.1.0", + "micromatch": "3.1.10", + "mime": "2.5.2", + "module-deps-sortable": "5.0.3", + "parse-filepath": "1.0.2", + "pify": "5.0.0", + "read-pkg-up": "4.0.0", + "remark": "13.0.0", + "remark-gfm": "1.0.0", + "remark-html": "13.0.1", + "remark-reference-links": "5.0.0", + "remark-toc": "7.2.0", + "resolve": "1.20.0", + "stream-array": "1.1.2", + "strip-json-comments": "2.0.1", + "tiny-lr": "1.1.1", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3", + "vfile": "4.2.1", + "vfile-reporter": "6.0.2", + "vfile-sort": "2.2.2", + "vinyl": "2.2.1", + "vinyl-fs": "3.0.3", + "vue-template-compiler": "2.6.14", + "yargs": "15.4.1" + }, + "dependencies": { + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "7.14.5", + "@babel/generator": "7.12.1", + "@babel/helper-module-transforms": "7.14.5", + "@babel/helpers": "7.14.6", + "@babel/parser": "7.12.3", + "@babel/template": "7.14.5", + "@babel/traverse": "7.14.7", + "@babel/types": "7.14.5", + "convert-source-map": "1.8.0", + "debug": "4.3.1", + "gensync": "1.0.0-beta.2", + "json5": "2.2.0", + "lodash": "4.17.21", + "resolve": "1.20.0", + "semver": "5.7.1", + "source-map": "0.5.7" + } + }, + "@babel/generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", + "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "dev": true, + "requires": { + "@babel/types": "7.14.5", + "jsesc": "2.5.2", + "source-map": "0.5.7" + } + }, + "@babel/parser": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", + "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "dev": true + }, "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": "2.0.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "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.1" + } + } } }, "camelcase": { @@ -34343,9 +6266,9 @@ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "string-width": "4.2.2", + "strip-ansi": "6.0.0", + "wrap-ansi": "6.2.0" } }, "color-convert": { @@ -34354,7 +6277,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -34363,13 +6286,87 @@ "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" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "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.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.1" + } + } + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" + } + }, + "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.2.2" + }, + "dependencies": { + "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.6" + } + } + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "load-json-file": { @@ -34378,10 +6375,10 @@ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.2.6", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" }, "dependencies": { "pify": { @@ -34398,8 +6395,29 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" + } + }, + "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.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "mime": { @@ -34408,13 +6426,19 @@ "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, + "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 + }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.3.0" } }, "parse-json": { @@ -34423,8 +6447,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2" } }, "path-exists": { @@ -34439,7 +6463,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -34462,9 +6486,9 @@ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "load-json-file": "4.0.0", + "normalize-package-data": "2.5.0", + "path-type": "3.0.0" } }, "read-pkg-up": { @@ -34473,10 +6497,16 @@ "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", "dev": true, "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "find-up": "3.0.0", + "read-pkg": "3.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-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -34489,15 +6519,25 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "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" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "4.3.0", + "string-width": "4.2.2", + "strip-ansi": "6.0.0" } }, "y18n": { @@ -34512,17 +6552,17 @@ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "cliui": "6.0.0", + "decamelize": "1.2.0", + "find-up": "4.1.0", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "require-main-filename": "2.0.0", + "set-blocking": "2.0.0", + "string-width": "4.2.2", + "which-module": "2.0.0", + "y18n": "4.0.3", + "yargs-parser": "18.1.3" }, "dependencies": { "find-up": { @@ -34531,8 +6571,8 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "5.0.0", + "path-exists": "4.0.0" } }, "locate-path": { @@ -34541,7 +6581,7 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "4.1.0" } }, "p-locate": { @@ -34550,7 +6590,7 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "2.3.0" } }, "path-exists": { @@ -34567,8 +6607,8 @@ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "camelcase": "5.3.1", + "decamelize": "1.2.0" } } } @@ -34579,10 +6619,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.2", + "void-elements": "2.0.1" } }, "domain-browser": { @@ -34597,7 +6637,7 @@ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { - "is-obj": "^2.0.0" + "is-obj": "2.0.0" } }, "dotgitignore": { @@ -34606,8 +6646,8 @@ "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", "dev": true, "requires": { - "find-up": "^3.0.0", - "minimatch": "^3.0.4" + "find-up": "3.0.0", + "minimatch": "3.0.4" }, "dependencies": { "find-up": { @@ -34616,7 +6656,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "locate-path": { @@ -34625,8 +6665,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "p-locate": { @@ -34635,7 +6675,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.3.0" } }, "path-exists": { @@ -34652,9 +6692,9 @@ "integrity": "sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ==" }, "duplexer": { - "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, "duplexer2": { @@ -34663,7 +6703,7 @@ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -34672,13 +6712,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -34689,10 +6738,10 @@ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.4", + "inherits": "2.0.3", + "readable-stream": "2.3.7", + "stream-shift": "1.0.1" }, "dependencies": { "readable-stream": { @@ -34701,13 +6750,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -34718,8 +6776,8 @@ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0" } }, "easy-table": { @@ -34728,8 +6786,8 @@ "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", "dev": true, "requires": { - "ansi-regex": "^3.0.0", - "wcwidth": ">=1.0.1" + "ansi-regex": "3.0.0", + "wcwidth": "1.0.1" }, "dependencies": { "ansi-regex": { @@ -34746,8 +6804,8 @@ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "edge-paths": { @@ -34756,45 +6814,28 @@ "integrity": "sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw==", "dev": true, "requires": { - "@types/which": "^1.3.2", - "which": "^2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "@types/which": "1.3.2", + "which": "2.0.2" } }, - "editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", - "dev": true - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", - "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", + "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", "dev": true, "requires": { - "jake": "^10.6.1" + "jake": "10.8.2" } }, "electron-to-chromium": { - "version": "1.3.752", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz", - "integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==", + "version": "1.3.755", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.755.tgz", + "integrity": "sha512-BJ1s/kuUuOeo1bF/EM2E4yqW9te0Hpof3wgwBx40AWJE18zsD1Tqo0kr7ijnOc+lRsrlrqKPauJAHqaxOItoUA==", "dev": true }, "elliptic": { @@ -34803,19 +6844,19 @@ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "bn.js": "4.12.0", + "brorand": "1.1.0", + "hash.js": "1.1.7", + "hmac-drbg": "1.0.1", + "inherits": "2.0.4", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, "inherits": { @@ -34827,9 +6868,9 @@ } }, "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "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": { @@ -34849,7 +6890,7 @@ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "engine.io": { @@ -34858,13 +6899,13 @@ "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.7", "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" + "cookie": "0.4.1", + "cors": "2.8.5", + "debug": "4.3.1", + "engine.io-parser": "4.0.2", + "ws": "7.4.6" }, "dependencies": { "cookie": { @@ -34887,6 +6928,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true } } }, @@ -34905,10 +6952,10 @@ "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" + "graceful-fs": "4.2.6", + "memory-fs": "0.4.1", + "object-assign": "4.1.1", + "tapable": "0.2.9" } }, "enquirer": { @@ -34917,7 +6964,7 @@ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "ansi-colors": "4.1.1" } }, "ent": { @@ -34927,12 +6974,12 @@ "dev": true }, "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error": { @@ -34941,7 +6988,7 @@ "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", "dev": true, "requires": { - "string-template": "~0.2.1" + "string-template": "0.2.1" } }, "error-ex": { @@ -34950,52 +6997,47 @@ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "dependencies": { - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - } + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, + "requires": { + "call-bind": "1.0.2", + "es-to-primitive": "1.2.1", + "function-bind": "1.1.1", + "get-intrinsic": "1.1.1", + "has": "1.0.3", + "has-symbols": "1.0.2", + "is-callable": "1.2.3", + "is-negative-zero": "2.0.1", + "is-regex": "1.1.3", + "is-string": "1.0.6", + "object-inspect": "1.10.3", + "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" } }, "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", + "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", "dev": true, "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "call-bind": "1.0.2", + "get-intrinsic": "1.1.1", + "has-symbols": "1.0.2", + "is-arguments": "1.1.0", + "is-map": "2.0.2", + "is-set": "2.0.2", + "is-string": "1.0.6", + "isarray": "2.0.5" }, "dependencies": { "isarray": { @@ -35012,9 +7054,9 @@ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "1.2.3", + "is-date-object": "1.0.4", + "is-symbol": "1.0.4" } }, "es5-ext": { @@ -35023,15 +7065,15 @@ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.3", + "next-tick": "1.0.0" } }, "es5-shim": { - "version": "4.5.14", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", - "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", + "version": "4.5.15", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.15.tgz", + "integrity": "sha512-FYpuxEjMeDvU4rulKqFdukQyZSTpzhg4ScQHrAosrlVpR6GFyaw14f74yn2+4BugniIS0Frpg7TvwZocU4ZMTw==", "dev": true }, "es6-iterator": { @@ -35040,9 +7082,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.1", + "es5-ext": "0.10.53", + "es6-symbol": "3.1.3" } }, "es6-map": { @@ -35051,12 +7093,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.1", + "es5-ext": "0.10.53", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.3", + "event-emitter": "0.3.5" } }, "es6-promise": { @@ -35067,11 +7109,11 @@ }, "es6-promisify": { "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "es6-promise": "^4.0.3" + "es6-promise": "4.2.8" } }, "es6-set": { @@ -35080,11 +7122,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.1", + "es5-ext": "0.10.53", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" }, "dependencies": { "es6-symbol": { @@ -35093,8 +7135,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.1", + "es5-ext": "0.10.53" } } } @@ -35105,8 +7147,8 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "d": "1.0.1", + "ext": "1.4.0" } }, "es6-weak-map": { @@ -35115,10 +7157,10 @@ "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "d": "1.0.1", + "es5-ext": "0.10.53", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.3" } }, "escalade": { @@ -35138,75 +7180,160 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "2.0.3", + "optionator": "0.8.3", + "source-map": "0.2.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "word-wrap": "1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "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=", + "dev": true, + "optional": true, + "requires": { + "amdefine": "1.0.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + } + } + }, "escope": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.3", + "esrecurse": "4.3.0", + "estraverse": "4.3.0" } }, "eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "@eslint/eslintrc": "0.4.2", + "ajv": "6.12.6", + "chalk": "4.1.1", + "cross-spawn": "7.0.3", + "debug": "4.3.1", + "doctrine": "3.0.0", + "enquirer": "2.3.6", + "escape-string-regexp": "4.0.0", + "eslint-scope": "5.1.1", + "eslint-utils": "2.1.0", + "eslint-visitor-keys": "2.1.0", + "espree": "7.3.1", + "esquery": "1.4.0", + "esutils": "2.0.3", + "fast-deep-equal": "3.1.3", + "file-entry-cache": "6.0.1", + "functional-red-black-tree": "1.0.1", + "glob-parent": "5.1.2", + "globals": "13.9.0", + "ignore": "4.0.6", + "import-fresh": "3.3.0", + "imurmurhash": "0.1.4", + "is-glob": "4.0.1", + "js-yaml": "3.14.1", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.4.1", + "lodash.merge": "4.6.2", + "minimatch": "3.0.4", + "natural-compare": "1.4.0", + "optionator": "0.9.1", + "progress": "2.0.3", + "regexpp": "3.2.0", + "semver": "7.3.5", + "strip-ansi": "6.0.0", + "strip-json-comments": "3.1.1", + "table": "6.7.1", + "text-table": "0.2.0", + "v8-compile-cache": "2.3.0" }, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "7.14.5" + } + }, "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" + "fast-deep-equal": "3.1.3", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.4.1" } }, "ansi-styles": { @@ -35215,7 +7342,16 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" } }, "chalk": { @@ -35224,8 +7360,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -35234,7 +7370,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -35249,9 +7385,9 @@ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "path-key": "3.1.1", + "shebang-command": "2.0.0", + "which": "2.0.2" } }, "debug": { @@ -35263,15 +7399,6 @@ "ms": "2.1.2" } }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -35284,7 +7411,7 @@ "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "type-fest": "0.20.2" } }, "has-flag": { @@ -35293,14 +7420,14 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "lru-cache": { @@ -35309,7 +7436,7 @@ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^4.0.0" + "yallist": "4.0.0" } }, "ms": { @@ -35318,72 +7445,22 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" + "lru-cache": "6.0.0" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "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" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" + "has-flag": "4.0.0" } }, "type-fest": { @@ -35392,15 +7469,6 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -35421,27 +7489,36 @@ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "debug": "2.6.9", + "resolve": "1.20.0" } }, "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", "dev": true, "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "debug": "3.2.7", + "pkg-dir": "2.0.0" }, "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "2.1.3" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "locate-path": { @@ -35450,17 +7527,23 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.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 + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -35469,7 +7552,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.3.0" } }, "p-try": { @@ -35490,7 +7573,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "2.1.0" } } } @@ -35501,50 +7584,61 @@ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", "dev": true, "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" + "eslint-utils": "2.1.0", + "regexpp": "3.2.0" } }, "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "requires": { + "array-includes": "3.1.3", + "array.prototype.flat": "1.2.4", + "debug": "2.6.9", + "doctrine": "2.1.0", + "eslint-import-resolver-node": "0.3.4", + "eslint-module-utils": "2.6.1", + "find-up": "2.1.0", + "has": "1.0.3", + "is-core-module": "2.4.0", + "minimatch": "3.0.4", + "object.values": "1.1.4", + "pkg-up": "2.0.0", + "read-pkg-up": "3.0.0", + "resolve": "1.20.0", + "tsconfig-paths": "3.9.0" }, "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "2.0.3" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "load-json-file": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.2.6", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" } }, "locate-path": { @@ -35553,8 +7647,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "p-limit": { @@ -35563,7 +7657,7 @@ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -35572,7 +7666,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.3.0" } }, "p-try": { @@ -35581,6 +7675,16 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2" + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -35588,33 +7692,39 @@ "dev": true }, "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "3.0.0" } }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "load-json-file": "4.0.0", + "normalize-package-data": "2.5.0", + "path-type": "3.0.0" } }, "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "find-up": "2.1.0", + "read-pkg": "3.0.0" } }, "strip-bom": { @@ -35631,12 +7741,12 @@ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "eslint-plugin-es": "3.0.1", + "eslint-utils": "2.1.0", + "ignore": "5.1.8", + "minimatch": "3.0.4", + "resolve": "1.20.0", + "semver": "6.3.0" }, "dependencies": { "ignore": { @@ -35644,24 +7754,18 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, "eslint-plugin-prebid": { - "version": "file:plugins/eslint" + "version": "file:plugins/eslint", + "dev": true }, "eslint-plugin-promise": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-standard": { "version": "3.1.0", @@ -35675,8 +7779,8 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "esrecurse": "4.3.0", + "estraverse": "4.3.0" } }, "eslint-utils": { @@ -35685,7 +7789,7 @@ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "1.3.0" }, "dependencies": { "eslint-visitor-keys": { @@ -35708,24 +7812,11 @@ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "7.4.1", + "acorn-jsx": "5.3.1", + "eslint-visitor-keys": "1.3.0" }, "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} - }, "eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", @@ -35746,7 +7837,7 @@ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { - "estraverse": "^5.1.0" + "estraverse": "5.2.0" }, "dependencies": { "estraverse": { @@ -35763,7 +7854,7 @@ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^5.2.0" + "estraverse": "5.2.0" }, "dependencies": { "estraverse": { @@ -35784,8 +7875,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "optional": true + "dev": true }, "esutils": { "version": "2.0.3", @@ -35804,35 +7894,35 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.1", + "es5-ext": "0.10.53" } }, "event-stream": { "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.2", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "evp_bytestokey": { @@ -35841,87 +7931,81 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "md5.js": "1.3.5", + "safe-buffer": "5.1.2" } }, "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.3", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.7.1", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "path-key": "^3.0.0" + "pump": "3.0.0" } }, "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^3.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } } } @@ -35938,13 +8022,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -35953,7 +8037,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -35962,7 +8046,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -35973,7 +8057,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.4" }, "dependencies": { "fill-range": { @@ -35982,26 +8066,20 @@ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "3.1.1", + "repeat-element": "1.1.4", + "repeat-string": "1.6.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-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "isobject": { @@ -36019,7 +8097,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -36030,7 +8108,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.3" } }, "expect": { @@ -36039,12 +8117,12 @@ "integrity": "sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w==", "dev": true, "requires": { - "@jest/types": "^27.0.2", - "ansi-styles": "^5.0.0", - "jest-get-type": "^27.0.1", - "jest-matcher-utils": "^27.0.2", - "jest-message-util": "^27.0.2", - "jest-regex-util": "^27.0.1" + "@jest/types": "27.0.2", + "ansi-styles": "5.2.0", + "jest-get-type": "27.0.1", + "jest-matcher-utils": "27.0.2", + "jest-message-util": "27.0.2", + "jest-regex-util": "27.0.1" }, "dependencies": { "ansi-styles": { @@ -36061,8 +8139,8 @@ "integrity": "sha512-Kn4Rtu5vKbDo95WNcjZ9XVz/qTPGZzumP9w7VSV4OxY5z6BAqSI2jb85EsqPxpavs33P+9Qse4Z+d5ilDD/dQw==", "dev": true, "requires": { - "expect": "^27.0.2", - "jest-matcher-utils": "^27.0.2" + "expect": "27.0.2", + "jest-matcher-utils": "27.0.2" } }, "express": { @@ -36070,36 +8148,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "requires": { - "accepts": "~1.3.7", + "accepts": "1.3.7", "array-flatten": "1.1.1", "body-parser": "1.19.0", "content-disposition": "0.5.3", - "content-type": "~1.0.4", + "content-type": "1.0.4", "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", + "proxy-addr": "2.0.7", "qs": "6.7.0", - "range-parser": "~1.2.1", + "range-parser": "1.2.1", "safe-buffer": "5.1.2", "send": "0.17.1", "serve-static": "1.14.1", "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", + "statuses": "1.5.0", + "type-is": "1.6.18", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" } }, "ext": { @@ -36108,13 +8186,13 @@ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "dev": true, "requires": { - "type": "^2.0.0" + "type": "2.5.0" }, "dependencies": { "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", "dev": true } } @@ -36131,8 +8209,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -36141,7 +8219,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -36152,9 +8230,9 @@ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "chardet": "0.7.0", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" } }, "extglob": { @@ -36163,14 +8241,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -36179,7 +8257,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -36188,7 +8266,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -36197,7 +8275,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-data-descriptor": { @@ -36206,7 +8284,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-descriptor": { @@ -36215,9 +8293,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" } } } @@ -36228,10 +8306,10 @@ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" + "@types/yauzl": "2.9.1", + "debug": "4.3.1", + "get-stream": "5.2.0", + "yauzl": "2.10.0" }, "dependencies": { "debug": { @@ -36269,10 +8347,10 @@ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "parse-node-version": "1.0.1", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -36299,7 +8377,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.4" } }, "fd-slicer": { @@ -36308,7 +8386,7 @@ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { - "pend": "~1.2.0" + "pend": "1.2.0" } }, "fibers": { @@ -36317,7 +8395,7 @@ "integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg==", "dev": true, "requires": { - "detect-libc": "^1.0.3" + "detect-libc": "1.0.3" } }, "figures": { @@ -36326,7 +8404,7 @@ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -36335,7 +8413,7 @@ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^3.0.4" + "flat-cache": "3.0.4" } }, "file-uri-to-path": { @@ -36346,12 +8424,12 @@ "optional": true }, "filelist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", - "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", + "integrity": "sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==", "dev": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "filename-regex": { @@ -36372,7 +8450,7 @@ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "to-regex-range": "^5.0.1" + "to-regex-range": "5.0.1" } }, "filter-obj": { @@ -36387,103 +8465,163 @@ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "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" + "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" } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "1.0.1", + "make-dir": "3.1.0", + "pkg-dir": "4.2.0" + } + }, + "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" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "detect-file": "1.0.0", + "is-glob": "4.0.1", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "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.1" + } + } } }, - "locate-path": { + "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.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.1" + } + } + } + }, + "is-number": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "kind-of": "3.2.2" + }, + "dependencies": { + "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.6" + } + } } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" + "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.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "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": { - "find-up": "^3.0.0" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } } } }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, "fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" } }, "flagged-respawn": { @@ -36504,8 +8642,8 @@ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "3.1.1", + "rimraf": "3.0.2" }, "dependencies": { "rimraf": { @@ -36514,7 +8652,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "7.1.7" } } } @@ -36531,8 +8669,8 @@ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "inherits": "2.0.3", + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -36541,21 +8679,30 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } }, "follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", "dev": true }, "for-in": { @@ -36570,7 +8717,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreach": { @@ -36594,874 +8741,488 @@ "fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", - "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "^1.0.0" - } - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "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=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "fs.extra": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", - "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", - "dev": true, - "requires": { - "fs-extra": "~0.6.1", - "mkdirp": "~0.3.5", - "walk": "^2.3.9" - }, - "dependencies": { - "fs-extra": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", - "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", - "dev": true, - "requires": { - "jsonfile": "~1.0.1", - "mkdirp": "0.3.x", - "ncp": "~0.4.2", - "rimraf": "~2.2.0" - } - }, - "jsonfile": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", - "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", - "dev": true - }, - "mkdirp": { - "version": "0.3.5", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", - "dev": true - }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "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==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "fun-hooks": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.9.tgz", - "integrity": "sha512-821UhoYfO9Sg01wAl/QsDRB088BW0aeOqzC1PXLxSlB+kaUVbK+Vp6wMDHU5huZZopYxmMmv5jDkEYqDpK3hqg==", - "requires": { - "typescript-tuple": "^2.2.1" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "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=", - "dev": true - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "generic-names": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", - "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", - "dev": true, - "optional": true, - "requires": { - "loader-utils": "^1.1.0" - }, - "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, - "optional": 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, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "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": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", "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==", + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "asynckit": "0.4.0", + "combined-stream": "1.0.8", + "mime-types": "2.1.31" } }, - "get-pkg-repo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", - "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "meow": "^3.3.0", - "normalize-package-data": "^2.3.0", - "parse-github-repo-url": "^1.3.0", - "through2": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } + "map-cache": "0.2.2" } }, - "get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { - "pump": "^3.0.0" + "null-check": "1.0.0" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "graceful-fs": "4.2.6", + "jsonfile": "6.1.0", + "universalify": "2.0.0" } }, - "git-raw-commits": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", - "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "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=", "dev": true, "requires": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "graceful-fs": "4.2.6", + "through2": "2.0.5" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "safe-buffer": "5.1.2" } }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + } + } + }, + "fs.extra": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", + "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", + "dev": true, + "requires": { + "fs-extra": "0.6.4", + "mkdirp": "0.3.5", + "walk": "2.3.14" + }, + "dependencies": { + "fs-extra": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", + "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", "dev": true, "requires": { - "yallist": "^4.0.0" + "jsonfile": "1.0.1", + "mkdirp": "0.3.5", + "ncp": "0.4.2", + "rimraf": "2.2.8" } }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "jsonfile": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", + "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", "dev": true }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - } - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "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" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", "dev": true }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "4.2.6", + "inherits": "2.0.3", + "mkdirp": "0.5.5", + "rimraf": "2.5.4" + }, + "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==", "dev": true, "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "minimist": "1.2.5" } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + } + } + }, + "fun-hooks": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.10.tgz", + "integrity": "sha512-7xBjdT+oMYOPWgwFxNiNzF4ubeUvim4zs1DnQqSSGyxu8UD7AW/6Z0iFsVRwuVSIZKUks2en2VHHotmNfj3ipw==", + "requires": { + "typescript-tuple": "2.2.1" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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=", + "dev": true + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "1.3.2" + } + }, + "generic-names": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", + "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", + "dev": true, + "optional": true, + "requires": { + "loader-utils": "1.4.0" + }, + "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, + "optional": true, "requires": { - "lru-cache": "^6.0.0" + "minimist": "1.2.5" } }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "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, + "optional": true, "requires": { - "min-indent": "^1.0.0" + "big.js": "5.2.2", + "emojis-list": "3.0.0", + "json5": "1.0.1" } - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, - "git-remote-origin-url": { + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "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==", "dev": true, "requires": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" + "function-bind": "1.1.1", + "has": "1.0.3", + "has-symbols": "1.0.2" } }, - "git-semver-tags": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", - "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", "dev": true, "requires": { - "meow": "^8.0.0", - "semver": "^6.0.0" + "hosted-git-info": "2.8.9", + "meow": "3.7.0", + "normalize-package-data": "2.5.0", + "parse-github-repo-url": "1.4.1", + "through2": "2.0.5" }, "dependencies": { "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true }, "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "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" - } - }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "camelcase": "2.1.1", + "map-obj": "1.0.1" } }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "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" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "yallist": "^4.0.0" + "repeating": "2.0.1" } }, - "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", - "dev": true - }, "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - } - }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.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" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.5", + "normalize-package-data": "2.5.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" } }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } }, "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "min-indent": "^1.0.0" + "get-stdin": "4.0.1" } }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "2.3.7", + "xtend": "4.0.2" + } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true } } }, + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "git-raw-commits": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", + "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "dev": true, + "requires": { + "dargs": "7.0.0", + "lodash": "4.17.21", + "meow": "8.1.2", + "split2": "3.2.2", + "through2": "4.0.2" + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "dev": true, + "requires": { + "gitconfiglocal": "1.0.0", + "pify": "2.3.0" + } + }, + "git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "requires": { + "meow": "8.1.2", + "semver": "6.3.0" + } + }, "git-up": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^5.0.0" + "is-ssh": "1.3.3", + "parse-url": "5.0.5" } }, "git-url-parse": { @@ -37470,7 +9231,7 @@ "integrity": "sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw==", "dev": true, "requires": { - "git-up": "^4.0.0" + "git-up": "4.0.2" } }, "gitconfiglocal": { @@ -37479,7 +9240,7 @@ "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", "dev": true, "requires": { - "ini": "^1.3.2" + "ini": "1.3.8" } }, "github-slugger": { @@ -37488,21 +9249,29 @@ "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", "dev": true, "requires": { - "emoji-regex": ">=6.0.0 <=6.1.1" + "emoji-regex": "6.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + } } }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -37511,8 +9280,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" }, "dependencies": { "glob-parent": { @@ -37521,7 +9290,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "is-extglob": { @@ -37536,7 +9305,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -37547,7 +9316,7 @@ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "4.0.1" } }, "glob-stream": { @@ -37556,16 +9325,16 @@ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "dev": true, "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" + "extend": "3.0.2", + "glob": "7.1.7", + "glob-parent": "3.1.0", + "is-negated-glob": "1.0.0", + "ordered-read-streams": "1.0.1", + "pumpify": "1.5.1", + "readable-stream": "2.3.7", + "remove-trailing-separator": "1.1.0", + "to-absolute-glob": "2.0.2", + "unique-stream": "2.3.1" }, "dependencies": { "glob-parent": { @@ -37574,8 +9343,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" } }, "is-glob": { @@ -37584,7 +9353,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } }, "readable-stream": { @@ -37593,13 +9362,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -37610,13 +9388,13 @@ "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" + "anymatch": "2.0.0", + "async-done": "1.3.2", + "chokidar": "2.1.8", + "is-negated-glob": "1.0.0", + "just-debounce": "1.1.0", + "normalize-path": "3.0.0", + "object.defaults": "1.1.0" }, "dependencies": { "anymatch": { @@ -37625,8 +9403,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" }, "dependencies": { "normalize-path": { @@ -37635,7 +9413,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } } } @@ -37652,16 +9430,16 @@ "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" + "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -37670,7 +9448,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -37681,18 +9459,18 @@ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "anymatch": "2.0.0", + "async-each": "1.0.3", + "braces": "2.3.2", + "fsevents": "1.2.13", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.2.0" } }, "fill-range": { @@ -37701,10 +9479,10 @@ "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" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -37713,7 +9491,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -37725,8 +9503,8 @@ "dev": true, "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "bindings": "1.5.0", + "nan": "2.14.2" } }, "glob-parent": { @@ -37735,8 +9513,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -37745,7 +9523,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -37756,22 +9534,16 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.13.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-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" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -37780,24 +9552,45 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } }, + "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.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -37806,9 +9599,18 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "graceful-fs": "4.2.6", + "micromatch": "3.1.10", + "readable-stream": "2.3.7" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "to-regex-range": { @@ -37817,8 +9619,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } } } @@ -37829,9 +9631,9 @@ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" } }, "global-prefix": { @@ -37840,11 +9642,22 @@ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.3", + "ini": "1.3.8", + "is-windows": "1.0.2", + "which": "1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + } } }, "globals": { @@ -37865,9 +9678,9 @@ "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" + "glob": "7.1.7", + "lodash": "4.17.21", + "minimatch": "3.0.4" } }, "glogg": { @@ -37876,7 +9689,7 @@ "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "got": { @@ -37885,17 +9698,17 @@ "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", "dev": true, "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "@sindresorhus/is": "4.0.1", + "@szmarczak/http-timer": "4.0.5", + "@types/cacheable-request": "6.0.1", + "@types/responselike": "1.0.0", + "cacheable-lookup": "5.0.4", + "cacheable-request": "7.0.2", + "decompress-response": "6.0.0", + "http2-wrapper": "1.0.3", + "lowercase-keys": "2.0.0", + "p-cancelable": "2.1.1", + "responselike": "2.0.0" } }, "graceful-fs": { @@ -37922,10 +9735,164 @@ "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "requires": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" + "glob-watcher": "5.0.5", + "gulp-cli": "2.3.0", + "undertaker": "1.3.0", + "vinyl-fs": "3.0.3" + }, + "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": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "requires": { + "ansi-colors": "1.1.0", + "archy": "1.0.0", + "array-sort": "1.0.0", + "color-support": "1.1.3", + "concat-stream": "1.6.2", + "copy-props": "2.0.5", + "fancy-log": "1.3.3", + "gulplog": "1.0.0", + "interpret": "1.4.0", + "isobject": "3.0.1", + "liftoff": "3.1.0", + "matchdep": "2.0.0", + "mute-stdout": "1.0.1", + "pretty-hrtime": "1.0.3", + "replace-homedir": "1.0.0", + "semver-greatest-satisfied-range": "1.1.0", + "v8flags": "3.2.0", + "yargs": "7.1.2" + } + }, + "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=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "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 + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "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=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.3", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.2", + "yargs-parser": "5.0.1" + } + }, + "yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "requires": { + "camelcase": "3.0.0", + "object.assign": "4.1.2" + } + } } }, "gulp-clean": { @@ -37934,14 +9901,14 @@ "integrity": "sha1-o0fUc6zqQBgvk1WHpFGUFnGSgQI=", "dev": true, "requires": { - "gulp-util": "^2.2.14", - "rimraf": "^2.2.8", - "through2": "^0.4.2" + "gulp-util": "2.2.20", + "rimraf": "2.5.4", + "through2": "0.4.2" }, "dependencies": { "ansi-regex": { "version": "0.2.1", - "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", "dev": true }, @@ -37951,17 +9918,33 @@ "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", "dev": true }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, "chalk": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", "dev": true, "requires": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" } }, "clone-stats": { @@ -37970,30 +9953,46 @@ "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", "dev": true }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "gulp-util": { "version": "2.2.20", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", "dev": true, "requires": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.1", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" }, "dependencies": { "through2": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" + "readable-stream": "1.0.34", + "xtend": "3.0.0" } } } @@ -38004,7 +10003,16 @@ "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", "dev": true, "requires": { - "ansi-regex": "^0.2.0" + "ansi-regex": "0.2.1" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" } }, "isarray": { @@ -38013,6 +10021,32 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.5", + "normalize-package-data": "2.5.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "minimist": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", @@ -38027,29 +10061,48 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, "strip-ansi": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "dev": true, "requires": { - "ansi-regex": "^0.2.1" + "ansi-regex": "0.2.1" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "4.0.1" } }, "supports-color": { @@ -38060,12 +10113,12 @@ }, "through2": { "version": "0.4.2", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" + "readable-stream": "1.0.34", + "xtend": "2.1.2" }, "dependencies": { "xtend": { @@ -38074,18 +10127,24 @@ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", "dev": true, "requires": { - "object-keys": "~0.4.0" + "object-keys": "0.4.0" } } } }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, "vinyl": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", "dev": true, "requires": { - "clone-stats": "~0.0.1" + "clone-stats": "0.0.1" } }, "xtend": { @@ -38096,178 +10155,15 @@ } } }, - "gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.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-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "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=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^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=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "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=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, - "yargs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", - "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.1" - } - }, - "yargs-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", - "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" - } - } - } - }, "gulp-concat": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", "dev": true, "requires": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" + "concat-with-sourcemaps": "1.1.0", + "through2": "2.0.5", + "vinyl": "2.2.1" }, "dependencies": { "readable-stream": { @@ -38276,13 +10172,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -38291,8 +10196,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -38300,24 +10205,24 @@ "gulp-connect": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", - "dev": true, - "requires": { - "ansi-colors": "^2.0.5", - "connect": "^3.6.6", - "connect-livereload": "^0.6.0", - "fancy-log": "^1.3.2", - "map-stream": "^0.0.7", - "send": "^0.16.2", - "serve-index": "^1.9.1", - "serve-static": "^1.13.2", - "tiny-lr": "^1.1.1" + "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", + "dev": true, + "requires": { + "ansi-colors": "2.0.5", + "connect": "3.7.0", + "connect-livereload": "0.6.1", + "fancy-log": "1.3.3", + "map-stream": "0.0.7", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.14.1", + "tiny-lr": "1.1.1" }, "dependencies": { "ansi-colors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", + "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", "dev": true }, "http-errors": { @@ -38326,10 +10231,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.4.0" } }, "map-stream": { @@ -38351,18 +10256,18 @@ "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.1", + "statuses": "1.4.0" } }, "setprototypeof": { @@ -38385,17 +10290,39 @@ "integrity": "sha512-fcFUQzFsN6dJ6KZlG+qPOEkqfcevRUXgztkYCvhNvJeSvOicC8ucutN4qR/ID8LmNZx9YPIkBzazTNnVvbh8wg==", "dev": true, "requires": { - "eslint": "^4.0.0", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.0" + "eslint": "4.19.1", + "fancy-log": "1.3.3", + "plugin-error": "1.0.1" }, "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, "ajv-keywords": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true, - "requires": {} + "dev": true }, "ansi-escapes": { "version": "3.2.0", @@ -38409,6 +10336,15 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, "chardet": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", @@ -38421,7 +10357,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -38436,9 +10372,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.5", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "debug": { @@ -38447,7 +10383,7 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.3" } }, "doctrine": { @@ -38456,7 +10392,7 @@ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.3" } }, "eslint": { @@ -38465,44 +10401,44 @@ "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", + "ajv": "5.5.2", + "babel-code-frame": "6.26.0", + "chalk": "2.4.2", + "concat-stream": "1.6.2", + "cross-spawn": "5.1.0", + "debug": "3.2.7", + "doctrine": "2.1.0", + "eslint-scope": "3.7.3", + "eslint-visitor-keys": "1.3.0", + "espree": "3.5.4", + "esquery": "1.4.0", + "esutils": "2.0.3", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.7", + "globals": "11.12.0", + "ignore": "3.3.10", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.14.1", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.21", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "natural-compare": "1.4.0", + "optionator": "0.8.3", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.3", + "regexpp": "1.1.0", + "require-uncached": "1.0.3", + "semver": "5.7.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", "table": "4.0.2", - "text-table": "~0.2.0" + "text-table": "0.2.0" } }, "eslint-scope": { @@ -38511,8 +10447,8 @@ "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.3.0", + "estraverse": "4.3.0" } }, "eslint-visitor-keys": { @@ -38527,8 +10463,8 @@ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "5.7.4", + "acorn-jsx": "3.0.1" } }, "external-editor": { @@ -38537,9 +10473,9 @@ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" + "chardet": "0.4.2", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" } }, "figures": { @@ -38548,7 +10484,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -38557,8 +10493,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.4", + "object-assign": "4.1.1" } }, "flat-cache": { @@ -38567,10 +10503,10 @@ "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "graceful-fs": "4.2.6", + "rimraf": "2.6.3", + "write": "0.2.1" } }, "ignore": { @@ -38585,20 +10521,20 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", + "ansi-escapes": "3.2.0", + "chalk": "2.4.2", + "cli-cursor": "2.1.0", + "cli-width": "2.2.1", + "external-editor": "2.2.0", + "figures": "2.0.0", + "lodash": "4.17.21", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "run-async": "2.4.1", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" } }, "is-fullwidth-code-point": { @@ -38607,6 +10543,26 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", @@ -38619,7 +10575,7 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } }, "ms": { @@ -38640,9 +10596,29 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "word-wrap": "1.2.3" } }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", @@ -38655,8 +10631,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "onetime": "2.0.1", + "signal-exit": "3.0.3" } }, "rimraf": { @@ -38665,16 +10641,37 @@ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "7.1.7" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" } }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0" + "is-fullwidth-code-point": "2.0.0" } }, "string-width": { @@ -38683,8 +10680,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -38693,7 +10690,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "strip-json-comments": { @@ -38708,12 +10705,30 @@ "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.4.2", + "lodash": "4.17.21", "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "string-width": "2.1.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" } } } @@ -38724,10 +10739,10 @@ "integrity": "sha512-HsG5VOgKHFRqZXnHGI6oGhPDg70p9pobM+dYOnjBZVLMQUHzLG6bfaPNRJ7XG707E+vWO3TfN0CND9UrYhk94g==", "dev": true, "requires": { - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.6.2", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", "map-stream": "0.0.7" }, "dependencies": { @@ -38743,7 +10758,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.keys": { @@ -38752,9 +10767,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.template": { @@ -38763,15 +10778,15 @@ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -38780,8 +10795,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "map-stream": { @@ -38798,10 +10813,10 @@ "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==", "dev": true, "requires": { - "concat-with-sourcemaps": "^1.1.0", - "lodash.template": "^4.5.0", + "concat-with-sourcemaps": "1.1.0", + "lodash.template": "4.5.0", "map-stream": "0.0.7", - "through2": "^2.0.0" + "through2": "2.0.5" }, "dependencies": { "lodash._reinterpolate": { @@ -38816,8 +10831,8 @@ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.templatesettings": "4.2.0" } }, "lodash.templatesettings": { @@ -38826,7 +10841,7 @@ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0" + "lodash._reinterpolate": "3.0.0" } }, "map-stream": { @@ -38841,13 +10856,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -38856,8 +10880,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -38868,9 +10892,9 @@ "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", "dev": true, "requires": { - "gulp-match": "^1.1.0", - "ternary-stream": "^3.0.0", - "through2": "^3.0.1" + "gulp-match": "1.1.0", + "ternary-stream": "3.0.0", + "through2": "3.0.2" }, "dependencies": { "inherits": { @@ -38885,8 +10909,8 @@ "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "inherits": "2.0.4", + "readable-stream": "3.6.0" } } } @@ -38897,7 +10921,7 @@ "integrity": "sha1-HNRF+9AJ4Np2lZoDp/SbNWav+Gg=", "dev": true, "requires": { - "through2": "^0.6.3" + "through2": "0.6.5" }, "dependencies": { "isarray": { @@ -38908,30 +10932,30 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, "through2": { "version": "0.6.5", - "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.2" } } } @@ -38942,34 +10966,27 @@ "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", "dev": true, "requires": { - "minimatch": "^3.0.3" + "minimatch": "3.0.4" } }, "gulp-replace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", - "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.3.tgz", + "integrity": "sha512-HcPHpWY4XdF8zxYkDODHnG2+7a3nD/Y8Mfu3aBgMiCFDW3X2GiOKXllsAmILcxe3KZT2BXoN18WrpEFm48KfLQ==", "dev": true, "requires": { - "istextorbinary": "2.2.1", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" + "@types/node": "14.17.4", + "@types/vinyl": "2.0.4", + "istextorbinary": "3.3.0", + "replacestream": "4.0.3", + "yargs-parser": "20.2.9" }, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true } } }, @@ -38979,12 +10996,12 @@ "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", "dev": true, "requires": { - "chalk": "^3.0.0", - "fancy-log": "^1.3.3", - "lodash.template": "^4.5.0", - "plugin-error": "^1.0.1", - "through2": "^3.0.1", - "tslib": "^1.10.0" + "chalk": "3.0.0", + "fancy-log": "1.3.3", + "lodash.template": "4.5.0", + "plugin-error": "1.0.1", + "through2": "3.0.2", + "tslib": "1.14.1" }, "dependencies": { "ansi-styles": { @@ -38993,7 +11010,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -39002,8 +11019,8 @@ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -39012,7 +11029,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -39045,8 +11062,8 @@ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.templatesettings": "4.2.0" } }, "lodash.templatesettings": { @@ -39055,7 +11072,7 @@ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0" + "lodash._reinterpolate": "3.0.0" } }, "supports-color": { @@ -39064,7 +11081,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } }, "through2": { @@ -39073,8 +11090,8 @@ "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "inherits": "2.0.4", + "readable-stream": "3.6.0" } } } @@ -39085,17 +11102,17 @@ "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", "dev": true, "requires": { - "@gulp-sourcemaps/identity-map": "^2.0.1", - "@gulp-sourcemaps/map-sources": "^1.0.0", - "acorn": "^6.4.1", - "convert-source-map": "^1.0.0", - "css": "^3.0.0", - "debug-fabulous": "^1.0.0", - "detect-newline": "^2.0.0", - "graceful-fs": "^4.0.0", - "source-map": "^0.6.0", - "strip-bom-string": "^1.0.0", - "through2": "^2.0.0" + "@gulp-sourcemaps/identity-map": "2.0.1", + "@gulp-sourcemaps/map-sources": "1.0.0", + "acorn": "6.4.2", + "convert-source-map": "1.8.0", + "css": "3.0.0", + "debug-fabulous": "1.1.0", + "detect-newline": "2.1.0", + "graceful-fs": "4.2.6", + "source-map": "0.6.1", + "strip-bom-string": "1.0.0", + "through2": "2.0.5" }, "dependencies": { "acorn": { @@ -39104,19 +11121,25 @@ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "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=", + "dev": true + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "source-map": { @@ -39125,14 +11148,23 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -39143,10 +11175,10 @@ "integrity": "sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw==", "dev": true, "requires": { - "plugin-error": "^1.0.1", + "plugin-error": "1.0.1", "terser": "5.4.0", - "through2": "^4.0.2", - "vinyl-sourcemaps-apply": "^0.2.1" + "through2": "4.0.2", + "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-util": { @@ -39155,24 +11187,24 @@ "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "dev": true, "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.3", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.5", + "multipipe": "0.1.2", + "object-assign": "3.0.0", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "through2": "2.0.5", + "vinyl": "0.5.3" }, "dependencies": { "ansi-regex": { @@ -39193,11 +11225,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "clone-stats": { @@ -39224,7 +11256,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.keys": { @@ -39233,9 +11265,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.template": { @@ -39244,15 +11276,15 @@ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -39261,8 +11293,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "object-assign": { @@ -39277,13 +11309,13 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "replace-ext": { @@ -39292,13 +11324,22 @@ "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "supports-color": { @@ -39313,8 +11354,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } }, "vinyl": { @@ -39323,8 +11364,8 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -39336,7 +11377,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.2" } }, "gzip-size": { @@ -39345,8 +11386,8 @@ "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", "dev": true, "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "duplexer": "0.1.2", + "pify": "4.0.1" }, "dependencies": { "pify": { @@ -39363,11 +11404,11 @@ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" + "minimist": "1.2.5", + "neo-async": "2.6.2", + "source-map": "0.6.1", + "uglify-js": "3.13.9", + "wordwrap": "1.0.0" }, "dependencies": { "source-map": { @@ -39385,25 +11426,25 @@ "dev": true }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.12.6", + "har-schema": "2.0.0" }, "dependencies": { "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + "fast-deep-equal": "3.1.3", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.4.1" } } } @@ -39420,7 +11461,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -39429,7 +11470,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -39440,6 +11481,12 @@ } } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -39452,13 +11499,13 @@ "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "has-value": { @@ -39467,9 +11514,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -39478,23 +11525,17 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "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 - }, "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" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -39503,7 +11544,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -39514,7 +11555,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -39525,9 +11566,9 @@ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "dev": true, "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "inherits": "2.0.4", + "readable-stream": "3.6.0", + "safe-buffer": "5.2.1" }, "dependencies": { "inherits": { @@ -39557,8 +11598,8 @@ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "hast-util-is-element": { @@ -39573,7 +11614,7 @@ "integrity": "sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA==", "dev": true, "requires": { - "xtend": "^4.0.0" + "xtend": "4.0.2" } }, "hast-util-to-html": { @@ -39582,16 +11623,16 @@ "integrity": "sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw==", "dev": true, "requires": { - "ccount": "^1.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-is-element": "^1.0.0", - "hast-util-whitespace": "^1.0.0", - "html-void-elements": "^1.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0", - "stringify-entities": "^3.0.1", - "unist-util-is": "^4.0.0", - "xtend": "^4.0.0" + "ccount": "1.1.0", + "comma-separated-tokens": "1.0.8", + "hast-util-is-element": "1.1.0", + "hast-util-whitespace": "1.0.4", + "html-void-elements": "1.0.5", + "property-information": "5.6.0", + "space-separated-tokens": "1.1.5", + "stringify-entities": "3.1.0", + "unist-util-is": "4.1.0", + "xtend": "4.0.2" } }, "hast-util-whitespace": { @@ -39618,9 +11659,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "hash.js": "1.1.7", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "homedir-polyfill": { @@ -39629,7 +11670,7 @@ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hoopy": { @@ -39667,17 +11708,17 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", + "statuses": "1.5.0", "toidentifier": "1.0.0" } }, "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", "dev": true }, "http-proxy": { @@ -39686,9 +11727,9 @@ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "4.0.7", + "follow-redirects": "1.14.1", + "requires-port": "1.0.0" } }, "http-signature": { @@ -39697,19 +11738,19 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.16.1" } }, "http2-wrapper": { - "version": "1.0.0-beta.5.2", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", - "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "quick-lru": "5.1.1", + "resolve-alpn": "1.1.2" } }, "https-browserify": { @@ -39724,17 +11765,17 @@ "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", "dev": true, "requires": { - "agent-base": "5", - "debug": "4" + "agent-base": "5.1.1", + "debug": "4.3.1" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -39745,18 +11786,12 @@ } } }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "icss-replace-symbols": { @@ -39770,14 +11805,12 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "optional": true, - "requires": {} + "dev": true }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { @@ -39792,8 +11825,8 @@ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "parent-module": "1.0.1", + "resolve-from": "4.0.0" }, "dependencies": { "resolve-from": { @@ -39828,8 +11861,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -39849,20 +11882,20 @@ "integrity": "sha512-hUDjc3vBkh/uk1gPfMAD/7Z188Q8cvTGl0nxwaCdwSbzFh6ZKkZh+s2ozVxbE5G9ZNRyeY0+lgbAIOUFsFf98w==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", + "ansi-escapes": "4.3.2", + "chalk": "4.1.1", + "cli-cursor": "3.1.0", + "cli-width": "3.0.0", + "external-editor": "3.1.0", + "figures": "3.2.0", + "lodash": "4.17.21", "mute-stream": "0.0.8", - "ora": "^5.3.0", - "run-async": "^2.4.0", - "rxjs": "^6.6.6", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "ora": "5.4.1", + "run-async": "2.4.1", + "rxjs": "6.6.7", + "string-width": "4.2.2", + "strip-ansi": "6.0.0", + "through": "2.3.8" }, "dependencies": { "ansi-styles": { @@ -39871,7 +11904,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -39880,8 +11913,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -39890,7 +11923,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -39911,7 +11944,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -39922,15 +11955,6 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -39948,8 +11972,8 @@ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "is-relative": "1.0.0", + "is-windows": "1.0.2" } }, "is-accessor-descriptor": { @@ -39958,22 +11982,16 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.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" + "is-buffer": "1.1.6" } } } @@ -39990,15 +12008,18 @@ "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", "dev": true, "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" + "is-alphabetical": "1.0.4", + "is-decimal": "1.0.4" } }, "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "1.0.2" + } }, "is-arrayish": { "version": "0.2.1", @@ -40007,9 +12028,9 @@ "dev": true }, "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", "dev": true }, "is-binary-path": { @@ -40018,25 +12039,28 @@ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "binary-extensions": "2.2.0" } }, "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "1.0.2" + } }, "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "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-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, "is-core-module": { @@ -40045,7 +12069,7 @@ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, "requires": { - "has": "^1.0.3" + "has": "1.0.3" } }, "is-data-descriptor": { @@ -40054,30 +12078,24 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.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" + "is-buffer": "1.1.6" } } } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", "dev": true }, "is-decimal": { @@ -40092,9 +12110,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -40123,7 +12141,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -40156,7 +12174,7 @@ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-hexadecimal": { @@ -40172,9 +12190,9 @@ "dev": true }, "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true }, "is-negated-glob": { @@ -40184,9 +12202,9 @@ "dev": true }, "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, "is-number": { @@ -40196,9 +12214,9 @@ "dev": true }, "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", "dev": true }, "is-obj": { @@ -40219,7 +12237,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-posix-bracket": { @@ -40241,12 +12259,13 @@ "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", "dev": true, "requires": { - "has": "^1.0.3" + "call-bind": "1.0.2", + "has-symbols": "1.0.2" } }, "is-relative": { @@ -40255,7 +12274,7 @@ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "is-unc-path": "1.0.0" } }, "is-resolvable": { @@ -40271,9 +12290,9 @@ "dev": true }, "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true }, "is-ssh": { @@ -40282,7 +12301,7 @@ "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", "dev": true, "requires": { - "protocols": "^1.1.0" + "protocols": "1.4.8" } }, "is-stream": { @@ -40292,18 +12311,18 @@ "dev": true }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", "dev": true }, "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "1.0.2" } }, "is-text-path": { @@ -40312,19 +12331,20 @@ "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { - "text-extensions": "^1.0.0" + "text-extensions": "1.9.0" } }, "is-typed-array": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", - "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.0", - "es-abstract": "^1.17.4", - "foreach": "^2.0.5", - "has-symbols": "^1.0.1" + "available-typed-arrays": "1.0.4", + "call-bind": "1.0.2", + "es-abstract": "1.18.3", + "foreach": "2.0.5", + "has-symbols": "1.0.2" } }, "is-typedarray": { @@ -40339,7 +12359,7 @@ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "unc-path-regex": "0.1.2" } }, "is-unicode-supported": { @@ -40384,7 +12404,7 @@ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "requires": { - "is-docker": "^2.0.0" + "is-docker": "2.2.1" } }, "isarray": { @@ -40423,64 +12443,54 @@ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "abbrev": "1.0.9", + "async": "1.5.2", + "escodegen": "1.8.1", + "esprima": "2.7.3", + "glob": "5.0.15", + "handlebars": "4.7.7", + "js-yaml": "3.14.1", + "mkdirp": "0.5.5", + "nopt": "3.0.6", + "once": "1.4.0", + "resolve": "1.1.7", + "supports-color": "3.2.3", + "which": "1.3.1", + "wordwrap": "1.0.0" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - } - }, "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-flag": { @@ -40489,13 +12499,31 @@ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "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==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } }, "resolve": { @@ -40504,23 +12532,22 @@ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "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=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" } } } @@ -40537,18 +12564,10 @@ "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" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@babel/core": "7.14.6", + "@istanbuljs/schema": "0.1.3", + "istanbul-lib-coverage": "3.0.0", + "semver": "6.3.0" } }, "istanbul-lib-report": { @@ -40557,9 +12576,9 @@ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "istanbul-lib-coverage": "3.0.0", + "make-dir": "3.1.0", + "supports-color": "7.2.0" }, "dependencies": { "has-flag": { @@ -40568,43 +12587,26 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "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" + "has-flag": "4.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" + "debug": "4.3.1", + "istanbul-lib-coverage": "3.0.0", + "source-map": "0.6.1" }, "dependencies": { "debug": { @@ -40616,27 +12618,12 @@ "ms": "2.1.2" } }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, "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 }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -40651,19 +12638,18 @@ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "html-escaper": "2.0.2", + "istanbul-lib-report": "3.0.0" } }, "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", + "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", "dev": true, "requires": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" + "binaryextensions": "2.3.0", + "textextensions": "3.3.0" } }, "jake": { @@ -40672,10 +12658,10 @@ "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", "dev": true, "requires": { - "async": "0.9.x", - "chalk": "^2.4.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "async": "0.9.2", + "chalk": "2.4.2", + "filelist": "1.0.2", + "minimatch": "3.0.4" } }, "jest-diff": { @@ -40684,10 +12670,10 @@ "integrity": "sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw==", "dev": true, "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.0.1", - "jest-get-type": "^27.0.1", - "pretty-format": "^27.0.2" + "chalk": "4.1.1", + "diff-sequences": "27.0.1", + "jest-get-type": "27.0.1", + "pretty-format": "27.0.2" }, "dependencies": { "ansi-styles": { @@ -40696,7 +12682,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -40705,8 +12691,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -40715,7 +12701,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -40736,7 +12722,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -40753,10 +12739,10 @@ "integrity": "sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA==", "dev": true, "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.0.2", - "jest-get-type": "^27.0.1", - "pretty-format": "^27.0.2" + "chalk": "4.1.1", + "jest-diff": "27.0.2", + "jest-get-type": "27.0.1", + "pretty-format": "27.0.2" }, "dependencies": { "ansi-styles": { @@ -40765,7 +12751,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -40774,8 +12760,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -40784,7 +12770,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -40805,7 +12791,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -40816,33 +12802,24 @@ "integrity": "sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.0.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "@babel/code-frame": "7.14.5", + "@jest/types": "27.0.2", + "@types/stack-utils": "2.0.0", + "chalk": "4.1.1", + "graceful-fs": "4.2.6", + "micromatch": "4.0.4", + "pretty-format": "27.0.2", + "slash": "3.0.0", + "stack-utils": "2.0.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, "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": "2.0.1" } }, "chalk": { @@ -40851,8 +12828,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -40861,7 +12838,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -40876,23 +12853,13 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "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" - } - }, "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" + "has-flag": "4.0.0" } } } @@ -40910,13 +12877,12 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "2.0.1" } }, "jsbn": { @@ -40980,12 +12946,12 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } }, "jsonfile": { @@ -40994,8 +12960,8 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "graceful-fs": "4.2.6", + "universalify": "2.0.0" } }, "jsonparse": { @@ -41004,16 +12970,6 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -41032,15 +12988,15 @@ "integrity": "sha1-v7P672WqEqMWBYcSlFwyb9jwFDQ=" }, "just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", "dev": true }, "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, "karma": { @@ -41049,83 +13005,44 @@ "integrity": "sha512-hbhRogUYIulfkBTZT7xoPrCYhRBnBoqbbL4fszWD0ReFGUxU+LYBr3dwKdAluaDQ/ynT9/7C+Lf7pPNW4gSx4Q==", "dev": true, "requires": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "^1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.3.0", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^3.1.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.28", - "yargs": "^16.1.1" + "body-parser": "1.19.0", + "braces": "3.0.2", + "chokidar": "3.5.2", + "colors": "1.4.0", + "connect": "3.7.0", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "glob": "7.1.7", + "graceful-fs": "4.2.6", + "http-proxy": "1.18.1", + "isbinaryfile": "4.0.8", + "lodash": "4.17.21", + "log4js": "6.3.0", + "mime": "2.5.2", + "minimatch": "3.0.4", + "qjobs": "1.2.0", + "range-parser": "1.2.1", + "rimraf": "3.0.2", + "socket.io": "3.1.2", + "source-map": "0.6.1", + "tmp": "0.2.1", + "ua-parser-js": "0.7.28", + "yargs": "16.2.0" }, "dependencies": { - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "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" - } - }, "mime": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "7.1.7" } }, "source-map": { @@ -41140,7 +13057,7 @@ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { - "rimraf": "^3.0.0" + "rimraf": "3.0.2" } }, "yargs": { @@ -41149,13 +13066,13 @@ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "cliui": "7.0.4", + "escalade": "3.1.1", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "string-width": "4.2.2", + "y18n": "5.0.8", + "yargs-parser": "20.2.9" } } } @@ -41164,8 +13081,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/karma-babel-preprocessor/-/karma-babel-preprocessor-8.0.1.tgz", "integrity": "sha512-5upyawNi3c7Gg6tPH1FWRVTmUijGf3v1GV4ScLM/2jKdDP18SlaKlUpu8eJrRI3STO8qK1bkqFcdgAA364nLYQ==", - "dev": true, - "requires": {} + "dev": true }, "karma-browserstack-launcher": { "version": "1.4.0", @@ -41173,9 +13089,9 @@ "integrity": "sha512-bUQK84U+euDfOUfEjcF4IareySMOBNRLrrl9q6cttIe8f011Ir6olLITTYMOJDcGY58wiFIdhPHSPd9Pi6+NfQ==", "dev": true, "requires": { - "browserstack": "~1.5.1", - "browserstacktunnel-wrapper": "~2.0.2", - "q": "~1.5.0" + "browserstack": "1.5.3", + "browserstacktunnel-wrapper": "2.0.4", + "q": "1.5.1" } }, "karma-chai": { @@ -41190,41 +13106,85 @@ "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", "dev": true, "requires": { - "which": "^1.2.1" + "which": "1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + } } }, "karma-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.2.tgz", - "integrity": "sha512-zge5qiGEIKDdzWciQwP4p0LSac4k/L6VfrBsERMUn5mpDvxhv1sPVOrSlpzpi70T7NhuEy4bgnpAKIYuumIMCw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz", + "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "3.0.0", + "istanbul-lib-instrument": "4.0.3", + "istanbul-lib-report": "3.0.0", + "istanbul-lib-source-maps": "4.0.0", + "istanbul-reports": "3.0.2", + "minimatch": "3.0.4" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.1", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "minimatch": "^3.0.4" + "istanbul-lib-coverage": "3.0.0", + "istanbul-lib-report": "3.0.0", + "istanbul-lib-source-maps": "3.0.6", + "istanbul-reports": "3.0.2", + "minimatch": "3.0.4" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "4.3.1", + "istanbul-lib-coverage": "2.0.5", + "make-dir": "2.1.0", + "rimraf": "2.7.1", + "source-map": "0.6.1" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + } + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "pify": "4.0.1", + "semver": "5.7.1" } }, "ms": { @@ -41233,6 +13193,27 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "7.1.7" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -41241,26 +13222,13 @@ } } }, - "karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", - "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - } - }, "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=", "dev": true, "requires": { - "es5-shim": "^4.0.5" + "es5-shim": "4.5.15" } }, "karma-firefox-launcher": { @@ -41269,19 +13237,8 @@ "integrity": "sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q==", "dev": true, "requires": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "is-wsl": "2.2.0", + "which": "2.0.2" } }, "karma-ie-launcher": { @@ -41290,7 +13247,7 @@ "integrity": "sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw=", "dev": true, "requires": { - "lodash": "^4.6.1" + "lodash": "4.17.21" } }, "karma-mocha": { @@ -41299,7 +13256,7 @@ "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", "dev": true, "requires": { - "minimist": "^1.2.3" + "minimist": "1.2.5" } }, "karma-mocha-reporter": { @@ -41308,9 +13265,9 @@ "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", "dev": true, "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "strip-ansi": "^4.0.0" + "chalk": "2.4.2", + "log-symbols": "2.2.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -41325,7 +13282,7 @@ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "2.4.2" } }, "strip-ansi": { @@ -41334,7 +13291,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -41364,12 +13321,12 @@ "dev": true }, "karma-sourcemap-loader": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz", - "integrity": "sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.8.tgz", + "integrity": "sha512-zorxyAakYZuBcHRJE+vbrK2o2JXLFWK8VVjiT/6P+ltLBUGUvqTEkUiQ119MGdOrK7mrmxXHZF1/pfT6GgIZ6g==", "dev": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.2.6" } }, "karma-spec-reporter": { @@ -41378,21 +13335,21 @@ "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", "dev": true, "requires": { - "colors": "^1.1.2" + "colors": "1.4.0" } }, "karma-webpack": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-3.0.5.tgz", - "integrity": "sha1-H/HjppD7c66V7pX5q1jzQc/HtA8=", + "integrity": "sha512-nRudGJWstvVuA6Tbju9tyGUfXTtI1UXMXoRHVmM2/78D0q6s/Ye2IC157PKNDC15PWFGR0mVIRtWLAdcfsRJoA==", "dev": true, "requires": { - "async": "^2.0.0", - "babel-runtime": "^6.0.0", - "loader-utils": "^1.0.0", - "lodash": "^4.0.0", - "source-map": "^0.5.6", - "webpack-dev-middleware": "^2.0.6" + "async": "2.6.3", + "babel-runtime": "6.26.0", + "loader-utils": "1.4.0", + "lodash": "4.17.21", + "source-map": "0.5.7", + "webpack-dev-middleware": "2.0.6" }, "dependencies": { "async": { @@ -41401,7 +13358,7 @@ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "4.17.21" } }, "json5": { @@ -41410,7 +13367,7 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.5" } }, "loader-utils": { @@ -41419,17 +13376,17 @@ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "big.js": "5.2.2", + "emojis-list": "3.0.0", + "json5": "1.0.1" } } } }, "keyv": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", - "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -41447,8 +13404,8 @@ "integrity": "sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg==", "dev": true, "requires": { - "@babel/parser": "^7.10.5", - "@babel/traverse": "^7.10.5" + "@babel/parser": "7.14.7", + "@babel/traverse": "7.14.7" } }, "last-run": { @@ -41457,8 +13414,8 @@ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, "requires": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" + "default-resolution": "2.0.0", + "es6-weak-map": "2.0.3" } }, "lazy-cache": { @@ -41473,7 +13430,7 @@ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, "requires": { - "readable-stream": "^2.0.5" + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -41482,13 +13439,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -41499,7 +13465,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "lcov-parse": { @@ -41514,32 +13480,17 @@ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", "dev": true, "requires": { - "flush-write-stream": "^1.0.2" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" + "flush-write-stream": "1.1.1" } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.2.1", + "type-check": "0.4.0" } }, "liftoff": { @@ -41548,14 +13499,14 @@ "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "extend": "3.0.2", + "findup-sync": "3.0.0", + "fined": "1.2.0", + "flagged-respawn": "1.0.1", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.20.0" } }, "lighthouse-logger": { @@ -41564,8 +13515,8 @@ "integrity": "sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw==", "dev": true, "requires": { - "debug": "^2.6.8", - "marky": "^1.2.0" + "debug": "2.6.9", + "marky": "1.2.2" } }, "lines-and-columns": { @@ -41600,11 +13551,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.2.6", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "loader-runner": { @@ -41619,18 +13570,18 @@ "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "big.js": "5.2.2", + "emojis-list": "3.0.0", + "json5": "2.2.0" } }, "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "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": "^5.0.0" + "p-locate": "4.1.0" } }, "lodash": { @@ -41663,7 +13614,7 @@ "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1" + "lodash._htmlescapes": "2.4.1" } }, "lodash._escapestringchar": { @@ -41726,8 +13677,8 @@ "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash._root": { @@ -41742,7 +13693,7 @@ "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } }, "lodash.camelcase": { @@ -41764,6 +13715,12 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "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=", + "dev": true + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -41782,9 +13739,9 @@ "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", "dev": true, "requires": { - "lodash._escapehtmlchar": "~2.4.1", - "lodash._reunescapedhtml": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash.flatten": { @@ -41841,9 +13798,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" }, "dependencies": { "lodash.isobject": { @@ -41852,7 +13809,7 @@ "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } } } @@ -41887,13 +13844,13 @@ "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", "dev": true, "requires": { - "lodash._escapestringchar": "~2.4.1", - "lodash._reinterpolate": "~2.4.1", - "lodash.defaults": "~2.4.1", - "lodash.escape": "~2.4.1", - "lodash.keys": "~2.4.1", - "lodash.templatesettings": "~2.4.1", - "lodash.values": "~2.4.1" + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" }, "dependencies": { "lodash.defaults": { @@ -41902,8 +13859,8 @@ "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" } } } @@ -41914,8 +13871,8 @@ "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", "dev": true, "requires": { - "lodash._reinterpolate": "~2.4.1", - "lodash.escape": "~2.4.1" + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" } }, "lodash.truncate": { @@ -41936,7 +13893,7 @@ "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", "dev": true, "requires": { - "lodash.keys": "~2.4.1" + "lodash.keys": "2.4.1" } }, "lodash.zip": { @@ -41957,8 +13914,8 @@ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "chalk": "4.1.1", + "is-unicode-supported": "0.1.0" }, "dependencies": { "ansi-styles": { @@ -41967,7 +13924,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -41976,8 +13933,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -41986,7 +13943,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -42007,7 +13964,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -42018,11 +13975,11 @@ "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", "dev": true, "requires": { - "date-format": "^3.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.1", - "rfdc": "^1.1.4", - "streamroller": "^2.2.4" + "date-format": "3.0.0", + "debug": "4.3.1", + "flatted": "2.0.2", + "rfdc": "1.3.0", + "streamroller": "2.2.4" }, "dependencies": { "debug": { @@ -42049,9 +14006,9 @@ } }, "loglevel": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", - "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", "dev": true }, "loglevel-plugin-prefix": { @@ -42063,11 +14020,11 @@ "loglevelnext": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha1-NvxPWZbWZA9Tn/IDuoGWQWgNdaI=", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", "dev": true, "requires": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" + "es6-symbol": "3.1.3", + "object.assign": "4.1.2" } }, "lolex": { @@ -42088,23 +14045,14 @@ "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", "dev": true }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.3" } }, "lowercase-keys": { @@ -42119,8 +14067,8 @@ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "lru-queue": { @@ -42129,7 +14077,7 @@ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "requires": { - "es5-ext": "~0.10.2" + "es5-ext": "0.10.53" } }, "magic-string": { @@ -42139,25 +14087,16 @@ "dev": true, "optional": true, "requires": { - "sourcemap-codec": "^1.4.4" + "sourcemap-codec": "1.4.8" } }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } + "semver": "6.3.0" } }, "make-iterator": { @@ -42166,7 +14105,7 @@ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.3" } }, "map-cache": { @@ -42193,7 +14132,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "markdown-table": { @@ -42202,7 +14141,7 @@ "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", "dev": true, "requires": { - "repeat-string": "^1.0.0" + "repeat-string": "1.6.1" } }, "marky": { @@ -42217,22 +14156,74 @@ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "dev": true, "requires": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", + "findup-sync": "2.0.0", + "micromatch": "3.1.10", + "resolve": "1.20.0", "stack-trace": "0.0.10" }, "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "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.1" + } + } + } + }, + "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.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.1" + } + } + } + }, "findup-sync": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" } }, "is-glob": { @@ -42241,7 +14232,58 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" + } + }, + "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.2.2" + }, + "dependencies": { + "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.6" + } + } + } + }, + "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.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "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": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" } } } @@ -42258,9 +14300,9 @@ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "hash-base": "3.1.0", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "mdast-util-definitions": { @@ -42269,7 +14311,7 @@ "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", "dev": true, "requires": { - "unist-util-visit": "^2.0.0" + "unist-util-visit": "2.0.3" } }, "mdast-util-find-and-replace": { @@ -42278,9 +14320,9 @@ "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", "dev": true, "requires": { - "escape-string-regexp": "^4.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" + "escape-string-regexp": "4.0.0", + "unist-util-is": "4.1.0", + "unist-util-visit-parents": "3.1.1" }, "dependencies": { "escape-string-regexp": { @@ -42297,11 +14339,11 @@ "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dev": true, "requires": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" + "@types/mdast": "3.0.3", + "mdast-util-to-string": "2.0.0", + "micromark": "2.11.4", + "parse-entities": "2.0.0", + "unist-util-stringify-position": "2.0.3" }, "dependencies": { "mdast-util-to-string": { @@ -42318,11 +14360,11 @@ "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", "dev": true, "requires": { - "mdast-util-gfm-autolink-literal": "^0.1.0", - "mdast-util-gfm-strikethrough": "^0.2.0", - "mdast-util-gfm-table": "^0.1.0", - "mdast-util-gfm-task-list-item": "^0.1.0", - "mdast-util-to-markdown": "^0.6.1" + "mdast-util-gfm-autolink-literal": "0.1.3", + "mdast-util-gfm-strikethrough": "0.2.3", + "mdast-util-gfm-table": "0.1.6", + "mdast-util-gfm-task-list-item": "0.1.6", + "mdast-util-to-markdown": "0.6.5" } }, "mdast-util-gfm-autolink-literal": { @@ -42331,9 +14373,9 @@ "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", "dev": true, "requires": { - "ccount": "^1.0.0", - "mdast-util-find-and-replace": "^1.1.0", - "micromark": "^2.11.3" + "ccount": "1.1.0", + "mdast-util-find-and-replace": "1.1.1", + "micromark": "2.11.4" } }, "mdast-util-gfm-strikethrough": { @@ -42342,7 +14384,7 @@ "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", "dev": true, "requires": { - "mdast-util-to-markdown": "^0.6.0" + "mdast-util-to-markdown": "0.6.5" } }, "mdast-util-gfm-table": { @@ -42351,8 +14393,8 @@ "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", "dev": true, "requires": { - "markdown-table": "^2.0.0", - "mdast-util-to-markdown": "~0.6.0" + "markdown-table": "2.0.0", + "mdast-util-to-markdown": "0.6.5" } }, "mdast-util-gfm-task-list-item": { @@ -42361,7 +14403,7 @@ "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", "dev": true, "requires": { - "mdast-util-to-markdown": "~0.6.0" + "mdast-util-to-markdown": "0.6.5" } }, "mdast-util-inject": { @@ -42370,7 +14412,7 @@ "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=", "dev": true, "requires": { - "mdast-util-to-string": "^1.0.0" + "mdast-util-to-string": "1.1.0" } }, "mdast-util-to-hast": { @@ -42379,14 +14421,14 @@ "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", "dev": true, "requires": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" + "@types/mdast": "3.0.3", + "@types/unist": "2.0.3", + "mdast-util-definitions": "4.0.0", + "mdurl": "1.0.1", + "unist-builder": "2.0.3", + "unist-util-generated": "1.1.6", + "unist-util-position": "3.1.0", + "unist-util-visit": "2.0.3" } }, "mdast-util-to-markdown": { @@ -42395,12 +14437,12 @@ "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", "dev": true, "requires": { - "@types/unist": "^2.0.0", - "longest-streak": "^2.0.0", - "mdast-util-to-string": "^2.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.0.0", - "zwitch": "^1.0.0" + "@types/unist": "2.0.3", + "longest-streak": "2.0.4", + "mdast-util-to-string": "2.0.0", + "parse-entities": "2.0.0", + "repeat-string": "1.6.1", + "zwitch": "1.0.5" }, "dependencies": { "mdast-util-to-string": { @@ -42423,22 +14465,28 @@ "integrity": "sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g==", "dev": true, "requires": { - "@types/mdast": "^3.0.3", - "@types/unist": "^2.0.3", - "extend": "^3.0.2", - "github-slugger": "^1.2.1", - "mdast-util-to-string": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit": "^2.0.0" + "@types/mdast": "3.0.3", + "@types/unist": "2.0.3", + "extend": "3.0.2", + "github-slugger": "1.3.0", + "mdast-util-to-string": "2.0.0", + "unist-util-is": "4.1.0", + "unist-util-visit": "2.0.3" }, "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + }, "github-slugger": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", "dev": true, "requires": { - "emoji-regex": ">=6.0.0 <=6.1.1" + "emoji-regex": "6.1.1" } }, "mdast-util-to-string": { @@ -42457,7 +14505,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -42466,7 +14514,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" }, "dependencies": { "mimic-fn": { @@ -42478,19 +14526,27 @@ } }, "memoizee": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.45", - "es6-weak-map": "^2.0.2", - "event-emitter": "^0.3.5", - "is-promise": "^2.1", - "lru-queue": "0.1", - "next-tick": "1", - "timers-ext": "^0.1.5" + "d": "1.0.1", + "es5-ext": "0.10.53", + "es6-weak-map": "2.0.3", + "event-emitter": "0.3.5", + "is-promise": "2.2.2", + "lru-queue": "0.1.0", + "next-tick": "1.1.0", + "timers-ext": "0.1.7" + }, + "dependencies": { + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + } } }, "memory-fs": { @@ -42499,8 +14555,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.8", + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -42509,50 +14565,199 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "1.2.1", + "camelcase-keys": "6.2.2", + "decamelize-keys": "1.1.0", + "hard-rejection": "2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "3.0.2", + "read-pkg-up": "7.0.1", + "redent": "3.0.0", + "trim-newlines": "3.0.1", + "type-fest": "0.18.1", + "yargs-parser": "20.2.9" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "5.3.1", + "map-obj": "4.2.1", + "quick-lru": "4.0.1" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "4.0.2", + "resolve": "1.20.0", + "semver": "7.3.5", + "validate-npm-package-license": "3.0.4" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "7.14.5", + "error-ex": "1.3.2", + "json-parse-even-better-errors": "2.3.1", + "lines-and-columns": "1.1.6" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "2.4.0", + "normalize-package-data": "2.5.0", + "parse-json": "5.2.0", + "type-fest": "0.6.0" + }, + "dependencies": { + "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.8.9", + "resolve": "1.20.0", + "semver": "5.7.1", + "validate-npm-package-license": "3.0.4" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "4.1.0", + "read-pkg": "5.2.0", + "type-fest": "0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } - } - } - }, - "meow": { - "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "lru-cache": "6.0.0" } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -42567,7 +14772,7 @@ "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "requires": { - "source-map": "^0.6.1" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -42595,8 +14800,8 @@ "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", "dev": true, "requires": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" + "debug": "4.3.1", + "parse-entities": "2.0.0" }, "dependencies": { "debug": { @@ -42622,12 +14827,12 @@ "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", "dev": true, "requires": { - "micromark": "~2.11.0", - "micromark-extension-gfm-autolink-literal": "~0.5.0", - "micromark-extension-gfm-strikethrough": "~0.6.5", - "micromark-extension-gfm-table": "~0.4.0", - "micromark-extension-gfm-tagfilter": "~0.3.0", - "micromark-extension-gfm-task-list-item": "~0.3.0" + "micromark": "2.11.4", + "micromark-extension-gfm-autolink-literal": "0.5.7", + "micromark-extension-gfm-strikethrough": "0.6.5", + "micromark-extension-gfm-table": "0.4.3", + "micromark-extension-gfm-tagfilter": "0.3.0", + "micromark-extension-gfm-task-list-item": "0.3.3" } }, "micromark-extension-gfm-autolink-literal": { @@ -42636,7 +14841,7 @@ "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", "dev": true, "requires": { - "micromark": "~2.11.3" + "micromark": "2.11.4" } }, "micromark-extension-gfm-strikethrough": { @@ -42645,7 +14850,7 @@ "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", "dev": true, "requires": { - "micromark": "~2.11.0" + "micromark": "2.11.4" } }, "micromark-extension-gfm-table": { @@ -42654,7 +14859,7 @@ "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", "dev": true, "requires": { - "micromark": "~2.11.0" + "micromark": "2.11.4" } }, "micromark-extension-gfm-tagfilter": { @@ -42669,118 +14874,17 @@ "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", "dev": true, "requires": { - "micromark": "~2.11.0" + "micromark": "2.11.4" } }, "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": { - "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-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": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "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" - } - } + "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.2", + "picomatch": "2.3.0" } }, "miller-rabin": { @@ -42789,14 +14893,14 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "bn.js": "4.12.0", + "brorand": "1.1.0" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -42807,16 +14911,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.48.0" } }, "mimic-fn": { @@ -42855,7 +14959,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -42870,9 +14974,9 @@ "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "arrify": "1.0.1", + "is-plain-obj": "1.1.0", + "kind-of": "6.0.3" }, "dependencies": { "is-plain-obj": { @@ -42889,8 +14993,8 @@ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -42899,7 +15003,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -42935,10 +15039,16 @@ "supports-color": "5.4.0" }, "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -42947,21 +15057,21 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "he": { @@ -42988,10 +15098,10 @@ "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -43008,22 +15118,22 @@ "integrity": "sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw==", "dev": true, "requires": { - "browser-resolve": "^1.7.0", - "cached-path-relative": "^1.0.0", - "concat-stream": "~1.5.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "konan": "^2.1.1", - "readable-stream": "^2.0.2", - "resolve": "^1.1.3", - "standard-version": "^9.0.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" + "JSONStream": "1.3.5", + "browser-resolve": "1.11.3", + "cached-path-relative": "1.0.2", + "concat-stream": "1.5.2", + "defined": "1.0.0", + "detective": "5.2.0", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "konan": "2.1.1", + "readable-stream": "2.3.7", + "resolve": "1.20.0", + "standard-version": "9.3.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.5", + "xtend": "4.0.2" }, "dependencies": { "concat-stream": { @@ -43032,62 +15142,79 @@ "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", "dev": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "~2.0.0", - "typedarray": "~0.0.5" + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" }, "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, "readable-stream": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } - }, - "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 } } }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, + "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 + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -43098,11 +15225,11 @@ "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "dev": true, "requires": { - "basic-auth": "~2.0.1", + "basic-auth": "2.0.1", "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "depd": "2.0.0", + "on-finished": "2.3.0", + "on-headers": "1.0.2" }, "dependencies": { "depd": { @@ -43133,7 +15260,7 @@ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "dev": true, "requires": { - "readable-stream": "~1.1.9" + "readable-stream": "1.1.14" } }, "isarray": { @@ -43144,19 +15271,19 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "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", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } @@ -43175,9 +15302,9 @@ "dev": true }, "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "dev": true, "optional": true }, @@ -43185,8 +15312,7 @@ "version": "3.1.23", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -43194,17 +15320,17 @@ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-windows": "1.0.2", + "kind-of": "6.0.3", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "natural-compare": { @@ -43215,7 +15341,7 @@ }, "ncp": { "version": "0.4.2", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", "dev": true }, @@ -43232,21 +15358,27 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "nise": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", "dev": true, "requires": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" + "@sinonjs/formatio": "3.2.2", + "@sinonjs/text-encoding": "0.7.1", + "just-extend": "4.2.1", + "lolex": "5.1.2", + "path-to-regexp": "1.8.0" }, "dependencies": { "@sinonjs/formatio": { @@ -43255,8 +15387,8 @@ "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", "dev": true, "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/commons": "1.8.3", + "@sinonjs/samsam": "3.3.3" } }, "isarray": { @@ -43271,7 +15403,7 @@ "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "1.8.3" } }, "path-to-regexp": { @@ -43297,29 +15429,29 @@ "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", + "assert": "1.5.0", + "browserify-zlib": "0.2.0", + "buffer": "4.9.2", + "console-browserify": "1.2.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "3.3.0", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.7", + "stream-browserify": "2.0.2", + "stream-http": "2.8.3", + "string_decoder": "1.3.0", + "timers-browserify": "2.0.12", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" + "url": "0.11.0", + "util": "0.11.1", + "vm-browserify": "1.1.2" }, "dependencies": { "buffer": { @@ -43328,9 +15460,9 @@ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.5.1", + "ieee754": "1.2.1", + "isarray": "1.0.0" } }, "punycode": { @@ -43345,13 +15477,24 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } } } @@ -43368,7 +15511,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } }, "normalize-package-data": { @@ -43377,10 +15520,18 @@ "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" + "hosted-git-info": "2.8.9", + "resolve": "1.20.0", + "semver": "5.7.1", + "validate-npm-package-license": "3.0.4" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "normalize-path": { @@ -43390,9 +15541,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true }, "now-and-later": { @@ -43401,7 +15552,7 @@ "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, "requires": { - "once": "^1.3.2" + "once": "1.4.0" } }, "npm-run-path": { @@ -43410,7 +15561,15 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" + }, + "dependencies": { + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + } } }, "null-check": { @@ -43443,9 +15602,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -43454,22 +15613,16 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, - "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" + "is-buffer": "1.1.6" } } } @@ -43481,13 +15634,13 @@ "dev": true }, "object-is": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", - "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "1.0.2", + "define-properties": "1.1.3" } }, "object-keys": { @@ -43502,19 +15655,19 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "call-bind": "1.0.2", + "define-properties": "1.1.3", + "has-symbols": "1.0.2", + "object-keys": "1.1.1" } }, "object.defaults": { @@ -43523,10 +15676,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" } }, "object.map": { @@ -43535,8 +15688,8 @@ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.omit": { @@ -43545,8 +15698,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" }, "dependencies": { "for-own": { @@ -43555,7 +15708,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } } } @@ -43566,7 +15719,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "object.reduce": { @@ -43575,20 +15728,19 @@ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "call-bind": "1.0.2", + "define-properties": "1.1.3", + "es-abstract": "1.18.3" } }, "on-finished": { @@ -43611,7 +15763,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -43620,22 +15772,22 @@ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "mimic-fn": "^2.1.0" + "mimic-fn": "2.1.0" } }, "opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true }, "opn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", - "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" }, "dependencies": { "is-wsl": { @@ -43652,13 +15804,13 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.10", + "wordwrap": "0.0.3" }, "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -43671,17 +15823,17 @@ } }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.4.1", + "prelude-ls": "1.2.1", + "type-check": "0.4.0", + "word-wrap": "1.2.3" } }, "ora": { @@ -43690,15 +15842,15 @@ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "bl": "4.1.0", + "chalk": "4.1.1", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.0", + "is-interactive": "1.0.0", + "is-unicode-supported": "0.1.0", + "log-symbols": "4.1.0", + "strip-ansi": "6.0.0", + "wcwidth": "1.0.1" }, "dependencies": { "ansi-styles": { @@ -43707,7 +15859,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -43716,8 +15868,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -43726,7 +15878,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -43747,7 +15899,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -43758,7 +15910,7 @@ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -43767,13 +15919,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -43785,48 +15946,12 @@ "dev": true }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } + "lcid": "1.0.0" } }, "os-tmpdir": { @@ -43836,9 +15961,9 @@ "dev": true }, "p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true }, "p-finally": { @@ -43853,27 +15978,16 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.2.0" } }, "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "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": "^3.0.2" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - } + "p-limit": "2.3.0" } }, "p-try": { @@ -43894,21 +16008,20 @@ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { - "callsites": "^3.0.0" + "callsites": "3.1.0" } }, "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", "dev": true, "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "asn1.js": "5.4.1", + "browserify-aes": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.1.2", + "safe-buffer": "5.1.2" } }, "parse-entities": { @@ -43917,12 +16030,12 @@ "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dev": true, "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "character-entities": "1.2.4", + "character-entities-legacy": "1.1.4", + "character-reference-invalid": "1.1.4", + "is-alphanumerical": "1.0.4", + "is-decimal": "1.0.4", + "is-hexadecimal": "1.0.4" } }, "parse-filepath": { @@ -43931,9 +16044,9 @@ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-github-repo-url": { @@ -43948,10 +16061,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" }, "dependencies": { "is-extglob": { @@ -43966,7 +16079,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -43977,7 +16090,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.2" } }, "parse-ms": { @@ -44004,10 +16117,10 @@ "integrity": "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" + "is-ssh": "1.3.3", + "protocols": "1.4.8", + "qs": "6.10.1", + "query-string": "6.14.1" }, "dependencies": { "qs": { @@ -44016,27 +16129,27 @@ "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", "dev": true, "requires": { - "side-channel": "^1.0.4" + "side-channel": "1.0.4" } } } }, "parse-url": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.3.tgz", - "integrity": "sha512-nrLCVMJpqo12X8uUJT4GJPd5AFaTOrGx/QpJy3HNcVtq0AZSstVIsnxS5fqNPuoqMUs3MyfBoOP6Zvu2Arok5A==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.5.tgz", + "integrity": "sha512-AwfVhXaQrNNI6UPUJq/GJN2qoY0L9gPgxhh9VbDP0bfBAJWaC/Zh8hjQ58YKTi4AagOT70fpadkYSKPo+eFb1w==", "dev": true, "requires": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.0.1", - "parse-path": "^4.0.0", - "protocols": "^1.4.0" + "is-ssh": "1.3.3", + "normalize-url": "4.5.0", + "parse-path": "4.0.3", + "protocols": "1.4.8" }, "dependencies": { "normalize-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", - "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "dev": true } } @@ -44072,7 +16185,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -44083,15 +16196,15 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-root": { @@ -44100,7 +16213,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -44120,42 +16233,42 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.2.6", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "pause-stream": { "version": "0.0.11", - "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "pbkdf2-compat": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", "integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=", "dev": true }, @@ -44195,7 +16308,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -44204,36 +16317,66 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^4.0.0" + "find-up": "4.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "2.1.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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "2.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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-try": "1.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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "1.3.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=", + "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=", + "dev": true } } }, @@ -44243,10 +16386,10 @@ "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" + "ansi-colors": "1.1.0", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" }, "dependencies": { "ansi-colors": { @@ -44255,7 +16398,7 @@ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { - "ansi-wrap": "^0.1.0" + "ansi-wrap": "0.1.0" } } } @@ -44273,15 +16416,15 @@ "dev": true }, "postcss": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.4.tgz", - "integrity": "sha512-/tZY0PXExXXnNhKv3TOvZAOUYRyuqcCbBm2c17YMDK0PlVII3K7/LKdt3ScHL+hhouddjUWi+1sKDf9xXW+8YA==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz", + "integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==", "dev": true, "optional": true, "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" + "colorette": "1.2.2", + "nanoid": "3.1.23", + "source-map-js": "0.6.2" } }, "postcss-modules": { @@ -44291,14 +16434,14 @@ "dev": true, "optional": true, "requires": { - "generic-names": "^2.0.1", - "icss-replace-symbols": "^1.1.0", - "lodash.camelcase": "^4.3.0", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "string-hash": "^1.1.1" + "generic-names": "2.0.1", + "icss-replace-symbols": "1.1.0", + "lodash.camelcase": "4.3.0", + "postcss-modules-extract-imports": "3.0.0", + "postcss-modules-local-by-default": "4.0.0", + "postcss-modules-scope": "3.0.0", + "postcss-modules-values": "4.0.0", + "string-hash": "1.1.3" } }, "postcss-modules-extract-imports": { @@ -44306,8 +16449,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "dev": true, - "optional": true, - "requires": {} + "optional": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -44316,9 +16458,9 @@ "dev": true, "optional": true, "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" + "icss-utils": "5.1.0", + "postcss-selector-parser": "6.0.6", + "postcss-value-parser": "4.1.0" } }, "postcss-modules-scope": { @@ -44328,7 +16470,7 @@ "dev": true, "optional": true, "requires": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "6.0.6" } }, "postcss-modules-values": { @@ -44338,7 +16480,7 @@ "dev": true, "optional": true, "requires": { - "icss-utils": "^5.0.0" + "icss-utils": "5.1.0" } }, "postcss-selector-parser": { @@ -44346,10 +16488,9 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", "dev": true, - "optional": true, "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "cssesc": "3.0.0", + "util-deprecate": "1.0.2" } }, "postcss-value-parser": { @@ -44360,9 +16501,9 @@ "optional": true }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "preserve": { @@ -44377,10 +16518,10 @@ "integrity": "sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==", "dev": true, "requires": { - "@jest/types": "^27.0.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "@jest/types": "27.0.2", + "ansi-regex": "5.0.0", + "ansi-styles": "5.2.0", + "react-is": "17.0.2" }, "dependencies": { "ansi-styles": { @@ -44393,17 +16534,17 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, "pretty-ms": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", - "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", "dev": true, "requires": { - "parse-ms": "^2.1.0" + "parse-ms": "2.1.0" } }, "printj": { @@ -44436,7 +16577,7 @@ "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dev": true, "requires": { - "xtend": "^4.0.0" + "xtend": "4.0.2" } }, "protocols": { @@ -44446,11 +16587,11 @@ "dev": true }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, @@ -44472,7 +16613,7 @@ "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", "dev": true, "requires": { - "event-stream": "=3.3.4" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -44493,18 +16634,18 @@ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "bn.js": "4.12.0", + "browserify-rsa": "4.1.0", + "create-hash": "1.2.0", + "parse-asn1": "5.1.6", + "randombytes": "2.1.0", + "safe-buffer": "5.1.2" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -44515,8 +16656,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.4", + "once": "1.4.0" } }, "pumpify": { @@ -44525,9 +16666,9 @@ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.7.1", + "inherits": "2.0.3", + "pump": "2.0.1" }, "dependencies": { "pump": { @@ -44536,8 +16677,8 @@ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.4", + "once": "1.4.0" } } } @@ -44554,18 +16695,18 @@ "integrity": "sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==", "dev": true, "requires": { - "debug": "^4.1.0", + "debug": "4.3.1", "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" + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.0", + "node-fetch": "2.6.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "7.5.0" }, "dependencies": { "agent-base": { @@ -44574,7 +16715,7 @@ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "requires": { - "debug": "4" + "debug": "4.3.1" } }, "debug": { @@ -44598,8 +16739,8 @@ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, "requires": { - "agent-base": "6", - "debug": "4" + "agent-base": "6.0.2", + "debug": "4.3.1" } }, "ms": { @@ -44614,7 +16755,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "7.1.7" } } } @@ -44648,10 +16789,10 @@ "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "dev": true, "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" + "decode-uri-component": "0.2.0", + "filter-obj": "1.1.0", + "split-on-first": "1.1.0", + "strict-uri-encode": "2.0.0" } }, "querystring": { @@ -44684,9 +16825,9 @@ "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", "dev": true, "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "is-number": "4.0.0", + "kind-of": "6.0.3", + "math-random": "1.0.4" }, "dependencies": { "is-number": { @@ -44703,7 +16844,7 @@ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { - "safe-buffer": "^5.1.0" + "safe-buffer": "5.1.2" } }, "randomfill": { @@ -44712,8 +16853,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "randombytes": "2.1.0", + "safe-buffer": "5.1.2" } }, "range-parser": { @@ -44744,9 +16885,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.5.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -44755,8 +16896,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -44765,8 +16906,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "path-exists": { @@ -44775,7 +16916,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } } } @@ -44786,9 +16927,9 @@ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "2.0.3", + "string_decoder": "1.3.0", + "util-deprecate": "1.0.2" } }, "readdir-glob": { @@ -44797,16 +16938,16 @@ "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", "dev": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "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==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "picomatch": "2.3.0" } }, "rechoir": { @@ -44815,7 +16956,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.20.0" } }, "recursive-readdir": { @@ -44828,30 +16969,27 @@ } }, "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "4.0.0", + "strip-indent": "3.0.0" }, "dependencies": { "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true } } }, "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, "regenerate-unicode-properties": { @@ -44860,7 +16998,7 @@ "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", "dev": true, "requires": { - "regenerate": "^1.4.0" + "regenerate": "1.4.2" } }, "regenerator-runtime": { @@ -44874,7 +17012,7 @@ "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, "requires": { - "@babel/runtime": "^7.8.4" + "@babel/runtime": "7.14.6" } }, "regex-cache": { @@ -44883,7 +17021,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regex-not": { @@ -44892,18 +17030,18 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "call-bind": "1.0.2", + "define-properties": "1.1.3" } }, "regexpp": { @@ -44913,17 +17051,17 @@ "dev": true }, "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "regenerate": "1.4.2", + "regenerate-unicode-properties": "8.2.0", + "regjsgen": "0.5.2", + "regjsparser": "0.6.9", + "unicode-match-property-ecmascript": "1.0.4", + "unicode-match-property-value-ecmascript": "1.2.0" } }, "regjsgen": { @@ -44933,17 +17071,17 @@ "dev": true }, "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -44955,9 +17093,9 @@ "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", "dev": true, "requires": { - "remark-parse": "^9.0.0", - "remark-stringify": "^9.0.0", - "unified": "^9.1.0" + "remark-parse": "9.0.0", + "remark-stringify": "9.0.1", + "unified": "9.2.1" } }, "remark-gfm": { @@ -44966,8 +17104,8 @@ "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", "dev": true, "requires": { - "mdast-util-gfm": "^0.1.0", - "micromark-extension-gfm": "^0.3.0" + "mdast-util-gfm": "0.1.2", + "micromark-extension-gfm": "0.3.3" } }, "remark-html": { @@ -44976,9 +17114,9 @@ "integrity": "sha512-K5KQCXWVz+harnyC+UVM/J9eJWCgjYRqFeZoZf2NgP0iFbuuw/RgMZv3MA34b/OEpGnstl3oiOUtZzD3tJ+CBw==", "dev": true, "requires": { - "hast-util-sanitize": "^3.0.0", - "hast-util-to-html": "^7.0.0", - "mdast-util-to-hast": "^10.0.0" + "hast-util-sanitize": "3.0.2", + "hast-util-to-html": "7.1.3", + "mdast-util-to-hast": "10.2.0" } }, "remark-parse": { @@ -44987,7 +17125,7 @@ "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "dev": true, "requires": { - "mdast-util-from-markdown": "^0.8.0" + "mdast-util-from-markdown": "0.8.5" } }, "remark-reference-links": { @@ -44996,7 +17134,7 @@ "integrity": "sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ==", "dev": true, "requires": { - "unist-util-visit": "^2.0.0" + "unist-util-visit": "2.0.3" } }, "remark-stringify": { @@ -45005,7 +17143,7 @@ "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", "dev": true, "requires": { - "mdast-util-to-markdown": "^0.6.0" + "mdast-util-to-markdown": "0.6.5" } }, "remark-toc": { @@ -45014,8 +17152,8 @@ "integrity": "sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg==", "dev": true, "requires": { - "@types/unist": "^2.0.3", - "mdast-util-toc": "^5.0.0" + "@types/unist": "2.0.3", + "mdast-util-toc": "5.1.0" } }, "remove-bom-buffer": { @@ -45024,16 +17162,8 @@ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "requires": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" - }, - "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 - } + "is-buffer": "1.1.6", + "is-utf8": "0.2.1" } }, "remove-bom-stream": { @@ -45042,9 +17172,9 @@ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", "dev": true, "requires": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "remove-bom-buffer": "3.0.0", + "safe-buffer": "5.1.2", + "through2": "2.0.5" }, "dependencies": { "readable-stream": { @@ -45053,13 +17183,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -45068,8 +17207,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -45081,9 +17220,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true }, "repeat-string": { @@ -45098,13 +17237,13 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.1.0" } }, "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", "dev": true }, "replace-homedir": { @@ -45113,9 +17252,9 @@ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" + "homedir-polyfill": "1.0.3", + "is-absolute": "1.0.0", + "remove-trailing-separator": "1.1.0" } }, "replacestream": { @@ -45124,9 +17263,9 @@ "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1", + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -45135,13 +17274,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -45152,26 +17300,26 @@ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.11.0", + "caseless": "0.12.0", + "combined-stream": "1.0.8", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.3", + "har-validator": "5.1.5", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.31", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.5.0", + "tunnel-agent": "0.6.0", + "uuid": "3.4.0" }, "dependencies": { "qs": { @@ -45212,8 +17360,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" }, "dependencies": { "resolve-from": { @@ -45236,14 +17384,14 @@ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "2.4.0", + "path-parse": "1.0.7" } }, "resolve-alpn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", - "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz", + "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==", "dev": true }, "resolve-dir": { @@ -45252,8 +17400,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } }, "resolve-from": { @@ -45268,7 +17416,7 @@ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", "dev": true, "requires": { - "value-or-function": "^3.0.0" + "value-or-function": "3.0.0" } }, "resolve-url": { @@ -45283,7 +17431,7 @@ "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "dev": true, "requires": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "2.0.0" } }, "resq": { @@ -45292,7 +17440,7 @@ "integrity": "sha512-hCUd0xMalqtPDz4jXIqs0M5Wnv/LZXN8h7unFOo4/nvExT9dDPbhwd3udRxLlp0HgBnHcV009UlduE9NZi7A6w==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1" + "fast-deep-equal": "2.0.1" }, "dependencies": { "fast-deep-equal": { @@ -45309,8 +17457,8 @@ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "onetime": "5.1.2", + "signal-exit": "3.0.3" } }, "ret": { @@ -45337,7 +17485,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -45346,7 +17494,7 @@ "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.7" } }, "ripemd160": { @@ -45355,8 +17503,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "3.1.0", + "inherits": "2.0.3" } }, "run-async": { @@ -45377,7 +17525,7 @@ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", "dev": true, "requires": { - "rx-lite": "*" + "rx-lite": "4.0.8" } }, "rxjs": { @@ -45386,7 +17534,7 @@ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.14.1" } }, "safe-buffer": { @@ -45402,11 +17550,11 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -45421,34 +17569,34 @@ "dev": true }, "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "7.0.7", + "ajv": "6.12.6", + "ajv-keywords": "3.5.2" }, "dependencies": { "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + "fast-deep-equal": "3.1.3", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.4.1" } } } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "semver-greatest-satisfied-range": { @@ -45457,7 +17605,7 @@ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", "dev": true, "requires": { - "sver-compat": "^1.5.0" + "sver-compat": "1.5.0" } }, "send": { @@ -45466,18 +17614,18 @@ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "1.7.2", "mime": "1.6.0", "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "on-finished": "2.3.0", + "range-parser": "1.2.1", + "statuses": "1.5.0" }, "dependencies": { "ms": { @@ -45493,7 +17641,7 @@ "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "type-fest": "0.20.2" }, "dependencies": { "type-fest": { @@ -45510,7 +17658,7 @@ "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, "requires": { - "randombytes": "^2.1.0" + "randombytes": "2.1.0" } }, "serve-index": { @@ -45519,13 +17667,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.7", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "escape-html": "1.0.3", + "http-errors": "1.6.3", + "mime-types": "2.1.31", + "parseurl": "1.3.3" }, "dependencies": { "http-errors": { @@ -45534,10 +17682,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.5.0" } }, "setprototypeof": { @@ -45553,9 +17701,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.3", "send": "0.17.1" } }, @@ -45571,10 +17719,10 @@ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -45583,7 +17731,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -45601,27 +17749,27 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "shelljs": { @@ -45630,9 +17778,9 @@ "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.7", + "interpret": "1.4.0", + "rechoir": "0.6.2" } }, "side-channel": { @@ -45641,9 +17789,9 @@ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "1.0.2", + "get-intrinsic": "1.1.1", + "object-inspect": "1.10.3" } }, "signal-exit": { @@ -45654,17 +17802,17 @@ }, "sinon": { "version": "4.5.0", - "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" + "@sinonjs/formatio": "2.0.0", + "diff": "3.5.0", + "lodash.get": "4.4.2", + "lolex": "2.7.5", + "nise": "1.5.3", + "supports-color": "5.5.0", + "type-detect": "4.0.8" }, "dependencies": { "diff": { @@ -45687,9 +17835,9 @@ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-styles": "4.3.0", + "astral-regex": "2.0.0", + "is-fullwidth-code-point": "3.0.0" }, "dependencies": { "ansi-styles": { @@ -45698,7 +17846,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "color-convert": { @@ -45707,7 +17855,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -45724,14 +17872,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.3", + "use": "3.1.1" }, "dependencies": { "define-property": { @@ -45740,7 +17888,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -45749,7 +17897,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -45760,9 +17908,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -45771,7 +17919,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -45780,7 +17928,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-data-descriptor": { @@ -45789,7 +17937,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" } }, "is-descriptor": { @@ -45798,9 +17946,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" } } } @@ -45811,22 +17959,16 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.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" + "is-buffer": "1.1.6" } } } @@ -45837,15 +17979,15 @@ "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", "dev": true, "requires": { - "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.8", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.1", - "engine.io": "~4.1.0", - "socket.io-adapter": "~2.1.0", - "socket.io-parser": "~4.0.3" + "@types/cookie": "0.4.0", + "@types/cors": "2.8.10", + "@types/node": "15.12.4", + "accepts": "1.3.7", + "base64id": "2.0.0", + "debug": "4.3.1", + "engine.io": "4.1.1", + "socket.io-adapter": "2.1.0", + "socket.io-parser": "4.0.4" }, "dependencies": { "debug": { @@ -45877,9 +18019,9 @@ "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", "dev": true, "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" + "@types/component-emitter": "1.2.10", + "component-emitter": "1.3.0", + "debug": "4.3.1" }, "dependencies": { "debug": { @@ -45924,11 +18066,11 @@ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.2", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.1", + "urix": "0.1.0" } }, "source-map-support": { @@ -45937,8 +18079,8 @@ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "buffer-from": "1.1.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -45950,9 +18092,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, "sourcemap-codec": { @@ -45980,8 +18122,8 @@ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.1", + "spdx-license-ids": "3.0.9" } }, "spdx-exceptions": { @@ -45996,23 +18138,23 @@ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.3.0", + "spdx-license-ids": "3.0.9" } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, "split": { "version": "0.3.3", - "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "split-on-first": { @@ -46027,7 +18169,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "split2": { @@ -46036,7 +18178,7 @@ "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, "requires": { - "readable-stream": "^3.0.0" + "readable-stream": "3.6.0" } }, "sprintf-js": { @@ -46051,15 +18193,15 @@ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "stack-trace": { @@ -46074,7 +18216,7 @@ "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", "dev": true, "requires": { - "escape-string-regexp": "^2.0.0" + "escape-string-regexp": "2.0.0" }, "dependencies": { "escape-string-regexp": { @@ -46091,28 +18233,41 @@ "integrity": "sha512-cYxxKXhYfI3S9+CA84HmrJa9B88H56V5FQ302iFF2TNwJukJCNoU8FgWt+11YtwKFXRkQQFpepC2QOF7aDq2Ow==", "dev": true, "requires": { - "chalk": "^2.4.2", + "chalk": "2.4.2", "conventional-changelog": "3.1.24", "conventional-changelog-config-spec": "2.1.0", "conventional-changelog-conventionalcommits": "4.5.0", "conventional-recommended-bump": "6.1.0", - "detect-indent": "^6.0.0", - "detect-newline": "^3.1.0", - "dotgitignore": "^2.1.0", - "figures": "^3.1.0", - "find-up": "^5.0.0", - "fs-access": "^1.0.1", - "git-semver-tags": "^4.0.0", - "semver": "^7.1.1", - "stringify-package": "^1.0.1", - "yargs": "^16.0.0" + "detect-indent": "6.1.0", + "detect-newline": "3.1.0", + "dotgitignore": "2.1.0", + "figures": "3.2.0", + "find-up": "5.0.0", + "fs-access": "1.0.1", + "git-semver-tags": "4.1.1", + "semver": "7.3.5", + "stringify-package": "1.0.1", + "yargs": "16.2.0" }, "dependencies": { - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "6.0.0", + "path-exists": "4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "5.0.0" + } }, "lru-cache": { "version": "6.0.0", @@ -46120,7 +18275,25 @@ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^4.0.0" + "yallist": "4.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "3.1.0" } }, "semver": { @@ -46129,7 +18302,7 @@ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "lru-cache": "6.0.0" } }, "yallist": { @@ -46144,13 +18317,13 @@ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "cliui": "7.0.4", + "escalade": "3.1.1", + "get-caller-file": "2.0.5", + "require-directory": "2.1.1", + "string-width": "4.2.2", + "y18n": "5.0.8", + "yargs-parser": "20.2.9" } } } @@ -46161,8 +18334,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -46171,7 +18344,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -46187,7 +18360,7 @@ "integrity": "sha1-nl9zRfITfDDuO0mLkRToC1K7frU=", "dev": true, "requires": { - "readable-stream": "~2.1.0" + "readable-stream": "2.1.5" }, "dependencies": { "process-nextick-args": { @@ -46198,22 +18371,22 @@ }, "readable-stream": { "version": "2.1.5", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", "dev": true, "requires": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } @@ -46225,8 +18398,8 @@ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -46235,13 +18408,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -46254,11 +18436,11 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.2" } }, "stream-combiner2": { @@ -46267,8 +18449,8 @@ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "duplexer2": "0.1.4", + "readable-stream": "2.3.7" }, "dependencies": { "readable-stream": { @@ -46277,13 +18459,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -46300,11 +18491,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.7", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.2" }, "dependencies": { "readable-stream": { @@ -46313,13 +18504,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -46336,9 +18536,9 @@ "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", "dev": true, "requires": { - "date-format": "^2.1.0", - "debug": "^4.1.1", - "fs-extra": "^8.1.0" + "date-format": "2.1.0", + "debug": "4.3.1", + "fs-extra": "8.1.0" }, "dependencies": { "date-format": { @@ -46362,9 +18562,9 @@ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.2.6", + "jsonfile": "4.0.0", + "universalify": "0.1.2" } }, "jsonfile": { @@ -46373,7 +18573,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.2.6" } }, "ms": { @@ -46396,15 +18596,6 @@ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, - "string_decoder": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", @@ -46424,156 +18615,45 @@ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "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 - } + "emoji-regex": "8.0.0", + "is-fullwidth-code-point": "3.0.0", + "strip-ansi": "6.0.0" } }, "string.prototype.trimend": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", - "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "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==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" + "call-bind": "1.0.2", + "define-properties": "1.1.3" } }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "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==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" + "call-bind": "1.0.2", + "define-properties": "1.1.3" } }, - "string.prototype.trimstart": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", - "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "safe-buffer": "5.2.1" }, "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } + "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==", + "dev": true } } }, @@ -46583,9 +18663,9 @@ "integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==", "dev": true, "requires": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "xtend": "^4.0.0" + "character-entities-html4": "1.1.4", + "character-entities-legacy": "1.1.4", + "xtend": "4.0.2" } }, "stringify-package": { @@ -46600,7 +18680,7 @@ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "5.0.0" } }, "strip-bom": { @@ -46609,7 +18689,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "strip-bom-string": { @@ -46620,23 +18700,17 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "min-indent": "1.0.1" } }, "strip-json-comments": { @@ -46651,7 +18725,7 @@ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", "dev": true, "requires": { - "minimist": "^1.1.0" + "minimist": "1.2.5" } }, "suffix": { @@ -46666,7 +18740,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "sver-compat": { @@ -46675,8 +18749,8 @@ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", "dev": true, "requires": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.3" } }, "table": { @@ -46685,12 +18759,12 @@ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "ajv": "8.6.0", + "lodash.clonedeep": "4.5.0", + "lodash.truncate": "4.4.2", + "slice-ansi": "4.0.0", + "string-width": "4.2.2", + "strip-ansi": "6.0.0" }, "dependencies": { "ajv": { @@ -46699,10 +18773,10 @@ "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "fast-deep-equal": "3.1.3", + "json-schema-traverse": "1.0.0", + "require-from-string": "2.0.2", + "uri-js": "4.4.1" } }, "json-schema-traverse": { @@ -46725,10 +18799,10 @@ "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "chownr": "1.1.4", + "mkdirp-classic": "0.5.3", + "pump": "3.0.0", + "tar-stream": "2.2.0" } }, "tar-stream": { @@ -46737,11 +18811,11 @@ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "bl": "4.1.0", + "end-of-stream": "1.4.4", + "fs-constants": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "3.6.0" } }, "temp-fs": { @@ -46750,7 +18824,7 @@ "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", "dev": true, "requires": { - "rimraf": "~2.5.2" + "rimraf": "2.5.4" } }, "ternary-stream": { @@ -46759,10 +18833,10 @@ "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", "dev": true, "requires": { - "duplexify": "^4.1.1", - "fork-stream": "^0.0.4", - "merge-stream": "^2.0.0", - "through2": "^3.0.1" + "duplexify": "4.1.1", + "fork-stream": "0.0.4", + "merge-stream": "2.0.0", + "through2": "3.0.2" }, "dependencies": { "duplexify": { @@ -46771,26 +18845,28 @@ "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", "dev": true, "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.4", + "inherits": "2.0.3", + "readable-stream": "3.6.0", + "stream-shift": "1.0.1" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "through2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" + "inherits": "2.0.4", + "readable-stream": "3.6.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } } } } @@ -46801,17 +18877,11 @@ "integrity": "sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==", "dev": true, "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "commander": "2.20.3", + "source-map": "0.7.3", + "source-map-support": "0.5.19" }, "dependencies": { - "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 - }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -46833,14 +18903,14 @@ "dev": true }, "textextensions": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", - "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", "dev": true }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -46850,7 +18920,7 @@ "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, "requires": { - "readable-stream": "3" + "readable-stream": "3.6.0" } }, "through2-filter": { @@ -46859,8 +18929,8 @@ "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "through2": "2.0.5", + "xtend": "4.0.2" }, "dependencies": { "readable-stream": { @@ -46869,13 +18939,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -46884,8 +18963,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -46897,12 +18976,12 @@ "dev": true }, "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "dev": true, "requires": { - "setimmediate": "^1.0.4" + "setimmediate": "1.0.5" } }, "timers-ext": { @@ -46911,8 +18990,8 @@ "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", "dev": true, "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "es5-ext": "0.10.53", + "next-tick": "1.0.0" } }, "tiny-hashes": { @@ -46926,27 +19005,27 @@ "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", "dev": true, "requires": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" + "body": "5.1.0", + "debug": "3.2.7", + "faye-websocket": "0.10.0", + "livereload-js": "2.4.0", + "object-assign": "4.1.1", + "qs": "6.7.0" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } @@ -46957,7 +19036,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-absolute-glob": { @@ -46966,8 +19045,8 @@ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "is-absolute": "1.0.0", + "is-negated-glob": "1.0.0" } }, "to-arraybuffer": { @@ -46988,22 +19067,16 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.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" + "is-buffer": "1.1.6" } } } @@ -47014,10 +19087,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -47026,7 +19099,7 @@ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "is-number": "^7.0.0" + "is-number": "7.0.0" } }, "to-through": { @@ -47035,7 +19108,7 @@ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", "dev": true, "requires": { - "through2": "^2.0.3" + "through2": "2.0.5" }, "dependencies": { "readable-stream": { @@ -47044,13 +19117,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -47059,8 +19141,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -47076,8 +19158,8 @@ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "1.8.0", + "punycode": "2.1.1" } }, "traverse": { @@ -47087,9 +19169,9 @@ "dev": true }, "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, "trim-off-newlines": { @@ -47116,10 +19198,10 @@ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", "dev": true, "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" + "@types/json5": "0.0.29", + "json5": "1.0.1", + "minimist": "1.2.5", + "strip-bom": "3.0.0" }, "dependencies": { "json5": { @@ -47128,7 +19210,7 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.5" } }, "strip-bom": { @@ -47157,7 +19239,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -47173,12 +19255,12 @@ "dev": true }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.2.1" } }, "type-detect": { @@ -47199,7 +19281,7 @@ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "mime-types": "2.1.31" } }, "typedarray": { @@ -47213,7 +19295,7 @@ "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", "requires": { - "typescript-logic": "^0.0.0" + "typescript-logic": "0.0.0" } }, "typescript-logic": { @@ -47226,7 +19308,7 @@ "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", "requires": { - "typescript-compare": "^0.0.2" + "typescript-compare": "0.0.2" } }, "ua-parser-js": { @@ -47236,10 +19318,11 @@ "dev": true }, "uglify-js": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", - "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", - "dev": true + "version": "3.13.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz", + "integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==", + "dev": true, + "optional": true }, "uglify-to-browserify": { "version": "1.0.2", @@ -47253,9 +19336,9 @@ "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", "dev": true, "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" + "source-map": "0.5.7", + "uglify-js": "2.8.29", + "webpack-sources": "1.4.3" }, "dependencies": { "camelcase": { @@ -47270,20 +19353,26 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "wordwrap": { @@ -47298,22 +19387,34 @@ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } } }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "1.1.1", + "has-bigints": "1.0.1", + "has-symbols": "1.0.2", + "which-boxed-primitive": "1.0.2" + } + }, "unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" + "buffer": "5.7.1", + "through": "2.3.8" } }, "unc-path-regex": { @@ -47323,20 +19424,29 @@ "dev": true }, "undertaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "bach": "1.2.0", + "collection-map": "1.0.0", + "es6-weak-map": "2.0.3", + "fast-levenshtein": "1.1.4", + "last-run": "1.1.1", + "object.defaults": "1.1.0", + "object.reduce": "1.0.1", + "undertaker-registry": "1.0.1" + }, + "dependencies": { + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true + } } }, "undertaker-registry": { @@ -47357,8 +19467,8 @@ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "1.0.4", + "unicode-property-aliases-ecmascript": "1.1.0" } }, "unicode-match-property-value-ecmascript": { @@ -47379,12 +19489,20 @@ "integrity": "sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA==", "dev": true, "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "bail": "1.0.5", + "extend": "3.0.2", + "is-buffer": "2.0.5", + "is-plain-obj": "2.1.0", + "trough": "1.0.5", + "vfile": "4.2.1" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } } }, "union-value": { @@ -47393,10 +19511,10 @@ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "2.0.1" } }, "unique-stream": { @@ -47405,8 +19523,8 @@ "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, "requires": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + "json-stable-stringify-without-jsonify": "1.0.1", + "through2-filter": "3.0.0" } }, "unist-builder": { @@ -47439,7 +19557,7 @@ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", "dev": true, "requires": { - "@types/unist": "^2.0.2" + "@types/unist": "2.0.3" } }, "unist-util-visit": { @@ -47448,9 +19566,9 @@ "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "dev": true, "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" + "@types/unist": "2.0.3", + "unist-util-is": "4.1.0", + "unist-util-visit-parents": "3.1.1" } }, "unist-util-visit-parents": { @@ -47459,8 +19577,8 @@ "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", "dev": true, "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" + "@types/unist": "2.0.3", + "unist-util-is": "4.1.0" } }, "universalify": { @@ -47480,8 +19598,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -47490,9 +19608,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -47520,15 +19638,15 @@ "integrity": "sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA==", "dev": true, "requires": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" + "big-integer": "1.6.48", + "binary": "0.3.0", + "bluebird": "3.4.7", + "buffer-indexof-polyfill": "1.0.2", + "duplexer2": "0.1.4", + "fstream": "1.0.12", + "listenercount": "1.0.1", + "readable-stream": "2.3.7", + "setimmediate": "1.0.5" }, "dependencies": { "bluebird": { @@ -47543,13 +19661,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } } } @@ -47561,12 +19688,12 @@ "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } }, "urix": { @@ -47600,13 +19727,13 @@ "dev": true }, "url-parse": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.0.tgz", - "integrity": "sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", "dev": true, "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "querystringify": "2.2.0", + "requires-port": "1.0.0" } }, "use": { @@ -47653,7 +19780,7 @@ "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.3" } }, "validate-npm-package-license": { @@ -47662,8 +19789,8 @@ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.1.1", + "spdx-expression-parse": "3.0.1" } }, "value-or-function": { @@ -47683,9 +19810,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "vfile": { @@ -47694,10 +19821,18 @@ "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dev": true, "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" + "@types/unist": "2.0.3", + "is-buffer": "2.0.5", + "unist-util-stringify-position": "2.0.3", + "vfile-message": "2.0.4" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } } }, "vfile-message": { @@ -47706,8 +19841,8 @@ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", "dev": true, "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" + "@types/unist": "2.0.3", + "unist-util-stringify-position": "2.0.3" } }, "vfile-reporter": { @@ -47716,12 +19851,12 @@ "integrity": "sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA==", "dev": true, "requires": { - "repeat-string": "^1.5.0", - "string-width": "^4.0.0", - "supports-color": "^6.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-sort": "^2.1.2", - "vfile-statistics": "^1.1.0" + "repeat-string": "1.6.1", + "string-width": "4.2.2", + "supports-color": "6.1.0", + "unist-util-stringify-position": "2.0.3", + "vfile-sort": "2.2.2", + "vfile-statistics": "1.1.4" }, "dependencies": { "supports-color": { @@ -47730,7 +19865,7 @@ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -47748,17 +19883,17 @@ "dev": true }, "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "clone": "2.1.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.3", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.1" }, "dependencies": { "clone": { @@ -47775,23 +19910,23 @@ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, "requires": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" + "fs-mkdirp-stream": "1.0.0", + "glob-stream": "6.1.0", + "graceful-fs": "4.2.6", + "is-valid-glob": "1.0.0", + "lazystream": "1.0.0", + "lead": "1.0.0", + "object.assign": "4.1.2", + "pumpify": "1.5.1", + "readable-stream": "2.3.7", + "remove-bom-buffer": "3.0.0", + "remove-bom-stream": "1.2.0", + "resolve-options": "1.1.0", + "through2": "2.0.5", + "to-through": "2.0.0", + "value-or-function": "3.0.0", + "vinyl": "2.2.1", + "vinyl-sourcemap": "1.1.0" }, "dependencies": { "readable-stream": { @@ -47800,13 +19935,22 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" } }, "through2": { @@ -47815,8 +19959,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.7", + "xtend": "4.0.2" } } } @@ -47827,13 +19971,13 @@ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", "dev": true, "requires": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" + "append-buffer": "1.0.2", + "convert-source-map": "1.8.0", + "graceful-fs": "4.2.6", + "normalize-path": "2.1.1", + "now-and-later": "2.0.1", + "remove-bom-buffer": "3.0.0", + "vinyl": "2.2.1" }, "dependencies": { "normalize-path": { @@ -47842,7 +19986,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } } } @@ -47853,7 +19997,7 @@ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { - "source-map": "^0.5.1" + "source-map": "0.5.7" } }, "vm-browserify": { @@ -47868,19 +20012,6 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", "dev": true }, - "vue": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.1.1.tgz", - "integrity": "sha512-j9fj3PNPMxo2eqOKYjMuss9XBS8ZtmczLY3kPvjcp9d3DbhyNqLYbaMQH18+1pDIzzVvQCQBvIf774LsjjqSKA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@vue/compiler-dom": "3.1.1", - "@vue/runtime-dom": "3.1.1", - "@vue/shared": "3.1.1" - } - }, "vue-template-compiler": { "version": "2.6.14", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", @@ -47888,8 +20019,8 @@ "dev": true, "optional": true, "requires": { - "de-indent": "^1.0.2", - "he": "^1.1.0" + "de-indent": "1.0.2", + "he": "1.2.0" } }, "walk": { @@ -47898,19 +20029,19 @@ "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", "dev": true, "requires": { - "foreachasync": "^3.0.0" + "foreachasync": "3.0.0" } }, "watchpack": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", - "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "requires": { - "chokidar": "^3.4.0", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "chokidar": "3.5.2", + "graceful-fs": "4.2.6", + "neo-async": "2.6.2", + "watchpack-chokidar2": "2.0.1" } }, "watchpack-chokidar2": { @@ -47920,7 +20051,7 @@ "dev": true, "optional": true, "requires": { - "chokidar": "^2.1.8" + "chokidar": "2.1.8" }, "dependencies": { "anymatch": { @@ -47930,8 +20061,8 @@ "dev": true, "optional": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" }, "dependencies": { "normalize-path": { @@ -47941,7 +20072,7 @@ "dev": true, "optional": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } } } @@ -47958,18 +20089,17 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "optional": 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" + "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -47977,9 +20107,8 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -47991,18 +20120,18 @@ "dev": true, "optional": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "anymatch": "2.0.0", + "async-each": "1.0.3", + "braces": "2.3.2", + "fsevents": "1.2.13", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.2.0" } }, "fill-range": { @@ -48010,12 +20139,11 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "optional": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -48023,9 +20151,8 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -48037,8 +20164,8 @@ "dev": true, "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "bindings": "1.5.0", + "nan": "2.14.2" } }, "glob-parent": { @@ -48048,8 +20175,8 @@ "dev": true, "optional": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -48059,7 +20186,7 @@ "dev": true, "optional": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -48071,24 +20198,16 @@ "dev": true, "optional": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.13.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, - "optional": 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, - "optional": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -48096,13 +20215,33 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } }, + "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.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -48110,13 +20249,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -48126,9 +20265,19 @@ "dev": true, "optional": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "graceful-fs": "4.2.6", + "micromatch": "3.1.10", + "readable-stream": "2.3.7" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.2" } }, "to-regex-range": { @@ -48136,10 +20285,9 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, - "optional": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } } } @@ -48150,35 +20298,41 @@ "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "dev": true, "requires": { - "defaults": "^1.0.3" + "defaults": "1.0.3" } }, "webdriver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.7.3.tgz", - "integrity": "sha512-/NfeRPREZPkY7pWVvnlyE2E4cfvl+lQmu9j1vE2GDL+oBwCHn+C5Vxwag6bOiBsrKcBan05Ghhlcl/o2uw9ZUA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.7.4.tgz", + "integrity": "sha512-bE6/A+OYb040GZ1MiuZebc8bOOYm797dmqEfmj6aoEQ4BMy1juiFlzCzeBzAlPrq33qPa8/CSYfH7rnkB3RRwg==", "dev": true, "requires": { - "@types/node": "^14.14.31", + "@types/node": "14.17.4", "@wdio/config": "7.7.3", "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", + "@wdio/protocols": "7.7.4", "@wdio/types": "7.7.3", "@wdio/utils": "7.7.3", - "got": "^11.0.2", - "lodash.merge": "^4.6.1" + "got": "11.8.2", + "lodash.merge": "4.6.2" }, "dependencies": { + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true + }, "@wdio/logger": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" } }, "ansi-styles": { @@ -48187,7 +20341,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -48196,8 +20350,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -48206,7 +20360,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -48227,58 +20381,64 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } }, "webdriverio": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.7.3.tgz", - "integrity": "sha512-3m18Ax0dKHBT7lMueUNEgYDHMRVodXokmAq/yAH+SqHFUbHdPrHOFK3d/snFZe41f6LHrLLzebVE7rvI4Zr1AA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.7.4.tgz", + "integrity": "sha512-VSWRj2mmvA8WbideFAYb5BMWPkBCJ7gJHhYrUSibTrMHKreRtX++cw/oGxxowy9/pTHsAW6OxlnaDxFL5Gt08A==", "dev": true, "requires": { - "@types/aria-query": "^4.2.1", - "@types/node": "^14.14.31", + "@types/aria-query": "4.2.1", + "@types/node": "14.17.4", "@wdio/config": "7.7.3", "@wdio/logger": "7.7.0", - "@wdio/protocols": "7.5.3", + "@wdio/protocols": "7.7.4", "@wdio/repl": "7.7.3", "@wdio/types": "7.7.3", "@wdio/utils": "7.7.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.7.3", - "devtools-protocol": "^0.0.887710", - "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", + "archiver": "5.3.0", + "aria-query": "4.2.2", + "atob": "2.1.2", + "css-shorthand-properties": "1.1.1", + "css-value": "0.0.1", + "devtools": "7.7.4", + "devtools-protocol": "0.0.892017", + "fs-extra": "10.0.0", + "get-port": "5.1.1", + "grapheme-splitter": "1.0.4", + "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.1", + "query-selector-shadow-dom": "1.0.0", + "resq": "1.10.0", "rgb2hex": "0.2.5", - "serialize-error": "^8.0.0", - "webdriver": "7.7.3" + "serialize-error": "8.1.0", + "webdriver": "7.7.4" }, "dependencies": { + "@types/node": { + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", + "dev": true + }, "@wdio/logger": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.7.0.tgz", "integrity": "sha512-XX/OkC8NlvsBdhKsb9j7ZbuQtF/Vuo0xf38PXdqYtVezOrYbDuba0hPG++g/IGNuAF34ZbSi+49cvz4u5w92kQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^6.0.0" + "chalk": "4.1.1", + "loglevel": "1.7.1", + "loglevel-plugin-prefix": "0.8.4", + "strip-ansi": "6.0.0" } }, "ansi-styles": { @@ -48287,7 +20447,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "chalk": { @@ -48296,8 +20456,8 @@ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "4.3.0", + "supports-color": "7.2.0" } }, "color-convert": { @@ -48306,7 +20466,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -48327,7 +20487,7 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "4.0.0" } } } @@ -48338,40 +20498,46 @@ "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", "dev": true, "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" + "acorn": "5.7.4", + "acorn-dynamic-import": "2.0.2", + "ajv": "6.12.6", + "ajv-keywords": "3.5.2", + "async": "2.6.3", + "enhanced-resolve": "3.4.1", + "escope": "3.6.0", + "interpret": "1.4.0", + "json-loader": "0.5.7", + "json5": "0.5.1", + "loader-runner": "2.4.0", + "loader-utils": "1.4.0", + "memory-fs": "0.4.1", + "mkdirp": "0.5.5", + "node-libs-browser": "2.2.1", + "source-map": "0.5.7", + "supports-color": "4.5.0", + "tapable": "0.2.9", + "uglifyjs-webpack-plugin": "0.4.6", + "watchpack": "1.7.5", + "webpack-sources": "1.4.3", + "yargs": "8.0.2" }, "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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" + "fast-deep-equal": "3.1.3", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.4.1" } }, "ansi-regex": { @@ -48386,7 +20552,7 @@ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "4.17.21" } }, "camelcase": { @@ -48401,9 +20567,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" }, "dependencies": { "string-width": { @@ -48412,20 +20578,52 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.5", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.3", + "strip-eof": "1.0.0" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "get-caller-file": { @@ -48434,6 +20632,12 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", @@ -48446,7 +20650,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "json5": { @@ -48457,14 +20661,14 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.2.6", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" } }, "loader-utils": { @@ -48473,9 +20677,9 @@ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "big.js": "5.2.2", + "emojis-list": "3.0.0", + "json5": "1.0.1" }, "dependencies": { "json5": { @@ -48484,7 +20688,7 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.5" } } } @@ -48495,8 +20699,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "mkdirp": { @@ -48505,7 +20709,18 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "p-limit": { @@ -48514,7 +20729,7 @@ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -48523,7 +20738,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.3.0" } }, "p-try": { @@ -48544,7 +20759,7 @@ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "2.3.0" } }, "read-pkg": { @@ -48553,9 +20768,9 @@ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "load-json-file": "2.0.0", + "normalize-package-data": "2.5.0", + "path-type": "2.0.0" } }, "read-pkg-up": { @@ -48564,8 +20779,8 @@ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "find-up": "2.1.0", + "read-pkg": "2.0.0" } }, "require-main-filename": { @@ -48574,14 +20789,29 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "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" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -48602,7 +20832,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -48613,7 +20843,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -48628,7 +20858,16 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" } }, "wrap-ansi": { @@ -48637,8 +20876,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "string-width": { @@ -48647,9 +20886,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -48666,19 +20905,19 @@ "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", "dev": true, "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.3", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.2", + "yargs-parser": "7.0.0" } }, "yargs-parser": { @@ -48687,44 +20926,32 @@ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } }, "webpack-bundle-analyzer": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz", - "integrity": "sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1", - "bfj": "^6.1.1", - "chalk": "^2.4.1", - "commander": "^2.18.0", - "ejs": "^2.6.1", - "express": "^4.16.3", - "filesize": "^3.6.1", - "gzip-size": "^5.0.0", - "lodash": "^4.17.15", - "mkdirp": "^0.5.1", - "opener": "^1.5.1", - "ws": "^6.0.0" + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz", + "integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==", + "dev": true, + "requires": { + "acorn": "7.4.1", + "acorn-walk": "7.2.0", + "bfj": "6.1.2", + "chalk": "2.4.2", + "commander": "2.20.3", + "ejs": "2.7.4", + "express": "4.17.1", + "filesize": "3.6.1", + "gzip-size": "5.1.1", + "lodash": "4.17.21", + "mkdirp": "0.5.5", + "opener": "1.5.2", + "ws": "6.2.2" }, "dependencies": { - "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "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 - }, "ejs": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", @@ -48737,7 +20964,7 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } }, "ws": { @@ -48746,7 +20973,7 @@ "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dev": true, "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "1.0.1" } } } @@ -48757,8 +20984,8 @@ "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", "dev": true, "requires": { - "source-list-map": "~0.1.7", - "source-map": "~0.4.1" + "source-list-map": "0.1.8", + "source-map": "0.4.4" }, "dependencies": { "source-list-map": { @@ -48769,34 +20996,34 @@ }, "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } }, "webpack-dev-middleware": { "version": "2.0.6", - "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { - "loud-rejection": "^1.6.0", - "memory-fs": "~0.4.1", - "mime": "^2.1.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "url-join": "^2.0.2", - "webpack-log": "^1.0.1" + "loud-rejection": "1.6.0", + "memory-fs": "0.4.1", + "mime": "2.5.2", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.1", + "url-join": "2.0.5", + "webpack-log": "1.2.0" }, "dependencies": { "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true } } @@ -48804,13 +21031,13 @@ "webpack-log": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha1-pLNM2msitRjbsKsy5WeWLVxypD0=", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", "dev": true, "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" + "chalk": "2.4.2", + "log-symbols": "2.2.0", + "loglevelnext": "1.0.5", + "uuid": "3.4.0" }, "dependencies": { "log-symbols": { @@ -48819,7 +21046,7 @@ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "2.4.2" } }, "uuid": { @@ -48836,8 +21063,8 @@ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "dev": true, "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "source-list-map": "2.0.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -48850,22 +21077,22 @@ }, "webpack-stream": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/webpack-stream/-/webpack-stream-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/webpack-stream/-/webpack-stream-3.2.0.tgz", "integrity": "sha1-Oh0WD7EdQXJ7fObzL3IkZPmLIYY=", "dev": true, "requires": { - "gulp-util": "^3.0.7", - "lodash.clone": "^4.3.2", - "lodash.some": "^4.2.2", - "memory-fs": "^0.3.0", - "through": "^2.3.8", - "vinyl": "^1.1.0", - "webpack": "^1.12.9" + "gulp-util": "3.0.8", + "lodash.clone": "4.5.0", + "lodash.some": "4.6.0", + "memory-fs": "0.3.0", + "through": "2.3.8", + "vinyl": "1.2.0", + "webpack": "1.15.0" }, "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true }, @@ -48875,8 +21102,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "arr-diff": { @@ -48885,7 +21112,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -48918,18 +21145,18 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.4" } }, "browserify-aes": { "version": "0.4.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", "integrity": "sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw=", "dev": true, "requires": { - "inherits": "^2.0.1" + "inherits": "2.0.3" } }, "browserify-zlib": { @@ -48938,7 +21165,7 @@ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", "dev": true, "requires": { - "pako": "~0.2.0" + "pako": "0.2.9" } }, "buffer": { @@ -48947,9 +21174,9 @@ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.5.1", + "ieee754": "1.2.1", + "isarray": "1.0.0" } }, "camelcase": { @@ -48964,15 +21191,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.2", + "async-each": "1.0.3", + "fsevents": "1.2.13", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1" } }, "cliui": { @@ -48981,8 +21208,8 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" } }, @@ -48994,7 +21221,7 @@ }, "crypto-browserify": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", "integrity": "sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw=", "dev": true, "requires": { @@ -49004,6 +21231,12 @@ "sha.js": "2.2.6" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", @@ -49016,9 +21249,9 @@ "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" + "graceful-fs": "4.2.6", + "memory-fs": "0.2.0", + "tapable": "0.1.10" }, "dependencies": { "memory-fs": { @@ -49041,7 +21274,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -49050,7 +21283,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "fill-range": { @@ -49059,10 +21292,10 @@ "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" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -49071,7 +21304,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -49083,8 +21316,8 @@ "dev": true, "optional": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "bindings": "1.5.0", + "nan": "2.14.2" } }, "glob-parent": { @@ -49093,7 +21326,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "has-flag": { @@ -49120,7 +21353,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" }, "dependencies": { "kind-of": { @@ -49137,22 +21370,16 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.13.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-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.3" }, "dependencies": { "kind-of": { @@ -49169,9 +21396,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" }, "dependencies": { "kind-of": { @@ -49194,7 +21421,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-number": { @@ -49203,7 +21430,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "json5": { @@ -49218,7 +21445,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "loader-utils": { @@ -49227,10 +21454,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" } }, "memory-fs": { @@ -49239,8 +21466,8 @@ "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.8", + "readable-stream": "2.3.7" } }, "micromatch": { @@ -49249,19 +21476,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "mkdirp": { @@ -49270,7 +21497,7 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } }, "node-libs-browser": { @@ -49279,28 +21506,28 @@ "integrity": "sha1-PicsCBnjCJNeJmdECNevDhSRuDs=", "dev": true, "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.1.4", - "buffer": "^4.9.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", + "assert": "1.5.0", + "browserify-zlib": "0.1.4", + "buffer": "4.9.2", + "console-browserify": "1.2.0", + "constants-browserify": "1.0.0", "crypto-browserify": "3.3.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", + "domain-browser": "1.2.0", + "events": "1.1.1", "https-browserify": "0.0.1", - "os-browserify": "^0.2.0", + "os-browserify": "0.2.1", "path-browserify": "0.0.0", - "process": "^0.11.0", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.0.5", - "stream-browserify": "^2.0.1", - "stream-http": "^2.3.1", - "string_decoder": "^0.10.25", - "timers-browserify": "^2.0.2", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.7", + "stream-browserify": "2.0.2", + "stream-http": "2.8.3", + "string_decoder": "0.10.31", + "timers-browserify": "2.0.12", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", + "url": "0.11.0", + "util": "0.10.4", "vm-browserify": "0.0.4" }, "dependencies": { @@ -49318,7 +21545,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "os-browserify": { @@ -49329,7 +21556,7 @@ }, "pako": { "version": "0.2.9", - "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", "dev": true }, @@ -49351,13 +21578,13 @@ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -49366,9 +21593,9 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "graceful-fs": "4.2.6", + "micromatch": "3.1.10", + "readable-stream": "2.3.7" }, "dependencies": { "arr-diff": { @@ -49389,16 +21616,16 @@ "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" + "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.4", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -49407,7 +21634,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -49418,13 +21645,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -49433,7 +21660,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -49442,7 +21669,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-descriptor": { @@ -49451,9 +21678,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -49470,14 +21697,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -49486,7 +21713,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -49495,7 +21722,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -49506,7 +21733,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -49515,7 +21742,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -49526,7 +21753,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -49535,7 +21762,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -49552,19 +21779,19 @@ "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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } } } @@ -49577,28 +21804,37 @@ }, "ripemd160": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", "integrity": "sha1-K/GYveFnys+lHAqSjoS2i74XH84=", "dev": true }, "sha.js": { "version": "2.2.6", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", "integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } }, "tapable": { "version": "0.1.10", - "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, @@ -49608,25 +21844,25 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "uglify-js": { "version": "2.7.5", - "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", "dev": true, "requires": { - "async": "~0.2.6", - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "async": "0.2.10", + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "async": { "version": "0.2.10", - "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true } @@ -49647,8 +21883,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -49667,14 +21903,14 @@ "integrity": "sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws=", "dev": true, "requires": { - "async": "^0.9.0", - "chokidar": "^1.0.0", - "graceful-fs": "^4.1.2" + "async": "0.9.2", + "chokidar": "1.7.0", + "graceful-fs": "4.2.6" }, "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true } @@ -49686,21 +21922,21 @@ "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=", "dev": true, "requires": { - "acorn": "^3.0.0", - "async": "^1.3.0", - "clone": "^1.0.2", - "enhanced-resolve": "~0.9.0", - "interpret": "^0.6.4", - "loader-utils": "^0.2.11", - "memory-fs": "~0.3.0", - "mkdirp": "~0.5.0", - "node-libs-browser": "^0.7.0", - "optimist": "~0.6.0", - "supports-color": "^3.1.0", - "tapable": "~0.1.8", - "uglify-js": "~2.7.3", - "watchpack": "^0.2.1", - "webpack-core": "~0.6.9" + "acorn": "3.3.0", + "async": "1.5.2", + "clone": "1.0.4", + "enhanced-resolve": "0.9.1", + "interpret": "0.6.6", + "loader-utils": "0.2.17", + "memory-fs": "0.3.0", + "mkdirp": "0.5.5", + "node-libs-browser": "0.7.0", + "optimist": "0.6.1", + "supports-color": "3.2.3", + "tapable": "0.1.10", + "uglify-js": "2.7.5", + "watchpack": "0.2.9", + "webpack-core": "0.6.9" } }, "wordwrap": { @@ -49711,13 +21947,13 @@ }, "yargs": { "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -49729,9 +21965,9 @@ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.5.3", + "safe-buffer": "5.1.2", + "websocket-extensions": "0.1.4" } }, "websocket-extensions": { @@ -49741,25 +21977,25 @@ "dev": true }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-boxed-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", - "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "requires": { - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-symbol": "^1.0.2" + "is-bigint": "1.0.2", + "is-boolean-object": "1.1.1", + "is-number-object": "1.0.5", + "is-string": "1.0.6", + "is-symbol": "1.0.4" } }, "which-collection": { @@ -49768,10 +22004,10 @@ "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "dev": true, "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "2.0.2", + "is-set": "2.0.2", + "is-weakmap": "2.0.1", + "is-weakset": "2.0.1" } }, "which-module": { @@ -49781,17 +22017,18 @@ "dev": true }, "which-typed-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", - "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.2", - "es-abstract": "^1.17.5", - "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" + "available-typed-arrays": "1.0.4", + "call-bind": "1.0.2", + "es-abstract": "1.18.3", + "foreach": "2.0.5", + "function-bind": "1.1.1", + "has-symbols": "1.0.2", + "is-typed-array": "1.1.5" } }, "wide-align": { @@ -49800,7 +22037,7 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -49821,8 +22058,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -49831,7 +22068,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -49855,9 +22092,9 @@ "dev": true }, "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.4.tgz", + "integrity": "sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g==", "dev": true }, "wrap-ansi": { @@ -49866,9 +22103,9 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "4.3.0", + "string-width": "4.2.2", + "strip-ansi": "6.0.0" }, "dependencies": { "ansi-styles": { @@ -49877,7 +22114,7 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "color-convert": "2.0.1" } }, "color-convert": { @@ -49886,7 +22123,7 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.4" } }, "color-name": { @@ -49909,7 +22146,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.5" }, "dependencies": { "mkdirp": { @@ -49918,17 +22155,16 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "1.2.5" } } } }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "requires": {} + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", + "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==", + "dev": true }, "xtend": { "version": "4.0.2", @@ -49949,24 +22185,15 @@ "dev": true }, "yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", + "integrity": "sha1-BU3oth8i7v23IHBZ6u+da4P7kxo=", + "dev": true }, "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { @@ -49975,16 +22202,16 @@ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "camelcase": "6.2.0", + "decamelize": "4.0.0", + "flat": "5.0.2", + "is-plain-obj": "2.1.0" }, "dependencies": { - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true } } @@ -49995,9 +22222,9 @@ "integrity": "sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA=", "dev": true, "requires": { - "cac": "^3.0.3", - "chalk": "^1.1.3", - "cross-spawn": "^4.0.2" + "cac": "3.0.4", + "chalk": "1.1.3", + "cross-spawn": "4.0.2" }, "dependencies": { "ansi-regex": { @@ -50018,11 +22245,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "strip-ansi": { @@ -50031,7 +22258,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "supports-color": { @@ -50048,8 +22275,8 @@ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "buffer-crc32": "0.2.13", + "fd-slicer": "1.1.0" } }, "yocto-queue": { @@ -50064,9 +22291,9 @@ "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", "dev": true, "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" + "archiver-utils": "2.1.0", + "compress-commons": "4.1.1", + "readable-stream": "3.6.0" } }, "zwitch": { diff --git a/package.json b/package.json index 4a93196cb98..20ad7fce908 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "eslint-plugin-prebid": "file:./plugins/eslint", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^3.0.1", - "execa": "^5.0.0", + "execa": "^1.0.0", "faker": "^5.5.3", "fs.extra": "^1.3.2", "gulp": "^4.0.0", @@ -90,7 +90,7 @@ "lodash": "^4.17.21", "mocha": "^5.0.0", "morgan": "^1.10.0", - "open": "^8.2.0", + "opn": "^5.4.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", "through2": "^4.0.2", @@ -99,7 +99,7 @@ "webpack": "^3.0.0", "webpack-bundle-analyzer": "^3.8.0", "webpack-stream": "^3.2.0", - "yargs": "^17.0.1" + "yargs": "^1.3.1" }, "dependencies": { "babel-plugin-transform-object-assign": "^6.22.0", diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 842bccd99b1..1048b8be173 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { "no-tabs": "off", "no-unused-expressions": "off", "import/no-duplicates": "off", + "import/extensions": "off", "no-template-curly-in-string": "off", "no-global-assign": "off", "no-path-concat": "off", From eb909ac1bbb61a7398e3cbf39daa395f31ee0894 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Thu, 24 Jun 2021 19:28:26 +0530 Subject: [PATCH 1199/1476] Added Media.net RTD Module (#6988) Co-authored-by: monis.q --- modules/.submodules.json | 1 + modules/medianetRtdProvider.js | 112 ++++++++++++++ modules/medianetRtdProvider.md | 38 +++++ test/spec/modules/medianetRtdProvider_spec.js | 146 ++++++++++++++++++ 4 files changed, 297 insertions(+) create mode 100644 modules/medianetRtdProvider.js create mode 100644 modules/medianetRtdProvider.md create mode 100644 test/spec/modules/medianetRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 04c92a2041e..2358bca7b4d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -42,6 +42,7 @@ "haloRtdProvider", "iasRtdProvider", "jwplayerRtdProvider", + "medianetRtdProvider", "optimeraRtdProvider", "permutiveRtdProvider", "reconciliationRtdProvider", diff --git a/modules/medianetRtdProvider.js b/modules/medianetRtdProvider.js new file mode 100644 index 00000000000..77889db5ff2 --- /dev/null +++ b/modules/medianetRtdProvider.js @@ -0,0 +1,112 @@ +import { submodule } from '../src/hook.js'; +import * as utils from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const MODULE_NAME = 'medianet'; +const SOURCE = MODULE_NAME + 'rtd'; +const AD_UNIT_CODE_TARGETING_KEY = 'mnadc'; +const OPEN_RTB_FIELD = 'ortb2Imp'; + +const getClientUrl = (customerId, domain) => `https://warp.media.net/js/tags/prebidrtdclient.js?cid=${customerId}&dn=${domain}`; + +window.mnjs = window.mnjs || {}; +window.mnjs.que = window.mnjs.que || []; + +function init(config) { + const customerId = config.params && config.params.cid; + if (!customerId || !utils.isStr(customerId) || utils.isEmptyStr(customerId)) { + utils.logError(`${SOURCE}: cid should be a string`); + return false; + } + + loadRtdScript(customerId); + executeCommand(() => window.mnjs.setData({ + module: 'iref', + name: 'initIRefresh', + data: {config, prebidGlobal: getGlobal()}, + }, SOURCE)); + return true; +} + +function getBidRequestData(requestBidsProps, callback, config, userConsent) { + executeCommand(() => { + let adUnits = getAdUnits(requestBidsProps.adUnits, requestBidsProps.adUnitCodes); + const request = window.mnjs.onPrebidRequestBid({requestBidsProps, config, userConsent}); + if (!request) { + callback(); + return; + } + const success = (adUnitProps, openRtbProps) => { + adUnits.forEach(adUnit => { + adUnit[OPEN_RTB_FIELD] = adUnit[OPEN_RTB_FIELD] || {}; + utils.mergeDeep(adUnit[OPEN_RTB_FIELD], openRtbProps[adUnit.code]); + utils.mergeDeep(adUnit, adUnitProps[adUnit.code]); + }); + callback(); + }; + const error = () => callback(); + request.onComplete(error, success); + }); +} + +function onAuctionInitEvent(auctionInit) { + executeCommand(() => window.mnjs.setData({ + module: 'iref', + name: 'auctionInit', + data: {auction: auctionInit}, + }, SOURCE)); +} + +function getTargetingData(adUnitCode) { + const adUnits = getAdUnits(undefined, adUnitCode); + let targetingData = {}; + if (window.mnjs.loaded && utils.isFn(window.mnjs.getTargetingData)) { + targetingData = window.mnjs.getTargetingData(adUnitCode, adUnits, SOURCE) || {}; + } + const targeting = {}; + adUnitCode.forEach(adUnitCode => { + targeting[adUnitCode] = targeting[adUnitCode] || {}; + targetingData[adUnitCode] = targetingData[adUnitCode] || {}; + targeting[adUnitCode] = { + // we use this to find gpt slot => prebid ad unit + [AD_UNIT_CODE_TARGETING_KEY]: adUnitCode, + ...targetingData[adUnitCode] + }; + }); + return targeting; +} + +function executeCommand(command) { + window.mnjs.que.push(command); +} + +function loadRtdScript(customerId) { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.async = true; + script.src = getClientUrl(customerId, window.location.hostname); + utils.insertElement(script, window.document, 'head'); +} + +function getAdUnits(adUnits, adUnitCodes) { + adUnits = adUnits || getGlobal().adUnits || []; + if (adUnitCodes && adUnitCodes.length) { + adUnits = adUnits.filter(unit => includes(adUnitCodes, unit.code)); + } + return adUnits; +} + +export const medianetRtdModule = { + name: MODULE_NAME, + init, + getBidRequestData, + onAuctionInitEvent, + getTargetingData, +}; + +function registerSubModule() { + submodule('realTimeData', medianetRtdModule); +} + +registerSubModule(); diff --git a/modules/medianetRtdProvider.md b/modules/medianetRtdProvider.md new file mode 100644 index 00000000000..ed74d3bf348 --- /dev/null +++ b/modules/medianetRtdProvider.md @@ -0,0 +1,38 @@ +## Overview + +Module Name: Media.net Realtime Module +Module Type: Rtd Provider +Maintainer: prebid-support@media.net + +# Description + +The module currently provisions Media.net's Intelligent Refresh configured by the publisher. + +### Intelligent Refresh + +Intelligent Refresh (IR) module lets publisher refresh their ad inventory without affecting page experience of visitors through configured criteria. The module optionally provides tracking of refresh inventory and appropriate targeting in GAM. Publisher configured criteria is fetched via an external JS payload. + +# Integration + +1) Build the Media.net Intelligent Refresh RTD module into the Prebid.js package with: + +``` +gulp build --modules=medianetRtdProvider +``` + +# Configurations + +2) Enable Media.net Real Time Module using `pbjs.setConfig` + +```javascript +pbjs.setConfig({ + realTimeData: { + dataProviders: [{ + name: 'medianet', + params: { + cid: '8CUX0H51C' + } + }] + } +}); +``` diff --git a/test/spec/modules/medianetRtdProvider_spec.js b/test/spec/modules/medianetRtdProvider_spec.js new file mode 100644 index 00000000000..7d73ecd5d44 --- /dev/null +++ b/test/spec/modules/medianetRtdProvider_spec.js @@ -0,0 +1,146 @@ +import * as medianetRTD from '../../../modules/medianetRtdProvider.js'; +import * as sinon from 'sinon'; +import { assert } from 'chai'; + +let sandbox; +let setDataSpy; +let getTargetingDataSpy; +let onPrebidRequestBidSpy; + +const conf = { + dataProviders: [{ + 'name': 'medianet', + 'params': { + 'cid': 'customer_id', + } + }] +}; + +describe('medianet realtime module', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + window.mnjs = window.mnjs || {}; + window.mnjs.que = window.mnjs.que || []; + window.mnjs.setData = setDataSpy = sandbox.spy(); + window.mnjs.getTargetingData = getTargetingDataSpy = sandbox.spy(); + window.mnjs.onPrebidRequestBid = onPrebidRequestBidSpy = sandbox.spy(); + }); + + afterEach(function () { + sandbox.restore(); + window.mnjs = {}; + }); + + it('init should return false when customer id is passed', function () { + assert.equal(medianetRTD.medianetRtdModule.init({}), false); + }); + + it('init should return true when customer id is passed', function () { + assert.equal(medianetRTD.medianetRtdModule.init(conf.dataProviders[0]), true); + }); + + it('init should pass config to js when loaded', function () { + medianetRTD.medianetRtdModule.init(conf.dataProviders[0]); + + const command = window.mnjs.que.pop(); + assert.isFunction(command); + command(); + + assert.equal(setDataSpy.called, true); + assert.equal(setDataSpy.args[0][0].name, 'initIRefresh'); + }); + + it('auctionInit should pass information to js when loaded', function () { + const auctionObject = {adUnits: []}; + medianetRTD.medianetRtdModule.onAuctionInitEvent(auctionObject); + + const command = window.mnjs.que.pop(); + assert.isFunction(command); + command(); + + assert.equal(setDataSpy.called, true); + assert.equal(setDataSpy.args[0][0].name, 'auctionInit'); + assert.deepEqual(setDataSpy.args[0][0].data, {auction: auctionObject}); + }); + + describe('getTargeting should work correctly', function () { + it('should return empty if not loaded', function () { + window.mnjs.loaded = false; + 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), { + code1: {'mnadc': 'code1'}, + code2: {'mnadc': 'code2'}, + }); + }); + + it('should call mnjs.getTargetingData if loaded', function () { + window.mnjs.loaded = true; + medianetRTD.medianetRtdModule.getTargetingData([]); + assert.equal(getTargetingDataSpy.called, true); + }); + }); + + describe('getBidRequestData should work correctly', function () { + it('callback should be called when we are not interested in request', function () { + const requestBidsProps = { + adUnits: [{ + code: 'code1', bids: [], + }], + adUnitCodes: ['code1'], + }; + const callbackSpy = sandbox.spy(); + medianetRTD.medianetRtdModule.getBidRequestData(requestBidsProps, callbackSpy, conf.dataProviders[0], {}); + + const command = window.mnjs.que.pop(); + assert.isFunction(command); + command(); + + assert.equal(onPrebidRequestBidSpy.called, true, 'onPrebidRequest should always be called'); + assert.equal(callbackSpy.called, true, 'when onPrebidRequest returns nothing callback should be called immediately'); + }); + + it('we should wait for callback till onComplete', function () { + const requestBidsProps = { + adUnits: [{ + code: 'code1', bids: [], + }], + adUnitCodes: ['code1'], + }; + + const refreshInformation = { + mnrf: '1', + mnrfc: 2, + }; + + const callbackSpy = sandbox.spy(); + const onCompleteSpy = sandbox.spy(); + window.mnjs.onPrebidRequestBid = onPrebidRequestBidSpy = () => { + onPrebidRequestBidSpy.called = true; + return {onComplete: onCompleteSpy}; + }; + medianetRTD.medianetRtdModule.getBidRequestData(requestBidsProps, callbackSpy, conf.dataProviders[0], {}); + + const command = window.mnjs.que.pop(); + assert.isFunction(command); + command(); + + assert.equal(callbackSpy.called, false, 'callback should not be called, as we are returning a request from onPrebidRequestBid'); + assert.equal(onPrebidRequestBidSpy.called, true, 'onPrebidRequestBid should be called once'); + assert.equal(onCompleteSpy.called, true, 'onComplete should be passed callback'); + assert.isFunction(onCompleteSpy.args[0][0], 'onCompleteSpy first argument error callback should be a function'); + assert.isFunction(onCompleteSpy.args[0][1], 'onCompleteSpy second argument success callback should be a function'); + onCompleteSpy.args[0][0](); + assert.equal(callbackSpy.callCount, 1, 'callback should be called when error callback is triggered'); + onCompleteSpy.args[0][1]({}, { + 'code1': {ext: {refresh: refreshInformation}} + }); + assert.equal(callbackSpy.callCount, 2, 'callback should be called when success callback is triggered'); + assert.isObject(requestBidsProps.adUnits[0].ortb2Imp, 'ORTB object should be set'); + assert.deepEqual(requestBidsProps.adUnits[0].ortb2Imp.ext.refresh, refreshInformation, 'ORTB should have refresh information should be set'); + }); + }); +}); From bfc6f98218be30639e610cb00b869c0e94b045a1 Mon Sep 17 00:00:00 2001 From: Jay Kandimalla <31004072+Jayaharshak@users.noreply.github.com> Date: Thu, 24 Jun 2021 11:07:15 -0400 Subject: [PATCH 1200/1476] Dmd Id System: add rest endpoint (#7066) * feat(prebid): DMD UserID Module reading from 1st party cookie [PREBID-1] * feat(prebid):additional parameter[PREB-1] * feat(prebid):update decode function and cacheobi[PREB-1] * test(prebid):added more test coverage[PREB-11] * feat(typo):cleared typo[PREB-11] * test(prebid):updated test cases[PREB-11] * feat(releasenote):added a release note[PREB-11] * fix(releasenote):removed unnecessary release note[PREB-11] * fix(test):updated failing test cases[PREB-11] * PREB-26 Reading third party cookies from dmdId module implemented * PREB-26 PR Comments resolved * fix(tests):Removed ascretion make sure we can use this code in multiple envs * fix(tests):Added ascretion to check request url * feat(credentials): Enabling with credential flag in ajax request * feat(credentials): Enabling with credential flag * feat(credentials): changed callback response from object to dgid string * ops(merge): Correct resolution on markdown conflict * ops(merge): Correct resolution on spec conflict * fix(conflict):Merge conflicts from upstream master are resolved * fix(unit testing): fixed test cases Co-authored-by: Matt Fitzgerald Co-authored-by: boppudikarthikc Co-authored-by: Karthik Boppudi Co-authored-by: mfitzgerald_dmd Co-authored-by: karthik <60045218+boppudikarthikc@users.noreply.github.com> Co-authored-by: Jay Kandimalla Co-authored-by: Jay kandimalla --- modules/dmdIdSystem.js | 65 ++++++++++++++++++++------- test/spec/modules/dmdIdSystem_spec.js | 54 ++++++++++++++++++++-- 2 files changed, 100 insertions(+), 19 deletions(-) diff --git a/modules/dmdIdSystem.js b/modules/dmdIdSystem.js index 7cf7b9fac95..72d3518c20f 100644 --- a/modules/dmdIdSystem.js +++ b/modules/dmdIdSystem.js @@ -7,6 +7,9 @@ import * as utils from '../src/utils.js'; import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; + +const MODULE_NAME = 'dmdId'; /** @type {Submodule} */ export const dmdIdSubmodule = { @@ -14,7 +17,7 @@ export const dmdIdSubmodule = { * used to link submodule with config * @type {string} */ - name: 'dmdId', + name: MODULE_NAME, /** * decode the stored id value for passing to bid requests @@ -37,22 +40,52 @@ export const dmdIdSubmodule = { * @returns {IdResponse|undefined} */ getId(config, consentData, cacheIdObj) { - try { - const configParams = (config && config.params) || {}; - if ( - !configParams || - !configParams.api_key || - typeof configParams.api_key !== 'string' - ) { - utils.logError('dmd submodule requires an api_key.'); - return; - } else { - return cacheIdObj; - } - } catch (e) { - utils.logError(`dmdIdSystem encountered an error`, e); + const configParams = (config && config.params) || {}; + if ( + !configParams || + !configParams.api_key || + typeof configParams.api_key !== 'string' + ) { + utils.logError('dmd submodule requires an api_key.'); + return; } - }, + // If cahceIdObj is null or undefined - calling AIX-API + if (cacheIdObj) { + return cacheIdObj; + } else { + const url = configParams && configParams.api_url + ? configParams.api_url + : `https://aix.hcn.health/api/v1/auths`; + // Setting headers + const headers = {}; + headers['x-api-key'] = configParams.api_key; + headers['x-domain'] = utils.getWindowLocation(); + // Response callbacks + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + let responseId; + try { + responseObj = JSON.parse(response); + if (responseObj && responseObj.dgid) { + responseId = responseObj.dgid; + } + } catch (error) { + utils.logError(error); + } + callback(responseId); + }, + error: error => { + utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true, customHeaders: headers }); + }; + return { callback: resp }; + } + } }; submodule('userId', dmdIdSubmodule); diff --git a/test/spec/modules/dmdIdSystem_spec.js b/test/spec/modules/dmdIdSystem_spec.js index 9c603eebbd5..3096a8e55f5 100644 --- a/test/spec/modules/dmdIdSystem_spec.js +++ b/test/spec/modules/dmdIdSystem_spec.js @@ -1,9 +1,15 @@ import * as utils from '../../../src/utils.js'; +import { server } from 'test/mocks/xhr.js'; +import { dmdIdSubmodule } from 'modules/dmdIdSystem.js'; -import {dmdIdSubmodule} from 'modules/dmdIdSystem.js'; - -describe('Dmd ID System', function() { +describe('Dmd ID System', function () { let logErrorStub; + const config = { + params: { + api_key: '33344ffjddk22k22k222k22234k', + api_url: 'https://aix.hcn.health/api/v1/auths' + } + }; beforeEach(function () { logErrorStub = sinon.stub(utils, 'logError'); @@ -45,4 +51,46 @@ describe('Dmd ID System', function() { let data = { 'dmdId': 'U12345' }; expect(dmdIdSubmodule.decode('U12345')).to.deep.equal(data); }); + + it('should return cacheObj if cacheObj is passed into getId', function () { + let data = { 'dmdId': 'U12345' }; + expect(dmdIdSubmodule.getId(config, {}, { cookie: 'dmd-dgid' })).to.deep.equal({ cookie: 'dmd-dgid' }); + expect(server.requests.length).to.eq(0); + }); + + it('Should invoke callback with response from API call', function () { + const callbackSpy = sinon.spy(); + const domain = utils.getWindowLocation() + const callback = dmdIdSubmodule.getId(config).callback; + callback(callbackSpy); + const request = server.requests[0]; + expect(request.method).to.eq('GET'); + expect(request.requestHeaders['x-domain']).to.be.eq(domain); + expect(request.url).to.eq(config.params.api_url); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ dgid: 'U12345' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal('U12345'); + }); + + it('Should log error if API response is not valid', function () { + const callbackSpy = sinon.spy(); + const domain = utils.getWindowLocation() + const callback = dmdIdSubmodule.getId(config).callback; + callback(callbackSpy); + const request = server.requests[0]; + expect(request.method).to.eq('GET'); + expect(request.requestHeaders['x-domain']).to.be.eq(domain); + expect(request.url).to.eq(config.params.api_url); + request.respond(400, { 'Content-Type': 'application/json' }, undefined); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('Should log error if API call throws error', function () { + const callbackSpy = sinon.spy(); + const callback = dmdIdSubmodule.getId(config).callback; + callback(callbackSpy); + const request = server.requests[0]; + expect(request.url).to.eq(config.params.api_url); + request.error(); + expect(logErrorStub.calledOnce).to.be.true; + }); }); From 235a33023b09fcbdb49c6f97479c3a5a43a795e5 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Thu, 24 Jun 2021 19:10:41 +0300 Subject: [PATCH 1201/1476] Adkernel Bid Adapter: renaming converge alias (#7097) --- modules/adkernelBidAdapter.js | 2 +- modules/convergeBidAdapter.md | 57 ----------------------------------- 2 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 modules/convergeBidAdapter.md diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 09f16a82221..3d868bb91d2 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -67,7 +67,7 @@ export const spec = { {code: 'stringads'}, {code: 'bcm'}, {code: 'engageadx'}, - {code: 'converge_digital', gvlid: 248}, + {code: 'converge', gvlid: 248}, {code: 'adomega'}, {code: 'denakop'} ], diff --git a/modules/convergeBidAdapter.md b/modules/convergeBidAdapter.md deleted file mode 100644 index ab916a8b3b6..00000000000 --- a/modules/convergeBidAdapter.md +++ /dev/null @@ -1,57 +0,0 @@ -# Overview - -Module Name: Converge Bidder Adapter -Module Type: Bidder Adapter -Maintainer: support@converge-digital.com - -# Description - -Module that connects to Converge demand source to fetch bids. -Converge Bid Adapter supports Banner and Video (instream and outstream). - -# Test Parameters -``` - var adUnits = [ - { - code: 'test-div', - sizes: [[300, 250]], - bids: [ - { - bidder: "converge", - params: { - uid: '59', - priceType: 'gross' // by default is 'net' - } - } - ] - },{ - code: 'test-div', - sizes: [[728, 90]], - bids: [ - { - bidder: "converge", - params: { - uid: 1, - priceType: 'gross', - keywords: { - brandsafety: ['disaster'], - topic: ['stress', 'fear'] - } - } - } - ] - },{ - code: 'test-div', - sizes: [[640, 360]], - mediaTypes: { video: {} }, - bids: [ - { - bidder: "converge", - params: { - uid: 60 - } - } - ] - } - ]; -``` From e39cb75179d861a36c64c62ca3338ceaa3384d2b Mon Sep 17 00:00:00 2001 From: Vitali Ioussoupov <84333122+pixfuture-media@users.noreply.github.com> Date: Thu, 24 Jun 2021 15:56:17 -0400 Subject: [PATCH 1202/1476] Pix Fixture Bid Adapter: add new bid adapter (#7069) * Add files via upload * Add files via upload * Update pixfutureBidAdapter.md * Update pixfutureBidAdapter.md * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter_spec.js * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter.js Bug fixing: has been lost "pubext" parameter * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter.md * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter.js * Update pixfutureBidAdapter.js Removing the trailing spaces in lines: 168 and 172 * Add files via upload * Update pixfutureBidAdapter.js removing trilling spaces in the lines: 168,172,178 * Update pixfutureBidAdapter.md --- modules/pixfutureBidAdapter.js | 323 ++++++++++++++++++ modules/pixfutureBidAdapter.md | 27 ++ test/spec/modules/pixfutureBidAdapter_spec.js | 255 ++++++++++++++ 3 files changed, 605 insertions(+) create mode 100644 modules/pixfutureBidAdapter.js create mode 100644 modules/pixfutureBidAdapter.md create mode 100644 test/spec/modules/pixfutureBidAdapter_spec.js diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js new file mode 100644 index 00000000000..edf6a35e997 --- /dev/null +++ b/modules/pixfutureBidAdapter.js @@ -0,0 +1,323 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import * as utils from '../src/utils.js'; +import { auctionManager } from '../src/auctionManager.js'; +import find from 'core-js-pure/features/array/find.js'; + +const SOURCE = 'pbjs'; +const storageManager = getStorageManager(); +const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; +export const spec = { + code: 'pixfuture', + hostname: 'https://prebid-js.pixfuture.com', + + getHostname() { + let ret = this.hostname; + try { + ret = storageManager.getDataFromLocalStorage('ov_pixbidder_host') || ret; + } catch (e) { + } + return ret; + }, + + isBidRequestValid(bid) { + return !!(bid.sizes && bid.bidId && bid.params && + (bid.params.pix_id && (typeof bid.params.pix_id === 'string'))); + }, + + buildRequests(validBidRequests, bidderRequest) { + const tags = validBidRequests.map(bidToTag); + const hostname = this.getHostname(); + return validBidRequests.map((bidRequest) => { + let referer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referer = bidderRequest.refererInfo.referer || ''; + } + + const userObjBid = find(validBidRequests, hasUserInfo); + let userObj = {}; + if (config.getConfig('coppa') === true) { + userObj = {'coppa': true}; + } + + if (userObjBid) { + Object.keys(userObjBid.params.user) + .filter(param => includes(USER_PARAMS, param)) + .forEach((param) => { + let uparam = utils.convertCamelToUnderscore(param); + if (param === 'segments' && utils.isArray(userObjBid.params.user[param])) { + let segs = []; + userObjBid.params.user[param].forEach(val => { + if (utils.isNumber(val)) { + segs.push({'id': val}); + } else if (utils.isPlainObject(val)) { + segs.push(val); + } + }); + userObj[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; + } + }); + } + + const schain = validBidRequests[0].schain; + + const payload = { + tags: [...tags], + user: userObj, + sdk: { + source: SOURCE, + version: '$prebid.version$' + }, + schain: schain + }; + + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent + } + + if (bidderRequest && bidderRequest.refererInfo) { + let refererinfo = { + rd_ref: encodeURIComponent(bidderRequest.refererInfo.referer), + rd_top: bidderRequest.refererInfo.reachedTop, + rd_ifs: bidderRequest.refererInfo.numIframes, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + }; + payload.referrer_detection = refererinfo; + } + + if (validBidRequests[0].userId) { + let eids = []; + + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.flocId.id`), 'chrome.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.criteoId`), 'criteo.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.unifiedId`), 'thetradedesk.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.id5Id`), 'id5.io', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.sharedId`), 'thetradedesk.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.identityLink`), 'liveramp.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.liveIntentId`), 'liveintent.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.fabrickId`), 'home.neustar', null); + + if (eids.length) { + payload.eids = eids; + } + } + + if (tags[0].publisher_id) { + payload.publisher_id = tags[0].publisher_id; + } + + const ret = { + url: `${hostname}/auc/auc.php`, + method: 'POST', + options: {withCredentials: false}, + data: { + v: $$PREBID_GLOBAL$$.version, + pageUrl: referer, + bidId: bidRequest.bidId, + auctionId: bidRequest.auctionId, + transactionId: bidRequest.transactionId, + adUnitCode: bidRequest.adUnitCode, + bidRequestCount: bidRequest.bidRequestCount, + sizes: bidRequest.sizes, + params: bidRequest.params, + pubext: payload + } + }; + if (bidderRequest && bidderRequest.gdprConsent) { + ret.data.gdprConsent = { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') && bidderRequest.gdprConsent.gdprApplies + }; + } + return ret; + }); + }, + + interpretResponse: function (serverResponse, { bidderRequest }) { + serverResponse = serverResponse.body; + const bids = []; + if (serverResponse.creatives.bids && serverResponse.placements) { + serverResponse.placements.forEach(serverBid => { + serverBid.creatives.forEach(creative => { + const bid = newBid(serverBid, creative, serverBid.placement_id, serverBid.uuid); + bid.mediaType = BANNER; + bids.push(bid); + }); + }); + } + + return bids; + }, +}; + +function newBid(serverBid, rtbBid, placementId, uuid) { + const bid = { + requestId: uuid, + cpm: rtbBid.cpm, + creativeId: rtbBid.creative_id, + currency: 'USD', + netRevenue: true, + ttl: 300, + adUnitCode: placementId + }; + + if (rtbBid.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [rtbBid.adomain] }); + }; + + Object.assign(bid, { + width: rtbBid.width, + height: rtbBid.height, + ad: rtbBid.code + }); + + return bid; +} + +// Functions related optional parameters +function bidToTag(bid) { + const tag = {}; + tag.sizes = transformSizes(bid.sizes); + tag.primary_size = tag.sizes[0]; + tag.ad_types = []; + tag.uuid = bid.bidId; + if (bid.params.placementId) { + tag.id = parseInt(bid.params.placementId, 10); + } else { + tag.code = bid.params.invCode; + } + tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; + tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.prebid = true; + tag.disable_psa = true; + let bidFloor = getBidFloor(bid); + if (bidFloor) { + tag.reserve = bidFloor; + } + if (bid.params.position) { + tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; + } + if (bid.params.trafficSourceCode) { + tag.traffic_source_code = bid.params.trafficSourceCode; + } + if (bid.params.privateSizes) { + tag.private_sizes = transformSizes(bid.params.privateSizes); + } + if (bid.params.supplyType) { + tag.supply_type = bid.params.supplyType; + } + if (bid.params.pubClick) { + tag.pubclick = bid.params.pubClick; + } + if (bid.params.extInvCode) { + tag.ext_inv_code = bid.params.extInvCode; + } + if (bid.params.publisherId) { + tag.publisher_id = parseInt(bid.params.publisherId, 10); + } + if (bid.params.externalImpId) { + tag.external_imp_id = bid.params.externalImpId; + } + if (!utils.isEmpty(bid.params.keywords)) { + let keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + tag.keywords = keywords; + } + + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + tag.gpid = gpid; + } + + if (bid.renderer) { + tag.video = Object.assign({}, tag.video, {custom_renderer_present: true}); + } + + if (bid.params.frameworks && utils.isArray(bid.params.frameworks)) { + tag['banner_frameworks'] = bid.params.frameworks; + } + + let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); + if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + tag.ad_types.push(BANNER); + } + + if (tag.ad_types.length === 0) { + delete tag.ad_types; + } + + return tag; +} + +function addUserId(eids, id, source, rti) { + if (id) { + if (rti) { + eids.push({source, id, rti_partner: rti}); + } else { + eids.push({source, id}); + } + } + return eids; +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +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; +} + +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.reserve) ? bid.params.reserve : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +registerBidder(spec); diff --git a/modules/pixfutureBidAdapter.md b/modules/pixfutureBidAdapter.md new file mode 100644 index 00000000000..e5728ededc6 --- /dev/null +++ b/modules/pixfutureBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +``` +Module Name: PixFuture Bid Adapter +Module Type: Bidder Adapter +Maintainer: admin@pixfuture.net +``` +# Description + +Module that connects to PixFuture demand sources + +# Test Parameters +``` +var adUnits = [{ + "bidderCode": "pixfuture", + "auctionId": "634c9d0e-306f-4a5c-974e-21697dfd4fcd", + "bidderRequestId": "5f85993da0f6be", + "bids": [ + { + "labelAny": [ + "display" + ], + "bidder": "pixfuture", + "params": { + "pix_id": "Abc123" + }]; +``` diff --git a/test/spec/modules/pixfutureBidAdapter_spec.js b/test/spec/modules/pixfutureBidAdapter_spec.js new file mode 100644 index 00000000000..a236478c9b4 --- /dev/null +++ b/test/spec/modules/pixfutureBidAdapter_spec.js @@ -0,0 +1,255 @@ +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from 'modules/pixfutureBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; + +describe('PixFutureAdapter', function () { + it('', 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'); + }); + }); + + // Test of isBidRequestValid method + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'pixfuture', + 'pageUrl': 'https://adinify.com/prebidjs/?pbjs_debug=true', + 'bidId': '236e806f760f0c', + 'auctionId': 'aa7f5d76-806b-4e0d-b795-cd6bd84ddc63', + 'transactionId': '0fdf67c0-7b48-4fef-9716-cc64d948e95d', + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'pix_id': '777' + } + }; + it('should return true when required params found (bid)', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found (bid.param=true)', function () { + delete bid.params; + bid.params = { + 'pix_id': '777' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'pix_id': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + // Test of buildRequest method + + describe('Test of buildRequest method', function () { + let validBidRequests = [{ + 'labelAny': ['display'], + 'bidder': 'pixfuture', + 'params': { + 'pix_id': '777' + }, + 'userId': { + 'criteoId': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'id5id': { + 'uid': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'ext': { + 'linkType': 2 + } + }, + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'sharedid': { + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }, + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'atype': 1 + }] + }, { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'atype': 1, + 'ext': { + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }] + }], + 'crumbs': { + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'transactionId': '09310832-cd12-478c-86dd-fbd819dff9d3', + 'sizes': [ + [300, 250] + ], + 'bidId': '279272f27dfb3e', + 'bidderRequestId': '10a0de227377a3', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'pixfuture.com', + 'sid': '14', + 'hp': 1 + }] + } + }]; + + let bidderRequests = + { + 'bidderCode': 'pixfuture', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'bidderRequestId': '10a0de227377a3', + 'bids': [{ + 'labelAny': ['display'], + 'bidder': 'pixfuture', + 'params': { + 'pix_id': '777' + }, + 'userId': { + 'criteoId': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'id5id': { + 'uid': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'ext': { + 'linkType': 2 + } + }, + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'sharedid': { + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }, + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'atype': 1 + }] + }, { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'atype': 1, + 'ext': { + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }] + }], + 'crumbs': { + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'transactionId': '09310832-cd12-478c-86dd-fbd819dff9d3', + 'sizes': [ + [300, 250] + ], + 'bidId': '279272f27dfb3e', + 'bidderRequestId': '10a0de227377a3', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'pixfuture.com', + 'sid': '14', + 'hp': 1 + }] + } + }], + 'auctionStart': 1620934247115, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'https://adinify.com/prebidjs/?pbjs_debug=true', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': ['https://adinify.com/prebidjs/?pbjs_debug=true'], + 'canonicalUrl': null + }, + 'start': 1620934247117 + }; + + // let bidderRequest = Object.assign({}, bidderRequests); + const request = spec.buildRequests(validBidRequests, bidderRequests); + // console.log(JSON.stringify(request)); + let bidRequest = Object.assign({}, request[0]); + + expect(bidRequest.data).to.exist; + expect(bidRequest.data.sizes).to.deep.equal([[300, 250]]); + expect(bidRequest.data.params).to.deep.equal({'pix_id': '777'}); + expect(bidRequest.data.adUnitCode).to.deep.equal('26335x300x250x14x_ADSLOT88'); + }); + }); + // Add other `describe` or `it` blocks as necessary +}); From 7057f54f1cf6d358e0a8fc79217fc7927b7daaa3 Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 25 Jun 2021 03:41:35 -0400 Subject: [PATCH 1203/1476] rubicon adapter: segtax change (#7098) --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 222d40043f6..b0d4885fc69 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -978,7 +978,7 @@ function applyFPD(bidRequest, mediaType, data) { let fpd = utils.mergeDeep({}, config.getConfig('ortb2') || {}, BID_FPD); let impData = utils.deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; - const SEGTAX = {user: [3], site: [1, 2]}; + const SEGTAX = {user: [4], site: [1, 2]}; const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; const validate = function(prop, key, parentName) { if (key === 'data' && Array.isArray(prop)) { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 4c0b27ca8be..fdbc70ef443 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -897,7 +897,7 @@ describe('the rubicon adapter', function () { const user = { data: [{ 'name': 'www.dataprovider1.com', - 'ext': { 'segtax': 3 }, + 'ext': { 'segtax': 4 }, 'segment': [ { 'id': '687' }, { 'id': '123' } From 708103a354c09b22de2dd07632069598433de58d Mon Sep 17 00:00:00 2001 From: Igor Tchibirev Date: Fri, 25 Jun 2021 04:14:53 -0400 Subject: [PATCH 1204/1476] Real Vu Analytics Adapter: update addUnitById() return value (#7088) * Remove _ps in _f=conf request * Replace " * realvuAnalyticsAdapter_spec updated * Update realvuAnalyticsAdapter.js * Update realvuAnalyticsAdapter.js Improve value returned by addUnitById() Co-authored-by: Igor Tchibirev --- modules/realvuAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 78fa61c9576..099bfda5e32 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -670,7 +670,7 @@ export let lib = { }; } let a = window.top1.realvu_aa.check(p1); - return a.r; + return a.riff; }, checkBidIn: function(partnerId, args, b) { // process a bid from hb From 4c20820836bd9a444255b7a0121716ce31dc627a Mon Sep 17 00:00:00 2001 From: amishra11j <86069270+amishra11j@users.noreply.github.com> Date: Fri, 25 Jun 2021 14:49:09 +0530 Subject: [PATCH 1205/1476] akamaiDAPIdSystem: add new ID submodule (#7084) --- .../gpt/akamaidap_email_example.html | 117 ++++++++++++++++++ .../gpt/akamaidap_signature_example.html | 116 +++++++++++++++++ modules/.submodules.json | 1 + modules/akamaiDAPIdSystem.js | 105 ++++++++++++++++ modules/akamaiDAPIdSystem.md | 41 ++++++ modules/userId/eids.js | 5 + modules/userId/eids.md | 7 ++ modules/userId/userId.md | 10 +- test/spec/modules/akamaiDAPIdSystem_spec.js | 100 +++++++++++++++ test/spec/modules/userId_spec.js | 25 ++-- 10 files changed, 515 insertions(+), 12 deletions(-) create mode 100755 integrationExamples/gpt/akamaidap_email_example.html create mode 100644 integrationExamples/gpt/akamaidap_signature_example.html create mode 100644 modules/akamaiDAPIdSystem.js create mode 100644 modules/akamaiDAPIdSystem.md create mode 100644 test/spec/modules/akamaiDAPIdSystem_spec.js diff --git a/integrationExamples/gpt/akamaidap_email_example.html b/integrationExamples/gpt/akamaidap_email_example.html new file mode 100755 index 00000000000..ef62876231e --- /dev/null +++ b/integrationExamples/gpt/akamaidap_email_example.html @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+
User IDs Sent to Bidding Adapter
+
+ + diff --git a/integrationExamples/gpt/akamaidap_signature_example.html b/integrationExamples/gpt/akamaidap_signature_example.html new file mode 100644 index 00000000000..94940a659c1 --- /dev/null +++ b/integrationExamples/gpt/akamaidap_signature_example.html @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+
User IDs Sent to Bidding Adapter
+
+ + diff --git a/modules/.submodules.json b/modules/.submodules.json index 2358bca7b4d..555e8adaefa 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -28,6 +28,7 @@ "uid2IdSystem", "admixerIdSystem", "dmdIdSystem", + "akamaiDAPId", "flocIdSystem", "amxIdSystem" ], diff --git a/modules/akamaiDAPIdSystem.js b/modules/akamaiDAPIdSystem.js new file mode 100644 index 00000000000..980fcfaa7f2 --- /dev/null +++ b/modules/akamaiDAPIdSystem.js @@ -0,0 +1,105 @@ +/** + * This module adds DAP to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/akamaiDAPIdSubmodule + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { uspDataHandler } from '../src/adapterManager.js'; + +const MODULE_NAME = 'akamaiDAPId'; +const STORAGE_KEY = 'akamai_dap_token'; + +export const storage = getStorageManager(); + +/** @type {Submodule} */ +export const akamaiDAPIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{dapId:string}} + */ + decode(value) { + utils.logMessage('akamaiDAPId [decode] value=', value); + return { dapId: value }; + }, + + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {ConsentData} [consentData] + * @param {SubmoduleConfig} [config] + * @returns {IdResponse|undefined} + */ + getId(config, consentData) { + const configParams = (config && config.params); + if (!configParams) { + utils.logError('User ID - akamaiDAPId submodule requires a valid configParams'); + return; + } else if (typeof configParams.apiHostname !== 'string') { + utils.logError('User ID - akamaiDAPId submodule requires a valid configParams.apiHostname'); + return; + } else if (typeof configParams.domain !== 'string') { + utils.logError('User ID - akamaiDAPId submodule requires a valid configParams.domain'); + return; + } else if (typeof configParams.type !== 'string') { + utils.logError('User ID - akamaiDAPId submodule requires a valid configParams.type'); + return; + } + const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; + const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const uspConsent = uspDataHandler.getConsentData(); + if (hasGdpr && (!gdprConsentString || gdprConsentString === '')) { + utils.logError('User ID - akamaiDAPId submodule requires consent string to call API'); + return; + } + // XXX: retrieve first-party data here if needed + let url = ''; + let postData; + let tokenName = ''; + if (configParams.type.indexOf('dap-signature:') == 0) { + let parts = configParams.type.split(':'); + let v = parts[1]; + url = `https://${configParams.apiHostname}/data-activation/v1/domain/${configParams.domain}/signature?v=${v}&gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + tokenName = 'SigToken'; + } else { + url = `https://${configParams.apiHostname}/data-activation/v1/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + postData = { + 'version': 1, + 'domain': configParams.domain, + 'identity': configParams.identity, + 'type': configParams.type + }; + tokenName = 'PubToken'; + } + + utils.logInfo('akamaiDAPId[getId] making API call for ' + tokenName); + + let cb = { + success: response => { + storage.setDataInLocalStorage(STORAGE_KEY, response); + }, + error: error => { + utils.logError('akamaiDAPId [getId:ajax.error] failed to retrieve ' + tokenName, error); + } + }; + + ajax(url, cb, JSON.stringify(postData), { contentType: 'application/json' }); + + let token = storage.getDataFromLocalStorage(STORAGE_KEY); + utils.logMessage('akamaiDAPId [getId] returning', token); + + return { id: token }; + } +}; + +submodule('userId', akamaiDAPIdSubmodule); diff --git a/modules/akamaiDAPIdSystem.md b/modules/akamaiDAPIdSystem.md new file mode 100644 index 00000000000..888a409b7a8 --- /dev/null +++ b/modules/akamaiDAPIdSystem.md @@ -0,0 +1,41 @@ +# Akamai Data Activation Platform Audience Segment ID Targeting + +The Akamai Data Activation Platform (DAP) is a privacy-first system that protects end-user privacy by only allowing them to be targeted as part of a larger cohort. DAP views hiding individuals in large cohorts as the best mechanism to prevent unauthorized tracking. + +The integration of DAP into Prebid.JS consists of creating a UserID plugin that interacts with the DAP API. The UserID module tokenizes the end-user identity into an ephemeral, secure pseudonymization called a dapId. The dapId is then supplied to the bid-stream where the SSP partner looks up cohort membership for that token, and supplies the cohorts to the rest of the bid-stream. + +In this system, no end-user identifier is supplied to the bid-stream, only cohorts. This is a foundational privacy principle DAP is built upon. + +## Onboarding + +Please reach out to your Akamai account representative(Prebid@akamai.com) to get provisioned on the DAP platform. + +## DAP Configuration + +First, make sure to add the DAP submodule to your Prebid.js package with: + +``` +gulp build --modules=akamaiDAPId,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'akamaiDAPId', + params: { + apiHostname: '', + domain: 'your-domain.com', + type: 'email' | 'mobile' | ... | 'dap-signature:1.0.0', + identity: ‘your@email.com’ | ‘6175551234' | ... + }, + }], + auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules + } +}); +``` +Refer to the sample integration example present at below location +Prebid.js/integrationExamples/gpt/akamaidap_email_example.html +Prebid.js/integrationExamples/gpt/akamaidap_signature_example.html diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 11c47e3ff8b..32f7c163f3c 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -203,6 +203,11 @@ const USER_IDS_CONFIG = { source: 'deepintent.com', atype: 3 }, + // Akamai Data Activation Platform (DAP) + 'dapId': { + source: 'akamai.com', + atype: 1 + }, // Admixer Id 'admixerId': { source: 'admixer.net', diff --git a/modules/userId/eids.md b/modules/userId/eids.md index f5b08bb1288..279a5a0cb49 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -182,6 +182,13 @@ userIdAsEids = [ atype: 3 }] }, + { + source: 'akamai.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, { source: 'deepintent.com', uids: [{ diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 446c9250b93..1dbdf7588ec 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -50,7 +50,15 @@ pbjs.setConfig({ // Replace partner with comma-separated (if more than one) Parrable Partner Client ID(s) for Parrable-aware bid adapters in use partner: "30182847-e426-4ff9-b2b5-9ca1324ea09b" } - }, { + },{ + name: 'akamaiDAPId', + params: { + apiHostname: '', + domain: 'your-domain.com', + type: 'email' | 'mobile' | ... | 'dap-signature:1.0.0', + identity: ‘your@email.com’ | ‘6175551234' | ... + } + },{ name: 'identityLink', params: { pid: '999', // Set your real identityLink placement ID here diff --git a/test/spec/modules/akamaiDAPIdSystem_spec.js b/test/spec/modules/akamaiDAPIdSystem_spec.js new file mode 100644 index 00000000000..afb5fdf043b --- /dev/null +++ b/test/spec/modules/akamaiDAPIdSystem_spec.js @@ -0,0 +1,100 @@ +import {akamaiDAPIdSubmodule} from 'modules/akamaiDAPIdSystem.js'; +import * as utils from 'src/utils.js'; +import {server} from 'test/mocks/xhr.js'; +import {getStorageManager} from '../../../src/storageManager.js'; + +export const storage = getStorageManager(); + +const signatureConfigParams = {params: { + apiHostname: 'prebid.dap.akadns.net', + domain: 'prebid.org', + type: 'dap-signature:1.0.0' +}}; + +const tokenizeConfigParams = {params: { + apiHostname: 'prebid.dap.akadns.net', + domain: 'prebid.org', + type: 'email', + identity: 'amishra@xyz.com' +}}; + +const consentData = { + gdprApplies: true, + consentString: 'BOkIpDSOkIpDSADABAENCc-AAAApOAFAAMAAsAMIAcAA_g' +}; + +const responseHeader = {'Content-Type': 'application/json'} + +const TEST_ID = '51sd61e3-sd82-4vea-8387-093dffca4a3a'; + +describe('akamaiDAPId getId', function () { + let logErrorStub; + + beforeEach(function () { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + logErrorStub.restore(); + }); + + describe('decode', function () { + it('should respond with an object with dapId containing the value', () => { + expect(akamaiDAPIdSubmodule.decode(TEST_ID)).to.deep.equal({ + dapId: TEST_ID + }); + }); + }); + + describe('getId', function () { + it('should log an error if no configParams were passed when getId', function () { + akamaiDAPIdSubmodule.getId(null); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should log an error if configParams were passed without apihostname', function () { + akamaiDAPIdSubmodule.getId({ params: { + domain: 'prebid.org', + type: 'dap-signature:1.0.0' + } }); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should log an error if configParams were passed without domain', function () { + akamaiDAPIdSubmodule.getId({ params: { + apiHostname: 'prebid.dap.akadns.net', + type: 'dap-signature:1.0.0' + } }); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should log an error if configParams were passed without type', function () { + akamaiDAPIdSubmodule.getId({ params: { + apiHostname: 'prebid.dap.akadns.net', + domain: 'prebid.org' + } }); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('akamaiDAPId submobile requires consent string to call API', function () { + let consentData = { + gdprApplies: true, + consentString: '' + }; + let submoduleCallback = akamaiDAPIdSubmodule.getId(signatureConfigParams, consentData); + expect(submoduleCallback).to.be.undefined; + }); + + it('should call the signature API and store token in Local storage', function () { + let submoduleCallback1 = akamaiDAPIdSubmodule.getId(signatureConfigParams, consentData).id; + expect(submoduleCallback1).to.be.eq(storage.getDataFromLocalStorage('akamai_dap_token')) + storage.removeDataFromLocalStorage('akamai_dap_token'); + }); + + it('should call the tokenize API and store token in Local storage', function () { + let submoduleCallback = akamaiDAPIdSubmodule.getId(tokenizeConfigParams, consentData).id; + expect(submoduleCallback).to.be.eq(storage.getDataFromLocalStorage('akamai_dap_token')) + storage.removeDataFromLocalStorage('akamai_dap_token'); + }); + }); +}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 8df7ad2dc53..6caf8b5e18e 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -47,6 +47,7 @@ import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {flocIdSubmodule} from 'modules/flocIdSystem.js' import { amxIdSubmodule } from '../../../modules/amxIdSystem.js'; +import {akamaiDAPIdSubmodule} from 'modules/akamaiDAPIdSystem.js' let assert = require('chai').assert; let expect = require('chai').expect; @@ -466,7 +467,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -474,14 +475,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -492,7 +493,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -509,7 +510,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -530,8 +531,8 @@ describe('User ID', function () { expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 21 configurations should result in 21 submodules add', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + it('config with 22 configurations should result in 22 submodules add', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -585,6 +586,8 @@ describe('User ID', function () { storage: {name: 'deepintentId', type: 'cookie'} }, { name: 'flocId' + }, { + name: 'akamaiDAPId' }, { name: 'dmdId', storage: {name: 'dmdId', type: 'cookie'} @@ -594,11 +597,11 @@ describe('User ID', function () { }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 21 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 22 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ @@ -614,7 +617,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -629,7 +632,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); init(config); config.setConfig({ userSync: { From 04378c07402bb6f8076fa806b6052fc8fbfea586 Mon Sep 17 00:00:00 2001 From: jsut Date: Mon, 28 Jun 2021 00:36:32 -0400 Subject: [PATCH 1206/1476] Fix the renderer tests so they can be run on their own (#7070) - running ./node_modules/gulp/bin/gulp.js test --file test/spec/renderer_spec.js fails without this change. The adUnits global is set by other tests, and the adloaderSub is loaded by test_index.js. When you run this file on it's own, it will fail. --- test/spec/renderer_spec.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js index dcca94396cd..50e21d2cb36 100644 --- a/test/spec/renderer_spec.js +++ b/test/spec/renderer_spec.js @@ -2,8 +2,19 @@ import { expect } from 'chai'; import { Renderer } from 'src/Renderer.js'; import * as utils from 'src/utils.js'; import { loadExternalScript } from 'src/adloader.js'; +require('test/mocks/adloaderStub.js'); describe('Renderer', function () { + let oldAdUnits; + beforeEach(function () { + oldAdUnits = $$PREBID_GLOBAL$$.adUnits; + $$PREBID_GLOBAL$$.adUnits = []; + }); + + afterEach(function () { + $$PREBID_GLOBAL$$.adUnits = oldAdUnits; + }); + describe('Renderer: A renderer installed on a bid response', function () { let testRenderer1; let testRenderer2; @@ -99,15 +110,12 @@ describe('Renderer', function () { }); describe('3rd party renderer', function () { - let adUnitsOld; let utilsSpy; before(function () { - adUnitsOld = $$PREBID_GLOBAL$$.adUnits; utilsSpy = sinon.spy(utils, 'logWarn'); }); after(function() { - $$PREBID_GLOBAL$$.adUnits = adUnitsOld; utilsSpy.restore(); }); From 19e8f52c5b17fb55b74b5b43e2d89e89585d61ab Mon Sep 17 00:00:00 2001 From: Thomas Sluga <61018578+thomas-vlyby@users.noreply.github.com> Date: Mon, 28 Jun 2021 09:33:02 +0200 Subject: [PATCH 1207/1476] VLYBY Bid Adapter: add new bid adapter (#6417) * vlybyAdapter init * initial vlyby prebid adapter * changes in md * small change in code * code optimized * circle changes * changes * prod endpoint added * md file change * params cleanup * clean up * Revert "clean up" This reverts commit 4202c76ca8dadd911c8047a5cc0caadcf8d9df7f. * Update vlybyBidAdapter.js * Update vlybyBidAdapter.js * test for endpoint * test coverage * test coverage added * added adomain to cover openRTB * added test coverage Co-authored-by: damjan25 --- modules/vlybyBidAdapter.js | 71 ++++++++++++++ modules/vlybyBidAdapter.md | 36 +++++++ test/spec/modules/vlybyBidAdapter_spec.js | 111 ++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 modules/vlybyBidAdapter.js create mode 100755 modules/vlybyBidAdapter.md create mode 100644 test/spec/modules/vlybyBidAdapter_spec.js diff --git a/modules/vlybyBidAdapter.js b/modules/vlybyBidAdapter.js new file mode 100644 index 00000000000..10352179044 --- /dev/null +++ b/modules/vlybyBidAdapter.js @@ -0,0 +1,71 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js' + +import { BANNER, VIDEO } from '../src/mediaTypes.js' + +const ENDPOINT = '//prebid.vlyby.com/'; +const BIDDER_CODE = 'vlyby'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO, BANNER], + + isBidRequestValid: function (bid) { + if (bid && bid.params && bid.params.publisherId) { + return true + } + return false + }, + + buildRequests: function (validBidRequests, bidderRequest = {}) { + const gdprConsent = bidderRequest.gdprConsent || {}; + return { + method: 'POST', + url: `${ENDPOINT}`, + data: { + request: { + auctionId: bidderRequest.auctionId + }, + gdprConsent: { + consentString: gdprConsent.consentString, + gdprApplies: gdprConsent.gdprApplies + }, + bidRequests: validBidRequests.map(({ params, sizes, bidId, adUnitCode }) => ({ + bidId, + adUnitCode, + params, + sizes + })) + }, + options: { + withCredentials: false, + contentType: 'application/json' + }, + validBidRequests: validBidRequests, + } + }, + interpretResponse: function(serverResponse, bidRequest) { + const bidResponses = []; + if (serverResponse.body) { + const vHB = serverResponse.body.bids; + try { + let bidResponse = { + requestId: vHB.bid, + cpm: vHB.cpm, + width: vHB.size.width, + height: vHB.size.height, + creativeId: vHB.creative.id, + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: vHB.creative.ad, + meta: { + adomain: vHB.adomain && Array.isArray(vHB.adomain) ? vHB.adomain : [] + } + }; + bidResponses.push(bidResponse); + } catch (e) { } + } + return bidResponses; + } +}; +registerBidder(spec); diff --git a/modules/vlybyBidAdapter.md b/modules/vlybyBidAdapter.md new file mode 100755 index 00000000000..41d9bbf8709 --- /dev/null +++ b/modules/vlybyBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +``` +Module Name: VLYBY Prebid Outstream +Module Type: Bidder Adapter +Tech-Support: prebid@vlyby.com +Client-Support: support@vlyby.com +``` + +# Description + +VLYBY Digital GmbH provides with this VLYBY Prebid Adapter a Mediation for the Outstream Product. Please contact support@vlyby.com for additional information and access to VLYBY User Interface and Prebid IDs. + +# Demo Implementation + +In most of the cases a Publisher will use his own AdServer for delivering Creatives to a Publisher-Website. This GPT implementation is only a skeleton. You need an additional Line-Item in your AdServer, Prebid Creative, access to VLYBY UI. + +### GPT Implementation +```javascript + var adUnits = [{ + code: '/your-network-id/adunit', + mediaTypes: { + banner: { + sizes: div_1_sizes + } + }, + bids: [{ + bidder: 'vlyby', + params: { + publisherId: 'f363eb2b75459b34592cc4', // needed - only demo + siteId: 'techpreview.vlyby_prebidadapter', // needed - only demo + placement:'default' // optional - provided by VLYBY UI + } + }] + }] +``` \ No newline at end of file diff --git a/test/spec/modules/vlybyBidAdapter_spec.js b/test/spec/modules/vlybyBidAdapter_spec.js new file mode 100644 index 00000000000..0e3bcdfbc4e --- /dev/null +++ b/test/spec/modules/vlybyBidAdapter_spec.js @@ -0,0 +1,111 @@ +import { expect } from 'chai' +import { spec } from 'modules/vlybyBidAdapter.js' +import { newBidder } from 'src/adapters/bidderFactory.js' + +const REQUEST = { + bidder: 'vlyby', + params: { + publisherId: 'f363eb2b75459b34592cc4' + }, + bidderRequestId: '2ab3ae978e021', + auctionId: 'a1427459-5be6-4076-b585-11a14eb5775', + adUnitCode: '/0000/vlyby', + bidId: '2d925f27f5079f', + sizes: [1, 1] +} + +const BIDDER_REQUEST = { + 'request': { + 'auctionId': 'a1427459-5be6-4076-b585-11a14eb57758' + }, + 'gdprConsent': { + + }, + 'bidRequests': [ + { + 'bidId': '2ab3ae978e021', + 'adUnitCode': '/0000/vlyby', + 'params': { + 'publisherId': 'f363eb2b75459b34592cc4' + }, + 'sizes': [[1, 1]] + } + ] +} + +const bids = { + bids: { + bid: '2ab3ae978e021', + cpm: 5.2, + size: { + width: 1, + height: 1 + }, + creative: { + id: '60fe2250-d13d-11eb-8983-d7b28b8ba5af', + ad: '' + }, + adomain: ['vlyby.com'] + } +} + +describe('vlybyBidAdapter', 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 request = { + 'params': { + 'publisherId': 'f363eb2b75459b34592cc4' + } + } + expect(spec.isBidRequestValid(request)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + expect(spec.isBidRequestValid({})).to.equal(false) + }) + }) + + describe('buildRequests', function () { + const bidRequests = [REQUEST] + const request = spec.buildRequests(bidRequests, BIDDER_REQUEST) + + it('sends bid request to ENDPOINT via POST', function () { + expect(request.method).to.equal('POST') + }) + + it('returns a list of valid requests', function () { + expect(request.validBidRequests).to.eql([REQUEST]) + }) + + it('sends params.publisherId', function () { + expect(request.validBidRequests[0].params.publisherId).to.eql(REQUEST.params.publisherId) + }) + }); + + describe('interpretResponse', function () { + it('nobid responses', function () { + expect(spec.interpretResponse({body: {}}).length).to.equal(0) + expect(spec.interpretResponse({body: []}).length).to.equal(0) + }) + + it('handles the response', function () { + const response = spec.interpretResponse({body: bids}); + + expect(response, 'response is not an Array').to.be.an('array') + expect(response[0].cpm, 'cpm does not match').to.equal(5.2) + expect(response[0].width, 'width does not match').to.equal(1) + expect(response[0].height, 'height does not match').to.equal(1) + expect(response[0].creativeId, 'creative ID does not match').to.equal('60fe2250-d13d-11eb-8983-d7b28b8ba5af') + expect(response[0].ad, 'creative Ad does not match').to.equal('') + expect(response[0].meta.adomain, 'creative Ad does not match').to.be.an('array') + }); + }); +}); From 5c0688a866aeac3a15aeecd3226185255b1ecfbc Mon Sep 17 00:00:00 2001 From: Eddy Pechuzal <46331062+epechuzal@users.noreply.github.com> Date: Mon, 28 Jun 2021 07:42:36 -0700 Subject: [PATCH 1208/1476] Sharethrough Bid Adapter: add support for GPID (#7089) * Parse and send gpid param * Update version of STR bidder * Fix broken test --- modules/sharethroughBidAdapter.js | 9 +++++++-- .../modules/sharethroughBidAdapter_spec.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index eea42af4a4c..967d8194607 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -const VERSION = '3.4.0'; +const VERSION = '3.4.1'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -29,9 +29,14 @@ export const sharethroughAdapterSpec = { instant_play_capable: canAutoPlayHTML5Video(), hbSource: 'prebid', hbVersion: '$prebid.version$', - strVersion: VERSION + strVersion: VERSION, }; + const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + query.gpid = gpid; + } + Object.assign(query, handleUniversalIds(bidRequest)); const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 68d7b6210df..3e406e1af44 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -14,6 +14,13 @@ const bidRequests = [ params: { pkey: 'aaaa1111' }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'adslot-id-1' + } + } + }, userId: { tdid: 'fake-tdid', pubcid: 'fake-pubcid', @@ -407,6 +414,18 @@ describe('sharethrough adapter spec', function() { expect(builtBidRequest.data.schain).to.eq(JSON.stringify(bidRequest.schain)); }); + describe('gpid', () => { + it('should include the gpid param if pbadslot is found in ortb2Imp in the bid request', () => { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.gpid).to.eq('adslot-id-1') + }); + + it('should not include the gpid param if pbadslot is not found in ortb2Imp in the bid request', () => { + const bidRequest = spec.buildRequests(bidRequests)[1]; + expect(bidRequest.data).to.not.include.any.keys('gpid'); + }); + }); + it('should add badv if provided', () => { const builtBidRequest = spec.buildRequests([bidRequests[3]])[0]; From 66bfe22b5b929f5d215f3b62b005782e7066ec03 Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Mon, 28 Jun 2021 08:41:37 -0700 Subject: [PATCH 1209/1476] IX Bid Adapter: Use openrtb imp[].banner.format to build requests (#7023) * use openrtb banner.format in request to cygnus * remove imp[].ext * update unit tests, removing imp[].ext --- modules/ixBidAdapter.js | 191 +++---- test/spec/modules/ixBidAdapter_spec.js | 658 ++++++++++++------------- 2 files changed, 406 insertions(+), 443 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index eb6c396f742..766ece90fce 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -16,6 +16,8 @@ const CENT_TO_DOLLAR_FACTOR = 100; const BANNER_TIME_TO_LIVE = 300; const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NET_REVENUE = true; +const MAX_REQUEST_SIZE = 8000; +const MAX_REQUEST_LIMIT = 4; const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 @@ -48,7 +50,7 @@ const PROVIDERS = [ 'pubcid', 'TDID', 'flocId' -] +]; const REQUIRED_VIDEO_PARAMS = ['mimes', 'minduration', 'maxduration']; // note: protocol/protocols is also reqd @@ -75,8 +77,8 @@ function bidToBannerImp(bid) { imp.banner.w = impSize[0]; imp.banner.h = impSize[1]; // populate sid with size if not id - if (!(utils.deepAccess(imp, 'ext.sid'))) { - imp.ext.sid = `${impSize[0]}x${impSize[1]}`; + if (!utils.deepAccess(imp, 'ext.sid')) { + imp.ext.sid = utils.parseGPTSingleSizeArray(impSize); } } @@ -112,8 +114,7 @@ function bidToVideoImp(bid) { } if (imp.video.minduration > imp.video.maxduration) { - utils.logError('IX Bid Adapter: video minduration [' + imp.video.minduration + - '] cannot be greater than video maxduration [' + imp.video.maxduration + ']'); + utils.logError(`IX Bid Adapter: video minduration [${imp.video.minduration}] cannot be greater than video maxduration [${imp.video.maxduration}]`); return {}; } @@ -137,7 +138,7 @@ function bidToVideoImp(bid) { imp.video.w = impSize[0]; imp.video.h = impSize[1]; if (!(utils.deepAccess(imp, 'ext.sid'))) { - imp.ext.sid = `${impSize[0]}x${impSize[1]}`; + imp.ext.sid = utils.parseGPTSingleSizeArray(impSize); } } else { utils.logWarn('IX Bid Adapter: Video size is missing in [mediaTypes.video] missing'); @@ -326,7 +327,7 @@ function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { const propInVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty(property); if (!propInMediaType && !propInVideoRef) { - utils.logError('IX Bid Adapter: ' + property + ' is not included in either the adunit or params level'); + utils.logError(`IX Bid Adapter: ${property} is not included in either the adunit or params level`); reqParamsPresent = false; } } @@ -468,11 +469,13 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Since bidderRequestId are the same for different bid request, just use the first one. r.id = validBidRequests[0].bidderRequestId.toString(); - r.site = {}; r.ext = {}; r.ext.source = 'prebid'; r.ext.ixdiag = {}; + r.ext.ixdiag.msd = 0; + r.ext.ixdiag.msi = 0; + r.imp = []; // getting ixdiags for adunits of the video, outstream & multi format (MF) style let ixdiag = buildIXDiag(validBidRequests); @@ -539,10 +542,31 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } const payload = {}; + // Use the siteId in the first bid request as the main siteId. + payload.s = validBidRequests[0].params.siteId; + payload.v = version; + payload.ac = 'j'; + payload.sd = 1; + + if (version === VIDEO_ENDPOINT_VERSION) { + payload.nf = 1; + } // Parse additional runtime configs. const bidderCode = (bidderRequest && bidderRequest.bidderCode) || 'ix'; const otherIxConfig = config.getConfig(bidderCode); + const requests = []; + let requestSequenceNumber = 0; + const transactionIds = Object.keys(impressions); + const baseRequestSize = `${baseUrl}${utils.parseQueryStringParameters({ ...payload, r: JSON.stringify(r) })}`.length; + + if (baseRequestSize > MAX_REQUEST_SIZE) { + utils.logError('ix bidder: Base request size has exceeded maximum request size.'); + return requests; + } + + let currentRequestSize = baseRequestSize; + if (otherIxConfig) { // Append firstPartyData to r.site.page if firstPartyData exists. if (typeof otherIxConfig.firstPartyData === 'object') { @@ -570,75 +594,64 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } } - // Use the siteId in the first bid request as the main siteId. - payload.s = validBidRequests[0].params.siteId; - payload.v = version; - payload.ac = 'j'; - payload.sd = 1; - if (version === VIDEO_ENDPOINT_VERSION) { - payload.nf = 1; - } - - const requests = []; - - const request = { - method: 'GET', - url: baseUrl, - data: payload - }; - - const BASE_REQ_SIZE = new Blob([`${request.url}${utils.parseQueryStringParameters({ ...request.data, r: JSON.stringify(r) })}`]).size; - let currReqSize = BASE_REQ_SIZE; - - const MAX_REQ_SIZE = 8000; - const MAX_REQ_LIMIT = 4; - let sn = 0; - let msi = 0; - let msd = 0; - r.ext.ixdiag.msd = 0; - r.ext.ixdiag.msi = 0; - r.imp = []; - let i = 0; - const transactionIds = Object.keys(impressions); - let currMissingImps = []; - - while (i < transactionIds.length && requests.length < MAX_REQ_LIMIT) { - const impObj = impressions[transactionIds[i]]; - msd = utils.deepAccess(impObj, 'missingCount') && utils.deepAccess(impObj, 'ixImps') ? impObj.missingCount : 0; + for (let adUnitIndex = 0; adUnitIndex < transactionIds.length; adUnitIndex++) { + if (currentRequestSize >= MAX_REQUEST_SIZE || requests.length >= MAX_REQUEST_LIMIT) { + break; + } - if (BASE_REQ_SIZE < MAX_REQ_SIZE) { - trimImpressions(impObj, MAX_REQ_SIZE - BASE_REQ_SIZE); - } else { - utils.logError('ix bidder: Base request size has exceeded maximum request size.'); + const adUnitImpressions = impressions[transactionIds[adUnitIndex]]; + const { missingCount = 0, missingImps: missingBannerImpressions = [], ixImps = [] } = adUnitImpressions; + let wasAdUnitImpressionsTrimmed = false; + let remainingRequestSize = MAX_REQUEST_SIZE - currentRequestSize; + const sourceImpressions = { ixImps, missingBannerImpressions }; + const impressionObjects = Object.keys(sourceImpressions) + .map((key) => sourceImpressions[key]) + .filter(item => Array.isArray(item)) + .reduce((acc, curr) => acc.concat(...curr), []); + + let currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length; + + while (currentImpressionSize > remainingRequestSize) { + wasAdUnitImpressionsTrimmed = true; + impressionObjects.pop(); + currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length; } - msi = utils.deepAccess(impObj, 'missingImps') && utils.deepAccess(impObj, 'ixImps') ? impObj.missingImps.length : 0; + if (BANNER in impressionObjects[0]) { + const { id, banner: { topframe } } = impressionObjects[0]; + const _bannerImpression = { + id, + banner: { + topframe, + format: impressionObjects.map(({ banner: { w, h }, ext }) => ({ w, h, ext })) + }, + } - let currImpsSize = new Blob([encodeURIComponent(JSON.stringify(impObj))]).size; - currReqSize += currImpsSize; - if (currReqSize < MAX_REQ_SIZE) { - // since not reading params.size, ixImps can be undefined/empty - if (impObj.ixImps && impObj.ixImps.length > 0) { - // pushing ix configured sizes first - r.imp.push(...impObj.ixImps); + if ('bidfloor' in impressionObjects[0]) { + _bannerImpression.bidfloor = impressionObjects[0].bidfloor; } - // update msd msi - r.ext.ixdiag.msd += msd; - r.ext.ixdiag.msi += msi; - if (impObj.hasOwnProperty('missingImps') && impObj.missingImps.length > 0) { - currMissingImps.push(...impObj.missingImps); + if ('bidfloorcur' in impressionObjects[0]) { + _bannerImpression.bidfloorcur = impressionObjects[0].bidfloorcur; } - i++; + r.imp.push(_bannerImpression); + r.ext.ixdiag.msd += missingCount; + r.ext.ixdiag.msi += missingBannerImpressions.length; } else { - // pushing missing sizes after configured ones + r.imp.push(...impressionObjects); + } + + const isLastAdUnit = adUnitIndex === transactionIds.length - 1; + + if (wasAdUnitImpressionsTrimmed || isLastAdUnit) { const clonedPayload = utils.deepClone(payload); + if (!isLastAdUnit || requestSequenceNumber) { + r.ext.ixdiag.sn = requestSequenceNumber; + clonedPayload.sn = requestSequenceNumber; + } - r.imp.push(...currMissingImps); - r.ext.ixdiag.sn = sn; - clonedPayload.sn = sn; - sn++; + requestSequenceNumber++; clonedPayload.r = JSON.stringify(r); requests.push({ @@ -646,33 +659,14 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { url: baseUrl, data: clonedPayload }); - currMissingImps = []; - currReqSize = BASE_REQ_SIZE; + + currentRequestSize = baseRequestSize; r.imp = []; - msd = 0; - msi = 0; r.ext.ixdiag.msd = 0; r.ext.ixdiag.msi = 0; } } - if (currReqSize > BASE_REQ_SIZE && currReqSize < MAX_REQ_SIZE && requests.length < MAX_REQ_LIMIT) { - const clonedPayload = utils.deepClone(payload); - r.imp.push(...currMissingImps); - - if (requests.length > 0) { - r.ext.ixdiag.sn = sn; - clonedPayload.sn = sn; - } - clonedPayload.r = JSON.stringify(r); - - requests.push({ - method: 'GET', - url: baseUrl, - data: clonedPayload - }); - } - return requests; } @@ -748,27 +742,6 @@ function buildIXDiag(validBidRequests) { return ixdiag; } -/** - * - * @param {Object} impressions containing ixImps and possibly missingImps - * - */ -function trimImpressions(impressions, maxSize) { - let currSize = new Blob([encodeURIComponent(JSON.stringify(impressions))]).size; - if (currSize < maxSize) { - return; - } - - while (currSize > maxSize) { - if (impressions.hasOwnProperty('missingImps') && impressions.missingImps.length > 0) { - impressions.missingImps.pop(); - } else if (impressions.hasOwnProperty('ixImps') && impressions.ixImps.length > 0) { - impressions.ixImps.pop(); - } - currSize = new Blob([encodeURIComponent(JSON.stringify(impressions))]).size; - } -} - /** * * @param {array} bannerSizeList list of banner sizes @@ -826,7 +799,7 @@ function updateMissingSizes(validBidRequest, missingBannerSizes, imp) { */ function createMissingBannerImp(bid, imp, newSize) { const newImp = utils.deepClone(imp); - newImp.ext.sid = `${newSize[0]}x${newSize[1]}`; + newImp.ext.sid = utils.parseGPTSingleSizeArray(newSize); newImp.banner.w = newSize[0]; newImp.banner.h = newSize[1]; diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index b021b56024c..4c24af6c082 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -26,7 +26,7 @@ describe('IndexexchangeAdapter', function () { } ] }; - const div_many_sizes = [ + const LARGE_SET_OF_SIZES = [ [300, 250], [600, 410], [336, 280], @@ -210,7 +210,7 @@ describe('IndexexchangeAdapter', function () { bidder: 'ix', params: { siteId: '123', - size: [300, 250] + size: [300, 250], }, mediaTypes: { video: { @@ -218,7 +218,7 @@ describe('IndexexchangeAdapter', function () { playerSize: [600, 700] }, banner: { - sizes: [[300, 250], [300, 600]] + sizes: [[300, 250], [300, 600], [400, 500]] } }, adUnitCode: 'div-gpt-ad-1460505748562-0', @@ -409,7 +409,7 @@ describe('IndexexchangeAdapter', function () { netId: 'testnetid123', // NetId IDP: 'userIDP000', // IDP fabrickId: 'fabrickId9000', // FabrickId - uid2: {id: 'testuid2'} // UID 2.0 + uid2: { id: 'testuid2' } // UID 2.0 }; const DEFAULT_USERIDASEIDS_DATA = createEidsArray(DEFAULT_USERID_DATA); @@ -461,7 +461,7 @@ describe('IndexexchangeAdapter', function () { const DEFAULT_USERID_BID_DATA = { lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4', - flocId: {id: '1234', version: 'chrome.1.2'} + flocId: { id: '1234', version: 'chrome.1.2' } }; const DEFAULT_FLOC_USERID_PAYLOAD = [ @@ -929,7 +929,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); }); - it('IX adapter reads floc id from prebid userId and adds it to eids when there is not other eids', function() { + it('IX adapter reads floc id from prebid userId and adds it to eids when there is not other eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; @@ -939,7 +939,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); }); - it('IX adapter reads floc id from prebid userId and appends it to eids', function() { + it('IX adapter reads floc id from prebid userId and appends it to eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); @@ -955,10 +955,10 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); }); - it('IX adapter reads empty floc obj from prebid userId it, floc is not added to eids', function() { + it('IX adapter reads empty floc obj from prebid userId it, floc is not added to eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); - cloneValidBid[0].userId = {'flocId': {}} + cloneValidBid[0].userId = { 'flocId': {} } const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -971,10 +971,10 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); }); - it('IX adapter reads floc obj from prebid userId it version is missing, floc is not added to eids', function() { + it('IX adapter reads floc obj from prebid userId it version is missing, floc is not added to eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); - cloneValidBid[0].userId = {'flocId': {'id': 'abcd'}} + cloneValidBid[0].userId = { 'flocId': { 'id': 'abcd' } } const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -987,10 +987,10 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); }); - it('IX adapter reads floc obj from prebid userId it ID is missing, floc is not added to eids', function() { + it('IX adapter reads floc obj from prebid userId it ID is missing, floc is not added to eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); - cloneValidBid[0].userId = {'flocId': {'version': 'chrome.a.b.c'}} + cloneValidBid[0].userId = { 'flocId': { 'version': 'chrome.a.b.c' } } const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -1003,10 +1003,10 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); }); - it('IX adapter reads floc id with empty id from prebid userId and it does not added to eids', function() { + it('IX adapter reads floc id with empty id from prebid userId and it does not added to eids', function () { const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); - cloneValidBid[0].userId = {flocID: {id: '', ver: 'chrome.1.2.3'}}; + cloneValidBid[0].userId = { flocID: { id: '', ver: 'chrome.1.2.3' } }; const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = JSON.parse(request.data.r); @@ -1277,14 +1277,14 @@ describe('IndexexchangeAdapter', function () { } }; const requests = spec.buildRequests(validBids, DEFAULT_OPTION); - const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].banner.format[0].ext; expect(dfp_ad_unit_code).to.equal(AD_UNIT_CODE); }); it('should not send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot does not exists', function () { const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); const requests = spec.buildRequests(validBids, DEFAULT_OPTION); - const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].banner.format[0].ext; expect(dfp_ad_unit_code).to.not.exist; }); @@ -1293,15 +1293,24 @@ describe('IndexexchangeAdapter', function () { const payload = JSON.parse(query.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); expect(payload.id).to.be.a('string'); - expect(payload.site).to.exist; expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); - expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); expect(payload.source.ext.schain).to.deep.equal(SAMPLE_SCHAIN); - expect(payload.imp).to.exist; expect(payload.imp).to.be.an('array'); - expect(payload.imp).to.have.lengthOf(2); + expect(payload.imp).to.have.lengthOf(1); + }); + + it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { + const bidWithIntId = utils.deepClone(DEFAULT_BANNER_VALID_BID); + bidWithIntId[0].bidderRequestId = 123456; + + request = spec.buildRequests(bidWithIntId, DEFAULT_OPTION)[0]; + + const payload = JSON.parse(request.data.r); + expect(bidWithIntId[0].bidderRequestId).to.be.a('number'); + expect(payload.id).to.equal(bidWithIntId[0].bidderRequestId.toString()); + expect(payload.id).to.be.a('string'); }); it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { @@ -1323,199 +1332,162 @@ describe('IndexexchangeAdapter', function () { it('impression should have correct format and value', function () { const impression = JSON.parse(query.r).imp[0]; - const sidValue = `${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner).to.exist; - expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.exist; + expect(impression.banner.format).to.be.length(2); expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal(sidValue); - }); - it('video impression should contain floors from priceFloors module', function () { - const bid = utils.deepClone(ONE_VIDEO[0]); - const flr = 5.5 - const floorInfo = {floor: flr, currency: 'USD'}; - bid.getFloor = function () { - return floorInfo; - } + impression.banner.format.map(({ w, h, ext }, index) => { + const size = DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[index]; + const sidValue = utils.parseGPTSingleSizeArray(size); - // check if floors are in imp - const requestBidFloor = spec.buildRequests([bid])[0]; - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(flr); - expect(imp1.bidfloorcur).to.equal('USD'); - expect(imp1.ext.fl).to.equal('p'); + expect(w).to.equal(size[0]); + expect(h).to.equal(size[1]); + expect(ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(ext.sid).to.equal(sidValue); + }); }); - it('banner impression should contain floors from priceFloors module', function () { - const floor300x250 = 3.25 - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]) + describe('build requests with price floors', () => { + const highFloor = 4.5; + const lowFloor = 3.5; + const currency = 'USD'; - const floorInfo = { floor: floor300x250, currency: 'USD' }; - bid.getFloor = function () { - return floorInfo; - }; + it('video impression should contain floors from priceFloors module', function () { + const bid = utils.deepClone(ONE_VIDEO[0]); + const expectedFloor = 3.25; + bid.getFloor = () => ({ floor: expectedFloor, currency }); + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; - // check if floors are in imp - const requestBidFloor = spec.buildRequests([bid])[0]; - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - - expect(imp1.bidfloorcur).to.equal('USD'); - expect(imp1.bidfloor).to.equal(floor300x250); - expect(imp1.ext.fl).to.equal('p'); - }); + expect(impression.bidfloor).to.equal(expectedFloor); + expect(impression.bidfloorcur).to.equal(currency); + expect(impression.ext.fl).to.equal('p'); + }); - it('should default to ix floors when priceFloors Module is not implemented', function () { - const bid = utils.deepClone(ONE_BANNER[0]); + it('banner impression should contain floors from priceFloors module', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]) + const expectedFloor = 3.25; + bid.getFloor = () => ({ floor: expectedFloor, currency }); + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; - bid.params.bidFloor = 4.5; - bid.params.bidFloorCur = 'USD'; + expect(impression.bidfloor).to.equal(expectedFloor); + expect(impression.bidfloorcur).to.equal(currency); + expect(impression.banner.format[0].ext.fl).to.equal('p'); + }); - const floorInfo = null; - bid.getFloor = function () { - return floorInfo; - }; + it('should default to ix floors when priceFloors Module is not implemented', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + bid.params.bidFloor = highFloor; + bid.params.bidFloorCur = 'USD' + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; - // check if floors are in imp - const requestBidFloor = spec.buildRequests([bid])[0]; - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(bid.params.bidFloor); - expect(imp1.bidfloorcur).to.equal(bid.params.bidFloorCur); - expect(imp1.ext.fl).to.equal('x'); - }); + expect(impression.bidfloor).to.equal(highFloor); + expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(impression.banner.format[0].ext.fl).to.equal('x'); + }); - it('should prioritize Floors Module over IX param floors', function () { - const bid = utils.deepClone(ONE_BANNER[0]); + it('should prioritize priceFloors Module over IX param floors', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + bid.params.bidFloor = lowFloor; + bid.params.bidFloorCur = 'USD'; + const expectedFloor = highFloor; + bid.getFloor = () => ({ floor: expectedFloor, currency }); + const requestBidFloor = spec.buildRequests([bid])[0]; + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + + expect(impression.bidfloor).to.equal(highFloor); + expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(impression.banner.format[0].ext.fl).to.equal('p'); + }); - const floor1 = 4.5 - const floor2 = 3.5 + it('impression should have bidFloor and bidFloorCur if configured', function () { + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.params.bidFloor = 50; + bid.params.bidFloorCur = 'USD'; + const requestBidFloor = spec.buildRequests([bid])[0]; + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; - bid.params.bidFloor = floor2 - bid.params.bidFloorCur = 'USD' + expect(impression.bidfloor).to.equal(bid.params.bidFloor); + expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); + expect(impression.banner.format[0].ext.fl).to.equal('x'); + }); - const floorInfo = { floor: floor1, currency: 'USD' }; - bid.getFloor = function () { - return floorInfo; - }; + it('missing sizes impressions should contain floors from priceFloors module ', function () { + const bid = utils.deepClone(ONE_BANNER[0]); + bid.mediaTypes.banner.sizes.push([500, 400]) - // check if floors are in imp - const requestBidFloor = spec.buildRequests([bid])[0]; - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(floorInfo.floor); - expect(imp1.bidfloorcur).to.equal(floorInfo.currency); - expect(imp1.ext.fl).to.equal('p'); - }); + const expectedFloor = 3.25; + bid.getFloor = () => ({ floor: expectedFloor, currency }); - it('impression should have bidFloor and bidFloorCur if configured', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid.params.bidFloor = 50; - bid.params.bidFloorCur = 'USD'; - const requestBidFloor = spec.buildRequests([bid])[0]; - const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + sinon.spy(bid, 'getFloor'); - expect(impression.bidfloor).to.equal(bid.params.bidFloor); - expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur); - expect(impression.ext.fl).to.equal('x'); - }); + const requestBidFloor = spec.buildRequests([bid])[0]; + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); - it('missing sizes impressions should contain floors from priceFloors module ', function () { - const bid = utils.deepClone(ONE_BANNER[0]); - bid.mediaTypes.banner.sizes.push([500, 400]) + expect(bid.getFloor.getCall(1).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(1).args[0].size[0]).to.equal(500); + expect(bid.getFloor.getCall(1).args[0].size[1]).to.equal(400); - const floorInfo = { floor: 3.25, currency: 'USD' }; - bid.getFloor = function () { - return floorInfo; - }; + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(impression.bidfloor).to.equal(expectedFloor); + expect(impression.bidfloorcur).to.equal(currency); + }); - sinon.spy(bid, 'getFloor'); + it('banner impressions should have pricefloors in AdUnit', function () { + const bid = utils.deepClone(ONE_BANNER[0]); - const requestBidFloor = spec.buildRequests([bid])[0]; - // called getFloor with 300 x 250 - expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); - expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); - expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); - - // called getFloor with 500 x 400 - expect(bid.getFloor.getCall(1).args[0].mediaType).to.equal('banner'); - expect(bid.getFloor.getCall(1).args[0].size[0]).to.equal(500); - expect(bid.getFloor.getCall(1).args[0].size[1]).to.equal(400); - - // both will have same floors due to mock getFloor - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(3.25); - expect(imp1.bidfloorcur).to.equal('USD'); - - const imp2 = JSON.parse(requestBidFloor.data.r).imp[1]; - expect(imp2.bidfloor).to.equal(3.25); - expect(imp2.bidfloorcur).to.equal('USD'); - }); - - it('#pricefloors inAdUnit, banner impressions should have floors', function () { - const bid = utils.deepClone(ONE_BANNER[0]); - - const flr = 4.3 - bid.floors = { - currency: 'USD', - schema: { - delimiter: '|', - fields: ['mediaType', 'size'] - }, - values: { - 'banner|300x250': flr, - 'banner|600x500': 6.5, - 'banner|*': 7.5 - } - }; - const floorInfo = { floor: flr, currency: 'USD' }; - bid.getFloor = function () { - return floorInfo; - }; + const expectedFloor = 4.3; + bid.floors = { + currency: 'USD', + schema: { + delimiter: '|', + fields: ['mediaType', 'size'] + }, + values: { + 'banner|300x250': expectedFloor, + 'banner|600x500': 6.5, + 'banner|*': 7.5 + } + }; + bid.getFloor = () => ({ floor: expectedFloor, currency }); - sinon.spy(bid, 'getFloor'); + sinon.spy(bid, 'getFloor'); - const requestBidFloor = spec.buildRequests([bid])[0]; - // called getFloor with 300 x 250 - expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); - expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); - expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); + const requestBidFloor = spec.buildRequests([bid])[0]; + expect(bid.getFloor.getCall(0).args[0].mediaType).to.equal('banner'); + expect(bid.getFloor.getCall(0).args[0].size[0]).to.equal(300); + expect(bid.getFloor.getCall(0).args[0].size[1]).to.equal(250); - const imp1 = JSON.parse(requestBidFloor.data.r).imp[0]; - expect(imp1.bidfloor).to.equal(flr); - expect(imp1.bidfloorcur).to.equal('USD'); + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(impression.bidfloor).to.equal(expectedFloor); + expect(impression.bidfloorcur).to.equal(currency); + }); }); it('payload without mediaType should have correct format and value', function () { const payload = JSON.parse(queryWithoutMediaType.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); - expect(payload.site).to.exist; expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); - expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); - expect(payload.imp).to.exist; expect(payload.imp).to.be.an('array'); expect(payload.imp).to.have.lengthOf(1); }); it('impression without mediaType should have correct format and value', function () { const impression = JSON.parse(queryWithoutMediaType.r).imp[0]; - const sidValue = `${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner).to.exist; - expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.exist; + expect(impression.banner.format).to.be.length(1); + expect(impression.banner.format[0].w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(impression.banner.format[0].h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal(sidValue); }); it('impression should have sid if id is configured as number', function () { @@ -1525,14 +1497,11 @@ describe('IndexexchangeAdapter', function () { const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner).to.exist; - expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);// undefined - 300 - expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.exist; + expect(impression.banner.format[0].w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(impression.banner.format[0].h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal('50'); + expect(impression.banner.format[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(impression.banner.format[0].ext.sid).to.equal('50'); }); it('impression should have sid if id is configured as string', function () { @@ -1540,131 +1509,146 @@ describe('IndexexchangeAdapter', function () { bid.params.id = 'abc'; const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner).to.exist; - expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.exist; + expect(impression.banner.format[0].w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(impression.banner.format[0].h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal('abc'); + expect(impression.banner.format[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(impression.banner.format[0].ext.sid).to.equal('abc'); }); - it('should add first party data to page url in bid request if it exists in config', function () { - config.setConfig({ - ix: { - firstPartyData: { - ab: 123, - cd: '123#ab', - 'e/f': 456, - 'h?g': '456#cd' + describe('first party data', () => { + it('should add first party data to page url in bid request if it exists in config', function () { + config.setConfig({ + ix: { + firstPartyData: { + ab: 123, + cd: '123#ab', + 'e/f': 456, + 'h?g': '456#cd' + } } - } - }); - - const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; - const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; - const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; - expect(pageUrl).to.equal(expectedPageUrl); - }); + }); - it('should not set first party data if it is not an object', function () { - config.setConfig({ - ix: { - firstPartyData: 500 - } + const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; + const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; + expect(pageUrl).to.equal(expectedPageUrl); }); - const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; - const pageUrl = JSON.parse(requestFirstPartyDataNumber.data.r).site.page; + it('should not set first party data if it is not an object', function () { + config.setConfig({ + ix: { + firstPartyData: 500 + } + }); - expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); - }); + const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + const pageUrl = JSON.parse(requestFirstPartyDataNumber.data.r).site.page; - it('should not set first party or timeout if it is not present', function () { - config.setConfig({ - ix: {} + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); }); - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; - const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; + it('should not set first party or timeout if it is not present', function () { + config.setConfig({ + ix: {} + }); - expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); - expect(requestWithoutConfig.data.t).to.be.undefined; - }); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - it('should not set first party or timeout if it is setConfig is not called', function () { - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; - const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); + expect(requestWithoutConfig.data.t).to.be.undefined; + }); - expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); - expect(requestWithoutConfig.data.t).to.be.undefined; - }); + it('should not set first party or timeout if it is setConfig is not called', function () { + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - it('should set timeout if publisher set it through setConfig', function () { - config.setConfig({ - ix: { - timeout: 500 - } + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); + expect(requestWithoutConfig.data.t).to.be.undefined; }); - const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; - expect(requestWithTimeout.data.t).to.equal(500); - }); + it('should set timeout if publisher set it through setConfig', function () { + config.setConfig({ + ix: { + timeout: 500 + } + }); + const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; - it('should set timeout if timeout is a string', function () { - config.setConfig({ - ix: { - timeout: '500' - } + expect(requestWithTimeout.data.t).to.equal(500); }); - const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; - expect(requestStringTimeout.data.t).to.be.undefined; + it('should set timeout if timeout is a string', function () { + config.setConfig({ + ix: { + timeout: '500' + } + }); + const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + + expect(requestStringTimeout.data.t).to.be.undefined; + }); }); - it('request should contain both banner and video requests', function () { + describe('request should contain both banner and video requests', function () { const request = spec.buildRequests([DEFAULT_BANNER_VALID_BID[0], DEFAULT_VIDEO_VALID_BID[0]]); - const bannerImp = JSON.parse(request[0].data.r).imp[0]; - expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(2); - expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); - expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(bannerImp.banner).to.exist; - expect(bannerImp.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(bannerImp.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); + it('should have banner request', () => { + const bannerImpression = JSON.parse(request[0].data.r).imp[0]; + + expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(1); + expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); + expect(bannerImpression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); + + expect(bannerImpression.banner.format).to.be.length(2); + expect(bannerImpression.banner.topframe).to.be.oneOf([0, 1]); + + bannerImpression.banner.format.map(({ w, h, ext }, index) => { + const size = DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[index]; + const sidValue = utils.parseGPTSingleSizeArray(size); - const videoImp = JSON.parse(request[1].data.r).imp[0]; - expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); - expect(videoImp.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); - expect(videoImp.video).to.exist; - expect(videoImp.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); - expect(videoImp.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + expect(w).to.equal(size[0]); + expect(h).to.equal(size[1]); + expect(ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); + expect(ext.sid).to.equal(sidValue); + }); + }); + + it('should have video request', () => { + const videoImpression = JSON.parse(request[1].data.r).imp[0]; + + expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); + expect(videoImpression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(videoImpression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(videoImpression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + }); }); it('single request under 8k size limit for large ad unit', function () { const options = {}; - const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid1.mediaTypes.banner.sizes = div_many_sizes; - const requests = spec.buildRequests([bid1], options); + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES; + const requests = spec.buildRequests([bid], options); - const reqSize = new Blob([`${requests[0].url}?${utils.parseQueryStringParameters(requests[0].data)}`]).size; + const reqSize = `${requests[0].url}?${utils.parseQueryStringParameters(requests[0].data)}`.length; expect(requests).to.be.an('array'); expect(requests).to.have.lengthOf(1); expect(reqSize).to.be.lessThan(8000); + expect(requests[0].data.sn).to.be.undefined; }); it('2 requests due to 2 ad units, one larger than url size', function () { - const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid1.mediaTypes.banner.sizes = div_many_sizes; - bid1.params.siteId = '124'; - bid1.adUnitCode = 'div-gpt-1' - bid1.transactionId = '152e36d1-1241-4242-t35e-y1dv34d12315'; - bid1.bidId = '2f6g5s5e'; + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES; + bid.params.siteId = '124'; + bid.adUnitCode = 'div-gpt-1' + bid.transactionId = '152e36d1-1241-4242-t35e-y1dv34d12315'; + bid.bidId = '2f6g5s5e'; - const requests = spec.buildRequests([bid1, DEFAULT_BANNER_VALID_BID[0]], DEFAULT_OPTION); + const requests = spec.buildRequests([bid, DEFAULT_BANNER_VALID_BID[0]], DEFAULT_OPTION); expect(requests).to.be.an('array'); expect(requests).to.have.lengthOf(2); expect(requests[0].data.sn).to.be.equal(0); @@ -1673,7 +1657,7 @@ describe('IndexexchangeAdapter', function () { it('6 ad units should generate only 4 requests', function () { const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid1.mediaTypes.banner.sizes = div_many_sizes; + bid1.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES; bid1.params.siteId = '121'; bid1.adUnitCode = 'div-gpt-1' bid1.transactionId = 'tr1'; @@ -1699,9 +1683,8 @@ describe('IndexexchangeAdapter', function () { expect(requests).to.be.an('array'); expect(requests).to.have.lengthOf(4); - // check if seq number increases for (var i = 0; i < requests.length; i++) { - const reqSize = new Blob([`${requests[i].url}?${utils.parseQueryStringParameters(requests[i].data)}`]).size; + const reqSize = `${requests[i].url}?${utils.parseQueryStringParameters(requests[i].data)}`.length; expect(reqSize).to.be.lessThan(8000); let payload = JSON.parse(requests[i].data.r); if (requests.length > 1) { @@ -1727,12 +1710,14 @@ describe('IndexexchangeAdapter', function () { bid3.mediaTypes.banner.sizes = [[330, 331], [332, 333], [300, 250]]; const requests = spec.buildRequests([bid1, bid2, bid3], DEFAULT_OPTION); + expect(requests).to.be.an('array'); expect(requests).to.have.lengthOf(1); const impressions = JSON.parse(requests[0].data.r).imp; expect(impressions).to.be.an('array'); - expect(impressions).to.have.lengthOf(9); + expect(impressions).to.have.lengthOf(3); + expect(requests[0].data.sn).to.be.undefined; }); it('request should contain the extra banner ad sizes that IX is not configured for using the first site id in the ad unit', function () { @@ -1745,24 +1730,21 @@ describe('IndexexchangeAdapter', function () { bid2.params.bidId = '2b3c4d5e'; const request = spec.buildRequests([bid, bid2], DEFAULT_OPTION)[0]; - const impressions = JSON.parse(request.data.r).imp; + const impression = JSON.parse(request.data.r).imp[0]; - expect(impressions).to.be.an('array'); - expect(impressions).to.have.lengthOf(4); + expect(impression.id).to.equal(bid.bidId); + expect(impression.banner.format).to.be.length(bid.mediaTypes.banner.sizes.length); + expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impressions[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()) - expect(impressions[1].ext.siteID).to.equal(bid2.params.siteId) - expect(impressions[2].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()) - expect(impressions[3].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()) + impression.banner.format.map(({ w, h, ext }, index) => { + const size = bid.mediaTypes.banner.sizes[index]; + const sidValue = utils.parseGPTSingleSizeArray(size); - expect(impressions[0].banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impressions[0].banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impressions[1].banner.w).to.equal(bid2.params.size[0]); - expect(impressions[1].banner.h).to.equal(bid2.params.size[1]); - expect(impressions[2].banner.w).to.equal(bid.mediaTypes.banner.sizes[2][0]); - expect(impressions[2].banner.h).to.equal(bid.mediaTypes.banner.sizes[2][1]); - expect(impressions[3].banner.w).to.equal(bid.mediaTypes.banner.sizes[3][0]); - expect(impressions[3].banner.h).to.equal(bid.mediaTypes.banner.sizes[3][1]); + expect(w).to.equal(size[0]); + expect(h).to.equal(size[1]); + expect(ext.siteID).to.equal(index === 1 ? bid2.params.siteId : bid.params.siteId); + expect(ext.sid).to.equal(sidValue); + }); }); it('request should contain the extra banner ad sizes and their corresponding site ids when there is multiple ad units', function () { @@ -1775,33 +1757,30 @@ describe('IndexexchangeAdapter', function () { bid.sizes = [[336, 280], [970, 90]] bid.mediaTypes.banner.sizes = [[336, 280], [970, 90]] - const request = spec.buildRequests([DEFAULT_BANNER_VALID_BID[0], bid], DEFAULT_OPTION)[0]; + const bids = [DEFAULT_BANNER_VALID_BID[0], bid]; + const request = spec.buildRequests(bids, DEFAULT_OPTION)[0]; const impressions = JSON.parse(request.data.r).imp; expect(impressions).to.be.an('array'); - expect(impressions).to.have.lengthOf(4); - expect(impressions[0].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impressions[1].ext.siteID).to.equal(bid.params.siteId); - expect(impressions[2].ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString()); - expect(impressions[3].ext.siteID).to.equal(bid.params.siteId); + expect(impressions).to.have.lengthOf(2); + expect(request.data.sn).to.be.undefined; - expect(impressions[0].banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impressions[0].banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); + impressions.map((impression, impressionIndex) => { + const firstSizeObject = bids[impressionIndex].mediaTypes.banner.sizes[0]; - expect(impressions[1].banner.w).to.equal(bid.params.size[0]); - expect(impressions[1].banner.h).to.equal(bid.params.size[1]); + expect(impression.banner.format).to.be.length(2); + expect(impression.banner.topframe).to.be.oneOf([0, 1]); - expect(impressions[2].banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][0]); - expect(impressions[2].banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][1]); + impression.banner.format.map(({ w, h, ext }, index) => { + const size = bids[impressionIndex].mediaTypes.banner.sizes[index]; + const sidValue = utils.parseGPTSingleSizeArray(size); - expect(impressions[3].banner.w).to.equal(bid.mediaTypes.banner.sizes[1][0]); - expect(impressions[3].banner.h).to.equal(bid.mediaTypes.banner.sizes[1][1]); - - expect(impressions[0].ext.sid).to.equal(`${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`); - expect(impressions[1].ext.sid).to.equal(`${bid.params.size[0].toString()}x${bid.params.size[1].toString()}`); - - expect(impressions[2].ext.sid).to.equal(`${DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][0].toString()}x${DEFAULT_BANNER_VALID_BID[0].mediaTypes.banner.sizes[1][1].toString()}`); - expect(impressions[3].ext.sid).to.equal(`${bid.mediaTypes.banner.sizes[1][0].toString()}x${bid.mediaTypes.banner.sizes[1][1].toString()}`); + expect(w).to.equal(size[0]); + expect(h).to.equal(size[1]); + expect(ext.siteID).to.equal(bids[impressionIndex].params.siteId.toString()); + expect(ext.sid).to.equal(sidValue); + }); + }); }); it('request should not contain the extra video ad sizes that IX is not configured for', function () { @@ -1823,14 +1802,14 @@ describe('IndexexchangeAdapter', function () { it('request should not contain missing sizes if detectMissingSizes = false', function () { const bid1 = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - bid1.mediaTypes.banner.sizes = div_many_sizes; + bid1.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES; const requests = spec.buildRequests([bid1, DEFAULT_BANNER_VALID_BID[0]], DEFAULT_OPTION); const impressions = JSON.parse(requests[0].data.r).imp; expect(impressions).to.be.an('array'); - expect(impressions).to.have.lengthOf(2); + expect(impressions).to.have.lengthOf(1); }); }); }); @@ -1850,24 +1829,18 @@ describe('IndexexchangeAdapter', function () { it('impression should have correct format and value', function () { const impression = JSON.parse(query.r).imp[0]; - const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; + const sidValue = utils.parseGPTSingleSizeArray(DEFAULT_VIDEO_VALID_BID[0].params.size); expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); - expect(impression.video).to.exist; expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); - expect(impression.video.placement).to.exist; expect(impression.video.placement).to.equal(1); expect(impression.video.minduration).to.exist; expect(impression.video.minduration).to.equal(0); - expect(impression.video.mimes).to.exist; expect(impression.video.mimes[0]).to.equal('video/mp4'); expect(impression.video.mimes[1]).to.equal('video/webm'); expect(impression.video.skippable).to.equal(false); - expect(impression.ext).to.exist; - expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); - expect(impression.ext.sid).to.equal(sidValue); // TODO undefined - 400x600 }); it('should not use default placement values when placement is defined at adUnit level', function () { @@ -1901,7 +1874,7 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.placement).to.equal(4); }); - it('should handle unexpected context', function() { + it('should handle unexpected context', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); bid.mediaTypes.video.context = 'VaccineJanssen'; const request = spec.buildRequests([bid])[0]; @@ -1949,16 +1922,14 @@ describe('IndexexchangeAdapter', function () { }); describe('buildRequestMultiFormat', function () { - describe('only banner bidder params set', function () { + it('only banner bidder params set', function () { const request = spec.buildRequests(DEFAULT_MULTIFORMAT_BANNER_VALID_BID) - - const bannerImp = JSON.parse(request[0].data.r).imp[0]; - expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(2); + const bannerImpression = JSON.parse(request[0].data.r).imp[0]; + expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(1); expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); - expect(bannerImp.id).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].bidId); - expect(bannerImp.banner).to.exist; - expect(bannerImp.banner.w).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[0]); - expect(bannerImp.banner.h).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[1]); + expect(bannerImpression.id).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].bidId); + expect(bannerImpression.banner.format[0].w).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[0]); + expect(bannerImpression.banner.format[0].h).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[1]); }); describe('only video bidder params set', function () { @@ -1968,10 +1939,10 @@ describe('IndexexchangeAdapter', function () { expect(JSON.parse(request[1].data.r).imp).to.have.lengthOf(1); expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); expect(videoImp.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); - expect(videoImp.video).to.exist; expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[0]); expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].params.size[1]); }); + it('should get missing sizes count 0 when params.size not used', function () { const bid = utils.deepClone(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]); delete bid.params.size; @@ -1981,25 +1952,44 @@ describe('IndexexchangeAdapter', function () { expect(diagObj.msi).to.equal(0); }); }); + describe('both banner and video bidder params set', function () { - const request = spec.buildRequests([DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0], DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]]); + const bids = [DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0], DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]]; + const request = spec.buildRequests(bids); - it('should return valid banner and video requests', function () { - const bannerImp = JSON.parse(request[0].data.r).imp[0]; - expect(JSON.parse(request[0].data.r).imp).to.have.lengthOf(4); + it('should return valid banner requests', function () { + const impressions = JSON.parse(request[0].data.r).imp; + + expect(impressions).to.have.lengthOf(2); expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); - expect(bannerImp.id).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].bidId); - expect(bannerImp.banner).to.exist; - expect(bannerImp.banner.w).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[0]); - expect(bannerImp.banner.h).to.equal(DEFAULT_MULTIFORMAT_BANNER_VALID_BID[0].params.size[1]); - const videoImp = JSON.parse(request[1].data.r).imp[0]; + impressions.map((impression, index) => { + const bid = bids[index]; + + expect(impression.id).to.equal(bid.bidId); + expect(impression.banner.format).to.be.length(bid.mediaTypes.banner.sizes.length); + expect(impression.banner.topframe).to.be.oneOf([0, 1]); + + impression.banner.format.map(({ w, h, ext }, index) => { + const size = bid.mediaTypes.banner.sizes[index]; + const sidValue = utils.parseGPTSingleSizeArray(size); + + expect(w).to.equal(size[0]); + expect(h).to.equal(size[1]); + expect(ext.siteID).to.equal(bid.params.siteId.toString()); + expect(ext.sid).to.equal(sidValue); + }); + }); + }); + + it('should return valid banner and video requests', function () { + const videoImpression = JSON.parse(request[1].data.r).imp[0]; + expect(JSON.parse(request[1].data.r).imp).to.have.lengthOf(1); expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); - expect(videoImp.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); - expect(videoImp.video).to.exist; - expect(videoImp.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[0]); - expect(videoImp.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[1]); + expect(videoImpression.id).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].bidId); + expect(videoImpression.video.w).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[0]); + expect(videoImpression.video.h).to.equal(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0].mediaTypes.video.playerSize[1]); }); it('should contain all correct IXdiag properties', function () { From 0657279e869e44b25ebf26ba46c3318293da13fb Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 28 Jun 2021 10:47:36 -0700 Subject: [PATCH 1210/1476] Prebid Analytics Manager: extend mocha testing timeout to fix flaky ie test (#7104) * Prebid Analytics Manager: Testing * remove extra params * revert * update sinon to check if sandbox is issue * revert sinon * update wdio-browserstack package * revert * add time to karma browser timeout * fix * try to adjust timeout in karma * revert * try extending time in mocha * make test timeout 4000ms --- test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index 2505af0c2a7..e504eced868 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -108,6 +108,7 @@ describe('Prebid Manager Analytics Adapter', function () { describe('build utm tag data', function () { let getDataFromLocalStorageStub; + this.timeout(4000) beforeEach(function () { getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); getDataFromLocalStorageStub.withArgs('pm_utm_source').returns('utm_source'); @@ -115,7 +116,6 @@ describe('Prebid Manager Analytics Adapter', function () { getDataFromLocalStorageStub.withArgs('pm_utm_campaign').returns('utm_camp'); getDataFromLocalStorageStub.withArgs('pm_utm_term').returns(''); getDataFromLocalStorageStub.withArgs('pm_utm_content').returns(''); - getDataFromLocalStorageStub.withArgs('pm_utm_source').returns('utm_source'); }); afterEach(function () { getDataFromLocalStorageStub.restore(); From 61332375e3960dcbd9124e00bdb2fe54bfb1e65f Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Mon, 28 Jun 2021 23:56:25 +0530 Subject: [PATCH 1211/1476] Send screen size instead of browser size (#7110) Co-authored-by: monis.q --- modules/medianetBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 1a4269a8a1d..3b7e555cc72 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -114,8 +114,8 @@ function getSize(size) { function getWindowSize() { return { - w: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || -1, - h: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || -1 + w: window.screen.width || -1, + h: window.screen.height || -1 } } From 9ffe6f10785d909bcd3514a0537892b5532fd4f7 Mon Sep 17 00:00:00 2001 From: Newton <5769156+iamnewton@users.noreply.github.com> Date: Mon, 28 Jun 2021 13:37:02 -0700 Subject: [PATCH 1212/1476] Prebid Core: Update renderAd to use hook #7091 --- src/prebid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index 7cdaa709d20..e65d2530943 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -398,7 +398,7 @@ function emitAdRenderFail({ reason, message, bid, id }) { * @param {string} id bid id to locate the ad * @alias module:pbjs.renderAd */ -$$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { +$$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); utils.logMessage('Calling renderAd with adId :' + id); @@ -488,7 +488,7 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { const message = `Error trying to write ad Id :${id} to the page. Missing document or adId`; emitAdRenderFail({ reason: MISSING_DOC_OR_ADID, message, id }); } -}; +}); /** * Remove adUnit from the $$PREBID_GLOBAL$$ configuration, if there are no addUnitCode(s) it will remove all From f0f970c2937ee0195e686d9f78b4788932c35c34 Mon Sep 17 00:00:00 2001 From: "Adserver.Online" <61009237+adserver-online@users.noreply.github.com> Date: Tue, 29 Jun 2021 11:01:00 +0300 Subject: [PATCH 1213/1476] Aso Bid Adapter: add new bid adapter (#7062) * Adserver.Online bidder adapter * Added Price Floors support * Added outstream renderer * Docs updated * coverage improved * Added config support to outstream renderer Co-authored-by: dev --- modules/asoBidAdapter.js | 349 ++++++++++++++++++++++++ modules/asoBidAdapter.md | 78 ++++++ test/spec/modules/asoBidAdapter_spec.js | 340 +++++++++++++++++++++++ 3 files changed, 767 insertions(+) create mode 100644 modules/asoBidAdapter.js create mode 100644 modules/asoBidAdapter.md create mode 100644 test/spec/modules/asoBidAdapter_spec.js diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js new file mode 100644 index 00000000000..5be4b982491 --- /dev/null +++ b/modules/asoBidAdapter.js @@ -0,0 +1,349 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {Renderer} from '../src/Renderer.js'; + +const BIDDER_CODE = 'aso'; +const DEFAULT_SERVER_URL = 'https://srv.aso1.net'; +const DEFAULT_SERVER_PATH = '/prebid/bidder'; +const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; +const TTL = 300; + +export const spec = { + + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: bid => { + return !!bid.params && !!bid.params.zone; + }, + + buildRequests: (validBidRequests, bidderRequest) => { + let serverRequests = []; + + utils._each(validBidRequests, bidRequest => { + const payload = createBasePayload(bidRequest, bidderRequest); + + const bannerParams = utils.deepAccess(bidRequest, 'mediaTypes.banner'); + const videoParams = utils.deepAccess(bidRequest, 'mediaTypes.video'); + + let imp; + + if (bannerParams && videoParams) { + utils.logWarn('Please note, multiple mediaTypes are not supported. The only banner will be used.') + } + + if (bannerParams) { + imp = createBannerImp(bidRequest, bannerParams) + } else if (videoParams) { + imp = createVideoImp(bidRequest, videoParams) + } + + if (imp) { + payload.imp.push(imp); + } else { + return; + } + + serverRequests.push({ + method: 'POST', + url: getEnpoint(bidRequest), + data: payload, + options: { + withCredentials: true, + crossOrigin: true + }, + bidRequest: bidRequest + }); + }); + + return serverRequests; + }, + + interpretResponse: (serverResponse, {bidRequest}) => { + const response = serverResponse && serverResponse.body; + + if (!response) { + return []; + } + + const serverBids = response.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []); + const serverBid = serverBids[0]; + + let bids = []; + + const bid = { + requestId: serverBid.impid, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + ttl: TTL, + creativeId: serverBid.crid, + netRevenue: true, + currency: response.cur, + mediaType: bidRequest.mediaType, + meta: { + mediaType: bidRequest.mediaType, + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + } + }; + + if (bid.mediaType === BANNER) { + bid.ad = serverBid.adm; + } else if (bid.mediaType === VIDEO) { + bid.vastXml = serverBid.adm; + if (utils.deepAccess(bidRequest, 'mediaTypes.video.context') === 'outstream') { + bid.adResponse = { + content: bid.vastXml, + }; + bid.renderer = createRenderer(bidRequest, OUTSTREAM_RENDERER_URL); + } + } + + bids.push(bid); + + return bids; + }, + + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + const urls = []; + + if (serverResponses && serverResponses.length !== 0) { + let query = ''; + if (gdprConsent) { + query = utils.tryAppendQueryString(query, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0)); + query = utils.tryAppendQueryString(query, 'consents_str', gdprConsent.consentString); + const consentsIds = getConsentsIds(gdprConsent); + if (consentsIds) { + query = utils.tryAppendQueryString(query, 'consents', consentsIds); + } + } + + if (uspConsent) { + query = utils.tryAppendQueryString(query, 'us_privacy', uspConsent); + } + + utils._each(serverResponses, resp => { + const userSyncs = utils.deepAccess(resp, 'body.ext.user_syncs'); + if (!userSyncs) { + return; + } + + utils._each(userSyncs, us => { + urls.push({ + type: us.type, + url: us.url + (query ? '?' + query : '') + }); + }); + }); + } + + return urls; + } +}; + +function outstreamRender(bid) { + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + sizes: [bid.width, bid.height], + targetId: bid.adUnitCode, + adResponse: bid.adResponse, + rendererOptions: bid.renderer.getConfig() + }); + }); +} + +function createRenderer(bid, url) { + const renderer = Renderer.install({ + id: bid.bidId, + url: url, + loaded: false, + config: utils.deepAccess(bid, 'renderer.options'), + adUnitCode: bid.adUnitCode + }); + renderer.setRender(outstreamRender); + return renderer; +} + +function getUrlsInfo(bidderRequest) { + let page = ''; + let referrer = ''; + + const {refererInfo} = bidderRequest; + + if (utils.inIframe()) { + page = refererInfo.referer; + } else { + const w = utils.getWindowTop(); + page = w.location.href; + referrer = w.document.referrer || ''; + } + + page = config.getConfig('pageUrl') || page; + const url = utils.parseUrl(page); + const domain = url.hostname; + + return { + domain, + page, + referrer + }; +} + +function getSize(paramSizes) { + const parsedSizes = utils.parseSizesInput(paramSizes); + const sizes = parsedSizes.map(size => { + const [width, height] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return {w, h}; + }); + + return sizes[0] || null; +} + +function getBidFloor(bidRequest, size) { + if (!utils.isFn(bidRequest.getFloor)) { + return null; + } + + const bidFloor = bidRequest.getFloor({ + mediaType: bidRequest.mediaType, + size: size ? [size.w, size.h] : '*' + }); + + if (!isNaN(bidFloor.floor)) { + return bidFloor; + } + + return null; +} + +function createBaseImp(bidRequest, size) { + const imp = { + id: bidRequest.bidId, + tagid: bidRequest.adUnitCode, + secure: 1 + }; + + const bidFloor = getBidFloor(bidRequest, size); + if (bidFloor !== null) { + imp.bidfloor = bidFloor.floor; + imp.bidfloorcur = bidFloor.currency; + } + + return imp; +} + +function createBannerImp(bidRequest, bannerParams) { + bidRequest.mediaType = BANNER; + + const size = getSize(bannerParams.sizes); + const imp = createBaseImp(bidRequest, size); + + imp.banner = { + w: size.w, + h: size.h, + topframe: utils.inIframe() ? 0 : 1 + } + + return imp; +} + +function createVideoImp(bidRequest, videoParams) { + bidRequest.mediaType = VIDEO; + const size = getSize(videoParams.playerSize); + const imp = createBaseImp(bidRequest, size); + + imp.video = { + mimes: videoParams.mimes, + minduration: videoParams.minduration, + startdelay: videoParams.startdelay, + linearity: videoParams.linearity, + maxduration: videoParams.maxduration, + skip: videoParams.skip, + protocols: videoParams.protocols, + skipmin: videoParams.skipmin, + api: videoParams.api + } + + if (size) { + imp.video.w = size.w; + imp.video.h = size.h; + } + + return imp; +} + +function getEnpoint(bidRequest) { + const serverUrl = bidRequest.params.serverUrl || DEFAULT_SERVER_URL; + const serverPath = bidRequest.params.serverPath || DEFAULT_SERVER_PATH; + + return serverUrl + serverPath + '?zid=' + bidRequest.params.zone + '&pbjs=$prebid.version$'; +} + +function getConsentsIds(gdprConsent) { + const consents = utils.deepAccess(gdprConsent, 'vendorData.purpose.consents', []); + let consentsIds = []; + for (const [key, value] of Object.entries(consents)) { + if (value === true) { + consentsIds.push(key); + } + } + return consentsIds.join(','); +} + +function createBasePayload(bidRequest, bidderRequest) { + const urlsInfo = getUrlsInfo(bidderRequest); + + const payload = { + id: bidRequest.auctionId + '_' + bidRequest.bidId, + at: 1, + tmax: bidderRequest.timeout, + site: { + id: urlsInfo.domain, + domain: urlsInfo.domain, + page: urlsInfo.page, + ref: urlsInfo.referrer + }, + device: { + dnt: utils.getDNT() ? 1 : 0, + h: window.innerHeight, + w: window.innerWidth, + }, + imp: [], + ext: {}, + user: {} + }; + + if (bidRequest.params.attr) { + utils.deepSetValue(payload, 'site.ext.attr', bidRequest.params.attr); + } + + if (bidderRequest.gdprConsent) { + utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + const consentsIds = getConsentsIds(bidderRequest.gdprConsent); + if (consentsIds) { + utils.deepSetValue(payload, 'user.ext.consents', consentsIds); + } + utils.deepSetValue(payload, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); + } + + if (bidderRequest.uspConsent) { + utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + if (config.getConfig('coppa')) { + utils.deepSetValue(payload, 'regs.coppa', 1); + } + + const eids = utils.deepAccess(bidRequest, 'userIdAsEids'); + if (eids && eids.length) { + utils.deepSetValue(payload, 'user.ext.eids', eids); + } + + return payload; +} + +registerBidder(spec); diff --git a/modules/asoBidAdapter.md b/modules/asoBidAdapter.md new file mode 100644 index 00000000000..32f4ebf5cef --- /dev/null +++ b/modules/asoBidAdapter.md @@ -0,0 +1,78 @@ +# Overview + +``` +Module Name: Adserver.Online Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@adsrv.org +``` + +# Description + +Adserver.Online Bidder Adapter for Prebid.js. + +For more information, please visit [Adserver.Online](https://adserver.online). + +# Parameters + +| Name | Scope | Description | Example | Type | +|---------------|----------|-------------------------|-----------|-----------| +| `zone` | required | Zone ID | `73815` | `Integer` | +| `attr` | optional | Custom targeting params | `{keywords: ["a", "b"]}` | `Object` | + + + +# Test parameters for banner +```js +var adUnits = [ + { + code: 'banner1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'aso', + params: { + zone: 73815 + } + } + ] + } +]; +``` + +# Test parameters for video +```js +var videoAdUnit = [ + { + code: 'video1', + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' // or 'outstream' + } + }, + bids: [{ + bidder: 'aso', + params: { + zone: 34668 + } + }] + } +]; +``` + +# Configuration + +The Adserver.Online Bid Adapter expects Prebid Cache (for video) to be enabled. + +``` +pbjs.setConfig({ + usePrebidCache: true, + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } +}); +``` diff --git a/test/spec/modules/asoBidAdapter_spec.js b/test/spec/modules/asoBidAdapter_spec.js new file mode 100644 index 00000000000..0dc779a300d --- /dev/null +++ b/test/spec/modules/asoBidAdapter_spec.js @@ -0,0 +1,340 @@ +import {expect} from 'chai'; +import {spec} from 'modules/asoBidAdapter.js'; +import {parseUrl} from 'src/utils.js'; +import {BANNER, VIDEO} from 'src/mediaTypes.js'; + +describe('Adserver.Online bidding adapter', function () { + const bannerRequest = { + bidder: 'aso', + params: { + zone: 1, + attr: { + keywords: ['a', 'b'], + tags: ['t1', 't2'] + } + }, + adUnitCode: 'adunit-banner', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [240, 400], + ] + } + }, + bidId: 'bidid1', + bidderRequestId: 'bidreq1', + auctionId: 'auctionid1', + userIdAsEids: [{ + source: 'src1', + uids: [ + { + id: 'id123...' + } + ] + }] + }; + + const videoRequest = { + bidder: 'aso', + params: { + zone: 2, + video: { + api: [2], + maxduration: 30 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + protocols: [1, 2], + mimes: ['video/mp4'], + } + }, + adUnitCode: 'adunit-video', + bidId: 'bidid12', + bidderRequestId: 'bidreq2', + auctionId: 'auctionid12' + }; + + const bidderRequest = { + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'https://example.com' + } + }; + + const gdprConsent = { + gdprApplies: true, + consentString: 'consentString', + vendorData: { + purpose: { + consents: { + 1: true, + 2: true, + 3: false + } + } + } + }; + + const uspConsent = 'usp_consent'; + + describe('isBidRequestValid', function () { + it('should return true when required params found in bidVideo', function () { + expect(spec.isBidRequestValid(videoRequest)).to.be.true + }); + + it('should return true when required params found in bidBanner', function () { + expect(spec.isBidRequestValid(bannerRequest)).to.be.true + }); + + it('should return false when required params not found', function () { + expect(spec.isBidRequestValid({})).to.be.false; + }); + + it('should return false when required params are not passed', function () { + const bid = Object.assign({}, bannerRequest); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.be.false + }); + + it('should return false when required zone param not found', function () { + const bid = JSON.parse(JSON.stringify(videoRequest)); + delete bid.params.zone; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + it('creates a valid banner request', function () { + bannerRequest.getFloor = () => ({ currency: 'USD', floor: 0.5 }); + + const requests = spec.buildRequests([bannerRequest], bidderRequest); + expect(requests).to.have.lengthOf(1); + const request = requests[0]; + + expect(request).to.exist; + expect(request.method).to.equal('POST'); + const parsedRequestUrl = parseUrl(request.url); + expect(parsedRequestUrl.hostname).to.equal('srv.aso1.net'); + expect(parsedRequestUrl.pathname).to.equal('/prebid/bidder'); + + const query = parsedRequestUrl.search; + expect(query.pbjs).to.equal('$prebid.version$'); + expect(query.zid).to.equal('1'); + + expect(request.data).to.exist; + + const payload = request.data; + + expect(payload.site).to.not.equal(null); + expect(payload.site.ref).to.equal(''); + expect(payload.site.page).to.equal('https://example.com'); + + expect(payload.device).to.not.equal(null); + expect(payload.device.w).to.equal(window.innerWidth); + expect(payload.device.h).to.equal(window.innerHeight); + + expect(payload.imp).to.have.lengthOf(1); + + expect(payload.imp[0].tagid).to.equal('adunit-banner'); + expect(payload.imp[0].banner).to.not.equal(null); + expect(payload.imp[0].banner.w).to.equal(300); + expect(payload.imp[0].banner.h).to.equal(250); + expect(payload.imp[0].bidfloor).to.equal(0.5); + expect(payload.imp[0].bidfloorcur).to.equal('USD'); + }); + + it('creates a valid video request', function () { + const requests = spec.buildRequests([videoRequest], bidderRequest); + expect(requests).to.have.lengthOf(1); + const request = requests[0]; + + expect(request).to.exist; + expect(request.method).to.equal('POST'); + const parsedRequestUrl = parseUrl(request.url); + expect(parsedRequestUrl.hostname).to.equal('srv.aso1.net'); + expect(parsedRequestUrl.pathname).to.equal('/prebid/bidder'); + + const query = parsedRequestUrl.search; + expect(query.pbjs).to.equal('$prebid.version$'); + expect(query.zid).to.equal('2'); + + expect(request.data).to.not.be.empty; + + const payload = request.data; + + expect(payload.site).to.not.equal(null); + expect(payload.site.ref).to.equal(''); + expect(payload.site.page).to.equal('https://example.com'); + + expect(payload.device).to.not.equal(null); + expect(payload.device.w).to.equal(window.innerWidth); + expect(payload.device.h).to.equal(window.innerHeight); + + expect(payload.imp).to.have.lengthOf(1); + + expect(payload.imp[0].tagid).to.equal('adunit-video'); + expect(payload.imp[0].video).to.not.equal(null); + expect(payload.imp[0].video.w).to.equal(640); + expect(payload.imp[0].video.h).to.equal(480); + expect(payload.imp[0].banner).to.be.undefined; + }); + }); + + describe('GDPR/USP compliance', function () { + it('should send GDPR/USP consent data if it applies', function () { + bidderRequest.gdprConsent = gdprConsent; + bidderRequest.uspConsent = uspConsent; + + const requests = spec.buildRequests([bannerRequest], bidderRequest); + expect(requests).to.have.lengthOf(1); + const request = requests[0]; + + expect(request.data).to.not.be.empty; + + const payload = request.data; + + expect(payload.user.ext.consent).to.equal('consentString'); + expect(payload.regs.ext.us_privacy).to.equal(uspConsent); + expect(payload.regs.ext.gdpr).to.equal(1); + }); + + it('should not send GDPR/USP consent data if it does not apply', function () { + bidderRequest.gdprConsent = null; + bidderRequest.uspConsent = null; + + const requests = spec.buildRequests([bannerRequest], bidderRequest); + expect(requests).to.have.lengthOf(1); + const request = requests[0]; + + expect(request.data).to.not.be.empty; + + const payload = request.data; + + expect(payload).to.not.have.nested.property('regs.ext.gdpr'); + expect(payload).to.not.have.nested.property('user.ext.consent'); + expect(payload).to.not.have.nested.property('regs.ext.us_privacy'); + }); + }); + + describe('response handler', function () { + const bannerResponse = { + body: { + id: 'auctionid1', + bidid: 'bidid1', + seatbid: [{ + bid: [ + { + impid: 'impid1', + price: 0.3, + crid: 321, + adm: '', + w: 300, + h: 250, + adomain: ['example.com'], + } + ] + }], + cur: 'USD', + ext: { + user_syncs: [ + { + url: 'sync_url', + type: 'iframe' + } + ] + } + }, + }; + + const videoResponse = { + body: { + id: 'auctionid2', + bidid: 'bidid2', + seatbid: [{ + bid: [ + { + impid: 'impid2', + price: 0.5, + crid: 123, + adm: '', + adomain: ['example.com'], + w: 640, + h: 480, + } + ] + }], + cur: 'USD' + }, + }; + + it('handles banner responses', function () { + bannerRequest.bidRequest = { + mediaType: BANNER + }; + const result = spec.interpretResponse(bannerResponse, bannerRequest); + + expect(result).to.have.lengthOf(1); + + expect(result[0]).to.exist; + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].mediaType).to.equal(BANNER); + expect(result[0].creativeId).to.equal(321); + expect(result[0].cpm).to.be.within(0.1, 0.5); + expect(result[0].ad).to.equal(''); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); + expect(result[0].dealId).to.not.exist; + expect(result[0].meta.advertiserDomains[0]).to.equal('example.com'); + }); + + it('handles video responses', function () { + const request = { + bidRequest: videoRequest + }; + request.bidRequest.mediaType = VIDEO; + + const result = spec.interpretResponse(videoResponse, request); + expect(result).to.have.lengthOf(1); + + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(480); + expect(result[0].mediaType).to.equal(VIDEO); + expect(result[0].creativeId).to.equal(123); + expect(result[0].cpm).to.equal(0.5); + expect(result[0].vastXml).to.equal(''); + expect(result[0].renderer).to.be.a('object'); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); + }); + + it('handles empty responses', function () { + const response = []; + const bidderRequest = {}; + + const result = spec.interpretResponse(response, bidderRequest); + expect(result.length).to.equal(0); + }); + + describe('getUserSyncs', function () { + const syncOptions = { + iframeEnabled: true + }; + + it('should return iframe sync option', function () { + expect(spec.getUserSyncs(syncOptions, [bannerResponse], gdprConsent, uspConsent)[0].type).to.equal('iframe'); + expect(spec.getUserSyncs(syncOptions, [bannerResponse], gdprConsent, uspConsent)[0].url).to.equal( + 'sync_url?gdpr=1&consents_str=consentString&consents=1%2C2&us_privacy=usp_consent&' + ); + }); + }); + }); +}); From 4136adb964797bc1bd25c38e4c7a0deffaaa75db Mon Sep 17 00:00:00 2001 From: Lisa Benmore Date: Tue, 29 Jun 2021 02:12:41 -0700 Subject: [PATCH 1214/1476] Gumgum: ADTS-134 Fetch IDL envelope and pass to ad server if available (#7095) --- modules/gumgumBidAdapter.js | 37 ++++++++++++++++------ test/spec/modules/gumgumBidAdapter_spec.js | 14 ++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index fdcbbd8a0f3..9b4ec41049c 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -83,14 +83,6 @@ function getWrapperCode(wrapper, data) { return wrapper.replace('AD_JSON', window.btoa(JSON.stringify(data))) } -function _getTradeDeskIDParam(userId) { - const unifiedIdObj = {}; - if (userId.tdid) { - unifiedIdObj.tdid = userId.tdid; - } - return unifiedIdObj; -} - function _getDigiTrustQueryParams(userId) { let digiTrustId = userId.digitrustid && userId.digitrustid.data; // Verify there is an ID and this user has not opted out @@ -230,6 +222,29 @@ function _getFloor (mediaTypes, staticBidFloor, bid) { return bidFloor; } +function getEids (userId) { + const idProperties = [ + 'uid', + 'eid', + 'lipbid' + ]; + + return Object.keys(userId).reduce(function (eids, provider) { + const eid = userId[provider]; + switch (typeof eid) { + case 'string': + eids[provider] = eid; + break; + + case 'object': + const idProp = idProperties.filter(prop => eid.hasOwnProperty(prop)); + idProp.length && (eids[provider] = eid[idProp[0]]); + break; + } + return eids; + }, {}); +} + /** * Make a server request from the list of BidRequests. * @@ -253,10 +268,14 @@ function buildRequests (validBidRequests, bidderRequest) { ortb2Imp } = bidRequest; const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest); + const eids = getEids(userId); let sizes = [1, 1]; let data = {}; let gpid = ''; + // ADTS-134 Retrieve ID envelopes + for (const eid in eids) data[eid] = eids[eid]; + // ADJS-1024 if (utils.deepAccess(ortb2Imp, 'ext.data.adserver.name')) { gpid = ortb2Imp.ext.data.adserver.adslot @@ -328,7 +347,7 @@ function buildRequests (validBidRequests, bidderRequest) { url: BID_ENDPOINT, method: 'GET', gpid: gpid, - data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId), _getTradeDeskIDParam(userId)) + data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId)) }) }); return bids; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 19d3309e3ee..75a9c5c975a 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -447,6 +447,20 @@ describe('gumgumAdapter', function () { const request = spec.buildRequests(bidRequests)[0]; expect(request.data).to.not.include.any.keys('tdid'); }); + it('should send IDL envelope ID if available', function () { + const idl_env = 'abc123'; + const request = { ...bidRequests[0], userId: { idl_env } }; + const bidRequest = spec.buildRequests([request])[0]; + + expect(bidRequest.data).to.have.property('idl_env'); + expect(bidRequest.data.idl_env).to.equal(idl_env); + }); + it('should not send IDL envelope if not available', function () { + const request = { ...bidRequests[0] }; + const bidRequest = spec.buildRequests([request])[0]; + + expect(bidRequest.data).to.not.have.property('idl_env'); + }); it('should send schain parameter in serialized form', function () { const serializedForm = '1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com!exchange2.com,abcd,1,bid-request-2,intermediary,intermediary.com' const request = spec.buildRequests(bidRequests)[0]; From 4f3dda050d94b48d878106b074d4995eee2b49d7 Mon Sep 17 00:00:00 2001 From: "Adserver.Online" <61009237+adserver-online@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:13:55 +0300 Subject: [PATCH 1215/1476] Object.entries replaced with backward compatible version (#7122) Co-authored-by: dev --- modules/asoBidAdapter.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js index 5be4b982491..8f06b8ed856 100644 --- a/modules/asoBidAdapter.js +++ b/modules/asoBidAdapter.js @@ -286,11 +286,13 @@ function getEnpoint(bidRequest) { function getConsentsIds(gdprConsent) { const consents = utils.deepAccess(gdprConsent, 'vendorData.purpose.consents', []); let consentsIds = []; - for (const [key, value] of Object.entries(consents)) { - if (value === true) { + + Object.keys(consents).forEach(function (key) { + if (consents[key] === true) { consentsIds.push(key); } - } + }); + return consentsIds.join(','); } From 0dc4d6521f46d6cec4b9e8fff1d49c85675e75fc Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 29 Jun 2021 14:59:17 +0200 Subject: [PATCH 1216/1476] Impactify Bid Adapter: Add meta.advertiserDomains support (#7113) * Update for Prebid 5.X * Update to Prebid 5.X --- modules/impactifyBidAdapter.js | 266 ++++++++++++ test/spec/modules/impactifyBidAdapter_spec.js | 399 ++++++++++++++++++ 2 files changed, 665 insertions(+) create mode 100644 modules/impactifyBidAdapter.js create mode 100644 test/spec/modules/impactifyBidAdapter_spec.js diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js new file mode 100644 index 00000000000..7e52669d33e --- /dev/null +++ b/modules/impactifyBidAdapter.js @@ -0,0 +1,266 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import {ajax} from '../src/ajax.js'; + +const BIDDER_CODE = 'impactify'; +const BIDDER_ALIAS = ['imp']; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_VIDEO_WIDTH = 640; +const DEFAULT_VIDEO_HEIGHT = 480; +const ORIGIN = 'https://sonic.impactify.media'; +const LOGGER_URI = 'https://logger.impactify.media'; +const AUCTIONURI = '/bidder'; +const COOKIESYNCURI = '/static/cookie_sync.html'; +const GVLID = 606; +const GETCONFIG = config.getConfig; + +const getDeviceType = () => { + // OpenRTB Device type + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { + return 5; + } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { + return 4; + } + return 2; +} + +const createOpenRtbRequest = (validBidRequests, bidderRequest) => { + // Create request and set imp bids inside + let request = { + id: bidderRequest.auctionId, + validBidRequests, + cur: [DEFAULT_CURRENCY], + imp: [] + }; + + // Force impactify debugging parameter + if (window.localStorage.getItem('_im_db_bidder') == 3) { + request.test = 3; + } + + // Set device/user/site + if (!request.device) request.device = {}; + if (!request.site) request.site = {}; + request.device = { + w: window.innerWidth, + h: window.innerHeight, + devicetype: getDeviceType(), + ua: navigator.userAgent, + js: 1, + dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, + language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', + }; + request.site = {page: bidderRequest.refererInfo.referer}; + + // Handle privacy settings for GDPR/CCPA/COPPA + let gdprApplies = 0; + if (bidderRequest.gdprConsent) { + if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + utils.deepSetValue(request, 'regs.ext.gdpr', gdprApplies); + + if (bidderRequest.uspConsent) { + utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); + this.syncStore.uspConsent = bidderRequest.uspConsent; + } + + if (GETCONFIG('coppa') == true) utils.deepSetValue(request, 'regs.coppa', 1); + + if (bidderRequest.uspConsent) { + utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + // Set buyer uid + utils.deepSetValue(request, 'user.buyeruid', utils.generateUUID()); + + // Create imps with bids + validBidRequests.forEach((bid) => { + let imp = { + id: bid.bidId, + bidfloor: bid.params.bidfloor ? bid.params.bidfloor : 0, + ext: { + impactify: { + appId: bid.params.appId, + format: bid.params.format, + style: bid.params.style + }, + }, + video: { + playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT], + context: 'outstream', + mimes: ['video/mp4'], + }, + }; + if (bid.params.container) { + imp.ext.impactify.container = bid.params.container; + } + request.imp.push(imp); + }); + + return request; +}; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: ['video'], + aliases: BIDDER_ALIAS, + + /** + * 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.appId || typeof bid.params.appId != 'string' || !bid.params.format || typeof bid.params.format != 'string' || !bid.params.style || typeof bid.params.style != 'string') { + return false; + } + if (bid.params.format != 'screen' && bid.params.format != 'display') { + return false; + } + if (bid.params.style != 'inline' && bid.params.style != 'impact' && bid.params.style != 'static') { + return false; + } + + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @param {bidderRequest} - the bidding request + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + // Create a clean openRTB request + let request = createOpenRtbRequest(validBidRequests, bidderRequest); + + return { + method: 'POST', + url: ORIGIN + AUCTIONURI, + data: JSON.stringify(request), + }; + }, + + /** + * 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; + let bidResponses = []; + + if (!serverBody) { + return bidResponses; + } + + if (!serverBody.seatbid || !serverBody.seatbid.length) { + return []; + } + + serverBody.seatbid.forEach((seatbid) => { + if (seatbid.bid.length) { + bidResponses = [ + ...bidResponses, + ...seatbid.bid + .filter((bid) => bid.price > 0) + .map((bid) => ({ + id: bid.id, + requestId: bid.impid, + cpm: bid.price, + currency: serverBody.cur, + netRevenue: true, + ad: bid.adm, + width: bid.w || 0, + height: bid.h || 0, + ttl: 300, + creativeId: bid.crid || 0, + hash: bid.hash, + expiry: bid.expiry, + meta: { + advertiserDomains: bid.adomain && bid.adomain.length ? bid.adomain : [] + } + })), + ]; + } + }); + + 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 (!serverResponses || serverResponses.length === 0) { + return []; + } + + if (!syncOptions.iframeEnabled) { + return []; + } + + let params = ''; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + params += `?gdpr_consent=${gdprConsent.consentString}`; + } + } + + if (uspConsent) { + params += `${params ? '&' : '?'}us_privacy=${encodeURIComponent(uspConsent)}`; + } + + if (document.location.search.match(/pbs_debug=true/)) params += `&pbs_debug=true`; + + return [{ + type: 'iframe', + url: ORIGIN + COOKIESYNCURI + params + }]; + }, + + /** + * 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) { + ajax(`${LOGGER_URI}/log/bidder/won`, null, JSON.stringify(bid), { + method: 'POST', + contentType: 'application/json' + }); + + return true; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ + onTimeout: function(data) { + ajax(`${LOGGER_URI}/log/bidder/timeout`, null, JSON.stringify(data[0]), { + method: 'POST', + contentType: 'application/json' + }); + + return true; + } +}; +registerBidder(spec); diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js new file mode 100644 index 00000000000..8bc02d9afce --- /dev/null +++ b/test/spec/modules/impactifyBidAdapter_spec.js @@ -0,0 +1,399 @@ +import { expect } from 'chai'; +import { spec } from 'modules/impactifyBidAdapter.js'; +import * as utils from 'src/utils.js'; + +const BIDDER_CODE = 'impactify'; +const BIDDER_ALIAS = ['imp']; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_VIDEO_WIDTH = 640; +const DEFAULT_VIDEO_HEIGHT = 480; +const ORIGIN = 'https://sonic.impactify.media'; +const LOGGER_URI = 'https://logger.impactify.media'; +const AUCTIONURI = '/bidder'; +const COOKIESYNCURI = '/static/cookie_sync.html'; +const GVLID = 606; + +var gdprData = { + 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'gdprApplies': true +}; + +describe('ImpactifyAdapter', function () { + describe('isBidRequestValid', function () { + let validBid = { + bidder: 'impactify', + params: { + appId: '1', + format: 'screen', + style: 'inline' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, validBid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when appId is missing', () => { + const bid = utils.deepClone(validBid); + delete bid.params.appId; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when appId is not a string', () => { + const bid = utils.deepClone(validBid); + + bid.params.appId = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.appId = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.appId = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.appId = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when format is missing', () => { + const bid = utils.deepClone(validBid); + delete bid.params.format; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when format is not a string', () => { + const bid = utils.deepClone(validBid); + + bid.params.format = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.format = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.format = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.format = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when format is not equals to screen or display', () => { + const bid = utils.deepClone(validBid); + if (bid.params.format != 'screen' && bid.params.format != 'display') { + expect(spec.isBidRequestValid(bid)).to.equal(false); + } + }); + + it('should return false when style is missing', () => { + const bid = utils.deepClone(validBid); + delete bid.params.style; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when style is not a string', () => { + const bid = utils.deepClone(validBid); + + bid.params.style = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.style = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.style = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.style = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + describe('buildRequests', function () { + let videoBidRequests = [ + { + bidder: 'impactify', + params: { + appId: '1', + format: 'screen', + style: 'inline' + }, + mediaTypes: { + video: { + context: 'instream' + } + }, + adUnitCode: 'adunit-code', + sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], + bidId: '123456789', + bidderRequestId: '987654321', + auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' + } + ]; + let videoBidderRequest = { + bidderRequestId: '98845765110', + auctionId: '165410516454', + bidderCode: 'impactify', + bids: [ + { + ...videoBidRequests[0] + } + ], + refererInfo: { + referer: 'https://impactify.io' + } + }; + + it('sends video bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(videoBidRequests, videoBidderRequest); + expect(request.url).to.equal(ORIGIN + AUCTIONURI); + expect(request.method).to.equal('POST'); + }); + }); + describe('interpretResponse', function () { + it('should get correct bid response', function () { + let response = { + id: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + seatbid: [ + { + bid: [ + { + id: '65820304700829014', + impid: '462c08f20d428', + price: 3.40, + adm: '', + adid: '97517771', + iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', + cid: '9325', + crid: '97517771', + w: 1, + h: 1, + hash: 'test', + expiry: 166192938, + meta: {'advertiserDomains': ['testdomain.com']}, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + prebid: { + type: 'video', + video: { + duration: 30, + primary_category: '' + } + }, + bidder: { + appnexus: { + brand_id: 182979, + auction_id: 8657683934873599656, + bidder_id: 2, + bid_ad_type: 1, + creative_info: { + video: { + duration: 30, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'impactify' + } + ], + cur: DEFAULT_CURRENCY, + ext: { + responsetimemillis: { + impactify: 114 + }, + prebid: { + auctiontimestamp: 1614587024591 + } + } + }; + let bidderRequest = { + bids: [ + { + bidId: '462c08f20d428', + adUnitCode: '/19968336/header-bid-tag-1', + auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + bidder: 'impactify', + sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + ] + } + let expectedResponse = [ + { + id: '65820304700829014', + requestId: '462c08f20d428', + cpm: 3.40, + currency: DEFAULT_CURRENCY, + netRevenue: true, + ad: '', + width: 1, + height: 1, + hash: 'test', + expiry: 166192938, + meta: {'advertiserDomains': ['testdomain.com']}, + ttl: 300, + creativeId: '97517771' + } + ]; + let result = spec.interpretResponse({ body: response }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + }); + describe('getUserSyncs', function () { + let videoBidRequests = [ + { + bidder: 'impactify', + params: { + appId: '1', + format: 'screen', + style: 'inline' + }, + mediaTypes: { + video: { + context: 'instream' + } + }, + adUnitCode: 'adunit-code', + sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], + bidId: '123456789', + bidderRequestId: '987654321', + auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' + } + ]; + let videoBidderRequest = { + bidderRequestId: '98845765110', + auctionId: '165410516454', + bidderCode: 'impactify', + bids: [ + { + ...videoBidRequests[0] + } + ], + refererInfo: { + referer: 'https://impactify.io' + } + }; + let validResponse = { + id: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + seatbid: [ + { + bid: [ + { + id: '65820304700829014', + impid: '462c08f20d428', + price: 3.40, + adm: '', + adid: '97517771', + iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', + cid: '9325', + crid: '97517771', + w: 1, + h: 1, + hash: 'test', + expiry: 166192938, + meta: {'advertiserDomains': ['testdomain.com']}, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + prebid: { + type: 'video', + video: { + duration: 30, + primary_category: '' + } + }, + bidder: { + appnexus: { + brand_id: 182979, + auction_id: 8657683934873599656, + bidder_id: 2, + bid_ad_type: 1, + creative_info: { + video: { + duration: 30, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'impactify' + } + ], + cur: DEFAULT_CURRENCY, + ext: { + responsetimemillis: { + impactify: 114 + }, + prebid: { + auctiontimestamp: 1614587024591 + } + } + }; + it('should return empty response if server response is false', function () { + const result = spec.getUserSyncs('bad', false, gdprData); + expect(result).to.be.empty; + }); + it('should return empty response if server response is empty', function () { + const result = spec.getUserSyncs('bad', [], gdprData); + expect(result).to.be.empty; + }); + it('should append the various values if they exist', function() { + const result = spec.getUserSyncs({iframeEnabled: true}, validResponse, gdprData); + expect(result[0].url).to.include('gdpr=1'); + expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); + }); + }); + + describe('On winning bid', function () { + const bid = { + ad: '', + cpm: '2' + }; + const result = spec.onBidWon(bid); + assert.ok(result); + }); + + describe('On bid Time out', function () { + const bid = { + ad: '', + cpm: '2' + }; + const result = spec.onTimeout(bid); + assert.ok(result); + }); +}) From 28d33163af1d81b12b2dff6a4ffcd5114506fbe0 Mon Sep 17 00:00:00 2001 From: vrtcal-dev <50931150+vrtcal-dev@users.noreply.github.com> Date: Tue, 29 Jun 2021 08:48:19 -0500 Subject: [PATCH 1217/1476] Vrtcal Bid Adapter: add adomain and make 5.0 compliant (#7106) * Added 5.0 compat bid adapter /w adomain support * Unit test file reinstated * fix lint errors * lint error Co-authored-by: Chris Huie --- modules/vrtcalBidAdapter.js | 94 ++++++++++++++++++++++ test/spec/modules/vrtcalBidAdapter_spec.js | 81 +++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 modules/vrtcalBidAdapter.js create mode 100644 test/spec/modules/vrtcalBidAdapter_spec.js diff --git a/modules/vrtcalBidAdapter.js b/modules/vrtcalBidAdapter.js new file mode 100644 index 00000000000..d23a05e23ca --- /dev/null +++ b/modules/vrtcalBidAdapter.js @@ -0,0 +1,94 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import {ajax} from '../src/ajax.js'; + +export const spec = { + code: 'vrtcal', + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + if (bid.bidId == '' || bid.auctionId == '') { return false; } else { return true; }// No extras params required + }, + buildRequests: function (bidRequests) { + const requests = bidRequests.map(function (bid) { + const params = { + + prebidJS: 1, + prebidAdUnitCode: bid.adUnitCode, + id: bid.bidId, + imp: [{ + id: '1', + banner: { + }, + bidfloor: 0.75 + }], + site: { + id: 'VRTCAL_FILLED', + name: 'VRTCAL_FILLED', + cat: ['VRTCAL_FILLED'], + domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0] + + }, + device: { + ua: 'VRTCAL_FILLED', + ip: 'VRTCAL_FILLED' + } + }; + + if (typeof (bid.mediaTypes) !== 'undefined' && typeof (bid.mediaTypes.banner) !== 'undefined' && typeof (bid.mediaTypes.banner.sizes) !== 'undefined') { + params.imp[0].banner.w = bid.mediaTypes.banner.sizes[0][0]; + params.imp[0].banner.h = bid.mediaTypes.banner.sizes[0][1]; + } else { + params.imp[0].banner.w = bid.sizes[0][0]; + params.imp[0].banner.h = bid.sizes[0][1]; + } + + return {method: 'POST', url: 'https://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804', data: JSON.stringify(params), options: {withCredentials: false, crossOrigin: true}} + }); + + return requests; + }, + interpretResponse: function (serverResponse, bidRequest) { + if (!serverResponse || !serverResponse.body) { + return []; + } + + const bidResponses = []; + + var response = serverResponse.body; + + if (response) { + const bidResponse = { + requestId: response.id, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + creativeId: response.seatbid[0].bid[0].crid, + currency: 'USD', + netRevenue: true, + ttl: 900, + ad: response.seatbid[0].bid[0].adm, + nurl: response.seatbid[0].bid[0].nurl + }; + + if (response.seatbid[0].bid[0].adomain && response.seatbid[0].bid[0].adomain.length) { + bidResponse.meta = { + advertiserDomains: response.seatbid[0].bid[0].adomain + }; + } + + bidResponses.push(bidResponse); + } + return bidResponses; + }, + onBidWon: function(bid) { + if (!bid.nurl) { return false; } + const winUrl = bid.nurl.replace( + /\$\{AUCTION_PRICE\}/, + bid.cpm + ); + ajax(winUrl, null); + return true; + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js new file mode 100644 index 00000000000..a5f2d475a29 --- /dev/null +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -0,0 +1,81 @@ +import { expect } from 'chai' +import { spec } from 'modules/vrtcalBidAdapter' +import { newBidder } from 'src/adapters/bidderFactory' + +describe('vrtcalBidAdapter', function () { + const adapter = newBidder(spec) + + let bidRequest = { + bidId: 'bidID0001', + transactionId: 'transID0001', + sizes: [[ 300, 250 ]] + } + + describe('isBidRequestValid', function () { + it('should return true 100% of time as no special additional params are required, thus no additional validation needed', function () { + expect(spec.isBidRequestValid(bidRequest)).to.be.true + }) + }) + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'vrtcal', + 'adUnitCode': 'adunit0001', + 'sizes': [[300, 250]], + 'bidId': 'bidID0001', + 'bidderRequestId': 'br0001', + 'auctionId': 'auction0001', + } + ]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + }); + + it('adUnitCode should be sent as prebidAdUnitCode parameters on any requests', function () { + expect(request[0].data).to.match(/"prebidAdUnitCode":"adunit0001"/); + }); + }); + + describe('interpretResponse', function () { + it('should form compliant bid object response', function () { + let res = { + body: { + id: 'bidID0001', + seatbid: [{ + bid: [{ + id: 'VRTB_240d3c8a3c12b68_1', + impid: '1', + price: 0.7554, + adm: 'TEST AD', + nurl: 'https://adplatform.vrtcal.com/wintracker', + w: 300, + h: 250, + crid: 'v2_1064_vrt_vrtcaltestdisplay2_300_250', + adomain: ['vrtcal.com'] + }], + seat: '16' + }], + cur: 'USD' + } + } + + let ir = spec.interpretResponse(res, bidRequest) + + expect(ir.length).to.equal(1) + + let en = ir[0] + + expect(en.requestId != null && + en.cpm != null && typeof en.cpm === 'number' && + en.width != null && typeof en.width === 'number' && + en.height != null && typeof en.height === 'number' && + en.ad != null && + en.creativeId != null + ).to.be.true + }) + }) +}) From de9d3db97d9bb8b3fe61a2f3b7d83a22e8fe885a Mon Sep 17 00:00:00 2001 From: mwehr-zeta <70167335+mwehr-zeta@users.noreply.github.com> Date: Tue, 29 Jun 2021 09:54:11 -0400 Subject: [PATCH 1218/1476] Zeta Bid Adapter: update Cookie Sync URL (#7090) * update cookie sync url * refactor conditional * use single-quotes to make lint happy --- modules/zetaBidAdapter.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index a6cbc831604..c6ce7e857ba 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -150,20 +150,24 @@ export const spec = { }, /** - * 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. - * @param gdprConsent The GDPR consent parameters - * @param uspConsent The USP consent parameters - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + * 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. + * @param definerId The calling entity's definer id + * @param gdprConsent The GDPR consent parameters + * @param uspConsent The USP consent parameters + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, definerId, gdprConsent, uspConsent) { const syncs = []; + if (definerId === '' || definerId === null) { + definerId = PREBID_DEFINER_ID; + } if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: USER_SYNC_URL.concat(PREBID_DEFINER_ID) + url: USER_SYNC_URL.concat(definerId) }); } return syncs; From b17b40f0afe6183b642c1ba06544e0e8fcfea2ff Mon Sep 17 00:00:00 2001 From: cpuBird <54024689+cpuBird@users.noreply.github.com> Date: Tue, 29 Jun 2021 19:34:11 +0530 Subject: [PATCH 1219/1476] support for response meta.advertiserDomains (v5.0) (#7119) --- modules/vdoaiBidAdapter.js | 131 +++++++++++++++++++ test/spec/modules/vdoaiBidAdapter_spec.js | 146 ++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 modules/vdoaiBidAdapter.js create mode 100644 test/spec/modules/vdoaiBidAdapter_spec.js diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js new file mode 100644 index 00000000000..4bf5a27c002 --- /dev/null +++ b/modules/vdoaiBidAdapter.js @@ -0,0 +1,131 @@ +import * as utils 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 = 'vdoai'; +const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; + +export const spec = { + code: BIDDER_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.placementId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @return Array Info describing the request to the server. + * @param validBidRequests + * @param bidderRequest + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (validBidRequests.length === 0) { + return []; + } + + return validBidRequests.map(bidRequest => { + const sizes = utils.getAdUnitSizes(bidRequest); + const payload = { + placementId: bidRequest.params.placementId, + sizes: sizes, + bidId: bidRequest.bidId, + referer: bidderRequest.refererInfo.referer, + id: bidRequest.auctionId, + mediaType: bidRequest.mediaTypes.video ? 'video' : 'banner' + }; + bidRequest.params.bidFloor && (payload['bidFloor'] = bidRequest.params.bidFloor); + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload + }; + }); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + const response = serverResponse.body; + const creativeId = response.adid || 0; + // const width = response.w || 0; + const width = response.width; + // const height = response.h || 0; + const height = response.height; + const cpm = response.price || 0; + + response.rWidth = width; + response.rHeight = height; + + const adCreative = response.vdoCreative; + + if (width !== 0 && height !== 0 && cpm !== 0 && creativeId !== 0) { + // const dealId = response.dealid || ''; + const currency = response.cur || 'USD'; + const netRevenue = true; + // const referrer = bidRequest.data.referer; + const bidResponse = { + requestId: response.bidId, + cpm: cpm, + width: width, + height: height, + creativeId: creativeId, + // dealId: dealId, + currency: currency, + netRevenue: netRevenue, + ttl: config.getConfig('_bidderTimeout'), + // referrer: referrer, + // ad: response.adm + // ad: adCreative, + mediaType: response.mediaType + }; + + if (response.mediaType == 'video') { + bidResponse.vastXml = adCreative; + } else { + bidResponse.ad = adCreative; + } + if (response.adDomain) { + bidResponse.meta = { + advertiserDomains: response.adDomain + } + } + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + + getUserSyncs: function(syncOptions, serverResponse) { + let syncUrls = serverResponse[0] && serverResponse[0].body && serverResponse[0].body.cookiesync && serverResponse[0].body.cookiesync.bidder_status; + + if (syncOptions.iframeEnabled && syncUrls && syncUrls.length > 0) { + let prebidSyncUrls = syncUrls.map(syncObj => { + return { + url: syncObj.usersync.url, + type: 'iframe' + } + }) + return prebidSyncUrls; + } + return []; + }, + + onTImeout: function(data) {}, + onBidWon: function(bid) {}, + onSetTargeting: function(bid) {} +}; +registerBidder(spec); diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js new file mode 100644 index 00000000000..b1cfa606d84 --- /dev/null +++ b/test/spec/modules/vdoaiBidAdapter_spec.js @@ -0,0 +1,146 @@ +import {assert, expect} from 'chai'; +import {spec} from 'modules/vdoaiBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; + +describe('vdoaiBidAdapter', function () { + const adapter = newBidder(spec); + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'vdoai', + 'params': { + placementId: 'testPlacementId' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '1234asdf1234', + 'bidderRequestId': '1234asdf1234asdf', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' + }; + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'vdoai', + 'params': { + placementId: 'testPlacementId' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + 'mediaTypes': 'banner' + } + ]; + + let bidderRequests = { + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com', + 'stack': ['https://example.com'] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT_URL, + 'data': { + 'placementId': 'testPlacementId', + 'width': '300', + 'height': '200', + 'bidId': 'bidId123', + 'referer': 'www.example.com' + } + + } + ]; + let serverResponse = { + body: { + 'vdoCreative': '

I am an ad

', + 'price': 4.2, + 'adid': '12345asdfg', + 'currency': 'EUR', + 'statusMessage': 'Bid available', + 'requestId': 'bidId123', + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'adDomain': ['text.abc'] + } + }; + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': 'bidId123', + 'cpm': 4.2, + 'width': 300, + 'height': 250, + 'creativeId': '12345asdfg', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 3000, + 'ad': '

I am an ad

', + 'meta': { + 'advertiserDomains': ['text.abc'] + } + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); + }); + + it('handles instream video responses', function () { + let serverResponse = { + body: { + 'vdoCreative': '', + 'price': 4.2, + 'adid': '12345asdfg', + 'currency': 'EUR', + 'statusMessage': 'Bid available', + 'requestId': 'bidId123', + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'mediaType': 'video' + } + }; + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT_URL, + 'data': { + 'placementId': 'testPlacementId', + 'width': '300', + 'height': '200', + 'bidId': 'bidId123', + 'referer': 'www.example.com', + 'mediaType': 'video' + } + } + ]; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + }); +}); From 1f5b03d1457089e08251e22edd61f819a495bd35 Mon Sep 17 00:00:00 2001 From: Roberto Hsu Wu Date: Tue, 29 Jun 2021 12:57:44 -0300 Subject: [PATCH 1220/1476] Gnet Bid Adapter: adomain change for Prebid 5.x and remove useless parameter (#7124) * Add files via upload * Add files via upload * Change params on gnetBidder * ADJ - Use parseSizesInput to get sizes ADJ - Check serverResponse object ADJ - Remove getUserSyncs function * ADJ - Change prebid endpoint * ADJ - Change endpoint on test * GnetBidAdapter update for Prebid 5.x Co-authored-by: Roberto Hsu --- modules/gnetBidAdapter.js | 104 ++++++++++++++++ modules/gnetBidAdapter.md | 5 +- test/spec/modules/gnetBidAdapter_spec.js | 145 +++++++++++++++++++++++ 3 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 modules/gnetBidAdapter.js create mode 100644 test/spec/modules/gnetBidAdapter_spec.js diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js new file mode 100644 index 00000000000..f45c10195cd --- /dev/null +++ b/modules/gnetBidAdapter.js @@ -0,0 +1,104 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'gnet'; +const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [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. + */ + isBidRequestValid: function (bid) { + return !!(bid.params.websiteId); + }, + + /** + * 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 bidRequests = []; + const referer = bidderRequest.refererInfo.referer; + + utils._each(validBidRequests, (request) => { + const data = {}; + + data.referer = referer; + data.adUnitCode = request.adUnitCode; + data.bidId = request.bidId; + data.transactionId = request.transactionId; + + data.sizes = utils.parseSizesInput(request.sizes); + + data.params = request.params; + + const payloadString = JSON.stringify(data); + + bidRequests.push({ + method: 'POST', + url: ENDPOINT, + mode: 'no-cors', + options: { + withCredentials: false, + }, + data: payloadString + }); + }); + + return bidRequests; + }, + + /** + * 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, requests) { + if (typeof serverResponse !== 'object') { + return []; + } + + const res = serverResponse && serverResponse.body; + + if (utils.isEmpty(res)) { + return []; + } + + if (res.bids) { + const bids = []; + utils._each(res.bids, (bidData) => { + const bid = { + requestId: bidData.bidId, + cpm: bidData.cpm, + currency: bidData.currency, + width: bidData.width, + height: bidData.height, + ad: bidData.ad, + ttl: 300, + meta: { + advertiserDomains: bidData.adomain ? bidData.adomain : [] + }, + creativeId: bidData.creativeId, + netRevenue: true, + }; + bids.push(bid); + }); + + return bids; + } + + return []; + }, +}; + +registerBidder(spec); diff --git a/modules/gnetBidAdapter.md b/modules/gnetBidAdapter.md index 6dac9be17b6..447d00d8ff2 100644 --- a/modules/gnetBidAdapter.md +++ b/modules/gnetBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: roberto.wu@grumft.com # Description -Module that connects to Example's demand sources +Connect to Gnet Project exchange for bids. # Test Parameters ``` @@ -24,8 +24,7 @@ Module that connects to Example's demand sources { bidder: 'gnet', params: { - websiteId: '4', - externalId: '4d52cccf30309282256012cf30309282' + websiteId: '4' } } ] diff --git a/test/spec/modules/gnetBidAdapter_spec.js b/test/spec/modules/gnetBidAdapter_spec.js new file mode 100644 index 00000000000..eeb33418a82 --- /dev/null +++ b/test/spec/modules/gnetBidAdapter_spec.js @@ -0,0 +1,145 @@ +import { + expect +} from 'chai'; +import { + spec +} from 'modules/gnetBidAdapter.js'; +import { + newBidder +} from 'src/adapters/bidderFactory.js'; + +const ENDPOINT = 'https://adserver.gnetproject.com/prebid.php'; + +describe('gnetAdapter', 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: 'gnet', + params: { + websiteId: '4' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + bidder: 'gnet', + params: { + websiteId: '4' + }, + adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', + sizes: [ + [300, 250], + ], + bidId: '2a19afd5173318', + bidderRequestId: '1f4001782ac16c', + auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', + transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' + }]; + + const bidderRequest = { + refererInfo: { + referer: 'https://gnetproject.com/' + } + }; + + it('sends bid request to ENDPOINT via POST', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].data).to.equal(JSON.stringify({ + 'referer': 'https://gnetproject.com/', + 'adUnitCode': '/150790500/4_ZONA_IAB_300x250_5', + 'bidId': '2a19afd5173318', + 'transactionId': '894bdff6-61ec-4bec-a5a9-f36a5bfccef5', + 'sizes': ['300x250'], + 'params': { + 'websiteId': '4' + } + })); + }); + }); + + describe('interpretResponse', function () { + const bidderRequests = [{ + bidder: 'gnet', + params: { + clientId: '123456' + }, + adUnitCode: '/150790500/4_ZONA_IAB_300x250_5', + sizes: [ + [300, 250], + ], + bidId: '2a19afd5173318', + bidderRequestId: '1f4001782ac16c', + auctionId: 'aba03555-4802-4c45-9f15-05ffa8594cff', + transactionId: '894bdff6-61ec-4bec-a5a9-f36a5bfccef5' + }]; + + it('should get correct banner bid response', function () { + const response = { + bids: [ + { + bidId: '2a19afd5173318', + cpm: 0.1, + currency: 'BRL', + width: 300, + height: 250, + ad: '

I am an ad

', + creativeId: '173560700', + } + ] + }; + + const expectedResponse = [ + { + requestId: '2a19afd5173318', + cpm: 0.1, + currency: 'BRL', + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 300, + meta: { + advertiserDomains: [] + }, + creativeId: '173560700', + netRevenue: true + } + ]; + + const result = spec.interpretResponse({ + body: response + }, bidderRequests); + expect(result).to.have.lengthOf(1); + expect(result).to.deep.have.same.members(expectedResponse); + }); + + it('handles nobid responses', function () { + const response = ''; + + const result = spec.interpretResponse({ + body: response + }, bidderRequests); + expect(result.length).to.equal(0); + }); + }); +}); From e1e5d691784ad3ce88169f6c7fe30f4beae8598c Mon Sep 17 00:00:00 2001 From: Pub-X <63354024+Pub-X@users.noreply.github.com> Date: Wed, 30 Jun 2021 01:20:45 +0900 Subject: [PATCH 1221/1476] Pub-X Bid Adapter: add adomain support (#7103) * add adomain support * fix linting error Co-authored-by: Chris Huie --- modules/pubxBidAdapter.js | 99 +++++++++++ test/spec/modules/pubxBidAdapter_spec.js | 199 +++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 modules/pubxBidAdapter.js create mode 100644 test/spec/modules/pubxBidAdapter_spec.js diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js new file mode 100644 index 00000000000..c7b63d3f7f7 --- /dev/null +++ b/modules/pubxBidAdapter.js @@ -0,0 +1,99 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'pubx'; +const BID_ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; +const USER_SYNC_URL = 'https://api.primecaster.net/primecaster_dmppv.html' +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + if (!(bid.params.sid)) { + return false; + } else { return true } + }, + buildRequests: function(validBidRequests) { + return validBidRequests.map(bidRequest => { + const bidId = bidRequest.bidId; + const params = bidRequest.params; + const sid = params.sid; + const payload = { + sid: sid + }; + return { + id: bidId, + method: 'GET', + url: BID_ENDPOINT, + data: payload, + } + }); + }, + interpretResponse: function(serverResponse, bidRequest) { + const body = serverResponse.body; + const bidResponses = []; + if (body.cid) { + const bidResponse = { + requestId: bidRequest.id, + cpm: body.cpm, + currency: body.currency, + width: body.width, + height: body.height, + creativeId: body.cid, + netRevenue: true, + ttl: body.TTL, + ad: body.adm + }; + if (body.adomains) { + utils.deepSetValue(bidResponse, 'meta.advertiserDomains', Array.isArray(body.adomains) ? body.adomains : [body.adomains]); + } + bidResponses.push(bidResponse); + } else {}; + return bidResponses; + }, + /** + * Determine which user syncs should occur + * @param {object} syncOptions + * @param {array} serverResponses + * @returns {array} User sync pixels + */ + getUserSyncs: function (syncOptions, serverResponses) { + const kwTag = document.getElementsByName('keywords'); + let kwString = ''; + let kwEnc = ''; + let titleContent = !!document.title && document.title; + let titleEnc = ''; + let descContent = !!document.getElementsByName('description') && !!document.getElementsByName('description')[0] && document.getElementsByName('description')[0].content; + let descEnc = ''; + const pageUrl = location.href.replace(/\?.*$/, ''); + const pageEnc = encodeURIComponent(pageUrl); + const refUrl = document.referrer.replace(/\?.*$/, ''); + const refEnc = encodeURIComponent(refUrl); + if (kwTag.length) { + const kwContents = kwTag[0].content; + if (kwContents.length > 20) { + const kwArray = kwContents.substr(0, 20).split(','); + kwArray.pop(); + kwString = kwArray.join(); + } else { + kwString = kwContents; + } + kwEnc = encodeURIComponent(kwString) + } else { } + if (titleContent) { + if (titleContent.length > 30) { + titleContent = titleContent.substr(0, 30); + } else {}; + titleEnc = encodeURIComponent(titleContent); + } else { }; + if (descContent) { + if (descContent.length > 60) { + descContent = descContent.substr(0, 60); + } else {}; + descEnc = encodeURIComponent(descContent); + } else { }; + return (syncOptions.iframeEnabled) ? [{ + type: 'iframe', + url: USER_SYNC_URL + '?pkw=' + kwEnc + '&pd=' + descEnc + '&pu=' + pageEnc + '&pref=' + refEnc + '&pt=' + titleEnc + }] : []; + } +} +registerBidder(spec); diff --git a/test/spec/modules/pubxBidAdapter_spec.js b/test/spec/modules/pubxBidAdapter_spec.js new file mode 100644 index 00000000000..06bb5b5f638 --- /dev/null +++ b/test/spec/modules/pubxBidAdapter_spec.js @@ -0,0 +1,199 @@ +import {expect} from 'chai'; +import {spec} from 'modules/pubxBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; + +describe('pubxAdapter', function () { + const adapter = newBidder(spec); + const ENDPOINT = 'https://api.primecaster.net/adlogue/api/slot/bid'; + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + const bid = { + bidder: 'pubx', + params: { + sid: '12345abc' + } + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + id: '26c1ee0038ac11', + params: { + sid: '12345abc' + } + } + ]; + + const data = { + banner: { + sid: '12345abc' + } + }; + + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET'); + }); + + it('should attach params to the banner request', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.deep.equal(data.banner); + }); + }); + + describe('getUserSyncs', function () { + const sandbox = sinon.sandbox.create(); + + const keywordsText = 'meta1,meta2,meta3,meta4,meta5'; + const descriptionText = 'description1description2description3description4description5description'; + + let documentStubMeta; + + beforeEach(function () { + documentStubMeta = sandbox.stub(document, 'getElementsByName'); + const metaElKeywords = document.createElement('meta'); + metaElKeywords.setAttribute('name', 'keywords'); + metaElKeywords.setAttribute('content', keywordsText); + documentStubMeta.withArgs('keywords').returns([metaElKeywords]); + + const metaElDescription = document.createElement('meta'); + metaElDescription.setAttribute('name', 'description'); + metaElDescription.setAttribute('content', descriptionText); + documentStubMeta.withArgs('description').returns([metaElDescription]); + }); + + afterEach(function () { + documentStubMeta.restore(); + }); + + let kwString = ''; + let kwEnc = ''; + let descContent = ''; + let descEnc = ''; + + it('returns empty sync array when iframe is not enabled', function () { + const syncOptions = {}; + expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]); + }); + + it('returns kwEnc when there is kwTag with more than 20 length', function () { + const kwArray = keywordsText.substr(0, 20).split(','); + kwArray.pop(); + kwString = kwArray.join(); + kwEnc = encodeURIComponent(kwString); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); + expect(syncs[0].url).to.include(`pkw=${kwEnc}`); + }); + + it('returns kwEnc when there is kwTag with more than 60 length', function () { + descContent = descContent.substr(0, 60); + descEnc = encodeURIComponent(descContent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); + expect(syncs[0].url).to.include(`pkw=${descEnc}`); + }); + + it('returns titleEnc when there is titleContent with more than 30 length', function () { + let titleText = 'title1title2title3title4title5title'; + const documentStubTitle = sandbox.stub(document, 'title').value(titleText); + + if (titleText.length > 30) { + titleText = titleText.substr(0, 30); + } + + const syncs = spec.getUserSyncs({ iframeEnabled: true }); + expect(syncs[0].url).to.include(`pt=${encodeURIComponent(titleText)}`); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + TTL: 300, + adm: '
some creative
', + cid: 'TKmB', + cpm: 500, + currency: 'JPY', + height: 250, + width: 300, + adomains: [ + 'test.com' + ], + } + } + + const bidRequests = [ + { + id: '26c1ee0038ac11', + params: { + sid: '12345abc' + } + } + ]; + + const bidResponses = [ + { + requestId: '26c1ee0038ac11', + cpm: 500, + currency: 'JPY', + width: 300, + height: 250, + creativeId: 'TKmB', + netRevenue: true, + ttl: 300, + ad: '
some creative
', + meta: { + advertiserDomains: [ + 'test.com' + ] + }, + } + ]; + it('should return empty array when required param is empty', function () { + const serverResponseWithCidEmpty = { + body: { + TTL: 300, + adm: '
some creative
', + cid: '', + cpm: '', + currency: 'JPY', + height: 250, + width: 300, + } + } + const result = spec.interpretResponse(serverResponseWithCidEmpty, bidRequests[0]); + expect(result).to.be.empty; + }); + it('handles banner responses', function () { + const result = spec.interpretResponse(serverResponse, bidRequests[0])[0]; + expect(result.requestId).to.equal(bidResponses[0].requestId); + expect(result.width).to.equal(bidResponses[0].width); + expect(result.height).to.equal(bidResponses[0].height); + expect(result.creativeId).to.equal(bidResponses[0].creativeId); + expect(result.currency).to.equal(bidResponses[0].currency); + expect(result.netRevenue).to.equal(bidResponses[0].netRevenue); + expect(result.ttl).to.equal(bidResponses[0].ttl); + expect(result.ad).to.equal(bidResponses[0].ad); + expect(result.meta.advertiserDomains).deep.to.equal(bidResponses[0].meta.advertiserDomains); + }); + }); +}); From 0492821b8ce055cac419ad00ac90eee1b66e5e92 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Wed, 30 Jun 2021 03:42:35 +0900 Subject: [PATCH 1222/1476] relaido Bid Adapter: Add Usersyncs a uuid in the query parameter (#7112) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * add uuid to SyncAPI URL Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- modules/relaidoBidAdapter.js | 4 ++-- test/spec/modules/relaidoBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index f69f8c5c6e2..14effa263a3 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.4'; +const ADAPTER_VERSION = '1.0.5'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -140,7 +140,7 @@ function getUserSyncs(syncOptions, serverResponses) { } return [{ type: 'iframe', - url: syncUrl + url: `${syncUrl}?uu=${getUuid()}` }]; } diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index c2082eb1e91..e372c67bb4e 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -322,7 +322,7 @@ describe('RelaidoAdapter', function () { let userSyncs = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); expect(userSyncs).to.deep.equal([{ type: 'iframe', - url: serverResponse.body.syncUrl + url: serverResponse.body.syncUrl + '?uu=hogehoge' }]); }); @@ -330,7 +330,7 @@ describe('RelaidoAdapter', function () { let userSyncs = spec.getUserSyncs({iframeEnabled: true}, []); expect(userSyncs).to.deep.equal([{ type: 'iframe', - url: 'https://api.relaido.jp/tr/v1/prebid/sync.html' + url: 'https://api.relaido.jp/tr/v1/prebid/sync.html?uu=hogehoge' }]); }); @@ -339,7 +339,7 @@ describe('RelaidoAdapter', function () { let userSyncs = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); expect(userSyncs).to.deep.equal([{ type: 'iframe', - url: 'https://api.relaido.jp/tr/v1/prebid/sync.html' + url: 'https://api.relaido.jp/tr/v1/prebid/sync.html?uu=hogehoge' }]); }); From 7fc2f480b4f3a9f90f5314c4c0b56df2754f6018 Mon Sep 17 00:00:00 2001 From: haruka-yamashita2 <39541428+haruka-yamashita2@users.noreply.github.com> Date: Wed, 30 Jun 2021 04:44:53 +0900 Subject: [PATCH 1223/1476] YieldOne Bid Adapter: add LiveRampID support. (#7118) Co-authored-by: kenichi-ichijo --- modules/yieldoneBidAdapter.js | 6 ++++++ test/spec/modules/yieldoneBidAdapter_spec.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 14a9b7dae48..73c3d2e8808 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -52,6 +52,12 @@ export const spec = { payload.h = size.split('x')[1]; } + // LiveRampID + const idlEnv = utils.deepAccess(bidRequest, 'userId.idl_env'); + if (utils.isStr(idlEnv) && !utils.isEmpty(idlEnv)) { + payload.lr_env = idlEnv; + } + return { method: 'GET', url: ENDPOINT_URL, diff --git a/test/spec/modules/yieldoneBidAdapter_spec.js b/test/spec/modules/yieldoneBidAdapter_spec.js index 91bbc142236..4186c5da41a 100644 --- a/test/spec/modules/yieldoneBidAdapter_spec.js +++ b/test/spec/modules/yieldoneBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/yieldoneBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { deepClone } from 'src/utils.js'; const ENDPOINT = 'https://y.one.impact-ad.jp/h_bid'; const USER_SYNC_URL = 'https://y.one.impact-ad.jp/push_sync'; @@ -103,6 +104,22 @@ describe('yieldoneBidAdapter', function() { expect(request[0].data.uc).to.equal('adunit-code1'); expect(request[1].data.uc).to.equal('adunit-code2'); }); + + describe('userid idl_env should be passed to querystring', function () { + const bid = deepClone([bidRequests[0]]); + + it('dont send LiveRampID if undefined', function () { + bid[0].userId = {}; + const request = spec.buildRequests(bid, bidderRequest); + expect(request[0].data).to.not.have.property('lr_env'); + }); + + it('should send LiveRampID if available', function () { + bid[0].userId = {idl_env: 'idl_env_sample'}; + const request = spec.buildRequests(bid, bidderRequest); + expect(request[0].data.lr_env).to.equal('idl_env_sample'); + }); + }); }); describe('interpretResponse', function () { From 072d61289bcffa4f7860a66e4edbef4b69eb3af2 Mon Sep 17 00:00:00 2001 From: Ruslan Sibgatullin Date: Wed, 30 Jun 2021 09:54:59 +0300 Subject: [PATCH 1224/1476] Smaato bid adapter: adpod support added (#7101) * Smaato bid adapter: Refactor tests * Smaato bid adapter: Rework bid request validation to handle params for adpod adunits differently * Smaato bid adapter: Pass adpod adunit parameters to Smaatos server * Smaato bid adapter: Add response interpretation for adpods * Smaato bid adapter: Bump adapter version * Smaato bid adapter: finalizing OTT support Co-authored-by: Jonas Gresens <5290643+jgresens@users.noreply.github.com> Co-authored-by: Ruslan Sibgatullin --- modules/smaatoBidAdapter.js | 269 +++++-- modules/smaatoBidAdapter.md | 33 +- test/spec/modules/smaatoBidAdapter_spec.js | 846 +++++++++++++++------ 3 files changed, 836 insertions(+), 312 deletions(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index 719d78892ce..6eabc872827 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -1,11 +1,11 @@ import * as utils 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 {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {ADPOD, BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.2' +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.3' const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const request = { @@ -56,21 +56,25 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const videoMediaType = utils.deepAccess(bidRequest, 'mediaTypes.video'); if (videoMediaType) { - request.imp[0].video = { - mimes: videoMediaType.mimes, - minduration: videoMediaType.minduration, - startdelay: videoMediaType.startdelay, - linearity: videoMediaType.linearity, - w: videoMediaType.playerSize[0][0], - h: videoMediaType.playerSize[0][1], - maxduration: videoMediaType.maxduration, - skip: videoMediaType.skip, - protocols: videoMediaType.protocols, - ext: { - rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 - }, - skipmin: videoMediaType.skipmin, - api: videoMediaType.api + if (videoMediaType.context === ADPOD) { + request.imp = createAdPodRequest(bidRequest, request, videoMediaType) + } else { + request.imp[0].video = { + mimes: videoMediaType.mimes, + minduration: videoMediaType.minduration, + startdelay: videoMediaType.startdelay, + linearity: videoMediaType.linearity, + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + maxduration: videoMediaType.maxduration, + skip: videoMediaType.skip, + protocols: videoMediaType.protocols, + ext: { + rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 + }, + skipmin: videoMediaType.skipmin, + api: videoMediaType.api + } } } @@ -113,9 +117,44 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: (bid) => { - return typeof bid.params === 'object' && - typeof bid.params.publisherId === 'string' && - typeof bid.params.adspaceId === 'string'; + if (typeof bid.params !== 'object') { + utils.logError('[SMAATO] Missing params object'); + return false; + } + + if (typeof bid.params.publisherId !== 'string') { + utils.logError('[SMAATO] Missing mandatory publisherId param'); + return false; + } + + if (utils.deepAccess(bid, 'mediaTypes.video.context') === ADPOD) { + utils.logInfo('[SMAATO] Verifying adpod bid request'); + + if (typeof bid.params.adbreakId !== 'string') { + utils.logError('[SMAATO] Missing for adpod request mandatory adbreakId param'); + return false; + } + + if (bid.params.adspaceId) { + utils.logError('[SMAATO] The adspaceId param is not allowed in an adpod bid request'); + return false; + } + } else { + utils.logInfo('[SMAATO] Verifying a non adpod bid request'); + + if (typeof bid.params.adspaceId !== 'string') { + utils.logError('[SMAATO] Missing mandatory adspaceId param'); + return false; + } + + if (bid.params.adbreakId) { + utils.logError('[SMAATO] The adbreakId param is only allowed in an adpod bid request'); + return false; + } + } + + utils.logInfo('[SMAATO] Verification done, all good'); + return true; }, buildRequests: (validBidRequests, bidderRequest) => { @@ -149,58 +188,70 @@ export const spec = { return []; // no bids } - let serverResponseHeaders = serverResponse.headers; - const smtAdType = serverResponseHeaders.get('X-SMT-ADTYPE'); + const serverResponseHeaders = serverResponse.headers; const smtExpires = serverResponseHeaders.get('X-SMT-Expires'); - let ttlSec = 300; utils.logInfo('[SMAATO] Expires:', smtExpires); - if (smtExpires) { - ttlSec = Math.floor((smtExpires - Date.now()) / 1000); - } + const ttlInSec = smtExpires ? Math.floor((smtExpires - Date.now()) / 1000) : 300; - const res = serverResponse.body; - utils.logInfo('[SMAATO] OpenRTB Response:', res); + const response = serverResponse.body; + utils.logInfo('[SMAATO] OpenRTB Response:', response); - var bids = []; - res.seatbid.forEach(sb => { - sb.bid.forEach(b => { + const smtAdType = serverResponseHeaders.get('X-SMT-ADTYPE'); + const bids = []; + response.seatbid.forEach(seatbid => { + seatbid.bid.forEach(bid => { let resultingBid = { - requestId: b.impid, - cpm: b.price || 0, - width: b.w, - height: b.h, - ttl: ttlSec, - creativeId: b.crid, - dealId: b.dealid || null, - netRevenue: utils.deepAccess(b, 'ext.net', true), - currency: res.cur, + requestId: bid.impid, + cpm: bid.price || 0, + width: bid.w, + height: bid.h, + ttl: ttlInSec, + creativeId: bid.crid, + dealId: bid.dealid || null, + netRevenue: utils.deepAccess(bid, 'ext.net', true), + currency: response.cur, meta: { - advertiserDomains: b.adomain, - networkName: b.bidderName, - agencyId: sb.seat + advertiserDomains: bid.adomain, + networkName: bid.bidderName, + agencyId: seatbid.seat } }; - switch (smtAdType) { - case 'Img': - resultingBid.ad = createImgAd(b.adm); - resultingBid.meta.mediaType = BANNER; - bids.push(resultingBid); - break; - case 'Richmedia': - resultingBid.ad = createRichmediaAd(b.adm); - resultingBid.meta.mediaType = BANNER; - bids.push(resultingBid); - break; - case 'Video': - resultingBid.vastXml = b.adm; - resultingBid.meta.mediaType = VIDEO; - bids.push(resultingBid); - break; - default: - utils.logInfo('[SMAATO] Invalid ad type:', smtAdType); + const videoContext = utils.deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context') + if (videoContext === ADPOD) { + resultingBid.vastXml = bid.adm; + resultingBid.mediaType = VIDEO; + if (config.getConfig('adpod.brandCategoryExclusion')) { + resultingBid.meta.primaryCatId = bid.cat[0]; + } + resultingBid.video = { + context: ADPOD, + durationSeconds: bid.ext.duration + }; + bids.push(resultingBid); + } else { + switch (smtAdType) { + case 'Img': + resultingBid.ad = createImgAd(bid.adm); + resultingBid.mediaType = BANNER; + bids.push(resultingBid); + break; + case 'Richmedia': + resultingBid.ad = createRichmediaAd(bid.adm); + resultingBid.mediaType = BANNER; + bids.push(resultingBid); + break; + case 'Video': + resultingBid.vastXml = bid.adm; + resultingBid.mediaType = VIDEO; + bids.push(resultingBid); + break; + default: + utils.logInfo('[SMAATO] Invalid ad type:', smtAdType); + } } + resultingBid.meta.mediaType = resultingBid.mediaType; }); }); @@ -253,3 +304,95 @@ const createRichmediaAd = (adm) => { return markup + '
'; }; + +function createAdPodRequest(bidRequest, request, videoMediaType) { + const tagid = utils.deepAccess(bidRequest, 'params.adbreakId') + const bce = config.getConfig('adpod.brandCategoryExclusion') + let imp = { + id: bidRequest.bidId, + tagid: tagid, + video: { + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + ext: { + context: ADPOD, + brandcategoryexclusion: bce !== undefined && bce + } + } + } + addOptionalAdpodParameters(request, videoMediaType, imp) + + const numberOfPlacements = getAdPodNumberOfPlacements(videoMediaType) + let imps = utils.fill(imp, numberOfPlacements) + + const durationRangeSec = videoMediaType.durationRangeSec + if (videoMediaType.requireExactDuration) { + // equal distribution of numberOfPlacement over all available durations + const divider = Math.ceil(numberOfPlacements / durationRangeSec.length) + const chunked = utils.chunk(imps, divider) + + // each configured duration is set as min/maxduration for a subset of requests + durationRangeSec.forEach((duration, index) => { + chunked[index].map(imp => { + const sequence = index + 1; + imp.video.minduration = duration + imp.video.maxduration = duration + imp.video.sequence = sequence + }); + }); + } else { + // all maxdurations should be the same + const maxDuration = utils.getMaxValueFromArray(durationRangeSec); + imps.map((imp, index) => { + const sequence = index + 1; + imp.video.maxduration = maxDuration + imp.video.sequence = sequence + }); + } + + return imps +} + +function getAdPodNumberOfPlacements(videoMediaType) { + const {adPodDurationSec, durationRangeSec, requireExactDuration} = videoMediaType + const minAllowedDuration = utils.getMinValueFromArray(durationRangeSec) + const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration) + + return requireExactDuration + ? Math.max(numberOfPlacements, durationRangeSec.length) + : numberOfPlacements +} + +const addOptionalAdpodParameters = (request, videoMediaType, imp) => { + const content = {} + + if (videoMediaType.tvSeriesName) { + content.series = videoMediaType.tvSeriesName + } + if (videoMediaType.tvEpisodeName) { + content.title = videoMediaType.tvEpisodeName + } + if (typeof videoMediaType.tvSeasonNumber === 'number') { + content.season = videoMediaType.tvSeasonNumber.toString() // conversion to string as in OpenRTB season is a string + } + if (typeof videoMediaType.tvEpisodeNumber === 'number') { + content.episode = videoMediaType.tvEpisodeNumber + } + if (typeof videoMediaType.contentLengthSec === 'number') { + content.len = videoMediaType.contentLengthSec + } + if (videoMediaType.contentMode && ['live', 'on-demand'].indexOf(videoMediaType.contentMode) >= 0) { + content.livestream = videoMediaType.contentMode === 'live' ? 1 : 0 + } + + if (!utils.isEmpty(content)) { + request.site.content = content + } + imp.video.mimes = videoMediaType.mimes + imp.video.startdelay = videoMediaType.startdelay + imp.video.linearity = videoMediaType.linearity + imp.video.skip = videoMediaType.skip + imp.video.protocols = videoMediaType.protocols + imp.video.skipmin = videoMediaType.skipmin + imp.video.api = videoMediaType.api +} diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md index d26d7ecf64e..41e1c952f2a 100644 --- a/modules/smaatoBidAdapter.md +++ b/modules/smaatoBidAdapter.md @@ -61,4 +61,35 @@ var adUnits = [{ } }] }]; -``` \ No newline at end of file +``` + +For adpod adunits: + +``` +var adUnits = [{ + "code": "adpod unit", + "mediaTypes": { + "video": { + "context": "adpod", + "playerSize": [640, 480], + "adPodDurationSec": 300, + "durationRangeSec": [15, 30], + "requireExactDuration": false, + "mimes": ["video/mp4"], + "startdelay": 0, + "linearity": 1, + "protocols": [7], + "skip": 1, + "skipmin": 5, + "api": [7], + } + }, + "bids": [{ + "bidder": "smaato", + "params": { + "publisherId": "1100042525", + "adbreakId": "330563103" + } + }] +}]; +``` diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 0abc8463d28..fdcf714e9b6 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,20 +1,15 @@ -import { spec } from 'modules/smaatoBidAdapter.js'; +import {spec} from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; -import { config } from 'src/config.js'; -import { createEidsArray } from 'modules/userId/eids.js'; +import {config} from 'src/config.js'; +import {createEidsArray} from 'modules/userId/eids.js'; const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const request = { - method: 'POST', - url: 'https://prebid.ad.smaato.net/oapi/prebid', - data: '' -}; - const REFERRER = 'http://example.com/page.html' const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' +const AUCTION_ID = '6653'; const defaultBidderRequest = { gdprConsent: { @@ -25,14 +20,13 @@ const defaultBidderRequest = { refererInfo: { referer: REFERRER, }, - timeout: 1200 + timeout: 1200, + auctionId: AUCTION_ID }; -const minimalBidderRequest = { - refererInfo: { - referer: REFERRER, - } -}; +const BANNER_PREBID_MEDIATYPE = { + sizes: [[300, 50]] +} const singleBannerBidRequest = { bidder: 'smaato', @@ -41,46 +35,13 @@ const singleBannerBidRequest = { adspaceId: 'adspaceId' }, mediaTypes: { - banner: { - sizes: [[300, 50]] - } + banner: BANNER_PREBID_MEDIATYPE }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', sizes: [[300, 50]], bidId: 'bidId', bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - -const inAppBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId', - app: { - ifa: 'aDeviceId', - geo: { - lat: 33.3, - lon: -88.8 - } - } - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, @@ -94,20 +55,78 @@ const extractPayloadOfFirstAndOnlyRequest = (reqs) => { describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { - it('has valid params', () => { - expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456'}})).to.be.true; - expect(spec.isBidRequestValid(singleBannerBidRequest)).to.be.true; - }); - it('has invalid params', () => { + it('is invalid, when params object is not present', () => { expect(spec.isBidRequestValid({})).to.be.false; + }); + + it('is invalid, when params object is empty', () => { expect(spec.isBidRequestValid({params: {}})).to.be.false; - expect(spec.isBidRequestValid({params: {publisherId: '123'}})).to.be.false; - expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: 456}})).to.be.false; + }); + + it('is invalid, when publisherId is present but of wrong type', () => { + expect(spec.isBidRequestValid({params: {publisherId: 123}})).to.be.false; + }); + + describe('for ad pod / long form video requests', () => { + const ADPOD = {video: {context: 'adpod'}} + it('is invalid, when adbreakId is missing', () => { + expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123'}})).to.be.false; + }); + + it('is invalid, when adbreakId is present but of wrong type', () => { + expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: 456}})).to.be.false; + }); + + it('is valid, when required params are present', () => { + expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: '456'}})).to.be.true; + }); + + it('is invalid, when forbidden adspaceId param is present', () => { + expect(spec.isBidRequestValid({ + mediaTypes: ADPOD, + params: {publisherId: '123', adbreakId: '456', adspaceId: '42'} + })).to.be.false; + }); + }); + + describe('for non adpod requests', () => { + it('is invalid, when adspaceId is missing', () => { + expect(spec.isBidRequestValid({params: {publisherId: '123'}})).to.be.false; + }); + + it('is invalid, when adspaceId is present but of wrong type', () => { + expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: 456}})).to.be.false; + }); + + it('is valid, when required params are present for minimal request', () => { + expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456'}})).to.be.true; + }); + + it('is invalid, when forbidden adbreakId param is present', () => { + expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456', adbreakId: '42'}})).to.be.false; + }); }); }); describe('buildRequests', () => { + const BANNER_OPENRTB_IMP = { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + } + describe('common', () => { + const MINIMAL_BIDDER_REQUEST = { + refererInfo: { + referer: REFERRER, + } + }; + it('auction type is 1 (first price auction)', () => { const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); @@ -140,16 +159,7 @@ describe('smaatoBidAdapterTest', () => { expect(req.imp).to.deep.equal([ { id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 - } - ] - }, + banner: BANNER_OPENRTB_IMP, tagid: 'adspaceId' } ]); @@ -175,7 +185,7 @@ describe('smaatoBidAdapterTest', () => { }); it('sends no gdpr applies if no gdpr exists', () => { - const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + const reqs = spec.buildRequests([singleBannerBidRequest], MINIMAL_BIDDER_REQUEST); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.ext.gdpr).to.not.exist; @@ -197,7 +207,7 @@ describe('smaatoBidAdapterTest', () => { }); it('sends no us_privacy if no us_privacy exists', () => { - const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + const reqs = spec.buildRequests([singleBannerBidRequest], MINIMAL_BIDDER_REQUEST); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.ext.us_privacy).to.not.exist; @@ -259,6 +269,37 @@ describe('smaatoBidAdapterTest', () => { }); describe('buildRequests for video imps', () => { + const VIDEO_OUTSTREAM_PREBID_MEDIATYPE = { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + const VIDEO_OUTSTREAM_OPENRTB_IMP = { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + } + it('sends correct video imps', () => { const singleVideoBidRequest = { bidder: 'smaato', @@ -267,27 +308,13 @@ describe('smaatoBidAdapterTest', () => { adspaceId: 'adspaceId' }, mediaTypes: { - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } + video: VIDEO_OUTSTREAM_PREBID_MEDIATYPE }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', sizes: [[300, 50]], bidId: 'bidId', bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, @@ -297,28 +324,7 @@ describe('smaatoBidAdapterTest', () => { const reqs = spec.buildRequests([singleVideoBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - video: { - mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 - }, - tagid: 'adspaceId' - } - ]); + expect(req.imp[0].video).to.deep.equal(VIDEO_OUTSTREAM_OPENRTB_IMP); }); it('allows combined banner and video imp in single bid request', () => { @@ -329,30 +335,14 @@ describe('smaatoBidAdapterTest', () => { adspaceId: 'adspaceId' }, mediaTypes: { - banner: { - sizes: [[300, 50]] - }, - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } + banner: BANNER_PREBID_MEDIATYPE, + video: VIDEO_OUTSTREAM_PREBID_MEDIATYPE }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', sizes: [[300, 50]], bidId: 'bidId', bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, @@ -362,63 +352,333 @@ describe('smaatoBidAdapterTest', () => { const reqs = spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 - } - ] + expect(req.imp[0].banner).to.deep.equal(BANNER_OPENRTB_IMP); + expect(req.imp[0].video).to.deep.equal(VIDEO_OUTSTREAM_OPENRTB_IMP); + }); + + describe('ad pod / long form video', () => { + describe('required parameters with requireExactDuration false', () => { + const ADBREAK_ID = 'adbreakId'; + const ADPOD = 'adpod'; + const BID_ID = '4331'; + const W = 640; + const H = 480; + const ADPOD_DURATION = 300; + const DURATION_RANGE = [15, 30]; + const longFormVideoBidRequest = { + params: { + publisherId: 'publisherId', + adbreakId: ADBREAK_ID, }, - video: { - mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + mediaTypes: { + video: { + context: ADPOD, + playerSize: [[W, H]], + adPodDurationSec: ADPOD_DURATION, + durationRangeSec: DURATION_RANGE, + requireExactDuration: false + } }, - tagid: 'adspaceId' - } - ]); + bidId: BID_ID + }; + + it('sends required fields', () => { + const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.id).to.be.equal(AUCTION_ID); + expect(req.imp.length).to.be.equal(ADPOD_DURATION / DURATION_RANGE[0]); + expect(req.imp[0].id).to.be.equal(BID_ID); + expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); + expect(req.imp[0].video.ext.context).to.be.equal(ADPOD); + expect(req.imp[0].video.w).to.be.equal(W); + expect(req.imp[0].video.h).to.be.equal(H); + expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[1]); + expect(req.imp[0].video.sequence).to.be.equal(1); + expect(req.imp[1].id).to.be.equal(BID_ID); + expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID); + expect(req.imp[1].video.ext.context).to.be.equal(ADPOD); + expect(req.imp[1].video.w).to.be.equal(W); + expect(req.imp[1].video.h).to.be.equal(H); + expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]); + expect(req.imp[1].video.sequence).to.be.equal(2); + }); + + it('sends brand category exclusion as true when config is set to true', () => { + config.setConfig({adpod: {brandCategoryExclusion: true}}); + + const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(true); + }); + + it('sends brand category exclusion as false when config is set to false', () => { + config.setConfig({adpod: {brandCategoryExclusion: false}}); + + const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false); + }); + + it('sends brand category exclusion as false when config is not set', () => { + const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false); + }); + }); + describe('required parameters with requireExactDuration true', () => { + const ADBREAK_ID = 'adbreakId'; + const ADPOD = 'adpod'; + const BID_ID = '4331'; + const W = 640; + const H = 480; + const ADPOD_DURATION = 5; + const DURATION_RANGE = [5, 15, 25]; + const longFormVideoBidRequest = { + params: { + publisherId: 'publisherId', + adbreakId: ADBREAK_ID, + }, + mediaTypes: { + video: { + context: ADPOD, + playerSize: [[W, H]], + adPodDurationSec: ADPOD_DURATION, + durationRangeSec: DURATION_RANGE, + requireExactDuration: true + } + }, + bidId: BID_ID + }; + + it('sends required fields', () => { + const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.id).to.be.equal(AUCTION_ID); + expect(req.imp.length).to.be.equal(DURATION_RANGE.length); + expect(req.imp[0].id).to.be.equal(BID_ID); + expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); + expect(req.imp[0].video.ext.context).to.be.equal(ADPOD); + expect(req.imp[0].video.w).to.be.equal(W); + expect(req.imp[0].video.h).to.be.equal(H); + expect(req.imp[0].video.minduration).to.be.equal(DURATION_RANGE[0]); + expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[0]); + expect(req.imp[0].video.sequence).to.be.equal(1); + expect(req.imp[1].id).to.be.equal(BID_ID); + expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID); + expect(req.imp[1].video.ext.context).to.be.equal(ADPOD); + expect(req.imp[1].video.w).to.be.equal(W); + expect(req.imp[1].video.h).to.be.equal(H); + expect(req.imp[1].video.minduration).to.be.equal(DURATION_RANGE[1]); + expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]); + expect(req.imp[1].video.sequence).to.be.equal(2); + expect(req.imp[2].id).to.be.equal(BID_ID); + expect(req.imp[2].tagid).to.be.equal(ADBREAK_ID); + expect(req.imp[2].video.ext.context).to.be.equal(ADPOD); + expect(req.imp[2].video.w).to.be.equal(W); + expect(req.imp[2].video.h).to.be.equal(H); + expect(req.imp[2].video.minduration).to.be.equal(DURATION_RANGE[2]); + expect(req.imp[2].video.maxduration).to.be.equal(DURATION_RANGE[2]); + expect(req.imp[2].video.sequence).to.be.equal(3); + }); + }); + + describe('forwarding of optional parameters', () => { + const MIMES = ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v']; + const STARTDELAY = 0; + const LINEARITY = 1; + const SKIP = 1; + const PROTOCOLS = [7]; + const SKIPMIN = 5; + const API = [7]; + const validBasicAdpodBidRequest = { + params: { + publisherId: 'publisherId', + adbreakId: 'adbreakId', + }, + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + mimes: MIMES, + startdelay: STARTDELAY, + linearity: LINEARITY, + skip: SKIP, + protocols: PROTOCOLS, + skipmin: SKIPMIN, + api: API + } + }, + bidId: 'bidId' + }; + + it('sends general video fields when they are present', () => { + const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].video.mimes).to.eql(MIMES); + expect(req.imp[0].video.startdelay).to.be.equal(STARTDELAY); + expect(req.imp[0].video.linearity).to.be.equal(LINEARITY); + expect(req.imp[0].video.skip).to.be.equal(SKIP); + expect(req.imp[0].video.protocols).to.eql(PROTOCOLS); + expect(req.imp[0].video.skipmin).to.be.equal(SKIPMIN); + expect(req.imp[0].video.api).to.eql(API); + }); + + it('sends series name when parameter is present', () => { + const SERIES_NAME = 'foo' + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.tvSeriesName = SERIES_NAME; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.series).to.be.equal(SERIES_NAME); + }); + + it('sends episode name when parameter is present', () => { + const EPISODE_NAME = 'foo' + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.tvEpisodeName = EPISODE_NAME; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.title).to.be.equal(EPISODE_NAME); + }); + + it('sends season number as string when parameter is present', () => { + const SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST = 42 + const SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST = '42' + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.tvSeasonNumber = SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.season).to.be.equal(SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST); + }); + + it('sends episode number when parameter is present', () => { + const EPISODE_NUMBER = 42 + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.tvEpisodeNumber = EPISODE_NUMBER; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.episode).to.be.equal(EPISODE_NUMBER); + }); + + it('sends content length when parameter is present', () => { + const LENGTH = 42 + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.contentLengthSec = LENGTH; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.len).to.be.equal(LENGTH); + }); + + it('sends livestream as 1 when content mode parameter is live', () => { + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.contentMode = 'live'; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.livestream).to.be.equal(1); + }); + + it('sends livestream as 0 when content mode parameter is on-demand', () => { + const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); + adpodRequestWithParameter.mediaTypes.video.contentMode = 'on-demand'; + + const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.content.livestream).to.be.equal(0); + }); + + it("doesn't send any optional parameters when none are present", () => { + const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].video.ext.requireExactDuration).to.not.exist; + expect(req.site.content).to.not.exist; + }); + }); }); }); describe('in-app requests', () => { - it('add geo and ifa info to device object', () => { + const LOCATION = { + lat: 33.3, + lon: -88.8 + } + const DEVICE_ID = 'aDeviceId' + const inAppBidRequestWithoutAppParams = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: BANNER_PREBID_MEDIATYPE + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; + + it('when geo and ifa info present, then add both to device object', () => { + const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); + inAppBidRequest.params.app = {ifa: DEVICE_ID, geo: LOCATION}; + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); - expect(req.device.ifa).to.equal('aDeviceId'); + expect(req.device.geo).to.deep.equal(LOCATION); + expect(req.device.ifa).to.equal(DEVICE_ID); }); - it('when geo is missing, then add only ifa to device object', () => { - const inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); - delete inAppBidRequestWithoutGeo.params.app.geo + it('when ifa is present but geo is missing, then add only ifa to device object', () => { + const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); + inAppBidRequest.params.app = {ifa: DEVICE_ID}; - const reqs = spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest); + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.equal('aDeviceId'); + expect(req.device.ifa).to.equal(DEVICE_ID); }); - it('add no specific device info if param does not exist', () => { - const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + it('when geo is present but ifa is missing, then add only geo to device object', () => { + const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); + inAppBidRequest.params.app = {geo: LOCATION}; + + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.deep.equal(LOCATION); + expect(req.device.ifa).to.not.exist; + }); + + it('when app param does not exist, then add no specific device info', () => { + const reqs = spec.buildRequests([inAppBidRequestWithoutAppParams], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.device.geo).to.not.exist; @@ -435,16 +695,13 @@ describe('smaatoBidAdapterTest', () => { adspaceId: 'adspaceId' }, mediaTypes: { - banner: { - sizes: [[300, 50]] - } + banner: BANNER_PREBID_MEDIATYPE }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', sizes: [[300, 50]], bidId: 'bidId', bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, @@ -469,6 +726,14 @@ describe('smaatoBidAdapterTest', () => { }); describe('interpretResponse', () => { + function buildBidRequest(payloadAsJsObj = {imp: [{}]}) { + return { + method: 'POST', + url: 'https://prebid.ad.smaato.net/oapi/prebid', + data: JSON.stringify(payloadAsJsObj) + } + } + const buildOpenRtbBidResponse = (adType) => { let adm = ''; @@ -559,99 +824,184 @@ describe('smaatoBidAdapterTest', () => { }; it('returns empty array on no bid responses', () => { - const response_with_empty_body = {body: {}} + const response_with_empty_body = {body: {}}; - const bids = spec.interpretResponse(response_with_empty_body, request); + const bids = spec.interpretResponse(response_with_empty_body, buildBidRequest()); - expect(bids).to.be.empty + expect(bids).to.be.empty; }); - it('single image reponse', () => { - const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), request); + describe('non ad pod', () => { + it('single image reponse', () => { + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), buildBidRequest()); - expect(bids).to.deep.equal([ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + mediaType: 'banner', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } } - } - ]); - }); + ]); + }); - it('single richmedia reponse', () => { - const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), request); + it('single richmedia reponse', () => { + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), buildBidRequest()); - expect(bids).to.deep.equal([ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '

RICHMEDIA CONTENT

', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '

RICHMEDIA CONTENT

', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + mediaType: 'banner', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } } - } - ]); - }); + ]); + }); - it('single video reponse', () => { - const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), request); + it('single video reponse', () => { + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), buildBidRequest()); - expect(bids).to.deep.equal([ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - vastXml: '', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'video' + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + mediaType: 'video', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } } - } - ]); + ]); + }); + + it('ignores bid response with invalid ad type', () => { + const serverResponse = buildOpenRtbBidResponse(ADTYPE_IMG); + serverResponse.headers.get = (header) => { + if (header === 'X-SMT-ADTYPE') { + return undefined; + } + }; + + const bids = spec.interpretResponse(serverResponse, buildBidRequest()); + + expect(bids).to.be.empty; + }); }); - it('ignores bid response with invalid ad type', () => { - const resp = buildOpenRtbBidResponse(ADTYPE_IMG); - resp.headers.get = (header) => { - if (header === 'X-SMT-ADTYPE') { - return undefined; - } - } + describe('ad pod', () => { + const bidRequestWithAdpodContext = buildBidRequest({imp: [{video: {ext: {context: 'adpod'}}}]}); + const PRIMARY_CAT_ID = 1337 + const serverResponse = { + body: { + bidid: '04db8629-179d-4bcd-acce-e54722969006', + cur: 'USD', + ext: {}, + id: '5ebea288-f13a-4754-be6d-4ade66c68877', + seatbid: [ + { + bid: [ + { + adm: '', + adomain: [ + 'smaato.com' + ], + bidderName: 'smaato', + cid: 'CM6523', + crid: 'CR69381', + dealid: '12345', + id: '6906aae8-7f74-4edd-9a4f-f49379a3cadd', + impid: '226416e6e6bf41', + iurl: 'https://prebid/iurl', + nurl: 'https://prebid/nurl', + price: 0.01, + w: 350, + h: 50, + cat: [PRIMARY_CAT_ID], + ext: { + duration: 42 + } + } + ], + seat: 'CM6523' + } + ] + }, + headers: {get: () => undefined} + }; - const bids = spec.interpretResponse(resp, request); + it('sets required values for adpod bid from server response', () => { + const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext); - expect(bids).to.be.empty + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + mediaType: 'video', + video: { + context: 'adpod', + durationSeconds: 42 + }, + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } + } + ]); + }); + + it('sets primary category id in case of enabled brand category exclusion', () => { + config.setConfig({adpod: {brandCategoryExclusion: true}}); + + const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext) + + expect(bids[0].meta.primaryCatId).to.be.equal(PRIMARY_CAT_ID) + }) }); it('uses correct TTL when expire header exists', () => { @@ -665,9 +1015,9 @@ describe('smaatoBidAdapterTest', () => { if (header === 'X-SMT-Expires') { return 2000 + (400 * 1000); } - } + }; - const bids = spec.interpretResponse(resp, request); + const bids = spec.interpretResponse(resp, buildBidRequest()); expect(bids[0].ttl).to.equal(400); @@ -678,10 +1028,10 @@ describe('smaatoBidAdapterTest', () => { const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.body.seatbid[0].bid[0].ext = {net: false}; - const bids = spec.interpretResponse(resp, request); + const bids = spec.interpretResponse(resp, buildBidRequest()); expect(bids[0].netRevenue).to.equal(false); - }) + }); }); describe('getUserSyncs', () => { From 4bf8d004490247a33048958f56939b370c12822d Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Wed, 30 Jun 2021 01:36:50 -0700 Subject: [PATCH 1225/1476] OpenX: add flocId (#7125) --- modules/openxBidAdapter.js | 4 ++++ test/spec/modules/openxBidAdapter_spec.js | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index ee4784350c4..4644b77bb08 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -34,6 +34,7 @@ export const USER_ID_CODE_TO_QUERY_ARG = { tapadId: 'tapadid', // Tapad Id tdid: 'ttduuid', // The Trade Desk Unified ID uid2: 'uid2', // Unified ID 2.0 + flocId: 'floc', // Chrome FLoC }; export const spec = { @@ -291,6 +292,9 @@ function appendUserIdsToQueryParams(queryParams, userIds) { if (USER_ID_CODE_TO_QUERY_ARG.hasOwnProperty(userIdProviderKey)) { switch (userIdProviderKey) { + case 'flocId': + queryParams[key] = userIdObjectOrValue.id; + break; case 'uid2': queryParams[key] = userIdObjectOrValue.id; break; diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 532c1d40ee0..2b3b02e66e2 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1066,7 +1066,8 @@ describe('OpenxAdapter', function () { quantcastId: '1111-quantcastid', tapadId: '111-tapadid', tdid: '1111-tdid', - uid2: {id: '1111-uid2'} + uid2: {id: '1111-uid2'}, + flocId: {id: '12144', version: 'chrome.1.1'} }; // generates the same set of tests for each id provider @@ -1104,6 +1105,9 @@ describe('OpenxAdapter', function () { let userIdValue; // handle cases where userId key refers to an object switch (userIdProviderKey) { + case 'flocId': + userIdValue = EXAMPLE_DATA_BY_ATTR.flocId.id; + break; case 'uid2': userIdValue = EXAMPLE_DATA_BY_ATTR.uid2.id; break; From 5c00a6d2f6f012bcecaf2d1d9e65f0a734d8dc98 Mon Sep 17 00:00:00 2001 From: Audiencerun <57719351+audiencerun@users.noreply.github.com> Date: Wed, 30 Jun 2021 17:08:59 +0200 Subject: [PATCH 1226/1476] Audiencerun Bid Adapter 1.1.0 (#6919) * audiencerun adapter : remove bid.adId, timeout pixel, gdpr version * Audiencerun fix: handle advertiserDomains on meta object and remove adId from spec tests * AudienceRun fix: add support for floors module and advertiserDomains Co-authored-by: Geoffroy Hutin Co-authored-by: Abdessalam Benharira --- modules/audiencerunBidAdapter.js | 119 ++++++++++++++---- modules/audiencerunBidAdapter.md | 2 +- .../modules/audiencerunBidAdapter_spec.js | 53 +++++++- 3 files changed, 144 insertions(+), 30 deletions(-) diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js index b90471ee21a..da0cbb39925 100644 --- a/modules/audiencerunBidAdapter.js +++ b/modules/audiencerunBidAdapter.js @@ -4,10 +4,52 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'audiencerun'; -const ENDPOINT_URL = 'https://d.audiencerun.com/prebid'; +const BASE_URL = 'https://d.audiencerun.com'; +const AUCTION_URL = `${BASE_URL}/prebid`; +const TIMEOUT_EVENT_URL = `${BASE_URL}/ps/pbtimeout`; +const DEFAULT_CURRENCY = 'USD'; + +let requestedBids = []; + +/** + * Gets bidder request referer + * + * @param {Object} bidderRequest + * @return {string} + */ +function getPageUrl(bidderRequest) { + return ( + config.getConfig('pageUrl') || + utils.deepAccess(bidderRequest, 'refererInfo.referer') || + null + ); +} + +/** + * Returns bidfloor through floors module if available + * + * @param {Object} bid + * @returns {number} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return utils.deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: BANNER, + size: '*', + }); + return bidFloor.floor; + } catch (_) { + return 0 + } +} export const spec = { - version: '1.0.0', + version: '1.1.0', code: BIDDER_CODE, supportedMediaTypes: [BANNER], @@ -33,50 +75,53 @@ export const spec = { * @param {*} bidderRequest * @return {ServerRequest} Info describing the request to the server. */ - buildRequests: function(bidRequests, bidderRequest) { - const bids = bidRequests.map(bid => { + buildRequests: function (bidRequests, bidderRequest) { + const bids = bidRequests.map((bid) => { const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes', []); return { zoneId: utils.getValue(bid.params, 'zoneId'), - sizes: sizes.map(size => ({ + sizes: sizes.map((size) => ({ w: size[0], - h: size[1] + h: size[1], })), - bidfloor: bid.params.bidfloor || 0.0, + bidfloor: getBidFloor(bid), bidId: bid.bidId, bidderRequestId: utils.getBidIdParameter('bidderRequestId', bid), adUnitCode: utils.getBidIdParameter('adUnitCode', bid), auctionId: utils.getBidIdParameter('auctionId', bid), - transactionId: utils.getBidIdParameter('transactionId', bid) + transactionId: utils.getBidIdParameter('transactionId', bid), }; }); const payload = { libVersion: this.version, - referer: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null, + referer: getPageUrl(bidderRequest), currencyCode: config.getConfig('currency.adServerCurrency'), timeout: config.getConfig('bidderTimeout'), - bids + bids, }; if (bidderRequest && bidderRequest.gdprConsent) { payload.gdpr = { consent: bidderRequest.gdprConsent.consentString, - applies: bidderRequest.gdprConsent.gdprApplies + applies: bidderRequest.gdprConsent.gdprApplies, + version: bidderRequest.gdprConsent.apiVersion, }; } else { payload.gdpr = { - consent: '' - } + consent: '', + }; } + requestedBids = bids; + return { method: 'POST', - url: ENDPOINT_URL, + url: AUCTION_URL, data: JSON.stringify(payload), options: { - withCredentials: true - } + withCredentials: true, + }, }; }, @@ -100,15 +145,22 @@ export const spec = { // Common properties bid.requestId = bidObject.bidId; - bid.adId = bidObject.zoneId; bid.cpm = parseFloat(bidObject.cpm); bid.creativeId = bidObject.crid; - bid.currency = bidObject.currency ? bidObject.currency.toUpperCase() : 'USD'; + bid.currency = bidObject.currency + ? bidObject.currency.toUpperCase() + : DEFAULT_CURRENCY; bid.height = bidObject.h; bid.width = bidObject.w; bid.netRevenue = bidObject.isNet ? bidObject.isNet : false; bid.ttl = 300; + bid.meta = { + advertiserDomains: + bidObject.adomain && Array.isArray(bidObject.adomain) + ? bidObject.adomain + : [], + }; bids.push(bid); }); @@ -122,21 +174,42 @@ export const spec = { * @param {ServerResponse[]} serverResponses List of server's responses. * @return {UserSync[]} The user syncs which should be dropped. */ - getUserSyncs: function(syncOptions, serverResponses) { + getUserSyncs: function (syncOptions, serverResponses) { if (!serverResponses || !serverResponses.length) return []; const syncs = []; - serverResponses.forEach(response => { - response.body.bid.forEach(bidObject => { + serverResponses.forEach((response) => { + response.body.bid.forEach((bidObject) => { syncs.push({ type: 'iframe', - url: bidObject.syncUrl + url: bidObject.syncUrl, }); }); }); return syncs; - } + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * + * @param {Array} timeoutData timeout specific data + */ + onTimeout: function (timeoutData) { + if (!utils.isArray(timeoutData)) { + return; + } + + timeoutData.forEach((bid) => { + const bidOnTimeout = requestedBids.find((requestedBid) => requestedBid.bidId === bid.bidId); + + if (bidOnTimeout) { + utils.triggerPixel( + `${TIMEOUT_EVENT_URL}/${bidOnTimeout.zoneId}/${bidOnTimeout.bidId}` + ); + } + }); + }, }; registerBidder(spec); diff --git a/modules/audiencerunBidAdapter.md b/modules/audiencerunBidAdapter.md index 3704922fdd5..2257e939f3b 100644 --- a/modules/audiencerunBidAdapter.md +++ b/modules/audiencerunBidAdapter.md @@ -2,7 +2,7 @@ **Module Name**: AudienceRun Bidder Adapter **Module Type**: Bidder Adapter -**Maintainer**: prebid@audiencerun.com +**Maintainer**: github@audiencerun.com # Description diff --git a/test/spec/modules/audiencerunBidAdapter_spec.js b/test/spec/modules/audiencerunBidAdapter_spec.js index 826944abaf5..7c1279f5073 100644 --- a/test/spec/modules/audiencerunBidAdapter_spec.js +++ b/test/spec/modules/audiencerunBidAdapter_spec.js @@ -10,7 +10,6 @@ const BID_SERVER_RESPONSE = { { 'bidId': '51ef8751f9aead', 'zoneId': '12345abcde', - 'adId': '1234', 'crid': '5678', 'cpm': 8.021951999999999999, 'currency': 'USD', @@ -19,7 +18,8 @@ const BID_SERVER_RESPONSE = { 'isNet': false, 'buying_type': 'rtb', 'syncUrl': 'https://ac.audiencerun.com/f/sync.html', - 'adm': '' + 'adm': '', + 'adomain': ['example.com'] } ] } @@ -77,7 +77,7 @@ describe('AudienceRun bid adapter tests', function() { }); describe('buildRequests', function() { - let bidRequests = [ + const bidRequests = [ { 'bidder': 'audiencerun', 'bidId': '51ef8751f9aead', @@ -96,6 +96,7 @@ describe('AudienceRun bid adapter tests', function() { 'bidRequestsCount': 1 } ]; + const bidRequest = bidRequests[0]; it('sends a valid bid request to ENDPOINT via POST', function() { const request = spec.buildRequests(bidRequests, { @@ -156,12 +157,43 @@ describe('AudienceRun bid adapter tests', function() { expect(payload2.gdpr.consent).to.equal(consentString); expect(payload2.gdpr.applies).to.equal(false); }); + + it('should use a bidfloor with a 0 value', function() { + const bid = Object.assign({}, bidRequest); + const request = spec.buildRequests([bid]); + const payload = JSON.parse(request.data); + expect(payload.bids[0].bidfloor).to.exist.and.to.equal(0); + }) + + it('should use bidfloor param value', function () { + const bid = Object.assign({}, bidRequest, { + params: { + 'bidfloor': 0.2 + } + }) + const request = spec.buildRequests([bid]); + const payload = JSON.parse(request.data); + expect(payload.bids[0].bidfloor).to.exist.and.to.equal(0.2); + }); + + it('should use floors module value', function () { + const bid = Object.assign({}, bidRequest, { + params: { + 'bidfloor': 0.5 + } + }) + bid.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const request = spec.buildRequests([bid]); + const payload = JSON.parse(request.data); + expect(payload.bids[0].bidfloor).to.exist.and.to.equal(1); + }); }); describe('interpretResponse', function () { const expectedResponse = [{ 'requestId': '51ef8751f9aead', - 'adId': '12345abcde', 'cpm': 8.021951999999999999, 'width': '728', 'height': '90', @@ -170,7 +202,10 @@ describe('AudienceRun bid adapter tests', function() { 'netRevenue': false, 'ttl': 300, 'ad': '', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; it('should get the correct bid response by display ad', function () { @@ -178,7 +213,7 @@ describe('AudienceRun bid adapter tests', function() { expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles empty bid response', function () { + it('should handle empty bid response', function () { const response = { body: {} }; @@ -201,4 +236,10 @@ describe('AudienceRun bid adapter tests', function() { expect(syncs).to.deep.equal([{type: 'iframe', url: 'https://ac.audiencerun.com/f/sync.html'}]) }); }); + + describe('onTimeout', function () { + it('should exists and be a function', () => { + expect(spec.onTimeout).to.exist.and.to.be.a('function'); + }); + }); }); From aea9e54760c7fe97151f1e07ef6d96d36af410fa Mon Sep 17 00:00:00 2001 From: AdmixerTech <35560933+AdmixerTech@users.noreply.github.com> Date: Thu, 1 Jul 2021 14:56:29 +0300 Subject: [PATCH 1227/1476] update handle, update tests (#7127) Co-authored-by: atkachov --- modules/admixerBidAdapter.js | 29 +++------------------ test/spec/modules/admixerBidAdapter_spec.js | 7 +++-- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index 70ea5666e84..1aecc85fe5a 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -4,7 +4,7 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'admixer'; const ALIASES = ['go2net', 'adblender']; -const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.1.aspx'; +const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; export const spec = { code: BIDDER_CODE, aliases: ALIASES, @@ -21,7 +21,7 @@ export const spec = { buildRequests: function (validRequest, bidderRequest) { const payload = { imps: [], - fpd: config.getLegacyFpd(config.getConfig('ortb2')) + ortb2: config.getConfig('ortb2'), }; let endpointUrl; if (bidderRequest) { @@ -43,9 +43,7 @@ export const spec = { } validRequest.forEach((bid) => { let imp = {}; - Object.keys(bid).forEach(key => { - (key === 'ortb2Imp') ? imp.fpd = config.getLegacyImpFpd(bid[key]) : imp[key] = bid[key]; - }); + Object.keys(bid).forEach(key => imp[key] = bid[key]); payload.imps.push(imp); }); const payloadString = JSON.stringify(payload); @@ -62,26 +60,7 @@ export const spec = { const bidResponses = []; try { const {body: {ads = []} = {}} = serverResponse; - ads.forEach((bidResponse) => { - const bidResp = { - requestId: bidResponse.bidId, - cpm: bidResponse.cpm, - width: bidResponse.width, - height: bidResponse.height, - ad: bidResponse.ad, - ttl: bidResponse.ttl, - creativeId: bidResponse.creativeId, - netRevenue: bidResponse.netRevenue, - currency: bidResponse.currency, - vastUrl: bidResponse.vastUrl, - dealId: bidResponse.dealId, - /** - * currently includes meta.advertiserDomains ; networkId ; advertiserId - */ - meta: bidResponse.meta, - }; - bidResponses.push(bidResp); - }); + ads.forEach((ad) => bidResponses.push(ad)); } catch (e) { utils.logError(e); } diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 030e98d23f9..27dc895e3a4 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -4,7 +4,7 @@ import {newBidder} from 'src/adapters/bidderFactory.js'; import {config} from '../../../src/config.js'; const BIDDER_CODE = 'admixer'; -const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.1.aspx'; +const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; const ENDPOINT_URL_CUSTOM = 'https://custom.admixer.net/prebid.aspx'; const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; @@ -101,7 +101,7 @@ describe('AdmixerAdapter', function () { 'creativeId': 'ccca3e5e-0c54-4761-9667-771322fbdffc', 'ttl': 360, 'netRevenue': false, - 'bidId': '5e4e763b6bc60b', + 'requestId': '5e4e763b6bc60b', 'dealId': 'asd123', 'meta': {'advertiserId': 123, 'networkId': 123, 'advertiserDomains': ['test.com']} }] @@ -112,13 +112,12 @@ describe('AdmixerAdapter', function () { const ads = response.body.ads; let expectedResponse = [ { - 'requestId': ads[0].bidId, + 'requestId': ads[0].requestId, 'cpm': ads[0].cpm, 'creativeId': ads[0].creativeId, 'width': ads[0].width, 'height': ads[0].height, 'ad': ads[0].ad, - 'vastUrl': undefined, 'currency': ads[0].currency, 'netRevenue': ads[0].netRevenue, 'ttl': ads[0].ttl, From 92be57968f146070d255c7dc51062dbc54ba8333 Mon Sep 17 00:00:00 2001 From: opeledtremor <83907615+opeledtremor@users.noreply.github.com> Date: Thu, 1 Jul 2021 15:04:53 +0300 Subject: [PATCH 1228/1476] Unruly Bid Adapter: consolidated adapter with RhythmOne (#7073) * Unruly and RhythmOne consolidated adapter - first commit * Finished the request and bid validation side * removed the user sync since now the backend is responsible for that. * Unruly and RhythmOne consolidated adapter - second commit * Finished handling the response now the adapter supports banner/instream/outstream * Unruly and RhythmOne consolidated adapter - third commit * fixed the unit tests for the outstream response part (still missing new unit tests for instream and banner) * Unruly and RhythmOne consolidated adapter - * added new unit tests for instream and banner * Unruly and RhythmOne consolidated adapter * changed the endpoint to the new endpoint * unitTests indentations * Unruly and RhythmOne consolidated adapter * Added missing semicolons * Unruly and RhythmOne consolidated adapter * fixes after code review * Unruly and RhythmOne consolidated adapter * added the ability to overwrite the end point for testing reasons. * removed the floorInfo object and changed it to floor * added more unit tests * added unmissable configuration to md file * Unruly and RhythmOne consolidated adapter * added check in instream if the response has vastUrl or vastXml + unit test * Unruly and RhythmOne consolidated adapter * Updated the md file to product request * Unruly and RhythmOne consolidated adapter - * added https for the endpoint as requested from prebid reviewer. Co-authored-by: Tal Lavon --- modules/unrulyBidAdapter.js | 294 +++++--- modules/unrulyBidAdapter.md | 93 ++- test/spec/modules/unrulyBidAdapter_spec.js | 790 ++++++++++++++++++--- 3 files changed, 950 insertions(+), 227 deletions(-) diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index 36a156d5c66..d04192acf15 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -1,137 +1,225 @@ import * as utils from '../src/utils.js' -import { Renderer } from '../src/Renderer.js' -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { VIDEO } from '../src/mediaTypes.js' +import {Renderer} from '../src/Renderer.js' +import {registerBidder} from '../src/adapters/bidderFactory.js' +import {VIDEO, BANNER} from '../src/mediaTypes.js' -function configureUniversalTag (exchangeRenderer) { - if (!exchangeRenderer.config) throw new Error('UnrulyBidAdapter: Missing renderer config.') - if (!exchangeRenderer.config.siteId) throw new Error('UnrulyBidAdapter: Missing renderer siteId.') +function configureUniversalTag(exchangeRenderer, requestId) { + if (!exchangeRenderer.config) throw new Error('UnrulyBidAdapter: Missing renderer config.'); + if (!exchangeRenderer.config.siteId) throw new Error('UnrulyBidAdapter: Missing renderer siteId.'); parent.window.unruly = parent.window.unruly || {}; parent.window.unruly['native'] = parent.window.unruly['native'] || {}; parent.window.unruly['native'].siteId = parent.window.unruly['native'].siteId || exchangeRenderer.config.siteId; + parent.window.unruly['native'].adSlotId = requestId; parent.window.unruly['native'].supplyMode = 'prebid'; } -function configureRendererQueue () { +function configureRendererQueue() { parent.window.unruly['native'].prebid = parent.window.unruly['native'].prebid || {}; parent.window.unruly['native'].prebid.uq = parent.window.unruly['native'].prebid.uq || []; } -function notifyRenderer (bidResponseBid) { +function notifyRenderer(bidResponseBid) { parent.window.unruly['native'].prebid.uq.push(['render', bidResponseBid]); } -const serverResponseToBid = (bid, rendererInstance) => ({ - requestId: bid.bidId, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - vastUrl: bid.vastUrl, - netRevenue: true, - creativeId: bid.bidId, - ttl: 360, - meta: { advertiserDomains: bid && bid.adomain ? bid.adomain : [] }, - currency: 'USD', - renderer: rendererInstance, - mediaType: VIDEO -}); - -const buildPrebidResponseAndInstallRenderer = bids => - bids - .filter(serverBid => { - const hasConfig = !!utils.deepAccess(serverBid, 'ext.renderer.config'); - const hasSiteId = !!utils.deepAccess(serverBid, 'ext.renderer.config.siteId'); - - if (!hasConfig) utils.logError(new Error('UnrulyBidAdapter: Missing renderer config.')); - if (!hasSiteId) utils.logError(new Error('UnrulyBidAdapter: Missing renderer siteId.')); - - return hasSiteId - }) - .map(serverBid => { - const exchangeRenderer = utils.deepAccess(serverBid, 'ext.renderer'); - - configureUniversalTag(exchangeRenderer); - configureRendererQueue(); - - const rendererInstance = Renderer.install(Object.assign({}, exchangeRenderer, { callback: () => {} })); - return { rendererInstance, serverBid }; - }) - .map( - ({rendererInstance, serverBid}) => { - const prebidBid = serverResponseToBid(serverBid, rendererInstance); - - const rendererConfig = Object.assign( - {}, - prebidBid, - { - renderer: rendererInstance, - adUnitCode: serverBid.ext.adUnitCode - } - ); - - rendererInstance.setRender(() => { notifyRenderer(rendererConfig) }); - - return prebidBid; +const addBidFloorInfo = (validBid) => { + Object.keys(validBid.mediaTypes).forEach((key) => { + let floor; + if (typeof validBid.getFloor === 'function') { + floor = validBid.getFloor({ + currency: 'USD', + mediaType: key, + size: '*' + }).floor || 0; + } else { + floor = validBid.params.floor || 0; + } + + validBid.mediaTypes[key].floor = floor; + }); +}; + +const RemoveDuplicateSizes = (validBid) => { + let bannerMediaType = utils.deepAccess(validBid, 'mediaTypes.banner'); + if (bannerMediaType) { + let seenSizes = {}; + let newSizesArray = []; + bannerMediaType.sizes.forEach((size) => { + if (!seenSizes[size.toString()]) { + seenSizes[size.toString()] = true; + newSizesArray.push(size); } - ); + }); -export const adapter = { - code: 'unruly', - supportedMediaTypes: [ VIDEO ], - isBidRequestValid: function(bid) { - if (!bid) return false; + bannerMediaType.sizes = newSizesArray; + } +}; - const context = utils.deepAccess(bid, 'mediaTypes.video.context'); +const getRequests = (conf, validBidRequests, bidderRequest) => { + const {bids, bidderRequestId, auctionId, bidderCode, ...bidderRequestData} = bidderRequest; + const invalidBidsCount = bidderRequest.bids.length - validBidRequests.length; + let requestBySiteId = {}; - return bid.mediaType === 'video' || context === 'outstream'; - }, + validBidRequests.forEach((validBid) => { + const currSiteId = validBid.params.siteId; + addBidFloorInfo(validBid); + RemoveDuplicateSizes(validBid); + requestBySiteId[currSiteId] = requestBySiteId[currSiteId] || []; + requestBySiteId[currSiteId].push(validBid); + }); - buildRequests: function(validBidRequests, bidderRequest) { - const url = 'https://targeting.unrulymedia.com/prebid'; - const method = 'POST'; - const data = { - bidRequests: validBidRequests, - bidderRequest - }; - const options = { contentType: 'text/plain' }; + let request = []; - return { - url, - method, - data, - options + Object.keys(requestBySiteId).forEach((key) => { + let data = { + bidderRequest: Object.assign({}, {bids: requestBySiteId[key], invalidBidsCount, ...bidderRequestData}) }; + + request.push(Object.assign({}, {data, ...conf})); + }); + + return request; +}; + +const handleBidResponseByMediaType = (bids) => { + let bidResponses = []; + + bids.forEach((bid) => { + let parsedBidResponse; + let bidMediaType = utils.deepAccess(bid, 'meta.mediaType'); + if (bidMediaType && bidMediaType.toLowerCase() === 'banner') { + bid.mediaType = BANNER; + parsedBidResponse = handleBannerBid(bid); + } else if (bidMediaType && bidMediaType.toLowerCase() === 'video') { + let context = utils.deepAccess(bid, 'meta.videoContext'); + bid.mediaType = VIDEO; + if (context === 'instream') { + parsedBidResponse = handleInStreamBid(bid); + } else if (context === 'outstream') { + parsedBidResponse = handleOutStreamBid(bid); + } + } + + if (parsedBidResponse) { + bidResponses.push(parsedBidResponse); + } + }); + + return bidResponses; +}; + +const handleBannerBid = (bid) => { + if (!bid.ad) { + utils.logError(new Error('UnrulyBidAdapter: Missing ad config.')); + return; + } + + return bid; +}; + +const handleInStreamBid = (bid) => { + if (!(bid.vastUrl || bid.vastXml)) { + utils.logError(new Error('UnrulyBidAdapter: Missing vastUrl or vastXml config.')); + return; + } + + return bid; +}; + +const handleOutStreamBid = (bid) => { + const hasConfig = !!utils.deepAccess(bid, 'ext.renderer.config'); + const hasSiteId = !!utils.deepAccess(bid, 'ext.renderer.config.siteId'); + + if (!hasConfig) { + utils.logError(new Error('UnrulyBidAdapter: Missing renderer config.')); + return; + } + if (!hasSiteId) { + utils.logError(new Error('UnrulyBidAdapter: Missing renderer siteId.')); + return; + } + + const exchangeRenderer = utils.deepAccess(bid, 'ext.renderer'); + + configureUniversalTag(exchangeRenderer, bid.requestId); + configureRendererQueue(); + + const rendererInstance = Renderer.install(Object.assign({}, exchangeRenderer)); + + const rendererConfig = Object.assign( + {}, + bid, + { + renderer: rendererInstance, + adUnitCode: utils.deepAccess(bid, 'ext.adUnitCode') + } + ); + + rendererInstance.setRender(() => { + notifyRenderer(rendererConfig) + }); + + bid.renderer = bid.renderer || rendererInstance; + return bid; +}; + +const isMediaTypesValid = (bid) => { + const mediaTypeVideoData = utils.deepAccess(bid, 'mediaTypes.video'); + const mediaTypeBannerData = utils.deepAccess(bid, 'mediaTypes.banner'); + let isValid = !!(mediaTypeVideoData || mediaTypeBannerData); + if (isValid && mediaTypeVideoData) { + isValid = isVideoMediaTypeValid(mediaTypeVideoData); + } + if (isValid && mediaTypeBannerData) { + isValid = isBannerMediaTypeValid(mediaTypeBannerData); + } + return isValid; +}; + +const isVideoMediaTypeValid = (mediaTypeVideoData) => { + if (!mediaTypeVideoData.context) { + return false; + } + + const supportedContexts = ['outstream', 'instream']; + return supportedContexts.indexOf(mediaTypeVideoData.context) !== -1; +}; + +const isBannerMediaTypeValid = (mediaTypeBannerData) => { + return mediaTypeBannerData.sizes; +}; + +export const adapter = { + code: 'unruly', + supportedMediaTypes: [VIDEO, BANNER], + isBidRequestValid: function (bid) { + let siteId = utils.deepAccess(bid, 'params.siteId'); + let isBidValid = siteId && isMediaTypesValid(bid); + return !!isBidValid; + }, + + buildRequests: function (validBidRequests, bidderRequest) { + let endPoint = 'https://targeting.unrulymedia.com/unruly_prebid'; + if (validBidRequests[0]) { + endPoint = utils.deepAccess(validBidRequests[0], 'params.endpoint') || endPoint; + } + + const url = endPoint; + const method = 'POST'; + const options = {contentType: 'application/json'}; + return getRequests({url, method, options}, validBidRequests, bidderRequest); }, - interpretResponse: function(serverResponse = {}) { + interpretResponse: function (serverResponse = {}) { const serverResponseBody = serverResponse.body; + const noBidsResponse = []; const isInvalidResponse = !serverResponseBody || !serverResponseBody.bids; return isInvalidResponse ? noBidsResponse - : buildPrebidResponseAndInstallRenderer(serverResponseBody.bids); - }, - - getUserSyncs: function(syncOptions, response, gdprConsent) { - let params = ''; - if (gdprConsent && 'gdprApplies' in gdprConsent) { - if (gdprConsent.gdprApplies && typeof gdprConsent.consentString === 'string') { - params += `?gdpr=1&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr=0`; - } - } - - const syncs = [] - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' + params - }); - } - return syncs; + : handleBidResponseByMediaType(serverResponseBody.bids); } }; diff --git a/modules/unrulyBidAdapter.md b/modules/unrulyBidAdapter.md index fc3c6c264be..5bfa09bb646 100644 --- a/modules/unrulyBidAdapter.md +++ b/modules/unrulyBidAdapter.md @@ -2,7 +2,7 @@ **Module Name**: Unruly Bid Adapter **Module Type**: Bidder Adapter -**Maintainer**: prodev@unrulymedia.com +**Maintainer**: prebidsupport@unrulygroup.com # Description @@ -11,21 +11,78 @@ Module that connects to UnrulyX for bids. # Test Parameters ```js - const adUnits = [{ - code: 'ad-slot', - sizes: [[728, 90], [300, 250]], - mediaTypes: { - video: { - context: 'outstream' - } - }, - bids: [{ - bidder: 'unruly', - params: { - targetingUUID: '6f15e139-5f18-49a1-b52f-87e5e69ee65e', - siteId: 1081534 - } - } - ] - }]; +const adUnits = + [ + { + "code": "outstream-ad", + "mediaTypes": { + "video": { + "context": "outstream", + "playerSize": [[640, 480]] + } + }, + "bids": [ + { + "bidder": "unruly", + "params": { + "siteId": 1081534 + } + } + ] + }, + { + "code": "unmissable-ad", + "mediaTypes": { + "video": { + "context": "outstream", + "playerSize": [[640, 480]] + } + }, + "bids": [ + { + "bidder": "unruly", + "params": { + "siteId": 1081534, + "featureOverrides": { + "canRunUnmissable": true + } + } + } + ] + }, + { + "code": "banner-ad", + "mediaTypes": { + "banner": { + "sizes": [[300, 250]] + } + }, + "bids": [ + { + "bidder": "unruly", + "params": { + "siteId": 1081534 + } + } + ] + }, + { + "code": "instream-ad", + "mediaTypes": { + "video": { + "context": "instream", + "mimes": ["video/mp4"], + "playerSize": [[640, 480]] + } + }, + "bids": [ + { + "bidder": "unruly", + "params": { + "siteId": 1081534 + } + } + ] + } + ]; ``` diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index b087ba042a9..6d1d8f9949f 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -1,16 +1,15 @@ /* globals describe, it, beforeEach, afterEach, sinon */ -import { expect } from 'chai' +import {expect} from 'chai' import * as utils from 'src/utils.js' -import { STATUS } from 'src/constants.json' -import { VIDEO } from 'src/mediaTypes.js' -import { Renderer } from 'src/Renderer.js' -import { adapter } from 'modules/unrulyBidAdapter.js' +import {VIDEO, BANNER} from 'src/mediaTypes.js' +import {Renderer} from 'src/Renderer.js' +import {adapter} from 'modules/unrulyBidAdapter.js' describe('UnrulyAdapter', function () { function createOutStreamExchangeBid({ adUnitCode = 'placement2', statusCode = 1, - bidId = 'foo', + requestId = 'foo', vastUrl = 'https://targeting.unrulymedia.com/in_article?uuid=74544e00-d43b-4f3a-a799-69d22ce979ce&supported_mime_type=application/javascript&supported_mime_type=video/mp4&tj=%7B%22site%22%3A%7B%22lang%22%3A%22en-GB%22%2C%22ref%22%3A%22%22%2C%22page%22%3A%22https%3A%2F%2Fdemo.unrulymedia.com%2FinArticle%2Finarticle_nypost_upbeat%2Ftravel_magazines.html%22%2C%22domain%22%3A%22demo.unrulymedia.com%22%7D%2C%22user%22%3A%7B%22profile%22%3A%7B%22quantcast%22%3A%7B%22segments%22%3A%5B%7B%22id%22%3A%22D%22%7D%2C%7B%22id%22%3A%22T%22%7D%5D%7D%7D%7D%7D&video_width=618&video_height=347' }) { return { @@ -30,15 +29,70 @@ describe('UnrulyAdapter', function () { 'bidderCode': 'unruly', 'width': 323, 'vastUrl': vastUrl, - 'bidId': bidId, - 'height': 323 + 'requestId': requestId, + 'creativeId': requestId, + 'height': 323, + 'netRevenue': true, + 'ttl': 360, + 'currency': 'USD', + 'meta': { + 'mediaType': 'video', + 'videoContext': 'outstream' + } } } const createExchangeResponse = (...bids) => ({ - body: { bids } + body: {bids} }); + const inStreamServerResponse = { + 'requestId': '262594d5d1f8104', + 'cpm': 0.3825, + 'currency': 'USD', + 'width': 640, + 'height': 480, + 'creativeId': 'cr-test-video-3', + 'netRevenue': true, + 'ttl': 350, + 'vastUrl': 'https://adserve.rhythmxchange.dvl/rtbtest/nurlvast?event=impnurl&doc_type=testad&doc_version=2&crid=cr-test-video-3&ssp=2057&pubid=545454&placementid=1052819&oppid=b516bc57-0475-4377-bdc6-369c44b31d46&mediatype=site&attempt_ts=1622740567081&extra=1', + 'meta': { + 'mediaType': 'video', + 'videoContext': 'instream' + } + }; + + const inStreamServerResponseWithVastXml = { + 'requestId': '262594d5d1f8104', + 'cpm': 0.3825, + 'currency': 'USD', + 'width': 640, + 'height': 480, + 'creativeId': 'cr-test-video-3', + 'netRevenue': true, + 'ttl': 350, + 'vastXml': 'https://adserve.rhythmxchange.dvl/rtbtest/nurlvast?event=impnurl&doc_type=testad&doc_version=2&crid=cr-test-video-3&ssp=2057&pubid=545454&placementid=1052819&oppid=b516bc57-0475-4377-bdc6-369c44b31d46&mediatype=site&attempt_ts=1622740567081&extra=1', + 'meta': { + 'mediaType': 'video', + 'videoContext': 'instream' + } + }; + + const bannerServerResponse = { + 'requestId': '2de3a9047fa9c6', + 'cpm': 5.34, + 'currency': 'USD', + 'width': 300, + 'height': 250, + 'creativeId': 'cr-test-banner-1', + 'netRevenue': true, + 'ttl': 350, + 'ad': "", + 'meta': { + 'mediaType': 'banner' + } + }; + let sandbox; let fakeRenderer; @@ -63,65 +117,547 @@ describe('UnrulyAdapter', function () { }); it('should contain the VIDEO mediaType', function () { - expect(adapter.supportedMediaTypes).to.deep.equal([ VIDEO ]) + expect(adapter.supportedMediaTypes).to.deep.equal([VIDEO, BANNER]) }); describe('isBidRequestValid', function () { it('should be a function', function () { expect(typeof adapter.isBidRequestValid).to.equal('function') }); - - it('should return false if bid is falsey', function () { + it('should return false if bid is false', function () { expect(adapter.isBidRequestValid()).to.be.false; }); - - it('should return true if bid.mediaType is "video"', function () { - const mockBid = { mediaType: 'video' }; - + it('should return true if bid.mediaType is "banner"', function () { + const mockBid = { + mediaTypes: { + banner: { + sizes: [ + [600, 500], + [300, 250] + ] + } + }, + params: { + siteId: 233261 + } + }; expect(adapter.isBidRequestValid(mockBid)).to.be.true; }); - it('should return true if bid.mediaTypes.video.context is "outstream"', function () { const mockBid = { mediaTypes: { video: { - context: 'outstream' + context: 'outstream', + mimes: ['video/mp4'], + playerSize: [[640, 480]] } + }, + params: { + siteId: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.true; + }); + it('should return true if bid.mediaTypes.video.context is "instream"', function () { + const mockBid = { + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mp4'], + playerSize: [[640, 480]] + } + }, + params: { + siteId: 233261 } }; - expect(adapter.isBidRequestValid(mockBid)).to.be.true; }); + it('should return false if bid.mediaTypes.video.context is not "instream" or "outstream"', function () { + const mockBid = { + mediaTypes: { + video: { + context: 'context', + mimes: ['video/mp4'], + playerSize: [[640, 480]] + } + }, + params: { + siteId: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.false; + }); + it('should return false if bid.mediaTypes.video.context not exist', function () { + const mockBid = { + mediaTypes: { + video: { + mimes: ['video/mp4'], + playerSize: [[640, 480]] + } + }, + params: { + siteId: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.false; + }); + it('should return false if bid.mediaType is not "video" or "banner"', function () { + const mockBid = { + mediaTypes: { + native: { + image: { + sizes: [300, 250] + } + } + }, + params: { + siteId: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.false; + }); + it('should return false if bid.mediaTypes is empty', function () { + const mockBid = { + mediaTypes: {}, + params: { + siteId: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.false; + }); + it('should return false if bid.params.siteId not exist', function () { + const mockBid = { + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'], + playerSize: [[640, 480]] + } + }, + params: { + targetingUUID: 233261 + } + }; + expect(adapter.isBidRequestValid(mockBid)).to.be.false; + }); }); describe('buildRequests', function () { + let mockBidRequests; + beforeEach(function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ] + }; + }); + it('should be a function', function () { expect(typeof adapter.buildRequests).to.equal('function'); }); it('should return an object', function () { - const mockBidRequests = ['mockBid']; - expect(typeof adapter.buildRequests(mockBidRequests)).to.equal('object') + // const mockBidRequests = ['mockBid']; + expect(typeof adapter.buildRequests(mockBidRequests.bids, mockBidRequests)).to.equal('object') + }); + it('should return an array with 2 items when the bids has different siteId\'s', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + }, + { + 'bidder': 'unruly', + 'params': { + 'siteId': 2234554, + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ] + }; + + let result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); + expect(typeof result).to.equal('object'); + expect(result.length).to.equal(2); + expect(result[0].data.bidderRequest.bids.length).to.equal(1); + expect(result[1].data.bidderRequest.bids.length).to.equal(1); + }); + it('should return an array with 1 items when the bids has same siteId', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + }, + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ] + }; + + let result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); + expect(typeof result).to.equal('object'); + expect(result.length).to.equal(1); + expect(result[0].data.bidderRequest.bids.length).to.equal(2); }); it('should return a server request with a valid exchange url', function () { - const mockBidRequests = ['mockBid']; - expect(adapter.buildRequests(mockBidRequests).url).to.equal('https://targeting.unrulymedia.com/prebid') + expect(adapter.buildRequests(mockBidRequests.bids, mockBidRequests)[0].url).to.equal('https://targeting.unrulymedia.com/unruly_prebid') + }); + it('should return a server request with a the end point url instead of the exchange url', function () { + mockBidRequests.bids[0].params.endpoint = '//testendpoint.com'; + expect(adapter.buildRequests(mockBidRequests.bids, mockBidRequests)[0].url).to.equal('//testendpoint.com'); }); it('should return a server request with method === POST', function () { - const mockBidRequests = ['mockBid']; - expect(adapter.buildRequests(mockBidRequests).method).to.equal('POST'); + expect(adapter.buildRequests(mockBidRequests.bids, mockBidRequests)[0].method).to.equal('POST'); }); - it('should ensure contentType is `text/plain`', function () { - const mockBidRequests = ['mockBid']; - expect(adapter.buildRequests(mockBidRequests).options).to.deep.equal({ - contentType: 'text/plain' + it('should ensure contentType is `application/json`', function () { + expect(adapter.buildRequests(mockBidRequests.bids, mockBidRequests)[0].options).to.deep.equal({ + contentType: 'application/json' }); }); it('should return a server request with valid payload', function () { - const mockBidRequests = ['mockBid']; - const mockBidderRequest = {bidderCode: 'mockBidder'}; - expect(adapter.buildRequests(mockBidRequests, mockBidderRequest).data) - .to.deep.equal({bidRequests: mockBidRequests, bidderRequest: mockBidderRequest}) - }) + const expectedResult = { + bidderRequest: { + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261 + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'mimes': [ + 'video/mp4' + ], + 'playerSize': [ + [ + 640, + 480 + ] + ], + 'floor': 0 + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'sizes': [ + [ + 640, + 480 + ] + ], + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b' + } + ], + 'invalidBidsCount': 0 + } + }; + + expect(adapter.buildRequests(mockBidRequests.bids, mockBidRequests)[0].data).to.deep.equal(expectedResult) + }); + it('should return request and remove the duplicate sizes', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 640, + 480 + ], + [ + 640, + 480 + ], + [ + 300, + 250 + ], + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ] + }; + + const expectedResult = { + bidderRequest: { + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 640, + 480 + ], + [ + 300, + 250 + ] + ], + 'floor': 0 + } + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ], + 'invalidBidsCount': 0 + } + }; + + let result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); + expect(result[0].data).to.deep.equal(expectedResult); + }); + + it('should return have the floor value from the bid', function () { + mockBidRequests = { + 'bidderCode': 'unruly', + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261, + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 640, + 480 + ] + ] + } + }, + 'floors': { + 'enforceFloors': true, + 'currency': 'USD', + 'schema': { + 'fields': [ + 'mediaType' + ] + }, + 'values': { + 'banner': 3 + }, + }, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ] + }; + + const getFloor = (data) => { + return {floor: 3} + }; + + mockBidRequests.bids[0].getFloor = getFloor; + + const expectedResult = { + bidderRequest: { + 'bids': [ + { + 'bidder': 'unruly', + 'params': { + 'siteId': 233261 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 640, + 480 + ] + ], + 'floor': 3 + } + }, + 'floors': { + 'enforceFloors': true, + 'currency': 'USD', + 'schema': { + 'fields': [ + 'mediaType' + ] + }, + 'values': { + 'banner': 3 + }, + }, + getFloor: getFloor, + 'adUnitCode': 'video2', + 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', + 'bidId': '27a3ee1626a5c7', + 'bidderRequestId': '12e00d17dff07b', + } + ], + 'invalidBidsCount': 0 + } + }; + + let result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); + expect(result[0].data).to.deep.equal(expectedResult); + }); }); describe('interpretResponse', function () { @@ -132,15 +668,28 @@ describe('UnrulyAdapter', function () { expect(adapter.interpretResponse()).to.deep.equal([]); }); it('should return [] when serverResponse has no bids', function () { - const mockServerResponse = { body: { bids: [] } }; + const mockServerResponse = {body: {bids: []}}; expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]) }); it('should return array of bids when receive a successful response from server', function () { - const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); const mockServerResponse = createExchangeResponse(mockExchangeBid); expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([ { + 'ext': { + 'statusCode': 1, + 'renderer': { + 'id': 'unruly_inarticle', + 'config': { + 'siteId': 123456, + 'targetingUUID': 'xxx-yyy-zzz' + }, + 'url': 'https://video.unrulymedia.com/native/prebid-loader.js' + }, + 'adUnitCode': 'video1' + }, requestId: 'mockBidId', + bidderCode: 'unruly', cpm: 20, width: 323, height: 323, @@ -148,7 +697,10 @@ describe('UnrulyAdapter', function () { netRevenue: true, creativeId: 'mockBidId', ttl: 360, - meta: { advertiserDomains: [] }, + 'meta': { + 'mediaType': 'video', + 'videoContext': 'outstream' + }, currency: 'USD', renderer: fakeRenderer, mediaType: 'video' @@ -160,7 +712,7 @@ describe('UnrulyAdapter', function () { expect(Renderer.install.called).to.be.false; expect(fakeRenderer.setRender.called).to.be.false; - const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); const mockRenderer = { url: 'value: mockRendererURL', config: { @@ -176,7 +728,7 @@ describe('UnrulyAdapter', function () { expect(Renderer.install.calledOnce).to.be.true; sinon.assert.calledWithExactly( Renderer.install, - Object.assign({}, mockRenderer, {callback: sinon.match.func}) + Object.assign({}, mockRenderer) ); sinon.assert.calledOnce(fakeRenderer.setRender); @@ -184,12 +736,12 @@ describe('UnrulyAdapter', function () { }); it('should return [] and log if bidResponse renderer config is not available', function () { - sinon.assert.notCalled(utils.logError) + sinon.assert.notCalled(utils.logError); expect(Renderer.install.called).to.be.false; expect(fakeRenderer.setRender.called).to.be.false; - const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); const mockRenderer = { url: 'value: mockRendererURL' }; @@ -199,24 +751,21 @@ describe('UnrulyAdapter', function () { expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); const logErrorCalls = utils.logError.getCalls(); - expect(logErrorCalls.length).to.equal(2); + expect(logErrorCalls.length).to.equal(1); - const [ configErrorCall, siteIdErrorCall ] = logErrorCalls; + const [configErrorCall] = logErrorCalls; expect(configErrorCall.args.length).to.equal(1); expect(configErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer config.'); - - expect(siteIdErrorCall.args.length).to.equal(1); - expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); }); it('should return [] and log if siteId is not available', function () { - sinon.assert.notCalled(utils.logError) + sinon.assert.notCalled(utils.logError); expect(Renderer.install.called).to.be.false; expect(fakeRenderer.setRender.called).to.be.false; - const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); const mockRenderer = { url: 'value: mockRendererURL', config: {} @@ -229,14 +778,14 @@ describe('UnrulyAdapter', function () { const logErrorCalls = utils.logError.getCalls(); expect(logErrorCalls.length).to.equal(1); - const [ siteIdErrorCall ] = logErrorCalls; + const [siteIdErrorCall] = logErrorCalls; expect(siteIdErrorCall.args.length).to.equal(1); expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); }); it('bid is placed on the bid queue when render is called', function () { - const exchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video', vastUrl: 'value: vastUrl' }); + const exchangeBid = createOutStreamExchangeBid({adUnitCode: 'video', vastUrl: 'value: vastUrl'}); const exchangeResponse = createExchangeResponse(exchangeBid); adapter.interpretResponse(exchangeResponse); @@ -256,7 +805,7 @@ describe('UnrulyAdapter', function () { }); it('should ensure that renderer is placed in Prebid supply mode', function () { - const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); const mockServerResponse = createExchangeResponse(mockExchangeBid); expect('unruly' in window.parent).to.equal(false); @@ -267,65 +816,94 @@ describe('UnrulyAdapter', function () { expect(supplyMode).to.equal('prebid'); }); - }); - describe('getUserSyncs', () => { - it('should push user sync iframe if enabled', () => { - const mockConsent = {} - const response = {} - const syncOptions = { iframeEnabled: true } - const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) - expect(syncs[0]).to.deep.equal({ - type: 'iframe', - url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' - }); - }) + it('should return correct response when ad type is instream with vastUrl', function () { + const mockServerResponse = createExchangeResponse(inStreamServerResponse); + const expectedResponse = inStreamServerResponse; + expectedResponse.mediaType = 'video'; - it('should not push user sync iframe if not enabled', () => { - const mockConsent = {} - const response = {} - const syncOptions = { iframeEnabled: false } - const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent); - expect(syncs).to.be.empty; + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([expectedResponse]); }); - }); - it('should not append consent params if gdpr does not apply', () => { - const mockConsent = {} - const response = {} - const syncOptions = { iframeEnabled: true } - const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) - expect(syncs[0]).to.deep.equal({ - type: 'iframe', - url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' - }) - }); + it('should return correct response when ad type is instream with vastXml', function () { + const mockServerResponse = {...createExchangeResponse(inStreamServerResponseWithVastXml)}; + const expectedResponse = inStreamServerResponseWithVastXml; + expectedResponse.mediaType = 'video'; - it('should append consent params if gdpr does apply and consent is given', () => { - const mockConsent = { - gdprApplies: true, - consentString: 'hello' - }; - const response = {} - const syncOptions = { iframeEnabled: true } - const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) - expect(syncs[0]).to.deep.equal({ - type: 'iframe', - url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html?gdpr=1&gdpr_consent=hello' - }) - }); + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([expectedResponse]); + }); - it('should append consent param if gdpr applies and no consent is given', () => { - const mockConsent = { - gdprApplies: true, - consentString: {} - }; - const response = {}; - const syncOptions = { iframeEnabled: true } - const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) - expect(syncs[0]).to.deep.equal({ - type: 'iframe', - url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html?gdpr=0' - }) - }) + it('should return [] and log if no vastUrl in instream response', function () { + const {vastUrl, ...inStreamServerResponseNoVast} = inStreamServerResponse; + const mockServerResponse = createExchangeResponse(inStreamServerResponseNoVast); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + + expect(logErrorCalls.length).to.equal(1); + + const [siteIdErrorCall] = logErrorCalls; + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing vastUrl or vastXml config.'); + }); + + it('should return correct response when ad type is banner', function () { + const mockServerResponse = createExchangeResponse(bannerServerResponse); + const expectedResponse = bannerServerResponse; + expectedResponse.mediaType = 'banner'; + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([expectedResponse]); + }); + + it('should return [] and log if no ad in banner response', function () { + const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; + const mockServerResponse = createExchangeResponse(bannerServerResponseNoAd); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + + expect(logErrorCalls.length).to.equal(1); + + const [siteIdErrorCall] = logErrorCalls; + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing ad config.'); + }); + + it('should return correct response for multiple bids', function () { + const outStreamServerResponse = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockServerResponse = createExchangeResponse(outStreamServerResponse, inStreamServerResponse, bannerServerResponse); + const expectedOutStreamResponse = outStreamServerResponse; + expectedOutStreamResponse.mediaType = 'video'; + + const expectedInStreamResponse = inStreamServerResponse; + expectedInStreamResponse.mediaType = 'video'; + + const expectedBannerResponse = bannerServerResponse; + expectedBannerResponse.mediaType = 'banner'; + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([expectedOutStreamResponse, expectedInStreamResponse, expectedBannerResponse]); + }); + + it('should return only valid bids', function () { + const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; + const mockServerResponse = createExchangeResponse(bannerServerResponseNoAd, inStreamServerResponse); + const expectedInStreamResponse = inStreamServerResponse; + expectedInStreamResponse.mediaType = 'video'; + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([expectedInStreamResponse]); + + const logErrorCalls = utils.logError.getCalls(); + + expect(logErrorCalls.length).to.equal(1); + + const [siteIdErrorCall] = logErrorCalls; + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing ad config.'); + }); + }); }); From abcf465e792f5acb6f22adbafca656d495f8badb Mon Sep 17 00:00:00 2001 From: jsut Date: Thu, 1 Jul 2021 09:50:15 -0400 Subject: [PATCH 1229/1476] Add AD_RENDER_SUCCEEDED event (#7059) - this enables interaction with the just rendered ad, as well as additional opportunities for measuring the speed of rendering code --- src/AnalyticsAdapter.js | 2 ++ src/constants.json | 1 + src/prebid.js | 13 ++++++++++++- test/spec/AnalyticsAdapter_spec.js | 12 ++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/AnalyticsAdapter.js b/src/AnalyticsAdapter.js index 80c12a3eb8e..97513b80cc7 100644 --- a/src/AnalyticsAdapter.js +++ b/src/AnalyticsAdapter.js @@ -18,6 +18,7 @@ const { BIDDER_DONE, SET_TARGETING, AD_RENDER_FAILED, + AD_RENDER_SUCCEEDED, AUCTION_DEBUG, ADD_AD_UNITS } @@ -113,6 +114,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler } [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 }), [AUCTION_INIT]: args => { diff --git a/src/constants.json b/src/constants.json index c6fad4df3c6..77d87e056d9 100644 --- a/src/constants.json +++ b/src/constants.json @@ -37,6 +37,7 @@ "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", "AD_RENDER_FAILED": "adRenderFailed", + "AD_RENDER_SUCCEEDED": "adRenderSucceeded", "TCF2_ENFORCEMENT": "tcf2Enforcement", "AUCTION_DEBUG": "auctionDebug", "BID_VIEWABLE": "bidViewable", diff --git a/src/prebid.js b/src/prebid.js index e65d2530943..203ca05bf01 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events.js'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, STALE_RENDER } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -391,6 +391,14 @@ function emitAdRenderFail({ reason, message, bid, id }) { events.emit(AD_RENDER_FAILED, data); } +function emitAdRenderSucceeded({ doc, bid, id }) { + const data = { doc }; + if (bid) data.bid = bid; + if (id) data.adId = id; + + events.emit(AD_RENDER_SUCCEEDED, data); +} + /** * This function will render the ad (based on params) in the given iframe document passed through. * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously @@ -442,6 +450,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { if (isRendererRequired(renderer)) { executeRenderer(renderer, bid); + emitAdRenderSucceeded({ doc, bid, id }); } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; emitAdRenderFail({reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id}); @@ -460,6 +469,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { doc.close(); setRenderSize(doc, width, height); utils.callBurl(bid); + emitAdRenderSucceeded({ doc, bid, id }); } else if (adUrl) { const iframe = utils.createInvisibleIframe(); iframe.height = height; @@ -471,6 +481,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { utils.insertElement(iframe, doc, 'body'); setRenderSize(doc, width, height); utils.callBurl(bid); + emitAdRenderSucceeded({ doc, bid, id }); } else { const message = `Error trying to write ad. No ad for bid response id: ${id}`; emitAdRenderFail({reason: NO_AD, message, bid, id}); diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 2b36848bd8f..a4acb6fc414 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -9,6 +9,7 @@ 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; @@ -86,6 +87,17 @@ FEATURE: Analytics Adapters API 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' }; From 86489e19b201ab0a31f91ada7f42e163fea5b51e Mon Sep 17 00:00:00 2001 From: logicad Date: Thu, 1 Jul 2021 23:53:48 +0900 Subject: [PATCH 1230/1476] Logicad Bid Adapter: Add meta.advertiserDomains support (#7114) Co-authored-by: ytsuchiya --- modules/logicadBidAdapter.js | 69 ++++++ test/spec/modules/logicadBidAdapter_spec.js | 249 ++++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 modules/logicadBidAdapter.js create mode 100644 test/spec/modules/logicadBidAdapter_spec.js diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js new file mode 100644 index 00000000000..2c919f9c157 --- /dev/null +++ b/modules/logicadBidAdapter.js @@ -0,0 +1,69 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'logicad'; +const ENDPOINT_URL = 'https://pb.ladsp.com/adrequest/prebid'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], + isBidRequestValid: function (bid) { + return !!(bid.params && bid.params.tid); + }, + buildRequests: function (bidRequests, bidderRequest) { + const requests = []; + for (let i = 0, len = bidRequests.length; i < len; i++) { + const request = { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(newBidRequest(bidRequests[i], bidderRequest)), + options: {}, + bidderRequest + }; + requests.push(request); + } + return requests; + }, + interpretResponse: function (serverResponse, bidderRequest) { + serverResponse = serverResponse.body; + const bids = []; + if (!serverResponse || serverResponse.error) { + return bids; + } + serverResponse.seatbid.forEach(function (seatbid) { + bids.push(seatbid.bid); + }) + return bids; + }, + getUserSyncs: function (syncOptions, serverResponses) { + if (serverResponses.length > 0 && serverResponses[0].body.userSync && + syncOptions.pixelEnabled && serverResponses[0].body.userSync.type == 'image') { + return [{ + type: 'image', + url: serverResponses[0].body.userSync.url + }]; + } + return []; + }, +}; + +function newBidRequest(bid, bidderRequest) { + return { + auctionId: bid.auctionId, + bidderRequestId: bid.bidderRequestId, + bids: [{ + adUnitCode: bid.adUnitCode, + bidId: bid.bidId, + transactionId: bid.transactionId, + sizes: bid.sizes, + params: bid.params, + mediaTypes: bid.mediaTypes + }], + prebidJsVersion: '$prebid.version$', + referrer: bidderRequest.refererInfo.referer, + auctionStartTime: bidderRequest.auctionStart, + eids: bid.userIdAsEids, + }; +} + +registerBidder(spec); diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js new file mode 100644 index 00000000000..6abca6b2e98 --- /dev/null +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -0,0 +1,249 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/logicadBidAdapter.js'; +import * as utils from 'src/utils.js'; + +describe('LogicadAdapter', function () { + const bidRequests = [{ + bidder: 'logicad', + bidId: '51ef8751f9aead', + params: { + tid: 'PJ2P', + page: 'https://www.logicad.com/' + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + sizes: [[300, 250], [300, 600]], + bidderRequestId: '418b37f85e772c', + auctionId: '18fd8b8b0bd757', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + userId: { + sharedid: { + id: 'fakesharedid', + third: 'fakesharedid' + } + }, + userIdAsEids: [{ + source: 'sharedid.org', + uids: [{ + id: 'fakesharedid', + atype: 1, + ext: { + third: 'fakesharedid' + } + }] + }] + }]; + const nativeBidRequests = [{ + bidder: 'logicad', + bidId: '51ef8751f9aead', + params: { + tid: 'bgjD1', + page: 'https://www.logicad.com/' + }, + adUnitCode: 'div-gpt-ad-1460505748561-1', + transactionId: 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + sizes: [[1, 1]], + bidderRequestId: '418b37f85e772c', + auctionId: '18fd8b8b0bd757', + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + } + }, + userId: { + sharedid: { + id: 'fakesharedid', + third: 'fakesharedid' + } + }, + userIdAsEids: [{ + source: 'sharedid.org', + uids: [{ + id: 'fakesharedid', + atype: 1, + ext: { + third: 'fakesharedid' + } + }] + }] + }]; + const bidderRequest = { + refererInfo: { + referer: 'fakeReferer', + reachedTop: true, + numIframes: 1, + stack: [] + }, + auctionStart: 1563337198010 + }; + const serverResponse = { + body: { + seatbid: + [{ + bid: { + requestId: '51ef8751f9aead', + cpm: 101.0234, + width: 300, + height: 250, + creativeId: '2019', + currency: 'JPY', + netRevenue: true, + ttl: 60, + ad: '
TEST
', + meta: { + advertiserDomains: ['logicad.com'] + } + } + }], + userSync: { + type: 'image', + url: 'https://cr-p31.ladsp.jp/cookiesender/31' + } + } + }; + const nativeServerResponse = { + body: { + seatbid: + [{ + bid: { + requestId: '51ef8751f9aead', + cpm: 101.0234, + width: 1, + height: 1, + creativeId: '2019', + currency: 'JPY', + netRevenue: true, + ttl: 60, + native: { + clickUrl: 'https://www.logicad.com', + image: { + url: 'https://cd.ladsp.com/img.png', + width: '1200', + height: '628' + }, + impressionTrackers: [ + 'https://example.com' + ], + sponsoredBy: 'Logicad', + title: 'Native Creative', + }, + meta: { + advertiserDomains: ['logicad.com'] + } + } + }], + userSync: { + type: 'image', + url: 'https://cr-p31.ladsp.jp/cookiesender/31' + } + } + }; + + describe('isBidRequestValid', function () { + it('should return true if the tid parameter is present', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + }); + + it('should return false if the tid parameter is not present', function () { + let bidRequest = utils.deepClone(bidRequests[0]); + delete bidRequest.params.tid; + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }); + + it('should return false if the params object is not present', function () { + let bidRequest = utils.deepClone(bidRequests); + delete bidRequest[0].params; + expect(spec.isBidRequestValid(bidRequest)).to.be.false; + }); + + it('should return true if the tid parameter is present for native request', function () { + expect(spec.isBidRequestValid(nativeBidRequests[0])).to.be.true; + }); + }); + + describe('buildRequests', function () { + it('should generate a valid single POST request for multiple bid requests', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://pb.ladsp.com/adrequest/prebid'); + expect(request.data).to.exist; + + const data = JSON.parse(request.data); + expect(data.auctionId).to.equal('18fd8b8b0bd757'); + expect(data.eids[0].source).to.equal('sharedid.org'); + expect(data.eids[0].uids[0].id).to.equal('fakesharedid'); + }); + }); + + describe('interpretResponse', function () { + it('should return an empty array if an invalid response is passed', function () { + const interpretedResponse = spec.interpretResponse({}, {}); + expect(interpretedResponse).to.be.an('array').that.is.empty; + }); + + it('should return valid response when passed valid server response', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + const interpretedResponse = spec.interpretResponse(serverResponse, request); + + expect(interpretedResponse).to.have.lengthOf(1); + + expect(interpretedResponse[0].requestId).to.equal(serverResponse.body.seatbid[0].bid.requestId); + expect(interpretedResponse[0].cpm).to.equal(serverResponse.body.seatbid[0].bid.cpm); + expect(interpretedResponse[0].width).to.equal(serverResponse.body.seatbid[0].bid.width); + expect(interpretedResponse[0].height).to.equal(serverResponse.body.seatbid[0].bid.height); + expect(interpretedResponse[0].creativeId).to.equal(serverResponse.body.seatbid[0].bid.creativeId); + expect(interpretedResponse[0].currency).to.equal(serverResponse.body.seatbid[0].bid.currency); + expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.seatbid[0].bid.netRevenue); + expect(interpretedResponse[0].ad).to.equal(serverResponse.body.seatbid[0].bid.ad); + expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.seatbid[0].bid.ttl); + expect(interpretedResponse[0].meta.advertiserDomains).to.equal(serverResponse.body.seatbid[0].bid.meta.advertiserDomains); + + // native + const nativeRequest = spec.buildRequests(nativeBidRequests, bidderRequest)[0]; + const interpretedResponseForNative = spec.interpretResponse(nativeServerResponse, nativeRequest); + + expect(interpretedResponseForNative).to.have.lengthOf(1); + + expect(interpretedResponseForNative[0].requestId).to.equal(nativeServerResponse.body.seatbid[0].bid.requestId); + expect(interpretedResponseForNative[0].cpm).to.equal(nativeServerResponse.body.seatbid[0].bid.cpm); + expect(interpretedResponseForNative[0].width).to.equal(nativeServerResponse.body.seatbid[0].bid.width); + expect(interpretedResponseForNative[0].height).to.equal(nativeServerResponse.body.seatbid[0].bid.height); + expect(interpretedResponseForNative[0].creativeId).to.equal(nativeServerResponse.body.seatbid[0].bid.creativeId); + expect(interpretedResponseForNative[0].currency).to.equal(nativeServerResponse.body.seatbid[0].bid.currency); + expect(interpretedResponseForNative[0].netRevenue).to.equal(nativeServerResponse.body.seatbid[0].bid.netRevenue); + expect(interpretedResponseForNative[0].ttl).to.equal(nativeServerResponse.body.seatbid[0].bid.ttl); + expect(interpretedResponseForNative[0].native.clickUrl).to.equal(nativeServerResponse.body.seatbid[0].bid.native.clickUrl); + expect(interpretedResponseForNative[0].native.image.url).to.equal(nativeServerResponse.body.seatbid[0].bid.native.image.url); + expect(interpretedResponseForNative[0].native.image.width).to.equal(nativeServerResponse.body.seatbid[0].bid.native.image.width); + expect(interpretedResponseForNative[0].native.impressionTrackers).to.equal(nativeServerResponse.body.seatbid[0].bid.native.impressionTrackers); + expect(interpretedResponseForNative[0].native.sponsoredBy).to.equal(nativeServerResponse.body.seatbid[0].bid.native.sponsoredBy); + expect(interpretedResponseForNative[0].native.title).to.equal(nativeServerResponse.body.seatbid[0].bid.native.title); + expect(interpretedResponseForNative[0].meta.advertiserDomains[0]).to.equal(serverResponse.body.seatbid[0].bid.meta.advertiserDomains[0]); + }); + }); + + describe('getUserSyncs', function () { + it('should perform usersync', function () { + let syncs = spec.getUserSyncs({pixelEnabled: false}, [serverResponse]); + expect(syncs).to.have.length(0); + + syncs = spec.getUserSyncs({pixelEnabled: true}, [serverResponse]); + expect(syncs).to.have.length(1); + + expect(syncs[0]).to.have.property('type', 'image'); + expect(syncs[0]).to.have.property('url', 'https://cr-p31.ladsp.jp/cookiesender/31'); + }); + }); +}); From db68d4e1cb30c7607cdf4ba211126b6376c7ee26 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 1 Jul 2021 18:09:20 +0200 Subject: [PATCH 1231/1476] smartx Bid Adapter: update renderer url and internal renderer behaviour (#7129) * 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 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 48 +++++----------------- test/spec/modules/smartxBidAdapter_spec.js | 5 ++- 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 657520bcde5..c29708c8420 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -55,10 +55,6 @@ export const spec = { utils.logError(BIDDER_CODE + ': slot parameter is not defined in outstream_options object in the configuration'); return false; } - if (!utils.getBidIdParameter('outstream_function', bid.params)) { - utils.logMessage(BIDDER_CODE + ': outstream_function parameter is not defined. The default outstream renderer will be injected in the header.'); - return true; - } } return true; @@ -295,7 +291,7 @@ export const spec = { const playersize = utils.deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); const renderer = Renderer.install({ id: 0, - url: '/', + url: 'https://dco.smartclip.net/?plc=7777778', config: { adText: 'SmartX Outstream Video Ad via Prebid.js', player_width: playersize[0][0], @@ -303,12 +299,11 @@ export const spec = { content_page_url: utils.deepAccess(bidderRequest, 'data.site.page'), ad_mute: +!!utils.deepAccess(currentBidRequest, 'params.ad_mute'), hide_skin: +!!utils.deepAccess(currentBidRequest, 'params.hide_skin'), - outstream_options: utils.deepAccess(currentBidRequest, 'params.outstream_options'), - outstream_function: utils.deepAccess(currentBidRequest, 'params.outstream_function') + outstream_options: utils.deepAccess(currentBidRequest, 'params.outstream_options') } }); try { - renderer.setRender(outstreamRender); + renderer.setRender(createOutstreamConfig); renderer.setEventHandlers({ impression: function impression() { return utils.logMessage('SmartX outstream video impression event'); @@ -333,7 +328,7 @@ export const spec = { } } -function createOutstreamScript(bid) { +function createOutstreamConfig(bid) { const confMinAdWidth = utils.getBidIdParameter('minAdWidth', bid.renderer.config.outstream_options) || 290; const confMaxAdWidth = utils.getBidIdParameter('maxAdWidth', bid.renderer.config.outstream_options) || 900; const confStartOpen = utils.getBidIdParameter('startOpen', bid.renderer.config.outstream_options); @@ -373,37 +368,14 @@ function createOutstreamScript(bid) { smartPlayObj.adResponse = bid.vastContent; const divID = '[id="' + elementId + '"]'; - var script = document.createElement('script'); - script.src = 'https://dco.smartclip.net/?plc=7777778'; - script.type = 'text/javascript'; - script.async = 'true'; - script.onload = script.onreadystatechange = function () { - try { - // eslint-disable-next-line - let _outstreamPlayer = new OutstreamPlayer(divID, smartPlayObj); - } catch (e) { - utils.logError('[SmartPlay][renderer] Error caught: ' + e); - } - }; - 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 slot = utils.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) { - utils.logError('[SMARTX][renderer] Error:' + err.message) - } + try { + // eslint-disable-next-line + let _outstreamPlayer = new OutstreamPlayer(divID, smartPlayObj); + } catch (e) { + utils.logError('[SMARTX][renderer] Error caught: ' + e); } + return smartPlayObj; } /** diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index f02aa81980f..89c03034ba4 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -506,8 +506,9 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); - expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - expect(scriptTag.getAttribute('src')).to.equal('https://dco.smartclip.net/?plc=7777778'); + // expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); + // expect(scriptTag.getAttribute('src')).to.equal('https://dco.smartclip.net/?plc=7777778'); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); window.document.getElementById.restore(); }); From faa39d956e4578397f83aa452f43759f7152747f Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 1 Jul 2021 10:34:10 -0700 Subject: [PATCH 1232/1476] Prebid 5.3.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20ad7fce908..52c98be2f90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.3.0-pre", + "version": "5.3.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 9058600012689d413718923448145c46c2b83e8f Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 1 Jul 2021 10:53:09 -0700 Subject: [PATCH 1233/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 52c98be2f90..71493dae8bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.3.0", + "version": "5.4.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 6a141e799485c2f3788b57397868687f451c32e0 Mon Sep 17 00:00:00 2001 From: antoinezaz Date: Fri, 2 Jul 2021 11:42:37 +0200 Subject: [PATCH 1234/1476] Ogury Bid Adapter: fix hostname (#7135) * update ogury bidder hostname * fix test --- modules/oguryBidAdapter.js | 2 +- test/spec/modules/oguryBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 0b4982bc8dc..d12135eafb1 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -6,7 +6,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ogury'; const DEFAULT_TIMEOUT = 1000; -const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; +const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; function isBidRequestValid(bid) { const adUnitSizes = getAdUnitSizes(bid); diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index d08bf2c8430..609ebbf0ac6 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec } from 'modules/oguryBidAdapter'; import { deepClone } from 'src/utils.js'; -const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; +const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; describe('OguryBidAdapter', function () { let bidRequests; From f48fa6f9a96a55a50023b6d32e7262d5483be453 Mon Sep 17 00:00:00 2001 From: EMX Digital <43830380+EMXDigital@users.noreply.github.com> Date: Tue, 6 Jul 2021 00:01:42 -0700 Subject: [PATCH 1235/1476] Emx Digital Bid Adapter: support for liveramp id and uid2.0 (#7133) * adding ccpa support for emx_digital adapter * emx_digital ccpa compliance: lint fix * emx 3.0 compliance update * fix outstream renderer issue, update test spec * refactor formatVideoResponse function to use core-js/find * Add support for schain forwarding * Resolved issue with Schain object location * prebid 5.0 floor module and advertiserDomain support * liveramp idl and uid2.0 support for prebid Co-authored-by: Nick Colletti Co-authored-by: Nick Colletti Co-authored-by: Kiyoshi Hara Co-authored-by: Dan Bogdan Co-authored-by: Jherez Taylor Co-authored-by: EMXDigital Co-authored-by: Rakesh Balakrishnan --- modules/emx_digitalBidAdapter.js | 41 +++++++++++++++++++ .../modules/emx_digitalBidAdapter_spec.js | 34 +++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index bcdcd7393f7..260ffe105f4 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -11,6 +11,11 @@ const RENDERER_URL = 'https://js.brealtime.com/outstream/1.30.0/bundle.js'; const ADAPTER_VERSION = '1.5.1'; const DEFAULT_CUR = 'USD'; +const EIDS_SUPPORTED = [ + { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' }, + { key: 'uid2.id', source: 'uidapi.com', rtiPartner: 'UID2', queryParam: 'uid2' } +]; + export const emxAdapter = { validateSizes: (sizes) => { if (!utils.isArray(sizes) || typeof sizes[0] === 'undefined') { @@ -168,6 +173,27 @@ export const emxAdapter = { } return emxData; + }, + // supporting eids + getEids(bidRequests) { + return EIDS_SUPPORTED + .map(emxAdapter.getUserId(bidRequests)) + .filter(x => x); + }, + getUserId(bidRequests) { + return ({ key, source, rtiPartner }) => { + let id = utils.deepAccess(bidRequests, `userId.${key}`); + return id ? emxAdapter.formatEid(id, source, rtiPartner) : null; + }; + }, + formatEid(id, source, rtiPartner) { + return { + source, + uids: [{ + id, + ext: { rtiPartner } + }] + }; } }; @@ -252,6 +278,21 @@ export const spec = { if (bidderRequest && bidderRequest.uspConsent) { emxData.us_privacy = bidderRequest.uspConsent } + + // adding eid support + if (bidderRequest.userId) { + let eids = emxAdapter.getEids(bidderRequest); + if (eids.length > 0) { + if (emxData.user && emxData.user.ext) { + emxData.user.ext.eids = eids; + } else { + emxData.user = { + ext: {eids} + }; + } + } + } + return { method: 'POST', url, diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 0f82122b9c1..5831a8506c1 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -414,6 +414,40 @@ describe('emx_digital Adapter', function () { expect(request.source.ext.schain).to.have.property('ver', '1.0'); expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.bids[0].schain.nodes[0].asi); }); + + it('should add liveramp identitylink id to request', () => { + const idl_env = '123'; + const bidRequestWithID = utils.deepClone(bidderRequest); + bidRequestWithID.userId = { idl_env }; + let requestWithID = spec.buildRequests(bidRequestWithID.bids, bidRequestWithID); + requestWithID = JSON.parse(requestWithID.data); + expect(requestWithID.user.ext.eids[0]).to.deep.equal({ + source: 'liveramp.com', + uids: [{ + id: idl_env, + ext: { + rtiPartner: 'idl' + } + }] + }); + }); + + it('should add UID 2.0 to request', () => { + const uid2 = { id: '456' }; + const bidRequestWithUID = utils.deepClone(bidderRequest); + bidRequestWithUID.userId = { uid2 }; + let requestWithUID = spec.buildRequests(bidRequestWithUID.bids, bidRequestWithUID); + requestWithUID = JSON.parse(requestWithUID.data); + expect(requestWithUID.user.ext.eids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: uid2.id, + ext: { + rtiPartner: 'UID2' + } + }] + }); + }); }); describe('interpretResponse', function () { From 498e7486548066d601a7550e6b689aa0b7ba0e97 Mon Sep 17 00:00:00 2001 From: Latyshev Dmitry Date: Tue, 6 Jul 2021 12:36:12 +0300 Subject: [PATCH 1236/1476] RtbSape Bid Adapter: restore for Prebid 5.x (#7081) * RtbSape Bid Adapter: restore for Prebid 5.x * RtbSape Bid Adapter: check adomain (#7081) Co-authored-by: Dmitry Latyshev --- modules/rtbsapeBidAdapter.js | 144 ++++++++++++++ test/spec/modules/rtbsapeBidAdapter_spec.js | 209 ++++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 modules/rtbsapeBidAdapter.js create mode 100644 test/spec/modules/rtbsapeBidAdapter_spec.js diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js new file mode 100644 index 00000000000..f5c648d5234 --- /dev/null +++ b/modules/rtbsapeBidAdapter.js @@ -0,0 +1,144 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {OUTSTREAM} from '../src/video.js'; +import {Renderer} from '../src/Renderer.js'; +import {triggerPixel} from '../src/utils.js'; + +const BIDDER_CODE = 'rtbsape'; +const ENDPOINT = 'https://ssp-rtb.sape.ru/prebid'; +const RENDERER_SRC = 'https://cdn-rtb.sape.ru/js/player.js'; +const MATCH_SRC = 'https://www.acint.net/mc/?dp=141'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['sape'], + 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 && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.video) && bid.params && bid.params.placeId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let tz = (new Date()).getTimezoneOffset() + let padInt = (v) => (v < 10 ? '0' + v : '' + v); + + return { + url: ENDPOINT, + method: 'POST', + data: { + auctionId: bidderRequest.auctionId, + requestId: bidderRequest.bidderRequestId, + bids: validBidRequests, + timezone: (tz > 0 ? '-' : '+') + padInt(Math.floor(Math.abs(tz) / 60)) + ':' + padInt(Math.abs(tz) % 60), + refererInfo: bidderRequest.refererInfo + }, + } + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {{data: {bids: [{mediaTypes: {banner: boolean}}]}}} bidRequest Info describing the request to the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + if (!(serverResponse.body && Array.isArray(serverResponse.body.bids))) { + return []; + } + + let bids = {}; + bidRequest.data.bids.forEach(bid => bids[bid.bidId] = bid); + + return serverResponse.body.bids + .filter(bid => typeof (bid.meta || {}).advertiserDomains !== 'undefined') + .map(bid => { + let requestBid = bids[bid.requestId]; + let context = utils.deepAccess(requestBid, 'mediaTypes.video.context'); + + if (context === OUTSTREAM && (bid.vastUrl || bid.vastXml)) { + let renderer = Renderer.install({ + id: bid.requestId, + url: RENDERER_SRC, + loaded: false + }); + + let muted = utils.deepAccess(requestBid, 'params.video.playerMuted'); + if (typeof muted === 'undefined') { + muted = true; + } + + bid.playerMuted = muted; + bid.renderer = renderer + + renderer.setRender(setOutstreamRenderer); + } + + return bid; + }); + }, + + /** + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function (syncOptions) { + const sync = []; + if (syncOptions.iframeEnabled) { + sync.push({ + type: 'iframe', + url: MATCH_SRC + }); + } + return sync; + }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} bid The bid that won the auction + */ + onBidWon: function(bid) { + if (bid.nurl) { + triggerPixel(bid.nurl); + } + } +} + +/** + * Initialize RtbSape outstream player + * + * @param bid + */ +function setOutstreamRenderer(bid) { + let props = {}; + if (bid.vastUrl) { + props.url = bid.vastUrl; + } + if (bid.vastXml) { + props.xml = bid.vastXml; + } + bid.renderer.push(() => { + let player = window.sapeRtbPlayerHandler(bid.adUnitCode, bid.width, bid.height, bid.playerMuted, {singleton: true}); + props.onComplete = () => player.destroy(); + props.onError = () => player.destroy(); + player.addSlot(props); + }); +} + +registerBidder(spec); diff --git a/test/spec/modules/rtbsapeBidAdapter_spec.js b/test/spec/modules/rtbsapeBidAdapter_spec.js new file mode 100644 index 00000000000..eea9e51b1a9 --- /dev/null +++ b/test/spec/modules/rtbsapeBidAdapter_spec.js @@ -0,0 +1,209 @@ +import {expect} from 'chai'; +import {spec} from 'modules/rtbsapeBidAdapter.js'; +import 'src/prebid.js'; +import * as utils from 'src/utils.js'; +import {executeRenderer, Renderer} from 'src/Renderer.js'; + +describe('rtbsapeBidAdapterTests', function () { + describe('isBidRequestValid', function () { + it('valid', function () { + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {placeId: 4321}})).to.equal(true); + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {video: true}, params: {placeId: 4321}})).to.equal(true); + }); + + it('invalid', function () { + expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {}})).to.equal(false); + expect(spec.isBidRequestValid({bidder: 'rtbsape', params: {placeId: 4321}})).to.equal(false); + }); + }); + + it('buildRequests', function () { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'rtbsape', + params: {placeId: 4321}, + sizes: [[240, 400]] + }]; + let bidderRequest = { + auctionId: '2e208334-cafe-4c2c-b06b-f055ff876852', + bidderRequestId: '1392d0aa613366', + refererInfo: {} + }; + let request = spec.buildRequests(bidRequestData, bidderRequest); + expect(request.data.auctionId).to.equal('2e208334-cafe-4c2c-b06b-f055ff876852'); + expect(request.data.requestId).to.equal('1392d0aa613366'); + expect(request.data.bids[0].bidId).to.equal('bid1234'); + expect(request.data.timezone).to.not.equal(undefined); + }); + + describe('interpretResponse', function () { + it('banner', function () { + let serverResponse = { + body: { + bids: [{ + requestId: 'bid1234', + cpm: 2.21, + currency: 'RUB', + width: 240, + height: 400, + netRevenue: true, + ad: 'Ad html', + meta: { + advertiserDomains: ['rtb.sape.ru'] + } + }] + } + }; + let bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(2.21); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(240); + expect(bid.height).to.equal(400); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid1234'); + expect(bid.ad).to.equal('Ad html'); + }); + + describe('video (outstream)', function () { + let bid; + + before(() => { + let serverResponse = { + body: { + bids: [{ + requestId: 'bid1234', + adUnitCode: 'ad-bid1234', + cpm: 3.32, + currency: 'RUB', + width: 600, + height: 340, + netRevenue: true, + vastUrl: 'https://cdn-rtb.sape.ru/vast/4321.xml', + meta: { + advertiserDomains: ['rtb.sape.ru'], + mediaType: 'video' + } + }] + } + }; + let serverRequest = { + data: { + bids: [{ + bidId: 'bid1234', + adUnitCode: 'ad-bid1234', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: { + placeId: 4321, + video: { + playerMuted: false + } + } + }] + } + }; + let bids = spec.interpretResponse(serverResponse, serverRequest); + expect(bids).to.have.lengthOf(1); + bid = bids[0]; + }); + + it('should add renderer', () => { + expect(bid).to.have.own.property('renderer'); + expect(bid.renderer).to.be.instanceof(Renderer); + expect(bid.renderer.url).to.equal('https://cdn-rtb.sape.ru/js/player.js'); + expect(bid.playerMuted).to.equal(false); + }); + + it('should create player instance', () => { + let spy = false; + + window.sapeRtbPlayerHandler = function (id, w, h, m) { + const player = {addSlot: () => [id, w, h, m]} + expect(spy).to.equal(false); + spy = sinon.spy(player, 'addSlot'); + return player; + }; + + executeRenderer(bid.renderer, bid); + bid.renderer.callback(); + expect(spy).to.not.equal(false); + expect(spy.called).to.be.true; + + const spyCall = spy.getCall(0); + expect(spyCall.args[0].url).to.be.equal('https://cdn-rtb.sape.ru/vast/4321.xml'); + expect(spyCall.returnValue[0]).to.be.equal('ad-bid1234'); + expect(spyCall.returnValue[1]).to.be.equal(600); + expect(spyCall.returnValue[2]).to.be.equal(340); + expect(spyCall.returnValue[3]).to.be.equal(false); + }); + }); + + it('skip adomain', function () { + let serverResponse = { + body: { + bids: [{ + requestId: 'bid1234', + cpm: 2.21, + currency: 'RUB', + width: 240, + height: 400, + netRevenue: true, + ad: 'Ad html 1' + }, { + requestId: 'bid1235', + cpm: 2.23, + currency: 'RUB', + width: 300, + height: 250, + netRevenue: true, + ad: 'Ad html 2', + meta: { + advertiserDomains: ['rtb.sape.ru'] + } + }] + } + }; + let bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(2.23); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid1235'); + expect(bid.ad).to.equal('Ad html 2'); + }); + }); + + it('getUserSyncs', function () { + const syncs = spec.getUserSyncs({iframeEnabled: true}); + expect(syncs).to.be.an('array').that.to.have.lengthOf(1); + expect(syncs[0]).to.deep.equal({type: 'iframe', url: 'https://www.acint.net/mc/?dp=141'}); + }); + + describe('onBidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + utils.triggerPixel.restore(); + }); + + it('called once', function () { + spec.onBidWon({cpm: '2.21', nurl: 'https://ssp-rtb.sape.ru/track?event=win'}); + expect(utils.triggerPixel.calledOnce).to.equal(true); + }); + + it('called false', function () { + spec.onBidWon({cpm: '2.21'}); + expect(utils.triggerPixel.called).to.equal(false); + }); + }); +}); From ebf67adcf08db3a6da5edda7e84a8a4508cfaa06 Mon Sep 17 00:00:00 2001 From: Desvillettes <30619957+AurelienMozoo@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:08:13 +0200 Subject: [PATCH 1237/1476] Add new ext field in bid Object (#7138) --- modules/oguryBidAdapter.js | 1 + modules/oguryBidAdapter.md | 5 +++- test/spec/modules/oguryBidAdapter_spec.js | 30 +++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index d12135eafb1..b0a19850a90 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -98,6 +98,7 @@ function interpretResponse(openRtbBidResponse) { creativeId: bid.id, netRevenue: true, ttl: 60, + ext: bid.ext, meta: { advertiserDomains: bid.adomain } diff --git a/modules/oguryBidAdapter.md b/modules/oguryBidAdapter.md index 00896762dc4..7264602de14 100644 --- a/modules/oguryBidAdapter.md +++ b/modules/oguryBidAdapter.md @@ -27,9 +27,12 @@ Ogury bid adapter supports Banner media type. params: { assetKey: 'OGY-CA41D116484F', adUnitId: '2c4d61d0-90aa-0139-0cda-0242ac120004' + xMargin?: 20 + yMargin?: 20 + gravity?: 'TOP_LEFT' || 'TOP_RIGHT' || 'BOTTOM_LEFT' || 'BOTTOM_RIGHT' || 'BOTTOM_CENTER' || 'TOP_CENTER' || 'CENTER' } } ] } ]; -``` +``` \ No newline at end of file diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 609ebbf0ac6..34af8606ea6 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -17,6 +17,9 @@ describe('OguryBidAdapter', function () { params: { assetKey: 'OGY-assetkey', adUnitId: 'adunitId', + xMargin: 20, + yMarging: 20, + gravity: 'TOP_LEFT', }, mediaTypes: { banner: { @@ -249,8 +252,18 @@ describe('OguryBidAdapter', function () { nurl: 'url', adm: `test creative
cookies
`, adomain: ['renault.fr'], - w: 300, - h: 250 + ext: { + adcontent: 'sample_creative', + advertid: '1a278c48-b79a-4bbf-b69f-3824803e7d87', + campaignid: '31724', + mediatype: 'image', + userid: 'ab4aabed-5230-49d9-9f1a-f06280d28366', + usersync: true, + advertiserid: '1', + isomidcompliant: false + }, + w: 180, + h: 101 }, { id: 'advertId2', impid: 'bidId2', @@ -258,6 +271,17 @@ describe('OguryBidAdapter', function () { nurl: 'url2', adm: `test creative
cookies
`, adomain: ['peugeot.fr'], + ext: { + adcontent: 'sample_creative', + advertid: '2a278c48-b79a-4bbf-b69f-3824803e7d87', + campaignid: '41724', + userid: 'bb4aabed-5230-49d9-9f1a-f06280d28366', + usersync: false, + advertiserid: '2', + isomidcompliant: true, + mediatype: 'image', + landingpageurl: 'https://ogury.com' + }, w: 600, h: 500 }], @@ -274,6 +298,7 @@ describe('OguryBidAdapter', function () { height: openRtbBidResponse.body.seatbid[0].bid[0].h, ad: openRtbBidResponse.body.seatbid[0].bid[0].adm, ttl: 60, + ext: openRtbBidResponse.body.seatbid[0].bid[0].ext, creativeId: openRtbBidResponse.body.seatbid[0].bid[0].id, netRevenue: true, meta: { @@ -287,6 +312,7 @@ describe('OguryBidAdapter', function () { height: openRtbBidResponse.body.seatbid[0].bid[1].h, ad: openRtbBidResponse.body.seatbid[0].bid[1].adm, ttl: 60, + ext: openRtbBidResponse.body.seatbid[0].bid[1].ext, creativeId: openRtbBidResponse.body.seatbid[0].bid[1].id, netRevenue: true, meta: { From ed0a2f94a493fefac6d1b4670afeb16d6c98a1f1 Mon Sep 17 00:00:00 2001 From: Desvillettes <30619957+AurelienMozoo@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:24:09 +0200 Subject: [PATCH 1238/1476] Change Bid Request contentType as from 'text/plain' to 'application/json' (#7139) --- modules/oguryBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index b0a19850a90..eb751fb5916 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -72,7 +72,8 @@ function buildRequests(validBidRequests, bidderRequest) { return { method: 'POST', url: BID_HOST, - data: openRtbBidRequestBanner + data: openRtbBidRequestBanner, + options: {contentType: 'application/json'}, }; } From 089e43ab908a57178d9679ca87bacc1e5cbae1b3 Mon Sep 17 00:00:00 2001 From: Arne Schulz Date: Tue, 6 Jul 2021 14:35:44 +0200 Subject: [PATCH 1239/1476] Orbidder Bid Adapter: add native support (#7047) * [ORBIDDER] add native support * [ORBIDDER] fix orbidder tests * ORBIDDER: fix test parameter documentation --- modules/orbidderBidAdapter.js | 89 ++++- modules/orbidderBidAdapter.md | 55 ++- test/spec/modules/orbidderBidAdapter_spec.js | 336 ++++++++++++++++--- 3 files changed, 410 insertions(+), 70 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index 0ca0eeafe47..907efd3dba8 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,13 +1,52 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; const storageManager = getStorageManager(); +/** + * Determines whether or not the given bid response is valid. + * + * @param {object} bidResponse The bid response to validate. + * @return boolean True if this is a valid bid response, and false if it is not valid. + */ +function isBidResponseValid(bidResponse) { + let requiredKeys = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency']; + + switch (bidResponse.mediaType) { + case BANNER: + requiredKeys = requiredKeys.concat(['width', 'height', 'ad']); + break; + case NATIVE: + if (!bidResponse.native.hasOwnProperty('impressionTrackers')) { + return false + } + break; + default: + return false + } + + for (const key of requiredKeys) { + if (!bidResponse.hasOwnProperty(key)) { + return false + } + } + + return true +} + export const spec = { code: 'orbidder', hostname: 'https://orbidder.otto.de', + supportedMediaTypes: [BANNER, NATIVE], + /** + * Returns a customzied hostname if 'ov_orbidder_host' is set in the browser's local storage. + * This is only used for integration testing. + * + * @return The hostname bid requests should be sent to. + */ getHostname() { let ret = this.hostname; try { @@ -17,6 +56,12 @@ export const spec = { return ret; }, + /** + * 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 if it is not valid. + */ isBidRequestValid(bid) { return !!(bid.sizes && bid.bidId && bid.params && (bid.params.accountId && (typeof bid.params.accountId === 'string')) && @@ -24,6 +69,13 @@ export const spec = { ((typeof bid.params.profile === 'undefined') || (typeof bid.params.profile === 'object'))); }, + /** + * Build a request from the list of valid BidRequests that will be sent by prebid to the orbidder /bid endpoint, i.e. the server. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the orbidder /bid endpoint, + * i.e. the server. + * @return The requests for the orbidder /bid endpoint, i.e. the server. + */ buildRequests(validBidRequests, bidderRequest) { const hostname = this.getHostname(); return validBidRequests.map((bidRequest) => { @@ -34,7 +86,7 @@ export const spec = { bidRequest.params.bidfloor = getBidFloor(bidRequest); - const ret = { + let httpReq = { url: `${hostname}/bid`, method: 'POST', options: { withCredentials: true }, @@ -46,40 +98,41 @@ export const spec = { transactionId: bidRequest.transactionId, adUnitCode: bidRequest.adUnitCode, bidRequestCount: bidRequest.bidRequestCount, + params: bidRequest.params, sizes: bidRequest.sizes, - params: bidRequest.params + mediaTypes: bidRequest.mediaTypes } }; + if (bidderRequest && bidderRequest.gdprConsent) { - ret.data.gdprConsent = { + httpReq.data.gdprConsent = { consentString: bidderRequest.gdprConsent.consentString, consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') && bidderRequest.gdprConsent.gdprApplies }; } - return ret; + return httpReq; }); }, + /** + * Unpack the response from the orbidder /bid endpoint into a list of bids. + * + * @param {*} serverResponse A successful response from the orbidder /bid endpoint, i.e. the server. + * @return {Bid[]} An array of bids from orbidder. + */ interpretResponse(serverResponse) { const bidResponses = []; serverResponse = serverResponse.body; if (serverResponse && (serverResponse.length > 0)) { - serverResponse.forEach((bid) => { - const bidResponse = {}; - for (const requiredKey of ['requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency']) { - if (!bid.hasOwnProperty(requiredKey)) { - return []; + serverResponse.forEach((bidResponse) => { + if (isBidResponseValid(bidResponse)) { + if (Array.isArray(bidResponse.advertiserDomains)) { + bidResponse.meta = { + advertiserDomains: bidResponse.advertiserDomains + } } - bidResponse[requiredKey] = bid[requiredKey]; + bidResponses.push(bidResponse); } - - if (Array.isArray(bid.advertiserDomains)) { - bidResponse.meta = { - advertiserDomains: bid.advertiserDomains - } - } - - bidResponses.push(bidResponse); }); } return bidResponses; diff --git a/modules/orbidderBidAdapter.md b/modules/orbidderBidAdapter.md index c7676e6774f..6b8fac5477a 100644 --- a/modules/orbidderBidAdapter.md +++ b/modules/orbidderBidAdapter.md @@ -12,19 +12,46 @@ Module that connects to orbidder demand sources # Test Parameters ``` -var adUnits = [{ - code: '/105091519/bidder_test', - mediaTypes: { - banner: { - sizes: [728, 90] - } +var adUnits = [ + { + code: 'test_banner', + mediaTypes: { + banner: { + sizes: [728, 90] + } + }, + bids: [{ + bidder: 'orbidder', + params: { + accountId: "someAccount", + placementId: "somePlace" + } + }], }, - bids: [{ - bidder: 'orbidder' - params: { - accountId: "someAccount", - placementId: "somePlace" - } - }] -}]; + { + code: 'test_native', + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + } + }, + }, + bids: [{ + bidder: 'orbidder', + params: { + accountId: "someAccount", + placementId: "somePlace" + } + }], + } +]; ``` diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index c6dbb24614a..0a18799ad4b 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -1,10 +1,12 @@ import {expect} from 'chai'; import {spec} from 'modules/orbidderBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; +import * as _ from 'lodash'; +import { BANNER, NATIVE } from '../../../src/mediaTypes.js'; describe('orbidderBidAdapter', () => { const adapter = newBidder(spec); - const defaultBidRequest = { + const defaultBidRequestBanner = { bidId: 'd66fa86787e0b0ca900a96eacfd5f0bb', auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d8', transactionId: 'd58851660c0c4461e4aa06344fc9c0c6', @@ -14,6 +16,38 @@ describe('orbidderBidAdapter', () => { params: { 'accountId': 'string1', 'placementId': 'string2' + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + } + }; + + const defaultBidRequestNative = { + bidId: 'd66fa86787e0b0ca900a96eacfd5f0bc', + auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d9', + transactionId: 'd58851660c0c4461e4aa06344fc9c0c7', + bidRequestCount: 1, + adUnitCode: 'adunit-code-native', + sizes: [], + params: { + 'accountId': 'string3', + 'placementId': 'string4' + }, + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true, + sizes: [300, 250] + }, + sponsoredBy: { + required: true + } + } } }; @@ -41,37 +75,84 @@ describe('orbidderBidAdapter', () => { }); describe('isBidRequestValid', () => { - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(defaultBidRequest)).to.equal(true); + it('banner: should return true when required params found', () => { + expect(spec.isBidRequestValid(defaultBidRequestBanner)).to.equal(true); + }); + + it('native: should return true when required params found', () => { + expect(spec.isBidRequestValid(defaultBidRequestNative)).to.equal(true); }); - it('accepts optional profile object', () => { - const bidRequest = deepClone(defaultBidRequest); + it('banner: accepts optional profile object', () => { + const bidRequest = deepClone(defaultBidRequestBanner); bidRequest.params.profile = {'key': 'value'}; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('performs type checking', () => { - const bidRequest = deepClone(defaultBidRequest); + it('native: accepts optional profile object', () => { + const bidRequest = deepClone(defaultBidRequestNative); + bidRequest.params.profile = {'key': 'value'}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('banner: performs type checking', () => { + const bidRequest = deepClone(defaultBidRequestBanner); bidRequest.params.accountId = 1; // supposed to be a string expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('doesn\'t accept malformed profile', () => { - const bidRequest = deepClone(defaultBidRequest); + it('native: performs type checking', () => { + const bidRequest = deepClone(defaultBidRequestNative); + bidRequest.params.accountId = 1; // supposed to be a string + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('banner: doesn\'t accept malformed profile', () => { + const bidRequest = deepClone(defaultBidRequestBanner); bidRequest.params.profile = 'another not usable string'; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should return false when required params are not passed', () => { - const bidRequest = deepClone(defaultBidRequest); + it('native: doesn\'t accept malformed profile', () => { + const bidRequest = deepClone(defaultBidRequestNative); + bidRequest.params.profile = 'another not usable string'; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('banner: should return false when required params are not passed', () => { + const bidRequest = deepClone(defaultBidRequestBanner); delete bidRequest.params; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); + + it('native: should return false when required params are not passed', () => { + const bidRequest = deepClone(defaultBidRequestNative); + delete bidRequest.params; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('banner: accepts optional bidfloor', () => { + const bidRequest = deepClone(defaultBidRequestBanner); + bidRequest.params.bidfloor = 123; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + + bidRequest.params.bidfloor = 1.23; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('native: accepts optional bidfloor', () => { + const bidRequest = deepClone(defaultBidRequestNative); + bidRequest.params.bidfloor = 123; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + + bidRequest.params.bidfloor = 1.23; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); }); describe('buildRequests', () => { - const request = buildRequest(defaultBidRequest); + const request = buildRequest(defaultBidRequestBanner); + const nativeRequest = buildRequest(defaultBidRequestNative); it('sends bid request to endpoint via https using post', () => { expect(request.method).to.equal('POST'); @@ -83,33 +164,85 @@ describe('orbidderBidAdapter', () => { expect(request.data.v).to.equal($$PREBID_GLOBAL$$.version); }); - it('sends correct bid parameters', () => { - // we add two, because we add referer information and version from bidderRequest object - expect(Object.keys(request.data).length).to.equal(Object.keys(defaultBidRequest).length + 2); + it('banner: sends correct bid parameters', () => { + // we add two, because we add pageUrl and version from bidderRequest object + expect(Object.keys(request.data).length).to.equal(Object.keys(defaultBidRequestBanner).length + 2); + + expect(request.data.bidId).to.equal(defaultBidRequestBanner.bidId); + expect(request.data.auctionId).to.equal(defaultBidRequestBanner.auctionId); + expect(request.data.transactionId).to.equal(defaultBidRequestBanner.transactionId); + expect(request.data.bidRequestCount).to.equal(defaultBidRequestBanner.bidRequestCount); + expect(request.data.adUnitCode).to.equal(defaultBidRequestBanner.adUnitCode); expect(request.data.pageUrl).to.equal('https://localhost:9876/'); - // expect(request.data.referrer).to.equal(''); - Object.keys(defaultBidRequest).forEach((key) => { - expect(request.data[key]).to.deep.equal(defaultBidRequest[key]); + expect(request.data.v).to.equal($$PREBID_GLOBAL$$.version); + expect(request.data.sizes).to.equal(defaultBidRequestBanner.sizes); + + expect(_.isEqual(request.data.params, defaultBidRequestBanner.params)).to.be.true; + expect(_.isEqual(request.data.mediaTypes, defaultBidRequestBanner.mediaTypes)).to.be.true; + }); + + it('native: sends correct bid parameters', () => { + // we add two, because we add pageUrl and version from bidderRequest object + expect(Object.keys(nativeRequest.data).length).to.equal(Object.keys(defaultBidRequestNative).length + 2); + + expect(nativeRequest.data.bidId).to.equal(defaultBidRequestNative.bidId); + expect(nativeRequest.data.auctionId).to.equal(defaultBidRequestNative.auctionId); + expect(nativeRequest.data.transactionId).to.equal(defaultBidRequestNative.transactionId); + expect(nativeRequest.data.bidRequestCount).to.equal(defaultBidRequestNative.bidRequestCount); + expect(nativeRequest.data.adUnitCode).to.equal(defaultBidRequestNative.adUnitCode); + expect(nativeRequest.data.pageUrl).to.equal('https://localhost:9876/'); + expect(nativeRequest.data.v).to.equal($$PREBID_GLOBAL$$.version); + expect(nativeRequest.data.sizes).to.be.empty; + + expect(_.isEqual(nativeRequest.data.params, defaultBidRequestNative.params)).to.be.true; + expect(_.isEqual(nativeRequest.data.mediaTypes, defaultBidRequestNative.mediaTypes)).to.be.true; + }); + + it('banner: handles empty gdpr object', () => { + const request = buildRequest(defaultBidRequestBanner, { + gdprConsent: {} }); + expect(request.data.gdprConsent.consentRequired).to.be.equal(false); }); - it('handles empty gdpr object', () => { - const request = buildRequest(defaultBidRequest, { + it('native: handles empty gdpr object', () => { + const request = buildRequest(defaultBidRequestNative, { gdprConsent: {} }); expect(request.data.gdprConsent.consentRequired).to.be.equal(false); }); - it('handles non-existent gdpr object', () => { - const request = buildRequest(defaultBidRequest, { + it('banner: handles non-existent gdpr object', () => { + const request = buildRequest(defaultBidRequestBanner, { + gdprConsent: null + }); + expect(request.data.gdprConsent).to.be.undefined; + }); + + it('native: handles non-existent gdpr object', () => { + const request = buildRequest(defaultBidRequestNative, { gdprConsent: null }); expect(request.data.gdprConsent).to.be.undefined; }); - it('handles properly filled gdpr object where gdpr applies', () => { + it('banner: handles properly filled gdpr object where gdpr applies', () => { + const consentString = 'someWeirdString'; + const request = buildRequest(defaultBidRequestBanner, { + gdprConsent: { + gdprApplies: true, + consentString: consentString + } + }); + + const gdprConsent = request.data.gdprConsent; + expect(gdprConsent.consentRequired).to.be.equal(true); + expect(gdprConsent.consentString).to.be.equal(consentString); + }); + + it('native: handles properly filled gdpr object where gdpr applies', () => { const consentString = 'someWeirdString'; - const request = buildRequest(defaultBidRequest, { + const request = buildRequest(defaultBidRequestNative, { gdprConsent: { gdprApplies: true, consentString: consentString @@ -121,9 +254,23 @@ describe('orbidderBidAdapter', () => { expect(gdprConsent.consentString).to.be.equal(consentString); }); - it('handles properly filled gdpr object where gdpr does not apply', () => { + it('banner: handles properly filled gdpr object where gdpr does not apply', () => { const consentString = 'someWeirdString'; - const request = buildRequest(defaultBidRequest, { + const request = buildRequest(defaultBidRequestBanner, { + gdprConsent: { + gdprApplies: false, + consentString: consentString + } + }); + + const gdprConsent = request.data.gdprConsent; + expect(gdprConsent.consentRequired).to.be.equal(false); + expect(gdprConsent.consentString).to.be.equal(consentString); + }); + + it('native: handles properly filled gdpr object where gdpr does not apply', () => { + const consentString = 'someWeirdString'; + const request = buildRequest(defaultBidRequestNative, { gdprConsent: { gdprApplies: false, consentString: consentString @@ -137,7 +284,7 @@ describe('orbidderBidAdapter', () => { }); describe('interpretResponse', () => { - it('should get correct bid response', () => { + it('banner: should get correct bid response', () => { const serverResponse = [ { 'width': 300, @@ -148,7 +295,8 @@ describe('orbidderBidAdapter', () => { 'requestId': '30b31c1838de1e', 'ttl': 60, 'netRevenue': true, - 'currency': 'EUR' + 'currency': 'EUR', + 'mediaType': BANNER, } ]; @@ -162,16 +310,14 @@ describe('orbidderBidAdapter', () => { 'ttl': 60, 'currency': 'EUR', 'ad': '', - 'netRevenue': true + 'netRevenue': true, + 'mediaType': BANNER, } ]; const result = spec.interpretResponse({body: serverResponse}); - expect(result.length).to.equal(expectedResponse.length); - Object.keys(expectedResponse[0]).forEach((key) => { - expect(result[0][key]).to.equal(expectedResponse[0][key]); - }); + expect(_.isEqual(expectedResponse, serverResponse)).to.be.true; }); it('should get correct bid response with advertiserDomains', () => { @@ -186,7 +332,8 @@ describe('orbidderBidAdapter', () => { 'ttl': 60, 'netRevenue': true, 'currency': 'EUR', - 'advertiserDomains': ['cm.tavira.pt'] + 'advertiserDomains': ['cm.tavira.pt'], + 'mediaType': BANNER } ]; @@ -203,7 +350,8 @@ describe('orbidderBidAdapter', () => { 'netRevenue': true, 'meta': { 'advertiserDomains': ['cm.tavira.pt'] - } + }, + 'mediaType': BANNER } ]; @@ -215,24 +363,136 @@ describe('orbidderBidAdapter', () => { }); }); - it('handles broken server response', () => { + it('native: should get correct bid response', () => { + const serverResponse = [ + { + 'creativeId': '29681110', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'mediaType': NATIVE, + 'native': { + 'image': { + 'url': 'image url', + 'width': 300, + 'height': 250, + }, + 'icon': { + 'url': 'icon url', + 'width': 50, + 'height': 50, + }, + 'impressionTrackers': 'imp tracker', + 'clickUrl': 'click', + 'sponsoredBy': 'brand', + 'cta': 'action', + 'body': 'text', + } + } + ]; + + const expectedResponse = [ + { + 'creativeId': '29681110', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'mediaType': NATIVE, + 'native': { + 'image': { + 'url': 'image url', + 'width': 300, + 'height': 250, + }, + 'icon': { + 'url': 'icon url', + 'width': 50, + 'height': 50, + }, + 'impressionTrackers': 'imp tracker', + 'clickUrl': 'click', + 'sponsoredBy': 'brand', + 'cta': 'action', + 'body': 'text', + } + } + ]; + + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(expectedResponse.length); + expect(_.isEqual(expectedResponse, serverResponse)).to.be.true; + }); + + it('banner: handles broken bid response, missing creativeId', () => { const serverResponse = [ { 'ad': '', 'cpm': 0.5, 'requestId': '30b31c1838de1e', - 'ttl': 60 + 'ttl': 60, + 'currency': 'EUR', + 'mediaType': BANNER, + 'width': 300, + 'height': 250, + 'netRevenue': true, + } + ]; + const result = spec.interpretResponse({body: serverResponse}); + expect(result.length).to.equal(0); + }); + + it('banner: handles broken bid response, missing ad', () => { + const serverResponse = [ + { + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'currency': 'EUR', + 'mediaType': BANNER, + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'creativeId': '29681110', } ]; const result = spec.interpretResponse({body: serverResponse}); + expect(result.length).to.equal(0); + }); + it('native: handles broken bid response, missing impressionTrackers', () => { + const serverResponse = [ + { + 'creativeId': '29681110', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'mediaType': NATIVE, + 'native': { + 'title': 'native title', + 'sponsoredBy': 'test brand', + 'image': { + 'url': 'image url', + 'width': 300, + 'height': 250, + }, + 'clickUrl': 'click' + } + } + ]; + const result = spec.interpretResponse({body: serverResponse}); expect(result.length).to.equal(0); }); it('handles nobid responses', () => { const serverResponse = []; const result = spec.interpretResponse({body: serverResponse}); - expect(result.length).to.equal(0); }); }); From 3dcbb8a143f09d96cf7468885793b504a0af9ddf Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Tue, 6 Jul 2021 18:00:01 +0300 Subject: [PATCH 1240/1476] TheMediaGrid Bid Adapter: fix keywords workflow (#7130) * 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 Co-authored-by: Chris Huie --- modules/gridBidAdapter.js | 52 +++++++++++++----------- test/spec/modules/gridBidAdapter_spec.js | 42 +++++++++++++++++-- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index d4aceaea162..16f8ff11574 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -78,9 +78,6 @@ export const spec = { } const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; bidsMap[bidId] = bid; - if (!pageKeywords && !utils.isEmpty(keywords)) { - pageKeywords = utils.transformBidderParamKeywords(keywords); - } const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { @@ -104,6 +101,12 @@ export const spec = { impObj.ext.gpid = impObj.ext.data.adserver.adslot; } } + if (!utils.isEmpty(keywords)) { + if (!pageKeywords) { + pageKeywords = keywords; + } + impObj.ext.bidder = { keywords }; + } if (bidFloor) { impObj.bidfloor = bidFloor; @@ -185,17 +188,28 @@ export const spec = { request.user = user; } - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null - }); + const userKeywords = utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null; + const siteKeywords = utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null; - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); + if (userKeywords) { + pageKeywords = pageKeywords || {}; + pageKeywords.user = pageKeywords.user || {}; + pageKeywords.user.ortb2 = [ + { + name: 'keywords', + keywords: userKeywords.split(','), + } + ]; } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); + if (siteKeywords) { + pageKeywords = pageKeywords || {}; + pageKeywords.site = pageKeywords.site || {}; + pageKeywords.site.ortb2 = [ + { + name: 'keywords', + keywords: siteKeywords.split(','), + } + ]; } if (pageKeywords) { @@ -311,16 +325,6 @@ function _getFloor (mediaTypes, bid) { return floor; } -function isPopulatedArray(arr) { - return !!(utils.isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function _getBidFromResponse(respItem) { if (!respItem) { utils.logError(LOG_ERROR_MESS.emptySeatbid); @@ -341,11 +345,11 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { const bid = bidRequest.bidsMap[serverBid.impid]; if (bid) { const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, + requestId: bid.bidId, // bid.bidderRequestId cpm: serverBid.price, width: serverBid.w, height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, + creativeId: serverBid.auid, // bid.bidId currency: 'USD', netRevenue: true, ttl: TIME_TO_LIVE, diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 4f5f62f2cb8..2e5d2e63677 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -402,11 +402,47 @@ describe('TheMediaGrid Adapter', function () { it('should contain the keyword values if it present in ortb2.(site/user)', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); - const request = spec.buildRequests([bidRequests[0]], bidderRequest); + arg => arg === 'ortb2.user' ? {'keywords': 'foo,any'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); + const keywords = { + 'site': { + 'somePublisher': [ + { + 'name': 'someName', + 'brandsafety': ['disaster'], + 'topic': ['stress', 'fear'] + } + ] + } + }; + const bidRequestWithKW = { ...bidRequests[0], params: { ...bidRequests[0].params, keywords } } + const request = spec.buildRequests([bidRequestWithKW], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload.ext.keywords).to.deep.equal([{'key': 'user', 'value': ['foo']}, {'key': 'context', 'value': ['bar']}]); + expect(payload.ext.keywords).to.deep.equal({ + 'site': { + 'somePublisher': [ + { + 'name': 'someName', + 'brandsafety': ['disaster'], + 'topic': ['stress', 'fear'] + } + ], + 'ortb2': [ + { + 'name': 'keywords', + 'keywords': ['bar'] + } + ] + }, + 'user': { + 'ortb2': [ + { + 'name': 'keywords', + 'keywords': ['foo', 'any'] + } + ] + } + }); getConfigStub.restore(); }); From 612884b9b24527547b4f079089eef0fc25ad029f Mon Sep 17 00:00:00 2001 From: VideoReach <49446045+VideoReach@users.noreply.github.com> Date: Tue, 6 Jul 2021 17:37:23 +0200 Subject: [PATCH 1241/1476] Video Reach Bid Adapter: update to v5 (#7142) * Update to v3 * Prebid 5 * Prebid 5 - 'adomain' Support Co-authored-by: VideoReach --- modules/videoreachBidAdapter.js | 124 +++++++++++++++ .../spec/modules/videoreachBidAdapter_spec.js | 145 ++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 modules/videoreachBidAdapter.js create mode 100644 test/spec/modules/videoreachBidAdapter_spec.js diff --git a/modules/videoreachBidAdapter.js b/modules/videoreachBidAdapter.js new file mode 100644 index 00000000000..c307fc3e865 --- /dev/null +++ b/modules/videoreachBidAdapter.js @@ -0,0 +1,124 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +const utils = require('../src/utils.js'); +const BIDDER_CODE = 'videoreach'; +const ENDPOINT_URL = 'https://a.videoreach.com/hb/'; +const GVLID = 547; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: ['banner'], + + isBidRequestValid: function(bid) { + return !!(bid.params.TagId); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + let data = { + data: validBidRequests.map(function(bid) { + return { + TagId: utils.getValue(bid.params, 'TagId'), + adUnitCode: utils.getBidIdParameter('adUnitCode', bid), + bidId: utils.getBidIdParameter('bidId', bid), + bidderRequestId: utils.getBidIdParameter('bidderRequestId', bid), + auctionId: utils.getBidIdParameter('auctionId', bid), + transactionId: utils.getBidIdParameter('transactionId', bid) + } + }) + }; + + if (bidderRequest && bidderRequest.refererInfo) { + data.referrer = bidderRequest.refererInfo.referer; + } + + if (bidderRequest && bidderRequest.gdprConsent) { + data.gdpr = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + } + + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(data) + }; + }, + + interpretResponse: function(serverResponse) { + const bidResponses = []; + serverResponse = serverResponse.body; + + if (serverResponse.responses) { + serverResponse.responses.forEach(function (bid) { + const bidResponse = { + cpm: bid.cpm, + width: bid.width, + height: bid.height, + currency: bid.currency, + netRevenue: true, + ttl: bid.ttl, + ad: bid.ad, + requestId: bid.bidId, + creativeId: bid.creativeId, + meta: { + advertiserDomains: bid && bid.adomain ? bid.adomain : [] + } + }; + bidResponses.push(bidResponse); + }); + } + return bidResponses; + }, + + getUserSyncs: function(syncOptions, responses, gdprConsent) { + const syncs = []; + + if (responses.length && responses[0].body.responses.length) { + let params = ''; + var gdpr; + + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += 'gdpr=' + gdprConsent.gdprApplies + '&gdpr_consent=' + gdprConsent.consentString; + } else { + params += 'gdpr_consent=' + gdprConsent.consentString; + } + } + + if (syncOptions.pixelEnabled) { + const SyncPixels = responses[0].body.responses[0].sync; + + if (SyncPixels) { + SyncPixels.forEach(sync => { + gdpr = (params) ? ((sync.split('?')[1] ? '&' : '?') + params) : ''; + + syncs.push({ + type: 'image', + url: sync + gdpr + }); + }); + } + } + + if (syncOptions.iframeEnabled) { + const SyncFrame = responses[0].body.responses[0].syncframe; + + if (SyncFrame) { + SyncFrame.forEach(sync => { + gdpr = (params) ? ((sync.split('?')[1] ? '&' : '?') + params) : ''; + + syncs.push({ + type: 'iframe', + url: sync + gdpr + }); + }); + } + } + } + + return syncs; + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/videoreachBidAdapter_spec.js b/test/spec/modules/videoreachBidAdapter_spec.js new file mode 100644 index 00000000000..67ad89eac3d --- /dev/null +++ b/test/spec/modules/videoreachBidAdapter_spec.js @@ -0,0 +1,145 @@ +import {expect} from 'chai'; +import {spec} from 'modules/videoreachBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +const ENDPOINT_URL = 'https://a.videoreach.com/hb/'; + +describe('videoreachBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + 'params': { + 'TagId': 'ABCDE' + }, + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'TagId': '' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'videoreach', + 'params': { + 'TagId': 'ABCDE' + }, + 'adUnitCode': 'adzone', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'sizes': [[1, 1]], + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'mediaTypes': { + 'banner': { + 'sizes': [1, 1] + }, + } + } + ]; + + it('send bid request to endpoint', function () { + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.equal(ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + + it('send bid request with GDPR to endpoint', function () { + let consentString = 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'; + + let bidderRequest = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr.consent_required).to.exist; + expect(payload.gdpr.consent_string).to.equal(consentString); + }); + }); + + describe('interpretResponse', function () { + let serverResponse = + { + 'body': { + 'responses': [{ + 'bidId': '242d506d4e4f15', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'cpm': 10.0, + 'width': '1', + 'height': '1', + 'ad': '', + 'ttl': 360, + 'creativeId': '5cb5dc9375c0e', + 'netRevenue': true, + 'currency': 'EUR', + 'sync': ['https:\/\/SYNC_URL'], + 'adomain': [] + }] + } + }; + + it('should handle response', function() { + let expectedResponse = [ + { + cpm: 10.0, + width: '1', + height: '1', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: '', + requestId: '242d506d4e4f15', + creativeId: '5cb5dc9375c0e', + meta: { + advertiserDomains: [] + } + } + ]; + + let result = spec.interpretResponse(serverResponse); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('should handles empty response', function() { + let serverResponse = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(serverResponse); + expect(result.length).to.equal(0); + }); + + describe('getUserSyncs', () => { + it('should push user sync images if enabled', () => { + const syncOptions = { pixelEnabled: true }; + const syncs = spec.getUserSyncs(syncOptions, [serverResponse]); + + expect(syncs[0]).to.deep.equal({ + type: 'image', + url: 'https://SYNC_URL' + }); + }) + }); + }); +}); From 26aa81b798fc030e9198e1a7974e67c7a8352580 Mon Sep 17 00:00:00 2001 From: Noam Tzuberi Date: Tue, 6 Jul 2021 19:15:05 +0300 Subject: [PATCH 1242/1476] Upgrade Rise Bid Adapter: update to version 5.0 (#7143) * 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 Co-authored-by: Noam Tzuberi --- modules/riseBidAdapter.js | 272 ++++++++++++++++ test/spec/modules/riseBidAdapter_spec.js | 396 +++++++++++++++++++++++ 2 files changed, 668 insertions(+) create mode 100644 modules/riseBidAdapter.js create mode 100644 test/spec/modules/riseBidAdapter_spec.js diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js new file mode 100644 index 00000000000..b03c5c15056 --- /dev/null +++ b/modules/riseBidAdapter.js @@ -0,0 +1,272 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import {VIDEO} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; + +const SUPPORTED_AD_TYPES = [VIDEO]; +const BIDDER_CODE = 'rise'; +const BIDDER_VERSION = '4.0.1'; +const TTL = 360; +const CURRENCY = 'USD'; +const SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; +const MODES = { + PRODUCTION: 'hb', + TEST: 'hb-test' +} +const SUPPORTED_SYNC_METHODS = { + IFRAME: 'iframe', + PIXEL: 'pixel' +} + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: SUPPORTED_AD_TYPES, + isBidRequestValid: function(bidRequest) { + return !!(bidRequest.params.org); + }, + buildRequests: function (bidRequests, bidderRequest) { + if (bidRequests.length === 0) { + return []; + } + + const requests = []; + + bidRequests.forEach(bid => { + requests.push(buildVideoRequest(bid, bidderRequest)); + }); + + return requests; + }, + interpretResponse: function({body}) { + const bidResponses = []; + + const bidResponse = { + requestId: body.requestId, + cpm: body.cpm, + width: body.width, + height: body.height, + creativeId: body.requestId, + currency: body.currency, + netRevenue: body.netRevenue, + ttl: body.ttl || TTL, + vastXml: body.vastXml, + mediaType: VIDEO + }; + + if (body.adomain && body.adomain.length) { + bidResponse.meta = {}; + bidResponse.meta.advertiserDomains = body.adomain + } + bidResponses.push(bidResponse); + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + for (const response of serverResponses) { + if (syncOptions.iframeEnabled && response.body.userSyncURL) { + syncs.push({ + type: 'iframe', + url: response.body.userSyncURL + }); + } + if (syncOptions.pixelEnabled && utils.isArray(response.body.userSyncPixels)) { + const pixels = response.body.userSyncPixels.map(pixel => { + return { + type: 'image', + url: pixel + } + }) + syncs.push(...pixels) + } + } + return syncs; + } +}; + +registerBidder(spec); + +/** + * Get floor price + * @param bid {bid} + * @returns {Number} + */ +function getFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return 0; + } + let floorResult = bid.getFloor({ + currency: CURRENCY, + mediaType: VIDEO, + size: '*' + }); + return floorResult.currency === CURRENCY ? floorResult.floor : 0; +} + +/** + * Build the video request + * @param bid {bid} + * @param bidderRequest {bidderRequest} + * @returns {Object} + */ +function buildVideoRequest(bid, bidderRequest) { + const sellerParams = generateParameters(bid, bidderRequest); + const {params} = bid; + return { + method: 'GET', + url: getEndpoint(params.testMode), + data: sellerParams + }; +} + +/** + * Get the the ad size from the bid + * @param bid {bid} + * @returns {Array} + */ +function getSizes(bid) { + if (utils.deepAccess(bid, 'mediaTypes.video.sizes')) { + return bid.mediaTypes.video.sizes[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { + return bid.sizes[0]; + } + return []; +} + +/** + * Get schain string value + * @param schainObject {Object} + * @returns {string} + */ +function getSupplyChain(schainObject) { + if (utils.isEmpty(schainObject)) { + return ''; + } + let scStr = `${schainObject.ver},${schainObject.complete}`; + schainObject.nodes.forEach((node) => { + scStr += '!'; + scStr += `${getEncodedValIfNotEmpty(node.asi)},`; + scStr += `${getEncodedValIfNotEmpty(node.sid)},`; + scStr += `${getEncodedValIfNotEmpty(node.hp)},`; + scStr += `${getEncodedValIfNotEmpty(node.rid)},`; + scStr += `${getEncodedValIfNotEmpty(node.name)},`; + scStr += `${getEncodedValIfNotEmpty(node.domain)}`; + }); + return scStr; +} + +/** + * Get encoded node value + * @param val {string} + * @returns {string} + */ +function getEncodedValIfNotEmpty(val) { + return !utils.isEmpty(val) ? encodeURIComponent(val) : ''; +} + +/** + * Get preferred user-sync method based on publisher configuration + * @param bidderCode {string} + * @returns {string} + */ +function getAllowedSyncMethod(filterSettings, bidderCode) { + const iframeConfigsToCheck = ['all', 'iframe']; + const pixelConfigToCheck = 'image'; + if (filterSettings && iframeConfigsToCheck.some(config => isSyncMethodAllowed(filterSettings[config], bidderCode))) { + return SUPPORTED_SYNC_METHODS.IFRAME; + } + if (!filterSettings || !filterSettings[pixelConfigToCheck] || isSyncMethodAllowed(filterSettings[pixelConfigToCheck], bidderCode)) { + return SUPPORTED_SYNC_METHODS.PIXEL; + } +} + +/** + * Check if sync rule is supported + * @param syncRule {Object} + * @param bidderCode {string} + * @returns {boolean} + */ +function isSyncMethodAllowed(syncRule, bidderCode) { + if (!syncRule) { + return false; + } + const isInclude = syncRule.filter === 'include'; + const bidders = utils.isArray(syncRule.bidders) ? syncRule.bidders : [bidderCode]; + return isInclude && utils.contains(bidders, bidderCode); +} + +/** + * Get the seller endpoint + * @param testMode {boolean} + * @returns {string} + */ +function getEndpoint(testMode) { + return testMode + ? SELLER_ENDPOINT + MODES.TEST + : SELLER_ENDPOINT + MODES.PRODUCTION; +} + +/** + * Generate query parameters for the request + * @param bid {bid} + * @param bidderRequest {bidderRequest} + * @returns {Object} + */ +function generateParameters(bid, bidderRequest) { + const timeout = config.getConfig('bidderTimeout'); + const { syncEnabled, filterSettings } = config.getConfig('userSync') || {}; + const [ width, height ] = getSizes(bid); + const { params } = bid; + const { bidderCode } = bidderRequest; + const domain = window.location.hostname; + + const requestParams = { + auction_start: utils.timestamp(), + ad_unit_code: utils.getBidIdParameter('adUnitCode', bid), + tmax: timeout, + width: width, + height: height, + publisher_id: params.org, + floor_price: Math.max(getFloor(bid), params.floorPrice), + ua: navigator.userAgent, + bid_id: utils.getBidIdParameter('bidId', bid), + bidder_request_id: utils.getBidIdParameter('bidderRequestId', bid), + transaction_id: utils.getBidIdParameter('transactionId', bid), + session_id: utils.getBidIdParameter('auctionId', bid), + publisher_name: domain, + site_domain: domain, + bidder_version: BIDDER_VERSION + }; + + if (syncEnabled) { + const allowedSyncMethod = getAllowedSyncMethod(filterSettings, bidderCode); + if (allowedSyncMethod) { + requestParams.cs_method = allowedSyncMethod; + } + } + + if (bidderRequest.uspConsent) { + requestParams.us_privacy = bidderRequest.uspConsent; + } + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + requestParams.gdpr = bidderRequest.gdprConsent.gdprApplies; + requestParams.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + + if (params.ifa) { + requestParams.ifa = params.ifa; + } + + if (bid.schain) { + requestParams.schain = getSupplyChain(bid.schain); + } + + if (bidderRequest && bidderRequest.refererInfo) { + requestParams.referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + requestParams.page_url = config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); + } + + return requestParams; +} diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js new file mode 100644 index 00000000000..b6c2f4fc61a --- /dev/null +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -0,0 +1,396 @@ +import { expect } from 'chai'; +import { spec } from 'modules/riseBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; +import { VIDEO } from '../../../src/mediaTypes.js'; +import { deepClone } from 'src/utils.js'; + +const ENDPOINT = 'https://hb.yellowblue.io/hb'; +const TEST_ENDPOINT = 'https://hb.yellowblue.io/hb-test'; +const TTL = 360; + +describe('riseAdapter', 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 () { + const bid = { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [['640', '480']], + 'params': { + 'org': 'jdye8weeyirk00000001' + } + }; + + it('should return true when required params are passed', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not found', function () { + const newBid = Object.assign({}, bid); + delete newBid.params; + newBid.params = { + 'org': null + }; + expect(spec.isBidRequestValid(newBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [[640, 480]], + 'params': { + 'org': 'jdye8weeyirk00000001' + }, + 'bidId': '299ffc8cca0b87', + 'bidderRequestId': '1144f487e563f9', + 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', + } + ]; + + const testModeBidRequests = [ + { + 'bidder': spec.code, + 'adUnitCode': 'adunit-code', + 'sizes': [[640, 480]], + 'params': { + 'org': 'jdye8weeyirk00000001', + 'testMode': true + }, + 'bidId': '299ffc8cca0b87', + 'bidderRequestId': '1144f487e563f9', + 'auctionId': 'bfc420c3-8577-4568-9766-a8a935fb620d', + } + ]; + + const bidderRequest = { + bidderCode: 'rise', + } + + it('sends bid request to ENDPOINT via GET', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('GET'); + } + }); + + it('sends bid request to test ENDPOINT via GET', function () { + const requests = spec.buildRequests(testModeBidRequests, bidderRequest); + for (const request of requests) { + expect(request.url).to.equal(TEST_ENDPOINT); + expect(request.method).to.equal('GET'); + } + }); + + it('should send the correct bid Id', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.bid_id).to.equal('299ffc8cca0b87'); + } + }); + + it('should send the correct width and height', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('width', 640); + expect(request.data).to.have.property('height', 480); + } + }); + + it('should respect syncEnabled option', function() { + config.setConfig({ + userSync: { + syncEnabled: false, + filterSettings: { + all: { + bidders: '*', + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('cs_method'); + } + }); + + it('should respect "iframe" filter settings', function () { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + iframe: { + bidders: [spec.code], + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'iframe'); + } + }); + + it('should respect "all" filter settings', function () { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + all: { + bidders: [spec.code], + filter: 'include' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'iframe'); + } + }); + + it('should send the pixel user sync param if userSync is enabled and no "iframe" or "all" configs are present', function () { + config.setConfig({ + userSync: { + syncEnabled: true + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('cs_method', 'pixel'); + } + }); + + it('should respect total exclusion', function() { + config.setConfig({ + userSync: { + syncEnabled: true, + filterSettings: { + image: { + bidders: [spec.code], + filter: 'exclude' + }, + iframe: { + bidders: [spec.code], + filter: 'exclude' + } + } + } + }); + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('cs_method'); + } + }); + + it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithUSP); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('us_privacy', '1YNN'); + } + }); + + it('should have an empty us_privacy param if usPrivacy is missing in the bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('us_privacy'); + } + }); + + it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.not.have.property('gdpr'); + expect(request.data).to.not.have.property('gdpr_consent'); + } + }); + + it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { + const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const requests = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('gdpr', true); + expect(request.data).to.have.property('gdpr_consent', 'test-consent-string'); + } + }); + + it('should have schain param if it is available in the bidRequest', () => { + const schain = { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }], + }; + bidRequests[0].schain = schain; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('schain', '1.0,1!indirectseller.com,00001,,,,'); + } + }); + + it('should set floor_price to getFloor.floor value if it is greater than params.floorPrice', function() { + const bid = deepClone(bidRequests[0]); + bid.getFloor = () => { + return { + currency: 'USD', + floor: 3.32 + } + } + bid.params.floorPrice = 0.64; + const request = spec.buildRequests([bid], bidderRequest)[0]; + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('floor_price', 3.32); + }); + + it('should set floor_price to params.floorPrice value if it is greater than getFloor.floor', function() { + const bid = deepClone(bidRequests[0]); + bid.getFloor = () => { + return { + currency: 'USD', + floor: 0.8 + } + } + bid.params.floorPrice = 1.5; + const request = spec.buildRequests([bid], bidderRequest)[0]; + expect(request.data).to.be.an('object'); + expect(request.data).to.have.property('floor_price', 1.5); + }); + }); + + describe('interpretResponse', function () { + const response = { + cpm: 12.5, + vastXml: '', + width: 640, + height: 480, + requestId: '21e12606d47ba7', + netRevenue: true, + currency: 'USD', + adomain: ['abc.com'] + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + { + requestId: '21e12606d47ba7', + cpm: 12.5, + width: 640, + height: 480, + creativeId: '21e12606d47ba7', + currency: 'USD', + netRevenue: true, + ttl: TTL, + vastXml: '', + mediaType: VIDEO, + meta: { + advertiserDomains: ['abc.com'] + } + } + ]; + const result = spec.interpretResponse({ body: response }); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + }) + + describe('getUserSyncs', function() { + const imageSyncResponse = { + body: { + userSyncPixels: [ + 'https://image-sync-url.test/1', + 'https://image-sync-url.test/2', + 'https://image-sync-url.test/3' + ] + } + }; + + const iframeSyncResponse = { + body: { + userSyncURL: 'https://iframe-sync-url.test' + } + }; + + it('should register all img urls from the response', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, [imageSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://image-sync-url.test/1' + }, + { + type: 'image', + url: 'https://image-sync-url.test/2' + }, + { + type: 'image', + url: 'https://image-sync-url.test/3' + } + ]); + }); + + it('should register the iframe url from the response', function() { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [iframeSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://iframe-sync-url.test' + } + ]); + }); + + it('should register both image and iframe urls from the responses', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [iframeSyncResponse, imageSyncResponse]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://iframe-sync-url.test' + }, + { + type: 'image', + url: 'https://image-sync-url.test/1' + }, + { + type: 'image', + url: 'https://image-sync-url.test/2' + }, + { + type: 'image', + url: 'https://image-sync-url.test/3' + } + ]); + }); + + it('should handle an empty response', function() { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('should handle when user syncs are disabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: false }, [imageSyncResponse]); + expect(syncs).to.deep.equal([]); + }); + }) +}); From 568a90d0332cc9286328d41a3856cc111fe89590 Mon Sep 17 00:00:00 2001 From: Alexander Alexandrov Date: Tue, 6 Jul 2021 19:46:07 +0300 Subject: [PATCH 1243/1476] Revcontent Bid Adapter: adjusted for Price Floors Module (#7093) * Original adapter * REVC-3691 | Prebid.js support for price floors module (#1) * REVC-3691 | Prebid.js support for price floors module | Call getFloor function; test-case is added * REVC-3691 | 1 - Snippet with multi-items (#2) * REVC-3691 | Prebid.js support for price floors module | Snippet for multi-item * REVC-3691 | Prebid.js support for price floors module | Muti-item + fixed test-cases (#3) * REVC-3691 | Prebid.js support for price floors module | Fixed demo pages * REVC-3691 | Prebid.js support for price floors module | Added demo with multi-items * manually kick off circleci tests Co-authored-by: Chris Huie --- .../gpt/revcontent_example_banner.html | 116 ++++++ ...le.html => revcontent_example_native.html} | 14 +- modules/revcontentBidAdapter.js | 291 ++++++++++++++ .../spec/modules/revcontentBidAdapter_spec.js | 376 ++++++++++++++++++ 4 files changed, 790 insertions(+), 7 deletions(-) create mode 100644 integrationExamples/gpt/revcontent_example_banner.html rename integrationExamples/gpt/{revcontent_example.html => revcontent_example_native.html} (92%) create mode 100644 modules/revcontentBidAdapter.js create mode 100644 test/spec/modules/revcontentBidAdapter_spec.js diff --git a/integrationExamples/gpt/revcontent_example_banner.html b/integrationExamples/gpt/revcontent_example_banner.html new file mode 100644 index 00000000000..cadd2e1a0b7 --- /dev/null +++ b/integrationExamples/gpt/revcontent_example_banner.html @@ -0,0 +1,116 @@ + + + + + Prebid.js Banner Example + + + + + + + + + + + +

Prebid.js Banner Example

+
+ +
+
+
+ +
+
+ diff --git a/integrationExamples/gpt/revcontent_example.html b/integrationExamples/gpt/revcontent_example_native.html similarity index 92% rename from integrationExamples/gpt/revcontent_example.html rename to integrationExamples/gpt/revcontent_example_native.html index d7a44df3014..07a06f3a25d 100644 --- a/integrationExamples/gpt/revcontent_example.html +++ b/integrationExamples/gpt/revcontent_example_native.html @@ -1,9 +1,9 @@ - - - + Prebid.js Native Example + +
diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js new file mode 100644 index 00000000000..777060a5eb6 --- /dev/null +++ b/modules/revcontentBidAdapter.js @@ -0,0 +1,291 @@ +// jshint esversion: 6, es3: false, node: true +'use strict'; + +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'revcontent'; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } +}; +const STYLE_EXTRA = ''; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], + isBidRequestValid: function (bid) { + return (typeof bid.params.apiKey !== 'undefined' && typeof bid.params.userId !== 'undefined'); + }, + buildRequests: (validBidRequests, bidderRequest) => { + const userId = validBidRequests[0].params.userId; + const widgetId = validBidRequests[0].params.widgetId; + const apiKey = validBidRequests[0].params.apiKey; + var domain = validBidRequests[0].params.domain; + var host = validBidRequests[0].params.endpoint; + + if (typeof host === 'undefined') { + host = 'trends.revcontent.com'; + } + + let serverRequests = []; + var refererInfo; + if (bidderRequest && bidderRequest.refererInfo) { + refererInfo = bidderRequest.refererInfo.referer; + } + + if (typeof domain === 'undefined') { + domain = extractHostname(refererInfo); + } + + var endpoint = 'https://' + host + '/rtb?apiKey=' + apiKey + '&userId=' + userId; + + if (!isNaN(widgetId) && widgetId > 0) { + endpoint = endpoint + '&widgetId=' + widgetId; + } + + const imp = validBidRequests.map((bid, id) => buildImp(bid, id)); + + let data = { + id: bidderRequest.auctionId, + imp: imp, + site: { + id: widgetId, + domain: domain, + page: refererInfo, + publisher: { + id: userId, + domain: domain + } + }, + device: { + ua: navigator.userAgent, + language: 'en' + }, + user: { + id: 1 + }, + at: 2 + }; + serverRequests.push({ + method: 'POST', + options: { + contentType: 'application/json' + }, + url: endpoint, + data: JSON.stringify(data), + bid: validBidRequests + }); + + return serverRequests; + }, + interpretResponse: function (serverResponse, serverRequest) { + let response = serverResponse.body; + if ((!response) || (!response.seatbid)) { + return []; + } + + let rtbRequest = JSON.parse(serverRequest.data); + let rtbBids = response.seatbid + .map(seatbid => seatbid.bid) + .reduce((a, b) => a.concat(b), []); + + return rtbBids.map(rtbBid => { + const bidIndex = +rtbBid.impid - 1; + let imp = rtbRequest.imp.filter(imp => imp.id.toString() === rtbBid.impid)[0]; + + let prBid = { + requestId: serverRequest.bid[bidIndex].bidId, + cpm: rtbBid.price, + creativeId: rtbBid.crid, + nurl: rtbBid.nurl, + currency: response.cur || 'USD', + ttl: 360, + netRevenue: true, + bidder: 'revcontent', + bidderCode: 'revcontent' + }; + if ('banner' in imp) { + prBid.mediaType = BANNER; + prBid.width = rtbBid.w; + prBid.height = rtbBid.h; + prBid.ad = STYLE_EXTRA + rtbBid.adm; + } else if ('native' in imp) { + let adm = JSON.parse(rtbBid.adm); + let ad = { + clickUrl: adm.link.url + }; + + adm.assets.forEach(asset => { + switch (asset.id) { + case 3: + ad['image'] = { + url: asset.img.url, + height: 1, + width: 1 + }; + break; + case 0: + ad['title'] = asset.title.text; + break; + case 5: + ad['sponsoredBy'] = asset.data.value || 'Revcontent'; + break; + } + }); + var size = serverRequest.bid[0].params.size; + prBid.width = size.width; + prBid.height = size.height; + + prBid.mediaType = NATIVE; + prBid.native = ad; + prBid.ad = displayNative(ad, getTemplate(serverRequest.bid[0].params.size, serverRequest.bid[0].params.template)); + } + + return prBid; + }); + }, + onBidWon: function (bid) { + if (bid.nurl) { + utils.triggerPixel(bid.nurl); + } + return true; + } +}; + +registerBidder(spec); + +function displayNative(ad, template) { + template = template.replace(/{image}/g, ad['image']['url']); + template = template.replace(/{title}/g, ad['title']); + template = template.replace(/{clickUrl}/g, ad['clickUrl']); + template = template.replace(/{sponsoredBy}/g, ad['sponsoredBy']); + return template; +} + +function getTemplate(size, customTemplate) { + if (typeof (customTemplate) !== 'undefined' && customTemplate !== '') { + return customTemplate; + } + + if (size.width == 300 && size.height == 250) { + return '

{title}

SEE MORE
'; + } + + if (size.width == 728 && size.height == 90) { + return '

{title}

>
'; + } + + if (size.width == 300 && size.height == 600) { + return '

{title}

>
'; + } + + return ''; +} + +function extractHostname(url) { + if (typeof url == 'undefined' || url == null) { + return ''; + } + var hostname; + if (url.indexOf('//') > -1) { + hostname = url.split('/')[2]; + } else { + hostname = url.split('/')[0]; + } + + hostname = hostname.split(':')[0]; + hostname = hostname.split('?')[0]; + + return hostname; +} + +function buildImp(bid, id) { + let bidfloor; + if (utils.isFn(bid.getFloor)) { + bidfloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }).floor; + } else { + bidfloor = utils.deepAccess(bid, `params.bidfloor`) || 0.1; + } + + let imp = { + id: id + 1, + tagid: bid.adUnitCode, + bidderRequestId: bid.bidderRequestId, + auctionId: bid.auctionId, + transactionId: bid.transactionId, + instl: 0, + bidfloor: bidfloor, + secure: '1' + }; + + let bannerReq = utils.deepAccess(bid, `mediaTypes.banner`); + let nativeReq = utils.deepAccess(bid, `mediaTypes.native`); + if (bannerReq) { + let sizes = utils.getAdUnitSizes(bid); + imp.banner = { + w: sizes[0][0], + h: sizes[0][1], + format: sizes.map(wh => utils.parseGPTSingleSizeArrayToRtbSize(wh)), + } + } else if (nativeReq) { + const assets = utils._map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1 + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); + imp.native = { + request: { + ver: '1.1', + context: 2, + contextsubtype: 21, + plcmttype: 1, + plcmtcnt: 1, + assets: assets + }, + ver: '1.1', + battr: [1, 3, 8, 11, 17] + }; + } + return imp; +} diff --git a/test/spec/modules/revcontentBidAdapter_spec.js b/test/spec/modules/revcontentBidAdapter_spec.js new file mode 100644 index 00000000000..022dd0e1aa9 --- /dev/null +++ b/test/spec/modules/revcontentBidAdapter_spec.js @@ -0,0 +1,376 @@ +// jshint esversion: 6, es3: false, node: true +import {assert, expect} from 'chai'; +import {spec} from 'modules/revcontentBidAdapter.js'; +import { NATIVE } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; +import * as utils from 'src/utils.js'; + +describe('revcontent adapter', function () { + let serverResponse, bidRequest, bidResponses; + let bids = []; + + describe('isBidRequestValid', function () { + let bid = { + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + bid.params.apiKey = undefined; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + it('should send request with correct structure', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + widgetId: 33861, + endpoint: 'trends-s0.revcontent.com' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + request = request[0]; + assert.equal(request.method, 'POST'); + assert.equal(request.url, 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673&widgetId=33861'); + assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.ok(request.data); + }); + + it('should have default request structure', function () { + let keys = 'method,options,url,data,bid'.split(','); + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + + request = request[0]; + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); + + it('should send info about device and unique bidfloor', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com', + bidfloor: 0.05 + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + request = JSON.parse(request[0].data); + assert.equal(request.imp[0].bidfloor, 0.05); + assert.equal(request.device.ua, navigator.userAgent); + }); + + it('should send info about device and use getFloor', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: {}, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com', + bidfloor: 0.05 + } + }]; + validBidRequests[0].getFloor = () => { + return { + floor: 0.07, + currency: 'USD' + }; + }; + let request = spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}); + request = JSON.parse(request[0].data); + assert.equal(request.imp[0].bidfloor, 0.07); + assert.equal(request.device.ua, navigator.userAgent); + }); + + it('should send info about the site and default bidfloor', function () { + let validBidRequests = [{ + bidder: 'revcontent', + nativeParams: { + image: { + required: false + }, + 'title': { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } + }, + params: { + size: {width: 300, height: 250}, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + } + }]; + let refererInfo = {referer: 'page'}; + let request = spec.buildRequests(validBidRequests, {refererInfo}); + + request = JSON.parse(request[0].data); + assert.equal(request.imp[0].bidfloor, 0.1); + assert.deepEqual(request.site, { + domain: 'test.com', + page: 'page', + publisher: {id: 673, domain: 'test.com'} + }); + }); + }); + + describe('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; + + let result = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(result.length, 0); + }); + + const serverResponse = { + body: { + id: '5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58', + seatbid: [ + { + bid: [ + { + id: '6bbe3eed-f443-4e2b-a8da-57fd6327b37d', + impid: '1', + price: 0.1, + crid: '4162547', + nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', + adm: '{"ver":"1.1","assets":[{"id":3,"required":1,"img":{"url":"//img.revcontent.com/?url=https://revcontent-p0.s3.amazonaws.com/content/images/15761052960288727821.jpg&static=true"}},{"id":0,"required":1,"title":{"text":"Do You Eat Any of These Craving-trigger Foods?"}},{"id":5,"required":1,"data":{"value":""}}],"link":{"url":"https://trends-s0.revcontent.com/click.php?d=A7EVbNYBVyonty19Ak08zCr9J54qg%2Bmduq6p0Zyn5%2F%2Bapm4deUo9VAXmOGEIbUBf6i7m3%2F%2FWJm%2FzTha8SJ%2Br9MZL9jhhUxDeiKb6aRY1biLrvr6tFUd1phvtKqVmPd76l9VBLFMxS1brSzKjRCJlIGmyGJg7ueFvxpE9X%2BpHmdbE2uqUdRC49ENO3XZyHCCKMAZ8XD29fasX9Kli9mKpZTqw8vayFlXbVYSUwB8wfSwCt1sIUrt0aICYc0jcyWU3785GTS1xXzQj%2FIVszFYYrdTWd%2BDijjNZtFny0OomPHp8lRy5VcQVCuLpw0Fks4myvsE38XcNvs4wO3tWTNrI%2BMqcW1%2BD2OnMSq5nN5FCbmi2ly%2F1LbN9fibaFvW%2FQbzQhN9ZsAwmhm409UTtdmSA6hd96vDxDWLeUJhVO3UQyI0yq2TtVnB9tEICD8mZNWwYehOab%2BQ1EWmTerF6ZCDx8RyZus1UrsDfRwvTCyUjCmkZhmeo4QVJkpPy6QobCsngSaxkkKhH%2Fb7coZyBXXEt3ORoYBLUbfRO6nR8GdIt8413vrYr4gTAroh46VcWK0ls0gFNe2u3%2FqP%2By1yLKbzDVaR%2Fa02G%2Biiqbw86sCYfsy7qK9atyjNTm8RkH6JLESUzxc6IEazu4iwHKGnu5phTacmseXCi8y9Y5AdBZn8VnLP%2F2a%2FyAqq93xEH%2BIrkAdhGRY1tY39rBYAtvH%2FVyNFZcong%2FutUMYbp0WhDNyfl6iWxmpE28Cx9KDcqXss0NIwQm0AWeu8ogJCIG3faAkm5PdFsUdf2X9h3HuFDbnbvnXW27ml6z9GykEzv%2F8aSZlMZ"}}' + } + ] + } + ], + bidid: '7f729368-edb2-427a-bde7-a55b3bf8837c' + }, + headers: {} + }; + + const bidRequest = { + method: 'POST', + options: { + contentType: 'application/json' + }, + url: 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673', + data: '{"id":"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58","imp":[{"id":1,"bidderRequestId":"14e4dab7b5396e8","auctionId":"5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58","transactionId":"69e69abf-a3ea-484d-a81c-d48dd0d5eaa3","native":{"request":{"ver":"1.1","context":2,"contextsubtype":21,"plcmttype":4,"plcmtcnt":4,"assets":[{"required":0,"id":3,"img":{"type":3}},{"required":0,"id":0,"title":{"len":140}},{"required":0,"id":5,"data":{"type":1}}]},"ver":"1.1","battr":[1,3,8,11,17]},"instl":0,"bidfloor":0.1,"secure":"1"}],"site":{"domain":"test.com","page":"https://feudfun.com/test22/revcontent_example.php","cat":["IAB17"],"publisher":{"id":673,"domain":"test.com"}},"device":{"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:71.0) Gecko/20100101 Firefox/71.0","language":"en"},"user":{"id":1},"at":2,"bcat":["IAB24","IAB25","IAB25-1","IAB25-2","IAB25-3","IAB25-4","IAB25-5","IAB25-6","IAB25-7","IAB26","IAB26-1","IAB26-2","IAB26-3","IAB26-4"]}', + bid: [ + { + bidder: 'revcontent', + params: { + size: { + width: 300, + height: 250 + }, + apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', + userId: 673, + domain: 'test.com', + endpoint: 'trends-s0.revcontent.com' + }, + crumbs: { + pubcid: '7a0b4adc-c109-49f0-aadc-4a4b62ebe269' + }, + nativeParams: { + image: { + required: false + }, + 'title': { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } + }, + mediaTypes: { + native: { + image: { + required: false + }, + title: { + required: false, + len: 140 + }, + clickUrl: { + required: false + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + } + } + }, + adUnitCode: '/19968336/header-bid-tag-1', + transactionId: '69e69abf-a3ea-484d-a81c-d48dd0d5eaa3', + sizes: [], + bidId: '294a7f446202848', + bidderRequestId: '14e4dab7b5396e8', + auctionId: '5d61ca27-1b7a-4d5a-90ad-bbfc93e53f58', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + } + ] + }; + + it('should set correct native params', function () { + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template 728x90', function () { + bidRequest.bid[0].params.size.width = 728; + bidRequest.bid[0].params.size.height = 90; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template 300x600', function () { + bidRequest.bid[0].params.size.width = 300; + bidRequest.bid[0].params.size.height = 600; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template custom template', function () { + bidRequest.bid[0].params.template = '

{title}

SEE MORE
'; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('validate template custom invalid template', function () { + bidRequest.bid[0].params.size.width = 100; + bidRequest.bid[0].params.size.height = 200; + + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.equal(result.bidder, 'revcontent'); + assert.equal(result.bidderCode, 'revcontent'); + assert.equal(result.mediaType, 'native'); + assert.equal(result.requestId, '294a7f446202848'); + assert.equal(result.cpm, '0.1'); + assert.equal(result.creativeId, '4162547'); + }); + + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{bid: []}], + cur: 'USD' + } + }; + let bidRequest = { + data: '{}', + bids: [{bidId: 'bidId1'}] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); + }); + + describe('onBidWon', function () { + it('default bid won', function () { + const bid = { + nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', + cpm: '0.1' + }; + const result = spec.onBidWon(bid); + assert.ok(result); + }); + }); + + describe('onBidWon', function() { + const bid = { + nurl: 'https://trends-s0.revcontent.com/push/track/?p=${AUCTION_PRICE}&d=nTCdHIfsgKOLFuV7DS1LF%2FnTk5HiFduGU65BgKgB%2BvKyG9YV7ceQWN76HMbBE0C6gwQeXUjravv3Hq5x9TT8CM6r2oUNgkGC9mhgv2yroTH9i3cSoH%2BilxyY19fMXFirtBz%2BF%2FEXKi4bsNh%2BDMPfj0L4elo%2FJEZmx4nslvOneJJjsFjJJtUJc%2F3UPivOisSCa%2B36mAgFQqt%2FSWBriYB%2BVAufz70LaGspF6T6jDzuIyVFJUpLhZVDtLRSJEzh7Lyzzw1FmYarp%2FPg0gZDY48aDdjw5A3Tlj%2Bap0cPHLDprNOyF0dmHDn%2FOVJEDRTWvrQ2JNK1t%2Fg1bGHIih0ec6XBVIBNurqRpLFBuUY6LgXCt0wRZWTByTEZ8AEv8IoYVILJAL%2BXL%2F9IyS4eTcdOUfn5X7gT8QBghCrAFrsCg8ZXKgWddTEXbpN1lU%2FzHdI5eSHkxkJ6WcYxSkY9PyripaIbmKiyb98LQMgTD%2B20RJO5dAmXTQTAcauw6IUPTjgSPEU%2Bd6L5Txd3CM00Hbd%2Bw1bREIQcpKEmlMwrRSwe4bu1BCjlh5A9gvU9Xc2sf7ekS3qPPmtp059r5IfzdNFQJB5aH9HqeDEU%2FxbMHx4ggMgojLBBL1fKrCKLAteEDQxd7PVmFJv7GHU2733vt5TnjKiEhqxHVFyi%2B0MIYMGIziM5HfUqfq3KUf%2F%2FeiCtJKXjg7FS6hOambdimSt7BdGDIZq9QECWdXsXcQqqVLwli27HYDMFVU3TWWRyjkjbhnQID9gQJlcpwIi87jVAODb6qP%2FKGQ%3D%3D', + cpm: '0.1' + }; + + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function() { + utils.triggerPixel.restore(); + }); + + it('make sure only 1 ajax call is happening', function() { + spec.onBidWon(bid); + expect(utils.triggerPixel.calledOnce).to.equal(true); + }); + }); +}); From ebaa5e55cc4e4b4737167493459849cb2615eac6 Mon Sep 17 00:00:00 2001 From: jsut Date: Wed, 7 Jul 2021 14:25:55 -0400 Subject: [PATCH 1244/1476] Inject creativeComment after render so it actually persists (#6860) * Inject creativeComment after render so it sticks around * send the event last in all the renderer flows --- src/prebid.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prebid.js b/src/prebid.js index 203ca05bf01..3264a5fc60b 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -446,10 +446,10 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { const {height, width, ad, mediaType, adUrl, renderer} = bid; const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); - utils.insertElement(creativeComment, doc, 'body'); if (isRendererRequired(renderer)) { executeRenderer(renderer, bid); + utils.insertElement(creativeComment, doc, 'html'); emitAdRenderSucceeded({ doc, bid, id }); } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; @@ -468,6 +468,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { doc.write(ad); doc.close(); setRenderSize(doc, width, height); + utils.insertElement(creativeComment, doc, 'html'); utils.callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else if (adUrl) { @@ -480,6 +481,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { utils.insertElement(iframe, doc, 'body'); setRenderSize(doc, width, height); + utils.insertElement(creativeComment, doc, 'html'); utils.callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else { From a2c68d3401491e9883fb105594de461fee0960eb Mon Sep 17 00:00:00 2001 From: amishra11j <86069270+amishra11j@users.noreply.github.com> Date: Thu, 8 Jul 2021 00:24:21 +0530 Subject: [PATCH 1245/1476] Updating the akamaiDAPIdSystem submodule with x1 APIs (#7137) --- .../gpt/akamaidap_email_example.html | 5 +- .../gpt/akamaidap_signature_example.html | 5 +- .../gpt/akamaidap_x1_example.html | 119 ++++++++++++++++++ modules/akamaiDAPIdSystem.js | 37 ++++-- modules/akamaiDAPIdSystem.md | 11 +- modules/userId/userId.md | 4 +- test/spec/modules/akamaiDAPIdSystem_spec.js | 25 +++- 7 files changed, 182 insertions(+), 24 deletions(-) create mode 100755 integrationExamples/gpt/akamaidap_x1_example.html diff --git a/integrationExamples/gpt/akamaidap_email_example.html b/integrationExamples/gpt/akamaidap_email_example.html index ef62876231e..828b2add787 100755 --- a/integrationExamples/gpt/akamaidap_email_example.html +++ b/integrationExamples/gpt/akamaidap_email_example.html @@ -60,8 +60,9 @@ apiHostname: 'prebid.dap.akadns.net', domain: 'prebid.org', type: 'email', - identity: 'aaryn@query.com' - } + identity: 'aaryn@query.com', + apiVersion: 'v1' + }, }, ], syncDelay: 5000, diff --git a/integrationExamples/gpt/akamaidap_signature_example.html b/integrationExamples/gpt/akamaidap_signature_example.html index 94940a659c1..e4c7c617653 100644 --- a/integrationExamples/gpt/akamaidap_signature_example.html +++ b/integrationExamples/gpt/akamaidap_signature_example.html @@ -59,8 +59,9 @@ params: { apiHostname: 'prebid.dap.akadns.net', domain: 'prebid.org', - type: 'dap-signature:1.0.0' - } + type: 'dap-signature:1.0.0', + apiVersion: 'v1' + }, }, ], syncDelay: 5000, diff --git a/integrationExamples/gpt/akamaidap_x1_example.html b/integrationExamples/gpt/akamaidap_x1_example.html new file mode 100755 index 00000000000..b1f16acc560 --- /dev/null +++ b/integrationExamples/gpt/akamaidap_x1_example.html @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+
User IDs Sent to Bidding Adapter
+
+ + diff --git a/modules/akamaiDAPIdSystem.js b/modules/akamaiDAPIdSystem.js index 980fcfaa7f2..c64502a8c5a 100644 --- a/modules/akamaiDAPIdSystem.js +++ b/modules/akamaiDAPIdSystem.js @@ -66,27 +66,38 @@ export const akamaiDAPIdSubmodule = { let url = ''; let postData; let tokenName = ''; - if (configParams.type.indexOf('dap-signature:') == 0) { - let parts = configParams.type.split(':'); - let v = parts[1]; - url = `https://${configParams.apiHostname}/data-activation/v1/domain/${configParams.domain}/signature?v=${v}&gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; - tokenName = 'SigToken'; + if (configParams.apiVersion === 'v1') { + if (configParams.type.indexOf('dap-signature:') == 0) { + let parts = configParams.type.split(':'); + let v = parts[1]; + url = `https://${configParams.apiHostname}/data-activation/v1/domain/${configParams.domain}/signature?v=${v}&gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + tokenName = 'SigToken'; + } else { + url = `https://${configParams.apiHostname}/data-activation/v1/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + postData = { + 'version': 1, + 'domain': configParams.domain, + 'identity': configParams.identity, + 'type': configParams.type + }; + tokenName = 'PubToken'; + } } else { - url = `https://${configParams.apiHostname}/data-activation/v1/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + url = `https://${configParams.apiHostname}/data-activation/x1/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; postData = { - 'version': 1, + 'version': configParams.apiVersion, 'domain': configParams.domain, 'identity': configParams.identity, - 'type': configParams.type + 'type': configParams.type, + 'attributes': configParams.attributes }; - tokenName = 'PubToken'; + tokenName = 'x1Token'; } - utils.logInfo('akamaiDAPId[getId] making API call for ' + tokenName); - let cb = { - success: response => { - storage.setDataInLocalStorage(STORAGE_KEY, response); + success: (response, request) => { + var token = (response === '') ? request.getResponseHeader('Akamai-DAP-Token') : response; + storage.setDataInLocalStorage(STORAGE_KEY, token); }, error: error => { utils.logError('akamaiDAPId [getId:ajax.error] failed to retrieve ' + tokenName, error); diff --git a/modules/akamaiDAPIdSystem.md b/modules/akamaiDAPIdSystem.md index 888a409b7a8..9b35709c3f2 100644 --- a/modules/akamaiDAPIdSystem.md +++ b/modules/akamaiDAPIdSystem.md @@ -15,7 +15,7 @@ Please reach out to your Akamai account representative(Prebid@akamai.com) to get First, make sure to add the DAP submodule to your Prebid.js package with: ``` -gulp build --modules=akamaiDAPId,userId +gulp build --modules=akamaiDAPIdSystem,userId ``` The following configuration parameters are available: @@ -29,13 +29,20 @@ pbjs.setConfig({ apiHostname: '', domain: 'your-domain.com', type: 'email' | 'mobile' | ... | 'dap-signature:1.0.0', - identity: ‘your@email.com’ | ‘6175551234' | ... + identity: ‘your@email.com’ | ‘6175551234' | ...', + apiVersion: 'v1' | 'x1', + attributes: '{ "cohorts": [ "3:14400", "5:14400", "7:0" ],"first_name": "...","last_name": "..." }' }, }], auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules } }); ``` + +In order to make use of v1 APIs, "apiVersion" needs to explicitly mentioned as 'v1'. The "apiVersion" defaults to x1 if not specified. +"attributes" can be configured in x1 API only and not v1 APIs. Please ensure that the "attributes" value is in same format as shown above. + Refer to the sample integration example present at below location Prebid.js/integrationExamples/gpt/akamaidap_email_example.html Prebid.js/integrationExamples/gpt/akamaidap_signature_example.html +Prebid.js/integrationExamples/gpt/akamaidap_x1_example.html diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 1dbdf7588ec..7d352505aa9 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -56,7 +56,9 @@ pbjs.setConfig({ apiHostname: '', domain: 'your-domain.com', type: 'email' | 'mobile' | ... | 'dap-signature:1.0.0', - identity: ‘your@email.com’ | ‘6175551234' | ... + identity: ‘your@email.com’ | ‘6175551234' | ..., + apiVersion: 'v1' | 'x1', + attributes: '{ "cohorts": [ "3:14400", "5:14400", "7:0" ],"first_name": "...","last_name": "..." }' } },{ name: 'identityLink', diff --git a/test/spec/modules/akamaiDAPIdSystem_spec.js b/test/spec/modules/akamaiDAPIdSystem_spec.js index afb5fdf043b..e44285eda34 100644 --- a/test/spec/modules/akamaiDAPIdSystem_spec.js +++ b/test/spec/modules/akamaiDAPIdSystem_spec.js @@ -8,14 +8,25 @@ export const storage = getStorageManager(); const signatureConfigParams = {params: { apiHostname: 'prebid.dap.akadns.net', domain: 'prebid.org', - type: 'dap-signature:1.0.0' + type: 'dap-signature:1.0.0', + apiVersion: 'v1' }}; const tokenizeConfigParams = {params: { apiHostname: 'prebid.dap.akadns.net', domain: 'prebid.org', type: 'email', - identity: 'amishra@xyz.com' + identity: 'amishra@xyz.com', + apiVersion: 'v1' +}}; + +const x1TokenizeConfigParams = {params: { + apiHostname: 'prebid.dap.akadns.net', + domain: 'prebid.org', + type: 'email', + identity: 'amishra@xyz.com', + apiVersion: 'x1', + attributes: '{ "cohorts": [ "3:14400", "5:14400", "7:0" ],"first_name": "Ace","last_name": "McCool" }' }}; const consentData = { @@ -85,16 +96,22 @@ describe('akamaiDAPId getId', function () { expect(submoduleCallback).to.be.undefined; }); - it('should call the signature API and store token in Local storage', function () { + it('should call the signature v1 API and store token in Local storage', function () { let submoduleCallback1 = akamaiDAPIdSubmodule.getId(signatureConfigParams, consentData).id; expect(submoduleCallback1).to.be.eq(storage.getDataFromLocalStorage('akamai_dap_token')) storage.removeDataFromLocalStorage('akamai_dap_token'); }); - it('should call the tokenize API and store token in Local storage', function () { + it('should call the tokenize v1 API and store token in Local storage', function () { let submoduleCallback = akamaiDAPIdSubmodule.getId(tokenizeConfigParams, consentData).id; expect(submoduleCallback).to.be.eq(storage.getDataFromLocalStorage('akamai_dap_token')) storage.removeDataFromLocalStorage('akamai_dap_token'); }); + + it('should call the tokenize x1 API and store token in Local storage', function () { + let submoduleCallback = akamaiDAPIdSubmodule.getId(x1TokenizeConfigParams, consentData).id; + expect(submoduleCallback).to.be.eq(storage.getDataFromLocalStorage('akamai_dap_token')) + storage.removeDataFromLocalStorage('akamai_dap_token'); + }); }); }); From 797c7ef9281060bdf211f2990062a613f1e1cca7 Mon Sep 17 00:00:00 2001 From: Artem Aleksashkin Date: Thu, 8 Jul 2021 02:00:57 +0300 Subject: [PATCH 1246/1476] Kubient Bid Adapter: add video support, issues fixes, and compliance with v5 (#7058) --- modules/kubientBidAdapter.js | 152 ++++++++ modules/kubientBidAdapter.md | 67 +++- test/spec/modules/kubientBidAdapter_spec.js | 399 ++++++++++++++++++++ 3 files changed, 602 insertions(+), 16 deletions(-) create mode 100644 modules/kubientBidAdapter.js create mode 100644 test/spec/modules/kubientBidAdapter_spec.js diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js new file mode 100644 index 00000000000..6751a8a567c --- /dev/null +++ b/modules/kubientBidAdapter.js @@ -0,0 +1,152 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'kubient'; +const END_POINT = 'https://kssp.kbntx.ch/kubprebidjs'; +const VERSION = '1.1'; +const VENDOR_ID = 794; +export const spec = { + code: BIDDER_CODE, + gvlid: VENDOR_ID, + supportedMediaTypes: [ BANNER, VIDEO ], + isBidRequestValid: function (bid) { + return !!( + bid && + bid.params && + bid.params.zoneid && + ((!bid.mediaTypes.video) || (bid.mediaTypes.video && bid.mediaTypes.video.playerSize && bid.mediaTypes.video.mimes && bid.mediaTypes.video.protocols)) + ); + }, + buildRequests: function (validBidRequests, bidderRequest) { + if (!validBidRequests || !bidderRequest) { + return; + } + return validBidRequests.map(function (bid) { + let floor = 0.0; + if (typeof bid.getFloor === 'function') { + const mediaType = (Object.keys(bid.mediaTypes).length == 1) ? Object.keys(bid.mediaTypes)[0] : '*'; + const sizes = bid.sizes || '*'; + const floorInfo = bid.getFloor({currency: 'USD', mediaType: mediaType, size: sizes}); + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { + floor = parseFloat(floorInfo.floor); + } + } + + let adSlot = { + bidId: bid.bidId, + zoneId: bid.params.zoneid || '', + floor: floor || 0.0 + }; + + if (bid.mediaTypes.banner) { + adSlot.banner = bid.mediaTypes.banner; + } + + if (bid.mediaTypes.video) { + adSlot.video = bid.mediaTypes.video; + } + + if (bid.schain) { + adSlot.schain = bid.schain; + } + + let data = { + v: VERSION, + requestId: bid.bidderRequestId, + adSlots: [adSlot], + tmax: bidderRequest.timeout, + gdpr: (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0, + consentGiven: kubientGetConsentGiven(bidderRequest.gdprConsent), + uspConsent: bidderRequest.uspConsent + }; + + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + data.referer = bidderRequest.refererInfo.referer + } + + if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) { + data.consent = bidderRequest.gdprConsent.consentString + } + + return { + method: 'POST', + url: END_POINT, + data: JSON.stringify(data) + }; + }); + }, + interpretResponse: function interpretResponse(serverResponse, request) { + if (!serverResponse || !serverResponse.body || !serverResponse.body.seatbid) { + return []; + } + let bidResponses = []; + serverResponse.body.seatbid.forEach(seatbid => { + let bids = seatbid.bid || []; + bids.forEach(bid => { + const bidResponse = { + requestId: bid.bidId, + cpm: bid.price, + currency: bid.cur, + width: bid.w, + height: bid.h, + creativeId: bid.creativeId, + netRevenue: bid.netRevenue, + ttl: bid.ttl, + ad: bid.adm, + meta: {} + }; + if (bid.meta && bid.meta.adomain && utils.isArray(bid.meta.adomain)) { + bidResponse.meta.advertiserDomains = bid.meta.adomain; + } + if (bid.mediaType === VIDEO) { + bidResponse.mediaType = VIDEO; + bidResponse.vastXml = bid.adm; + } + bidResponses.push(bidResponse); + }); + }); + return bidResponses; + }, + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncs = []; + let gdprParams = ''; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + gdprParams = `?consent_str=${gdprConsent.consentString}`; + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = gdprParams + `&gdpr=${Number(gdprConsent.gdprApplies)}`; + } + gdprParams = gdprParams + `&consent_given=` + kubientGetConsentGiven(gdprConsent); + } + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: 'https://kdmp.kbntx.ch/init.html' + gdprParams + }); + } + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: 'https://kdmp.kbntx.ch/init.png' + gdprParams + }); + } + return syncs; + } +}; + +function kubientGetConsentGiven(gdprConsent) { + let consentGiven = 0; + if (typeof gdprConsent !== 'undefined') { + let apiVersion = utils.deepAccess(gdprConsent, `apiVersion`); + switch (apiVersion) { + case 1: + consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendorConsents.${VENDOR_ID}`) ? 1 : 0; + break; + case 2: + consentGiven = utils.deepAccess(gdprConsent, `vendorData.vendor.consents.${VENDOR_ID}`) ? 1 : 0; + break; + } + } + return consentGiven; +} +registerBidder(spec); diff --git a/modules/kubientBidAdapter.md b/modules/kubientBidAdapter.md index 9f3e1d5f52e..a45d26dbc31 100644 --- a/modules/kubientBidAdapter.md +++ b/modules/kubientBidAdapter.md @@ -6,21 +6,56 @@ ​ # Description ​ -Connects to Kubient KSSP demand source to fetch bids. +Connects to Kubient KSSP demand source to fetch bids. Banners and Video supported ​ -# Test Parameters +# Banner Test Parameters +``` +var adUnits = [ + { + code: 'banner-ad-unit', + mediaTypes: { + banner: { + sizes: [[300, 100]] + } + }, + bids: [{ + bidder: 'kubient', + params: { + zoneid: "5fbb948f1e22b", + } + }] + } +]; +​ +# Video Test Parameters +​ +var adUnits = [ + { + code: 'video-ad-unit', + mediaTypes: { + video: { + playerSize: [300, 250], // required + context: 'instream', // required + mimes: ['video/mp4','video/x-flv'], // required + protocols: [ 2, 3 ], // required, set at least 1 value in array + placement: 1, // optional, defaults to 2 when context = outstream + api: [ 1, 2 ], // optional + skip: 0, // optional + minduration: 5, // optional + maxduration: 30, // optional + playbackmethod: [1,3], // optional + battr: [ 13, 14 ], // optional + linearity: 1, // optional + minbitrate: 10, // optional + maxbitrate: 10 // optional + } + }, + bids: [{ + bidder: 'kubient', + params: { + zoneid: "60ad1c0b35864", + } + }] + } +]; ``` - var adUnits = [{ - code: 'banner-ad-div', - mediaTypes: { - banner: { - sizes: [[300, 250],[728, 90]], - } - }, - bids: [{ - "bidder": "kubient", - "params": { - "zoneid": "5fbb948f1e22b", - } - }] - }]; diff --git a/test/spec/modules/kubientBidAdapter_spec.js b/test/spec/modules/kubientBidAdapter_spec.js new file mode 100644 index 00000000000..5449de0c4de --- /dev/null +++ b/test/spec/modules/kubientBidAdapter_spec.js @@ -0,0 +1,399 @@ +import { expect, assert } from 'chai'; +import { spec } from 'modules/kubientBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; + +describe('KubientAdapter', function () { + let bidBanner = { + bidId: '2dd581a2b6281d', + bidder: 'kubient', + bidderRequestId: '145e1d6a7837c9', + params: { + zoneid: '5678' + }, + getFloor: function(params) { + return { + floor: 0.05, + currency: 'USD' + }; + }, + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + schain: { + ver: '1.1', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + domain: 'example.com' + } + ] + } + }; + let bidVideo = { + bidId: '1dd581a2b6281d', + bidder: 'kubient', + bidderRequestId: '245e1d6a7837c9', + params: { + zoneid: '5676' + }, + getFloor: function(params) { + return { + floor: 1.0, + currency: 'USD' + }; + }, + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e61', + schain: { + ver: '1.1', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + domain: 'example.com' + } + ] + } + }; + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let uspConsentData = '1YCC'; + let bidderRequest = { + bidderCode: 'kubient', + auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', + bidderRequestId: 'ffffffffffffff', + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000, + refererInfo: { + referer: 'http://www.example.com', + reachedTop: true, + }, + gdprConsent: { + consentString: consentString, + gdprApplies: true + }, + uspConsent: uspConsentData + }; + describe('buildRequestBanner', function () { + let serverRequests = spec.buildRequests([bidBanner], Object.assign({}, bidderRequest, {bids: [bidBanner]})); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequests).to.be.an('array'); + }); + for (let i = 0; i < serverRequests.length; i++) { + let serverRequest = serverRequests[i]; + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest.method).to.be.a('string'); + expect(serverRequest.url).to.be.a('string'); + expect(serverRequest.data).to.be.a('string'); + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://kssp.kbntx.ch/kubprebidjs'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = JSON.parse(serverRequest.data); + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('v', 'requestId', 'adSlots', 'gdpr', 'referer', 'tmax', 'consent', 'consentGiven', 'uspConsent'); + expect(data.v).to.exist.and.to.be.a('string'); + expect(data.requestId).to.exist.and.to.be.a('string'); + expect(data.referer).to.be.a('string'); + expect(data.tmax).to.exist.and.to.be.a('number'); + expect(data.gdpr).to.exist.and.to.be.within(0, 1); + expect(data.consent).to.equal(consentString); + expect(data.uspConsent).to.exist.and.to.equal(uspConsentData); + for (let j = 0; j < data['adSlots'].length; j++) { + let adSlot = data['adSlots'][i]; + expect(adSlot).to.have.all.keys('bidId', 'zoneId', 'floor', 'banner', 'schain'); + expect(adSlot.bidId).to.be.a('string').and.to.equal(bidBanner.bidId); + expect(adSlot.zoneId).to.be.a('string').and.to.equal(bidBanner.params.zoneid); + expect(adSlot.floor).to.be.a('number'); + expect(adSlot.schain).to.be.an('object'); + expect(adSlot.banner).to.be.an('object'); + } + }); + } + }); + describe('buildRequestVideo', function () { + let serverRequests = spec.buildRequests([bidVideo], Object.assign({}, bidderRequest, {bids: [bidVideo]})); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequests).to.be.an('array'); + }); + for (let i = 0; i < serverRequests.length; i++) { + let serverRequest = serverRequests[i]; + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest.method).to.be.a('string'); + expect(serverRequest.url).to.be.a('string'); + expect(serverRequest.data).to.be.a('string'); + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://kssp.kbntx.ch/kubprebidjs'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = JSON.parse(serverRequest.data); + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('v', 'requestId', 'adSlots', 'gdpr', 'referer', 'tmax', 'consent', 'consentGiven', 'uspConsent'); + expect(data.v).to.exist.and.to.be.a('string'); + expect(data.requestId).to.exist.and.to.be.a('string'); + expect(data.referer).to.be.a('string'); + expect(data.tmax).to.exist.and.to.be.a('number'); + expect(data.gdpr).to.exist.and.to.be.within(0, 1); + expect(data.consent).to.equal(consentString); + expect(data.uspConsent).to.exist.and.to.equal(uspConsentData); + for (let j = 0; j < data['adSlots'].length; j++) { + let adSlot = data['adSlots'][i]; + expect(adSlot).to.have.all.keys('bidId', 'zoneId', 'floor', 'video', 'schain'); + expect(adSlot.bidId).to.be.a('string').and.to.equal(bidVideo.bidId); + expect(adSlot.zoneId).to.be.a('string').and.to.equal(bidVideo.params.zoneid); + expect(adSlot.floor).to.be.a('number'); + expect(adSlot.schain).to.be.an('object'); + expect(adSlot.video).to.be.an('object'); + } + }); + } + }); + + describe('isBidRequestValid', function () { + it('Should return true when required params are found', function () { + expect(spec.isBidRequestValid(bidBanner)).to.be.true; + expect(spec.isBidRequestValid(bidVideo)).to.be.true; + }); + it('Should return false when required params are not found', function () { + expect(spec.isBidRequestValid(bidBanner)).to.be.true; + expect(spec.isBidRequestValid(bidVideo)).to.be.true; + }); + it('Should return false when params are not found', function () { + delete bidBanner.params; + expect(spec.isBidRequestValid(bidBanner)).to.be.false; + delete bidVideo.params; + expect(spec.isBidRequestValid(bidVideo)).to.be.false; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret response', function () { + const serverResponse = { + body: + { + seatbid: [ + { + bid: [ + { + bidId: '000', + price: 1.5, + adm: '
test
', + creativeId: 'creativeId', + w: 300, + h: 250, + cur: 'USD', + netRevenue: false, + ttl: 360, + meta: {adomain: ['google.com', 'yahoo.com']} + } + ] + } + ] + } + }; + let bannerResponses = spec.interpretResponse(serverResponse); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'ad', 'creativeId', 'width', 'height', 'currency', 'netRevenue', 'ttl', 'meta'); + expect(dataItem.requestId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].bidId); + expect(dataItem.cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(dataItem.ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(dataItem.creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].creativeId); + expect(dataItem.width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(dataItem.height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(dataItem.currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].cur); + expect(dataItem.netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(serverResponse.body.seatbid[0].bid[0].netRevenue); + expect(dataItem.ttl).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].ttl); + expect(dataItem.meta).to.exist.and.to.be.a('object'); + expect(dataItem.meta.advertiserDomains).to.exist.and.to.be.a('array').and.to.equal(serverResponse.body.seatbid[0].bid[0].meta.adomain); + }); + + it('Should return no ad when not given a server response', function () { + const ads = spec.interpretResponse(null); + expect(ads).to.be.an('array').and.to.have.length(0); + }); + }); + + describe('interpretResponse Video', function () { + it('Should interpret response', function () { + const serverResponse = { + body: + { + seatbid: [ + { + bid: [ + { + bidId: '000', + price: 1.5, + adm: '
test
', + creativeId: 'creativeId', + w: 300, + h: 250, + mediaType: VIDEO, + cur: 'USD', + netRevenue: false, + ttl: 360, + meta: {adomain: ['google.com', 'yahoo.com']} + } + ] + } + ] + } + }; + let bannerResponses = spec.interpretResponse(serverResponse); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'ad', 'creativeId', 'width', 'height', 'currency', 'netRevenue', 'ttl', 'meta', 'mediaType', 'vastXml'); + expect(dataItem.requestId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].bidId); + expect(dataItem.cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(dataItem.ad).to.exist.and.to.be.a('string').and.to.have.string(serverResponse.body.seatbid[0].bid[0].adm); + expect(dataItem.creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].creativeId); + expect(dataItem.width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(dataItem.height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(dataItem.currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].cur); + expect(dataItem.netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(serverResponse.body.seatbid[0].bid[0].netRevenue); + expect(dataItem.ttl).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].ttl); + expect(dataItem.meta).to.exist.and.to.be.a('object'); + expect(dataItem.meta.advertiserDomains).to.exist.and.to.be.a('array').and.to.equal(serverResponse.body.seatbid[0].bid[0].meta.adomain); + expect(dataItem.mediaType).to.exist.and.to.equal(VIDEO); + expect(dataItem.vastXml).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].adm); + }); + + it('Should return no ad when not given a server response', function () { + const ads = spec.interpretResponse(null); + expect(ads).to.be.an('array').and.to.have.length(0); + }); + }); + + describe('getUserSyncs', function () { + it('should register the sync iframe without gdpr', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&consent_given=0'); + }); + it('should register the sync iframe with gdpr', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=0'); + }); + it('should register the sync iframe with gdpr vendor', function () { + let syncOptions = { + iframeEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString, + apiVersion: 1, + vendorData: { + vendorConsents: { + 794: 1 + } + } + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.html?consent_str=' + consentString + '&gdpr=1&consent_given=1'); + }); + it('should register the sync image without gdpr', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&consent_given=0'); + }); + it('should register the sync image with gdpr', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=0'); + }); + it('should register the sync image with gdpr vendor', function () { + let syncOptions = { + pixelEnabled: true + }; + let serverResponses = null; + let gdprConsent = { + gdprApplies: true, + consentString: consentString, + apiVersion: 2, + vendorData: { + vendor: { + consents: { + 794: 1 + } + } + } + }; + let uspConsent = null; + let syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent); + expect(syncs).to.be.an('array').and.to.have.length(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('https://kdmp.kbntx.ch/init.png?consent_str=' + consentString + '&gdpr=1&consent_given=1'); + }); + }) +}); From 5eeba1bb1917c68f05e517d7b72d787063020897 Mon Sep 17 00:00:00 2001 From: "Takaaki.Kojima" Date: Thu, 8 Jul 2021 17:36:04 +0900 Subject: [PATCH 1247/1476] AdGeneration Bid Adapter: adding adomain and updates for ver5.x. (#7150) * update AdGenerationBidAdapter for ver5.x. adding adomain * Fixed adomain value check. * Fixed test_spec for adomain * Fixed adomain value check. * Fixed test_spec for adomain --- modules/adgenerationBidAdapter.js | 242 +++++ .../modules/adgenerationBidAdapter_spec.js | 829 ++++++++++++++++++ 2 files changed, 1071 insertions(+) create mode 100644 modules/adgenerationBidAdapter.js create mode 100644 test/spec/modules/adgenerationBidAdapter_spec.js diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js new file mode 100644 index 00000000000..f43fd284bad --- /dev/null +++ b/modules/adgenerationBidAdapter.js @@ -0,0 +1,242 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; + +const ADG_BIDDER_CODE = 'adgeneration'; + +export const spec = { + code: ADG_BIDDER_CODE, + aliases: ['adg'], // short code + supportedMediaTypes: [BANNER, NATIVE], + /** + * 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.id); + }, + /** + * 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 ADGENE_PREBID_VERSION = '1.1.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; + let data = ``; + data = utils.tryAppendQueryString(data, 'posall', 'SSPLOC'); + const id = utils.getBidIdParameter('id', validReq.params); + data = utils.tryAppendQueryString(data, 'id', id); + data = utils.tryAppendQueryString(data, 'sdktype', '0'); + data = utils.tryAppendQueryString(data, 'hb', 'true'); + data = utils.tryAppendQueryString(data, 't', 'json3'); + data = utils.tryAppendQueryString(data, 'transactionid', validReq.transactionId); + data = utils.tryAppendQueryString(data, 'sizes', getSizes(validReq)); + data = utils.tryAppendQueryString(data, 'currency', getCurrencyType()); + data = utils.tryAppendQueryString(data, 'pbver', '$prebid.version$'); + data = utils.tryAppendQueryString(data, 'sdkname', 'prebidjs'); + data = utils.tryAppendQueryString(data, 'adapterver', ADGENE_PREBID_VERSION); + // native以外にvideo等の対応が入った場合は要修正 + if (!validReq.mediaTypes || !validReq.mediaTypes.native) { + data = utils.tryAppendQueryString(data, 'imark', '1'); + } + data = utils.tryAppendQueryString(data, 'tp', bidderRequest.refererInfo.referer); + // remove the trailing "&" + if (data.lastIndexOf('&') === data.length - 1) { + data = data.substring(0, data.length - 1); + } + serverRequests.push({ + method: 'GET', + url: url, + data: data, + bidRequest: validBidRequests[i] + }); + } + return serverRequests; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidRequests + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequests) { + const body = serverResponse.body; + if (!body.results || body.results.length < 1) { + return []; + } + const bidRequest = bidRequests.bidRequest; + const bidResponse = { + requestId: bidRequest.bidId, + cpm: body.cpm || 0, + width: body.w ? body.w : 1, + height: body.h ? body.h : 1, + creativeId: body.creativeid || '', + dealId: body.dealid || '', + currency: getCurrencyType(), + netRevenue: true, + ttl: body.ttl || 10, + }; + if (body.adomain && Array.isArray(body.adomain) && body.adomain.length) { + bidResponse.meta = { + advertiserDomains: body.adomain + } + } + if (isNative(body)) { + bidResponse.native = createNativeAd(body); + bidResponse.mediaType = NATIVE; + } else { + // banner + bidResponse.ad = createAd(body, bidRequest); + } + return [bidResponse]; + }, + + /** + * 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) { + const syncs = []; + return syncs; + } +}; + +function createAd(body, bidRequest) { + let ad = body.ad; + if (body.vastxml && body.vastxml.length > 0) { + ad = `
${createAPVTag()}${insertVASTMethod(bidRequest.bidId, body.vastxml)}`; + } + ad = appendChildToBody(ad, body.beacon); + if (removeWrapper(ad)) return removeWrapper(ad); + return ad; +} + +function isNative(body) { + if (!body) return false; + return body.native_ad && body.native_ad.assets.length > 0; +} + +function createNativeAd(body) { + let native = {}; + if (body.native_ad && body.native_ad.assets.length > 0) { + const assets = body.native_ad.assets; + for (let i = 0, len = assets.length; i < len; i++) { + switch (assets[i].id) { + case 1: + native.title = assets[i].title.text; + break; + case 2: + native.image = { + url: assets[i].img.url, + height: assets[i].img.h, + width: assets[i].img.w, + }; + break; + case 3: + native.icon = { + url: assets[i].img.url, + height: assets[i].img.h, + width: assets[i].img.w, + }; + break; + case 4: + native.sponsoredBy = assets[i].data.value; + break; + case 5: + native.body = assets[i].data.value; + break; + case 6: + native.cta = assets[i].data.value; + break; + case 502: + native.privacyLink = encodeURIComponent(assets[i].data.value); + break; + } + } + native.clickUrl = body.native_ad.link.url; + native.clickTrackers = body.native_ad.link.clicktrackers || []; + native.impressionTrackers = body.native_ad.imptrackers || []; + if (body.beaconurl && body.beaconurl != '') { + native.impressionTrackers.push(body.beaconurl) + } + } + return native; +} + +function appendChildToBody(ad, data) { + return ad.replace(/<\/\s?body>/, `${data}`); +} + +function createAPVTag() { + const APVURL = 'https://cdn.apvdr.com/js/VideoAd.min.js'; + let apvScript = document.createElement('script'); + apvScript.type = 'text/javascript'; + apvScript.id = 'apv'; + apvScript.src = APVURL; + return apvScript.outerHTML; +} + +function insertVASTMethod(targetId, vastXml) { + let apvVideoAdParam = { + s: targetId + }; + let script = document.createElement(`script`); + script.type = 'text/javascript'; + script.innerHTML = `(function(){ new APV.VideoAd(${JSON.stringify(apvVideoAdParam)}).load('${vastXml.replace(/\r?\n/g, '')}'); })();`; + return script.outerHTML; +} + +/** + * + * @param ad + */ +function removeWrapper(ad) { + const bodyIndex = ad.indexOf(''); + const lastBodyIndex = ad.lastIndexOf(''); + if (bodyIndex === -1 || lastBodyIndex === -1) return false; + return ad.substr(bodyIndex, lastBodyIndex).replace('', '').replace('', ''); +} + +/** + * request + * @param validReq request + * @returns {?string} 300x250,320x50... + */ +function getSizes(validReq) { + const sizes = validReq.sizes; + if (!sizes || sizes.length < 1) return null; + let sizesStr = ''; + for (const i in sizes) { + const size = sizes[i]; + if (size.length !== 2) return null; + sizesStr += `${size[0]}x${size[1]},`; + } + if (sizesStr || sizesStr.lastIndexOf(',') === sizesStr.length - 1) { + sizesStr = sizesStr.substring(0, sizesStr.length - 1); + } + return sizesStr; +} + +/** + * @return {?string} USD or JPY + */ +function getCurrencyType() { + if (config.getConfig('currency.adServerCurrency') && config.getConfig('currency.adServerCurrency').toUpperCase() === 'USD') return 'USD'; + return 'JPY'; +} + +registerBidder(spec); diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js new file mode 100644 index 00000000000..7eacba2f7d4 --- /dev/null +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -0,0 +1,829 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adgenerationBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {NATIVE} from 'src/mediaTypes.js'; +import {config} from 'src/config.js'; +import prebid from '../../../package.json'; + +describe('AdgenerationAdapter', function () { + const adapter = newBidder(spec); + const ENDPOINT = ['https://api-test.scaleout.jp/adsv/v1', 'https://d.socdm.com/adsv/v1']; + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + const bid = { + 'bidder': 'adg', + 'params': { + id: '58278', // banner + } + }; + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { // banner + bidder: 'adg', + params: { + id: '58278', + currency: 'JPY', + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [320, 100]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' + }, + { // native + bidder: 'adg', + params: { + id: '58278', + currency: 'JPY', + }, + mediaTypes: { + native: { + image: { + required: true + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + }, + icon: { + required: true + } + }, + }, + adUnitCode: 'adunit-code', + sizes: [[1, 1]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' + } + ]; + const bidderRequest = { + refererInfo: { + referer: 'https://example.com' + } + }; + 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.1.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.1.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.1.0&tp=https%3A%2F%2Fexample.com' + }; + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.equal(ENDPOINT[1]); + expect(request.method).to.equal('GET'); + }); + + it('sends bid request to debug ENDPOINT via GET', function () { + bidRequests[0].params.debug = true; + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.url).to.equal(ENDPOINT[0]); + expect(request.method).to.equal('GET'); + }); + + it('should attache params to the banner request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.equal(data.banner); + }); + + it('should attache params to the native request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[1]; + expect(request.data).to.equal(data.native); + }); + it('allows setConfig to set bidder currency for JPY', function () { + config.setConfig({ + currency: { + adServerCurrency: 'JPY' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.equal(data.banner); + config.resetConfig(); + }); + it('allows setConfig to set bidder currency for USD', function () { + config.setConfig({ + currency: { + adServerCurrency: 'USD' + } + }); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.equal(data.bannerUSD); + config.resetConfig(); + }); + }); + describe('interpretResponse', function () { + const bidRequests = { + banner: { + bidRequest: { + 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' + }, + }, + native: { + bidRequest: { + bidder: 'adg', + params: { + id: '58278', // banner + }, + mediaTypes: { + native: { + image: { + required: true + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + }, + icon: { + required: true + } + } + }, + adUnitCode: 'adunit-code', + sizes: [[1, 1]], + bidId: '2f6ac468a9c15e', + bidderRequestId: '14a9f773e30243', + auctionId: '4aae9f05-18c6-4fcd-80cf-282708cd584a', + transactionTd: 'f76f6dfd-d64f-4645-a29f-682bac7f431a' + }, + }, + }; + + const serverResponse = { + noAd: { + results: [], + }, + normal: { + banner: { + ad: '
', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + w: 320, + h: 100, + location_params: null, + locationid: '58279', + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000, + results: [ + {ad: '
'}, + ], + adomain: ['advertiserdomain.com'] + }, + native: { + ad: '↵ ↵ ↵ ↵ ↵
↵ ', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + location_params: null, + locationid: '58279', + adomain: ['advertiserdomain.com'], + native_ad: { + assets: [ + { + data: { + label: 'accompanying_text', + value: 'AD' + }, + id: 501 + }, + { + data: { + label: 'optout_url', + value: 'https://supership.jp/optout/#' + }, + id: 502 + }, + { + data: { + ext: { + black_back: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_white.png', + }, + label: 'information_icon_url', + value: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_gray.png', + id: 503 + } + }, + { + id: 1, + required: 1, + title: {text: 'Title'} + }, + { + id: 2, + img: { + h: 250, + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + w: 300 + }, + required: 1 + }, + { + id: 3, + img: { + h: 300, + url: 'https://placehold.jp/300x300.png', + w: 300 + }, + required: 1 + }, + { + data: {value: 'Description'}, + id: 5, + required: 0 + }, + { + data: {value: 'CTA'}, + id: 6, + required: 0 + }, + { + data: {value: 'Sponsored'}, + id: 4, + required: 0 + } + ], + imptrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'], + link: { + clicktrackers: [ + 'https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif' + ], + url: 'https://supership.jp' + }, + }, + results: [ + {ad: 'Creative<\/body>'} + ], + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000 + } + }, + emptyAdomain: { + banner: { + ad: '
', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + w: 320, + h: 100, + location_params: null, + locationid: '58279', + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000, + results: [ + {ad: '
'}, + ], + adomain: [] + }, + native: { + ad: '↵ ↵ ↵ ↵ ↵
↵ ', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + location_params: null, + locationid: '58279', + adomain: [], + native_ad: { + assets: [ + { + data: { + label: 'accompanying_text', + value: 'AD' + }, + id: 501 + }, + { + data: { + label: 'optout_url', + value: 'https://supership.jp/optout/#' + }, + id: 502 + }, + { + data: { + ext: { + black_back: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_white.png', + }, + label: 'information_icon_url', + value: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_gray.png', + id: 503 + } + }, + { + id: 1, + required: 1, + title: {text: 'Title'} + }, + { + id: 2, + img: { + h: 250, + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + w: 300 + }, + required: 1 + }, + { + id: 3, + img: { + h: 300, + url: 'https://placehold.jp/300x300.png', + w: 300 + }, + required: 1 + }, + { + data: {value: 'Description'}, + id: 5, + required: 0 + }, + { + data: {value: 'CTA'}, + id: 6, + required: 0 + }, + { + data: {value: 'Sponsored'}, + id: 4, + required: 0 + } + ], + imptrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'], + link: { + clicktrackers: [ + 'https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif' + ], + url: 'https://supership.jp' + }, + }, + results: [ + {ad: 'Creative<\/body>'} + ], + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000 + } + }, + noAdomain: { + banner: { + ad: '
', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + w: 320, + h: 100, + location_params: null, + locationid: '58279', + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000, + results: [ + {ad: '
'}, + ], + }, + native: { + ad: '↵ ↵ ↵ ↵ ↵
↵ ', + beacon: '', + cpm: 36.0008, + displaytype: '1', + ids: {}, + location_params: null, + locationid: '58279', + native_ad: { + assets: [ + { + data: { + label: 'accompanying_text', + value: 'AD' + }, + id: 501 + }, + { + data: { + label: 'optout_url', + value: 'https://supership.jp/optout/#' + }, + id: 502 + }, + { + data: { + ext: { + black_back: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_white.png', + }, + label: 'information_icon_url', + value: 'https://i.socdm.com/sdk/img/icon_adg_optout_26x26_gray.png', + id: 503 + } + }, + { + id: 1, + required: 1, + title: {text: 'Title'} + }, + { + id: 2, + img: { + h: 250, + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + w: 300 + }, + required: 1 + }, + { + id: 3, + img: { + h: 300, + url: 'https://placehold.jp/300x300.png', + w: 300 + }, + required: 1 + }, + { + data: {value: 'Description'}, + id: 5, + required: 0 + }, + { + data: {value: 'CTA'}, + id: 6, + required: 0 + }, + { + data: {value: 'Sponsored'}, + id: 4, + required: 0 + } + ], + imptrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'], + link: { + clicktrackers: [ + 'https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif' + ], + url: 'https://supership.jp' + }, + }, + results: [ + {ad: 'Creative<\/body>'} + ], + rotation: '0', + scheduleid: '512603', + sdktype: '0', + creativeid: '1k2kv35vsa5r', + dealid: 'fd5sa5fa7f', + ttl: 1000 + } + } + }; + + const bidResponses = { + normal: { + banner: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 320, + height: 100, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + ad: '
', + adomain: ['advertiserdomain.com'] + }, + native: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 1, + height: 1, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + adomain: ['advertiserdomain.com'], + ad: '↵
', + native: { + title: 'Title', + image: { + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + height: 250, + width: 300 + }, + icon: { + url: 'https://placehold.jp/300x300.png', + height: 300, + width: 300 + }, + sponsoredBy: 'Sponsored', + body: 'Description', + cta: 'CTA', + privacyLink: 'https://supership.jp/optout/#', + clickUrl: 'https://supership.jp', + clickTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif'], + impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] + }, + mediaType: NATIVE + } + }, + emptyAdomain: { + banner: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 320, + height: 100, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + ad: '
', + adomain: [] + }, + native: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 1, + height: 1, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + adomain: [], + ad: '↵
', + native: { + title: 'Title', + image: { + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + height: 250, + width: 300 + }, + icon: { + url: 'https://placehold.jp/300x300.png', + height: 300, + width: 300 + }, + sponsoredBy: 'Sponsored', + body: 'Description', + cta: 'CTA', + privacyLink: 'https://supership.jp/optout/#', + clickUrl: 'https://supership.jp', + clickTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif'], + impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] + }, + mediaType: NATIVE + }, + }, + noAdomain: { + banner: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 320, + height: 100, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + ad: '
', + }, + native: { + requestId: '2f6ac468a9c15e', + cpm: 36.0008, + width: 1, + height: 1, + creativeId: '1k2kv35vsa5r', + dealId: 'fd5sa5fa7f', + currency: 'JPY', + netRevenue: true, + ttl: 1000, + ad: '↵
', + native: { + title: 'Title', + image: { + url: 'https://sdk-temp.s3-ap-northeast-1.amazonaws.com/adg-sample-ad/img/300x250.png', + height: 250, + width: 300 + }, + icon: { + url: 'https://placehold.jp/300x300.png', + height: 300, + width: 300 + }, + sponsoredBy: 'Sponsored', + body: 'Description', + cta: 'CTA', + privacyLink: 'https://supership.jp/optout/#', + clickUrl: 'https://supership.jp', + clickTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1_clicktracker_access.gif'], + impressionTrackers: ['https://adg-dummy-dsp.s3-ap-northeast-1.amazonaws.com/1x1.gif'] + }, + mediaType: NATIVE + } + }, + }; + + it('no bid responses', function () { + const result = spec.interpretResponse({body: serverResponse.noAd}, bidRequests.banner); + expect(result.length).to.equal(0); + }); + + it('handles banner responses', function () { + const result = spec.interpretResponse({body: serverResponse.normal.banner}, bidRequests.banner)[0]; + expect(result.requestId).to.equal(bidResponses.normal.banner.requestId); + expect(result.width).to.equal(bidResponses.normal.banner.width); + expect(result.height).to.equal(bidResponses.normal.banner.height); + expect(result.creativeId).to.equal(bidResponses.normal.banner.creativeId); + expect(result.dealId).to.equal(bidResponses.normal.banner.dealId); + expect(result.currency).to.equal(bidResponses.normal.banner.currency); + expect(result.netRevenue).to.equal(bidResponses.normal.banner.netRevenue); + expect(result.ttl).to.equal(bidResponses.normal.banner.ttl); + expect(result.ad).to.equal(bidResponses.normal.banner.ad); + expect(result.meta.advertiserDomains).deep.equal(bidResponses.normal.banner.adomain); + }); + + it('handles native responses', function () { + const result = spec.interpretResponse({body: serverResponse.normal.native}, bidRequests.native)[0]; + expect(result.requestId).to.equal(bidResponses.normal.native.requestId); + expect(result.width).to.equal(bidResponses.normal.native.width); + expect(result.height).to.equal(bidResponses.normal.native.height); + expect(result.creativeId).to.equal(bidResponses.normal.native.creativeId); + expect(result.dealId).to.equal(bidResponses.normal.native.dealId); + expect(result.currency).to.equal(bidResponses.normal.native.currency); + expect(result.netRevenue).to.equal(bidResponses.normal.native.netRevenue); + expect(result.ttl).to.equal(bidResponses.normal.native.ttl); + expect(result.native.title).to.equal(bidResponses.normal.native.native.title); + expect(result.native.image.url).to.equal(bidResponses.normal.native.native.image.url); + expect(result.native.image.height).to.equal(bidResponses.normal.native.native.image.height); + expect(result.native.image.width).to.equal(bidResponses.normal.native.native.image.width); + expect(result.native.icon.url).to.equal(bidResponses.normal.native.native.icon.url); + expect(result.native.icon.width).to.equal(bidResponses.normal.native.native.icon.width); + expect(result.native.icon.height).to.equal(bidResponses.normal.native.native.icon.height); + expect(result.native.sponsoredBy).to.equal(bidResponses.normal.native.native.sponsoredBy); + expect(result.native.body).to.equal(bidResponses.normal.native.native.body); + expect(result.native.cta).to.equal(bidResponses.normal.native.native.cta); + expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.normal.native.native.privacyLink); + expect(result.native.clickUrl).to.equal(bidResponses.normal.native.native.clickUrl); + expect(result.native.impressionTrackers[0]).to.equal(bidResponses.normal.native.native.impressionTrackers[0]); + expect(result.native.clickTrackers[0]).to.equal(bidResponses.normal.native.native.clickTrackers[0]); + expect(result.mediaType).to.equal(bidResponses.normal.native.mediaType); + expect(result.meta.advertiserDomains).deep.equal(bidResponses.normal.native.adomain); + }); + + it('handles banner responses for empty adomain', function () { + const result = spec.interpretResponse({body: serverResponse.emptyAdomain.banner}, bidRequests.banner)[0]; + expect(result.requestId).to.equal(bidResponses.emptyAdomain.banner.requestId); + expect(result.width).to.equal(bidResponses.emptyAdomain.banner.width); + expect(result.height).to.equal(bidResponses.emptyAdomain.banner.height); + expect(result.creativeId).to.equal(bidResponses.emptyAdomain.banner.creativeId); + expect(result.dealId).to.equal(bidResponses.emptyAdomain.banner.dealId); + expect(result.currency).to.equal(bidResponses.emptyAdomain.banner.currency); + expect(result.netRevenue).to.equal(bidResponses.emptyAdomain.banner.netRevenue); + expect(result.ttl).to.equal(bidResponses.emptyAdomain.banner.ttl); + expect(result.ad).to.equal(bidResponses.emptyAdomain.banner.ad); + expect(result).to.not.have.any.keys('meta'); + expect(result).to.not.have.any.keys('advertiserDomains'); + }); + + it('handles native responses for empty adomain', function () { + const result = spec.interpretResponse({body: serverResponse.emptyAdomain.native}, bidRequests.native)[0]; + expect(result.requestId).to.equal(bidResponses.emptyAdomain.native.requestId); + expect(result.width).to.equal(bidResponses.emptyAdomain.native.width); + expect(result.height).to.equal(bidResponses.emptyAdomain.native.height); + expect(result.creativeId).to.equal(bidResponses.emptyAdomain.native.creativeId); + expect(result.dealId).to.equal(bidResponses.emptyAdomain.native.dealId); + expect(result.currency).to.equal(bidResponses.emptyAdomain.native.currency); + expect(result.netRevenue).to.equal(bidResponses.emptyAdomain.native.netRevenue); + expect(result.ttl).to.equal(bidResponses.emptyAdomain.native.ttl); + expect(result.native.title).to.equal(bidResponses.emptyAdomain.native.native.title); + expect(result.native.image.url).to.equal(bidResponses.emptyAdomain.native.native.image.url); + expect(result.native.image.height).to.equal(bidResponses.emptyAdomain.native.native.image.height); + expect(result.native.image.width).to.equal(bidResponses.emptyAdomain.native.native.image.width); + expect(result.native.icon.url).to.equal(bidResponses.emptyAdomain.native.native.icon.url); + expect(result.native.icon.width).to.equal(bidResponses.emptyAdomain.native.native.icon.width); + expect(result.native.icon.height).to.equal(bidResponses.emptyAdomain.native.native.icon.height); + expect(result.native.sponsoredBy).to.equal(bidResponses.emptyAdomain.native.native.sponsoredBy); + expect(result.native.body).to.equal(bidResponses.emptyAdomain.native.native.body); + expect(result.native.cta).to.equal(bidResponses.emptyAdomain.native.native.cta); + expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.emptyAdomain.native.native.privacyLink); + expect(result.native.clickUrl).to.equal(bidResponses.emptyAdomain.native.native.clickUrl); + expect(result.native.impressionTrackers[0]).to.equal(bidResponses.emptyAdomain.native.native.impressionTrackers[0]); + expect(result.native.clickTrackers[0]).to.equal(bidResponses.emptyAdomain.native.native.clickTrackers[0]); + expect(result.mediaType).to.equal(bidResponses.emptyAdomain.native.mediaType); + expect(result).to.not.have.any.keys('meta'); + expect(result).to.not.have.any.keys('advertiserDomains'); + }); + + it('handles banner responses for no adomain', function () { + const result = spec.interpretResponse({body: serverResponse.noAdomain.banner}, bidRequests.banner)[0]; + expect(result.requestId).to.equal(bidResponses.noAdomain.banner.requestId); + expect(result.width).to.equal(bidResponses.noAdomain.banner.width); + expect(result.height).to.equal(bidResponses.noAdomain.banner.height); + expect(result.creativeId).to.equal(bidResponses.noAdomain.banner.creativeId); + expect(result.dealId).to.equal(bidResponses.noAdomain.banner.dealId); + expect(result.currency).to.equal(bidResponses.noAdomain.banner.currency); + expect(result.netRevenue).to.equal(bidResponses.noAdomain.banner.netRevenue); + expect(result.ttl).to.equal(bidResponses.noAdomain.banner.ttl); + expect(result.ad).to.equal(bidResponses.noAdomain.banner.ad); + expect(result).to.not.have.any.keys('meta'); + expect(result).to.not.have.any.keys('advertiserDomains'); + }); + + it('handles native responses for no adomain', function () { + const result = spec.interpretResponse({body: serverResponse.noAdomain.native}, bidRequests.native)[0]; + expect(result.requestId).to.equal(bidResponses.noAdomain.native.requestId); + expect(result.width).to.equal(bidResponses.noAdomain.native.width); + expect(result.height).to.equal(bidResponses.noAdomain.native.height); + expect(result.creativeId).to.equal(bidResponses.noAdomain.native.creativeId); + expect(result.dealId).to.equal(bidResponses.noAdomain.native.dealId); + expect(result.currency).to.equal(bidResponses.noAdomain.native.currency); + expect(result.netRevenue).to.equal(bidResponses.noAdomain.native.netRevenue); + expect(result.ttl).to.equal(bidResponses.noAdomain.native.ttl); + expect(result.native.title).to.equal(bidResponses.noAdomain.native.native.title); + expect(result.native.image.url).to.equal(bidResponses.noAdomain.native.native.image.url); + expect(result.native.image.height).to.equal(bidResponses.noAdomain.native.native.image.height); + expect(result.native.image.width).to.equal(bidResponses.noAdomain.native.native.image.width); + expect(result.native.icon.url).to.equal(bidResponses.noAdomain.native.native.icon.url); + expect(result.native.icon.width).to.equal(bidResponses.noAdomain.native.native.icon.width); + expect(result.native.icon.height).to.equal(bidResponses.noAdomain.native.native.icon.height); + expect(result.native.sponsoredBy).to.equal(bidResponses.noAdomain.native.native.sponsoredBy); + expect(result.native.body).to.equal(bidResponses.noAdomain.native.native.body); + expect(result.native.cta).to.equal(bidResponses.noAdomain.native.native.cta); + expect(decodeURIComponent(result.native.privacyLink)).to.equal(bidResponses.noAdomain.native.native.privacyLink); + expect(result.native.clickUrl).to.equal(bidResponses.noAdomain.native.native.clickUrl); + expect(result.native.impressionTrackers[0]).to.equal(bidResponses.noAdomain.native.native.impressionTrackers[0]); + expect(result.native.clickTrackers[0]).to.equal(bidResponses.noAdomain.native.native.clickTrackers[0]); + expect(result.mediaType).to.equal(bidResponses.noAdomain.native.mediaType); + expect(result).to.not.have.any.keys('meta'); + expect(result).to.not.have.any.keys('advertiserDomains'); + }); + }); +}); From 70ba09cb35e97bf3a4ce74b3adafd141804d3e49 Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Thu, 8 Jul 2021 14:52:31 +0200 Subject: [PATCH 1248/1476] Adnuntius Bid Adater: URL changes (#7154) * Master merge issues * Adnuntius Bid Adapter: Added tests for gdpr and segments * Moved segments to read from ortb2 instead of a custom value. * Changed bidder to read segments from ortb2. * fixing lgtm alert * Read USI from meta-information in browser. * Changing URL and appending parameters. --- modules/adnuntiusBidAdapter.js | 7 ++++--- test/spec/modules/adnuntiusBidAdapter_spec.js | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index ba9a8bdddb5..68c3e9caffd 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -5,7 +5,7 @@ import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; -const ENDPOINT_URL = 'https://delivery.adnuntius.com/i?tzo='; +const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const GVLID = 855; const checkSegment = function (segment) { @@ -33,7 +33,6 @@ const handleMeta = function () { adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) } const meta = (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} - utils.logMessage('STORE', adnMeta, meta) return meta } @@ -62,6 +61,8 @@ export const spec = { const tzo = new Date().getTimezoneOffset(); const gdprApplies = utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); const consentString = utils.deepAccess(bidderRequest, 'gdprConsent.consentString'); + + request.push('tzo=' + tzo) request.push('format=json') if (gdprApplies !== undefined) request.push('consentString=' + consentString); if (segments.length > 0) request.push('segments=' + segments.join(',')); @@ -87,7 +88,7 @@ export const spec = { const network = networkKeys[j]; requests.push({ method: 'POST', - url: ENDPOINT_URL + tzo + '&' + request.join('&'), + url: ENDPOINT_URL + '?' + request.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index f5f3846cace..e2682b8d0d5 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -7,7 +7,7 @@ import * as utils from 'src/utils.js'; import { getStorageManager } from 'src/storageManager.js'; describe('adnuntiusBidAdapter', function () { - const URL = 'https://delivery.adnuntius.com/i?tzo='; + const URL = 'https://ads.adnuntius.delivery/i?tzo='; const GVLID = 855; const usi = utils.generateUUID() const meta = [{ key: 'usi', value: usi }] @@ -19,7 +19,6 @@ describe('adnuntiusBidAdapter', function () { }); const tzo = new Date().getTimezoneOffset(); const ENDPOINT_URL = `${URL}${tzo}&format=json&userId=${usi}`; - // const ENDPOINT_URL_SEGMENTS_ = `${URL}${tzo}&format=json`; 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); From ee22d5adea8b1d4e61b86028543176666fa5618a Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 8 Jul 2021 17:56:02 +0200 Subject: [PATCH 1249/1476] Prebid 5.4.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71493dae8bd..7b949e0b55d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.4.0-pre", + "version": "5.4.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bd8832e3d388721c583828d4841745b196f18e1a Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 8 Jul 2021 18:13:02 +0200 Subject: [PATCH 1250/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b949e0b55d..181945c1dd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.4.0", + "version": "5.5.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From d1ef14a9d29c60c60d7ef4599159b8a0bb79952c Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Thu, 8 Jul 2021 22:55:12 +0600 Subject: [PATCH 1251/1476] Zeta Ssp Bid Adapter: multiple bid responses (#7034) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 47 +++++------ test/spec/modules/zetaSspBidAdapter_spec.js | 87 +++++++++++++-------- 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 54c0657e9df..76ceea0fdd1 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -102,29 +102,32 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function (serverResponse, bidRequest) { - let bidResponse = []; - if (Object.keys(serverResponse.body).length !== 0) { - let zetaResponse = serverResponse.body; - let zetaBid = zetaResponse.seatbid[0].bid[0]; - let bid = { - requestId: zetaBid.impid, - cpm: zetaBid.price, - currency: zetaResponse.cur, - width: zetaBid.w, - height: zetaBid.h, - ad: zetaBid.adm, - ttl: TTL, - creativeId: zetaBid.crid, - netRevenue: NET_REV, - }; - if (zetaBid.adomain && zetaBid.adomain.length) { - bid.meta = { - advertiserDomains: zetaBid.adomain - }; - } - bidResponse.push(bid); + let bidResponses = []; + const response = (serverResponse || {}).body; + if (response && response.seatbid && response.seatbid[0].bid && response.seatbid[0].bid.length) { + response.seatbid.forEach(zetaSeatbid => { + zetaSeatbid.bid.forEach(zetaBid => { + let bid = { + requestId: zetaBid.impid, + cpm: zetaBid.price, + currency: response.cur, + width: zetaBid.w, + height: zetaBid.h, + ad: zetaBid.adm, + ttl: TTL, + creativeId: zetaBid.crid, + netRevenue: NET_REV, + }; + if (zetaBid.adomain && zetaBid.adomain.length) { + bid.meta = { + advertiserDomains: zetaBid.adomain + }; + } + bidResponses.push(bid); + }) + }) } - return bidResponse; + return bidResponses; }, /** diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 080c487a29e..9f25a489dab 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,6 @@ import {spec} from '../../../modules/zetaSspBidAdapter.js' -describe('Zeta Ssp Bid Adapter', function() { +describe('Zeta Ssp Bid Adapter', function () { const eids = [ { 'source': 'example.com', @@ -83,45 +83,66 @@ describe('Zeta Ssp Bid Adapter', function() { expect(payload).to.not.be.empty; }); - const responseBody = { - id: '12345', - seatbid: [ - { - bid: [ + it('Test the response parsing function', function () { + const response = { + body: { + id: '12345', + seatbid: [ { - id: 'auctionId', - impid: 'impId', - price: 0.0, - adm: 'adMarkup', - crid: 'creativeId', - adomain: [ - 'https://example.com' - ], - h: 250, - w: 300 + bid: [ + { + id: 'auctionId', + impid: 'impId', + price: 0.0, + adm: 'adMarkup', + crid: 'creativeId', + adomain: [ + 'https://example.com' + ], + h: 250, + w: 300 + }, + { + id: 'auctionId2', + impid: 'impId2', + price: 0.1, + adm: 'adMarkup2', + crid: 'creativeId2', + adomain: [ + 'https://example2.com' + ], + h: 150, + w: 200 + } + ] } - ] + ], + cur: 'USD' } - ], - cur: 'USD' - }; - - it('Test the response parsing function', function () { - const receivedBid = responseBody.seatbid[0].bid[0]; - const response = {}; - response.body = responseBody; + }; const bidResponse = spec.interpretResponse(response, null); expect(bidResponse).to.not.be.empty; - const bid = bidResponse[0]; - expect(bid).to.not.be.empty; - expect(bid.ad).to.equal(receivedBid.adm); - expect(bid.cpm).to.equal(receivedBid.price); - expect(bid.height).to.equal(receivedBid.h); - expect(bid.width).to.equal(receivedBid.w); - expect(bid.requestId).to.equal(receivedBid.impid); - expect(bid.meta.advertiserDomains).to.equal(receivedBid.adomain); + const bid1 = bidResponse[0]; + const receivedBid1 = response.body.seatbid[0].bid[0]; + expect(bid1).to.not.be.empty; + expect(bid1.ad).to.equal(receivedBid1.adm); + expect(bid1.cpm).to.equal(receivedBid1.price); + expect(bid1.height).to.equal(receivedBid1.h); + expect(bid1.width).to.equal(receivedBid1.w); + expect(bid1.requestId).to.equal(receivedBid1.impid); + expect(bid1.meta.advertiserDomains).to.equal(receivedBid1.adomain); + + const bid2 = bidResponse[1]; + const receivedBid2 = response.body.seatbid[0].bid[1]; + expect(bid2).to.not.be.empty; + expect(bid2.ad).to.equal(receivedBid2.adm); + expect(bid2.cpm).to.equal(receivedBid2.price); + expect(bid2.height).to.equal(receivedBid2.h); + expect(bid2.width).to.equal(receivedBid2.w); + expect(bid2.requestId).to.equal(receivedBid2.impid); + expect(bid2.meta.advertiserDomains).to.equal(receivedBid2.adomain); }); it('Different cases for user syncs', function () { From 53e7dacb506a5640a9be6db423b18a16f50ddb39 Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Fri, 9 Jul 2021 00:09:49 +0530 Subject: [PATCH 1252/1476] BrightMountainMedia: add UserId support (#7148) * Update BrightMountainMedia cookie sync URL * Bright Mountain Media: Update bidder code * Bright Mountain Media: Add brightmountainmedia as alias * Bright Mountain Media: Update Bid Endpoint * BrightMountainMedia Bid Adapter: add floors module support * BrightMountainMedia Bid Adapter: support advertiserDomains * BrightMountainMedia Bid Adapter: support userId * BrightMountainMedia: update read userid form userIdAsEids --- modules/brightMountainMediaBidAdapter.js | 10 ++++- .../brightMountainMediaBidAdapter_spec.js | 43 ++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 531238c7400..51356098b58 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -42,9 +42,10 @@ export const spec = { 'deviceWidth': winTop.screen.width, 'deviceHeight': winTop.screen.height, 'language': (navigator && navigator.language) ? navigator.language : '', - 'secure': 1, + 'secure': document.location.protocol === 'https:' ? 1 : 0, 'host': location.host, - 'page': location.pathname, + 'page': location.href, + 'prebidVersion': '$prebid.version$', 'placements': placements }; if (bidderRequest) { @@ -59,6 +60,7 @@ export const spec = { placementId: bid.params.placement_id, bidId: bid.bidId, floor: {}, + userIds: {}, }; if (bid.mediaTypes.hasOwnProperty(BANNER)) { @@ -108,6 +110,10 @@ export const spec = { if (bid.schain) { placement.schain = bid.schain; } + + if (bid.userIdAsEids) { + placement.userIds = { eids: bid.userIdAsEids }; + } placements.push(placement); } return { diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 17f23c5acd3..114c1f19a6e 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -86,7 +86,7 @@ describe('brightMountainMediaBidAdapter_spec', 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'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'prebidVersion', 'placements'); expect(data.deviceWidth).to.be.a('number'); expect(data.deviceHeight).to.be.a('number'); expect(data.language).to.be.a('string'); @@ -145,6 +145,47 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(request.data.placements[0].schain).to.be.an('object'); }); + it('sends userId info if exists', function () { + const userIdAsEids = [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5-ZHMOaW5vh_TJhKVSaTWmuoTpwqjGGwx5v0WbaSV8yw', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '00000000000000000000000000', + 'atype': 1 + } + ] + } + ]; + bidBanner.userIdAsEids = userIdAsEids; + const request = spec.buildRequests([bidBanner], bidderRequest); + + expect(request.data.placements[0]).to.have.property('userIds'); + expect(request.data.placements[0].userIds).to.not.equal(null).and.to.not.be.undefined; + expect(request.data.placements[0].userIds.eids.length).to.greaterThan(0); + for (let index in request.data.placements[0].userIds.eids) { + let eid = request.data.placements[0].userIds.eids[index]; + expect(eid.source).to.not.equal(null).and.to.not.be.undefined; + expect(eid.uids).to.not.equal(null).and.to.not.be.undefined; + for (let uidsIndex in eid.uids) { + let uid = eid.uids[uidsIndex]; + expect(uid.id).to.not.equal(null).and.to.not.be.undefined; + } + } + }); + bidderRequest['bids'] = [bidVideo]; serverRequest = spec.buildRequests([bidVideo], bidderRequest); testServerRequestBody(serverRequest); From c6433348839e15f77a3a9693a5eca935dc6f3769 Mon Sep 17 00:00:00 2001 From: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Date: Thu, 8 Jul 2021 20:48:15 +0200 Subject: [PATCH 1253/1476] Proxistore Bid Adapter: support advertiserDomains (#7136) * support advertiserDomains * Revert "support advertiserDomains" This reverts commit e7488d069e24d11f3afd6f6b996506b24961fc3e. * remove package-lock.json * Revert "remove package-lock.json" This reverts commit cd9dd5df192bfea84f7cde9d3fb7a444650380dd. * Revert "Revert "support advertiserDomains"" This reverts commit b80f1c15d5f49c48781f7af958abafef83465819. * add adapter * Revert "Revert "support advertiserDomains"" This reverts commit b80f1c15d5f49c48781f7af958abafef83465819. * copy package-lock.json from prebid master --- modules/proxistoreBidAdapter.js | 204 ++++++++++++++++++ .../spec/modules/proxistoreBidAdapter_spec.js | 169 +++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 modules/proxistoreBidAdapter.js create mode 100644 test/spec/modules/proxistoreBidAdapter_spec.js diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js new file mode 100644 index 00000000000..2e4c7eb22cb --- /dev/null +++ b/modules/proxistoreBidAdapter.js @@ -0,0 +1,204 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'proxistore'; +const PROXISTORE_VENDOR_ID = 418; + +function _createServerRequest(bidRequests, bidderRequest) { + var sizeIds = []; + bidRequests.forEach(function (bid) { + var sizeId = { + id: bid.bidId, + sizes: bid.sizes.map(function (size) { + return { + width: size[0], + height: size[1], + }; + }), + floor: _assignFloor(bid), + segments: _assignSegments(bid), + }; + sizeIds.push(sizeId); + }); + var payload = { + auctionId: bidRequests[0].auctionId, + transactionId: bidRequests[0].auctionId, + bids: sizeIds, + website: bidRequests[0].params.website, + language: bidRequests[0].params.language, + gdpr: { + applies: false, + consentGiven: false, + }, + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + var gdprConsent = bidderRequest.gdprConsent; + + if ( + typeof gdprConsent.gdprApplies === 'boolean' && + gdprConsent.gdprApplies + ) { + payload.gdpr.applies = true; + } + + if ( + typeof gdprConsent.consentString === 'string' && + gdprConsent.consentString + ) { + payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; + } + + if (gdprConsent.vendorData) { + var vendorData = gdprConsent.vendorData; + var apiVersion = gdprConsent.apiVersion; + + if ( + apiVersion === 2 && + vendorData.vendor && + vendorData.vendor.consents && + typeof vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== + 'undefined' + ) { + payload.gdpr.consentGiven = + !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; + } else if ( + apiVersion === 1 && + vendorData.vendorConsents && + typeof vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== + 'undefined' + ) { + payload.gdpr.consentGiven = + !!vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; + } + } + } + + var options = { + contentType: 'application/json', + withCredentials: payload.gdpr.consentGiven, + customHeaders: { + version: '1.0.4', + }, + }; + var endPointUri = + payload.gdpr.consentGiven || !payload.gdpr.applies + ? 'https://abs.proxistore.com/'.concat( + payload.language, + '/v3/rtb/prebid/multi' + ) + : 'https://abs.cookieless-proxistore.com/'.concat( + payload.language, + '/v3/rtb/prebid/multi' + ); + return { + method: 'POST', + url: endPointUri, + data: JSON.stringify(payload), + options: options, + }; +} + +function _assignSegments(bid) { + if ( + bid.ortb2 && + bid.ortb2.user && + bid.ortb2.user.ext && + bid.ortb2.user.ext.data + ) { + return ( + bid.ortb2.user.ext.data || { + segments: [], + contextual_categories: {}, + } + ); + } + + return { + segments: [], + contextual_categories: {}, + }; +} + +function _createBidResponse(response) { + return { + requestId: response.requestId, + cpm: response.cpm, + width: response.width, + height: response.height, + ad: response.ad, + ttl: response.ttl, + creativeId: response.creativeId, + currency: response.currency, + netRevenue: response.netRevenue, + vastUrl: response.vastUrl, + vastXml: response.vastXml, + dealId: response.dealId, + meta: response.meta, + }; +} +/** + * Determines whether or not the given bid request is valid. + * + * @param bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + +function isBidRequestValid(bid) { + return !!(bid.params.website && bid.params.language); +} +/** + * Make a server request from the list of BidRequests. + * + * @param bidRequests - an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + +function buildRequests(bidRequests, bidderRequest) { + var request = _createServerRequest(bidRequests, bidderRequest); + + return request; +} +/** + * Unpack the response from the server into a list of bids. + * + * @param serverResponse A successful response from the server. + * @param bidRequest Request original server request + * @return An array of bids which were nested inside the server. + */ + +function interpretResponse(serverResponse, bidRequest) { + return serverResponse.body.map(_createBidResponse); +} + +function _assignFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + // eslint-disable-next-line no-console + console.log(bid.params.bidFloor); + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: 'banner', + size: '*', + }); + + if ( + utils.isPlainObject(floor) && + !isNaN(floor.floor) && + floor.currency === 'EUR' + ) { + return floor.floor; + } + return null; +} + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: isBidRequestValid, + buildRequests: buildRequests, + interpretResponse: interpretResponse, +}; + +registerBidder(spec); diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js new file mode 100644 index 00000000000..95f29423492 --- /dev/null +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -0,0 +1,169 @@ +import { expect } from 'chai'; +import { spec } from 'modules/proxistoreBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from '../../../src/config.js'; + +const BIDDER_CODE = 'proxistore'; +describe('ProxistoreBidAdapter', function () { + const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const bidderRequest = { + bidderCode: BIDDER_CODE, + auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', + bidderRequestId: '10edf38ec1a719', + gdprConsent: { + apiVersion: 2, + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: true, + }, + }, + }, + }, + }; + let bid = { + sizes: [[300, 600]], + params: { + website: 'example.fr', + language: 'fr', + }, + ortb2: { + user: { ext: { data: { segments: [], contextual_categories: {} } } }, + }, + auctionId: 442133079, + bidId: 464646969, + transactionId: 511916005, + }; + describe('isBidRequestValid', function () { + it('it should be true if required params are presents and there is no info in the local storage', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function () { + const date = new Date(); + date.setMinutes(date.getMinutes() - 1); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function () { + const date = new Date(); + date.setMinutes(date.getMinutes() - 10); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + const url = { + cookieBase: 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi', + cookieLess: + 'https://abs.cookieless-proxistore.com/fr/v3/rtb/prebid/multi', + }; + + let request = spec.buildRequests([bid], bidderRequest); + it('should return a valid object', function () { + expect(request).to.be.an('object'); + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + }); + it('request method should be POST', function () { + expect(request.method).to.equal('POST'); + }); + it('should have the value consentGiven to true bc we have 418 in the vendor list', function () { + const data = JSON.parse(request.data); + expect(data.gdpr.consentString).equal( + bidderRequest.gdprConsent.consentString + ); + expect(data.gdpr.applies).to.be.true; + expect(data.gdpr.consentGiven).to.be.true; + }); + it('should contain a valid url', function () { + // has gdpr consent + expect(request.url).equal(url.cookieBase); + // doens't have gpdr consent + bidderRequest.gdprConsent.vendorData = null; + + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + + // api v2 + bidderRequest.gdprConsent = { + gdprApplies: true, + allowAuctionWithoutConsent: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: true, + }, + }, + }, + apiVersion: 2, + }; + // has gdpr consent + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieBase); + + bidderRequest.gdprConsent.vendorData.vendor = {}; + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + }); + it('should have a property a length of bids equal to one if there is only one bid', function () { + const data = JSON.parse(request.data); + expect(data.hasOwnProperty('bids')).to.be.true; + expect(data.bids).to.be.an('array'); + expect(data.bids.length).equal(1); + expect(data.bids[0].hasOwnProperty('id')).to.be.true; + expect(data.bids[0].sizes).to.be.an('array'); + }); + it('should correctly set bidfloor on imp when getfloor in scope', function () { + let data = JSON.parse(request.data); + expect(data.bids[0].floor).to.be.null; + + bid.params['bidFloor'] = 1; + let req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + // eslint-disable-next-line no-console + console.log(data.bids[0]); + expect(data.bids[0].floor).equal(1); + bid.getFloor = function () { + return { currency: 'USD', floor: 1.0 }; + }; + req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + expect(data.bids[0].floor).to.be.null; + }); + }); + describe('interpretResponse', function () { + const emptyResponseParam = { body: [] }; + const fakeResponseParam = { + body: [ + { + ad: '', + cpm: 6.25, + creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', + currency: 'EUR', + dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', + height: 600, + netRevenue: true, + requestId: '3543724f2a033c9', + segments: [], + ttl: 10, + vastUrl: null, + vastXml: null, + width: 300, + }, + ], + }; + + it('should always return an array', function () { + let response = spec.interpretResponse(emptyResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(0); + response = spec.interpretResponse(fakeResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(1); + }); + }); +}); From ee219995af06878b4bc48e9fc5b63cc3f463a33d Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Thu, 8 Jul 2021 13:39:45 -0700 Subject: [PATCH 1254/1476] Merkle userid module: storing session data and domain (#7100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Merkle userid module: storing session data * Merkle: Setting session data * Fixing review comments * Updating to get the root domain if “sv_domain” is not configured Co-authored-by: skocheri --- modules/merkleIdSystem.js | 126 +++++++++++++++++++++++++++----------- modules/userId/userId.md | 6 +- 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index 353cc45473d..4ab29ec6f68 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -8,42 +8,80 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js' -import { getStorageManager } from '../src/storageManager.js'; +import {getStorageManager} from '../src/storageManager.js'; const MODULE_NAME = 'merkleId'; -const SESSION_COOKIE_NAME = '_svsid'; const ID_URL = 'https://id2.sv.rkdms.com/identity/'; +const DEFAULT_REFRESH = 7 * 3600; +const SESSION_COOKIE_NAME = '_svsid'; export const storage = getStorageManager(); function getSession(configParams) { let session = null; - if (typeof configParams.sv_session !== 'string') { + if (typeof configParams.sv_session === 'string') { session = configParams.sv_session; } else { - session = readCookie() || readFromLocalStorage(); + session = storage.getCookie(SESSION_COOKIE_NAME); } return session; } -function readCookie() { - return storage.cookiesAreEnabled() ? storage.getCookie(SESSION_COOKIE_NAME) : null; +function setCookie(name, value, expires) { + let expTime = new Date(); + expTime.setTime(expTime.getTime() + expires * 1000 * 60); + storage.setCookie(name, value, expTime.toUTCString()); } -function readFromLocalStorage() { - return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(SESSION_COOKIE_NAME) : null; +function setSession(storage, response) { + utils.logInfo('Merkle setting session '); + if (response && response.c && response.c.value && typeof response.c.value === 'string') { + setCookie(SESSION_COOKIE_NAME, response.c.value, storage.expires); + } } function constructUrl(configParams) { const session = getSession(configParams); let url = ID_URL + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; if (session) { - url.append(`&sv_session=${session}`); + url = `${url}&sv_session=${session}`; } utils.logInfo('Merkle url :' + url); return url; } +function generateId(configParams, configStorage) { + const url = constructUrl(configParams); + + const resp = function (callback) { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + setSession(configStorage, responseObj) + utils.logInfo('Merkle responseObj ' + JSON.stringify(responseObj)); + } catch (error) { + utils.logError(error); + } + } + + const date = new Date().toUTCString(); + responseObj.date = date; + utils.logInfo('Merkle responseObj with date ' + JSON.stringify(responseObj)); + callback(responseObj); + }, + error: error => { + utils.logError(`${MODULE_NAME}: merkleId fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); + }; + return resp; +} + /** @type {Submodule} */ export const merkleIdSubmodule = { /** @@ -60,7 +98,7 @@ export const merkleIdSubmodule = { decode(value) { const id = (value && value.pam_id && typeof value.pam_id.id === 'string') ? value.pam_id : undefined; utils.logInfo('Merkle id ' + JSON.stringify(id)); - return id ? { 'merkleId': id } : undefined; + return id ? {'merkleId': id} : undefined; }, /** * performs action to obtain id and return a value in the callback's response argument @@ -70,7 +108,10 @@ export const merkleIdSubmodule = { * @returns {IdResponse|undefined} */ getId(config, consentData) { + utils.logInfo('User ID - merkleId generating id'); + const configParams = (config && config.params) || {}; + if (!configParams || typeof configParams.vendor !== 'string') { utils.logError('User ID - merkleId submodule requires a valid vendor to be defined'); return; @@ -85,41 +126,54 @@ export const merkleIdSubmodule = { utils.logError('User ID - merkleId submodule requires a valid sv_pubid string to be defined'); return; } + if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { + utils.logError('User ID - merkleId submodule does not currently handle consent strings'); + return; + } if (typeof configParams.sv_domain !== 'string') { - utils.logError('User ID - merkleId submodule requires a valid sv_domain string to be defined'); - return; + configParams.sv_domain = merkleIdSubmodule.findRootDomain(); } + const configStorage = (config && config.storage) || {}; + const resp = generateId(configParams, configStorage) + return {callback: resp}; + }, + extendId: function (config = {}, consentData, storedId) { + utils.logInfo('User ID - merkleId stored id ' + storedId); + const configParams = (config && config.params) || {}; + if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { utils.logError('User ID - merkleId submodule does not currently handle consent strings'); return; } - const url = constructUrl(configParams); - - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - utils.logInfo('Merkle responseObj ' + JSON.stringify(responseObj)); - } catch (error) { - utils.logError(error); - } - } - callback(responseObj); - }, - error: error => { - utils.logError(`${MODULE_NAME}: merkleId fetch encountered an error`, error); - callback(); - } - }; - ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); - }; - return {callback: resp}; + + if (typeof configParams.sv_domain !== 'string') { + configParams.sv_domain = merkleIdSubmodule.findRootDomain(); + } + const configStorage = (config && config.storage) || {}; + if (configStorage && configStorage.refreshInSeconds && typeof configParams.refreshInSeconds === 'number') { + return {id: storedId}; + } + let refreshInSeconds = DEFAULT_REFRESH; + if (configParams && configParams.refreshInSeconds && typeof configParams.refreshInSeconds === 'number') { + refreshInSeconds = configParams.refreshInSeconds; + utils.logInfo('User ID - merkleId param refreshInSeconds' + refreshInSeconds); + } + const storedDate = new Date(storedId.date); + let refreshNeeded = false; + if (storedDate) { + refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > refreshInSeconds * 1000); + if (refreshNeeded) { + utils.logInfo('User ID - merkleId needs refreshing id'); + const resp = generateId(configParams, configStorage) + return {callback: resp}; + } + } + utils.logInfo('User ID - merkleId not refreshed'); + return {id: storedId}; } + }; submodule('userId', merkleIdSubmodule); diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 7d352505aa9..9bbe0bdde6a 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -101,7 +101,8 @@ pbjs.setConfig({ vendor:'sdfg', sv_cid:'dfg', sv_pubid:'xcv', - sv_domain:'zxv' + sv_domain:'zxv', + refreshInSeconds: 10 // Refreshes the id based on this configuration, else by default every 7 days }, storage: { type: "cookie", @@ -218,7 +219,8 @@ pbjs.setConfig({ vendor:'sdfg', sv_cid:'dfg', sv_pubid:'xcv', - sv_domain:'zxv' + sv_domain:'zxv', + refreshInSeconds: 10 // Refreshes the id based on this configuration, else by default every 7 days }, storage: { type: "html5", From cdea78824232cad538ddcb6ca805c1d23f5132c4 Mon Sep 17 00:00:00 2001 From: nlavallee-sortable <67378928+nlavallee-sortable@users.noreply.github.com> Date: Fri, 9 Jul 2021 08:08:34 -0400 Subject: [PATCH 1255/1476] Sortable Bid Adapter: update to comply with Prebid 5.x (#7157) * Re-add Sortable bid adapter and tests as they existed in Prebid 4.x. * Update Sortable bid adapter to comply with Prebid 5.x updates. Use the price floors module. Remove floor size map logic. Add advertiserDomains to bid responses. Update unit tests. --- modules/sortableBidAdapter.js | 365 +++++++++++++ test/spec/modules/sortableBidAdapter_spec.js | 536 +++++++++++++++++++ 2 files changed, 901 insertions(+) create mode 100644 modules/sortableBidAdapter.js create mode 100644 test/spec/modules/sortableBidAdapter_spec.js diff --git a/modules/sortableBidAdapter.js b/modules/sortableBidAdapter.js new file mode 100644 index 00000000000..906369eccc3 --- /dev/null +++ b/modules/sortableBidAdapter.js @@ -0,0 +1,365 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { createEidsArray } from './userId/eids.js'; + +const BIDDER_CODE = 'sortable'; +const SERVER_URL = 'https://c.deployads.com'; + +function setAssetRequired(native, asset) { + if (native.required) { + asset.required = 1; + } + return asset; +} + +function buildNativeRequest(nativeMediaType) { + const assets = []; + const title = nativeMediaType.title; + if (title) { + assets.push(setAssetRequired(title, { + title: {len: title.len} + })); + } + const img = nativeMediaType.image; + if (img) { + assets.push(setAssetRequired(img, { + img: { + type: 3, // Main + wmin: 1, + hmin: 1 + } + })); + } + const icon = nativeMediaType.icon; + if (icon) { + assets.push(setAssetRequired(icon, { + img: { + type: 1, // Icon + wmin: 1, + hmin: 1 + } + })); + } + const body = nativeMediaType.body; + if (body) { + assets.push(setAssetRequired(body, {data: {type: 2}})); + } + const cta = nativeMediaType.cta; + if (cta) { + assets.push(setAssetRequired(cta, {data: {type: 12}})); + } + const sponsoredBy = nativeMediaType.sponsoredBy; + if (sponsoredBy) { + assets.push(setAssetRequired(sponsoredBy, {data: {type: 1}})); + } + + utils._each(assets, (asset, id) => asset.id = id); + return { + ver: '1', + request: JSON.stringify({ + ver: '1', + assets + }) + }; +} + +function tryParseNativeResponse(adm) { + let native = null; + try { + native = JSON.parse(adm); + } catch (e) { + utils.logError('Sortable bid adapter unable to parse native bid response:\n\n' + e); + } + return native && native.native; +} + +function createImgObject(img) { + if (img.w || img.h) { + return { + url: img.url, + width: img.w, + height: img.h + }; + } else { + return img.url; + } +} + +function interpretNativeResponse(response) { + const native = {}; + if (response.link) { + native.clickUrl = response.link.url; + } + utils._each(response.assets, asset => { + switch (asset.id) { + case 1: + native.title = asset.title.text; + break; + case 2: + native.image = createImgObject(asset.img); + break; + case 3: + native.icon = createImgObject(asset.img); + break; + case 4: + native.body = asset.data.value; + break; + case 5: + native.cta = asset.data.value; + break; + case 6: + native.sponsoredBy = asset.data.value; + break; + } + }); + return native; +} + +function transformSyncs(responses, type, syncs) { + utils._each(responses, res => { + if (res.body && res.body.ext && res.body.ext.sync_dsps && res.body.ext.sync_dsps.length) { + utils._each(res.body.ext.sync_dsps, sync => { + if (sync[0] === type && sync[1]) { + syncs.push({type, url: sync[1]}); + } + }); + } + }); +} + +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.floor ? bid.params.floor : null; + } + + // MediaType and Size will automatically get set for us if the bid only has + // one media type or one size. + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE, VIDEO], + + isBidRequestValid: function(bid) { + const sortableConfig = config.getConfig('sortable'); + const haveSiteId = (sortableConfig && !!sortableConfig.siteId) || bid.params.siteId; + const floor = getBidFloor(bid); + const validFloor = !floor || utils.isNumber(floor); + const validKeywords = !bid.params.keywords || + (utils.isPlainObject(bid.params.keywords) && + Object.keys(bid.params.keywords).every(key => + utils.isStr(key) && utils.isStr(bid.params.keywords[key]) + )) + const isBanner = !bid.mediaTypes || bid.mediaTypes[BANNER] || !(bid.mediaTypes[NATIVE] || bid.mediaTypes[VIDEO]); + const bannerSizes = isBanner ? utils.deepAccess(bid, `mediaType.${BANNER}.sizes`) || bid.sizes : null; + return !!(bid.params.tagId && haveSiteId && validFloor && validKeywords && (!isBanner || + (bannerSizes && bannerSizes.length > 0 && bannerSizes.every(sizeArr => sizeArr.length == 2 && sizeArr.every(num => utils.isNumber(num)))))); + }, + + buildRequests: function(validBidReqs, bidderRequest) { + const sortableConfig = config.getConfig('sortable') || {}; + const globalSiteId = sortableConfig.siteId; + let loc = utils.parseUrl(bidderRequest.refererInfo.referer); + + const sortableImps = utils._map(validBidReqs, bid => { + const rv = { + id: bid.bidId, + tagid: bid.params.tagId, + ext: {} + }; + const bannerMediaType = utils.deepAccess(bid, `mediaTypes.${BANNER}`); + const nativeMediaType = utils.deepAccess(bid, `mediaTypes.${NATIVE}`); + const videoMediaType = utils.deepAccess(bid, `mediaTypes.${VIDEO}`); + if (bannerMediaType || !(nativeMediaType || videoMediaType)) { + const bannerSizes = (bannerMediaType && bannerMediaType.sizes) || bid.sizes; + rv.banner = { + format: utils._map(bannerSizes, ([width, height]) => ({w: width, h: height})) + }; + } + if (nativeMediaType) { + rv.native = buildNativeRequest(nativeMediaType); + } + if (videoMediaType && videoMediaType.context === 'instream') { + const video = {placement: 1}; + video.mimes = videoMediaType.mimes || []; + video.minduration = utils.deepAccess(bid, 'params.video.minduration') || 10; + video.maxduration = utils.deepAccess(bid, 'params.video.maxduration') || 60; + const startDelay = utils.deepAccess(bid, 'params.video.startdelay'); + if (startDelay != null) { + video.startdelay = startDelay; + } + if (videoMediaType.playerSize && videoMediaType.playerSize.length) { + const size = videoMediaType.playerSize[0]; + video.w = size[0]; + video.h = size[1]; + } + if (videoMediaType.api) { + video.api = videoMediaType.api; + } + if (videoMediaType.protocols) { + video.protocols = videoMediaType.protocols; + } + if (videoMediaType.playbackmethod) { + video.playbackmethod = videoMediaType.playbackmethod; + } + rv.video = video; + } + const floor = getBidFloor(bid); + if (floor) { + rv.floor = floor; + } + if (bid.params.keywords) { + rv.ext.keywords = bid.params.keywords; + } + if (bid.params.bidderParams) { + utils._each(bid.params.bidderParams, (params, partner) => { + rv.ext[partner] = params; + }); + } + return rv; + }); + const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + const bidUserId = validBidReqs[0].userId; + const eids = createEidsArray(bidUserId); + const sortableBidReq = { + id: utils.getUniqueIdentifierStr(), + imp: sortableImps, + source: { + ext: { + schain: validBidReqs[0].schain + } + }, + regs: { + ext: {} + }, + site: { + domain: loc.hostname, + page: loc.href, + ref: loc.href, + publisher: { + id: globalSiteId || validBidReqs[0].params.siteId, + }, + device: { + w: screen.width, + h: screen.height + }, + }, + user: { + ext: {} + } + }; + if (bidderRequest && bidderRequest.timeout > 0) { + sortableBidReq.tmax = bidderRequest.timeout; + } + if (gdprConsent) { + sortableBidReq.user = { + ext: { + consent: gdprConsent.consentString + } + }; + if (typeof gdprConsent.gdprApplies == 'boolean') { + sortableBidReq.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0 + } + } + if (eids.length) { + sortableBidReq.user.ext.eids = eids; + } + if (bidderRequest.uspConsent) { + sortableBidReq.regs.ext.us_privacy = bidderRequest.uspConsent; + } + return { + method: 'POST', + url: `${SERVER_URL}/openrtb2/auction?src=$$REPO_AND_VERSION$$&host=${loc.hostname}`, + data: JSON.stringify(sortableBidReq), + options: {contentType: 'text/plain'} + }; + }, + + interpretResponse: function(serverResponse) { + const { body: {id, seatbid} } = serverResponse; + const sortableBids = []; + if (id && seatbid) { + utils._each(seatbid, seatbid => { + utils._each(seatbid.bid, bid => { + const bidObj = { + requestId: bid.impid, + cpm: parseFloat(bid.price), + width: parseInt(bid.w), + height: parseInt(bid.h), + creativeId: bid.crid || bid.id, + dealId: bid.dealid || null, + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ttl: 60, + meta: { + advertiserDomains: bid.adomain || [] + } + }; + if (bid.adm) { + const adFormat = utils.deepAccess(bid, 'ext.ad_format') + if (adFormat === 'native') { + let native = tryParseNativeResponse(bid.adm); + if (!native) { + return; + } + bidObj.mediaType = NATIVE; + bidObj.native = interpretNativeResponse(native); + } else if (adFormat === 'instream') { + bidObj.mediaType = VIDEO; + bidObj.vastXml = bid.adm; + } else { + bidObj.mediaType = BANNER; + bidObj.ad = bid.adm; + if (bid.nurl) { + bidObj.ad += utils.createTrackPixelHtml(decodeURIComponent(bid.nurl)); + } + } + } else if (bid.nurl) { + bidObj.adUrl = bid.nurl; + } + if (bid.ext) { + bidObj[BIDDER_CODE] = bid.ext; + } + sortableBids.push(bidObj); + }); + }); + } + return sortableBids; + }, + + getUserSyncs: (syncOptions, responses) => { + const syncs = []; + if (syncOptions.iframeEnabled) { + transformSyncs(responses, 'iframe', syncs); + } + if (syncOptions.pixelEnabled) { + transformSyncs(responses, 'image', syncs); + } + return syncs; + }, + + onTimeout(details) { + fetch(`${SERVER_URL}/prebid/timeout`, { + method: 'POST', + body: JSON.stringify(details), + mode: 'no-cors', + headers: new Headers({ + 'Content-Type': 'text/plain' + }) + }); + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/sortableBidAdapter_spec.js b/test/spec/modules/sortableBidAdapter_spec.js new file mode 100644 index 00000000000..17f77e96d51 --- /dev/null +++ b/test/spec/modules/sortableBidAdapter_spec.js @@ -0,0 +1,536 @@ +import { expect } from 'chai'; +import { spec } from 'modules/sortableBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; + +describe('sortableBidAdapter', function() { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + function makeBid() { + return { + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'keywords': { + 'key1': 'val1', + 'key2': 'val2' + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + } + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when tagId not passed correctly', function () { + let bid = makeBid(); + delete bid.params.tagId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when sizes not passed correctly', function () { + let bid = makeBid(); + delete bid.sizes; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when sizes are wrong length', function () { + let bid = makeBid(); + bid.sizes = [[300]]; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when sizes are empty', function () { + let bid = makeBid(); + bid.sizes = []; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = makeBid(); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when the keywords are invalid', function () { + let bid = makeBid(); + bid.params.keywords = { + 'badval': 1234 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.params.keywords = 'a'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when the keywords are missing or empty', function () { + let bid = makeBid(); + bid.params.keywords = {}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + delete bid.params.keywords; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true with video media type', () => { + const videoBid = { + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + 'video': { + } + } + }; + expect(spec.isBidRequestValid(videoBid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'floor': 0.21, + 'keywords': { + 'key1': 'val1', + 'key2': 'val2' + } + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }, { + 'bidder': 'sortable', + 'params': { + 'tagId': '403371', + 'siteId': 'example.com', + 'floor': 0.21 + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + 'native': { + 'body': {'required': true, 'sendId': true}, + 'clickUrl': {'required': true, 'sendId': true}, + 'cta': {'required': true, 'sendId': true}, + 'icon': {'required': true, 'sendId': true}, + 'image': {'required': true, 'sendId': true}, + 'sponsoredBy': {'required': true, 'sendId': true}, + 'title': {'required': true, 'sendId': true, 'len': 100} + } + } + }]; + + const request = spec.buildRequests(bidRequests, {refererInfo: { referer: 'http://example.com/page?param=val' }}); + const requestBody = JSON.parse(request.data); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('attaches source and version to endpoint URL as query params', function () { + const ENDPOINT = `https://c.deployads.com/openrtb2/auction?src=$$REPO_AND_VERSION$$&host=example.com`; + expect(request.url).to.equal(ENDPOINT); + }); + + it('sends screen dimensions', function () { + expect(requestBody.site.device.w).to.equal(screen.width); + expect(requestBody.site.device.h).to.equal(screen.height); + }); + + it('includes the ad size in the bid request', function () { + expect(requestBody.imp[0].banner.format[0].w).to.equal(300); + expect(requestBody.imp[0].banner.format[0].h).to.equal(250); + }); + + it('includes the params in the bid request', function () { + expect(requestBody.imp[0].ext.keywords).to.deep.equal( + {'key1': 'val1', + 'key2': 'val2'} + ); + expect(requestBody.site.publisher.id).to.equal('example.com'); + expect(requestBody.imp[0].tagid).to.equal('403370'); + expect(requestBody.imp[0].floor).to.equal(0.21); + }); + + it('sets domain and href correctly', function () { + expect(requestBody.site.domain).to.equal('example.com'); + expect(requestBody.site.page).to.equal('http://example.com/page?param=val'); + }); + + it('should have the version in native object set for native bid', function() { + expect(requestBody.imp[1].native.ver).to.equal('1'); + }); + + it('should have the assets set for native bid', function() { + const assets = JSON.parse(requestBody.imp[1].native.request).assets; + expect(assets[0]).to.deep.equal({'title': {'len': 100}, 'required': 1, 'id': 0}); + expect(assets[1]).to.deep.equal({'img': {'type': 3, 'wmin': 1, 'hmin': 1}, 'required': 1, 'id': 1}); + expect(assets[2]).to.deep.equal({'img': {'type': 1, 'wmin': 1, 'hmin': 1}, 'required': 1, 'id': 2}); + expect(assets[3]).to.deep.equal({'data': {'type': 2}, 'required': 1, 'id': 3}); + expect(assets[4]).to.deep.equal({'data': {'type': 12}, 'required': 1, 'id': 4}); + expect(assets[5]).to.deep.equal({'data': {'type': 1}, 'required': 1, 'id': 5}); + }); + + const videoBidRequests = [{ + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'video': { + 'minduration': 5, + 'maxduration': 10, + 'startdelay': 0 + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'mimes': ['video/x-ms-wmv'], + 'playerSize': [[400, 300]], + 'api': [0], + 'protocols': [2, 3], + 'playbackmethod': [1] + } + } + }]; + + const videoRequest = spec.buildRequests(videoBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); + const videoRequestBody = JSON.parse(videoRequest.data); + + it('should include video params', () => { + const video = videoRequestBody.imp[0].video; + expect(video.mimes).to.deep.equal(['video/x-ms-wmv']); + expect(video.w).to.equal(400); + expect(video.h).to.equal(300); + expect(video.api).to.deep.equal([0]); + expect(video.protocols).to.deep.equal([2, 3]); + expect(video.playbackmethod).to.deep.equal([1]); + expect(video.minduration).to.equal(5); + expect(video.maxduration).to.equal(10); + expect(video.startdelay).to.equal(0); + }); + + it('sets domain and href correctly', function () { + expect(videoRequestBody.site.domain).to.equal('localhost'); + expect(videoRequestBody.site.page).to.equal('http://localhost:9876/'); + }); + + const gdprBidRequests = [{ + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'floor': 0.21, + 'keywords': {} + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + const consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; + + function getGdprRequestBody(gdprApplies, consentString) { + const gdprRequest = spec.buildRequests(gdprBidRequests, {'gdprConsent': { + 'gdprApplies': gdprApplies, + 'consentString': consentString + }, + refererInfo: { + referer: 'http://localhost:9876/' + }}); + return JSON.parse(gdprRequest.data); + } + + it('should handle gdprApplies being present and true', function() { + const gdprRequestBody = getGdprRequestBody(true, consentString); + expect(gdprRequestBody.regs.ext.gdpr).to.equal(1); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdprApplies being present and false', function() { + const gdprRequestBody = getGdprRequestBody(false, consentString); + expect(gdprRequestBody.regs.ext.gdpr).to.equal(0); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdprApplies being undefined', function() { + const gdprRequestBody = getGdprRequestBody(undefined, consentString); + expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); + expect(gdprRequestBody.user.ext.consent).to.equal(consentString); + }) + + it('should handle gdprConsent being undefined', function() { + const gdprRequest = spec.buildRequests(gdprBidRequests, {refererInfo: { referer: 'http://localhost:9876/' }}); + const gdprRequestBody = JSON.parse(gdprRequest.data); + expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); + expect(gdprRequestBody.user.ext.consent).to.equal(undefined); + }) + + const eidsBidRequests = [{ + 'bidder': 'sortable', + 'params': { + 'tagId': '403370', + 'siteId': 'example.com', + 'floor': 0.21, + 'keywords': {} + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + it('should not set user ids when none present', function() { + const eidsRequest = spec.buildRequests(eidsBidRequests, {refererInfo: { + referer: 'http://localhost:9876/' + }}); + const eidsRequestBody = JSON.parse(eidsRequest.data); + + expect(eidsRequestBody.user.ext.eids).to.equal(undefined); + }) + + it('should set user ids when present', function() { + eidsBidRequests[0].userId = { criteoId: 'sample-userid' }; + const eidsRequest = spec.buildRequests(eidsBidRequests, {refererInfo: { + referer: 'http://localhost:9876/' + }}); + const eidsRequestBody = JSON.parse(eidsRequest.data); + + expect(eidsRequestBody.user.ext.eids.length).to.equal(1); + }) + }); + + describe('interpretResponse', function () { + function makeResponse() { + return { + body: { + 'id': '5e5c23a5ba71e78', + 'seatbid': [ + { + 'bid': [ + { + 'id': '6vmb3isptf', + 'crid': 'sortablescreative', + 'impid': '322add653672f68', + 'price': 1.22, + 'adm': '', + 'attr': [5], + 'h': 90, + 'nurl': 'http://nurl', + 'w': 728 + } + ], + 'seat': 'MOCK' + } + ], + 'bidid': '5e5c23a5ba71e78' + } + }; + } + + function makeNativeResponse() { + return { + body: { + 'id': '5e5c23a5ba71e77', + 'seatbid': [ + { + 'bid': [ + { + 'id': '6vmb3isptf', + 'crid': 'sortablescreative', + 'impid': '322add653672f67', + 'price': 1.55, + 'adm': '{"native":{"link":{"clicktrackers":[],"url":"https://www.sortable.com/"},"assets":[{"title":{"text":"Ads With Sortable"},"id":1},{"img":{"w":790,"url":"https://path.to/image","h":294},"id":2},{"img":{"url":"https://path.to/icon"},"id":3},{"data":{"value":"Body here"},"id":4},{"data":{"value":"Learn More"},"id":5},{"data":{"value":"Sortable"},"id":6}],"imptrackers":[],"ver":1}}', + 'ext': {'ad_format': 'native'}, + 'h': 90, + 'nurl': 'http://nurl', + 'w': 728 + } + ], + 'seat': 'MOCK' + } + ], + 'bidid': '5e5c23a5ba71e77' + } + }; + } + + const expectedBid = { + 'requestId': '322add653672f68', + 'cpm': 1.22, + 'width': 728, + 'height': 90, + 'creativeId': 'sortablescreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'meta': { 'advertiserDomains': [] }, + 'ttl': 60, + 'ad': '
' + }; + + const expectedNativeBid = { + 'requestId': '322add653672f67', + 'cpm': 1.55, + 'width': 728, + 'height': 90, + 'creativeId': 'sortablescreative', + 'dealId': null, + 'currency': 'USD', + 'netRevenue': true, + 'sortable': { 'ad_format': 'native' }, + 'mediaType': 'native', + 'meta': { 'advertiserDomains': [] }, + 'ttl': 60, + 'native': { + 'clickUrl': 'https://www.sortable.com/', + 'title': 'Ads With Sortable', + 'image': {'url': 'https://path.to/image', 'height': 294, 'width': 790}, + 'icon': 'https://path.to/icon', + 'body': 'Body here', + 'cta': 'Learn More', + 'sponsoredBy': 'Sortable' + } + }; + + it('should get the correct bid response', function () { + let result = spec.interpretResponse(makeResponse()); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedBid); + }); + + it('should handle a missing crid', function () { + let noCridResponse = makeResponse(); + delete noCridResponse.body.seatbid[0].bid[0].crid; + const fallbackCrid = noCridResponse.body.seatbid[0].bid[0].id; + let noCridResult = Object.assign({}, expectedBid, {'creativeId': fallbackCrid}); + let result = spec.interpretResponse(noCridResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noCridResult); + }); + + it('should handle a missing nurl', function () { + let noNurlResponse = makeResponse(); + delete noNurlResponse.body.seatbid[0].bid[0].nurl; + let noNurlResult = Object.assign({}, expectedBid); + noNurlResult.ad = ''; + let result = spec.interpretResponse(noNurlResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noNurlResult); + }); + + it('should handle a missing adm', function () { + let noAdmResponse = makeResponse(); + delete noAdmResponse.body.seatbid[0].bid[0].adm; + let noAdmResult = Object.assign({}, expectedBid); + delete noAdmResult.ad; + noAdmResult.adUrl = 'http://nurl'; + let result = spec.interpretResponse(noAdmResponse); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(noAdmResult); + }); + + it('handles empty bid response', function () { + let response = { + body: { + 'id': '5e5c23a5ba71e78', + 'seatbid': [] + } + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + + it('should get the correct native bid response', function () { + let result = spec.interpretResponse(makeNativeResponse()); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedNativeBid); + }); + + it('fail to parse invalid native bid response', function () { + let response = makeNativeResponse(); + response.body.seatbid[0].bid[0].adm = ''; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + + it('should keep custom properties', () => { + const customProperties = {test: 'a test message', param: {testParam: 1}}; + const expectedResult = Object.assign({}, expectedBid, {[spec.code]: customProperties}); + const response = makeResponse(); + response.body.seatbid[0].bid[0].ext = customProperties; + const result = spec.interpretResponse(response); + expect(result.length).to.equal(1); + expect(result[0]).to.deep.equal(expectedResult); + }); + + it('should handle instream response', () => { + const response = makeResponse(); + const bid = response.body.seatbid[0].bid[0]; + delete bid.nurl; + bid.ext = {ad_format: 'instream'}; + const result = spec.interpretResponse(response)[0]; + expect(result.mediaType).to.equal('video'); + expect(result.vastXml).to.equal(bid.adm); + }); + + it('should return iframe syncs', () => { + const syncResponse = { + ext: { + sync_dsps: [ + ['iframe', 'http://example-dsp/sync-iframe'], + ['image', 'http://example-dsp/sync-image'] + ] + } + }; + expect(spec.getUserSyncs({iframeEnabled: true}, [{body: syncResponse}])).to.deep.equal([{ + type: 'iframe', + url: 'http://example-dsp/sync-iframe' + }]); + }); + + it('should return image syncs', () => { + const syncResponse = { + ext: { + sync_dsps: [ + ['iframe', 'http://example-dsp/sync-iframe'], + ['image', 'http://example-dsp/sync-image'] + ] + } + }; + expect(spec.getUserSyncs({pixelEnabled: true}, [{body: syncResponse}])).to.deep.equal([{ + type: 'image', + url: 'http://example-dsp/sync-image' + }]); + }); + }); +}); From 237c88b1e33fa7e8ae9caefa3c525d199964be0d Mon Sep 17 00:00:00 2001 From: Alexey Sukhikh Date: Fri, 9 Jul 2021 18:44:39 +0300 Subject: [PATCH 1256/1476] GrowAdvertising: add native support (#7126) * Add native support * Update docs --- modules/growadvertisingBidAdapter.js | 70 ++++++++-- modules/growadvertisingBidAdapter.md | 32 ++++- .../modules/growadvertisingBidAdapter_spec.js | 125 ++++++++++++++---- 3 files changed, 185 insertions(+), 42 deletions(-) diff --git a/modules/growadvertisingBidAdapter.js b/modules/growadvertisingBidAdapter.js index b5103a06fa8..0626b137a1b 100644 --- a/modules/growadvertisingBidAdapter.js +++ b/modules/growadvertisingBidAdapter.js @@ -2,11 +2,14 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import {triggerPixel} from '../src/utils.js'; const BIDDER_CODE = 'growads'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], isBidRequestValid: function (bid) { return bid.params && !!bid.params.zoneId; @@ -59,6 +62,7 @@ export const spec = { let isCorrectCPM = true; let minCPM; let maxCPM; + let bid = {}; let body = serverResponse.body; @@ -86,32 +90,74 @@ export const spec = { isCorrectCPM = false; } - // Ensure that response ad matches one of the placement sizes. - utils._each(utils.deepAccess(request, 'mediaTypes.banner.sizes', []), function (size) { - if (width === size[0] && height === size[1]) { - isCorrectSize = true; - } - }); - - if (isCorrectCPM && isCorrectSize) { - bidResponses.push({ + if (isCorrectCPM) { + bid = { requestId: request.bidId, bidderCode: request.bidder, creativeId: response.creativeId, cpm: CPM, width: width, height: height, - ad: response.ad, currency: response.currency, netRevenue: true, ttl: response.ttl, + adUnitCode: request.adUnitCode, referrer: utils.deepAccess(request, 'refererInfo.referer') - }); + }; + + if (response.hasOwnProperty(NATIVE)) { + bid[NATIVE] = { + title: response[NATIVE].title, + body: response[NATIVE].body, + body2: response[NATIVE].body2, + cta: response[NATIVE].cta, + sponsoredBy: response[NATIVE].sponsoredBy, + clickUrl: response[NATIVE].clickUrl, + impressionTrackers: response[NATIVE].impressionTrackers, + }; + + if (response[NATIVE].image) { + bid[NATIVE].image = { + url: response[NATIVE].image.url, + height: response[NATIVE].image.height, + width: response[NATIVE].image.width + }; + } + + if (response[NATIVE].icon) { + bid[NATIVE].icon = { + url: response[NATIVE].icon.url, + height: response[NATIVE].icon.height, + width: response[NATIVE].icon.width + }; + } + bid.mediaType = NATIVE; + isCorrectSize = true; + } else { + bid.ad = response.ad; + bid.mediaType = BANNER; + // Ensure that response ad matches one of the placement sizes. + utils._each(utils.deepAccess(request, 'mediaTypes.banner.sizes', []), function (size) { + if (width === size[0] && height === size[1]) { + isCorrectSize = true; + } + }); + } + + if (isCorrectSize) { + bidResponses.push(bid); + } } } return bidResponses; - } + }, + + onBidWon: function (bid) { + if (bid.vurl) { + triggerPixel(bid.vurl); + } + }, }; registerBidder(spec); diff --git a/modules/growadvertisingBidAdapter.md b/modules/growadvertisingBidAdapter.md index f20b853a33f..f17691e9b9f 100644 --- a/modules/growadvertisingBidAdapter.md +++ b/modules/growadvertisingBidAdapter.md @@ -35,6 +35,36 @@ var adUnits = [ } } ] - } + }, + // Native adUnit + { + code: 'native-div', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + }, + } + }, + bids: [ + { + bidder: 'growads', + params: { + zoneId: 'YpQobqT-vEybhHx-1qaNMFx-Wj3Kwc2', + domain: 'native-test.growadvertising.com' + } + } + ] + }, ]; ``` diff --git a/test/spec/modules/growadvertisingBidAdapter_spec.js b/test/spec/modules/growadvertisingBidAdapter_spec.js index 30d9207d4c2..55eea06cca8 100644 --- a/test/spec/modules/growadvertisingBidAdapter_spec.js +++ b/test/spec/modules/growadvertisingBidAdapter_spec.js @@ -1,19 +1,46 @@ import { expect } from 'chai'; import { spec } from 'modules/growadvertisingBidAdapter.js'; import * as utils from '../../../src/utils.js'; +import {BANNER, NATIVE} from '../../../src/mediaTypes.js'; describe('GrowAdvertising Adapter', function() { const ZONE_ID = 'unique-zone-id'; - const serverResponse = { + const serverResponseBanner = { body: { status: 'success', width: 300, height: 250, creativeId: 'ULqaukILu0RnMa0FyidOtkji4Po3qbgQ9ceRVGlhjLLKnrrLAATmGNCwtE99Ems8', - ad: '', + ad: '', cpm: 1, ttl: 180, currency: 'USD', + type: BANNER, + } + }; + const serverResponseNative = { + body: { + status: 'success', + width: 400, + height: 300, + creativeId: 'ULqaukILu0RnMa0FyidOtkji4Po3qbgQ9ceRVGlhjLLKnrrLAATmGNCwtE99Ems9', + cpm: 2, + ttl: 180, + currency: 'USD', + native: { + title: 'Test title', + body: 'Test body', + body2: null, + sponsoredBy: 'Sponsored by', + cta: null, + clickUrl: 'https://example.org', + image: { + width: 400, + height: 300, + url: 'https://image.source.com/img', + } + }, + type: NATIVE } }; let bidRequests = []; @@ -32,6 +59,25 @@ describe('GrowAdvertising Adapter', function() { sizes: [[300, 250], [300, 600]], }, }, + }, + { + bidder: 'growads', + params: { + zoneId: ZONE_ID, + }, + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + }, + }, } ]; }); @@ -122,39 +168,60 @@ describe('GrowAdvertising Adapter', function() { }); describe('bid responses', function () { - it('should return complete bid response', function () { - let bids = spec.interpretResponse(serverResponse, {bidRequest: bidRequests[0]}); - - expect(bids).to.be.lengthOf(1); - expect(bids[0].bidderCode).to.equal('growads'); - expect(bids[0].cpm).to.equal(1); - expect(bids[0].width).to.equal(300); - expect(bids[0].height).to.equal(250); - expect(bids[0].creativeId).to.have.length.above(1); - expect(bids[0].ad).to.have.length.above(1); - }); + describe(BANNER, function () { + it('should return complete bid response banner', function () { + let bids = spec.interpretResponse(serverResponseBanner, {bidRequest: bidRequests[0]}); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].bidderCode).to.equal('growads'); + expect(bids[0].cpm).to.equal(1); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].creativeId).to.have.length.above(1); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].mediaType).to.equal(BANNER); + }); - it('should return empty bid on incorrect size', function () { - let response = utils.mergeDeep(serverResponse, { - body: { - width: 150, - height: 150 - } + it('should return empty bid on incorrect size', function () { + let response = utils.mergeDeep(serverResponseBanner, { + body: { + width: 150, + height: 150 + } + }); + + let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + expect([]).to.be.lengthOf(0); }); - let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); - expect([]).to.be.lengthOf(0); - }); + it('should return empty bid on incorrect CPM', function () { + let response = utils.mergeDeep(serverResponseBanner, { + body: { + cpm: 10 + } + }); - it('should return empty bid on incorrect CPM', function () { - let response = utils.mergeDeep(serverResponse, { - body: { - cpm: 10 - } + let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + expect([]).to.be.lengthOf(0); }); + }); - let bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); - expect([]).to.be.lengthOf(0); + describe(NATIVE, function () { + it('should return complete bid response banner', function () { + let bids = spec.interpretResponse(serverResponseNative, {bidRequest: bidRequests[1]}); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].bidderCode).to.equal('growads'); + expect(bids[0].cpm).to.equal(2); + expect(bids[0].width).to.equal(400); + expect(bids[0].height).to.equal(300); + expect(bids[0].creativeId).to.have.length.above(1); + expect(bids[0]).property('native'); + expect(bids[0].native.title).to.have.length.above(1); + expect(bids[0].native.body).to.have.length.above(1); + expect(bids[0].native).property('image'); + expect(bids[0].mediaType).to.equal(NATIVE); + }); }); }); }); From f150d713e8a5d4b17be5aed0c4f5dd71eba64596 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Mon, 12 Jul 2021 09:03:19 +0300 Subject: [PATCH 1257/1476] TheMediaGrid: added keywords formatting in the ad request (#7164) * 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 Co-authored-by: Chris Huie --- modules/gridBidAdapter.js | 54 ++++++++++++++++++++++-- test/spec/modules/gridBidAdapter_spec.js | 36 ++++++++++++++-- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 16f8ff11574..854b82aa84d 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -213,9 +213,12 @@ export const spec = { } if (pageKeywords) { - request.ext = { - keywords: pageKeywords - }; + pageKeywords = reformatKeywords(pageKeywords); + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } } if (gdprConsent && gdprConsent.gdprApplies) { @@ -419,6 +422,51 @@ function createBannerRequest(bid, mediaType) { return result; } +function reformatKeywords(pageKeywords) { + const formatedPageKeywords = {}; + Object.keys(pageKeywords).forEach((name) => { + const keywords = pageKeywords[name]; + if (keywords) { + if (name === 'site' || name === 'user') { + const formatedKeywords = {}; + Object.keys(keywords).forEach((pubName) => { + if (Array.isArray(keywords[pubName])) { + const formatedPublisher = []; + keywords[pubName].forEach((pubItem) => { + if (typeof pubItem === 'object' && pubItem.name) { + const formatedPubItem = { name: pubItem.name, segments: [] }; + Object.keys(pubItem).forEach((key) => { + if (Array.isArray(pubItem[key])) { + pubItem[key].forEach((keyword) => { + if (keyword) { + if (typeof keyword === 'string') { + formatedPubItem.segments.push({ name: key, value: keyword }); + } else if (key === 'segments' && typeof keyword.name === 'string' && typeof keyword.value === 'string') { + formatedPubItem.segments.push(keyword); + } + } + }); + } + }); + if (formatedPubItem.segments.length) { + formatedPublisher.push(formatedPubItem); + } + } + }); + if (formatedPublisher.length) { + formatedKeywords[pubName] = formatedPublisher; + } + } + }); + formatedPageKeywords[name] = formatedKeywords; + } else { + formatedPageKeywords[name] = keywords; + } + } + }); + return Object.keys(formatedPageKeywords).length && formatedPageKeywords; +} + function outstreamRender (bid) { bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 2e5d2e63677..1ec0200885c 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -412,6 +412,17 @@ describe('TheMediaGrid Adapter', function () { 'topic': ['stress', 'fear'] } ] + }, + 'user': { + 'formatedPublisher': [ + { + 'name': 'fomatedName', + 'segments': [ + { 'name': 'segName1', 'value': 'segVal1' }, + { 'name': 'segName2', 'value': 'segVal2' } + ] + } + ] } }; const bidRequestWithKW = { ...bidRequests[0], params: { ...bidRequests[0].params, keywords } } @@ -423,22 +434,39 @@ describe('TheMediaGrid Adapter', function () { 'somePublisher': [ { 'name': 'someName', - 'brandsafety': ['disaster'], - 'topic': ['stress', 'fear'] + 'segments': [ + { 'name': 'brandsafety', 'value': 'disaster' }, + { 'name': 'topic', 'value': 'stress' }, + { 'name': 'topic', 'value': 'fear' } + ] } ], 'ortb2': [ { 'name': 'keywords', - 'keywords': ['bar'] + 'segments': [ + { 'name': 'keywords', 'value': 'bar' } + ] } ] }, 'user': { + 'formatedPublisher': [ + { + 'name': 'fomatedName', + 'segments': [ + { 'name': 'segName1', 'value': 'segVal1' }, + { 'name': 'segName2', 'value': 'segVal2' } + ] + } + ], 'ortb2': [ { 'name': 'keywords', - 'keywords': ['foo', 'any'] + 'segments': [ + { 'name': 'keywords', 'value': 'foo' }, + { 'name': 'keywords', 'value': 'any' } + ] } ] } From dcd2789dd8a3fcf3f0ff035be841ad142252e5f9 Mon Sep 17 00:00:00 2001 From: cs83 Date: Mon, 12 Jul 2021 10:38:58 +0300 Subject: [PATCH 1258/1476] Smartico Bid Adapter: add adomain for Prebid 5.0 (#7115) * Adding smartico adapter * bug #6486 fix, added maintainer email * bug #6486 fix, modified test parameters * bug #6486 fix, modified test parameters #2 * #6486 applied review related updates & fixes * #6486 applied review related updates & fixes #2 * #6486 applied review related updates & fixes #3 * samrtico adapter bug fix * smartico adapter unit test update after bug fixing * smartico adapter bug fix #2 * smartico adapter bug fix #3 * fix linting errors Co-authored-by: Dmitri Co-authored-by: Chris Huie --- modules/smarticoBidAdapter.js | 117 +++++++++++++++++ test/spec/modules/smarticoBidAdapter_spec.js | 126 +++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 modules/smarticoBidAdapter.js create mode 100644 test/spec/modules/smarticoBidAdapter_spec.js diff --git a/modules/smarticoBidAdapter.js b/modules/smarticoBidAdapter.js new file mode 100644 index 00000000000..e923ed98e16 --- /dev/null +++ b/modules/smarticoBidAdapter.js @@ -0,0 +1,117 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; + +const SMARTICO_CONFIG = { + bidRequestUrl: 'https://trmads.eu/preBidRequest', + widgetUrl: 'https://trmads.eu/get', + method: 'POST' +} + +const BIDDER_CODE = 'smartico'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid && bid.params && bid.params.token && bid.params.placementId); + }, + buildRequests: function (validBidRequests, bidderRequest) { + var i + var j + var bid + var bidParam + var bidParams = [] + var sizes + var frameWidth = Math.round(window.screen.width) + var frameHeight = Math.round(window.screen.height) + for (i = 0; i < validBidRequests.length; i++) { + bid = validBidRequests[i] + if (bid.sizes) { + sizes = bid.sizes + } else if (typeof (BANNER) != 'undefined' && bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + sizes = bid.mediaTypes[BANNER].sizes + } else if (frameWidth && frameHeight) { + sizes = [[frameWidth, frameHeight]] + } else { + sizes = [] + } + for (j = 0; j < sizes.length; j++) { + bidParam = { + token: bid.params.token || '', + bidId: bid.bidId, + 'banner-format-width': sizes[j][0], + 'banner-format-height': sizes[j][1] + } + if (bid.params.bannerFormat) { + bidParam['banner-format'] = bid.params.bannerFormat + } + if (bid.params.language) { + bidParam.language = bid.params.language + } + if (bid.params.region) { + bidParam.region = bid.params.region + } + if (bid.params.regions && (bid.params.regions instanceof String || (bid.params.regions instanceof Array && bid.params.regions.length))) { + bidParam.regions = bid.params.regions + if (bidParam.regions instanceof Array) { + bidParam.regions = bidParam.regions.join(',') + } + } + bidParams.push(bidParam) + } + } + + var ServerRequestObjects = { + method: SMARTICO_CONFIG.method, + url: SMARTICO_CONFIG.bidRequestUrl, + bids: validBidRequests, + data: {bidParams: bidParams, auctionId: bidderRequest.auctionId} + } + return ServerRequestObjects; + }, + interpretResponse: function (serverResponse, bidRequest) { + var i + var bid + var bidObject + var url + var html + var ad + var ads + var token + var language + var scriptId + var bidResponses = [] + ads = serverResponse.body + for (i = 0; i < ads.length; i++) { + ad = ads[i] + bid = find(bidRequest.bids, bid => bid.bidId === ad.bidId) + if (bid) { + token = bid.params.token || '' + + language = bid.params.language || SMARTICO_CONFIG.language || '' + + scriptId = encodeURIComponent('smartico-widget-' + bid.params.placementId + '-' + i) + + url = SMARTICO_CONFIG.widgetUrl + '?token=' + encodeURIComponent(token) + '&auction-id=' + encodeURIComponent(bid.auctionId) + '&from-auction-buffer=1&own_session=1&ad=' + encodeURIComponent(ad.id) + '&scriptid=' + scriptId + (ad.bannerFormatAlias ? '&banner-format=' + encodeURIComponent(ad.bannerFormatAlias) : '') + (language ? '&language=' + encodeURIComponent(language) : '') + + html = ''; + ad += '' + return ad; +} function interpretResponse(serverResponse, bidRequest) { let bidResponses = []; if (serverResponse && serverResponse.body) { @@ -181,6 +198,10 @@ function interpretResponse(serverResponse, bidRequest) { try { let bidResponse = {}; if (bidRequest && bidRequest.data && bidRequest.data.bidId && bidRequest.data.bidId !== '') { + let mediaType = VIDEO; + if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && !bidRequest.bidRequest.mediaTypes[VIDEO]) { + mediaType = BANNER; + } let xmlStr = serverResponse.body; let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); if (xml && xml.getElementsByTagName('parsererror').length == 0) { @@ -195,18 +216,27 @@ function interpretResponse(serverResponse, bidRequest) { bidResponse.creativeId = xml.getElementsByTagName('Ad') && xml.getElementsByTagName('Ad')[0] && xml.getElementsByTagName('Ad')[0].getAttribute('id') ? xml.getElementsByTagName('Ad')[0].getAttribute('id') : 'creativeId'; bidResponse.currency = cpmData.currency; bidResponse.netRevenue = true; - var blob = new Blob([xmlStr], { - type: 'application/xml' - }); - bidResponse.vastUrl = window.URL.createObjectURL(blob); - bidResponse.vastXml = xmlStr; - bidResponse.mediaType = VIDEO; + bidResponse.mediaType = mediaType; + if (mediaType === VIDEO) { + try { + var blob = new Blob([xmlStr], { + type: 'application/xml' + }); + bidResponse.vastUrl = window.URL.createObjectURL(blob); + } catch (ex) { + utils.logError('Aniview Debug create vastXml error:\n\n' + ex); + } + bidResponse.vastXml = xmlStr; + if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { + bidResponse.renderer = newRenderer(bidRequest); + } + } else { + bidResponse.ad = buildBanner(xmlStr, bidRequest, bidResponse); + } bidResponse.meta = { advertiserDomains: [] }; - if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = newRenderer(bidRequest); } - bidResponses.push(bidResponse); } } else {} @@ -279,8 +309,8 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['avantisvideo', 'selectmediavideo'], - supportedMediaTypes: [VIDEO], + aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch'], + supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid, buildRequests, interpretResponse, diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js index b6d63fc2a8e..a9498af046c 100644 --- a/test/spec/modules/aniviewBidAdapter_spec.js +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -204,6 +204,29 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(bidResponse.renderer.loaded).to.equal(false) expect(bidResponse.width).to.equal(640) expect(bidResponse.height).to.equal(480) + }); + + it('Support banner format', function () { + const bidRequest = spec.buildRequests([ + { + bidId: '253dcb69fb2577', + params: { + playerDomain: 'example.com', + AV_PUBLISHERID: '55b78633181f4603178b4568', + AV_CHANNELID: '55b7904d181f46410f8b4568' + }, + mediaTypes: { + banner: { + sizes: [[640, 480]], + } + } + } + ])[0] + const bidResponse = spec.interpretResponse(serverResponse, bidRequest)[0] + + expect(bidResponse.ad).to.have.string('https://example.com/script/6.1/prebidRenderer.js'); + expect(bidResponse.width).to.equal(640) + expect(bidResponse.height).to.equal(480) }) }); From dc5f0283168253f37ce28e098dc6cc7e939bb4d7 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Mon, 12 Jul 2021 11:02:12 -0400 Subject: [PATCH 1260/1476] PBS adapter - fix logic for undefined endpoint URLs (#7076) * PBS adapter - fix logic for undefined endpoint URLs * update messages and add unit test * fix lint error * new tests and other changes Co-authored-by: Chris Huie --- modules/prebidServerBidAdapter/index.js | 14 ++++- .../modules/prebidServerBidAdapter_spec.js | 54 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8641c832121..e8bdeb557eb 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -133,6 +133,13 @@ function formatUrlParams(option) { let temp = option[prop]; option[prop] = { p1Consent: temp, noP1Consent: temp }; } + if (utils.isPlainObject(option[prop]) && (!option[prop].p1Consent || !option[prop].noP1Consent)) { + ['p1Consent', 'noP1Consent'].forEach((conUrl) => { + if (!option[prop][conUrl]) { + utils.logWarn(`s2sConfig.${prop}.${conUrl} not defined. PBS request will be skipped in some P1 scenarios.`); + } + }); + } }); } @@ -1097,9 +1104,10 @@ export function PrebidServer() { const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig, requestedBidders); const requestJson = request && JSON.stringify(request); utils.logInfo('BidRequest: ' + requestJson); - if (request && requestJson) { + const endpointUrl = getMatchingConsentUrl(s2sBidRequest.s2sConfig.endpoint, gdprConsent); + if (request && requestJson && endpointUrl) { ajax( - getMatchingConsentUrl(s2sBidRequest.s2sConfig.endpoint, gdprConsent), + endpointUrl, { success: response => handleResponse(response, requestedBidders, bidRequests, addBidResponse, done, s2sBidRequest.s2sConfig), error: done @@ -1107,6 +1115,8 @@ export function PrebidServer() { requestJson, { contentType: 'text/plain', withCredentials: true } ); + } else { + utils.logError('PBS request not made. Check endpoints.'); } } }; diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index a02e3b0586c..9ef895e3e1e 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -506,6 +506,60 @@ describe('S2S Adapter', function () { resetSyncedStatus(); }); + it('should block request if config did not define p1Consent URL in endpoint object config', function() { + let badConfig = utils.deepClone(CONFIG); + badConfig.endpoint = { noP1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; + config.setConfig({ s2sConfig: badConfig }); + + let badCfgRequest = utils.deepClone(REQUEST); + badCfgRequest.s2sConfig = badConfig; + + adapter.callBids(badCfgRequest, BID_REQUESTS, addBidResponse, done, ajax); + + expect(server.requests.length).to.equal(0); + }); + + it('should block request if config did not define noP1Consent URL in endpoint object config', function() { + let badConfig = utils.deepClone(CONFIG); + badConfig.endpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; + config.setConfig({ s2sConfig: badConfig }); + + let badCfgRequest = utils.deepClone(REQUEST); + badCfgRequest.s2sConfig = badConfig; + + let badBidderRequest = utils.deepClone(BID_REQUESTS); + badBidderRequest[0].gdprConsent = { + consentString: 'abc123', + addtlConsent: 'superduperconsent', + gdprApplies: true, + apiVersion: 2, + vendorData: { + purpose: { + consents: { + 1: false + } + } + } + }; + + adapter.callBids(badCfgRequest, badBidderRequest, addBidResponse, done, ajax); + + expect(server.requests.length).to.equal(0); + }); + + it('should block request if config did not define any URLs in endpoint object config', function() { + let badConfig = utils.deepClone(CONFIG); + badConfig.endpoint = {}; + config.setConfig({ s2sConfig: badConfig }); + + let badCfgRequest = utils.deepClone(REQUEST); + badCfgRequest.s2sConfig = badConfig; + + adapter.callBids(badCfgRequest, BID_REQUESTS, addBidResponse, done, ajax); + + expect(server.requests.length).to.equal(0); + }); + it('should not add outstrean without renderer', function () { config.setConfig({ s2sConfig: CONFIG }); From ab2f31954c4790d92d343e6ee702a454e773f78c Mon Sep 17 00:00:00 2001 From: Olivier Date: Mon, 12 Jul 2021 17:50:37 +0200 Subject: [PATCH 1261/1476] Adagio Bid Adapter: typo in native response handler (#7170) --- modules/adagioBidAdapter.js | 4 ++-- test/spec/modules/adagioBidAdapter_spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 27379f016af..e8893b89282 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -504,8 +504,8 @@ function _parseNativeBidResponse(bid) { if (bid.admNative.link.url) { native.clickUrl = bid.admNative.link.url; } - if (Array.isArray(bid.admNative.link.clickTrackers)) { - native.clickTrackers = bid.admNative.link.clickTrackers + if (Array.isArray(bid.admNative.link.clicktrackers)) { + native.clickTrackers = bid.admNative.link.clicktrackers } } diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index edbfafc3ec0..7c8e6ec2362 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -943,7 +943,7 @@ describe('Adagio bid adapter', () => { ver: '1.2', link: { url: 'https://i.am.a.click.url', - clickTrackers: [ + clicktrackers: [ 'https://i.am.a.clicktracker.url' ] }, From 932e20f0b7914f8d95cb0716c2b076d448c8b790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Mon, 12 Jul 2021 18:13:29 +0200 Subject: [PATCH 1262/1476] ParrableIdSystem: cache request filter status in first-party cookie (#7044) * Add tpcSupport, read it from xxhr and store it in compound cookie * Parse a cookie boolean number (0, 1) as a boolean value * Improve conditions * Add tests * Tests passing * Read the cookie splitting parrableId and params (tpc, tpcUntil) * Adapt tests * Revert linting in test task * Add request filter status handling * Improve date treatment * Replace Math.trunc with Math.floor * Run only parrable tests * Run all tests * Add tpcSupport, read it from xxhr and store it in compound cookie * Parse a cookie boolean number (0, 1) as a boolean value * Improve conditions * Add tests * Tests passing * Read the cookie splitting parrableId and params (tpc, tpcUntil) * Adapt tests * Revert linting in test task * Add request filter status handling * Improve date treatment * Replace Math.trunc with Math.floor * Run only parrable tests * Run all tests * Increase cookie TTL * Remove line break * Increase cookie expire time to 20s * Fix lint problems * Increase cookie time to 30s * Trigger CI run Co-authored-by: Ian Flournoy Co-authored-by: Victor Co-authored-by: victorigualada <21220224+victorigualada@users.noreply.github.com> Co-authored-by: Ian Flournoy --- modules/parrableIdSystem.js | 41 ++++++++++- test/spec/modules/parrableIdSystem_spec.js | 85 +++++++++++++++------- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 36ac7070ec1..1d74061e544 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -5,6 +5,8 @@ * @requires module:modules/userId */ +// ci trigger: 1 + import * as utils from '../src/utils.js' import find from 'core-js-pure/features/array/find.js'; import { ajax } from '../src/ajax.js'; @@ -34,7 +36,7 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); - if (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) { // unpack a value of 0 or 1 as boolean + if (pair[0] === 'ccpaOptout' || pair[0] === 'ibaOptout') { // unpack a value of 0 or 1 as boolean parrableId[pair[0]] = Boolean(+pair[1]); } else if (!isNaN(pair[1])) { // convert to number if is a number parrableId[pair[0]] = +pair[1] @@ -64,6 +66,10 @@ function serializeParrableId(parrableIdAndParams) { components.push(tpcSupportComponent); components.push(tpcUntil); } + if (parrableIdAndParams.filteredUntil) { + components.push(`filteredUntil:${parrableIdAndParams.filteredUntil}`); + components.push(`filterHits:${parrableIdAndParams.filterHits}`); + } return components.join(','); } @@ -96,12 +102,20 @@ function readCookie() { const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); if (parrableIdStr) { const parsedCookie = deserializeParrableId(decodeURIComponent(parrableIdStr)); - const { tpc, tpcUntil, ...parrableId } = parsedCookie; + const { tpc, tpcUntil, filteredUntil, filterHits, ...parrableId } = parsedCookie; let { eid, ibaOptout, ccpaOptout, ...params } = parsedCookie; if ((Date.now() / 1000) >= tpcUntil) { params.tpc = undefined; } + + if ((Date.now() / 1000) < filteredUntil) { + params.shouldFilter = true; + params.filteredUntil = filteredUntil; + } else { + params.shouldFilter = false; + params.filterHits = filterHits; + } return { parrableId, params }; } return null; @@ -197,6 +211,11 @@ function epochFromTtl(ttl) { return Math.floor((Date.now() / 1000) + ttl); } +function incrementFilterHits(parrableId, params) { + params.filterHits += 1; + writeCookie({ ...parrableId, ...params }) +} + function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; @@ -212,7 +231,8 @@ function fetchId(configParams, gdprConsentData) { const eid = parrableId ? parrableId.eid : null; const refererInfo = getRefererInfo(); - const tpcSupport = params ? params.tpc : null + const tpcSupport = params ? params.tpc : null; + const shouldFilter = params ? params.shouldFilter : null; const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; @@ -230,6 +250,10 @@ function fetchId(configParams, gdprConsentData) { tpcSupport }; + if (shouldFilter === false) { + data.filterHits = params.filterHits; + } + const searchParams = { data: encodeBase64UrlSafe(btoa(JSON.stringify(data))), gdpr: gdprApplies ? 1 : 0, @@ -271,6 +295,10 @@ function fetchId(configParams, gdprConsentData) { newParams.tpcSupport = responseObj.tpcSupport; newParams.tpcUntil = epochFromTtl(responseObj.tpcSupportTtl); } + if (responseObj.filterTtl) { + newParams.filteredUntil = epochFromTtl(responseObj.filterTtl); + newParams.filterHits = 0; + } } } catch (error) { utils.logError(error); @@ -288,7 +316,12 @@ function fetchId(configParams, gdprConsentData) { cb(); } }; - ajax(PARRABLE_URL, callbacks, searchParams, options); + + if (shouldFilter) { + incrementFilterHits(parrableId, params); + } else { + ajax(PARRABLE_URL, callbacks, searchParams, options); + } }; return { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 256be7340b7..44118fb50de 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -64,6 +64,10 @@ function serializeParrableId(parrableId) { str += `,${tpcSupportComponent}`; str += `,tpcUntil:${parrableId.tpcUntil}`; } + if (parrableId.filteredUntil) { + str += `,filteredUntil:${parrableId.filteredUntil}`; + str += `,filterHits:${parrableId.filterHits}`; + } return str; } @@ -249,7 +253,7 @@ describe('Parrable ID System', function() { }); }); - describe('third party cookie support status', function () { + describe('third party cookie support', function () { let logErrorStub; let callbackSpy = sinon.spy(); @@ -327,13 +331,30 @@ describe('Parrable ID System', function() { ); }); }); + }); + + describe('request-filter status', function () { + let logErrorStub; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + callbackSpy.resetHistory(); + removeParrableCookie(); + }); - describe('when getting tpcSupport from cookie', function () { + afterEach(function() { + logErrorStub.restore(); + }); + + describe('when getting filterTtl from XHR response', function () { let request; let dateNowStub; const dateNowMock = Date.now(); - const tpcSupportTtl = dateNowMock; - const tpcUntilExpired = 1; + const filterTtl = 1000; before(() => { dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); @@ -343,45 +364,57 @@ describe('Parrable ID System', function() { dateNowStub.restore(); }); - it('should send tpcSupport in the XHR', function () { - writeParrableCookie({ - eid: P_COOKIE_EID, - tpc: true, - tpcUntil: (dateNowMock / 1000) + 1 - }); + it('should set filteredUntil in the cookie', function () { let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); callback(callbackSpy); request = server.requests[0]; - let queryParams = utils.parseQS(request.url.split('?')[1]); - let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, filterTtl }) + ); - expect(data.tpcSupport).to.equal(true); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent( + 'eid:' + P_XHR_EID + + ',filteredUntil:' + Math.floor((dateNowMock / 1000) + filterTtl) + + ',filterHits:0') + ); }); - it('should unset tpcSupport from cookie when tpcUntil reached', function () { + it('should increment filterHits in the cookie', function () { writeParrableCookie({ - eid: P_COOKIE_EID, - tpcSupport: true, - tpcUntil: tpcUntilExpired + eid: P_XHR_EID, + filteredUntil: Math.floor((dateNowMock / 1000) + filterTtl), + filterHits: 0 }); let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); callback(callbackSpy); - request = server.requests[0]; - request.respond( - 200, - RESPONSE_HEADERS, - JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent( + 'eid:' + P_XHR_EID + + ',filteredUntil:' + Math.floor((dateNowMock / 1000) + filterTtl) + + ',filterHits:1') ); + }); + + it('should send filterHits in the XHR', function () { + const filterHits = 1; + writeParrableCookie({ + eid: P_XHR_EID, + filteredUntil: Math.floor(dateNowMock / 1000), + filterHits + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; let queryParams = utils.parseQS(request.url.split('?')[1]); let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); - expect(data.tpcSupport).to.equal(undefined); - expect(storage.getCookie(P_COOKIE_NAME)).to.equal( - encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) - ); + expect(data.filterHits).to.equal(filterHits); }); }); }); From 2281aad432575384610b0a5c2f5039a4857bf8ef Mon Sep 17 00:00:00 2001 From: Devastator Date: Tue, 13 Jul 2021 01:45:41 +0800 Subject: [PATCH 1263/1476] Innity Bid Adapter: support adomains for Prebid 5.0 (#7168) * Innity Adapter for Prebid.js 1.0 * Fix Innity Adapter Test Spec Error (#2180) * Fix utils.timestamp and remove userSyncs (#2180) * Added Prebid.js 3.0 Support * Innity Bid Adapter: Add meta adomain support --- modules/innityBidAdapter.js | 58 ++++++++++ test/spec/modules/innityBidAdapter_spec.js | 124 +++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 modules/innityBidAdapter.js create mode 100644 test/spec/modules/innityBidAdapter_spec.js diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js new file mode 100644 index 00000000000..ab7ee07db4a --- /dev/null +++ b/modules/innityBidAdapter.js @@ -0,0 +1,58 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'innity'; +const ENDPOINT = 'https://as.innity.com/synd/'; + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.pub && bid.params.zone); + }, + buildRequests: function(validBidRequests, bidderRequest) { + return validBidRequests.map(bidRequest => { + let parseSized = utils.parseSizesInput(bidRequest.sizes); + let arrSize = parseSized[0].split('x'); + return { + method: 'GET', + url: ENDPOINT, + data: { + cb: utils.timestamp(), + ver: 2, + hb: 1, + output: 'js', + pub: bidRequest.params.pub, + zone: bidRequest.params.zone, + url: bidderRequest && bidderRequest.refererInfo ? encodeURIComponent(bidderRequest.refererInfo.referer) : '', + width: arrSize[0], + height: arrSize[1], + vpw: window.screen.width, + vph: window.screen.height, + callback: 'json', + callback_uid: bidRequest.bidId, + auction: bidRequest.auctionId, + }, + }; + }); + }, + interpretResponse: function(serverResponse, request) { + const res = serverResponse.body; + const bidResponse = { + requestId: res.callback_uid, + cpm: parseFloat(res.cpm) / 100, + width: res.width, + height: res.height, + creativeId: res.creative_id, + currency: 'USD', + netRevenue: true, + ttl: 60, + ad: '' + res.tag, + meta: { + advertiserDomains: res.adomain && res.adomain.length ? res.adomain : [], + mediaType: res.mediaType, + } + }; + return [bidResponse]; + } +} +registerBidder(spec); diff --git a/test/spec/modules/innityBidAdapter_spec.js b/test/spec/modules/innityBidAdapter_spec.js new file mode 100644 index 00000000000..d4a28ec2100 --- /dev/null +++ b/test/spec/modules/innityBidAdapter_spec.js @@ -0,0 +1,124 @@ +import { expect } from 'chai'; +import { spec } from 'modules/innityBidAdapter.js'; + +describe('innityAdapterTest', () => { + describe('bidRequestValidity', () => { + it('bidRequest with pub ID and zone ID param', () => { + expect(spec.isBidRequestValid({ + bidder: 'innity', + params: { + 'pub': 267, + 'zone': 62546 + }, + })).to.equal(true); + }); + + it('bidRequest with no required params', () => { + expect(spec.isBidRequestValid({ + bidder: 'innity', + params: { + }, + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'innity', + 'params': { + 'pub': 267, + 'zone': 62546 + }, + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [300, 250], + 'bidId': '51ef8751f9aead', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + let bidderRequest = { + refererInfo: { + referer: 'https://refererExample.com' + } + }; + + it('bidRequest HTTP method', () => { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('GET'); + }); + }); + + it('bidRequest with complete data', () => { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.pub).to.equal(267); + expect(requests[0].data.zone).to.equal(62546); + expect(requests[0].data.width).to.equal('300'); + expect(requests[0].data.height).to.equal('250'); + expect(requests[0].data.url).to.equal(encodeURIComponent('https://refererExample.com')); + expect(requests[0].data.callback_uid).to.equal('51ef8751f9aead'); + }); + + it('bidRequest without referer URL', () => { + delete bidderRequest.refererInfo; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.url).to.equal(''); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = { + 'method': 'GET', + 'url': 'https://as.innity.com/synd/?', + 'data': { + 'ver': 2, + 'hb': 1, + 'output': 'js', + 'pub': 267, + 'zone': 62546, + 'width': '300', + 'height': '250', + 'callback': 'json', + 'callback_uid': '51ef8751f9aead', + 'url': 'https://refererExample.com', + 'cb': '', + } + }; + + let advDomains = ['advertiserExample.com']; + + let bidResponse = { + body: { + 'cpm': 100, + 'width': '300', + 'height': '250', + 'creative_id': '148186', + 'callback_uid': '51ef8751f9aead', + 'tag': '', + 'adomain': advDomains, + }, + headers: {} + }; + + it('result is correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0].requestId).to.equal('51ef8751f9aead'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal('300'); + expect(result[0].height).to.equal('250'); + expect(result[0].creativeId).to.equal('148186'); + expect(result[0].currency).to.equal('USD'); + expect(result[0].ttl).to.equal(60); + expect(result[0].ad).to.equal(''); + expect(result[0].meta.advertiserDomains).to.equal(advDomains); + }); + + it('result with no advertiser domain', () => { + bidResponse.body.adomain = []; + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0].meta.advertiserDomains.length).to.equal(0); + expect(result[0].meta.advertiserDomains).to.deep.equal([]); + }); + }); +}); From a633a637db703f90cb7dda9b160ea8b7fc401113 Mon Sep 17 00:00:00 2001 From: kouichi matsumoto Date: Tue, 13 Jul 2021 16:26:42 +0900 Subject: [PATCH 1264/1476] GMOSSP Support IntimateMerger (#7134) --- modules/gmosspBidAdapter.js | 4 ++++ test/spec/modules/gmosspBidAdapter_spec.js | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index bf57e63d53c..2b9bd6db034 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -2,9 +2,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'gmossp'; const ENDPOINT = 'https://sp.gmossp-sp.jp/hb/prebid/query.ad'; +const storage = getStorageManager(); export const spec = { code: BIDDER_CODE, @@ -32,6 +34,7 @@ export const spec = { const urlInfo = getUrlInfo(bidderRequest.refererInfo); const cur = getCurrencyType(); const dnt = utils.getDNT() ? '1' : '0'; + const imuid = storage.getCookie('_im_uid.1000283') || ''; for (let i = 0; i < validBidRequests.length; i++) { let queryString = ''; @@ -46,6 +49,7 @@ export const spec = { queryString = utils.tryAppendQueryString(queryString, 'bid', bid); queryString = utils.tryAppendQueryString(queryString, 'ver', ver); queryString = utils.tryAppendQueryString(queryString, 'sid', sid); + queryString = utils.tryAppendQueryString(queryString, 'im_uid', imuid); queryString = utils.tryAppendQueryString(queryString, 'url', urlInfo.url); queryString = utils.tryAppendQueryString(queryString, 'ref', urlInfo.ref); queryString = utils.tryAppendQueryString(queryString, 'cur', cur); diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index d29e27554c2..c22badb9b4c 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/gmosspBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import {getStorageManager} from 'src/storageManager'; import * as utils from 'src/utils.js'; const ENDPOINT = 'https://sp.gmossp-sp.jp/hb/prebid/query.ad'; @@ -35,6 +36,7 @@ describe('GmosspAdapter', function () { }); describe('buildRequests', function () { + const storage = getStorageManager(); const bidRequests = [ { bidder: 'gmossp', @@ -60,19 +62,21 @@ describe('GmosspAdapter', function () { referer: 'https://hoge.com' } }; + storage.setCookie('_im_uid.1000283', 'h.0a4749e7ffe09fa6'); const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.equal(ENDPOINT); expect(requests[0].method).to.equal('GET'); - expect(requests[0].data).to.equal('tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&url=https%3A%2F%2Fhoge.com&cur=JPY&dnt=0&'); + expect(requests[0].data).to.equal('tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&im_uid=h.0a4749e7ffe09fa6&url=https%3A%2F%2Fhoge.com&cur=JPY&dnt=0&'); }); - it('should use fallback if refererInfo.referer in bid request is empty', function () { + it('should use fallback if refererInfo.referer in bid request is empty and _im_uid.1000283 cookie is empty', function () { const bidderRequest = { refererInfo: { referer: '' } }; + storage.setCookie('_im_uid.1000283', ''); const requests = spec.buildRequests(bidRequests, bidderRequest); const result = 'tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&url=' + encodeURIComponent(window.top.location.href) + '&cur=JPY&dnt=0&'; From 3cfbf9de18d0c1d0043bb9fc5dae8a0de02f90fd Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 13 Jul 2021 13:09:57 +0530 Subject: [PATCH 1265/1476] PubMatic Analytics Adapter: added support for Bidder alias (#7153) * Changed net revenue to True * Added support for bidderAlias * Removed the code converting bidder to lowercase Co-authored-by: Azhar --- modules/pubmaticAnalyticsAdapter.js | 13 +- .../modules/pubmaticAnalyticsAdapter_spec.js | 127 ++++++++++++++++++ 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index c7eeaf87fdc..9aa40a2282d 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -82,7 +82,7 @@ function setMediaTypes(types, bid) { function copyRequiredBidDetails(bid) { return utils.pick(bid, [ - 'bidder', bidder => bidder.toLowerCase(), + 'bidder', 'bidId', 'status', () => NO_BID, // default a bid to NO_BID until response is recieved or bid is timed out 'finalSource as source', @@ -196,12 +196,17 @@ function getValueForKgpv(bid, adUnitId) { } } +function getAdapterNameForAlias(aliasName) { + return adapterManager.aliasRegistry[aliasName] || aliasName; +} + function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { let bid = adUnit.bids[bidId]; partnerBids.push({ - 'pn': bid.bidder, + 'pn': getAdapterNameForAlias(bid.bidder), + 'bc': bid.bidder, 'bidid': bid.bidId, 'db': bid.bidResponse ? 0 : 1, 'kgpv': getValueForKgpv(bid, adUnitId), @@ -287,6 +292,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { function executeBidWonLoggerCall(auctionId, adUnitId) { const winningBidId = cache.auctions[auctionId].adUnitCodes[adUnitId].bidWon; const winningBid = cache.auctions[auctionId].adUnitCodes[adUnitId].bids[winningBidId]; + const adapterName = getAdapterNameForAlias(winningBid.bidder); let pixelURL = END_POINT_WIN_BID_LOGGER; pixelURL += 'pubid=' + publisherId; pixelURL += '&purl=' + enc(config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''); @@ -296,7 +302,8 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += '&pid=' + enc(profileId); pixelURL += '&pdvid=' + enc(profileVersionId); pixelURL += '&slot=' + enc(adUnitId); - pixelURL += '&pn=' + enc(winningBid.bidder); + pixelURL += '&pn=' + enc(adapterName); + pixelURL += '&bc=' + enc(winningBid.bidder); pixelURL += '&en=' + enc(winningBid.bidResponse.bidPriceUSD); pixelURL += '&eg=' + enc(winningBid.bidResponse.bidGrossCpmUSD); pixelURL += '&kgpv=' + enc(getValueForKgpv(winningBid, adUnitId)); diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index 11de7b13208..92e4c3c16c7 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -1,4 +1,5 @@ import pubmaticAnalyticsAdapter from 'modules/pubmaticAnalyticsAdapter.js'; +import adapterManager from 'src/adapterManager.js'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config.js'; import { @@ -342,6 +343,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); expect(data.s[0].ps[0].piid).to.equal('partnerImpressionID-1'); expect(data.s[0].ps[0].db).to.equal(0); @@ -366,6 +368,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].piid).to.equal('partnerImpressionID-2'); expect(data.s[1].ps[0].db).to.equal(0); @@ -401,6 +404,7 @@ describe('pubmatic analytics adapter', function () { expect(decodeURIComponent(data.slot)).to.equal('/19968336/header-bid-tag-0'); expect(decodeURIComponent(data.kgpv)).to.equal('/19968336/header-bid-tag-0'); expect(data.pn).to.equal('pubmatic'); + expect(data.bc).to.equal('pubmatic'); expect(data.eg).to.equal('1.23'); expect(data.en).to.equal('1.23'); expect(data.piid).to.equal('partnerImpressionID-1'); @@ -441,6 +445,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); expect(data.s[0].ps[0].kgpv).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps[0].eg).to.equal(1.23); @@ -508,6 +513,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); expect(data.s[0].ps[0].kgpv).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps[0].eg).to.equal(1); @@ -551,6 +557,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(1); expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); @@ -587,6 +594,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(1); expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); @@ -629,6 +637,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); @@ -685,6 +694,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); @@ -730,6 +740,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('*'); @@ -783,6 +794,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('*'); @@ -833,6 +845,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('*'); @@ -885,6 +898,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); expect(data.s[1].ps[0].db).to.equal(0); expect(data.s[1].ps[0].kgpv).to.equal('*'); @@ -910,5 +924,118 @@ describe('pubmatic analytics adapter', function () { firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); expect(data.kgpv).to.equal('*'); }); + + it('Logger: best case + win tracker in case of Bidder Aliases', function() { + MOCK.BID_REQUESTED['bids'][0]['bidder'] = 'pubmatic_alias'; + adapterManager.aliasRegistry['pubmatic_alias'] = 'pubmatic'; + + sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { + return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]] + }); + + config.setConfig({ + testGroupId: 15 + }); + + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + clock.tick(2000 + 1000); + expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker + let request = requests[2]; // logger is executed late, trackers execute first + expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); + let data = getLoggerJsonFromRequest(request.requestBody); + expect(data.pubid).to.equal('9999'); + expect(data.pid).to.equal('1111'); + expect(data.pdvid).to.equal('20'); + expect(data.iid).to.equal('25c6d7f5-699a-4bfc-87c9-996f915341fa'); + expect(data.to).to.equal('3000'); + expect(data.purl).to.equal('http://www.test.com/page.html'); + expect(data.orig).to.equal('www.test.com'); + expect(data.tst).to.equal(1519767016); + expect(data.tgid).to.equal(15); + 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].sz).to.deep.equal(['640x480']); + expect(data.s[0].ps).to.be.an('array'); + expect(data.s[0].ps.length).to.equal(1); + expect(data.s[0].ps[0].pn).to.equal('pubmatic'); + expect(data.s[0].ps[0].bc).to.equal('pubmatic_alias'); + expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); + expect(data.s[0].ps[0].piid).to.equal('partnerImpressionID-1'); + expect(data.s[0].ps[0].db).to.equal(0); + expect(data.s[0].ps[0].kgpv).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].ps[0].kgpsv).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].ps[0].psz).to.equal('640x480'); + expect(data.s[0].ps[0].eg).to.equal(1.23); + expect(data.s[0].ps[0].en).to.equal(1.23); + expect(data.s[0].ps[0].di).to.equal(''); + expect(data.s[0].ps[0].dc).to.equal(''); + expect(data.s[0].ps[0].l1).to.equal(3214); + expect(data.s[0].ps[0].l2).to.equal(0); + expect(data.s[0].ps[0].ss).to.equal(0); + expect(data.s[0].ps[0].t).to.equal(0); + expect(data.s[0].ps[0].wb).to.equal(1); + 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'); + + // slot 2 + expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); + 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); + expect(data.s[1].ps[0].pn).to.equal('pubmatic'); + expect(data.s[1].ps[0].bc).to.equal('pubmatic'); + expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); + expect(data.s[1].ps[0].piid).to.equal('partnerImpressionID-2'); + expect(data.s[1].ps[0].db).to.equal(0); + expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); + expect(data.s[1].ps[0].psz).to.equal('728x90'); + expect(data.s[1].ps[0].eg).to.equal(1.52); + expect(data.s[1].ps[0].en).to.equal(1.52); + expect(data.s[1].ps[0].di).to.equal('the-deal-id'); + expect(data.s[1].ps[0].dc).to.equal('PMP'); + expect(data.s[1].ps[0].mi).to.equal('matched-impression'); + expect(data.s[1].ps[0].l1).to.equal(3214); + expect(data.s[1].ps[0].l2).to.equal(0); + expect(data.s[1].ps[0].ss).to.equal(1); + expect(data.s[1].ps[0].t).to.equal(0); + expect(data.s[1].ps[0].wb).to.equal(1); + 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'); + + // tracker slot1 + let firstTracker = requests[0].url; + expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); + data = {}; + firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); + expect(data.pubid).to.equal('9999'); + expect(decodeURIComponent(data.purl)).to.equal('http://www.test.com/page.html'); + expect(data.tst).to.equal('1519767014'); + expect(data.iid).to.equal('25c6d7f5-699a-4bfc-87c9-996f915341fa'); + expect(data.bidid).to.equal('2ecff0db240757'); + expect(data.pid).to.equal('1111'); + expect(data.pdvid).to.equal('20'); + expect(decodeURIComponent(data.slot)).to.equal('/19968336/header-bid-tag-0'); + expect(decodeURIComponent(data.kgpv)).to.equal('/19968336/header-bid-tag-0'); + expect(data.pn).to.equal('pubmatic'); + expect(data.bc).to.equal('pubmatic_alias'); + expect(data.eg).to.equal('1.23'); + expect(data.en).to.equal('1.23'); + expect(data.piid).to.equal('partnerImpressionID-1'); + }); }); }); From 2852ab2d31994671c1260b2797b690e0fb110b98 Mon Sep 17 00:00:00 2001 From: Marco Cosentino <807030+cosenmarco@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:27:27 +0200 Subject: [PATCH 1266/1476] ID5 Analytics: redaction process skips 'ext' on ID5 ID (#7141) * #26 id5analytics redactin process skips 'ext' on ID5 ID * #26 stop using startsWith because IE11 doesn't like it Co-authored-by: Marco Cosentino --- modules/id5AnalyticsAdapter.js | 13 ++++++++++--- test/spec/modules/id5AnalyticsAdapter_spec.js | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index abf23d2925d..d2803aa3102 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -243,10 +243,14 @@ function deepTransformingClone(obj, transform, currentPath = []) { // takes (obj, prop) and transforms property "prop" in object "obj". // The "match" is an array of path parts. Each part is either a string or an array. // In case of array, it represents alternatives which all would match. -// Special path part '*' matches any subproperty +// Special path part '*' matches any subproperty or array index. +// Prefixing a part with "!" makes it negative match (doesn't work with multiple alternatives) const CLEANUP_RULES = {}; CLEANUP_RULES[AUCTION_END] = [{ - match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', ['userId', 'crumbs'], '*'], + match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', ['userId', 'crumbs'], '!id5id'], + apply: 'redact' +}, { + match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', ['userId', 'crumbs'], 'id5id', 'uid'], apply: 'redact' }, { match: [['adUnits', 'bidderRequests'], '*', 'bids', '*', 'userIdAsEids', '*', 'uids', '*', ['id', 'ext']], @@ -288,7 +292,10 @@ function transformFnFromCleanupRules(eventType) { } for (let fragment = 0; fragment < ruleMatcher.length && match; fragment++) { const choices = makeSureArray(ruleMatcher[fragment]); - match = !choices.every((choice) => choice !== '*' && path[fragment] !== choice); + match = !choices.every((choice) => choice !== '*' && + (choice.charAt(0) === '!' + ? path[fragment] === choice.substring(1) + : path[fragment] !== choice)); } if (match) { const transformfn = TRANSFORM_FUNCTIONS[transformation]; diff --git a/test/spec/modules/id5AnalyticsAdapter_spec.js b/test/spec/modules/id5AnalyticsAdapter_spec.js index 4a1e708ed7d..be5998967c9 100644 --- a/test/spec/modules/id5AnalyticsAdapter_spec.js +++ b/test/spec/modules/id5AnalyticsAdapter_spec.js @@ -322,12 +322,22 @@ describe('ID5 analytics adapter', () => { expect(body.event).to.equal('auctionEnd'); expect(body.payload.adUnits[0].bids[0].userId).to.eql({ 'criteoId': '__ID5_REDACTED__', - 'id5id': '__ID5_REDACTED__', + 'id5id': { + 'uid': '__ID5_REDACTED__', + 'ext': { + 'linkType': 1 + } + }, 'tdid': '__ID5_REDACTED__' }); expect(body.payload.bidderRequests[0].bids[0].userId).to.eql({ 'sharedid': '__ID5_REDACTED__', - 'id5id': '__ID5_REDACTED__', + 'id5id': { + 'uid': '__ID5_REDACTED__', + 'ext': { + 'linkType': 1 + } + }, 'tdid': '__ID5_REDACTED__' }); body.payload.adUnits[0].bids[0].userIdAsEids.forEach((userId) => { From 7e9e832e6987a21dc60b5aedfab833f3b53b556b Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Tue, 13 Jul 2021 23:42:53 +1200 Subject: [PATCH 1267/1476] Make sharedIdSystem return pubcid instead of sharedId (#7099) --- modules/sharedIdSystem.js | 6 +-- test/spec/modules/sharedIdSystem_spec.js | 60 ++++++++++++++++++++---- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 5bb20492758..35848f7bfbc 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -12,7 +12,7 @@ import { uspDataHandler, coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; const GVLID = 887; -const storage = getStorageManager(GVLID, 'pubCommonId'); +export const storage = getStorageManager(GVLID, 'pubCommonId'); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const SHAREDID_OPT_OUT_VALUE = '00000000000000000000000000'; @@ -33,7 +33,7 @@ function storeData(config, value) { try { if (value) { const key = config.storage.name + SHAREDID_SUFFIX; - const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); + const expiresStr = (new Date(Date.now() + (config.storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); if (config.storage.type === COOKIE) { if (storage.cookiesAreEnabled()) { @@ -123,7 +123,7 @@ function handleResponse(pubcid, callback, config) { } } // Pass pubcid even though there is no change in order to trigger decode - callback(responseObj.sharedId); + callback(pubcid); } catch (error) { utils.logError(error); } diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js index d8056fcc616..4c3de9cfec0 100644 --- a/test/spec/modules/sharedIdSystem_spec.js +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -1,29 +1,62 @@ import { sharedIdSystemSubmodule, + storage } from 'modules/sharedIdSystem.js'; import { server } from 'test/mocks/xhr.js'; import {uspDataHandler} from 'src/adapterManager'; import sinon from 'sinon'; +import * as utils from 'src/utils.js'; let expect = require('chai').expect; describe('SharedId System', function() { - const SHAREDID_RESPONSE = {sharedId: 'testsharedid'}; - let uspConsentDataStub; + const UUID = '15fde1dc-1861-4894-afdf-b757272f3568'; + const START_TIME_MILLIS = 1234; + + before(function() { + sinon.stub(utils, 'generateUUID').returns(UUID); + }); + + after(function() { + utils.generateUUID.restore(); + }); + describe('Xhr Requests from getId()', function() { - let callbackSpy = sinon.spy(); + const SHAREDID_RESPONSE = {sharedId: 'testsharedid'}; + const callbackSpy = sinon.spy(); + + let uspConsentDataStub + let setCookeStub; + let sandbox; beforeEach(function() { + sandbox = sinon.sandbox.create(); + + uspConsentDataStub = sandbox.stub(uspDataHandler, 'getConsentData'); + setCookeStub = sandbox.stub(storage, 'setCookie'); + + sandbox.stub(storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(utils, 'hasDeviceAccess').returns(true); + + sandbox.useFakeTimers(START_TIME_MILLIS); + callbackSpy.resetHistory(); - uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); }); afterEach(function () { - uspConsentDataStub.restore(); + sandbox.restore(); }); it('should call shared id endpoint without consent data and handle a valid response', function () { - let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, undefined).callback; + let config = { + storage: { + type: 'cookie', + name: '_pubcid', + expires: 10 + } + }; + + let submoduleCallback = sharedIdSystemSubmodule.getId(config, undefined).callback; submoduleCallback(callbackSpy); let request = server.requests[0]; @@ -33,7 +66,16 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(UUID); + + expect(setCookeStub.calledThrice).to.be.true; + + let testCookieName = `_gd${START_TIME_MILLIS}`; + expect(setCookeStub.firstCall.args).to.eql([testCookieName, '1', undefined, undefined, 'localhost']); + expect(setCookeStub.secondCall.args).to.eql([testCookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, 'localhost']); + + let expires = new Date(START_TIME_MILLIS + (config.storage.expires * (60 * 60 * 24 * 1000))).toUTCString(); + expect(setCookeStub.lastCall.args).to.eql(['_pubcid_sharedid', 'testsharedid', expires, 'LAX', undefined]); }); it('should call shared id endpoint with consent data and handle a valid response', function () { @@ -52,7 +94,7 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(UUID); }); it('should call shared id endpoint with usp consent data and handle a valid response', function () { @@ -72,7 +114,7 @@ describe('SharedId System', function() { request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(SHAREDID_RESPONSE.sharedId); + expect(callbackSpy.lastCall.lastArg).to.equal(UUID); }); }); }); From cc10b155aa92c339bbc070c11aea1a24f4ab31ec Mon Sep 17 00:00:00 2001 From: Michael Callari Date: Tue, 13 Jul 2021 08:55:29 -0400 Subject: [PATCH 1268/1476] Adding score file check on auction init. (#7172) --- modules/optimeraRtdProvider.js | 38 +++++++++++++++++-- test/spec/modules/optimeraRtdProvider_spec.js | 5 +++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js index 2ed1bd78c42..1e439b28c94 100644 --- a/modules/optimeraRtdProvider.js +++ b/modules/optimeraRtdProvider.js @@ -64,6 +64,12 @@ export let device = 'default'; */ export let optimeraTargeting = {}; +/** + * Flag to indicateo if a new score file should be fetched. + * @type {string} + */ +export let fetchScoreFile = true; + /** * Make the request for the Score File. */ @@ -108,6 +114,17 @@ export function returnTargetingData(adUnits, config) { return targeting; } +/** + * Fetch a new score file when an auction starts. + * Only fetch the new file if a new score file is needed. + */ +export function onAuctionInit(auctionDetails, config, userConsent) { + setScoresURL(); + if (fetchScoreFile) { + scoreFileRequest(); + } +} + /** * Initialize the Module. */ @@ -134,14 +151,25 @@ export function init(moduleConfig) { /** * Set the score file url. - * This fully-formed URL for the data endpoint request to fetch + * + * This fully-formed URL is for the data endpoint request to fetch * the targeting values. This is not a js library, rather JSON * which has the targeting values for the page. + * + * The score file url is based on the web page url. If the new score file URL + * has been updated, set the fetchScoreFile flag to true to is can be fetched. + * */ export function setScoresURL() { const optimeraHost = window.location.host; const optimeraPathName = window.location.pathname; - scoresURL = `${scoresBaseURL}${clientID}/${optimeraHost}${optimeraPathName}.js`; + let newScoresURL = `${scoresBaseURL}${clientID}/${optimeraHost}${optimeraPathName}.js`; + if (scoresURL !== newScoresURL) { + scoresURL = newScoresURL; + fetchScoreFile = true; + } else { + fetchScoreFile = false; + } } /** @@ -169,10 +197,14 @@ export const optimeraSubmodule = { * @type {string} */ name: 'optimeraRTD', + /** + * get data when an auction starts + * @function + */ + onAuctionInitEvent: onAuctionInit, /** * get data and send back to realTimeData module * @function - * @param {string[]} adUnitsCodes */ getTargetingData: returnTargetingData, init, diff --git a/test/spec/modules/optimeraRtdProvider_spec.js b/test/spec/modules/optimeraRtdProvider_spec.js index f653389310b..8b1866d044a 100644 --- a/test/spec/modules/optimeraRtdProvider_spec.js +++ b/test/spec/modules/optimeraRtdProvider_spec.js @@ -81,4 +81,9 @@ describe('Optimera RTD error logging', () => { optimeraRTD.init(conf.dataProviders[0]); expect(utils.logError.called).to.equal(true); }); + + it('if adUnits is not an array should log an error', () => { + optimeraRTD.returnTargetingData('test'); + expect(utils.logError.called).to.equal(true); + }); }); From f81220c116bc2a18d8819a855acdd4e143f3dac1 Mon Sep 17 00:00:00 2001 From: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Date: Tue, 13 Jul 2021 06:44:29 -0700 Subject: [PATCH 1269/1476] Openx Analytics Adapter: optimize config, endpoint override, & send late impression (#7158) * openxAnalyticsAdapter: add endpoint override * openxAnalyticsAdapter: remove unused config * openxAnalyticsAdapter: record configId/optimizerConfig * openxAnalyticsAdapter: Send impressions separately if late * openxAnalyticsAdapter: add auctionId in auction payload Co-authored-by: Luigi Sayson --- modules/openxAnalyticsAdapter.js | 46 +++++++++++++------ .../modules/openxAnalyticsAdapter_spec.js | 15 +++++- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 07966e9e401..0c01d4b461e 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -46,9 +46,9 @@ const UTM_TO_CAMPAIGN_PROPERTIES = { * @property {string} orgId * @property {string} publisherPlatformId * @property {number} publisherAccountId + * @property {string} configId + * @property {string} optimizerConfig * @property {number} sampling - * @property {boolean} enableV2 - * @property {boolean} testPipeline * @property {Object} campaign * @property {number} payloadWaitTime * @property {number} payloadWaitTimePadding @@ -141,9 +141,9 @@ function isValidConfig({options: analyticsOptions}) { ['orgId', 'string', hasOrgId], ['publisherPlatformId', 'string', !hasOrgId], ['publisherAccountId', 'number', !hasOrgId], + ['configId', 'string', false], + ['optimizerConfig', 'string', false], ['sampling', 'number', false], - ['enableV2', 'boolean', false], - ['testPipeline', 'boolean', false], ['adIdKey', 'string', false], ['payloadWaitTime', 'number', false], ['payloadWaitTimePadding', 'number', false], @@ -271,7 +271,7 @@ function prebidAnalyticsEventHandler({eventType, args}) { * @property {Array} bidsReceived //: [] * @property {Array} winningBids //: [] * @property {number} timeout //: 3000 - * @property {Object} config //: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1, enableV2: true}/* + * @property {Object} config //: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1}/* */ function onAuctionInit({auctionId, timestamp: startTime, timeout, adUnitCodes}) { @@ -437,7 +437,18 @@ function onBidWon(bidResponse) { `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}.bids.${adId}`); if (winningBid) { - winningBid.winner = true + 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}`, + () => { + utils.logInfo(`Openx Analytics - Sending complete impression event for ${adId} at ${Date.now()}`) + }); + } else { + utils.logInfo(`Openx Analytics - impression event for ${adId} will be sent with auction data`) + } } } @@ -529,17 +540,20 @@ function getPageOffset() { } 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(ENDPOINT, deleteAuctionMap, payload, { contentType: 'application/json' }); - function deleteAuctionMap() { - delete auctionMap[auction.id]; - } + ajax(analyticsConfig.endpoint || ENDPOINT, () => { + utils.logInfo(`OpenX Analytics - Sending complete auction at ${Date.now()}`); + }, payload, { contentType: 'application/json' }); }, delayTime); } @@ -606,15 +620,19 @@ function getAuctionByGoogleTagSLot(slot) { } function buildAuctionPayload(auction) { - let {startTime, endTime, state, timeout, auctionOrder, userIds, adUnitCodeToAdUnitMap} = auction; - let {orgId, publisherPlatformId, publisherAccountId, campaign} = analyticsConfig; + 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, @@ -624,7 +642,7 @@ function buildAuctionPayload(auction) { deviceType: detectMob() ? 'Mobile' : 'Desktop', deviceOSType: detectOS(), browser: detectBrowser(), - testCode: analyticsConfig.testCode, + testCode: testCode, // return an array of module name that have user data userIdProviders: buildUserIdProviders(userIds), adUnits: buildAdUnitsPayload(adUnitCodeToAdUnitMap), @@ -653,6 +671,7 @@ function buildAuctionPayload(auction) { timedOut, bidResponses: utils._map(bidRequest.bids, (bidderBidResponse) => { let { + adId, cpm, creativeId, ts, @@ -671,6 +690,7 @@ function buildAuctionPayload(auction) { } = bidderBidResponse; return { + bidId: adId, microCpm: cpm * 1000000, netRevenue, currency, diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index b946efe922d..d7d2d31669c 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -62,8 +62,9 @@ describe('openx analytics adapter', function() { orgId: 'test-org-id', publisherAccountId: 123, publisherPlatformId: 'test-platform-id', + configId: 'my_config', + optimizerConfig: 'my my optimizer', sample: 1.0, - enableV2: true, payloadWaitTime: SLOT_LOAD_WAIT_TIME, payloadWaitTimePadding: SLOT_LOAD_WAIT_TIME }; @@ -292,6 +293,18 @@ describe('openx analytics adapter', function() { 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 () { From d517ed8d4485cdf1df222fa05214ac49a4b34e43 Mon Sep 17 00:00:00 2001 From: Gena Date: Tue, 13 Jul 2021 17:45:48 +0300 Subject: [PATCH 1270/1476] AdtelligentIdSystem : add new ID submodule (#6948) * Adtelligent User Id module * Adtelligent User Id module * fix lint * add unit tests to user id * Add markdown file --- modules/adtelligentBidAdapter.js | 7 ++ modules/adtelligentIdSystem.js | 91 +++++++++++++++++++ modules/adtelligentIdSystem.md | 33 +++++++ modules/userId/eids.js | 5 + test/spec/modules/adtelligentIdSystem_spec.js | 30 ++++++ 5 files changed, 166 insertions(+) create mode 100644 modules/adtelligentIdSystem.js create mode 100644 modules/adtelligentIdSystem.md create mode 100644 test/spec/modules/adtelligentIdSystem_spec.js diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index d21931a6dcb..e1c6dc1ca35 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -179,6 +179,13 @@ function bidToTag(bidRequests, adapterRequest) { if (utils.deepAccess(bidRequests[0], 'userId')) { tag.UserIds = utils.deepAccess(bidRequests[0], 'userId'); } + if (utils.deepAccess(bidRequests[0], 'userIdAsEids')) { + tag.UserEids = utils.deepAccess(bidRequests[0], 'userIdAsEids'); + } + if (window.adtDmp && window.adtDmp.ready) { + tag.DMPId = window.adtDmp.getUID(); + } + // end publisher env const bids = [] diff --git a/modules/adtelligentIdSystem.js b/modules/adtelligentIdSystem.js new file mode 100644 index 00000000000..fb3b5f6fe2a --- /dev/null +++ b/modules/adtelligentIdSystem.js @@ -0,0 +1,91 @@ +/** + * This module adds Adtelligent DMP Tokens to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/adtelligentIdSystem + * @requires module:modules/userId + */ + +import * as ajax from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; + +const gvlid = 410; +const moduleName = 'adtelligent'; +const syncUrl = 'https://idrs.adtelligent.com/get'; + +function buildUrl(opts) { + const queryPairs = []; + for (let key in opts) { + queryPairs.push(`${key}=${encodeURIComponent(opts[key])}`); + } + return `${syncUrl}?${queryPairs.join('&')}`; +} + +function requestRemoteIdAsync(url, cb) { + ajax.ajaxBuilder()( + url, + { + success: response => { + const jsonResponse = JSON.parse(response); + const { u: dmpId } = jsonResponse; + cb(dmpId); + }, + error: () => { + cb(); + } + }, + null, + { + method: 'GET', + contentType: 'application/json', + withCredentials: true + } + ); +} + +/** @type {Submodule} */ +export const adtelligentIdModule = { + /** + * used to link submodule with config + * @type {string} + */ + name: moduleName, + gvlid: gvlid, + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{adtelligentId: string}} + */ + decode(uid) { + return { adtelligentId: uid }; + }, + /** + * get the Adtelligent Id from local storages and initiate a new user sync + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {IdResponse} + */ + getId(config, consentData) { + const gdpr = consentData && consentData.gdprApplies ? 1 : 0; + const gdprConsent = gdpr ? consentData.consentString : ''; + const url = buildUrl({ + gdpr, + gdprConsent + }); + + if (window.adtDmp && window.adtDmp.ready) { + return { id: window.adtDmp.getUID() } + } + + return { + callback: (cb) => { + requestRemoteIdAsync(url, (id) => { + cb(id); + }); + } + + } + } +}; + +submodule('userId', adtelligentIdModule); diff --git a/modules/adtelligentIdSystem.md b/modules/adtelligentIdSystem.md new file mode 100644 index 00000000000..291a5e70bde --- /dev/null +++ b/modules/adtelligentIdSystem.md @@ -0,0 +1,33 @@ +### Adtelligent Id Sytem + +The [Adtelligent](https://adtelligent.com) ID system is a uniq per-session user identifier for providing high quality DMP data for advertisers + +#### Adtelligent Id Sytem Configuration Example + +{% highlight javascript %} + pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'adtelligent' + }] + } + }); +{% endhighlight %} + +Example with a short storage for ~10 minutes and refresh in 5 minutes: + +{% highlight javascript %} + pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'adtelligent', + storage: { + type: "html5", + name: "adt_id", + expires:0.003, + refreshInSeconds: 60 * 5 + } + }] + } + }); +{% endhighlight %} diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 32f7c163f3c..e11eb3edb68 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -213,6 +213,11 @@ const USER_IDS_CONFIG = { source: 'admixer.net', atype: 3 }, + // Adtelligent Id + 'adtelligentId': { + source: 'adtelligent.com', + atype: 3 + }, amxId: { source: 'amxrtb.com', atype: 1, diff --git a/test/spec/modules/adtelligentIdSystem_spec.js b/test/spec/modules/adtelligentIdSystem_spec.js new file mode 100644 index 00000000000..f3c7262c67a --- /dev/null +++ b/test/spec/modules/adtelligentIdSystem_spec.js @@ -0,0 +1,30 @@ +import { adtelligentIdModule } from 'modules/adtelligentIdSystem' +import * as ajaxLib from 'src/ajax.js'; + +const adtUserIdRemoteResponse = { u: 'test1' }; +const adtUserIdLocalResponse = 'test2'; + +describe('AdtelligentId module', function () { + it('gets remote id', function () { + const ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return (url, cbObj) => { + cbObj.success(JSON.stringify(adtUserIdRemoteResponse)) + } + }); + const moduleIdCallbackResponse = adtelligentIdModule.getId(); + moduleIdCallbackResponse.callback((id) => { + expect(id).to.equal(adtUserIdRemoteResponse.u) + }) + ajaxBuilderStub.restore(); + }) + it('gets id from page context', function () { + window.adtDmp = { + ready: true, + getUID() { + return adtUserIdLocalResponse; + } + } + const moduleIdResponse = adtelligentIdModule.getId(); + assert.deepEqual(moduleIdResponse, { id: adtUserIdLocalResponse }); + }) +}) From 39dde9ad5d2f959a6c9af9568caa6f96389af661 Mon Sep 17 00:00:00 2001 From: Params10 <48449191+Params10@users.noreply.github.com> Date: Tue, 13 Jul 2021 22:22:36 +0530 Subject: [PATCH 1271/1476] Iqm Bid Adapter: add new bid adapter (#7111) --- modules/iqmBidAdapter.js | 290 ++++++++++++++++++++++++ modules/iqmBidAdapter.md | 80 +++++-- test/spec/modules/iqmBidAdapter_spec.js | 227 +++++++++++++++++++ 3 files changed, 577 insertions(+), 20 deletions(-) create mode 100644 modules/iqmBidAdapter.js create mode 100644 test/spec/modules/iqmBidAdapter_spec.js diff --git a/modules/iqmBidAdapter.js b/modules/iqmBidAdapter.js new file mode 100644 index 00000000000..e7599fd54c0 --- /dev/null +++ b/modules/iqmBidAdapter.js @@ -0,0 +1,290 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import * as utils from '../src/utils.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {INSTREAM} from '../src/video.js'; + +const BIDDER_CODE = 'iqm'; +const VERSION = 'v.1.0.0'; +const VIDEO_ORTB_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay' +]; +var ENDPOINT_URL = 'https://pbd.bids.iqm.com'; + +export const spec = { + supportedMediaTypes: [BANNER, VIDEO], + code: BIDDER_CODE, + aliases: ['iqm'], + + /** + * 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) { + const banner = utils.deepAccess(bid, 'mediaTypes.banner'); + const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + if ((videoMediaType && context === INSTREAM)) { + const videoBidderParams = utils.deepAccess(bid, 'params.video', {}); + + if (!Array.isArray(videoMediaType.playerSize)) { + return false; + } + + if (!videoMediaType.context) { + return false; + } + + const videoParams = { + ...videoMediaType, + ...videoBidderParams + }; + + if (!Array.isArray(videoParams.mimes) || videoParams.mimes.length === 0) { + return false; + } + + if (!Array.isArray(videoParams.protocols) || videoParams.protocols.length === 0) { + return false; + } + + if ( + typeof videoParams.placement !== 'undefined' && + typeof videoParams.placement !== 'number' + ) { + return false; + } + if ( + videoMediaType.context === INSTREAM && + typeof videoParams.startdelay !== 'undefined' && + typeof videoParams.startdelay !== 'number' + ) { + return false; + } + + return !!(bid && bid.params && bid.params.publisherId && bid.params.placementId); + } else { + if (banner === 'undefined') { + return false; + } + return !!(bid && bid.params && bid.params.publisherId && bid.params.placementId); + } + }, + /** + * Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. + *It prepares a bid request with the required information for the DSP side and sends this request to alloted endpoint + * parameter{validBidRequests, bidderRequest} bidderRequest object is useful because it carries a couple of bid parameters that are global to all the bids. + */ + buildRequests: function (validBidRequests, bidderRequest) { + return validBidRequests.map(bid => { + var finalRequest = {}; + let bidfloor = utils.getBidIdParameter('bidfloor', bid.params); + + const imp = { + id: bid.bidId, + secure: 1, + bidfloor: bidfloor || 0, + displaymanager: 'Prebid.js', + displaymanagerver: VERSION, + + } + if (utils.deepAccess(bid, 'mediaTypes.banner')) { + imp.banner = getSize(bid.sizes); + imp.mediatype = 'banner'; + } else if (utils.deepAccess(bid, 'mediaTypes.video')) { + imp.video = _buildVideoORTB(bid); + imp.mediatype = 'video'; + } + const site = getSite(bid); + let device = getDevice(bid.params); + finalRequest = { + sizes: bid.sizes, + id: bid.bidId, + publisherId: utils.getBidIdParameter('publisherId', bid.params), + placementId: utils.getBidIdParameter('placementId', bid.params), + device: device, + site: site, + imp: imp, + auctionId: bid.auctionId, + adUnitCode: bid.adUnitCode, + bidderRequestId: bid.bidderRequestId, + uuid: bid.bidId, + bidderRequest + } + const request = { + method: 'POST', + url: ENDPOINT_URL, + data: finalRequest, + options: { + withCredentials: false + }, + + } + return request; + }); + }, + /** + * Takes Response from server as input and request. + *It parses the response from server side and generates bidresponses for with required rendering paramteres + * parameter{serverResponse, bidRequest} serverReponse: Response from the server side with ad creative. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + serverResponse = serverResponse.body; + if (serverResponse && utils.isArray(serverResponse.seatbid)) { + utils._each(serverResponse.seatbid, function (bidList) { + utils._each(bidList.bid, function (bid) { + const responseCPM = parseFloat(bid.price); + if (responseCPM > 0.0 && bid.impid) { + const bidResponse = { + requestId: bidRequest.data.id, + currency: serverResponse.cur || 'USD', + cpm: responseCPM, + netRevenue: true, + creativeId: bid.crid || '', + adUnitCode: bidRequest.data.adUnitCode, + auctionId: bidRequest.data.auctionId, + mediaType: bidRequest.data.imp.mediatype, + + ttl: bid.ttl || config.getConfig('_bidderTimeout') + }; + + if (bidRequest.data.imp.mediatype === VIDEO) { + bidResponse.width = bid.w || bidRequest.data.imp.video.w; + bidResponse.height = bid.h || bidRequest.data.imp.video.h; + bidResponse.adResponse = { + content: bid.adm, + height: bidRequest.data.imp.video.h, + width: bidRequest.data.imp.video.w + }; + + if (bidRequest.data.imp.video.context === INSTREAM) { + bidResponse.vastUrl = bid.adm; + } + } else if (bidRequest.data.imp.mediatype === BANNER) { + bidResponse.ad = bid.adm; + bidResponse.width = bid.w || bidRequest.data.imp.banner.w; + bidResponse.height = bid.h || bidRequest.data.imp.banner.h; + } + bidResponses.push(bidResponse); + } + }) + }); + } + return bidResponses; + }, + +}; + +let getDevice = function (bidparams) { + const language = navigator.language ? 'language' : 'userLanguage'; + return { + geo: bidparams.geo, + h: screen.height, + w: screen.width, + dnt: _getDNT() ? 1 : 0, + language: navigator[language].split('-')[0], + make: navigator.vendor ? navigator.vendor : '', + ua: navigator.userAgent, + devicetype: _isMobile() ? 1 : _isConnectedTV() ? 3 : 2 + }; +}; + +let _getDNT = function () { + return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1' || navigator.doNotTrack === 'yes'; +}; + +let getSize = function (sizes) { + let sizeMap; + if (sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') { + sizeMap = {w: sizes[0], h: sizes[1]}; + } else { + sizeMap = {w: sizes[0][0], h: sizes[0][1]}; + } + return sizeMap; +}; + +function _isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(global.navigator.userAgent); +} + +function _isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(global.navigator.userAgent); +} + +function getSite(bidderRequest) { + let domain = ''; + let page = ''; + let referrer = ''; + const Id = 1; + + const {refererInfo} = bidderRequest; + + if (canAccessTopWindow()) { + const wt = utils.getWindowTop(); + domain = wt.location.hostname; + page = wt.location.href; + referrer = wt.document.referrer || ''; + } else if (refererInfo.reachedTop) { + const url = utils.parseUrl(refererInfo.referer); + domain = url.hostname; + page = refererInfo.referer; + } else if (refererInfo.stack && refererInfo.stack.length && refererInfo.stack[0]) { + const url = utils.parseUrl(refererInfo.stack[0]); + domain = url.hostname; + } + + return { + domain, + page, + Id, + referrer + }; +}; + +function canAccessTopWindow() { + try { + if (utils.getWindowTop().location.href) { + return true; + } + } catch (error) { + return false; + } +} + +function _buildVideoORTB(bidRequest) { + const videoAdUnit = utils.deepAccess(bidRequest, 'mediaTypes.video'); + const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {}); + const video = {} + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams // Bidder Specific overrides + }; + video.context = 1; + const {w, h} = getSize(videoParams.playerSize[0]); + video.w = w; + video.h = h; + + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; + } + }); + + video.placement = video.placement || 2; + + video.startdelay = video.startdelay || 0; + video.placement = 1; + video.context = INSTREAM; + + return video; +} +registerBidder(spec); diff --git a/modules/iqmBidAdapter.md b/modules/iqmBidAdapter.md index d6d1b4d037d..85dfdc7e078 100644 --- a/modules/iqmBidAdapter.md +++ b/modules/iqmBidAdapter.md @@ -10,9 +10,8 @@ Maintainer: hbteam@iqm.com | Name | Scope | Description | Example | | :------------ | :------- | :------------------------ | :------------------- | -| `publisherId` | required | The Publisher ID from iQM | "df5fd732-c5f3-11e7" | -| `tagId` | required | The tag ID from iQM | "1c5c9ec2-c5f4-11e7" | -| `placementId` | required | The Placement ID from iQM | "50cc36fe-c5f4-11e7" | +| `publisherId` | required | The Publisher ID from iQM | "df5fd732-c5f3-11e7-abc4-cec278b6b50a" | +| `placementId` | required | The Placement ID from iQM | 23451 | | `bidfloor` | optional | Bid Floor | 0.50 | # Description @@ -21,21 +20,62 @@ Module that connects to iQM demand sources # Test Parameters ``` - var adUnits = [ - { - code: 'test-div1', - sizes: [[320, 50]], // display 320x50 - bids: [ - { - bidder: 'iqm', - params: { - publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', - tagId: '1c5c9ec2-c5f4-11e7-abc4-cec278b6b50a', - placementId: '50cc36fe-c5f4-11e7-abc4-cec278b6b50a', - bidfloor: 0.50, - } - } - ] - } - ]; +var adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300,250]] + } + }, + + bids: [{ + bidder: 'iqm', + params: { + geo:{ + country:'USA' + }, + + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + bidfloor: 0.50 + } + }] + + }] + +``` + +# adUnit Video + +``` + var videoAdUnit = { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [{ + bidder: 'iqm', + params: { + // placementId: iosDevice ? 13239390 : 13232361, // Add your own placement id here. Note, skippable video is not supported on iOS + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + geo:{ + country:'USA' + }, + + bidfloor: 0.05, + video: { + placement :2, + mimes: ['video/mp4'], + protocols: [2,5], + skipppable: true, + playback_method: ['auto_play_sound_off'] + } + } + }] + }; + ``` diff --git a/test/spec/modules/iqmBidAdapter_spec.js b/test/spec/modules/iqmBidAdapter_spec.js new file mode 100644 index 00000000000..27693937330 --- /dev/null +++ b/test/spec/modules/iqmBidAdapter_spec.js @@ -0,0 +1,227 @@ +import { expect } from 'chai'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import {spec} from 'modules/iqmBidAdapter'; + +const ENDPOINT = 'https://pbd.bids.iqm.com'; + +describe('iqmAdapter', 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: 'iqm', + params: { + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + bidfloor: 0.50 + }, + + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return false when no bid', function () { + expect(spec.isBidRequestValid()).to.equal(false); + }); + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return false when it is video and mimes and protcol are not present', function () { + const bid = { + adUnitCode: 'div-gpt-ad-1460505748561-0', + auctionId: 'a0aca162-e3d0-44db-a465-5c96a64fa5fb', + bidId: '2cbdc9b506be33', + bidRequestsCount: 1, + bidder: 'iqm', + bidderRequestId: '185c3a4c7f88ec', + bidderRequestsCount: 1, + bidderWinsCount: 0, + crumbs: {pubcid: 'f56a553d-370d-4cea-b31a-7214a3d8f8e1'}, + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [ + 640, + 480 + ] + ] + } + }, + params: { + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + geo: { + country: 'USA' + }, + + bidfloor: 0.50, + video: { + placement: 2, + mimes: null, + protocols: null, + skipppable: true, + playback_method: ['auto_play_sound_off'] + } + }, + src: 'client', + transactionId: 'a57d06fd-cc6d-4a90-87af-c10727998f0b' }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + it('should return false when required params are not found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + placementId: 0, + publisherId: null + + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let validBidRequests = [ + {bidder: 'iqm', + params: { + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + bidfloor: 0.5}, + crumbs: { + pubcid: 'a0f51f64-6d86-41d0-abaf-7ece71404d94'}, + fpd: {'context': {'pbAdSlot': '/19968336/header-bid-tag-0'}}, + mediaTypes: { + banner: { + sizes: [[300, 250]]}}, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: '56fe8d92-ff6e-4c34-90ad-2f743cd0eae8', + sizes: [[300, 250]], + bidId: '266d810da21904', + bidderRequestId: '13c05d264c7ffe', + auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0}]; + + let bidderRequest = {bidderCode: 'iqm', auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', bidderRequestId: '13c05d264c7ffe', bids: [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5}, crumbs: {pubcid: 'a0f51f64-6d86-41d0-abaf-7ece71404d94'}, fpd: {context: {pbAdSlot: '/19968336/header-bid-tag-0'}}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: '/19968336/header-bid-tag-0', transactionId: '56fe8d92-ff6e-4c34-90ad-2f743cd0eae8', sizes: [[300, 250]], bidId: '266d810da21904', bidderRequestId: '13c05d264c7ffe', auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], auctionStart: 1615205942159, timeout: 7000, refererInfo: {referer: 'http://test.localhost:9999/integrationExamples/gpt/hello_world.html', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://test.localhost:9999/integrationExamples/gpt/hello_world.html'], canonicalUrl: null}, start: 1615205942162}; + + it('should parse out sizes', function () { + let temp = []; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = request[0].data; + + expect(payload.sizes).to.exist; + expect(payload.sizes[0]).to.deep.equal([300, 250]); + }); + + it('should populate the ad_types array on all requests', function () { + // const bidRequest = Object.assign({}, bidRequests[0]); + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = request[0].data; + + expect(payload.imp.mediatype).to.deep.equal('banner'); + }); + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request[0].url).to.equal(ENDPOINT); + expect(request[0].method).to.equal('POST'); + }); + it('should attach valid video params to the tag', function () { + let validBidRequests_video = [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5, video: {placement: 2, mimes: ['video/mp4'], protocols: [2, 5], skipppable: true, playback_method: ['auto_play_sound_off']}}, crumbs: {pubcid: '09b8f065-9d1b-4a36-bd0c-ea22e2dad807'}, fpd: {context: {pbAdSlot: 'video1'}}, mediaTypes: {video: {playerSize: [[640, 480]], context: 'instream'}}, adUnitCode: 'video1', transactionId: '86795c66-acf9-4dd5-998f-6d5362aaa541', sizes: [[640, 480]], bidId: '28bfb7e2d12897', bidderRequestId: '16e1ce8481bc6d', auctionId: '3140a2ec-d567-4db0-9bbb-eb6fa20ccb71', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; + let bidderRequest_video = {bidderCode: 'iqm', auctionId: '3140a2ec-d567-4db0-9bbb-eb6fa20ccb71', bidderRequestId: '16e1ce8481bc6d', bids: [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5, video: {placement: 2, mimes: ['video/mp4'], protocols: [2, 5], skipppable: true, playback_method: ['auto_play_sound_off']}}, crumbs: {pubcid: '09b8f065-9d1b-4a36-bd0c-ea22e2dad807'}, fpd: {context: {pbAdSlot: 'video1'}}, mediaTypes: {video: {playerSize: [[640, 480]], context: 'instream'}}, adUnitCode: 'video1', transactionId: '86795c66-acf9-4dd5-998f-6d5362aaa541', sizes: [[640, 480]], bidId: '28bfb7e2d12897', bidderRequestId: '16e1ce8481bc6d', auctionId: '3140a2ec-d567-4db0-9bbb-eb6fa20ccb71', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], auctionStart: 1615271191985, timeout: 3000, refererInfo: {referer: 'http://test.localhost:9999/integrationExamples/gpt/pbjs_video_adUnit.html', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://test.localhost:9999/integrationExamples/gpt/pbjs_video_adUnit.html'], canonicalUrl: null}, start: 1615271191988}; + const request = spec.buildRequests(validBidRequests_video, bidderRequest_video); + const payload = request[0].data; + expect(payload.imp.id).to.exist; + expect(payload.imp.displaymanager).to.exist; + expect(payload.imp.displaymanagerver).to.exist; + + expect(payload.imp.video).to.deep.equal({ + context: 'instream', + w: 640, + h: 480, + mimes: ['video/mp4'], + placement: 1, + protocols: [2, 5], + startdelay: 0 + }); + }); + + it('should add referer info to payload', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = request[0].data; + + expect(payload.bidderRequest.refererInfo).to.exist; + expect(payload.bidderRequest.refererInfo).to.deep.equal({referer: 'http://test.localhost:9999/integrationExamples/gpt/hello_world.html', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://test.localhost:9999/integrationExamples/gpt/hello_world.html'], canonicalUrl: null}); + }); + }) + + describe('interpretResponse', function () { + let tempResult = {requestId: '2d9601dd8328f8', currency: 'USD', cpm: 4.5, netRevenue: true, creativeId: 'cr-121004', adUnitCode: 'div-gpt-ad-1460505748561-0', 'auctionId': '22a4f3d8-511f-46ba-91be-53b9949e4b48', mediaType: 'banner', ttl: 3000, ad: " ", width: 844, height: 617}; + let validBidRequests_temp = [ + {bidder: 'iqm', + params: { + publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', + placementId: 23451, + bidfloor: 0.5}, + crumbs: { + pubcid: 'a0f51f64-6d86-41d0-abaf-7ece71404d94'}, + fpd: {'context': {'pbAdSlot': '/19968336/header-bid-tag-0'}}, + mediaTypes: { + banner: { + sizes: [[300, 250]]}}, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: '56fe8d92-ff6e-4c34-90ad-2f743cd0eae8', + sizes: [[300, 250]], + bidId: '266d810da21904', + bidderRequestId: '13c05d264c7ffe', + auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0}]; + let bidderRequest = {bidderCode: 'iqm', auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', bidderRequestId: '13c05d264c7ffe', bids: [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5}, crumbs: {pubcid: 'a0f51f64-6d86-41d0-abaf-7ece71404d94'}, fpd: {context: {pbAdSlot: '/19968336/header-bid-tag-0'}}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: '/19968336/header-bid-tag-0', transactionId: '56fe8d92-ff6e-4c34-90ad-2f743cd0eae8', sizes: [[300, 250]], bidId: '266d810da21904', bidderRequestId: '13c05d264c7ffe', auctionId: '565ab569-ab95-40d6-8b42-b9707a92062f', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], auctionStart: 1615205942159, timeout: 7000, refererInfo: {referer: 'http://test.localhost:9999/integrationExamples/gpt/hello_world.html', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://test.localhost:9999/integrationExamples/gpt/hello_world.html'], canonicalUrl: null}, start: 1615205942162}; + let response = { + + id: '5bdbab92aae961cfbdf7465d', + seatbid: [{bid: [{id: 'bid-5bdbab92aae961cfbdf7465d-5bdbab92aae961cfbdf74653', impid: '5bdbab92aae961cfbdf74653', price: 9.9, nurl: 'https://winn.stage.iqm.com/smaato?raw=w9XViV4dovBHrxujHhBj-l-uWB08CUOMW_oR-EUxZbaWLL0ENzcMlP3CJFEURN6FgRp_HdjAjxTYHR7uG4S6h6dl_vjU_YNABiPd607-iTqxOCl-2cKLo-hhQus4sMw01VIqyqrPmzOTHTwJm4vTjUIoWMPZbARgQvUnBzjRH9xeYS-Bv3kgAW9NSBfgBZeLyT3WJJ_3VKIE_Iurt8OjpA%3D%3D&req_id=5bdbab92aae961cfbdf7465d&ap=${AUCTION_PRICE}', adm: " ", adomain: ['click.iqm.com'], iurl: 'https://d3jme5si7t6llb.cloudfront.net/image/1/404/owVo6mc_1588902031079.png', cid: '169218', crid: 'cr-301435', attr: [], h: 250, w: 250}]}], + bidid: '5bdbab92aae961cfbdf7465d' + }; + + it('should get correct bid response', function () { + let expectedResponse = [ + {requestId: '49ad5f21156efd', currency: 'USD', cpm: 9.9, netRevenue: true, creativeId: 'cr-301435', adUnitCode: '/19968336/header-bid-tag-0', auctionId: '853cddf1-8d13-4482-bd88-f5ef927d5ab3', mediaType: 'banner', ttl: 3000, ad: " ", width: 250, height: 250} + ]; + let temprequest = spec.buildRequests(validBidRequests_temp, bidderRequest); + + let result = spec.interpretResponse({ body: response }, temprequest[0]); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + let validBidRequests_temp_video = + [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5, video: {placement: 2, mimes: ['video/mp4'], protocols: [2, 5], skipppable: true, playback_method: ['auto_play_sound_off']}}, crumbs: {pubcid: 'cd86c3ff-d630-40e6-83ab-420e9e800594'}, fpd: {context: {pbAdSlot: 'video1'}}, mediaTypes: {video: {playerSize: [[640, 480]], context: 'instream'}}, adUnitCode: 'video1', transactionId: '8335b266-7a41-45f9-86a2-92fdc7cf0cd9', sizes: [[640, 480]], bidId: '26274beff25455', bidderRequestId: '17c5d8c3168761', auctionId: '2c592dcf-7dfc-4823-8203-dd1ebab77fe0', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; + let bidderRequest_video = {bidderCode: 'iqm', auctionId: '3140a2ec-d567-4db0-9bbb-eb6fa20ccb71', bidderRequestId: '16e1ce8481bc6d', bids: [{bidder: 'iqm', params: {publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a', placementId: 23451, bidfloor: 0.5, video: {placement: 2, mimes: ['video/mp4'], protocols: [2, 5], skipppable: true, playback_method: ['auto_play_sound_off']}}, crumbs: {pubcid: '09b8f065-9d1b-4a36-bd0c-ea22e2dad807'}, fpd: {context: {pbAdSlot: 'video1'}}, mediaTypes: {video: {playerSize: [[640, 480]], context: 'instream'}}, adUnitCode: 'video1', transactionId: '86795c66-acf9-4dd5-998f-6d5362aaa541', sizes: [[640, 480]], bidId: '28bfb7e2d12897', bidderRequestId: '16e1ce8481bc6d', auctionId: '3140a2ec-d567-4db0-9bbb-eb6fa20ccb71', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], auctionStart: 1615271191985, timeout: 3000, refererInfo: {referer: 'http://test.localhost:9999/integrationExamples/gpt/pbjs_video_adUnit.html', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://test.localhost:9999/integrationExamples/gpt/pbjs_video_adUnit.html'], canonicalUrl: null}, start: 1615271191988}; + + it('handles non-banner media responses', function () { + let response = {id: '2341234', seatbid: [{bid: [{id: 'bid-2341234-1', impid: '1', price: 9, nurl: 'https://frontend.stage.iqm.com/static/vast-01.xml', adm: 'http://cdn.iqm.com/pbd?raw=312730_203cf73dc83fb_2824348636878_pbd', adomain: ['app1.stage.iqm.com'], cid: '168900', crid: 'cr-304503', attr: []}]}], bidid: '2341234'}; + + let temprequest_video = spec.buildRequests(validBidRequests_temp_video, bidderRequest_video); + + let result = spec.interpretResponse({ body: response }, temprequest_video[0]); + expect(result[0]).to.have.property('vastUrl'); + }); + }); +}); From 79379b06cd7b6a937d40e590d6a4efc31427de84 Mon Sep 17 00:00:00 2001 From: atamak Date: Wed, 14 Jul 2021 03:24:59 +0900 Subject: [PATCH 1272/1476] Support Liveramp (#7165) --- modules/microadBidAdapter.js | 6 ++++++ test/spec/microadBidAdapter_spec.js | 30 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 1a2f6f28019..c78a66aecee 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -1,5 +1,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; const BIDDER_CODE = 'microad'; @@ -81,6 +82,11 @@ export const spec = { } } + const idlEnv = utils.deepAccess(bid, 'userId.idl_env') + if (!utils.isEmpty(idlEnv) && utils.isStr(idlEnv)) { + params['idl_env'] = idlEnv + } + requests.push({ method: 'GET', url: ENDPOINT_URLS[ENVIRONMENT], diff --git a/test/spec/microadBidAdapter_spec.js b/test/spec/microadBidAdapter_spec.js index 21e65462234..be310fb8e3c 100644 --- a/test/spec/microadBidAdapter_spec.js +++ b/test/spec/microadBidAdapter_spec.js @@ -265,6 +265,36 @@ describe('microadBidAdapter', () => { expect(request.url.lastIndexOf('https', 0) === 0).to.be.true; }); }); + + it('should add Liveramp identity link if it is available in request parameters', () => { + const bidRequestWithLiveramp = Object.assign({}, bidRequestTemplate, { + userId: {idl_env: 'idl-env-sample'} + }); + const requests = spec.buildRequests([bidRequestWithLiveramp], bidderRequest) + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + idl_env: 'idl-env-sample' + }) + ); + }) + }); + + 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 + }) + ); + }) + }); }); describe('interpretResponse', () => { From 188ff81d7a675171ae41a48ed515315896c27130 Mon Sep 17 00:00:00 2001 From: amishra11j <86069270+amishra11j@users.noreply.github.com> Date: Wed, 14 Jul 2021 00:23:55 +0530 Subject: [PATCH 1273/1476] Moving {domain} parameter from the JSON body to the URL path (#7176) --- modules/akamaiDAPIdSystem.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/akamaiDAPIdSystem.js b/modules/akamaiDAPIdSystem.js index c64502a8c5a..a78d65b40a9 100644 --- a/modules/akamaiDAPIdSystem.js +++ b/modules/akamaiDAPIdSystem.js @@ -83,10 +83,9 @@ export const akamaiDAPIdSubmodule = { tokenName = 'PubToken'; } } else { - url = `https://${configParams.apiHostname}/data-activation/x1/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; + url = `https://${configParams.apiHostname}/data-activation/x1/domain/${configParams.domain}/identity/tokenize?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}&us_privacy=${uspConsent}`; postData = { 'version': configParams.apiVersion, - 'domain': configParams.domain, 'identity': configParams.identity, 'type': configParams.type, 'attributes': configParams.attributes From 48f313349dd5a2408349576d7d9439ac62a2b65a Mon Sep 17 00:00:00 2001 From: aaroniniguez Date: Wed, 14 Jul 2021 00:34:38 -0700 Subject: [PATCH 1274/1476] UserID: bugfix for refreshUserIds with updated info (#7105) * BugFix UserID module refreshUserIds * break from for loop --- modules/userId/index.js | 20 ++++++++++++++++ test/spec/modules/userId_spec.js | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/modules/userId/index.js b/modules/userId/index.js index d4d138ea83a..a6a824fe89e 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -613,6 +613,11 @@ function refreshUserIds(options, callback) { utils.logInfo(`${MODULE_NAME} - refreshing ${submodule.submodule.name}`); populateSubmoduleId(submodule, consentData, storedConsentData, true); + updateInitializedSubmodules(submodule); + + if (initializedSubmodules.length) { + setPrebidServerEidPermissions(initializedSubmodules); + } if (utils.isFn(submodule.callback)) { callbackSubmodules.push(submodule); @@ -711,6 +716,21 @@ function initSubmodules(submodules, consentData) { }, []); } +function updateInitializedSubmodules(submodule) { + let updated = false; + for (let i = 0; i < initializedSubmodules.length; i++) { + if (submodule.config.name.toLowerCase() === initializedSubmodules[i].config.name.toLowerCase()) { + updated = true; + initializedSubmodules[i] = submodule; + break; + } + } + + if (!updated) { + initializedSubmodules.push(submodule); + } +} + /** * list of submodule configurations with valid 'storage' or 'value' obj definitions * * storage config: contains values for storing/retrieving User ID data in browser storage diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 6caf8b5e18e..9b36de6a94b 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -365,6 +365,46 @@ describe('User ID', function () { expect(mockIdCallback.callCount).to.equal(1); }); + it('pbjs.refreshUserIds updates submodules', function() { + let sandbox = sinon.createSandbox(); + let mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); + let mockIdSystem = { + name: 'mockId', + decode: function(value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: mockIdCallback + }; + setSubmoduleRegistry([mockIdSystem]); + init(config); + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'mockId', + value: {id: {mockId: '1111'}} + }] + } + }); + + expect(getGlobal().getUserIds().id.mockId).to.equal('1111'); + + // update to new config value + config.setConfig({ + userSync: { + syncDelay: 0, + userIds: [{ + name: 'mockId', + value: {id: {mockId: '1212'}} + }] + } + }); + getGlobal().refreshUserIds({ submoduleNames: ['mockId'] }); + expect(getGlobal().getUserIds().id.mockId).to.equal('1212'); + }); + it('pbjs.refreshUserIds refreshes single', function() { coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('REFRESH', '', EXPIRED_COOKIE_DATE); From f9707279da3ebc660e18d2dcef7c4b4919dcb3a6 Mon Sep 17 00:00:00 2001 From: Vadim Mazzherin Date: Wed, 14 Jul 2021 13:39:22 +0600 Subject: [PATCH 1275/1476] showheroes-bs Bid Adapter: Add support for advertiserDomains (#7169) * 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 Co-authored-by: veranevera Co-authored-by: Elizaveta Voziyanova <44549195+h2p4x8@users.noreply.github.com> --- modules/showheroes-bsBidAdapter.js | 3 +++ test/spec/modules/showheroes-bsBidAdapter_spec.js | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index d0eb8c6a589..8d94a8e508b 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -166,6 +166,9 @@ function createBids(bidRes, reqData) { bidUnit.netRevenue = true; bidUnit.width = bid.size.width; bidUnit.height = bid.size.height; + bidUnit.meta = { + advertiserDomains: bid.adomain || [] + }; if (bid.vastXml) { bidUnit.vastXml = bid.vastXml; bidUnit.adResponse = { diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 9525146a629..ad2210c18c6 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -9,6 +9,8 @@ const bidderRequest = { } } +const adomain = ['showheroes.com']; + const gdpr = { 'gdprConsent': { 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', @@ -301,6 +303,7 @@ describe('shBidAdapter', function () { 'size': {'width': 640, 'height': 480}, 'vastTag': 'https:\/\/video-library.stage.showheroes.com\/commercial\/wrapper?player_id=47427aa0-f11a-4d24-abca-1295a46a46cd&ad_bidder=showheroes-bs&master_shadt=1&description_url=https%3A%2F%2Fbid-service.stage.showheroes.com%2Fvast%2Fad%2Fcache%2F4840b920-40e1-4e09-9231-60bbf088c8d6', 'vastXml': vastXml, + 'adomain': adomain, }; const responseVideo = { @@ -340,6 +343,9 @@ describe('shBidAdapter', function () { 'ttl': 300, 'adResponse': { 'content': vastXml + }, + 'meta': { + 'advertiserDomains': adomain } } ] From 1a24e4d12b9483a5b15ebf8d0994e184568bc6b3 Mon Sep 17 00:00:00 2001 From: Sachin Shastri Date: Wed, 14 Jul 2021 06:08:07 -0700 Subject: [PATCH 1276/1476] QuantcastId : Add support for firing pixel in quantcastIdSystem submodule (#7107) --- modules/quantcastIdSystem.js | 182 +++++++++- modules/quantcastIdSystem.md | 46 +++ modules/userId/userId.md | 6 + test/spec/modules/quantcastIdSystem_spec.js | 372 +++++++++++++++++++- 4 files changed, 601 insertions(+), 5 deletions(-) create mode 100644 modules/quantcastIdSystem.md diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index e86c130dc5b..7d82be884da 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -7,11 +7,159 @@ import {submodule} from '../src/hook.js' import { getStorageManager } from '../src/storageManager.js'; +import { triggerPixel, logInfo } from '../src/utils.js'; +import { uspDataHandler, coppaDataHandler, gdprDataHandler } from '../src/adapterManager.js'; const QUANTCAST_FPA = '__qca'; +const DEFAULT_COOKIE_EXP_DAYS = 392; // (13 months - 2 days) +const DAY_MS = 86400000; +const PREBID_PCODE = 'p-KceJUEvXN48CE'; +const QSERVE_URL = 'https://pixel.quantserve.com/pixel'; +const QUANTCAST_VENDOR_ID = '11'; +const PURPOSE_DATA_COLLECT = '1'; +const PURPOSE_PRODUCT_IMPROVEMENT = '10'; +const QC_TCF_REQUIRED_PURPOSES = [PURPOSE_DATA_COLLECT, PURPOSE_PRODUCT_IMPROVEMENT]; +const QC_TCF_CONSENT_FIRST_PURPOSES = [PURPOSE_DATA_COLLECT]; +const QC_TCF_CONSENT_ONLY_PUPROSES = [PURPOSE_DATA_COLLECT]; +const GDPR_PRIVACY_STRING = gdprDataHandler.getConsentData(); +const US_PRIVACY_STRING = uspDataHandler.getConsentData(); export const storage = getStorageManager(); +export function firePixel(clientId, cookieExpDays = DEFAULT_COOKIE_EXP_DAYS) { + // check for presence of Quantcast Measure tag _qevent obj and publisher provided clientID + if (!window._qevents && clientId && clientId != '') { + var fpa = storage.getCookie(QUANTCAST_FPA); + var fpan = '0'; + var domain = quantcastIdSubmodule.findRootDomain(); + var now = new Date(); + var usPrivacyParamString = ''; + var firstPartyParamStrings; + var gdprParamStrings; + + if (!fpa) { + var et = now.getTime(); + var expires = new Date(et + (cookieExpDays * DAY_MS)).toGMTString(); + var rand = Math.round(Math.random() * 2147483647); + fpa = `B0-${rand}-${et}`; + fpan = '1'; + storage.setCookie(QUANTCAST_FPA, fpa, expires, '/', domain, null); + } + + firstPartyParamStrings = `&fpan=${fpan}&fpa=${fpa}`; + gdprParamStrings = '&gdpr=0'; + if (GDPR_PRIVACY_STRING && typeof GDPR_PRIVACY_STRING.gdprApplies === 'boolean' && GDPR_PRIVACY_STRING.gdprApplies) { + gdprParamStrings = `gdpr=1&gdpr_consent=${GDPR_PRIVACY_STRING.consentString}`; + } + if (US_PRIVACY_STRING && typeof US_PRIVACY_STRING === 'string') { + usPrivacyParamString = `&us_privacy=${US_PRIVACY_STRING}`; + } + + let url = QSERVE_URL + + '?d=' + domain + + '&client_id=' + clientId + + '&a=' + PREBID_PCODE + + usPrivacyParamString + + gdprParamStrings + + firstPartyParamStrings; + + triggerPixel(url); + } +}; + +export function hasGDPRConsent(gdprConsent) { + // Check for GDPR consent for purpose 1 and 10, and drop request if consent has not been given + // Remaining consent checks are performed server-side. + if (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) { + if (!gdprConsent.vendorData) { + return false; + } + if (gdprConsent.apiVersion === 1) { + // We are not supporting TCF v1 + return false; + } + if (gdprConsent.apiVersion === 2) { + return checkTCFv2(gdprConsent.vendorData); + } + } + return true; +} + +export function checkTCFv2(vendorData, requiredPurposes = QC_TCF_REQUIRED_PURPOSES) { + var gdprApplies = vendorData.gdprApplies; + var purposes = vendorData.purpose; + var vendors = vendorData.vendor; + var qcConsent = vendors && vendors.consents && vendors.consents[QUANTCAST_VENDOR_ID]; + var qcInterest = vendors && vendors.legitimateInterests && vendors.legitimateInterests[QUANTCAST_VENDOR_ID]; + var restrictions = vendorData.publisher ? vendorData.publisher.restrictions : {}; + + if (!gdprApplies) { + return true; + } + + return requiredPurposes.map(function(purpose) { + var purposeConsent = purposes.consents ? purposes.consents[purpose] : false; + var purposeInterest = purposes.legitimateInterests ? purposes.legitimateInterests[purpose] : false; + + var qcRestriction = restrictions && restrictions[purpose] + ? restrictions[purpose][QUANTCAST_VENDOR_ID] + : null; + + if (qcRestriction === 0) { + return false; + } + + // Seek consent or legitimate interest based on our default legal + // basis for the purpose, falling back to the other if possible. + if ( + // we have positive vendor consent + qcConsent && + // there is positive purpose consent + purposeConsent && + // publisher does not require legitimate interest + qcRestriction !== 2 && + // purpose is a consent-first purpose or publisher has explicitly restricted to consent + (QC_TCF_CONSENT_FIRST_PURPOSES.indexOf(purpose) != -1 || qcRestriction === 1) + ) { + return true; + } else if ( + // publisher does not require consent + qcRestriction !== 1 && + // we have legitimate interest for vendor + qcInterest && + // there is legitimate interest for purpose + purposeInterest && + // purpose's legal basis does not require consent + QC_TCF_CONSENT_ONLY_PUPROSES.indexOf(purpose) == -1 && + // purpose is a legitimate-interest-first purpose or publisher has explicitly restricted to legitimate interest + (QC_TCF_CONSENT_FIRST_PURPOSES.indexOf(purpose) == -1 || qcRestriction === 2) + ) { + return true; + } + + return false; + }).reduce(function(a, b) { + return a && b; + }, true); +} + +/** + * tests if us_privacy consent string is present, us_privacy applies, and notice_given / do-not-sell is set to yes + * @returns {boolean} + */ +export function hasCCPAConsent(usPrivacyConsent) { + if ( + usPrivacyConsent && + typeof usPrivacyConsent === 'string' && + usPrivacyConsent.length == 4 && + usPrivacyConsent.charAt(1) == 'Y' && + usPrivacyConsent.charAt(2) == 'Y' + ) { + return false + } + return true; +} + /** @type {Submodule} */ export const quantcastIdSubmodule = { /** @@ -20,6 +168,12 @@ export const quantcastIdSubmodule = { */ name: 'quantcastId', + /** + * Vendor id of Quantcast + * @type {Number} + */ + gvlid: QUANTCAST_VENDOR_ID, + /** * decode the stored id value for passing to bid requests * @function @@ -34,9 +188,35 @@ export const quantcastIdSubmodule = { * @function * @returns {{id: {quantcastId: string} | undefined}}} */ - getId() { + getId(config) { // Consent signals are currently checked on the server side. let fpa = storage.getCookie(QUANTCAST_FPA); + + const coppa = coppaDataHandler.getCoppa(); + + if (coppa || !hasCCPAConsent(US_PRIVACY_STRING) || !hasGDPRConsent(GDPR_PRIVACY_STRING)) { + var expired = new Date(0).toUTCString(); + var domain = quantcastIdSubmodule.findRootDomain(); + logInfo('QuantcastId: Necessary consent not present for Id, exiting QuantcastId'); + storage.setCookie(QUANTCAST_FPA, '', expired, '/', domain, null); + return undefined; + } + + const configParams = (config && config.params) || {}; + const storageParams = (config && config.storage) || {}; + + var clientId = configParams.clientId || ''; + var cookieExpDays = storageParams.expires || DEFAULT_COOKIE_EXP_DAYS; + + // Callbacks on Event Listeners won't trigger if the event is already complete so this check is required + if (document.readyState === 'complete') { + firePixel(clientId, cookieExpDays); + } else { + window.addEventListener('load', function () { + firePixel(clientId, cookieExpDays); + }); + } + return { id: fpa ? { quantcastId: fpa } : undefined } } }; diff --git a/modules/quantcastIdSystem.md b/modules/quantcastIdSystem.md new file mode 100644 index 00000000000..cf76099e4a5 --- /dev/null +++ b/modules/quantcastIdSystem.md @@ -0,0 +1,46 @@ +#### Overview + +``` +Module Name: Quantcast Id System +Module Type: Id System +Maintainer: asig@quantcast.com +``` + +#### Description + + The Prebid Quantcast ID module stores a Quantcast ID in a first party cookie. The ID is then made available in the bid request. The ID from the cookie added in the bidstream allows Quantcast to more accurately bid on publisher inventories without third party cookies, which can result in better monetization across publisher sites from Quantcast. And, it’s free to use! For easier integration, you can work with one of our SSP partners, like PubMatic, who can facilitate the legal process as well as the software integration for you. + + Add it to your Prebid.js package with: + + `gulp build --modules=userId,quantcastIdSystem` + + Quantcast’s privacy policies for the services rendered can be found at + https://www.quantcast.com/privacy/ + + Publishers deploying the module are responsible for ensuring legally required notices and choices for users. + + The Quantcast ID module will only perform any action and return an ID in situations where: + 1. the publisher has not set a ‘coppa' flag on the prebid configuration on their site (see [pbjs.setConfig.coppa](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-coppa)) + 2. there is not a IAB us-privacy string indicating the digital property has provided user notice and the user has made a choice to opt out of sale + 3. if GDPR applies, an IAB TCF v2 string exists indicating that Quantcast does not have consent for purpose 1 (cookies, device identifiers, or other information can be stored or accessed on your device for the purposes presented to you), or an established legal basis (by default legitimate interest) for purpose 10 (your data can be used to improve existing systems and software, and to develop new products). + + #### Quantcast ID Configuration + + | Param under userSync.userIds[] | Scope | Type | Description | Example | + | --- | --- | --- | --- | --- | + | name | Required | String | `"quantcastId"` | `"quantcastId"` | + | params | Optional | Object | Details for Quantcast initialization. | | + | params.ClientID | Optional | String | Optional parameter for Quantcast prebid managed service partners. The parameter is not required for websites with Quantcast Measure tag. Reach out to Quantcast for ClientID if you are not an existing Quantcast prebid managed service partner: quantcast-idsupport@quantcast.com. | | + + + #### Quantcast ID Example + +```js + pbjs.setConfig({ + userSync: { + userIds: [{ + name: "quantcastId" + }] + } + }); +``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 9bbe0bdde6a..1c7f854f725 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -129,6 +129,12 @@ pbjs.setConfig({ params: { token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" } + },{ + name: 'quantcastId', + storage: { + type: 'cookie', + expires : 30 + } }], syncDelay: 5000, auctionDelay: 1000 diff --git a/test/spec/modules/quantcastIdSystem_spec.js b/test/spec/modules/quantcastIdSystem_spec.js index 12c8689fd3f..e9d44dd6124 100644 --- a/test/spec/modules/quantcastIdSystem_spec.js +++ b/test/spec/modules/quantcastIdSystem_spec.js @@ -1,15 +1,25 @@ -import { quantcastIdSubmodule, storage } from 'modules/quantcastIdSystem.js'; +import { quantcastIdSubmodule, storage, firePixel, hasCCPAConsent, hasGDPRConsent, checkTCFv2 } from 'modules/quantcastIdSystem.js'; +import * as utils from 'src/utils.js'; +import {coppaDataHandler} from 'src/adapterManager'; describe('QuantcastId module', function () { beforeEach(function() { - storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); + sinon.stub(coppaDataHandler, 'getCoppa'); + sinon.stub(utils, 'triggerPixel'); + sinon.stub(window, 'addEventListener'); }); - it('getId() should return a quantcast id when the Quantcast first party cookie exists', function () { - storage.setCookie('__qca', 'P0-TestFPA'); + afterEach(function () { + utils.triggerPixel.restore(); + coppaDataHandler.getCoppa.restore(); + window.addEventListener.restore(); + }); + it('getId() should return a quantcast id when the Quantcast first party cookie exists', function () { + sinon.stub(storage, 'getCookie').returns('P0-TestFPA'); const id = quantcastIdSubmodule.getId(); expect(id).to.be.deep.equal({id: {quantcastId: 'P0-TestFPA'}}); + storage.getCookie.restore(); }); it('getId() should return an empty id when the Quantcast first party cookie is missing', function () { @@ -17,3 +27,357 @@ describe('QuantcastId module', function () { expect(id).to.be.deep.equal({id: undefined}); }); }); + +describe('QuantcastId fire pixel', function () { + beforeEach(function () { + storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); + sinon.stub(storage, 'setCookie'); + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + utils.triggerPixel.restore(); + storage.setCookie.restore(); + }); + + it('fpa should be set when not present on this call', function () { + firePixel('clientId'); + var urlString = utils.triggerPixel.getCall(0).args[0]; + var parsedUrl = utils.parseUrl(urlString); + var urlSearchParams = parsedUrl.search; + assert.equal(urlSearchParams.fpan, '1'); + assert.notEqual(urlSearchParams.fpa, null); + }); + + it('fpa should be extracted from the Quantcast first party cookie when present on this call', function () { + sinon.stub(storage, 'getCookie').returns('P0-TestFPA'); + firePixel('clientId'); + var urlString = utils.triggerPixel.getCall(0).args[0]; + var parsedUrl = utils.parseUrl(urlString); + var urlSearchParams = parsedUrl.search; + assert.equal(urlSearchParams.fpan, '0'); + assert.equal(urlSearchParams.fpa, 'P0-TestFPA'); + storage.getCookie.restore(); + }); + + it('function to trigger pixel is called once', function () { + firePixel('clientId'); + expect(utils.triggerPixel.calledOnce).to.equal(true); + }); + + it('function to trigger pixel is not called when client id is absent', function () { + firePixel(); + expect(utils.triggerPixel.calledOnce).to.equal(false); + }); +}); + +describe('Quantcast CCPA consent check', function() { + it('returns true when CCPA constent string is not present', function() { + expect(hasCCPAConsent()).to.equal(true); + }); + + it("returns true when notice_given or do-not-sell in CCPA constent string is not 'Y' ", function() { + expect(hasCCPAConsent('1NNN')).to.equal(true); + expect(hasCCPAConsent('1YNN')).to.equal(true); + expect(hasCCPAConsent('1NYN')).to.equal(true); + }); + + it("returns false when CCPA consent string is present, and notice_given or do-not-sell in the string is 'Y' ", function() { + expect(hasCCPAConsent('1YYN')).to.equal(false); + }); +}); + +describe('Quantcast GDPR consent check', function() { + it("returns true when GDPR doesn't apply", function() { + expect(hasGDPRConsent({gdprApplies: false})).to.equal(true); + }); + + it('returns false if denied consent, even if special purpose 1 treatment is true in DE', function() { + expect(checkTCFv2({ + gdprApplies: true, + publisherCC: 'DE', + purposeOneTreatment: true, + vendor: { + consents: { '11': false } + }, + purpose: { + consents: { '1': false } + }, + publisher: { + restrictions: { + '1': { + '11': 0 // flatly disallow Quantcast + } + } + } + }, ['1'])).to.equal(false); + }); + + it('returns false if publisher flatly denies required purpose', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true } + }, + purpose: { + consents: { '1': true } + }, + publisher: { + restrictions: { + '1': { + '11': 0 // flatly disallow Quantcast + } + } + } + }, ['1'])).to.equal(false); + }); + + it('returns true if positive consent for required purpose', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true } + }, + purpose: { + consents: { '1': true } + } + }, ['1'])).to.equal(true); + }); + + it('returns false if positive consent but publisher requires legitimate interest for required purpose', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true } + }, + purpose: { + consents: { '1': true } + }, + publisher: { + restrictions: { + '1': { + '11': 2 // require legitimate interest for Quantcast + } + } + } + }, ['1'])).to.equal(false); + }); + + it('returns false if no vendor consent and no legitimate interest', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': false } + }, + purpose: { + consents: { '1': true } + } + }, ['1'])).to.equal(false); + }); + + it('returns false if no purpose consent and no legitimate interest', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true } + }, + purpose: { + consents: { '1': false } + } + }, ['1'])).to.equal(false); + }); + + it('returns false if no consent, but legitimate interest for consent-first purpose, and no restrictions specified', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '1': false }, + legitimateInterests: { '1': true } + } + }, ['1'])).to.equal(false); + }); + + it('returns false if consent, but no legitimate interest for legitimate-interest-first purpose, and no restrictions specified', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '10': true }, + legitimateInterests: { '10': false } + } + }, ['10'])).to.equal(false); + }); + + it('returns true if consent, but no legitimate interest for legitimate-interest-first purpose, and corresponding consent restriction specified', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '10': true }, + legitimateInterests: { '10': false } + }, + publisher: { + restrictions: { + '10': { + '11': 1 // require consent for Quantcast + } + } + } + }, ['10'])).to.equal(true); + }); + + it('returns false if no consent but legitimate interest for required purpose other than 1, but publisher requires consent', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': false }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '10': false }, + legitimateInterests: { '10': true } + }, + publisher: { + restrictions: { + '10': { + '11': 1 // require consent for Quantcast + } + } + } + }, ['10'])).to.equal(false); + }); + + it('returns false if no consent and no legitimate interest for vendor for required purpose other than 1', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': false }, + legitimateInterests: { '11': false } + }, + purpose: { + consents: { '10': false }, + legitimateInterests: { '10': true } + } + }, ['10'])).to.equal(false); + }); + + it('returns false if no consent and no legitimate interest for required purpose other than 1', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': false }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '10': false }, + legitimateInterests: { '10': false } + } + }, ['10'])).to.equal(false); + }); + + it('returns false if no consent but legitimate interest for required purpose, but required purpose is purpose 1', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': false }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { '1': false }, + legitimateInterests: { '1': true } + } + }, ['1'])).to.equal(false); + }); + + it('returns true if different legal bases for multiple required purposes', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { + '1': true, + '10': false + }, + legitimateInterests: { + '1': false, + '10': true + } + }, + publisher: { + restrictions: { + '10': { + '11': 2 // require legitimate interest for Quantcast + } + } + } + })).to.equal(true); + }); + + it('returns true if full consent and legitimate interest for all required purposes with no restrictions specified', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { + '1': true, + '3': true, + '7': true, + '8': true, + '9': true, + '10': true + }, + legitimateInterests: { + '1': true, + '3': true, + '7': true, + '8': true, + '9': true, + '10': true + } + } + })).to.equal(true); + }); + + it('returns false if one of multiple required purposes has no legal basis', function() { + expect(checkTCFv2({ + gdprApplies: true, + vendor: { + consents: { '11': true }, + legitimateInterests: { '11': true } + }, + purpose: { + consents: { + '1': true, + '10': false + }, + legitimateInterests: { + '11': false, + '10': true + } + }, + publisher: { + restrictions: { + '10': { + '11': 1 // require consent for Quantcast + } + } + } + })).to.equal(false); + }); +}); From 6ce4162888d14bd8861b1f9d17adde0521fb857f Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Wed, 14 Jul 2021 17:24:19 +0200 Subject: [PATCH 1277/1476] interpretResponse video enhancements (#7171) Co-authored-by: francesco --- modules/onetagBidAdapter.js | 5 +++- test/spec/modules/onetagBidAdapter_spec.js | 31 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 86f29b0dd73..aab68176d87 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -109,7 +109,10 @@ function interpretResponse(serverResponse, bidderRequest) { if (bid.mediaType === BANNER) { responseBid.ad = bid.ad; } else if (bid.mediaType === VIDEO) { - const {context, adUnitCode} = find(requestData.bids, (item) => item.bidId === bid.requestId); + const {context, adUnitCode} = find(requestData.bids, (item) => + item.bidId === bid.requestId && + item.type === VIDEO + ); if (context === INSTREAM) { responseBid.vastUrl = bid.vastUrl; responseBid.videoCacheKey = bid.videoCacheKey; diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index f51e95039ea..9fb73f7774b 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -105,9 +105,36 @@ describe('onetag', function () { }); }); describe('multi format bidRequest', function () { - const multiFormatBid = createMultiFormatBid(); it('Should return true when correct multi format bid is passed', function () { - expect(spec.isBidRequestValid(multiFormatBid)).to.be.true; + 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; + 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() { + const requestBid = createMultiFormatBid(); + const multiFormatRequest = spec.buildRequests([ requestBid ]); + const serverResponse = { + body: { + bids: [ + { + mediaType: BANNER, + requestId: requestBid.bidId, + ad: 'test-banner' + }, { + mediaType: VIDEO, + requestId: requestBid.bidId, + vastUrl: 'test-video' + } + ] + } + }; + const responseBids = spec.interpretResponse(serverResponse, multiFormatRequest); + expect(responseBids.length).to.equal(2); + expect(responseBids[0].ad).to.equal('test-banner'); + expect(responseBids[1].vastUrl).to.equal('test-video'); }); }); }); From fc60a2c1f7bcbd4a8f83775fb078a0baecd1e0dc Mon Sep 17 00:00:00 2001 From: Nick Jacob Date: Wed, 14 Jul 2021 12:33:46 -0400 Subject: [PATCH 1278/1476] update amxIdSystem endpoint and internal timeout (#7174) --- modules/amxIdSystem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/amxIdSystem.js b/modules/amxIdSystem.js index 8df518e4259..28323b01188 100644 --- a/modules/amxIdSystem.js +++ b/modules/amxIdSystem.js @@ -15,8 +15,8 @@ const NAME = 'amxId'; const GVL_ID = 737; const ID_KEY = NAME; const version = '1.0'; -const SYNC_URL = 'https://translator.a-mo.net/sync'; -const AJAX_TIMEOUT = 150; +const SYNC_URL = 'https://id.a-mx.com/sync/'; +const AJAX_TIMEOUT = 300; function validateConfig(config) { if (config == null || config.storage == null) { From 391ecc0462abc2420a4dd3e1df1872f315473038 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Wed, 14 Jul 2021 22:08:20 +0300 Subject: [PATCH 1279/1476] TheMediaGrid: added ext.bidder.grid.demandSource processing (#7179) * 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 Co-authored-by: Chris Huie --- modules/gridBidAdapter.js | 4 ++ test/spec/modules/gridBidAdapter_spec.js | 62 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 854b82aa84d..52ef1b402e8 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -362,6 +362,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { dealId: serverBid.dealid }; + if (serverBid.ext && serverBid.ext.bidder && serverBid.ext.bidder.grid && serverBid.ext.bidder.grid.demandSource) { + bidResponse.adserverTargeting = { 'hb_ds': serverBid.ext.bidder.grid.demandSource }; + } + if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; bidResponse.mediaType = VIDEO; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1ec0200885c..8aa743586f5 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -985,6 +985,68 @@ describe('TheMediaGrid Adapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); + + it('response with ext.bidder.grid.demandSource', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const serverResponse = { + 'bid': [ + { + 'impid': '26d6f897b516', + 'price': 1.15, + 'adm': '
test content 1
', + 'auid': 1, + 'h': 250, + 'w': 300, + 'dealid': 11, + 'ext': { + 'bidder': { + 'grid': { + 'demandSource': 'someValue' + } + } + } + } + ], + 'seat': '1' + }; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '26d6f897b516', + '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: [] + }, + 'adserverTargeting': { + 'hb_ds': 'someValue' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); describe('user sync', function () { From 98fbd9cb59a7658a79d916ce17c44668864e576b Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 14 Jul 2021 12:09:25 -0700 Subject: [PATCH 1280/1476] pass along userId providers (#7180) --- modules/rubiconAnalyticsAdapter.js | 7 ++++ .../modules/rubiconAnalyticsAdapter_spec.js | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index d25401ee70f..0d47bee235f 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -293,6 +293,10 @@ function sendMessage(auctionId, bidWonId, trigger) { auction.serverTimeoutMillis = serverConfig.timeout; } + if (auctionCache.userIds.length) { + auction.user = {ids: auctionCache.userIds}; + } + message.auctions = [auction]; let bidsWon = Object.keys(auctionCache.bidsWon).reduce((memo, adUnitCode) => { @@ -585,6 +589,9 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { } cacheEntry.gdprConsent = utils.deepAccess(args, 'bidderRequests.0.gdprConsent'); cacheEntry.session = storage.localStorageIsEnabled() && updateRpaCookie(); + cacheEntry.userIds = Object.keys(utils.deepAccess(args, 'bidderRequests.0.bids.0.userId', {})).map(id => { + return {provider: id, hasId: true} + }); cache.auctions[args.auctionId] = cacheEntry; // register to listen to gpt events if not done yet if (!cache.gpt.registered && utils.isGptPubadsDefined()) { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index d8aa9ede9a5..ea8a25ee27d 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -853,6 +853,42 @@ describe('rubicon analytics adapter', function () { expect(message).to.deep.equal(ANALYTICS_MESSAGE); }); + it('should pass along user ids', function () { + let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); + auctionInit.bidderRequests[0].bids[0].userId = { + criteoId: 'sadfe4334', + lotamePanoramaId: 'asdf3gf4eg', + pubcid: 'dsfa4545-svgdfs5', + sharedId: {id1: 'asdf', id2: 'sadf4344'} + }; + + events.emit(AUCTION_INIT, auctionInit); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + + let message = JSON.parse(request.requestBody); + validate(message); + + expect(message.auctions[0].user).to.deep.equal({ + ids: [ + {provider: 'criteoId', 'hasId': true}, + {provider: 'lotamePanoramaId', 'hasId': true}, + {provider: 'pubcid', 'hasId': true}, + {provider: 'sharedId', 'hasId': true}, + ] + }); + }); + it('should handle bidResponse dimensions correctly', function () { events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); From ad36dcddee48287318975b1052ae17d621e9f34b Mon Sep 17 00:00:00 2001 From: Gena Date: Thu, 15 Jul 2021 12:38:17 +0300 Subject: [PATCH 1281/1476] Openweb Bid Adapter: add new bid adapter (#7175) --- modules/openwebBidAdapter.js | 247 +++++++++++++ modules/openwebBidAdapter.md | 27 ++ test/spec/modules/openwebBidAdapter_spec.js | 387 ++++++++++++++++++++ 3 files changed, 661 insertions(+) create mode 100644 modules/openwebBidAdapter.js create mode 100644 modules/openwebBidAdapter.md create mode 100644 test/spec/modules/openwebBidAdapter_spec.js diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js new file mode 100644 index 00000000000..7aa4e05663e --- /dev/null +++ b/modules/openwebBidAdapter.js @@ -0,0 +1,247 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ADPOD, BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import find from 'core-js-pure/features/array/find.js'; + +const ENDPOINT = 'https://ghb.spotim.market'; +const BIDDER_CODE = 'openweb'; +const DISPLAY = 'display'; +const syncsCache = {}; + +export const spec = { + code: BIDDER_CODE, + gvlid: 280, + supportedMediaTypes: [VIDEO, BANNER, ADPOD], + isBidRequestValid: function (bid) { + return utils.isNumber(utils.deepAccess(bid, 'params.aid')); + }, + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + + function addSyncs(bid) { + const uris = bid.cookieURLs; + const types = bid.cookieURLSTypes || []; + + if (Array.isArray(uris)) { + uris.forEach((uri, i) => { + const type = types[i] || 'image'; + + if ((!syncOptions.pixelEnabled && type === 'image') || + (!syncOptions.iframeEnabled && type === 'iframe') || + syncsCache[uri]) { + return; + } + + syncsCache[uri] = true; + syncs.push({ + type: type, + url: uri + }) + }) + } + } + + if (syncOptions.pixelEnabled || syncOptions.iframeEnabled) { + utils.isArray(serverResponses) && serverResponses.forEach((response) => { + if (response.body) { + if (utils.isArray(response.body)) { + response.body.forEach(b => { + addSyncs(b); + }) + } else { + addSyncs(response.body) + } + } + }) + } + return syncs; + }, + /** + * Make a server request from the list of BidRequests + * @param bidRequests + * @param adapterRequest + */ + buildRequests: function (bidRequests, adapterRequest) { + const { tag, bids } = bidToTag(bidRequests, adapterRequest); + return [{ + data: Object.assign({}, tag, { BidRequests: bids }), + adapterRequest, + method: 'POST', + url: ENDPOINT + }]; + }, + + /** + * Unpack the response from the server into a list of bids + * @param serverResponse + * @param bidderRequest + * @return {Bid[]} An array of bids which were nested inside the server + */ + interpretResponse: function (serverResponse, { adapterRequest }) { + serverResponse = serverResponse.body; + let bids = []; + + if (!utils.isArray(serverResponse)) { + return parseRTBResponse(serverResponse, adapterRequest); + } + + serverResponse.forEach(serverBidResponse => { + bids = utils.flatten(bids, parseRTBResponse(serverBidResponse, adapterRequest)); + }); + + return bids; + }, + + transformBidParams(params) { + return utils.convertTypes({ + 'aid': 'number', + }, params); + } +}; + +function parseRTBResponse(serverResponse, adapterRequest) { + const isEmptyResponse = !serverResponse || !utils.isArray(serverResponse.bids); + const bids = []; + + if (isEmptyResponse) { + return bids; + } + + serverResponse.bids.forEach(serverBid => { + const request = find(adapterRequest.bids, (bidRequest) => { + return bidRequest.bidId === serverBid.requestId; + }); + + if (serverBid.cpm !== 0 && request !== undefined) { + const bid = createBid(serverBid, request); + + bids.push(bid); + } + }); + + return bids; +} + +function bidToTag(bidRequests, adapterRequest) { + // start publisher env + const tag = { + Domain: utils.deepAccess(adapterRequest, 'refererInfo.referer') + }; + if (config.getConfig('coppa') === true) { + tag.Coppa = 1; + } + if (utils.deepAccess(adapterRequest, 'gdprConsent.gdprApplies')) { + tag.GDPR = 1; + tag.GDPRConsent = utils.deepAccess(adapterRequest, 'gdprConsent.consentString'); + } + if (utils.deepAccess(adapterRequest, 'uspConsent')) { + tag.USP = utils.deepAccess(adapterRequest, 'uspConsent'); + } + if (utils.deepAccess(bidRequests[0], 'schain')) { + tag.Schain = utils.deepAccess(bidRequests[0], 'schain'); + } + if (utils.deepAccess(bidRequests[0], 'userId')) { + tag.UserIds = utils.deepAccess(bidRequests[0], 'userId'); + } + if (utils.deepAccess(bidRequests[0], 'userIdAsEids')) { + tag.UserEids = utils.deepAccess(bidRequests[0], 'userIdAsEids'); + } + // end publisher env + const bids = [] + + for (let i = 0, length = bidRequests.length; i < length; i++) { + const bid = prepareBidRequests(bidRequests[i]); + bids.push(bid); + } + + return { tag, bids }; +} + +/** + * Parse mediaType + * @param bidReq {object} + * @returns {object} + */ +function prepareBidRequests(bidReq) { + const mediaType = utils.deepAccess(bidReq, 'mediaTypes.video') ? VIDEO : DISPLAY; + const sizes = mediaType === VIDEO ? utils.deepAccess(bidReq, 'mediaTypes.video.playerSize') : utils.deepAccess(bidReq, 'mediaTypes.banner.sizes'); + const bidReqParams = { + 'CallbackId': bidReq.bidId, + 'Aid': bidReq.params.aid, + 'AdType': mediaType, + 'Sizes': utils.parseSizesInput(sizes).join(',') + }; + + bidReqParams.PlacementId = bidReq.adUnitCode; + if (bidReq.params.iframe) { + bidReqParams.AdmType = 'iframe'; + } + if (mediaType === VIDEO) { + const context = utils.deepAccess(bidReq, 'mediaTypes.video.context'); + if (context === ADPOD) { + bidReqParams.Adpod = utils.deepAccess(bidReq, 'mediaTypes.video'); + } + } + return bidReqParams; +} + +/** + * Prepare all parameters for request + * @param bidderRequest {object} + * @returns {object} + */ +function getMediaType(bidderRequest) { + return utils.deepAccess(bidderRequest, 'mediaTypes.video') ? VIDEO : BANNER; +} + +/** + * Configure new bid by response + * @param bidResponse {object} + * @param bidRequest {Object} + * @returns {object} + */ +function createBid(bidResponse, bidRequest) { + const mediaType = getMediaType(bidRequest) + const context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); + const bid = { + requestId: bidResponse.requestId, + creativeId: bidResponse.cmpId, + height: bidResponse.height, + currency: bidResponse.cur, + width: bidResponse.width, + cpm: bidResponse.cpm, + netRevenue: true, + mediaType, + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } + }; + + if (mediaType === BANNER) { + return Object.assign(bid, { + ad: bidResponse.ad, + adUrl: bidResponse.adUrl, + }); + } + if (context === ADPOD) { + Object.assign(bid, { + meta: { + primaryCatId: bidResponse.primaryCatId, + }, + video: { + context: ADPOD, + durationSeconds: bidResponse.durationSeconds + } + }); + } + + Object.assign(bid, { + vastUrl: bidResponse.vastUrl + }); + + return bid; +} + +registerBidder(spec); diff --git a/modules/openwebBidAdapter.md b/modules/openwebBidAdapter.md new file mode 100644 index 00000000000..dc8bfa6c59e --- /dev/null +++ b/modules/openwebBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +**Module Name**: OpenWeb Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: monetization@openweb.com + +# Description + +OpenWeb.com official prebid adapter. Available in both client and server side versions. +OpenWeb header bidding adapter provides solution for accessing both Video and Display demand. + +# Test Parameters +``` + var adUnits = [ + // Banner adUnit + { + code: 'div-test-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'openweb', + params: { + aid: 529814 + } + }] + } + ]; +``` diff --git a/test/spec/modules/openwebBidAdapter_spec.js b/test/spec/modules/openwebBidAdapter_spec.js new file mode 100644 index 00000000000..c515c21690a --- /dev/null +++ b/test/spec/modules/openwebBidAdapter_spec.js @@ -0,0 +1,387 @@ +import { expect } from 'chai'; +import { spec } from 'modules/openwebBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; + +const DEFAULT_ADATPER_REQ = { bidderCode: 'openweb' }; +const DISPLAY_REQUEST = { + 'bidder': 'openweb', + 'params': { + 'aid': 12345 + }, + 'schain': { ver: 1 }, + 'userId': { criteo: 2 }, + 'mediaTypes': { 'banner': { 'sizes': [300, 250] } }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', +}; + +const VIDEO_REQUEST = { + 'bidder': 'openweb', + 'mediaTypes': { + 'video': { + 'playerSize': [[480, 360], [640, 480]] + } + }, + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d' +}; + +const ADPOD_REQUEST = { + 'bidder': 'openweb', + 'mediaTypes': { + 'video': { + 'context': 'adpod', + 'playerSize': [[640, 480]], + 'anyField': 10 + } + }, + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '2e41f65424c87c' +}; + +const SERVER_VIDEO_RESPONSE = { + 'source': { 'aid': 12345, 'pubId': 54321 }, + 'bids': [{ + 'vastUrl': 'vastUrl', + 'requestId': '2e41f65424c87c', + 'url': '44F2AEB9BFC881B3', + 'creative_id': 342516, + 'durationSeconds': 30, + 'cmpId': 342516, + 'height': 480, + 'cur': 'USD', + 'width': 640, + 'cpm': 0.9, + 'adomain': ['a.com'] + }] +}; +const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; +const SERVER_DISPLAY_RESPONSE = { + 'source': { 'aid': 12345, 'pubId': 54321 }, + 'bids': [{ + 'ad': '', + 'adUrl': 'adUrl', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }], + 'cookieURLs': ['link1', 'link2'] +}; +const SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS = { + 'source': { 'aid': 12345, 'pubId': 54321 }, + 'bids': [{ + 'ad': '', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }], + 'cookieURLs': ['link3', 'link4'], + 'cookieURLSTypes': ['image', 'iframe'] +}; + +const videoBidderRequest = { + bidderCode: 'bidderCode', + bids: [{ mediaTypes: { video: {} }, bidId: '2e41f65424c87c' }] +}; + +const displayBidderRequest = { + bidderCode: 'bidderCode', + bids: [{ bidId: '2e41f65424c87c' }] +}; + +const displayBidderRequestWithConsents = { + bidderCode: 'bidderCode', + bids: [{ bidId: '2e41f65424c87c' }], + gdprConsent: { + gdprApplies: true, + consentString: 'test' + }, + uspConsent: 'iHaveIt' +}; + +const videoEqResponse = [{ + vastUrl: 'vastUrl', + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'video', + netRevenue: true, + currency: 'USD', + height: 480, + width: 640, + ttl: 300, + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } +}]; + +const displayEqResponse = [{ + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'banner', + netRevenue: true, + currency: 'USD', + ad: '', + adUrl: 'adUrl', + height: 250, + width: 300, + ttl: 300, + cpm: 0.9, + meta: { + advertiserDomains: [] + } + +}]; + +describe('openwebBidAdapter', () => { + const adapter = newBidder(spec); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('user syncs', () => { + describe('as image', () => { + it('should be returned if pixel enabled', () => { + const syncs = spec.getUserSyncs({ pixelEnabled: true }, [{ body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS }]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[0]]); + expect(syncs.map(s => s.type)).to.deep.equal(['image']); + }) + }) + + describe('as iframe', () => { + it('should be returned if iframe enabled', () => { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [{ body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS }]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[1]]); + expect(syncs.map(s => s.type)).to.deep.equal(['iframe']); + }) + }) + + describe('user sync', () => { + it('should not be returned if passed syncs where already used', () => { + const syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [{ body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS }]); + + expect(syncs).to.deep.equal([]); + }) + + it('should not be returned if pixel not set', () => { + const syncs = spec.getUserSyncs({}, [{ body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS }]); + + expect(syncs).to.be.empty; + }); + }); + describe('user syncs with both types', () => { + it('should be returned if pixel and iframe enabled', () => { + const mockedServerResponse = Object.assign({}, SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS, { 'cookieURLs': ['link5', 'link6'] }); + const syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [{ body: mockedServerResponse }]); + + expect(syncs.map(s => s.url)).to.deep.equal(mockedServerResponse.cookieURLs); + expect(syncs.map(s => s.type)).to.deep.equal(mockedServerResponse.cookieURLSTypes); + }); + }); + }); + + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(VIDEO_REQUEST)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, VIDEO_REQUEST); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let videoBidRequests = [VIDEO_REQUEST]; + let displayBidRequests = [DISPLAY_REQUEST]; + let videoAndDisplayBidRequests = [DISPLAY_REQUEST, VIDEO_REQUEST]; + const displayRequest = spec.buildRequests(displayBidRequests, DEFAULT_ADATPER_REQ); + const videoRequest = spec.buildRequests(videoBidRequests, DEFAULT_ADATPER_REQ); + const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, DEFAULT_ADATPER_REQ); + + it('building requests as arrays', () => { + expect(videoRequest).to.be.a('array'); + expect(displayRequest).to.be.a('array'); + expect(videoAndDisplayRequests).to.be.a('array'); + }) + + it('sending as POST', () => { + const postActionMethod = 'POST' + const comparator = br => br.method === postActionMethod; + expect(videoRequest.every(comparator)).to.be.true; + expect(displayRequest.every(comparator)).to.be.true; + expect(videoAndDisplayRequests.every(comparator)).to.be.true; + }); + it('forms correct ADPOD request', () => { + const pbBidReqData = spec.buildRequests([ADPOD_REQUEST], DEFAULT_ADATPER_REQ)[0].data; + const impRequest = pbBidReqData.BidRequests[0] + expect(impRequest.AdType).to.be.equal('video'); + expect(impRequest.Adpod).to.be.a('object'); + expect(impRequest.Adpod.anyField).to.be.equal(10); + }) + it('sends correct video bid parameters', () => { + const data = videoRequest[0].data; + + const eq = { + CallbackId: '84ab500420319d', + AdType: 'video', + Aid: 12345, + Sizes: '480x360,640x480', + PlacementId: 'adunit-code' + }; + expect(data.BidRequests[0]).to.deep.equal(eq); + }); + + it('sends correct display bid parameters', () => { + const data = displayRequest[0].data; + + const eq = { + CallbackId: '84ab500420319d', + AdType: 'display', + Aid: 12345, + Sizes: '300x250', + PlacementId: 'adunit-code' + }; + + expect(data.BidRequests[0]).to.deep.equal(eq); + }); + + it('sends correct video and display bid parameters', () => { + const bidRequests = videoAndDisplayRequests[0].data; + const expectedBidReqs = [{ + CallbackId: '84ab500420319d', + AdType: 'display', + Aid: 12345, + Sizes: '300x250', + PlacementId: 'adunit-code' + }, { + CallbackId: '84ab500420319d', + AdType: 'video', + Aid: 12345, + Sizes: '480x360,640x480', + PlacementId: 'adunit-code' + }] + + expect(bidRequests.BidRequests).to.deep.equal(expectedBidReqs); + }); + + describe('publisher environment', () => { + const sandbox = sinon.sandbox.create(); + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'coppa': true + }; + return config[key]; + }); + const bidRequestWithPubSettingsData = spec.buildRequests([DISPLAY_REQUEST], displayBidderRequestWithConsents)[0].data; + sandbox.restore(); + it('sets GDPR', () => { + expect(bidRequestWithPubSettingsData.GDPR).to.be.equal(1); + expect(bidRequestWithPubSettingsData.GDPRConsent).to.be.equal(displayBidderRequestWithConsents.gdprConsent.consentString); + }); + it('sets USP', () => { + expect(bidRequestWithPubSettingsData.USP).to.be.equal(displayBidderRequestWithConsents.uspConsent); + }) + it('sets Coppa', () => { + expect(bidRequestWithPubSettingsData.Coppa).to.be.equal(1); + }) + it('sets Schain', () => { + expect(bidRequestWithPubSettingsData.Schain).to.be.deep.equal(DISPLAY_REQUEST.schain); + }) + it('sets UserId\'s', () => { + expect(bidRequestWithPubSettingsData.UserIds).to.be.deep.equal(DISPLAY_REQUEST.userId); + }) + }) + }); + + describe('interpretResponse', () => { + let serverResponse; + let adapterRequest; + let eqResponse; + + afterEach(() => { + serverResponse = null; + adapterRequest = null; + eqResponse = null; + }); + + it('should get correct video bid response', () => { + serverResponse = SERVER_VIDEO_RESPONSE; + adapterRequest = videoBidderRequest; + eqResponse = videoEqResponse; + + bidServerResponseCheck(); + }); + + it('should get correct display bid response', () => { + serverResponse = SERVER_DISPLAY_RESPONSE; + adapterRequest = displayBidderRequest; + eqResponse = displayEqResponse; + + bidServerResponseCheck(); + }); + + function bidServerResponseCheck() { + const result = spec.interpretResponse({ body: serverResponse }, { adapterRequest }); + + expect(result).to.deep.equal(eqResponse); + } + + function nobidServerResponseCheck() { + const noBidServerResponse = { bids: [] }; + const noBidResult = spec.interpretResponse({ body: noBidServerResponse }, { adapterRequest }); + + expect(noBidResult.length).to.equal(0); + } + + it('handles video nobid responses', () => { + adapterRequest = videoBidderRequest; + + nobidServerResponseCheck(); + }); + + it('handles display nobid responses', () => { + adapterRequest = displayBidderRequest; + + nobidServerResponseCheck(); + }); + + it('forms correct ADPOD response', () => { + const videoBids = spec.interpretResponse({ body: SERVER_VIDEO_RESPONSE }, { adapterRequest: { bids: [ADPOD_REQUEST] } }); + expect(videoBids[0].video.durationSeconds).to.be.equal(30); + expect(videoBids[0].video.context).to.be.equal('adpod'); + }) + }); +}); From 485c0e75008d03469db94b837f37612615b5aa6d Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 15 Jul 2021 11:13:36 -0400 Subject: [PATCH 1282/1476] Update gridBidAdapter.js --- modules/gridBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 52ef1b402e8..897aa6151ba 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -357,7 +357,8 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [], + networkName: serverBid.ext && serverBid.ext.bidder && serverBid.ext.bidder.grid && serverBid.ext.bidder.grid.demandSource ? serverBid.ext.bidder.grid.demandSource : '' }, dealId: serverBid.dealid }; From 236755084ab8ed4d88a47f1d716db40b8468c7ff Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 15 Jul 2021 11:14:46 -0400 Subject: [PATCH 1283/1476] Revert last commit, wrong branch --- modules/gridBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 897aa6151ba..52ef1b402e8 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -357,8 +357,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid.adomain ? serverBid.adomain : [], - networkName: serverBid.ext && serverBid.ext.bidder && serverBid.ext.bidder.grid && serverBid.ext.bidder.grid.demandSource ? serverBid.ext.bidder.grid.demandSource : '' + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] }, dealId: serverBid.dealid }; From d6b646f8071f31ec62a94603041702a011a6a9dd Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 15 Jul 2021 10:47:23 -0700 Subject: [PATCH 1284/1476] Prebid 5.5.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 181945c1dd4..18188ec47b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.5.0-pre", + "version": "5.5.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From fba096ded51c5be79a227788fcfb9979b9733a9b Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Thu, 15 Jul 2021 11:05:11 -0700 Subject: [PATCH 1285/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 18188ec47b0..bdf16df1c41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.5.0", + "version": "5.6.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 63b8929e86fd2a9daebb9ae83dd650383b231837 Mon Sep 17 00:00:00 2001 From: Sourabh Gandhe Date: Fri, 16 Jul 2021 17:43:43 +0530 Subject: [PATCH 1286/1476] Deep Intent Bid Adapter: backward compatible eids added (#7187) * backward compatible eids added * eslint fixes Co-authored-by: Sourabh Gandhe --- modules/deepintentBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index a6a6cac6570..25c6ee9b25b 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -137,6 +137,7 @@ function injectEids(openRtbBidRequest, validBidRequests) { const bidUserIdAsEids = utils.deepAccess(validBidRequests, '0.userIdAsEids'); if (utils.isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { utils.deepSetValue(openRtbBidRequest, 'user.eids', bidUserIdAsEids); + utils.deepSetValue(openRtbBidRequest, 'user.ext.eids', bidUserIdAsEids); } } From d41af07819a6d66a8533fc7e6cba2fc6ecf46d14 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 16 Jul 2021 13:07:05 -0400 Subject: [PATCH 1287/1476] MediaGrid Adapter: Put demandSource in bid.meta (#7184) --- modules/gridBidAdapter.js | 1 + test/spec/modules/gridBidAdapter_spec.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 52ef1b402e8..0da15499e0c 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -364,6 +364,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { 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; } if (serverBid.content_type === 'video') { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 8aa743586f5..1044139a507 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -1036,7 +1036,8 @@ describe('TheMediaGrid Adapter', function () { 'netRevenue': true, 'ttl': 360, 'meta': { - advertiserDomains: [] + advertiserDomains: [], + demandSource: 'someValue' }, 'adserverTargeting': { 'hb_ds': 'someValue' From 7e95a31bd4b77788ad114187fc94e378823990a0 Mon Sep 17 00:00:00 2001 From: Xingwang Liao <60087633+xingwangl@users.noreply.github.com> Date: Mon, 19 Jul 2021 16:27:32 +0800 Subject: [PATCH 1288/1476] Opera Ads Bid Adapter: add new bid adapter (#7152) * feat(operaads): add Opera Ads bid adapter * fix(operaads): fix sharedId support * chore(operaads): remove user sync support * feat(operaads): no need to set width and height when native ad is requested * fix(operaads): decode native icon url * test(operaads): add more test cases * fix(operaads): fix native response parse * feat(operaads): track bid won * test(operaads): update test cases --- modules/operaadsBidAdapter.js | 818 +++++++++++++++++++ modules/operaadsBidAdapter.md | 155 ++++ test/spec/modules/operaadsBidAdapter_spec.js | 698 ++++++++++++++++ 3 files changed, 1671 insertions(+) create mode 100644 modules/operaadsBidAdapter.js create mode 100644 modules/operaadsBidAdapter.md create mode 100644 test/spec/modules/operaadsBidAdapter_spec.js diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js new file mode 100644 index 00000000000..85ba25c1404 --- /dev/null +++ b/modules/operaadsBidAdapter.js @@ -0,0 +1,818 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; + +const BIDDER_CODE = 'operaads'; + +const ENDPOINT = 'https://s.adx.opera.com/ortb/v2/'; + +const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; + +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_LANGUAGE = 'en'; +const NET_REVENUE = true; + +const BANNER_DEFAULTS = { + SIZE: [300, 250] +} + +const VIDEO_DEFAULTS = { + PROTOCOLS: [2, 3, 5, 6], + MIMES: ['video/mp4'], + PLAYBACK_METHODS: [1, 2, 3, 4], + DELIVERY: [1], + API: [1, 2, 5], + SIZE: [640, 480] +} + +const NATIVE_DEFAULTS = { + IMAGE_TYPE: { + ICON: 1, + MAIN: 3, + }, + ASSET_ID: { + TITLE: 1, + IMAGE: 2, + ICON: 3, + BODY: 4, + SPONSORED: 5, + CTA: 6 + }, + DATA_ASSET_TYPE: { + SPONSORED: 1, + DESC: 2, + CTA_TEXT: 12, + }, + LENGTH: { + TITLE: 90, + BODY: 140, + SPONSORED: 25, + CTA: 20 + } +} + +export const spec = { + code: BIDDER_CODE, + + // short code + aliases: ['opera'], + + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid + * @returns boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + if (!bid) { + utils.logWarn(BIDDER_CODE, 'Invalid bid,', bid); + return false; + } + + if (!bid.params) { + utils.logWarn(BIDDER_CODE, 'bid.params is required.') + return false; + } + + if (!bid.params.placementId) { + utils.logWarn(BIDDER_CODE, 'bid.params.placementId is required.') + return false; + } + + if (!bid.params.endpointId) { + utils.logWarn(BIDDER_CODE, 'bid.params.endpointId is required.') + return false; + } + + if (!bid.params.publisherId) { + utils.logWarn(BIDDER_CODE, 'bid.params.publisherId is required.') + return false; + } + + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} validBidRequests An array of bidRequest objects + * @param {bidderRequest} bidderRequest The master bidRequest object. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + return validBidRequests.map(validBidRequest => (buildOpenRtbBidRequest(validBidRequest, bidderRequest))) + }, + + /** + * 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) { + let bidResponses = []; + + let serverBody; + if ((serverBody = serverResponse.body) && serverBody.seatbid && utils.isArray(serverBody.seatbid)) { + serverBody.seatbid.forEach((seatbidder) => { + if (seatbidder.bid && utils.isArray(seatbidder.bid)) { + bidResponses = seatbidder.bid.map((bid) => buildBidResponse(bid, bidRequest.originalBidRequest, serverBody)); + } + }); + } + + 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) { + return []; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * + * @param {data} timeoutData Containing timeout specific data + */ + onTimeout: function (timeoutData) { }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * + * @param {Bid} bid The bid that won the auction + */ + onBidWon: function (bid) { + if (!bid || !utils.isStr(bid.nurl)) { + return; + } + + let winCpm, winCurr; + if (Object.prototype.hasOwnProperty.call(bid, 'originalCpm')) { + winCpm = bid.originalCpm; + winCurr = bid.originalCurrency; + } else { + winCpm = bid.cpm; + winCurr = bid.currency; + } + + utils.triggerPixel( + bid.nurl + .replace(/\$\{AUCTION_PRICE\}/g, winCpm) + .replace(/\$\{AUCTION_CURRENCY\}/g, winCurr) + ); + }, + + /** + * Register bidder specific code, which will execute when the adserver targeting has been set for a bid from this bidder + * + * @param {Bid} bid The bid of which the targeting has been set + */ + onSetTargeting: function (bid) { } +} + +/** + * Buid openRtb request from bidRequest and bidderRequest + * + * @param {BidRequest} bidRequest + * @param {BidderRequest} bidderRequest + * @returns {Request} + */ +function buildOpenRtbBidRequest(bidRequest, bidderRequest) { + const currencies = getCurrencies(bidRequest); + + const pageReferrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + + // build OpenRTB request body + const payload = { + id: bidderRequest.auctionId, + tmax: bidderRequest.timeout || config.getConfig('bidderTimeout'), + test: config.getConfig('debug') ? 1 : 0, + imp: createImp(bidRequest, currencies[0]), + device: getDevice(), + site: { + id: String(utils.deepAccess(bidRequest, 'params.publisherId')), + domain: getDomain(pageReferrer), + page: pageReferrer, + ref: window.self === window.top ? document.referrer : '', + }, + at: 1, + bcat: getBcat(bidRequest), + cur: currencies, + regs: { + coppa: config.getConfig('coppa') ? 1 : 0, + ext: {} + }, + user: { + id: getUserId(bidRequest) + } + } + + const gdprConsent = utils.deepAccess(bidderRequest, 'gdprConsent'); + if (!!gdprConsent && gdprConsent.gdprApplies) { + utils.deepSetValue(payload, 'regs.ext.gdpr', 1); + utils.deepSetValue(payload, 'user.ext.consent', gdprConsent.consentString); + } + + const uspConsent = utils.deepAccess(bidderRequest, 'uspConsent'); + if (uspConsent) { + utils.deepSetValue(payload, 'regs.ext.us_privacy', uspConsent); + } + + const eids = utils.deepAccess(bidRequest, 'userIdAsEids', []); + if (eids.length > 0) { + utils.deepSetValue(payload, 'user.eids', eids); + } + + return { + method: 'POST', + url: ENDPOINT + String(utils.deepAccess(bidRequest, 'params.publisherId')) + + '?ep=' + String(utils.deepAccess(bidRequest, 'params.endpointId')), + data: JSON.stringify(payload), + options: { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': 2.5 + } + }, + // set original bid request, so we can get it from interpretResponse + originalBidRequest: bidRequest + } +} + +/** + * Build bid response from openrtb bid response. + * + * @param {OpenRtbBid} bid + * @param {BidRequest} bidRequest + * @param {OpenRtbResponseBody} responseBody + * @returns {BidResponse} + */ +function buildBidResponse(bid, bidRequest, responseBody) { + let mediaType = BANNER; + let nativeResponse; + + if (/VAST\s+version/.test(bid.adm)) { + mediaType = VIDEO; + } else { + let markup; + try { + markup = JSON.parse(bid.adm); + } catch (e) { + markup = null; + } + + // OpenRtb Markup Response Object + // https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf#5.1 + if (markup && utils.isPlainObject(markup.native)) { + mediaType = NATIVE; + nativeResponse = markup.native; + } + } + + const currency = responseBody.cur || DEFAULT_CURRENCY; + const cpm = (parseFloat(bid.price) || 0).toFixed(2); + + const categories = utils.deepAccess(bid, 'cat', []); + + const bidResponse = { + requestId: bid.impid, + cpm: cpm, + currency: currency, + mediaType: mediaType, + ttl: 300, + creativeId: bid.crid || bid.id, + netRevenue: NET_REVENUE, + nurl: bid.nurl, + lurl: bid.lurl, + meta: { + mediaType: mediaType, + primaryCatId: categories[0], + secondaryCatIds: categories.slice(1), + } + }; + + if (bid.adomain && utils.isArray(bid.adomain) && bid.adomain.length > 0) { + bidResponse.meta.advertiserDomains = bid.adomain; + bidResponse.meta.clickUrl = bid.adomain[0]; + } + + switch (mediaType) { + case VIDEO: { + const playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize', VIDEO_DEFAULTS.SIZE); + const size = canonicalizeSizesArray(playerSize)[0]; + + bidResponse.vastXml = bid.adm; + + bidResponse.width = bid.w || size[0]; + bidResponse.height = bid.h || size[1]; + + const context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); + + // if outstream video, add a default render for it. + if (context === OUTSTREAM) { + // fill adResponse, will be used in ANOutstreamVideo.renderAd + bidResponse.adResponse = { + content: bidResponse.vastXml, + width: bidResponse.width, + height: bidResponse.height, + player_width: size[0], + player_height: size[1], + }; + bidResponse.renderer = createRenderer(bidRequest); + } + break; + } + case NATIVE: { + bidResponse.native = interpretNativeAd(nativeResponse, currency, cpm); + break; + } + default: { + bidResponse.ad = bid.adm; + + bidResponse.width = bid.w; + bidResponse.height = bid.h; + } + } + return bidResponse; +} + +/** + * Convert OpenRtb native response to bid native object. + * + * @param {OpenRtbNativeResponse} nativeResponse + * @param {String} currency + * @param {String} cpm + * @returns {BidNative} native + */ +function interpretNativeAd(nativeResponse, currency, cpm) { + const native = {}; + + // OpenRtb Link Object + // https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf#5.7 + const clickUrl = utils.deepAccess(nativeResponse, 'link.url'); + if (clickUrl && utils.isStr(clickUrl)) { + native.clickUrl = decodeURIComponent(clickUrl); + } + + const clickTrackers = utils.deepAccess(nativeResponse, 'link.clicktrackers'); + if (clickTrackers && utils.isArray(clickTrackers)) { + native.clickTrackers = clickTrackers + .filter(Boolean) + .map( + url => decodeURIComponent(url) + .replace(/\$\{AUCTION_PRICE\}/g, cpm) + .replace(/\$\{AUCTION_CURRENCY\}/g, currency) + ); + } + + if (nativeResponse.imptrackers && utils.isArray(nativeResponse.imptrackers)) { + native.impressionTrackers = nativeResponse.imptrackers + .filter(Boolean) + .map( + url => decodeURIComponent(url) + .replace(/\$\{AUCTION_PRICE\}/g, cpm) + .replace(/\$\{AUCTION_CURRENCY\}/g, currency) + ); + } + + if (nativeResponse.jstracker && utils.isStr(nativeResponse.jstracker)) { + native.javascriptTrackers = [nativeResponse.jstracker]; + } + + let assets; + if ((assets = nativeResponse.assets) && utils.isArray(assets)) { + assets.forEach((asset) => { + switch (asset.id) { + case NATIVE_DEFAULTS.ASSET_ID.TITLE: { + const title = utils.deepAccess(asset, 'title.text'); + if (title) { + native.title = title; + } + break; + } + case NATIVE_DEFAULTS.ASSET_ID.IMAGE: { + if (asset.img) { + native.image = { + url: decodeURIComponent(asset.img.url), + width: asset.img.w, + height: asset.img.h + } + } + break; + } + case NATIVE_DEFAULTS.ASSET_ID.ICON: { + if (asset.img) { + native.icon = { + url: decodeURIComponent(asset.img.url), + width: asset.img.w, + height: asset.img.h + } + } + break; + } + case NATIVE_DEFAULTS.ASSET_ID.BODY: { + const body = utils.deepAccess(asset, 'data.value'); + if (body) { + native.body = body; + } + break; + } + case NATIVE_DEFAULTS.ASSET_ID.SPONSORED: { + const sponsoredBy = utils.deepAccess(asset, 'data.value'); + if (sponsoredBy) { + native.sponsoredBy = sponsoredBy; + } + break; + } + case NATIVE_DEFAULTS.ASSET_ID.CTA: { + const cta = utils.deepAccess(asset, 'data.value'); + if (cta) { + native.cta = cta; + } + break; + } + } + }); + } + + return native; +} + +/** + * Create an imp array + * + * @param {BidRequest} bidRequest + * @param {Currency} cur + * @returns {Imp[]} + */ +function createImp(bidRequest, cur) { + const imp = []; + + const floor = getBidFloor(bidRequest, cur); + + const impItem = { + id: bidRequest.bidId, + tagid: String(utils.deepAccess(bidRequest, 'params.placementId')), + bidfloor: floor, + }; + + let mediaType; + let bannerReq, videoReq, nativeReq; + + if ((bannerReq = utils.deepAccess(bidRequest, 'mediaTypes.banner'))) { + const size = canonicalizeSizesArray(bannerReq.sizes || BANNER_DEFAULTS.SIZE)[0]; + + impItem.banner = { + w: size[0], + h: size[1], + pos: 0, + }; + + mediaType = BANNER; + } else if ((videoReq = utils.deepAccess(bidRequest, 'mediaTypes.video'))) { + const size = canonicalizeSizesArray(videoReq.playerSize || VIDEO_DEFAULTS.SIZE)[0]; + + impItem.video = { + w: size[0], + h: size[1], + pos: 0, + mimes: videoReq.mimes || VIDEO_DEFAULTS.MIMES, + protocols: videoReq.protocols || VIDEO_DEFAULTS.PROTOCOLS, + startdelay: typeof videoReq.startdelay === 'number' ? videoReq.startdelay : 0, + skip: typeof videoReq.skip === 'number' ? videoReq.skip : 0, + playbackmethod: videoReq.playbackmethod || VIDEO_DEFAULTS.PLAYBACK_METHODS, + delivery: videoReq.delivery || VIDEO_DEFAULTS.DELIVERY, + api: videoReq.api || VIDEO_DEFAULTS.API, + placement: videoReq.context === OUTSTREAM ? 3 : 1, + }; + + mediaType = VIDEO; + } else if ((nativeReq = utils.deepAccess(bidRequest, 'mediaTypes.native'))) { + const params = bidRequest.nativeParams || nativeReq; + + const request = { + native: { + ver: '1.1', + assets: createNativeAssets(params), + } + }; + + impItem.native = { + ver: '1.1', + request: JSON.stringify(request), + }; + + mediaType = NATIVE; + } + + if (mediaType) { + imp.push(impItem); + } + + return imp; +} + +/** + * Convert bid sizes to size array + * + * @param {Size[]|Size[][]} sizes + * @returns {Size[][]} + */ +function canonicalizeSizesArray(sizes) { + if (sizes.length === 2 && !utils.isArray(sizes[0])) { + return [sizes]; + } + return sizes; +} + +/** + * Create Assets Object for Native request + * + * @param {Object} params + * @returns {Asset[]} + */ +function createNativeAssets(params) { + const assets = []; + + if (params.title) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.TITLE, + required: params.title.required ? 1 : 0, + title: { + len: params.title.len || NATIVE_DEFAULTS.LENGTH.TITLE + } + }) + } + + if (params.image) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.IMAGE, + required: params.image.required ? 1 : 0, + img: mapNativeImage(params.image, NATIVE_DEFAULTS.IMAGE_TYPE.MAIN) + }) + } + + if (params.icon) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.ICON, + required: params.icon.required ? 1 : 0, + img: mapNativeImage(params.icon, NATIVE_DEFAULTS.IMAGE_TYPE.ICON) + }) + } + + if (params.sponsoredBy) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.SPONSORED, + required: params.sponsoredBy.required ? 1 : 0, + data: { + type: NATIVE_DEFAULTS.DATA_ASSET_TYPE.SPONSORED, + len: params.sponsoredBy.len | NATIVE_DEFAULTS.LENGTH.SPONSORED + } + }) + } + + if (params.body) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.BODY, + required: params.body.required ? 1 : 0, + data: { + type: NATIVE_DEFAULTS.DATA_ASSET_TYPE.DESC, + len: params.body.len || NATIVE_DEFAULTS.LENGTH.BODY + } + }) + } + + if (params.cta) { + assets.push({ + id: NATIVE_DEFAULTS.ASSET_ID.CTA, + required: params.cta.required ? 1 : 0, + data: { + type: NATIVE_DEFAULTS.DATA_ASSET_TYPE.CTA_TEXT, + len: params.cta.len || NATIVE_DEFAULTS.LENGTH.CTA + } + }) + } + + return assets; +} + +/** + * Create native image object + * + * @param {Object} image + * @param {Number} type + * @returns {NativeImage} + */ +function mapNativeImage(image, type) { + const img = { type: type }; + + if (image.aspect_ratios) { + const ratio = image.aspect_ratios[0]; + const minWidth = ratio.min_width || 100; + + img.wmin = minWidth; + img.hmin = (minWidth / ratio.ratio_width * ratio.ratio_height); + } + + if (image.sizes) { + const size = canonicalizeSizesArray(image.sizes)[0]; + + img.w = size[0]; + img.h = size[1]; + } + + return img; +} + +/** + * Get user id from bid request. if no user id module used, return a new uuid. + * + * @param {BidRequest} bidRequest + * @returns {String} userId + */ +function getUserId(bidRequest) { + let sharedId = utils.deepAccess(bidRequest, 'userId.sharedid.id'); + if (sharedId) { + return sharedId; + } + + for (const idModule of ['pubcid', 'tdid']) { + let userId = utils.deepAccess(bidRequest, `userId.${idModule}`); + if (userId) { + return userId; + } + } + + return utils.generateUUID(); +} + +/** + * Get publisher domain + * + * @param {String} referer + * @returns {String} domain + */ +function getDomain(referer) { + let domain; + + if (!(domain = config.getConfig('publisherDomain'))) { + const u = utils.parseUrl(referer); + domain = u.hostname; + } + + return domain.replace(/^https?:\/\/([\w\-\.]+)(?::\d+)?/, '$1'); +} + +/** + * Get bid floor price + * + * @param {BidRequest} bid + * @param {String} cur + * @returns {Number} floor price + */ +function getBidFloor(bid, cur) { + let floorInfo = {}; + + if (typeof bid.getFloor === 'function') { + floorInfo = bid.getFloor({ + currency: cur, + mediaType: '*', + size: '*' + }); + } + + return floorInfo.floor || 0.0; +} + +/** + * Get currencies from bid request + * + * @param {BidRequest} bidRequest + * @returns {String[]} currencies + */ +function getCurrencies(bidRequest) { + let currencies = []; + + const pCur = utils.deepAccess(bidRequest, 'params.currency'); + if (pCur) { + currencies = currencies.concat(pCur); + } + + if (!currencies.length) { + let currency; + if ((currency = config.getConfig('currency')) && currency.adServerCurrency) { + currencies.push(currency.adServerCurrency); + } else { + currencies.push(DEFAULT_CURRENCY); + } + } + + return currencies; +} + +/** + * Get bcat + * + * @param {BidRequest} bidRequest + * @returns {String[]} + */ +function getBcat(bidRequest) { + let bcat = []; + + const pBcat = utils.deepAccess(bidRequest, 'params.bcat'); + if (pBcat) { + bcat = bcat.concat(pBcat); + } + + return bcat; +} + +/** + * Get device info + * + * @returns {Object} + */ +function getDevice() { + const device = config.getConfig('device') || {}; + + device.w = device.w || window.screen.width; + device.h = device.h || window.screen.height; + device.ua = device.ua || navigator.userAgent; + device.language = device.language || getLanguage(); + device.dnt = typeof device.dnt === 'number' + ? device.dnt : (utils.getDNT() ? 1 : 0); + + return device; +} + +/** + * Get browser language + * + * @returns {String} language + */ +function getLanguage() { + const lang = (navigator.languages && navigator.languages[0]) || + navigator.language || navigator.userLanguage; + return lang ? lang.split('-')[0] : DEFAULT_LANGUAGE; +} + +/** + * Create render for outstream video. + * + * @param {BidRequest} bidRequest + * @returns + */ +function createRenderer(bidRequest) { + const globalRenderer = utils.deepAccess(bidRequest, 'renderer'); + const currentRenderer = utils.deepAccess(bidRequest, 'mediaTypes.video.renderer'); + + let url = OUTSTREAM_RENDERER_URL; + let config = {}; + let render = function (bid) { + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + sizes: [bid.width, bid.height], + targetId: bid.adUnitCode, + adResponse: bid.adResponse, + }); + }); + }; + + if (currentRenderer) { + url = currentRenderer.url; + config = currentRenderer.options; + render = currentRenderer.render; + } else if (globalRenderer) { + url = globalRenderer.url; + config = globalRenderer.options; + render = globalRenderer.render; + } + + const renderer = Renderer.install({ + id: bidRequest.bidId, + url: url, + loaded: false, + config: config, + adUnitCode: bidRequest.adUnitCode + }); + + try { + renderer.setRender(render); + } catch (e) { + utils.logError(BIDDER_CODE, 'Error calling setRender on renderer', e); + } + return renderer; +} + +registerBidder(spec); diff --git a/modules/operaadsBidAdapter.md b/modules/operaadsBidAdapter.md new file mode 100644 index 00000000000..9bfe3e76b88 --- /dev/null +++ b/modules/operaadsBidAdapter.md @@ -0,0 +1,155 @@ +# OperaAds Bidder Adapter + +## Overview + +``` +Module Name: OperaAds Bidder Adapter +Module Type: Bidder Adapter +Maintainer: adtech-prebid-group@opera.com +``` + +## Description + +Module that connects to OperaAds's demand sources + +## Bid Parameters + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `placementId` | required | String | The Placement Id provided by Opera Ads. | `s12345678` +| `endpointId` | required | String | The Endpoint Id provided by Opera Ads. | `ep12345678` +| `publisherId` | required | String | The Publisher Id provided by Opera Ads. | `pub12345678` +| `currency` | optional | String or String[] | Currency. | `USD` +| `bcat` | optional | String or String[] | The bcat value. | `IAB9-31` + +### Bid Video Parameters + +Set these parameters to `bid.mediaTypes.video`. + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `context` | optional | String | `instream` or `outstream`. | `instream` +| `mimes` | optional | String[] | Content MIME types supported. | `['video/mp4']` +| `playerSize` | optional | Number[] or Number[][] | Video player size in device independent pixels | `[[640, 480]]` +| `protocols` | optional | Number[] | Array of supported video protocls. | `[1, 2, 3, 4, 5, 6, 7, 8]` +| `startdelay` | optional | Number | Indicates the start delay in seconds for pre-roll, mid-roll, or post-roll ad placements. | `0` +| `skip` | optional | Number | Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes. | `1` +| `playbackmethod` | optional | Number[] | Playback methods that may be in use. | `[2]` +| `delivery` | optional | Number[] | Supported delivery methods. | `[1]` +| `api` | optional | Number[] | List of supported API frameworks for this impression. | `[1, 2, 5]` + +### Bid Native Parameters + +Set these parameters to `bid.nativeParams` or `bid.mediaTypes.native`. + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `title` | optional | Object | Config for native asset title. | `{required: true, len: 25}` +| `image` | optional | Object | Config for native asset image. | `{required: true, sizes: [[300, 250]], aspect_ratios: [{min_width: 300, min_height: 250, ratio_width: 1, ratio_height: 1}]}` +| `icon` | optional | Object | Config for native asset icon. | `{required: true, sizes: [[60, 60]], aspect_ratios: [{min_width: 60, min_height: 60, ratio_width: 1, ratio_height: 1}]}}` +| `sponsoredBy` | optional | Object | Config for native asset sponsoredBy. | `{required: true, len: 20}` +| `body` | optional | Object | Config for native asset body. | `{required: true, len: 200}` +| `cta` | optional | Object | Config for native asset cta. | `{required: true, len: 20}` + +## Example + +### Banner Ads + +```javascript +var adUnits = [{ + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'operaads', + params: { + placementId: 's12345678', + endpointId: 's12345678', + publisherId: 's12345678' + } + }] +}]; +``` + +### Video Ads + +```javascript +var adUnits = [{ + code: 'video-ad-div', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + } + }, + bids: [{ + bidder: 'operaads', + params: { + placementId: 's12345678', + endpointId: 's12345678', + publisherId: 's12345678' + } + }] +}]; +``` + +* For video ads, enable prebid cache. + +```javascript +pbjs.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } +}); +``` + +### Native Ads + +```javascript +var adUnits = [{ + code: 'native-ad-div', + mediaTypes: { + native: { + title: { required: true, len: 75 }, + image: { required: true, sizes: [[300, 250]] }, + body: { len: 200 }, + sponsoredBy: { len: 20 } + } + }, + bids: [{ + bidder: 'operaads', + params: { + placementId: 's12345678', + endpointId: 's12345678', + publisherId: 's12345678' + } + }] +}]; +``` + +### User Ids + +Opera Ads Bid Adapter uses `sharedId`, `pubcid` or `tdid`, please config at least one. + +```javascript +pbjs.setConfig({ + ..., + userSync: { + userIds: [{ + name: 'sharedId', + storage: { + name: '_sharedID', // name of the 1st party cookie + type: 'cookie', + expires: 30 + } + }] + } +}); +``` diff --git a/test/spec/modules/operaadsBidAdapter_spec.js b/test/spec/modules/operaadsBidAdapter_spec.js new file mode 100644 index 00000000000..92e51c5473d --- /dev/null +++ b/test/spec/modules/operaadsBidAdapter_spec.js @@ -0,0 +1,698 @@ +import { expect } from 'chai'; +import { spec } from 'modules/operaadsBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; + +describe('Opera Ads Bid Adapter', function () { + describe('Test isBidRequestValid', function () { + it('undefined bid should return false', function () { + expect(spec.isBidRequestValid()).to.be.false; + }); + + it('null bid should return false', function () { + expect(spec.isBidRequestValid(null)).to.be.false; + }); + + it('bid.params should be set', function () { + expect(spec.isBidRequestValid({})).to.be.false; + }); + + it('bid.params.placementId should be set', function () { + expect(spec.isBidRequestValid({ + params: { endpointId: 'ep12345678', publisherId: 'pub12345678' } + })).to.be.false; + }); + + it('bid.params.publisherId should be set', function () { + expect(spec.isBidRequestValid({ + params: { placementId: 's12345678', endpointId: 'ep12345678' } + })).to.be.false; + }); + + it('bid.params.endpointId should be set', function () { + expect(spec.isBidRequestValid({ + params: { placementId: 's12345678', publisherId: 'pub12345678' } + })).to.be.false; + }); + + it('valid bid should return true', function () { + expect(spec.isBidRequestValid({ + params: { placementId: 's12345678', endpointId: 'ep12345678', publisherId: 'pub12345678' } + })).to.be.true; + }); + }); + + describe('Test buildRequests', function () { + const bidderRequest = { + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + auctionStart: Date.now(), + bidderCode: 'myBidderCode', + bidderRequestId: '15246a574e859f', + refererInfo: { + referer: 'http://example.com', + stack: ['http://example.com'] + }, + gdprConsent: { + gdprApplies: true, + consentString: 'IwuyYwpjmnsauyYasIUWwe' + }, + uspConsent: 'Oush3@jmUw82has', + timeout: 3000 + }; + + it('build request object', function () { + const bidRequests = [ + { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + banner: { sizes: [[300, 250]] } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }, + { + adUnitCode: 'test-native', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f4622', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + native: { + title: { + required: true, + len: 20, + }, + image: { + required: true, + sizes: [300, 250], + aspect_ratios: [{ + ratio_width: 1, + ratio_height: 1 + }] + }, + icon: { + required: true, + sizes: [60, 60], + aspect_ratios: [{ + ratio_width: 1, + ratio_height: 1 + }] + }, + sponsoredBy: { + required: true, + len: 20 + }, + body: { + required: true, + len: 140 + }, + cta: { + required: true, + len: 20, + } + } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }, + { + adUnitCode: 'test-native2', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f4632', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + native: { + title: {}, + image: {}, + icon: {}, + sponsoredBy: {}, + body: {}, + cta: {} + } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }, + { + adUnitCode: 'test-native3', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f4633', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + native: {}, + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }, + { + adUnitCode: 'test-video', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f4623', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [2, 3, 5, 6], + startdelay: 0, + skip: 1, + playbackmethod: [1, 2, 3, 4], + delivery: [1], + api: [1, 2, 5], + } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }, + { + adUnitCode: 'test-video', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f4643', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + video: {} + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + } + ]; + + let reqs; + + expect(function () { + reqs = spec.buildRequests(bidRequests, bidderRequest); + }).to.not.throw(); + + expect(reqs).to.be.an('array').that.have.lengthOf(bidRequests.length); + + for (let i = 0, len = reqs.length; i < len; i++) { + const req = reqs[i]; + const bidRequest = bidRequests[i]; + + expect(req.method).to.equal('POST'); + expect(req.url).to.equal('https://s.adx.opera.com/ortb/v2/' + + bidRequest.params.publisherId + '?ep=' + bidRequest.params.endpointId); + + expect(req.options).to.be.an('object'); + expect(req.options.contentType).to.contain('application/json'); + expect(req.options.customHeaders).to.be.an('object'); + expect(req.options.customHeaders['x-openrtb-version']).to.equal(2.5); + + expect(req.originalBidRequest).to.equal(bidRequest); + + expect(req.data).to.be.a('string'); + + let requestData; + expect(function () { + requestData = JSON.parse(req.data); + }).to.not.throw(); + + expect(requestData.id).to.equal(bidderRequest.auctionId); + expect(requestData.tmax).to.equal(bidderRequest.timeout); + expect(requestData.test).to.equal(0); + expect(requestData.imp).to.be.an('array').that.have.lengthOf(1); + expect(requestData.device).to.be.an('object'); + expect(requestData.site).to.be.an('object'); + expect(requestData.site.id).to.equal(bidRequest.params.publisherId); + expect(requestData.site.domain).to.not.be.empty; + expect(requestData.site.page).to.equal(bidderRequest.refererInfo.referer); + expect(requestData.at).to.equal(1); + expect(requestData.bcat).to.be.an('array').that.is.empty; + expect(requestData.cur).to.be.an('array').that.not.be.empty; + expect(requestData.user).to.be.an('object'); + + let impItem = requestData.imp[0]; + expect(impItem).to.be.an('object'); + expect(impItem.id).to.equal(bidRequest.bidId); + expect(impItem.tagid).to.equal(bidRequest.params.placementId); + expect(impItem.bidfloor).to.be.a('number'); + + if (bidRequest.mediaTypes.banner) { + expect(impItem.banner).to.be.an('object'); + } else if (bidRequest.mediaTypes.native) { + expect(impItem.native).to.be.an('object'); + } else if (bidRequest.mediaTypes.video) { + expect(impItem.video).to.be.an('object'); + } else { + expect.fail('should not happen'); + } + } + }); + + it('currency in params should be used', function () { + const bidRequests = [ + { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678', + currency: 'RMB' + } + } + ]; + + const reqs = spec.buildRequests(bidRequests, bidderRequest); + + expect(reqs).to.be.an('array').that.have.lengthOf(1); + + for (const req of reqs) { + let requestData; + expect(function () { + requestData = JSON.parse(req.data); + }).to.not.throw(); + + expect(requestData.cur).to.be.an('array').that.includes('RMB'); + } + }); + + it('bcat in params should be used', function () { + const bidRequests = [ + { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678', + bcat: ['IAB1-1'] + } + } + ]; + + const reqs = spec.buildRequests(bidRequests, bidderRequest); + + expect(reqs).to.be.an('array').that.have.lengthOf(1); + + for (const req of reqs) { + let requestData; + expect(function () { + requestData = JSON.parse(req.data); + }).to.not.throw(); + + expect(requestData.bcat).to.be.an('array').that.includes('IAB1-1'); + } + }); + + it('sharedid should be used', function () { + const bidRequests = [{ + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + banner: { sizes: [[300, 250]] } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + }, + userId: { + sharedid: { + id: '01F5DEQW731Q2VKT031KBKMW5W' + } + }, + userIdAsEids: [{ + source: 'pubcid.org', + uids: [{ + atype: 1, + id: '01F5DEQW731Q2VKT031KBKMW5W' + }] + }] + }]; + + const reqs = spec.buildRequests(bidRequests, bidderRequest); + + let requestData; + expect(function () { + requestData = JSON.parse(reqs[0].data); + }).to.not.throw(); + + expect(requestData.user.id).to.equal(bidRequests[0].userId.sharedid.id); + }); + + it('pubcid should be used when sharedid is empty', function () { + const bidRequests = [{ + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + banner: { sizes: [[300, 250]] } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + }, + userId: { + 'pubcid': '21F5DEQW731Q2VKT031KBKMW5W' + }, + userIdAsEids: [{ + source: 'pubcid.org', + uids: [{ + atype: 1, + id: '21F5DEQW731Q2VKT031KBKMW5W' + }] + }] + }]; + + const reqs = spec.buildRequests(bidRequests, bidderRequest); + + let requestData; + expect(function () { + requestData = JSON.parse(reqs[0].data); + }).to.not.throw(); + + expect(requestData.user.id).to.equal(bidRequests[0].userId.pubcid); + }); + + it('random uid will be generate when userId is empty', function () { + const bidRequests = [{ + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { + banner: { sizes: [[300, 250]] } + }, + params: { + placementId: 's12345678', + publisherId: 'pub12345678', + endpointId: 'ep12345678' + } + }]; + + const reqs = spec.buildRequests(bidRequests, bidderRequest); + + let requestData; + expect(function () { + requestData = JSON.parse(reqs[0].data); + }).to.not.throw(); + + expect(requestData.user.id).to.not.be.empty; + }) + }); + + describe('Test adapter request', function () { + const adapter = newBidder(spec); + + it('adapter.callBids exists and is a function', function () { + expect(adapter.callBids).to.be.a('function'); + }); + }); + + describe('Test response interpretResponse', function () { + it('Test banner interpretResponse', function () { + const serverResponse = { + body: { + 'id': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + 'seatbid': [ + { + 'bid': [ + { + 'id': '003004d9c05c6bc7fec0', + 'impid': '22c4871113f461', + 'price': 1.04, + 'nurl': 'https://s.adx.opera.com/win', + 'lurl': 'https://s.adx.opera.com/loss', + 'adm': '', + 'adomain': [ + 'opera.com', + ], + 'cid': '0.49379027', + 'crid': '0.49379027', + 'cat': [ + 'IAB9-31', + 'IAB8' + ], + 'language': 'EN', + 'h': 300, + 'w': 250, + 'exp': 500, + 'ext': {} + } + ], + 'seat': 'adv4199760017536' + } + ], + 'bidid': '003004d9c05c6bc7fec0', + 'cur': 'USD' + } + }; + + const bidResponses = spec.interpretResponse(serverResponse, { + originalBidRequest: { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + placementId: 's12345678', + publisherId: 'pub123456', + endpointId: 'ep1234566' + }, + src: 'client', + transactionId: '4781e6ac-93c4-42ba-86fe-ab5f278863cf' + } + }); + + expect(bidResponses).to.be.an('array').that.is.not.empty; + + const bid = serverResponse.body.seatbid[0].bid[0]; + const bidResponse = bidResponses[0]; + + expect(bidResponse.mediaType).to.equal(BANNER); + expect(bidResponse.requestId).to.equal(bid.impid); + expect(bidResponse.cpm).to.equal(parseFloat(bid.price).toFixed(2)) + expect(bidResponse.currency).to.equal(serverResponse.body.cur); + expect(bidResponse.creativeId).to.equal(bid.crid || bid.id); + expect(bidResponse.netRevenue).to.be.true; + expect(bidResponse.nurl).to.equal(bid.nurl); + expect(bidResponse.lurl).to.equal(bid.lurl); + + expect(bidResponse.meta).to.be.an('object'); + expect(bidResponse.meta.mediaType).to.equal(BANNER); + expect(bidResponse.meta.primaryCatId).to.equal('IAB9-31'); + expect(bidResponse.meta.secondaryCatIds).to.deep.equal(['IAB8']); + expect(bidResponse.meta.advertiserDomains).to.deep.equal(bid.adomain); + expect(bidResponse.meta.clickUrl).to.equal(bid.adomain[0]); + + expect(bidResponse.ad).to.equal(bid.adm); + expect(bidResponse.width).to.equal(bid.w); + expect(bidResponse.height).to.equal(bid.h); + }); + + it('Test video interpretResponse', function () { + const serverResponse = { + body: { + 'id': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + 'seatbid': [ + { + 'bid': [ + { + 'id': '003004d9c05c6bc7fec0', + 'impid': '22c4871113f461', + 'price': 1.04, + 'nurl': 'https://s.adx.opera.com/win', + 'lurl': 'https://s.adx.opera.com/loss', + 'adm': 'Static VAST TemplateStatic VAST Taghttp://example.com/pixel.gif?asi=[ADSERVINGID]00:00:08http://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://example.com/pixel.gifhttp://www.jwplayer.com/http://example.com/pixel.gif?r=[REGULATIONS]&gdpr=[GDPRCONSENT]&pu=[PAGEURL]&da=[DEVICEUA] http://example.com/uploads/myPrerollVideo.mp4 https://example.com/adchoices-sm.pnghttps://sample-url.com', + 'adomain': [ + 'opera.com', + ], + 'cid': '0.49379027', + 'crid': '0.49379027', + 'cat': [ + 'IAB9-31', + 'IAB8' + ], + 'language': 'EN', + 'h': 300, + 'w': 250, + 'exp': 500, + 'ext': {} + } + ], + 'seat': 'adv4199760017536' + } + ], + 'bidid': '003004d9c05c6bc7fec0', + 'cur': 'USD' + } + }; + + const bidResponses = spec.interpretResponse(serverResponse, { + originalBidRequest: { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { video: { context: 'outstream' } }, + params: { + placementId: 's12345678', + publisherId: 'pub123456', + endpointId: 'ep1234566' + }, + src: 'client', + transactionId: '4781e6ac-93c4-42ba-86fe-ab5f278863cf' + } + }); + + expect(bidResponses).to.be.an('array').that.is.not.empty; + + const bid = serverResponse.body.seatbid[0].bid[0]; + const bidResponse = bidResponses[0]; + + expect(bidResponse.mediaType).to.equal(VIDEO); + expect(bidResponse.vastXml).to.equal(bid.adm); + expect(bidResponse.width).to.equal(bid.w); + expect(bidResponse.height).to.equal(bid.h); + + expect(bidResponse.adResponse).to.be.an('object'); + expect(bidResponse.renderer).to.be.an('object'); + }); + + it('Test native interpretResponse', function () { + const serverResponse = { + body: { + 'id': 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + 'seatbid': [ + { + 'bid': [ + { + 'id': '003004d9c05c6bc7fec0', + 'impid': '22c4871113f461', + 'price': 1.04, + 'nurl': 'https://s.adx.opera.com/win', + 'lurl': 'https://s.adx.opera.com/loss', + 'adm': '{"native":{"ver":"1.1","assets":[{"id":1,"required":1,"title":{"text":"The first personal browser"}},{"id":2,"required":1,"img":{"url":"https://res.adx.opera.com/xxx.png","w":720,"h":1280}},{"id":3,"required":1,"img":{"url":"https://res.adx.opera.com/xxx.png","w":60,"h":60}},{"id":4,"required":1,"data":{"value":"Download Opera","len":14}},{"id":5,"required":1,"data":{"value":"Opera","len":5}},{"id":6,"required":1,"data":{"value":"Download","len":8}}],"link":{"url":"https://www.opera.com/mobile/opera","clicktrackers":["https://thirdpart-click.tracker.com","https://t-odx.op-mobile.opera.com/click"]},"imptrackers":["https://thirdpart-imp.tracker.com","https://t-odx.op-mobile.opera.com/impr"],"jstracker":""}}', + 'adomain': [ + 'opera.com', + ], + 'cid': '0.49379027', + 'crid': '0.49379027', + 'cat': [ + 'IAB9-31', + 'IAB8' + ], + 'language': 'EN', + 'h': 300, + 'w': 250, + 'exp': 500, + 'ext': {} + } + ], + 'seat': 'adv4199760017536' + } + ], + 'bidid': '003004d9c05c6bc7fec0', + 'cur': 'USD' + } + }; + + const bidResponses = spec.interpretResponse(serverResponse, { + originalBidRequest: { + adUnitCode: 'test-div', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidId: '22c4871113f461', + bidder: 'operaads', + bidderRequestId: '15246a574e859f', + mediaTypes: { native: { } }, + params: { + placementId: 's12345678', + publisherId: 'pub123456', + endpointId: 'ep1234566' + }, + src: 'client', + transactionId: '4781e6ac-93c4-42ba-86fe-ab5f278863cf' + } + }); + + expect(bidResponses).to.be.an('array').that.is.not.empty; + + const bidResponse = bidResponses[0]; + + expect(bidResponse.mediaType).to.equal(NATIVE) + expect(bidResponse.native).to.be.an('object'); + expect(bidResponse.native.clickUrl).is.not.empty; + expect(bidResponse.native.clickTrackers).to.have.lengthOf(2); + expect(bidResponse.native.impressionTrackers).to.have.lengthOf(2); + expect(bidResponse.native.javascriptTrackers).to.have.lengthOf(1); + }); + + it('Test empty server response', function () { + const bidResponses = spec.interpretResponse({}, {}); + + expect(bidResponses).to.be.an('array').that.is.empty; + }); + + it('Test empty bid response', function () { + const bidResponses = spec.interpretResponse({ body: { seatbid: null } }, {}); + + expect(bidResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('Test getUserSyncs', function () { + it('getUserSyncs should return empty array', function () { + expect(spec.getUserSyncs()).to.be.an('array').that.is.empty; + }); + }); + + describe('Test onTimeout', function () { + it('onTimeout should not throw', function () { + expect(spec.onTimeout()).to.not.throw; + }); + }); + + describe('Test onBidWon', function () { + it('onBidWon should not throw', function () { + expect(spec.onBidWon({nurl: '#', originalCpm: '1.04', currency: 'USD'})).to.not.throw; + }); + }); + + describe('Test onSetTargeting', function () { + it('onSetTargeting should not throw', function () { + expect(spec.onSetTargeting()).to.not.throw; + }); + }); +}); From 35736f9cdabf340ad06e83edcafa15de0b6ca51d Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 19 Jul 2021 10:52:41 -0700 Subject: [PATCH 1289/1476] add mantis (#7173) --- modules/mantisBidAdapter.js | 310 ++++++++++++++++++ test/spec/modules/mantisBidAdapter_spec.js | 361 +++++++++++++++++++++ 2 files changed, 671 insertions(+) create mode 100644 modules/mantisBidAdapter.js create mode 100644 test/spec/modules/mantisBidAdapter_spec.js diff --git a/modules/mantisBidAdapter.js b/modules/mantisBidAdapter.js new file mode 100644 index 00000000000..61b7c31c8e4 --- /dev/null +++ b/modules/mantisBidAdapter.js @@ -0,0 +1,310 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); + +function inIframe() { + try { + return window.self !== window.top && !window.mantis_link; + } catch (e) { + return true; + } +} +function pixel(url, parent) { + var img = document.createElement('img'); + img.src = url; + img.style.cssText = 'display:none !important;'; + (parent || document.body).appendChild(img); +} +export function onVisible(win, element, doOnVisible, time, pct) { + var started = null; + var notified = false; + var onNotVisible = null; + var whenNotVisible = function () { + if (notified && onNotVisible) { + onNotVisible(); + } + notified = false; + }; + var interval; + var listener; + var doCheck = function (winWidth, winHeight, rect) { + var hidden = typeof document.hidden !== 'undefined' && document.hidden; + if (rect.width == 0 || rect.height == 0 || hidden) { + return whenNotVisible(); + } + var minHeight = (rect.height * pct); + var minWidth = (rect.width * pct); + var inView = ( + ( + (rect.top < 0 && rect.bottom >= minHeight) || + (rect.top > 0 && (winHeight - rect.top) >= minHeight) + ) && + ( + (rect.left < 0 && rect.right >= minWidth) || + (rect.left > 0 && (winWidth - rect.left) >= minWidth) + ) + ); + if (!inView) { + return whenNotVisible(); + } + if (!started && time) { + started = Date.now(); + return whenNotVisible(); + } + if (time && Date.now() - started < time) { + return whenNotVisible(); + } + if (notified) { + return; + } + doOnVisible(function (ack) { + if (ack) { + notified = true; + } else { + interval && clearInterval(interval); + listener && listener(); + } + }, function (onHidden) { + onNotVisible = onHidden; + }); + }; + if (isAmp()) { + listener = win.context.observeIntersection(function (changes) { + changes.forEach(function (change) { + doCheck(change.rootBounds.width, change.rootBounds.height, change.boundingClientRect); + }); + }); + } + interval = setInterval(function () { + var winHeight = (win.innerHeight || document.documentElement.clientHeight); + var winWidth = (win.innerWidth || document.documentElement.clientWidth); + doCheck(winWidth, winHeight, element.getBoundingClientRect()); + }, 100); +} +function storeUuid(uuid) { + if (window.mantis_uuid) { + return false; + } + window.mantis_uuid = uuid; + if (storage.hasLocalStorage()) { + try { + storage.setDataInLocalStorage('mantis:uuid', uuid); + } catch (ex) { + } + } +} + +function onMessage(type, callback) { + window.addEventListener('message', function (event) { + if (event.data.mantis && event.data.type == type) { + callback(event.data.data); + } + }, false); +} +function isSendable(val) { + if (val === null || val === undefined) { + return false; + } + if (typeof val === 'string') { + return !(!val || /^\s*$/.test(val)); + } + if (typeof val === 'number') { + return !isNaN(val); + } + return true; +} +function isObject(value) { + return Object.prototype.toString.call(value) === '[object Object]'; +} +function isAmp() { + return typeof window.context === 'object' && (window.context.tagName === 'AMP-AD' || window.context.tagName === 'AMP-EMBED'); +} +function isSecure() { + return document.location.protocol === 'https:'; +} +function isArray(value) { + return Object.prototype.toString.call(value) === '[object Array]'; +} + +function jsonToQuery(data, chain, form) { + var parts = form || []; + for (var key in data) { + var queryKey = key; + if (chain) { + queryKey = chain + '[' + key + ']'; + } + var val = data[key]; + if (isArray(val)) { + for (var index = 0; index < val.length; index++) { + var akey = queryKey + '[' + index + ']'; + var aval = val[index]; + if (isObject(aval)) { + jsonToQuery(aval, akey, parts); + } + } + } else if (isObject(val) && val != data) { + jsonToQuery(val, queryKey, parts); + } else if (isSendable(val)) { + parts.push(queryKey + '=' + encodeURIComponent(val)); + } + } + return parts.join('&'); +} + +function buildMantisUrl(path, data, domain) { + var params = { + referrer: document.referrer, + tz: new Date().getTimezoneOffset(), + buster: new Date().getTime(), + secure: isSecure(), + version: 9 + }; + + if (window.mantis_uuid) { + params.uuid = window.mantis_uuid; + } else if (storage.hasLocalStorage()) { + var localUuid = storage.getDataFromLocalStorage('mantis:uuid'); + if (localUuid) { + params.uuid = localUuid; + } + } + if (!inIframe()) { + try { + params.title = window.top.document.title; + params.referrer = window.top.document.referrer; + params.url = window.top.document.location.href; + } catch (ex) { + } + } else { + params.iframe = true; + } + if (isAmp()) { + params.amp = true; + if (!params.url && window.context.canonicalUrl) { + params.url = window.context.canonicalUrl; + } + if (!params.url && window.context.location) { + params.url = window.context.location.href; + } + if (!params.referrer && window.context.referrer) { + params.referrer = window.context.referrer; + } + } + Object.keys(data).forEach(function (key) { + params[key] = data[key]; + }); + var query = jsonToQuery(params); + return (window.mantis_domain === undefined ? domain || 'https://mantodea.mantisadnetwork.com' : window.mantis_domain) + path + '?' + query; +} + +export const spec = { + code: 'mantis', + supportedMediaTypes: ['banner'], + isBidRequestValid: function (bid) { + return !!(bid.params.property && (bid.params.code || bid.params.zoneId || bid.params.zone)); + }, + buildRequests: function (validBidRequests, bidderRequest) { + var property = null; + validBidRequests.some(function (bid) { + if (bid.params.property) { + property = bid.params.property; + return true; + } + }); + const query = { + measurable: true, + usp: bidderRequest && bidderRequest.uspConsent, + bids: validBidRequests.map(function (bid) { + return { + bidId: bid.bidId, + config: bid.params, + sizes: bid.sizes.map(function (size) { + return {width: size[0], height: size[1]}; + }) + }; + }), + property: property + }; + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + // we purposefully do not track data for users in the EU + query.consent = false; + } + + return { + method: 'GET', + url: buildMantisUrl('/prebid/display', query) + '&foo', + data: '' + }; + }, + interpretResponse: function (serverResponse) { + storeUuid(serverResponse.body.uuid); + return serverResponse.body.ads.map(function (ad) { + return { + requestId: ad.bid, + cpm: ad.cpm, + width: ad.width, + height: ad.height, + ad: ad.html, + meta: { + advertiserDomains: ad.domains || [] + }, + ttl: ad.ttl || serverResponse.body.ttl || 86400, + creativeId: ad.view, + netRevenue: true, + currency: 'USD' + }; + }); + }, + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: buildMantisUrl('/prebid/iframe', {gdpr: gdprConsent, uspConsent: uspConsent}) + }]; + } + if (syncOptions.pixelEnabled) { + return [{ + type: 'image', + url: buildMantisUrl('/prebid/pixel', {gdpr: gdprConsent, uspConsent: uspConsent}) + }]; + } + } +}; + +export function sfPostMessage ($sf, width, height, callback) { + var viewed = false; + // eslint-disable-next-line no-undef + $sf.ext.register(width, height, function () { + // eslint-disable-next-line no-undef + if ($sf.ext.inViewPercentage() < 50 || viewed) { + return; + } + viewed = true; + callback(); + }); +}; + +export function iframePostMessage (win, name, callback) { + var frames = document.getElementsByTagName('iframe'); + for (var i = 0; i < frames.length; i++) { + var frame = frames[i]; + if (frame.name == name) { + onVisible(win, frame, function (stop) { + callback(); + stop(); + }, 1000, 0.50); + } + } +} + +onMessage('iframe', function (data) { + if (window.$sf) { + sfPostMessage(window.$sf, data.width, data.height, () => pixel(data.pixel)); + } else { + iframePostMessage(window, data.frame, () => pixel(data.pixel)); + } +}); + +registerBidder(spec); diff --git a/test/spec/modules/mantisBidAdapter_spec.js b/test/spec/modules/mantisBidAdapter_spec.js new file mode 100644 index 00000000000..579f41e620d --- /dev/null +++ b/test/spec/modules/mantisBidAdapter_spec.js @@ -0,0 +1,361 @@ +import {expect} from 'chai'; +import {spec, storage} from 'modules/mantisBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {sfPostMessage, iframePostMessage} from 'modules/mantisBidAdapter'; + +describe('MantisAdapter', function () { + const adapter = newBidder(spec); + const sandbox = sinon.sandbox.create(); + let clock; + + beforeEach(function () { + clock = sandbox.useFakeTimers(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'mantis', + 'params': { + 'property': '10433394', + 'zone': 'zone' + }, + '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 required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('viewability', function() { + it('iframe (viewed)', () => { + let viewed = false; + + sandbox.stub(document, 'getElementsByTagName').withArgs('iframe').returns([ + { + name: 'mantis', + getBoundingClientRect: () => ({ + top: 10, + bottom: 260, + left: 10, + right: 190, + width: 300, + height: 250 + }) + } + ]); + + iframePostMessage({innerHeight: 500, innerWidth: 500}, 'mantis', () => viewed = true); + + sandbox.clock.runAll(); + + expect(viewed).to.equal(true); + }); + + it('safeframe (viewed)', () => { + let viewed = false; + + sfPostMessage({ + ext: { + register: (width, height, callback) => { + expect(width).to.equal(100); + expect(height).to.equal(200); + + callback(); + }, + inViewPercentage: () => 60 + } + }, 100, 200, () => viewed = true); + + expect(viewed).to.equal(true); + }); + + it('safeframe (unviewed)', () => { + let viewed = false; + + sfPostMessage({ + ext: { + register: (width, height, callback) => { + expect(width).to.equal(100); + expect(height).to.equal(200); + + callback(); + }, + inViewPercentage: () => 30 + } + }, 100, 200, () => viewed = true); + + expect(viewed).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'mantis', + 'params': { + 'property': '10433394', + 'zone': 'zone' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('gdpr consent not required', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: false}}); + + expect(request.url).not.to.include('consent=false'); + }); + + it('gdpr consent required', function () { + const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: true}}); + + expect(request.url).to.include('consent=false'); + }); + + it('usp consent', function () { + const request = spec.buildRequests(bidRequests, {uspConsent: 'foobar'}); + + expect(request.url).to.include('usp=foobar'); + }); + + it('domain override', function () { + window.mantis_domain = 'https://foo'; + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.include('https://foo'); + + delete window.mantis_domain; + }); + + it('standard request', function () { + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.include('property=10433394'); + expect(request.url).to.include('bids[0][bidId]=30b31c1838de1e'); + expect(request.url).to.include('bids[0][config][zone]=zone'); + expect(request.url).to.include('bids[0][sizes][0][width]=300'); + expect(request.url).to.include('bids[0][sizes][0][height]=250'); + expect(request.url).to.include('bids[0][sizes][1][width]=300'); + expect(request.url).to.include('bids[0][sizes][1][height]=600'); + }); + + it('use window uuid', function () { + window.mantis_uuid = 'foo'; + + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.include('uuid=foo'); + + delete window.mantis_uuid; + }); + + it('use storage uuid', function () { + sandbox.stub(storage, 'hasLocalStorage').callsFake(() => true); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('mantis:uuid').returns('bar'); + + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.include('uuid=bar'); + }); + + it('detect amp', function () { + var oldContext = window.context; + + window.context = {}; + window.context.tagName = 'AMP-AD'; + window.context.canonicalUrl = 'foo'; + + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.include('amp=true'); + expect(request.url).to.include('url=foo'); + + delete window.context.tagName; + delete window.context.canonicalUrl; + + window.context = oldContext; + }); + }); + + describe('getUserSyncs', function () { + it('iframe', function () { + let result = spec.getUserSyncs({ + iframeEnabled: true + }); + + expect(result[0].type).to.equal('iframe'); + expect(result[0].url).to.include('https://mantodea.mantisadnetwork.com/prebid/iframe'); + }); + + it('pixel', function () { + let result = spec.getUserSyncs({ + pixelEnabled: true + }); + + expect(result[0].type).to.equal('image'); + expect(result[0].url).to.include('https://mantodea.mantisadnetwork.com/prebid/pixel'); + }); + }); + + describe('interpretResponse', function () { + it('use ad ttl if provided', function () { + let response = { + body: { + ttl: 360, + uuid: 'uuid', + ads: [ + { + bid: 'bid', + cpm: 1, + view: 'view', + width: 300, + ttl: 250, + height: 250, + html: '' + } + ] + } + }; + + let expectedResponse = [ + { + requestId: 'bid', + cpm: 1, + width: 300, + height: 250, + ttl: 250, + ad: '', + creativeId: 'view', + netRevenue: true, + meta: { + advertiserDomains: [] + }, + currency: 'USD' + } + ]; + let bidderRequest; + + let result = spec.interpretResponse(response, {bidderRequest}); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('use global ttl if provded', function () { + let response = { + body: { + ttl: 360, + uuid: 'uuid', + ads: [ + { + bid: 'bid', + cpm: 1, + view: 'view', + domains: ['foobar.com'], + width: 300, + height: 250, + html: '' + } + ] + } + }; + + let expectedResponse = [ + { + requestId: 'bid', + cpm: 1, + width: 300, + height: 250, + ttl: 360, + ad: '', + creativeId: 'view', + netRevenue: true, + meta: { + advertiserDomains: ['foobar.com'] + }, + currency: 'USD' + } + ]; + let bidderRequest; + + let result = spec.interpretResponse(response, {bidderRequest}); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('display ads returned', function () { + let response = { + body: { + uuid: 'uuid', + ads: [ + { + bid: 'bid', + cpm: 1, + view: 'view', + width: 300, + domains: ['foobar.com'], + height: 250, + html: '' + } + ] + } + }; + + let expectedResponse = [ + { + requestId: 'bid', + cpm: 1, + width: 300, + height: 250, + ttl: 86400, + ad: '', + creativeId: 'view', + netRevenue: true, + meta: { + advertiserDomains: ['foobar.com'] + }, + currency: 'USD' + } + ]; + let bidderRequest; + + sandbox.stub(storage, 'hasLocalStorage').returns(true); + const spy = sandbox.spy(storage, 'setDataInLocalStorage'); + + let result = spec.interpretResponse(response, {bidderRequest}); + + expect(spy.calledWith('mantis:uuid', 'uuid')); + expect(result[0]).to.deep.equal(expectedResponse[0]); + expect(window.mantis_uuid).to.equal(response.body.uuid); + }); + + it('no ads returned', function () { + let response = { + body: { + ads: [] + } + }; + let bidderRequest; + + let result = spec.interpretResponse(response, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); +}); From 980a22db764d47c01fd35a4a68ba022b1caaf3b9 Mon Sep 17 00:00:00 2001 From: jsut Date: Mon, 19 Jul 2021 15:53:27 -0400 Subject: [PATCH 1290/1476] Leverage defined methods from utils in secureCreatives (#7196) - use isGptPubadsDefined instead of rolling our own - add isApnGetTagDefined to utils, which does a deeper existence confirmation and start using it. --- src/secureCreatives.js | 6 +++--- src/utils.js | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/secureCreatives.js b/src/secureCreatives.js index a172ec62630..60e60688c64 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,7 +6,7 @@ import events from './events.js'; import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js'; import constants from './constants.json'; -import { logWarn, replaceAuctionPrice, deepAccess } from './utils.js'; +import { logWarn, replaceAuctionPrice, deepAccess, isGptPubadsDefined, isApnGetTagDefined } from './utils.js'; import { auctionManager } from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; @@ -118,9 +118,9 @@ function resizeRemoteCreative({ adId, adUnitCode, width, height }) { } function getElementIdBasedOnAdServer(adId, adUnitCode) { - if (window.googletag) { + if (isGptPubadsDefined()) { return getDfpElementId(adId) - } else if (window.apntag) { + } else if (isApnGetTagDefined()) { return getAstElementId(adUnitCode) } else { return adUnitCode; diff --git a/src/utils.js b/src/utils.js index 35ce3e7d0dd..03c76529ddf 100644 --- a/src/utils.js +++ b/src/utils.js @@ -665,6 +665,12 @@ export function isGptPubadsDefined() { } } +export function isApnGetTagDefined() { + if (window.apntag && isFn(window.apntag.getTag)) { + return true; + } +} + // This function will get highest cpm value bid, in case of tie it will return the bid with lowest timeToRespond export const getHighestCpm = getHighestCpmCallback('timeToRespond', (previous, current) => previous > current); From 4d38732bc6f53bcd60826fc2387dddde07b68309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Mon, 19 Jul 2021 22:11:28 +0200 Subject: [PATCH 1291/1476] Sublime Bid Adapter : send notid in pixel & use timeout from timeoutData (#7167) * DL-1746: Remove trId and use notid instead (#31) * Remove trId and use notid instead * Update version to 0.7.3 * Set default value to empty string Co-authored-by: SublimeJeremy * DL-1780: Fix pubtimeout (#32) * Update timeout value with onTimeout data * Add test for onTimeout * Update state with timeout asap Co-authored-by: SublimeJeremy --- modules/sublimeBidAdapter.js | 26 +++++-- test/spec/modules/sublimeBidAdapter_spec.js | 78 ++++++++++++++------- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 854f58df8fe..462665202a5 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.7.2'; +const SUBLIME_VERSION = '0.7.3'; /** * Identify the current device type @@ -40,7 +40,8 @@ export function log(msg, obj) { export const state = { zoneId: '', transactionId: '', - notifyId: '' + notifyId: '', + timeout: config.getConfig('bidderTimeout'), }; /** @@ -66,9 +67,9 @@ export function sendEvent(eventName, sspName) { e: eventName, src: 'pa', puid: state.transactionId || state.notifyId, - trId: state.transactionId || state.notifyId, + notid: state.notifyId || '', pbav: SUBLIME_VERSION, - pubtimeout: config.getConfig('bidderTimeout'), + pubtimeout: state.timeout, pubpbv: '$prebid.version$', device: detectDevice(), }; @@ -109,6 +110,8 @@ function buildRequests(validBidRequests, bidderRequest) { timeout: (typeof bidderRequest === 'object' && !!bidderRequest) ? bidderRequest.timeout : config.getConfig('bidderTimeout'), }; + setState({ timeout: commonPayload.timeout }); + // RefererInfo if (bidderRequest && bidderRequest.refererInfo) { commonPayload.referer = bidderRequest.refererInfo.referer; @@ -221,7 +224,7 @@ function interpretResponse(serverResponse, bidRequest) { /** * Send pixel when bidWon event is triggered - * @param {Object} timeoutData + * @param {Object} bid */ function onBidWon(bid) { log('Bid won', bid); @@ -230,10 +233,16 @@ function onBidWon(bid) { /** * Send debug when we timeout - * @param {Object} timeoutData + * @param {Array[{}]} timeoutData */ function onTimeout(timeoutData) { log('Timeout from adapter', timeoutData); + + const timeout = utils.deepAccess(timeoutData, '0.timeout'); + if (timeout) { + // Set timeout to the one we got from the bid + setState({ timeout }); + } sendEvent('bidtimeout'); } @@ -241,12 +250,15 @@ export const spec = { code: BIDDER_CODE, gvlid: BIDDER_GVLID, aliases: [], - sendEvent: sendEvent, isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, onBidWon: onBidWon, onTimeout: onTimeout, + // Exposed for test purpose + sendEvent: sendEvent, + setState: setState, + state: state, }; registerBidder(spec); diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index fab487cb65f..5c72c6c4dc5 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -4,10 +4,10 @@ import { newBidder } from 'src/adapters/bidderFactory.js'; let utils = require('src/utils'); -describe('Sublime Adapter', function() { +describe('Sublime Adapter', function () { const adapter = newBidder(spec); - describe('sendEvent', function() { + describe('sendEvent', function () { let sandbox; const triggeredPixelProperties = [ 't', @@ -16,7 +16,7 @@ describe('Sublime Adapter', function() { 'e', 'src', 'puid', - 'trId', + 'notid', 'pbav', 'pubpbv', 'device', @@ -40,13 +40,13 @@ describe('Sublime Adapter', function() { }); }) - describe('inherited functions', function() { - it('exists and is a function', function() { + describe('inherited functions', function () { + it('exists and is a function', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', function() { + describe('isBidRequestValid', function () { const bid = { bidder: 'sublime', params: { @@ -55,18 +55,18 @@ describe('Sublime Adapter', function() { }, }; - it('should return true when required params found', function() { + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when required params are not passed', function() { + it('should return false when required params are not passed', function () { const bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('buildRequests', function() { + describe('buildRequests', function () { const bidRequests = [ { bidder: 'sublime', @@ -103,24 +103,24 @@ describe('Sublime Adapter', function() { const request = spec.buildRequests(bidRequests, bidderRequest); - it('should have a post method', function() { + it('should have a post method', function () { expect(request[0].method).to.equal('POST'); expect(request[1].method).to.equal('POST'); }); - it('should contains a request id equals to the bid id', function() { + it('should contains a request id equals to the bid id', function () { for (let i = 0; i < request.length; i = i + 1) { expect(JSON.parse(request[i].data).requestId).to.equal(bidRequests[i].bidId); } }); - it('should have an url that contains bid keyword', function() { + it('should have an url that contains bid keyword', function () { expect(request[0].url).to.match(/bid/); expect(request[1].url).to.match(/bid/); }); }); - describe('buildRequests: default arguments', function() { + describe('buildRequests: default arguments', function () { const bidRequests = [{ bidder: 'sublime', adUnitCode: 'sublime_code', @@ -134,12 +134,12 @@ describe('Sublime Adapter', function() { const request = spec.buildRequests(bidRequests); - it('should have an url that match the default endpoint', function() { + it('should have an url that match the default endpoint', function () { expect(request[0].url).to.equal('https://pbjs.sskzlabs.com/bid'); }); }); - describe('interpretResponse', function() { + describe('interpretResponse', function () { const serverResponse = { 'request_id': '3db3773286ee59', 'sspname': 'foo', @@ -147,11 +147,11 @@ describe('Sublime Adapter', function() { 'ad': '', }; - it('should get correct bid response', function() { + it('should get correct bid response', function () { // Mock the fire method top.window.sublime = { analytics: { - fire: function() {} + fire: function () { } } }; @@ -167,15 +167,15 @@ describe('Sublime Adapter', function() { sspname: 'foo', netRevenue: true, ttl: 600, - pbav: '0.7.2', + pbav: '0.7.3', ad: '', }, ]; - const result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({ body: serverResponse }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('should get correct default size for 1x1', function() { + it('should get correct default size for 1x1', function () { const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', @@ -197,7 +197,7 @@ describe('Sublime Adapter', function() { } }; - const result = spec.interpretResponse({body: serverResponse}, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); const expectedResponse = { requestId: 'xyz654_2', @@ -210,7 +210,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.2', + pbav: '0.7.3', sspname: 'sublime' }; @@ -263,7 +263,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.2', + pbav: '0.7.3', }; expect(result[0]).to.deep.equal(expectedResponse); @@ -301,7 +301,7 @@ describe('Sublime Adapter', function() { }); }); - it('should add advertiserDomains', function() { + it('should add advertiserDomains', function () { const responseWithAdvertiserDomains = utils.deepClone(serverResponse); responseWithAdvertiserDomains.advertiserDomains = ['a_sublime_adomain']; @@ -319,7 +319,7 @@ describe('Sublime Adapter', function() { }); }); - describe('onBidWon', function() { + describe('onBidWon', function () { let sandbox; const bid = { foo: 'bar' }; @@ -334,6 +334,34 @@ describe('Sublime Adapter', function() { expect(params.e).to.equal('bidwon'); }); + afterEach(function () { + sandbox.restore(); + }); + }); + + describe('onTimeout', function () { + let sandbox; + // Array of bids that timed out + const timeoutData = [{ + timeout: 1234 + }]; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + it('should trigger "bidtimeout" pixel', function () { + sandbox.spy(utils, 'triggerPixel'); + spec.onTimeout(timeoutData); + const params = utils.parseUrl(utils.triggerPixel.args[0][0]).search; + expect(params.e).to.equal('bidtimeout'); + }); + + it('should set timeout value in state', function () { + spec.onTimeout(timeoutData); + expect(spec.state).to.deep.equal({ timeout: 1234, debug: false, notifyId: undefined, transactionId: undefined, zoneId: 123 }); + }); + afterEach(function () { sandbox.restore(); }); From 750ea2bf7cc0f1441671ac5df59152cd484a732d Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Tue, 20 Jul 2021 07:07:47 -0400 Subject: [PATCH 1292/1476] add meta.advertiserDomains (#7190) Co-authored-by: Mark Monday --- modules/ajaBidAdapter.js | 197 +++++++++++++++ test/spec/modules/ajaBidAdapter_spec.js | 322 ++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 modules/ajaBidAdapter.js create mode 100644 test/spec/modules/ajaBidAdapter_spec.js diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js new file mode 100644 index 00000000000..933b06baa23 --- /dev/null +++ b/modules/ajaBidAdapter.js @@ -0,0 +1,197 @@ +import { Renderer } from '../src/Renderer.js'; +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'aja'; +const URL = 'https://ad.as.amanad.adtdp.com/v2/prebid'; +const SDK_TYPE = 5; +const AD_TYPE = { + BANNER: 1, + NATIVE: 2, + VIDEO: 3, +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO, BANNER, NATIVE], + + isBidRequestValid: function(bid) { + return !!(bid.params.asi); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + var bidRequests = []; + for (var i = 0, len = validBidRequests.length; i < len; i++) { + var bid = validBidRequests[i]; + var queryString = ''; + const asi = utils.getBidIdParameter('asi', bid.params); + queryString = utils.tryAppendQueryString(queryString, 'asi', asi); + queryString = utils.tryAppendQueryString(queryString, 'skt', SDK_TYPE); + queryString = utils.tryAppendQueryString(queryString, 'prebid_id', bid.bidId); + queryString = utils.tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); + + if (bidderRequest && bidderRequest.refererInfo) { + queryString = utils.tryAppendQueryString(queryString, 'page_url', bidderRequest.refererInfo.referer); + } + + bidRequests.push({ + method: 'GET', + url: URL, + data: queryString + }); + } + + return bidRequests; + }, + + interpretResponse: function(bidderResponse, request) { + const bidderResponseBody = bidderResponse.body; + + if (!bidderResponseBody.is_ad_return) { + return []; + } + + const ad = bidderResponseBody.ad; + + const bid = { + requestId: ad.prebid_id, + cpm: ad.price, + creativeId: ad.creative_id, + dealId: ad.deal_id, + currency: ad.currency || 'USD', + netRevenue: true, + ttl: 300, // 5 minutes + meta: { + advertiserDomains: ad.adomain || [] + }, + } + + if (AD_TYPE.VIDEO === ad.ad_type) { + const videoAd = bidderResponseBody.ad.video; + Object.assign(bid, { + vastXml: videoAd.vtag, + width: videoAd.w, + height: videoAd.h, + renderer: newRenderer(bidderResponseBody), + adResponse: bidderResponseBody, + mediaType: VIDEO + }); + } else if (AD_TYPE.BANNER === ad.ad_type) { + const bannerAd = bidderResponseBody.ad.banner; + Object.assign(bid, { + width: bannerAd.w, + height: bannerAd.h, + ad: bannerAd.tag, + mediaType: BANNER + }); + try { + bannerAd.imps.forEach(impTracker => { + const tracker = utils.createTrackPixelHtml(impTracker); + bid.ad += tracker; + }); + } catch (error) { + utils.logError('Error appending tracking pixel', error); + } + } else if (AD_TYPE.NATIVE === ad.ad_type) { + const nativeAds = ad.native.template_and_ads.ads; + + nativeAds.forEach(nativeAd => { + const assets = nativeAd.assets; + + Object.assign(bid, { + mediaType: NATIVE + }); + + bid.native = { + title: assets.title, + body: assets.description, + cta: assets.cta_text, + sponsoredBy: assets.sponsor, + clickUrl: assets.lp_link, + impressionTrackers: nativeAd.imps, + privacyLink: assets.adchoice_url, + }; + + if (assets.img_main !== undefined) { + bid.native.image = { + url: assets.img_main, + width: parseInt(assets.img_main_width, 10), + height: parseInt(assets.img_main_height, 10) + }; + } + + if (assets.img_icon !== undefined) { + bid.native.icon = { + url: assets.img_icon, + width: parseInt(assets.img_icon_width, 10), + height: parseInt(assets.img_icon_height, 10) + }; + } + }); + } + + return [bid]; + }, + + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + if (!serverResponses.length) { + return syncs; + } + + const bidderResponseBody = serverResponses[0].body; + + if (syncOptions.pixelEnabled && bidderResponseBody.syncs) { + bidderResponseBody.syncs.forEach(sync => { + syncs.push({ + type: 'image', + url: sync + }); + }); + } + + if (syncOptions.iframeEnabled && bidderResponseBody.sync_htmls) { + bidderResponseBody.sync_htmls.forEach(sync => { + syncs.push({ + type: 'iframe', + url: sync + }); + }); + } + + return syncs; + }, +} + +function newRenderer(bidderResponse) { + const renderer = Renderer.install({ + id: bidderResponse.ad.prebid_id, + url: bidderResponse.ad.video.purl, + loaded: false, + }); + + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on newRenderer', err); + } + + return renderer; +} + +function outstreamRender(bid) { + bid.renderer.push(() => { + window.aja_vast_player.init({ + vast_tag: bid.adResponse.ad.video.vtag, + ad_unit_code: bid.adUnitCode, // target div id to render video + width: bid.width, + height: bid.height, + progress: bid.adResponse.ad.video.progress, + loop: bid.adResponse.ad.video.loop, + inread: bid.adResponse.ad.video.inread + }); + }); +} + +registerBidder(spec); diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js new file mode 100644 index 00000000000..addf6a0a409 --- /dev/null +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -0,0 +1,322 @@ +import { spec } from 'modules/ajaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +const ENDPOINT = 'https://ad.as.amanad.adtdp.com/v2/prebid'; + +describe('AjaAdapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + '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 required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'asi': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'aja', + 'params': { + 'asi': '123456' + }, + 'adUnitCode': 'adunit', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + let bidderRequest = { + refererInfo: { + referer: 'https://hoge.com' + } + }; + + it('sends bid request to ENDPOINT via GET', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.equal(ENDPOINT); + expect(requests[0].method).to.equal('GET'); + expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&'); + }); + }); + + describe('interpretResponse', function () { + it('should get correct banner bid response', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 1, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'USD', + 'creative_id': '123abc', + 'banner': { + 'w': 300, + 'h': 250, + 'tag': '
', + 'imps': [ + 'https://as.amanad.adtdp.com/v1/imp' + ] + }, + 'adomain': [ + 'www.example.com' + ] + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true, + 'meta': { + 'advertiserDomains': [ + 'www.example.com' + ] + } + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles video responses', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 3, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'video': { + 'w': 300, + 'h': 250, + 'vtag': '', + 'purl': 'https://cdn/player', + 'progress': true, + 'loop': false, + 'inread': false + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('renderer'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles native response', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 2, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'native': { + 'template_and_ads': { + 'head': '', + 'body_wrapper': '', + 'body': '', + 'ads': [ + { + 'ad_format_id': 10, + 'assets': { + 'ad_spot_id': '123abc', + 'index': 0, + 'adchoice_url': 'https://aja-kk.co.jp/optout', + 'cta_text': 'cta', + 'img_icon': 'https://example.com/img_icon', + 'img_icon_width': '50', + 'img_icon_height': '50', + 'img_main': 'https://example.com/img_main', + 'img_main_width': '200', + 'img_main_height': '100', + 'lp_link': 'https://example.com/lp?k=v', + 'sponsor': 'sponsor', + 'title': 'ad_title', + 'description': 'ad_desc' + }, + 'imps': [ + 'https://example.com/imp' + ], + 'inviews': [ + 'https://example.com/inview' + ], + 'jstracker': '', + 'disable_trimming': false + } + ] + } + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'mediaType': 'native', + 'currency': 'JPY', + 'ttl': 300, + 'netRevenue': true, + 'native': { + 'title': 'ad_title', + 'body': 'ad_desc', + 'cta': 'cta', + 'sponsoredBy': 'sponsor', + 'image': { + 'url': 'https://example.com/img_main', + 'width': 200, + 'height': 100 + }, + 'icon': { + 'url': 'https://example.com/img_icon', + 'width': 50, + 'height': 50 + }, + 'clickUrl': 'https://example.com/lp?k=v', + 'impressionTrackers': [ + 'https://example.com/imp' + ], + 'privacyLink': 'https://aja-kk.co.jp/optout' + }, + 'meta': { + 'advertiserDomains': [] + } + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}) + expect(result).to.deep.equal(expectedResponse) + }); + + it('handles nobid responses', function () { + let response = { + 'is_ad_return': false, + 'ad': {} + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const bidResponse1 = { + body: { + 'is_ad_return': true, + 'ad': { /* ad body */ }, + 'syncs': [ + 'https://example.test/pixel/1' + ], + 'sync_htmls': [ + 'https://example.test/iframe/1' + ] + } + }; + + const bidResponse2 = { + body: { + 'is_ad_return': true, + 'ad': { /* ad body */ }, + 'syncs': [ + 'https://example.test/pixel/2' + ] + } + }; + + it('should use a sync url from first response (pixel and iframe)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://example.test/pixel/1' + }, + { + type: 'iframe', + url: 'https://example.test/iframe/1' + } + ]); + }); + + it('handle empty response (e.g. timeout)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('returns empty syncs when not pixel enabled and not iframe enabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([]); + }); + + it('returns pixel syncs when pixel enabled and not iframe enabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://example.test/pixel/1' + } + ]); + }); + + it('returns iframe syncs when not pixel enabled and iframe enabled', function() { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://example.test/iframe/1' + } + ]); + }); + }); +}); From 2a899c6fd508089412546e2add7ffb8c1e898e04 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Tue, 20 Jul 2021 07:51:32 -0400 Subject: [PATCH 1293/1476] adds meta.advertiserDomains (#7191) Co-authored-by: Mark Monday --- modules/adheseBidAdapter.js | 236 +++++++++ test/spec/modules/adheseBidAdapter_spec.js | 532 +++++++++++++++++++++ 2 files changed, 768 insertions(+) create mode 100644 modules/adheseBidAdapter.js create mode 100644 test/spec/modules/adheseBidAdapter_spec.js diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js new file mode 100644 index 00000000000..88f3e0e0e4f --- /dev/null +++ b/modules/adheseBidAdapter.js @@ -0,0 +1,236 @@ +'use strict'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'adhese'; +const GVLID = 553; +const USER_SYNC_BASE_URL = 'https://user-sync.adhese.com/iframe/user_sync.html'; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: function(bid) { + return !!(bid.params.account && bid.params.location && (bid.params.format || bid.mediaTypes.banner.sizes)); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + if (validBidRequests.length === 0) { + return null; + } + const { gdprConsent, refererInfo } = bidderRequest; + + const gdprParams = (gdprConsent && gdprConsent.consentString) ? { xt: [gdprConsent.consentString] } : {}; + const refererParams = (refererInfo && refererInfo.referer) ? { xf: [base64urlEncode(refererInfo.referer)] } : {}; + const commonParams = { ...gdprParams, ...refererParams }; + + const slots = validBidRequests.map(bid => ({ + slotname: bidToSlotName(bid), + parameters: cleanTargets(bid.params.data) + })); + + const payload = { + slots: slots, + parameters: commonParams, + vastContentAsUrl: true, + user: { + ext: { + eids: getEids(validBidRequests), + } + } + }; + + const account = getAccount(validBidRequests); + const uri = 'https://ads-' + account + '.adhese.com/json'; + + return { + method: 'POST', + url: uri, + data: JSON.stringify(payload), + bids: validBidRequests, + options: { + contentType: 'application/json' + } + }; + }, + + interpretResponse: function(serverResponse, request) { + const serverAds = serverResponse.body.reduce(function(map, ad) { + map[ad.slotName] = ad; + return map; + }, {}); + + serverResponse.account = getAccount(request.bids); + + return request.bids + .map(bid => ({ + bid: bid, + ad: serverAds[bidToSlotName(bid)] + })) + .filter(item => item.ad) + .map(item => adResponse(item.bid, item.ad)); + }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { + if (syncOptions.iframeEnabled && serverResponses.length > 0) { + const account = serverResponses[0].account; + if (account) { + let syncurl = USER_SYNC_BASE_URL + '?account=' + account; + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&consentString=' + encodeURIComponent(gdprConsent.consentString || ''); + } + return [{type: 'iframe', url: syncurl}]; + } + } + return []; + } +}; + +function adResponse(bid, ad) { + const price = getPrice(ad); + const adDetails = getAdDetails(ad); + const markup = getAdMarkup(ad); + + const bidResponse = getbaseAdResponse({ + requestId: bid.bidId, + mediaType: ad.extension.mediaType, + cpm: Number(price.amount), + currency: price.currency, + width: Number(ad.width), + height: Number(ad.height), + creativeId: adDetails.creativeId, + dealId: adDetails.dealId, + adhese: { + originData: adDetails.originData, + origin: adDetails.origin, + originInstance: adDetails.originInstance + }, + meta: { + advertiserDomains: ad.adomain || [] + } + }); + + if (bidResponse.mediaType === VIDEO) { + if (ad.cachedBodyUrl) { + bidResponse.vastUrl = ad.cachedBodyUrl + } else { + bidResponse.vastXml = markup; + } + } else { + const counter = ad.impressionCounter ? "" : ''; + bidResponse.ad = markup + counter; + } + return bidResponse; +} + +function cleanTargets(target) { + const targets = {}; + if (target) { + Object.keys(target).forEach(function (key) { + const val = target[key]; + const dirtyValues = Array.isArray(val) ? val : [val]; + const values = dirtyValues.filter(v => v === 0 || v); + if (values.length > 0) { + if (targets[key]) { + const distinctValues = values.filter(v => targets[key].indexOf(v) < 0); + targets[key].push.apply(targets[key], distinctValues); + } else { + targets[key] = values; + } + } + }); + } + return targets; +} + +function bidToSlotName(bid) { + if (bid.params.format) { + return bid.params.location + '-' + bid.params.format; + } + + var sizes = bid.mediaTypes.banner.sizes; + sizes.sort(); + var format = sizes.map(size => size[0] + 'x' + size[1]).join('_'); + + if (format.length > 0) { + return bid.params.location + '-' + format; + } else { + return bid.params.location; + } +} + +function getAccount(validBidRequests) { + return validBidRequests[0].params.account; +} + +function getEids(validBidRequests) { + if (validBidRequests[0] && validBidRequests[0].userIdAsEids) { + return validBidRequests[0].userIdAsEids; + } +} + +function getbaseAdResponse(response) { + return Object.assign({ netRevenue: true, ttl: 360 }, response); +} + +function isAdheseAd(ad) { + return !ad.origin || ad.origin === 'JERLICIA'; +} + +function getAdMarkup(ad) { + if (!isAdheseAd(ad) || (ad.ext === 'js' && ad.body !== undefined && ad.body !== '' && ad.body.match(/ { + let bidRequest = { + bids: [ minimalBid() ] + }; + + it('should get correct ssp banner response', () => { + let sspBannerResponse = { + body: [ + { + origin: 'APPNEXUS', + originInstance: '', + ext: 'js', + slotID: '10', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + originData: { + seatbid: [{ + bid: [{ + crid: '60613369', + dealid: null + }], + seat: '958' + }] + }, + width: '728', + height: '90', + body: '
', + tracker: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', + impressionCounter: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', + extension: {'prebid': {'cpm': {'amount': '1.000000', 'currency': 'USD'}}, mediaType: 'banner'}, + adomain: [ + 'www.example.com' + ] + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + ad: '
', + cpm: 1, + currency: 'USD', + creativeId: '60613369', + dealId: '', + width: 728, + height: 90, + mediaType: 'banner', + netRevenue: NET_REVENUE, + ttl: TTL, + adhese: { + origin: 'APPNEXUS', + originInstance: '', + originData: { + adType: 'leaderboard', + seatbid: [ + { + bid: [ { crid: '60613369', dealid: null } ], + seat: '958' + } + ], + slotId: '10', + slotName: '_main_page_-leaderboard' + } + }, + meta: { + advertiserDomains: [ + 'www.example.com' + ] + }, + }]; + expect(spec.interpretResponse(sspBannerResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct ssp video response', () => { + let sspVideoResponse = { + body: [ + { + origin: 'RUBICON', + ext: 'js', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + width: '640', + height: '350', + body: '', + extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastXml: '', + cpm: 2.1, + currency: 'USD', + creativeId: 'RUBICON', + dealId: '', + width: 640, + height: 350, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + adhese: { + origin: 'RUBICON', + originInstance: '', + originData: {} + }, + meta: { + advertiserDomains: [] + }, + }]; + expect(spec.interpretResponse(sspVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct ssp cache video response', () => { + let sspCachedVideoResponse = { + body: [ + { + origin: 'RUBICON', + ext: 'js', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + width: '640', + height: '350', + cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + cpm: 2.1, + currency: 'USD', + creativeId: 'RUBICON', + dealId: '', + width: 640, + height: 350, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + adhese: { + origin: 'RUBICON', + originInstance: '', + originData: {} + }, + meta: { + advertiserDomains: [] + }, + }]; + expect(spec.interpretResponse(sspCachedVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct Adhese banner response', () => { + const adheseBannerResponse = { + body: [ + { + adType: 'largeleaderboard', // it can differ from the requested slot + adFormat: 'largeleaderboard', + timeStamp: '1544009030000', + orderId: '22051', + adspaceId: '162363', + body: '', + tag: '', + tracker: 'https://hosts-demo.adhese.com/track/tracker', + altText: '', + height: '150', + width: '840', + tagUrl: 'https://pool-demo.adhese.com/pool/lib/90511.js', + libId: '90511', + id: '742898', + advertiserId: '2081', + ext: 'js', + url: 'https://hosts-demo.adhese.com/raylene/url', + clickTag: 'https://hosts-demo.adhese.com/raylene/clickTag', + poolPath: 'https://hosts-demo.adhese.com/pool/lib/', + orderName: 'Luminus boiler comodity-Pareto -201812', + creativeName: 'nl_demo _network_ron_dlbd_840x150_fix_dir_asv_std_dis_brd_nrt_na_red', + slotName: '_main_page_-leaderboard', + slotID: '29306', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true, + extension: { + prebid: { + cpm: { + amount: '5.96', + currency: 'USD' + } + }, + mediaType: 'banner' + } + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + ad: '', + adhese: { + origin: '', + originInstance: '', + originData: { + adFormat: 'largeleaderboard', + adId: '742898', + adType: 'largeleaderboard', + adspaceId: '162363', + libId: '90511', + orderProperty: undefined, + priority: undefined, + viewableImpressionCounter: undefined, + slotId: '29306', + slotName: '_main_page_-leaderboard', + advertiserId: '2081' + } + }, + cpm: 5.96, + currency: 'USD', + creativeId: '742898', + dealId: '22051', + width: 840, + height: 150, + mediaType: 'banner', + netRevenue: NET_REVENUE, + ttl: TTL, + meta: { + advertiserDomains: [] + }, + }]; + expect(spec.interpretResponse(adheseBannerResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct Adhese video response', () => { + const adheseVideoResponse = { + body: [ + { + adType: 'preroll', + adFormat: '', + orderId: '22248', + adspaceId: '164196', + body: '', + height: '360', + width: '640', + tag: "", + libId: '89860', + id: '742470', + advertiserId: '2263', + ext: 'advar', + orderName: 'Smartphoto EOY-20181112', + creativeName: 'PREROLL', + slotName: '_main_page_-leaderboard', + slotID: '41711', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true, + extension: { + mediaType: 'video' + } + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastXml: '', + adhese: { + origin: '', + originInstance: '', + originData: { + adFormat: '', + adId: '742470', + adType: 'preroll', + adspaceId: '164196', + libId: '89860', + orderProperty: undefined, + priority: undefined, + viewableImpressionCounter: undefined, + slotId: '41711', + slotName: '_main_page_-leaderboard', + advertiserId: '2263', + } + }, + cpm: 0, + currency: 'USD', + creativeId: '742470', + dealId: '22248', + width: 640, + height: 360, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + meta: { + advertiserDomains: [] + }, + }]; + expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should get correct Adhese cached video response', () => { + const adheseVideoResponse = { + body: [ + { + adType: 'preroll', + adFormat: '', + orderId: '22248', + adspaceId: '164196', + body: '', + height: '360', + width: '640', + extension: { + mediaType: 'video' + }, + cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + libId: '89860', + id: '742470', + advertiserId: '2263', + ext: 'advar', + orderName: 'Smartphoto EOY-20181112', + creativeName: 'PREROLL', + slotName: '_main_page_-leaderboard', + slotID: '41711', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + adhese: { + origin: '', + originInstance: '', + originData: { + adFormat: '', + adId: '742470', + adType: 'preroll', + adspaceId: '164196', + libId: '89860', + orderProperty: undefined, + priority: undefined, + viewableImpressionCounter: undefined, + slotId: '41711', + slotName: '_main_page_-leaderboard', + advertiserId: '2263', + } + }, + cpm: 0, + currency: 'USD', + creativeId: '742470', + dealId: '22248', + width: 640, + height: 360, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + meta: { + advertiserDomains: [] + }, + }]; + expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + + it('should return no bids for empty adserver response', () => { + let adserverResponse = { body: [] }; + expect(spec.interpretResponse(adserverResponse, bidRequest)).to.be.empty; + }); + }); +}); From eef3277879d1cc2881dc52e17070b98c04174f33 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Tue, 20 Jul 2021 09:04:46 -0400 Subject: [PATCH 1294/1476] adds meta.advertiserDomains (#7192) Co-authored-by: Mark Monday --- modules/ebdrBidAdapter.js | 155 ++++++++++++++ test/spec/modules/ebdrBidAdapter_spec.js | 245 +++++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 modules/ebdrBidAdapter.js create mode 100644 test/spec/modules/ebdrBidAdapter_spec.js diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js new file mode 100644 index 00000000000..cfbbbee61cb --- /dev/null +++ b/modules/ebdrBidAdapter.js @@ -0,0 +1,155 @@ +import * as utils from '../src/utils.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +const BIDDER_CODE = 'ebdr'; +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ BANNER, VIDEO ], + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.zoneid); + }, + buildRequests: function(bids) { + const rtbServerDomain = 'dsp.bnmla.com'; + let domain = window.location.host; + let page = window.location.pathname + location.search + location.hash; + let ebdrImps = []; + const ebdrReq = {}; + let ebdrParams = {}; + let zoneid = ''; + let requestId = ''; + bids.forEach(bid => { + utils.logInfo('Log bid', bid); + let bidFloor = utils.getBidIdParameter('bidfloor', bid.params); + let whArr = getWidthAndHeight(bid); + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video) ? VIDEO : BANNER; + zoneid = utils.getBidIdParameter('zoneid', bid.params); + requestId = bid.bidderRequestId; + ebdrImps.push({ + id: bid.bidId, + [_mediaTypes]: { + w: whArr[0], + h: whArr[1] + }, + bidfloor: bidFloor + }) + ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes, + w: whArr[0], + h: whArr[1] + }; + ebdrParams['latitude'] = utils.getBidIdParameter('latitude', bid.params); + ebdrParams['longitude'] = utils.getBidIdParameter('longitude', bid.params); + ebdrParams['ifa'] = (utils.getBidIdParameter('IDFA', bid.params).length > utils.getBidIdParameter('ADID', bid.params).length) ? utils.getBidIdParameter('IDFA', bid.params) : utils.getBidIdParameter('ADID', bid.params); + }); + let ebdrBidReq = { + id: requestId, + imp: ebdrImps, + site: { + domain: domain, + page: page + }, + device: { + geo: { + lat: ebdrParams.latitude, + log: ebdrParams.longitude + }, + ifa: ebdrParams.ifa + } + }; + return { + method: 'GET', + url: 'https://' + rtbServerDomain + '/hb?' + '&zoneid=' + zoneid + '&br=' + encodeURIComponent(JSON.stringify(ebdrBidReq)), + bids: ebdrReq + }; + }, + interpretResponse: function(serverResponse, bidRequest) { + utils.logInfo('Log serverResponse', serverResponse); + utils.logInfo('Log bidRequest', bidRequest); + let ebdrResponseImps = []; + const ebdrResponseObj = serverResponse.body; + if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) { + return []; + } + ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => { + let responseCPM; + responseCPM = parseFloat(ebdrBid.price); + let adm; + let type; + let _mediaTypes; + let vastURL; + if (bidRequest.bids[ebdrBid.id].mediaTypes == BANNER) { + adm = decodeURIComponent(ebdrBid.adm) + type = 'ad'; + _mediaTypes = BANNER; + } else { + adm = ebdrBid.adm + type = 'vastXml' + _mediaTypes = VIDEO; + if (ebdrBid.nurl) { + vastURL = ebdrBid.nurl; + } + } + let response = { + requestId: ebdrBid.id, + [type]: adm, + mediaType: _mediaTypes, + creativeId: ebdrBid.crid, + cpm: responseCPM, + width: ebdrBid.w, + height: ebdrBid.h, + currency: 'USD', + netRevenue: true, + ttl: 3600, + meta: { + advertiserDomains: ebdrBid.adomain || [] + } + }; + if (vastURL) { + response.vastUrl = vastURL; + } + ebdrResponseImps.push(response); + }); + return ebdrResponseImps; + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = [] + if (syncOptions.pixelEnabled) { + const ebdrResponseObj = serverResponses.body; + if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) { + return []; + } + ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => { + if (ebdrBid.iurl && ebdrBid.iurl.length > 0) { + syncs.push({ + type: 'image', + url: ebdrBid.iurl + }); + } + }); + } + return syncs; + } +} +function getWidthAndHeight(bid) { + let adW = null; + let adH = null; + // Handing old bidder only has size object + if (bid.sizes && bid.sizes.length) { + let sizeArrayLength = bid.sizes.length; + if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') { + adW = bid.sizes[0]; + adH = bid.sizes[1]; + } + } + let _mediaTypes = bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER; + if (bid.mediaTypes && bid.mediaTypes[_mediaTypes]) { + if (_mediaTypes == BANNER && bid.mediaTypes[_mediaTypes].sizes && bid.mediaTypes[_mediaTypes].sizes[0] && bid.mediaTypes[_mediaTypes].sizes[0].length === 2) { + adW = bid.mediaTypes[_mediaTypes].sizes[0][0]; + adH = bid.mediaTypes[_mediaTypes].sizes[0][1]; + } else if (_mediaTypes == VIDEO && bid.mediaTypes[_mediaTypes].playerSize && bid.mediaTypes[_mediaTypes].playerSize.length === 2) { + adW = bid.mediaTypes[_mediaTypes].playerSize[0]; + adH = bid.mediaTypes[_mediaTypes].playerSize[1]; + } + } + return [adW, adH]; +} +registerBidder(spec); diff --git a/test/spec/modules/ebdrBidAdapter_spec.js b/test/spec/modules/ebdrBidAdapter_spec.js new file mode 100644 index 00000000000..1c46381500f --- /dev/null +++ b/test/spec/modules/ebdrBidAdapter_spec.js @@ -0,0 +1,245 @@ +import { expect } from 'chai'; +import { spec } from 'modules/ebdrBidAdapter.js'; +import { VIDEO, BANNER } from 'src/mediaTypes.js'; +import * as utils from 'src/utils.js'; + +describe('ebdrBidAdapter', function () { + let bidRequests; + + beforeEach(function () { + bidRequests = [ + { + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bidder: 'ebdr', + params: { + zoneid: '99999', + bidfloor: '1.00', + IDFA: 'xxx-xxx', + ADID: 'xxx-xxx', + latitude: '34.089811', + longitude: '-118.392805' + }, + bidId: '2c5e8a1a84522d', + bidderRequestId: '1d0c4017f02458', + auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' + }, { + adUnitCode: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + video: { + context: 'instream', + playerSize: [300, 250] + } + }, + bidder: 'ebdr', + params: { + zoneid: '99998', + bidfloor: '1.00', + IDFA: 'xxx-xxx', + ADID: 'xxx-xxx', + latitude: '34.089811', + longitude: '-118.392805' + }, + bidId: '23a01e95856577', + bidderRequestId: '1d0c4017f02458', + auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314' + } + ]; + }); + + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed', function () { + const bidRequest = bidRequests[0]; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the only required param is missing', function () { + const bidRequest = bidRequests[0]; + bidRequest.params = { + zoneid: '99998', + bidfloor: '1.00', + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the "bidfloor" param is missing', function () { + const bidRequest = bidRequests[0]; + bidRequest.params = { + zoneid: '99998', + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return false when no bid params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when a bid request is not passed', function () { + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); + + describe('spec.buildRequests', function () { + describe('for banner bids', function () { + it('must handle an empty bid size', function () { + bidRequests[0].mediaTypes = { banner: {} }; + const requests = spec.buildRequests(bidRequests); + const bidRequest = {}; + bidRequest['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: null, h: null }; + expect(requests.bids['2c5e8a1a84522d']).to.deep.equals(bidRequest['2c5e8a1a84522d']); + }); + it('should create a single GET', function () { + bidRequests[0].mediaTypes = { banner: {} }; + bidRequests[1].mediaTypes = { banner: {} }; + const requests = spec.buildRequests(bidRequests); + expect(requests.method).to.equal('GET'); + }); + it('must parse bid size from a nested array', function () { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {sizes: [[ width, height ]]} }; + const requests = spec.buildRequests([ bidRequest ]); + const data = {}; + data['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: width, h: height }; + expect(requests.bids['2c5e8a1a84522d']).to.deep.equal(data['2c5e8a1a84522d']); + }); + }); + describe('for video bids', function () { + it('must handle an empty bid size', function () { + bidRequests[1].mediaTypes = { video: {} }; + const requests = spec.buildRequests(bidRequests); + const bidRequest = {}; + bidRequest['23a01e95856577'] = { mediaTypes: VIDEO, w: null, h: null }; + expect(requests.bids['23a01e95856577']).to.deep.equals(bidRequest['23a01e95856577']); + }); + + it('should create a GET request for each bid', function () { + const bidRequest = bidRequests[1]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests.method).to.equal('GET'); + }); + }); + }); + + describe('spec.interpretResponse', function () { + describe('for video bids', function () { + it('should return no bids if the response is not valid', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return a valid video bid response', function () { + const ebdrReq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + }; + }); + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '23a01e95856577', impid: '23a01e95856577', price: 0.81, adid: 'abcde-12345', nurl: 'https://cdn0.bnmla.com/vtest.xml', adm: '\nStatic VASTStatic VAST Tag00:00:15https//www.engagebdr.com/c', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD'}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); + expect(bidResponse[0]).to.deep.equal({ + requestId: bidRequests[1].bidId, + vastXml: serverResponse.seatbid[0].bid[0].adm, + mediaType: 'video', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + currency: 'USD', + netRevenue: true, + ttl: 3600, + vastUrl: serverResponse.seatbid[0].bid[0].nurl, + meta: { + advertiserDomains: [ + 'advertiserdomain.com' + ] + } + }); + }); + }); + + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response is empty', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return valid banner bid responses', function () { + const ebdrReq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + }; + }); + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; + const bidResponse = spec.interpretResponse({ body: serverResponse }, ebdrReq); + expect(bidResponse[0]).to.deep.equal({ + requestId: bidRequests[ 0 ].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + mediaType: 'banner', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + currency: 'USD', + netRevenue: true, + ttl: 3600, + meta: { + advertiserDomains: [ + 'advertiserdomain.com' + ] + }, + }); + }); + }); + }); + describe('spec.getUserSyncs', function () { + let syncOptions + beforeEach(function () { + syncOptions = { + enabledBidders: ['ebdr'], // only these bidders are allowed to sync + pixelEnabled: true + } + }); + it('sucess with usersync url', function () { + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: 'https://match.bnmla.com/usersync?sspid=59&redir=', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; + const result = []; + result.push({type: 'image', url: 'https://match.bnmla.com/usersync?sspid=59&redir='}); + expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); + }); + + it('sucess without usersync url', function () { + const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '2c5e8a1a84522d', impid: '2c5e8a1a84522d', price: 0.81, adid: 'abcde-12345', nurl: '', adm: '
', adomain: ['advertiserdomain.com'], iurl: '', cid: 'campaign1', crid: 'abcde-12345', w: 300, h: 250}], seat: '19513bcfca8006'}], bidid: '19513bcfca8006', cur: 'USD', w: 300, h: 250}; + const result = []; + expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); + }); + it('empty response', function () { + const serverResponse = {}; + const result = []; + expect(spec.getUserSyncs(syncOptions, { body: serverResponse })).to.deep.equal(result); + }); + }); +}); From d0d6cbf91e625e04b650ce0d65fca0ec458ecaac Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Tue, 20 Jul 2021 09:27:09 -0400 Subject: [PATCH 1295/1476] adds meta.advertiserDomains (#7193) Co-authored-by: Mark Monday --- modules/clickforceBidAdapter.js | 132 ++++++++++++ .../spec/modules/clickforceBidAdapter_spec.js | 201 ++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 modules/clickforceBidAdapter.js create mode 100644 test/spec/modules/clickforceBidAdapter_spec.js diff --git a/modules/clickforceBidAdapter.js b/modules/clickforceBidAdapter.js new file mode 100644 index 00000000000..2e758c509f7 --- /dev/null +++ b/modules/clickforceBidAdapter.js @@ -0,0 +1,132 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE} from '../src/mediaTypes.js'; +const BIDDER_CODE = 'clickforce'; +const ENDPOINT_URL = 'https://ad.holmesmind.com/adserver/prebid.json?cb=' + new Date().getTime() + '&hb=1&ver=1.21'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], + /** + * 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 && bid.params && !!bid.params.zone; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests) { + const bidParams = []; + utils._each(validBidRequests, function(bid) { + bidParams.push({ + z: bid.params.zone, + bidId: bid.bidId + }); + }); + return { + method: 'POST', + url: ENDPOINT_URL, + data: bidParams, + validBidRequests: validBidRequests + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {*} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + const cfResponses = []; + const bidRequestList = []; + + if (typeof bidRequest != 'undefined') { + utils._each(bidRequest.validBidRequests, function(req) { + bidRequestList[req.bidId] = req; + }); + } + + utils._each(serverResponse.body, function(response) { + if (response.requestId != null) { + // native ad size + if (response.width == 3) { + cfResponses.push({ + requestId: response.requestId, + cpm: response.cpm, + width: response.width, + height: response.height, + creativeId: response.creativeId, + currency: response.currency, + netRevenue: response.netRevenue, + ttl: response.ttl, + native: { + title: response.tag.content.title, + body: response.tag.content.content, + image: { + url: response.tag.content.image, + height: 900, + width: 1600 + }, + icon: { + url: response.tag.content.icon, + height: 900, + width: 900 + }, + clickUrl: response.tag.cu, + cta: response.tag.content.button_text, + sponsoredBy: response.tag.content.advertiser, + impressionTrackers: response.tag.iu, + }, + mediaType: 'native', + meta: { + advertiserDomains: response.adomain || [] + }, + }); + } else { + // display ad + cfResponses.push({ + requestId: response.requestId, + cpm: response.cpm, + width: response.width, + height: response.height, + creativeId: response.creativeId, + currency: response.currency, + netRevenue: response.netRevenue, + ttl: response.ttl, + ad: response.tag, + mediaType: 'banner', + meta: { + advertiserDomains: response.adomain || [] + }, + }); + } + } + }); + return cfResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: 'https://cdn.holmesmind.com/js/capmapping.htm' + }] + } else if (syncOptions.pixelEnabled) { + return [{ + type: 'image', + url: 'https://c.holmesmind.com/cm' + }] + } + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/clickforceBidAdapter_spec.js b/test/spec/modules/clickforceBidAdapter_spec.js new file mode 100644 index 00000000000..c4c4d77e954 --- /dev/null +++ b/test/spec/modules/clickforceBidAdapter_spec.js @@ -0,0 +1,201 @@ +import { expect } from 'chai'; +import { spec } from 'modules/clickforceBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('ClickforceAdapter', 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': 'clickforce', + 'params': { + 'zone': '6682' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + '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 required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'someIncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [{ + 'bidder': 'clickforce', + 'params': { + 'zone': '6682' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponse', function () { + let response = [{ + 'cpm': 0.5, + 'width': '300', + 'height': '250', + 'callback_uid': '220ed41385952a', + 'type': 'Default Ad', + 'tag': '', + 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', + 'requestId': '220ed41385952a', + 'currency': 'USD', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6682', + 'adomain': [ + 'www.example.com' + ] + }]; + + let response1 = [{ + 'cpm': 0.0625, + 'width': '3', + 'height': '3', + 'callback_uid': '2e27ec595bf1a', + 'type': 'public Bid', + 'tag': { + 'content': { + 'title': 'title', + 'content': 'content', + 'advertiser': 'advertiser', + 'button_text': 'button_text', + 'image': 'image', + 'icon': 'icon' + }, + 'cu': ['cu'], + 'iu': ['iu'], + 'p': '6878:11062:32586:8380573788dad9b9fc17edde444c4dcf:2795' + }, + 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', + 'requestId': '2e27ec595bf1a', + 'currency': 'USD', + 'ttl': 60, + 'netRevenue': true, + 'zone': '6878' + }]; + + let expectedResponse = [{ + 'requestId': '220ed41385952a', + 'cpm': 0.5, + 'width': '300', + 'height': '250', + 'creativeId': '1f99ac5c3ef10a4097499a5686b30aff-6682', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 60, + 'ad': '', + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': [ + 'www.example.com' + ] + } + }]; + + let expectedResponse1 = [{ + 'requestId': '2e27ec595bf1a', + 'cpm': 0.0625, + 'width': '3', + 'height': '3', + 'creativeId': '8380573788dad9b9fc17edde444c4dcf-6878', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 60, + 'mediaType': 'native', + 'native': { + 'image': { + 'url': 'image', + 'width': 1600, + 'height': 900 + }, + 'title': 'title', + 'sponsoredBy': 'advertiser', + 'body': 'content', + 'icon': { + 'url': 'icon', + 'width': 900, + 'height': 900 + }, + 'clickUrl': 'cu', + 'impressionTrackers': ['iu'] + }, + 'meta': { + 'advertiserDomains': [] + } + }]; + + it('should get the correct bid response by display ad', function () { + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('should get the correct bid response by native ad', function () { + let bidderRequest; + let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse1[0])); + }); + + it('handles empty bid response', function () { + let response = { + body: {} + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs function', function () { + it('should register type is iframe', function () { + const syncOptions = { + 'iframeEnabled': 'true' + } + let userSync = spec.getUserSyncs(syncOptions); + expect(userSync[0].type).to.equal('iframe'); + expect(userSync[0].url).to.equal('https://cdn.holmesmind.com/js/capmapping.htm'); + }); + + it('should register type is image', function () { + const syncOptions = { + 'pixelEnabled': 'true' + } + let userSync = spec.getUserSyncs(syncOptions); + expect(userSync[0].type).to.equal('image'); + expect(userSync[0].url).to.equal('https://c.holmesmind.com/cm'); + }); + }); +}); From d03a458100334c3efe1791e4c93ac840784783a6 Mon Sep 17 00:00:00 2001 From: Alexander Fominov Date: Tue, 20 Jul 2021 17:25:51 +0300 Subject: [PATCH 1296/1476] MARJAVA-889 Update getintentBidAdapter to Prebid 5.0 requirements (#7197) - use floors module for bid floors - add advertiserDomains to bid response - support video parameters at the adunit level - update tests --- modules/getintentBidAdapter.js | 220 ++++++++++++++++++ test/spec/modules/getintentBidAdapter_spec.js | 220 ++++++++++++++++++ 2 files changed, 440 insertions(+) create mode 100644 modules/getintentBidAdapter.js create mode 100644 test/spec/modules/getintentBidAdapter_spec.js diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js new file mode 100644 index 00000000000..9127bc4be6e --- /dev/null +++ b/modules/getintentBidAdapter.js @@ -0,0 +1,220 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { isInteger } from '../src/utils.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'getintent'; +const IS_NET_REVENUE = true; +const BID_HOST = 'px.adhigh.net'; +const BID_BANNER_PATH = '/rtb/direct_banner'; +const BID_VIDEO_PATH = '/rtb/direct_vast'; +const BID_RESPONSE_TTL_SEC = 360; +const FLOOR_PARAM = 'floor'; +const CURRENCY_PARAM = 'cur'; +const DEFAULT_CURRENCY = 'RUB'; +const VIDEO_PROPERTIES = { + 'protocols': 'protocols', + 'mimes': 'mimes', + 'min_dur': 'minduration', + 'max_dur': 'maxduration', + 'min_btr': 'minbitrate', + 'max_btr': 'maxbitrate', + 'vi_format': null, + 'api': 'api', + 'skippable': 'skip', +}; +const SKIPPABLE_ALLOW = 'ALLOW'; +const SKIPPABLE_NOT_ALLOW = 'NOT_ALLOW'; + +const OPTIONAL_PROPERTIES = [ + 'sid' +]; + +export const spec = { + code: BIDDER_CODE, + aliases: ['getintentAdapter'], + supportedMediaTypes: ['video', 'banner'], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid to validate. + * @return {boolean} True if this is a valid bid, and false otherwise. + * */ + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.pid && bid.params.tid); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests - an array of bids. + * @return ServerRequest[] + */ + buildRequests: function(bidRequests) { + return bidRequests.map(bidRequest => { + let giBidRequest = buildGiBidRequest(bidRequest); + return { + method: 'GET', + url: buildUrl(giBidRequest), + data: giBidRequest, + }; + }); + }, + + /** + * Callback for bids, after the call to DSP completes. + * Parse the response from the server into a list of bids. + * + * @param {object} serverResponse A response from the GetIntent's server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse) { + let responseBody = serverResponse.body; + const bids = []; + if (responseBody && responseBody.no_bid !== 1) { + let size = parseSize(responseBody.size); + let bid = { + requestId: responseBody.bid_id, + ttl: BID_RESPONSE_TTL_SEC, + netRevenue: IS_NET_REVENUE, + currency: responseBody.currency, + creativeId: responseBody.creative_id, + cpm: responseBody.cpm, + width: size[0], + height: size[1], + meta: { + advertiserDomains: responseBody.adomain || [], + } + }; + if (responseBody.vast_url) { + bid.mediaType = 'video'; + bid.vastUrl = responseBody.vast_url; + } else { + bid.mediaType = 'banner'; + bid.ad = responseBody.ad; + } + bids.push(bid); + } + return bids; + } + +} + +function buildUrl(bid) { + return 'https://' + BID_HOST + (bid.is_video ? BID_VIDEO_PATH : BID_BANNER_PATH); +} + +/** + * Builds GI bid request from BidRequest. + * + * @param {BidRequest} bidRequest. + * @return {object} GI bid request. + * */ +function buildGiBidRequest(bidRequest) { + let giBidRequest = { + bid_id: bidRequest.bidId, + pid: bidRequest.params.pid, // required + tid: bidRequest.params.tid, // required + known: bidRequest.params.known || 1, + is_video: bidRequest.mediaType === 'video', + resp_type: 'JSON', + provider: 'direct.prebidjs' + }; + if (bidRequest.sizes) { + giBidRequest.size = produceSize(bidRequest.sizes); + } + + const currency = utils.getBidIdParameter(CURRENCY_PARAM, bidRequest.params); + const floorInfo = getBidFloor(bidRequest, currency); + if (floorInfo.floor) { + giBidRequest[FLOOR_PARAM] = floorInfo.floor; + } + if (floorInfo.currency) { + giBidRequest[CURRENCY_PARAM] = floorInfo.currency; + } + + if (giBidRequest.is_video) { + addVideo(bidRequest.params.video, bidRequest.mediaTypes.video, giBidRequest); + } + addOptional(bidRequest.params, giBidRequest, OPTIONAL_PROPERTIES); + return giBidRequest; +} + +function getBidFloor(bidRequest, currency) { + let floorInfo = {}; + + if (utils.isFn(bidRequest.getFloor)) { + floorInfo = bidRequest.getFloor({ + currency: currency || DEFAULT_CURRENCY, + mediaType: bidRequest.mediaType, + size: bidRequest.sizes || '*' + }); + } + + return { + floor: floorInfo.floor || bidRequest.params[FLOOR_PARAM] || 0, + currency: floorInfo.currency || currency || '', + }; +} + +function addVideo(videoParams, mediaTypesVideoParams, giBidRequest) { + videoParams = videoParams || {}; + mediaTypesVideoParams = mediaTypesVideoParams || {}; + + for (let videoParam in VIDEO_PROPERTIES) { + let paramValue; + + const mediaTypesVideoParam = VIDEO_PROPERTIES[videoParam]; + if (videoParams.hasOwnProperty(videoParam)) { + paramValue = videoParams[videoParam]; + } else if (mediaTypesVideoParam !== null && mediaTypesVideoParams.hasOwnProperty(mediaTypesVideoParam)) { + if (mediaTypesVideoParam === 'skip') { + paramValue = mediaTypesVideoParams[mediaTypesVideoParam] === 1 ? SKIPPABLE_ALLOW : SKIPPABLE_NOT_ALLOW; + } else { + paramValue = mediaTypesVideoParams[mediaTypesVideoParam]; + } + } + + if (typeof paramValue !== 'undefined') { + giBidRequest[videoParam] = Array.isArray(paramValue) ? paramValue.join(',') : paramValue; + } + } +} + +function addOptional(params, request, props) { + for (let i = 0; i < props.length; i++) { + if (params.hasOwnProperty(props[i])) { + request[props[i]] = params[props[i]]; + } + } +} + +/** + * @param {String} s The string representing a size (e.g. "300x250"). + * @return {Number[]} An array with two elements: [width, height] (e.g.: [300, 250]). + * */ +function parseSize(s) { + return s.split('x').map(Number); +} + +/** + * @param {Array} sizes An array of sizes/numbers to be joined into single string. + * May be an array (e.g. [300, 250]) or array of arrays (e.g. [[300, 250], [640, 480]]. + * @return {String} The string with sizes, e.g. array of sizes [[50, 50], [80, 80]] becomes "50x50,80x80" string. + * */ +function produceSize (sizes) { + function sizeToStr(s) { + if (Array.isArray(s) && s.length === 2 && isInteger(s[0]) && isInteger(s[1])) { + return s.join('x'); + } else { + throw "Malformed parameter 'sizes'"; + } + } + if (Array.isArray(sizes) && Array.isArray(sizes[0])) { + return sizes.map(sizeToStr).join(','); + } else { + return sizeToStr(sizes); + } +} + +registerBidder(spec); diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js new file mode 100644 index 00000000000..bb0b5ba826c --- /dev/null +++ b/test/spec/modules/getintentBidAdapter_spec.js @@ -0,0 +1,220 @@ +import { expect } from 'chai' +import { spec } from 'modules/getintentBidAdapter.js' +import {deepClone} from 'src/utils'; + +describe('GetIntent Adapter Tests:', function () { + const bidRequests = [{ + bidId: 'bid12345', + params: { + pid: 'p1000', + tid: 't1000' + }, + sizes: [[300, 250]] + }, + { + bidId: 'bid54321', + params: { + pid: 'p1000', + tid: 't1000' + }, + sizes: [[50, 50], [100, 100]] + }]; + const videoBidRequest = { + mediaTypes: { + video: { + protocols: [1, 2, 3], + mimes: ['video/mp4'], + minduration: 5, + maxduration: 30, + minbitrate: 500, + maxbitrate: 1000, + api: [2], + skip: 1 + } + }, + bidId: 'bid789', + params: { + pid: 'p1001', + tid: 't1001', + }, + sizes: [300, 250], + mediaType: 'video' + }; + const videoBidRequestWithVideoParams = { + mediaTypes: { + video: { + protocols: [1, 2, 3], + mimes: ['video/mp4'], + minduration: 5, + maxduration: 30, + minbitrate: 500, + maxbitrate: 1000, + api: [2], + skip: 0 + } + }, + bidId: 'bid789', + params: { + pid: 'p1001', + tid: 't1001', + video: { + mimes: ['video/mp4', 'application/javascript'], + max_dur: 20, + api: [1, 2], + skippable: 'ALLOW' + } + }, + sizes: [300, 250], + mediaType: 'video' + }; + + it('Verify build request', function () { + const serverRequests = spec.buildRequests(bidRequests); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('https://px.adhigh.net/rtb/direct_banner'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid12345'); + expect(serverRequest.data.pid).to.equal('p1000'); + expect(serverRequest.data.tid).to.equal('t1000'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(false); + serverRequest = serverRequests[1]; + expect(serverRequest.data.size).to.equal('50x50,100x100'); + }); + + it('Verify build video request', function () { + const serverRequests = spec.buildRequests([videoBidRequest]); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('https://px.adhigh.net/rtb/direct_vast'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid789'); + expect(serverRequest.data.pid).to.equal('p1001'); + expect(serverRequest.data.tid).to.equal('t1001'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(true); + expect(serverRequest.data.protocols).to.equal('1,2,3'); + expect(serverRequest.data.mimes).to.equal('video/mp4'); + expect(serverRequest.data.min_dur).to.equal(5); + expect(serverRequest.data.max_dur).to.equal(30); + expect(serverRequest.data.min_btr).to.equal(500); + expect(serverRequest.data.max_btr).to.equal(1000); + expect(serverRequest.data.api).to.equal('2'); + expect(serverRequest.data.skippable).to.equal('ALLOW'); + }); + + it('Verify build video request with video params', function () { + const serverRequests = spec.buildRequests([videoBidRequestWithVideoParams]); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('https://px.adhigh.net/rtb/direct_vast'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid789'); + expect(serverRequest.data.pid).to.equal('p1001'); + expect(serverRequest.data.tid).to.equal('t1001'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(true); + expect(serverRequest.data.mimes).to.equal('video/mp4,application/javascript'); + expect(serverRequest.data.max_dur).to.equal(20); + expect(serverRequest.data.api).to.equal('1,2'); + expect(serverRequest.data.skippable).to.equal('ALLOW'); + }); + + it('Verify bid floor without price floors module', function() { + const bidRequestWithFloor = deepClone(bidRequests[0]); + bidRequestWithFloor.params.floor = 10 + bidRequestWithFloor.params.cur = 'USD' + + const serverRequests = spec.buildRequests([bidRequestWithFloor]); + let serverRequest = serverRequests[0]; + expect(serverRequest.data.cur).to.equal('USD'); + expect(serverRequest.data.floor).to.equal(10); + }); + + it('Verify bid floor with price floors module', function() { + const bidRequestWithFloor = deepClone(bidRequests[0]); + bidRequestWithFloor.params.floor = 10 + bidRequestWithFloor.params.cur = 'USD' + const getFloorResponse = {floor: 5, currency: 'EUR'}; + bidRequestWithFloor.getFloor = () => getFloorResponse; + + const serverRequests = spec.buildRequests([bidRequestWithFloor]); + let serverRequest = serverRequests[0]; + expect(serverRequest.data.cur).to.equal('EUR'); + expect(serverRequest.data.floor).to.equal(5); + }); + + it('Verify parse response', function () { + const serverResponse = { + body: { + bid_id: 'bid12345', + cpm: 2.25, + currency: 'USD', + size: '300x250', + creative_id: '1000', + ad: 'Ad markup' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(2.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('1000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.mediaType).to.equal('banner'); + expect(bid.ad).to.equal('Ad markup'); + }); + + it('Verify parse video response', function () { + const serverResponse = { + body: { + bid_id: 'bid789', + cpm: 3.25, + currency: 'USD', + size: '300x250', + creative_id: '2000', + vast_url: 'https://vast.xml/url' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('2000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid789'); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastUrl).to.equal('https://vast.xml/url'); + }); + + it('Verify bidder code', function () { + expect(spec.code).to.equal('getintent'); + }); + + it('Verify bidder aliases', function () { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('getintentAdapter'); + }); + + it('Verify supported media types', function () { + expect(spec.supportedMediaTypes).to.have.lengthOf(2); + expect(spec.supportedMediaTypes[0]).to.equal('video'); + expect(spec.supportedMediaTypes[1]).to.equal('banner'); + }); + + it('Verify if bid request valid', function () { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + expect(spec.isBidRequestValid(bidRequests[1])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { test: 123 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { pid: 111, tid: 222 } })).to.equal(true); + }); +}); From 42529cd476c7176468cb6ce5848269a0b9971444 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Tue, 20 Jul 2021 08:26:45 -0700 Subject: [PATCH 1297/1476] SharedIdSystem: return pubcid instead of sharedId (#7149) * Sharedid fix * Sharedid fix * Sharedid fix * Sharedid fix * Sharedid fix * Sharedid fix * Sharedid fix * Sharedid fix * Fix test failure * Fix test failure * Fix test failure * Fix test failure * Fixing decode on optout Co-authored-by: skocheri --- modules/sharedIdSystem.js | 269 +++++------------------ test/spec/modules/sharedIdSystem_spec.js | 126 +++++------ 2 files changed, 105 insertions(+), 290 deletions(-) diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 35848f7bfbc..2e3abd6b1a2 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -7,170 +7,66 @@ import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import { uspDataHandler, coppaDataHandler } from '../src/adapterManager.js'; +import { coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; const GVLID = 887; -export const storage = getStorageManager(GVLID, 'pubCommonId'); +const storage = getStorageManager(GVLID, 'pubCommonId'); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; -const SHAREDID_OPT_OUT_VALUE = '00000000000000000000000000'; -const SHAREDID_URL = 'https://id.sharedid.org/id'; -const SHAREDID_SUFFIX = '_sharedid'; -const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const SHAREDID_DEFAULT_STATE = true; - +const OPTOUT_NAME = '_pubcid_optout'; const PUB_COMMON_ID = 'PublisherCommonId'; /** - * Store sharedid in either cookie or local storage - * @param {Object} config Need config.storage object to derive key, expiry time, and storage type. - * @param {string} value Shareid value to store + * Read a value either from cookie or local storage + * @param {string} name Name of the item + * @param {string} type storage type override + * @returns {string|null} a string if item exists */ - -function storeData(config, value) { - try { - if (value) { - const key = config.storage.name + SHAREDID_SUFFIX; - const expiresStr = (new Date(Date.now() + (config.storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); - - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - storage.setCookie(key, value, expiresStr, 'LAX', sharedIdSystemSubmodule.domainOverride()); - } - } else if (config.storage.type === LOCAL_STORAGE) { - if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(`${key}_exp`, expiresStr); - storage.setDataInLocalStorage(key, value); - } +function readValue(name, type) { + if (type === COOKIE) { + return storage.getCookie(name); + } else if (type === LOCAL_STORAGE) { + if (storage.hasLocalStorage()) { + const expValue = storage.getDataFromLocalStorage(`${name}_exp`); + if (!expValue) { + return storage.getDataFromLocalStorage(name); + } else if ((new Date(expValue)).getTime() - Date.now() > 0) { + return storage.getDataFromLocalStorage(name) } } - } catch (error) { - utils.logError(error); } } -/** - * Read sharedid from cookie or local storage - * @param config Need config.storage to derive key and storage type - * @return {string} - */ -function readData(config) { - try { - const key = config.storage.name + SHAREDID_SUFFIX; - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - return storage.getCookie(key); - } - } else if (config.storage.type === LOCAL_STORAGE) { - if (storage.hasLocalStorage()) { - const expValue = storage.getDataFromLocalStorage(`${key}_exp`); - if (!expValue) { - return storage.getDataFromLocalStorage(key); - } else if ((new Date(expValue)).getTime() - Date.now() > 0) { - return storage.getDataFromLocalStorage(key) - } - } +function getIdCallback(pubcid, pixelCallback) { + return function (callback) { + if (typeof pixelCallback === 'function') { + pixelCallback(); } - } catch (error) { - utils.logError(error); + callback(pubcid); } } -/** - * Delete sharedid from cookie or local storage - * @param config Need config.storage to derive key and storage type - */ -function delData(config) { - try { - const key = config.storage.name + SHAREDID_SUFFIX; - if (config.storage.type === COOKIE) { - if (storage.cookiesAreEnabled()) { - storage.setCookie(key, '', EXPIRED_COOKIE_DATE); - } - } else if (config.storage.type === LOCAL_STORAGE) { - storage.removeDataFromLocalStorage(`${key}_exp`); - storage.removeDataFromLocalStorage(key); - } - } catch (error) { - utils.logError(error); +function queuePixelCallback(pixelUrl, id = '', callback) { + if (!pixelUrl) { + return; } -} -/** - * setup success and error handler for sharedid callback thru ajax - * @param {string} pubcid Current pubcommon id - * @param {function} callback userId module callback. - * @param {Object} config Need config.storage to derive sharedid storage params - * @return {{success: success, error: error}} - */ + // Use pubcid as a cache buster + const urlInfo = utils.parseUrl(pixelUrl); + urlInfo.search.id = encodeURIComponent('pubcid:' + id); + const targetUrl = utils.buildUrl(urlInfo); -function handleResponse(pubcid, callback, config) { - return { - success: function (responseBody) { - if (responseBody) { - try { - let responseObj = JSON.parse(responseBody); - utils.logInfo('PubCommonId: Generated SharedId: ' + responseObj.sharedId); - if (responseObj.sharedId) { - if (responseObj.sharedId !== SHAREDID_OPT_OUT_VALUE) { - // Store sharedId locally - storeData(config, responseObj.sharedId); - } else { - // Delete local copy if the user has opted out - delData(config); - } - } - // Pass pubcid even though there is no change in order to trigger decode - callback(pubcid); - } catch (error) { - utils.logError(error); - } - } - }, - error: function (statusText, responseBody) { - utils.logInfo('PubCommonId: failed to get sharedid'); - } - } + return function () { + utils.triggerPixel(targetUrl); + }; } -/** - * Builds and returns the shared Id URL with attached consent data if applicable - * @param {Object} consentData - * @return {string} - */ -function sharedIdUrl(consentData) { - const usPrivacyString = uspDataHandler.getConsentData(); - let sharedIdUrl = SHAREDID_URL; - if (usPrivacyString && typeof usPrivacyString === 'string') { - sharedIdUrl = `${SHAREDID_URL}?us_privacy=${usPrivacyString}`; - } - if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return sharedIdUrl; - if (usPrivacyString) { - sharedIdUrl = `${sharedIdUrl}&gdpr=1&gdpr_consent=${consentData.consentString}` - return sharedIdUrl; - } - sharedIdUrl = `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}`; - return sharedIdUrl +function hasOptedOut() { + return !!((storage.cookiesAreEnabled() && readValue(OPTOUT_NAME, COOKIE)) || + (storage.hasLocalStorage() && readValue(OPTOUT_NAME, LOCAL_STORAGE))); } -/** - * Wraps pixelCallback in order to call sharedid sync - * @param {string} pubcid Pubcommon id value - * @param {function|undefined} pixelCallback fires a pixel to first party server - * @param {Object} config Need config.storage to derive sharedid storage params. - * @return {function(...[*]=)} - */ - -function getIdCallback(pubcid, pixelCallback, config, consentData) { - return function (callback) { - if (typeof pixelCallback === 'function') { - pixelCallback(); - } - ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true}); - } -} export const sharedIdSystemSubmodule = { /** * used to link submodule with config @@ -183,20 +79,7 @@ export const sharedIdSystemSubmodule = { * @type {Number} */ gvlid: GVLID, - makeCallback: function (pixelUrl, id = '') { - if (!pixelUrl) { - return; - } - // Use pubcid as a cache buster - const urlInfo = utils.parseUrl(pixelUrl); - urlInfo.search.id = encodeURIComponent('pubcid:' + id); - const targetUrl = utils.buildUrl(urlInfo); - - return function () { - utils.triggerPixel(targetUrl); - }; - }, /** * decode the stored id value for passing to bid requests * @function @@ -205,14 +88,12 @@ export const sharedIdSystemSubmodule = { * @returns {{pubcid:string}} */ decode(value, config) { - const idObj = {'pubcid': value}; - const {params: {enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; - - if (enableSharedId) { - const sharedId = readData(config); - if (sharedId) idObj['sharedid'] = {id: sharedId}; + if (hasOptedOut()) { + utils.logInfo('PubCommonId decode: Has opted-out'); + return undefined; } - + utils.logInfo(' Decoded value PubCommonId ' + value); + const idObj = {'pubcid': value}; return idObj; }, /** @@ -224,12 +105,17 @@ export const sharedIdSystemSubmodule = { * @returns {IdResponse} */ getId: function (config = {}, consentData, storedId) { + if (hasOptedOut()) { + utils.logInfo('PubCommonId: Has opted-out'); + return; + } const coppa = coppaDataHandler.getCoppa(); + if (coppa) { utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const {params: {create = true, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + const {params: {create = true, pixelUrl} = {}} = config; let newId = storedId; if (!newId) { try { @@ -243,10 +129,8 @@ export const sharedIdSystemSubmodule = { if (!newId) newId = (create && utils.hasDeviceAccess()) ? utils.generateUUID() : undefined; } - const pixelCallback = this.makeCallback(pixelUrl, newId); - const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback; - - return {id: newId, callback: combinedCallback}; + const pixelCallback = queuePixelCallback(pixelUrl, newId); + return {id: newId, callback: getIdCallback(newId, pixelCallback)}; }, /** * performs action to extend an id. There are generally two ways to extend the expiration time @@ -259,8 +143,7 @@ export const sharedIdSystemSubmodule = { * having the script-side overwriting server-side. This applies to both pubcid and sharedid. * * On the other hand, if there is no pixelUrl, then the extendId should return storedId so that - * its expiration time is updated. Sharedid, however, will have to be updated by this submodule - * separately. + * its expiration time is updated. * * @function * @param {SubmoduleParams} [config] @@ -269,67 +152,25 @@ export const sharedIdSystemSubmodule = { * @returns {IdResponse|undefined} */ extendId: function(config = {}, consentData, storedId) { + if (hasOptedOut()) { + utils.logInfo('PubCommonId: Has opted-out'); + return {id: undefined}; + } const coppa = coppaDataHandler.getCoppa(); if (coppa) { utils.logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config; + const {params: {extend = false, pixelUrl} = {}} = config; if (extend) { - try { - if (typeof window[PUB_COMMON_ID] === 'object') { - if (enableSharedId) { - // If the page includes its own pubcid module, then there is nothing to do - // except to update sharedid's expiration time - storeData(config, readData(config)); - } - return; - } - } catch (e) { - } - if (pixelUrl) { - const callback = this.makeCallback(pixelUrl, storedId); + const callback = queuePixelCallback(pixelUrl, storedId); return {callback: callback}; } else { - if (enableSharedId) { - // Update with the same value to extend expiration time - storeData(config, readData(config)); - } return {id: storedId}; } } - }, - - /** - * @param {string} domain - * @param {HTMLDocument} document - * @return {(string|undefined)} - */ - domainOverride: function () { - const domainElements = document.domain.split('.'); - const cookieName = `_gd${Date.now()}`; - for (let i = 0, topDomain, testCookie; i < domainElements.length; i++) { - const nextDomain = domainElements.slice(i).join('.'); - - // write test cookie - storage.setCookie(cookieName, '1', undefined, undefined, nextDomain); - - // read test cookie to verify domain was valid - testCookie = storage.getCookie(cookieName); - - // delete test cookie - storage.setCookie(cookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, nextDomain); - - if (testCookie === '1') { - // cookie was written successfully using test domain so the topDomain is updated - topDomain = nextDomain; - } else { - // cookie failed to write using test domain so exit by returning the topDomain - return topDomain; - } - } } }; diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js index 4c3de9cfec0..534d0b3f381 100644 --- a/test/spec/modules/sharedIdSystem_spec.js +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -1,45 +1,34 @@ -import { - sharedIdSystemSubmodule, - storage -} from 'modules/sharedIdSystem.js'; -import { server } from 'test/mocks/xhr.js'; -import {uspDataHandler} from 'src/adapterManager'; +import {sharedIdSystemSubmodule, storage} from 'modules/sharedIdSystem.js'; +import {coppaDataHandler} from 'src/adapterManager'; + import sinon from 'sinon'; import * as utils from 'src/utils.js'; let expect = require('chai').expect; -describe('SharedId System', function() { +describe('SharedId System', function () { const UUID = '15fde1dc-1861-4894-afdf-b757272f3568'; - const START_TIME_MILLIS = 1234; - before(function() { + before(function () { sinon.stub(utils, 'generateUUID').returns(UUID); + sinon.stub(utils, 'logInfo'); }); - after(function() { + after(function () { utils.generateUUID.restore(); + utils.logInfo.restore(); }); - - describe('Xhr Requests from getId()', function() { - const SHAREDID_RESPONSE = {sharedId: 'testsharedid'}; + describe('SharedId System getId()', function () { const callbackSpy = sinon.spy(); - let uspConsentDataStub - let setCookeStub; + let coppaDataHandlerDataStub let sandbox; - beforeEach(function() { + beforeEach(function () { sandbox = sinon.sandbox.create(); - - uspConsentDataStub = sandbox.stub(uspDataHandler, 'getConsentData'); - setCookeStub = sandbox.stub(storage, 'setCookie'); - - sandbox.stub(storage, 'cookiesAreEnabled').returns(true); + coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa'); sandbox.stub(utils, 'hasDeviceAccess').returns(true); - - sandbox.useFakeTimers(START_TIME_MILLIS); - + coppaDataHandlerDataStub.returns(''); callbackSpy.resetHistory(); }); @@ -47,7 +36,7 @@ describe('SharedId System', function() { sandbox.restore(); }); - it('should call shared id endpoint without consent data and handle a valid response', function () { + it('should call UUID', function () { let config = { storage: { type: 'cookie', @@ -58,63 +47,48 @@ describe('SharedId System', function() { let submoduleCallback = sharedIdSystemSubmodule.getId(config, undefined).callback; submoduleCallback(callbackSpy); - - let request = server.requests[0]; - expect(request.url).to.equal('https://id.sharedid.org/id'); - expect(request.withCredentials).to.be.true; - - request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); - expect(callbackSpy.calledOnce).to.be.true; expect(callbackSpy.lastCall.lastArg).to.equal(UUID); - - expect(setCookeStub.calledThrice).to.be.true; - - let testCookieName = `_gd${START_TIME_MILLIS}`; - expect(setCookeStub.firstCall.args).to.eql([testCookieName, '1', undefined, undefined, 'localhost']); - expect(setCookeStub.secondCall.args).to.eql([testCookieName, '', 'Thu, 01 Jan 1970 00:00:01 GMT', undefined, 'localhost']); - - let expires = new Date(START_TIME_MILLIS + (config.storage.expires * (60 * 60 * 24 * 1000))).toUTCString(); - expect(setCookeStub.lastCall.args).to.eql(['_pubcid_sharedid', 'testsharedid', expires, 'LAX', undefined]); }); - - it('should call shared id endpoint with consent data and handle a valid response', function () { - let consentData = { - gdprApplies: true, - consentString: 'abc12345234', - }; - - let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, consentData).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - expect(request.url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234'); - expect(request.withCredentials).to.be.true; - - request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); - - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(UUID); + it('should log message if coppa is set', function () { + coppaDataHandlerDataStub.returns('true'); + sharedIdSystemSubmodule.getId({}); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); }); + }); + describe('SharedId System extendId()', function () { + const callbackSpy = sinon.spy(); + let coppaDataHandlerDataStub; + let sandbox; - it('should call shared id endpoint with usp consent data and handle a valid response', function () { - uspConsentDataStub.returns('1YYY'); - let consentData = { - gdprApplies: true, - consentString: 'abc12345234', + beforeEach(function () { + sandbox = sinon.sandbox.create(); + coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa'); + sandbox.stub(utils, 'hasDeviceAccess').returns(true); + callbackSpy.resetHistory(); + coppaDataHandlerDataStub.returns(''); + }); + afterEach(function () { + sandbox.restore(); + }); + it('should call UUID', function () { + let config = { + params: { + extend: true + }, + storage: { + type: 'cookie', + name: '_pubcid', + expires: 10 + } }; - - let submoduleCallback = sharedIdSystemSubmodule.getId(undefined, consentData).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - expect(request.url).to.equal('https://id.sharedid.org/id?us_privacy=1YYY&gdpr=1&gdpr_consent=abc12345234'); - expect(request.withCredentials).to.be.true; - - request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE)); - - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(UUID); + let pubcommId = sharedIdSystemSubmodule.extendId(config, undefined, 'TestId').id; + expect(pubcommId).to.equal('TestId'); + }); + it('should log message if coppa is set', function () { + coppaDataHandlerDataStub.returns('true'); + sharedIdSystemSubmodule.extendId({}, undefined, 'TestId'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); }); }); }); From 9a918866b20b479ac7ba79eab5cb82cf9ddfe77a Mon Sep 17 00:00:00 2001 From: redaguermas Date: Tue, 20 Jul 2021 10:52:11 -0700 Subject: [PATCH 1298/1476] Nobid Bid Adapter: add support for alias duration (#7177) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. * Added support for the "meta" attribute in bid response. * Added "duration" alias. * Removed package-lock.json * Added test for the Duration media alias. * Removed nobidBidAdapter.js.orig * Fixed aliad "gvlid" instead of "gvlib". Added use case. * Removed mistake file. * Remove IDE files. * Put back the original package-lock.json Co-authored-by: Reda Guermas --- modules/nobidBidAdapter.js | 10 +- test/spec/modules/nobidBidAdapter_spec.js | 138 ++++++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 3aabd8f0635..619b46ecd1f 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -4,9 +4,10 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); +const GVLID = 816; const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.3.0'; +const storage = getStorageManager(GVLID, BIDDER_CODE); +window.nobidVersion = '1.3.1'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -144,6 +145,7 @@ function nobidBuildRequests(bids, bidderRequest) { state['ref'] = document.referrer; state['gdpr'] = gdprConsent(bidderRequest); state['usp'] = uspConsent(bidderRequest); + state['pjbdr'] = (bidderRequest && bidderRequest.bidderCode) ? bidderRequest.bidderCode : 'nobid'; const sch = schain(bids); if (sch) state['schain'] = sch; const cop = coppa(); @@ -337,6 +339,10 @@ window.addEventListener('message', function (event) { }, false); export const spec = { code: BIDDER_CODE, + gvlid: GVLID, + aliases: [ + { code: 'duration', gvlid: 674 } + ], supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index c44b0ce3fc2..f775e439a80 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -50,6 +50,142 @@ describe('Nobid Adapter', function () { }); }); + describe('isDurationBidRequestValid', function () { + const SITE_ID = 2; + const REFERER = 'https://www.examplereferer.com'; + const BIDDER_CODE = 'duration'; + let bidRequests = [ + { + 'bidder': BIDDER_CODE, + 'params': { + 'siteId': SITE_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + let bidderRequest = { + refererInfo: {referer: REFERER}, bidderCode: BIDDER_CODE + } + + it('should add source and version to the tag', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.sid).to.equal(SITE_ID); + expect(payload.pjbdr).to.equal(BIDDER_CODE); + expect(payload.l).to.exist.and.to.equal(encodeURIComponent(REFERER)); + expect(payload.tt).to.exist; + expect(payload.a).to.exist; + expect(payload.t).to.exist; + expect(payload.tz).to.exist; + expect(payload.r).to.exist; + expect(payload.lang).to.exist; + expect(payload.ref).to.exist; + expect(payload.gdpr).to.exist; + }); + + it('sends bid request to ad size', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a.length).to.exist.and.to.equal(1); + expect(payload.a[0].z[0][0]).to.equal(300); + expect(payload.a[0].z[0][1]).to.equal(250); + }); + + it('sends bid request to div id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].d).to.equal('adunit-code'); + }); + + it('sends bid request to site id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].sid).to.equal(2); + expect(payload.a[0].at).to.equal('banner'); + expect(payload.a[0].params.siteId).to.equal(2); + }); + + it('sends bid request to ad type', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.a).to.exist; + expect(payload.a[0].at).to.equal('banner'); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.contain('ads.servenobid.com/adreq'); + expect(request.method).to.equal('POST'); + }); + + it('should add gdpr consent information to the request', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consentString).to.exist.and.to.equal(consentString); + expect(payload.gdpr.consentRequired).to.exist.and.to.be.true; + }); + + it('should add gdpr consent information to the request', function () { + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + gdprApplies: false + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consentString).to.not.exist; + expect(payload.gdpr.consentRequired).to.exist.and.to.be.false; + }); + + it('should add usp consent information to the request', function () { + let bidderRequest = { + 'bidderCode': 'nobid', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': '1Y-N' + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.usp).to.exist; + expect(payload.usp).to.exist.and.to.equal('1Y-N'); + }); + }); + describe('isVideoBidRequestValid', function () { let bid = { bidder: 'nobid', @@ -114,6 +250,7 @@ describe('Nobid Adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.equal(SITE_ID); + expect(payload.pjbdr).to.equal('nobid'); expect(payload.l).to.exist.and.to.equal(encodeURIComponent(REFERER)); expect(payload.a).to.exist; expect(payload.t).to.exist; @@ -202,6 +339,7 @@ describe('Nobid Adapter', function () { it('should add source and version to the tag', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); + expect(payload.pjbdr).to.equal('nobid'); expect(payload.sid).to.equal(SITE_ID); expect(payload.l).to.exist.and.to.equal(encodeURIComponent(REFERER)); expect(payload.a).to.exist; From 11ce031a44fac894a41ca487cdfcde93caad2298 Mon Sep 17 00:00:00 2001 From: jsfledd Date: Tue, 20 Jul 2021 11:55:53 -0700 Subject: [PATCH 1299/1476] Nativo Bid Adapter: Identify refreshed inventory to bidder endpoint (#7201) --- modules/nativoBidAdapter.js | 9 ++++++++- test/spec/modules/nativoBidAdapter_spec.js | 17 +++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index fc0925bf2ca..880ff4b6a8e 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -11,6 +11,7 @@ const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] const bidRequestMap = {} +const adUnitsRequested = {} // Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html @@ -57,6 +58,8 @@ export const spec = { // 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]++ : 0 return { adUnitCode: adUnit.adUnitCode, mediaTypes: adUnit.mediaTypes, @@ -72,10 +75,14 @@ export const spec = { key: 'ntv_ppc', value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 }, + { + key: 'ntv_dbr', + value: btoa(JSON.stringify(adUnitsRequested)) + }, { key: 'ntv_url', value: encodeURIComponent(pageUrl), - }, + } ] if (bidderRequest.gdprConsent) { diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 6f489a65d3c..4202b7c6f91 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -69,6 +69,7 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pb_rid') expect(request.url).to.include('ntv_ppc') expect(request.url).to.include('ntv_url') + expect(request.url).to.include('ntv_dbr') }) }) }) @@ -121,8 +122,8 @@ describe('interpretResponse', function () { bids: [ { params: { - placementId: 1 - } + placementId: 1, + }, }, ], } @@ -173,11 +174,11 @@ describe('getUserSyncs', function () { const gdprConsent = { gdprApplies: true, - consentString: '111111' + consentString: '111111', } const uspConsent = { - uspConsent: '1YYY' + uspConsent: '1YYY', } it('Returns empty array if no supported user syncs', function () { @@ -207,7 +208,9 @@ describe('getUserSyncs', function () { 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.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + expect(userSync[0].url).to.contain( + 'gdpr=1&gdpr_consent=111111&us_privacy=1YYY' + ) }) it('Returns valid URL and type', function () { @@ -224,6 +227,8 @@ describe('getUserSyncs', function () { 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.contain('gdpr=1&gdpr_consent=111111&us_privacy=1YYY') + expect(userSync[0].url).to.contain( + 'gdpr=1&gdpr_consent=111111&us_privacy=1YYY' + ) }) }) From 17aad75e2ce92e83312c8a5ad6146cfce3f4bf15 Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:57:40 +0400 Subject: [PATCH 1300/1476] DSPX Adapter: Fix userSync problem with passback responses (#7199) Co-authored-by: Alexander --- modules/dspxBidAdapter.js | 24 +++++++++++++----------- test/spec/modules/dspxBidAdapter_spec.js | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 9f17b69fb02..df0f6f3b8ea 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -152,17 +152,19 @@ export const spec = { } } - if (syncOptions.iframeEnabled) { - serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ - type: 'iframe', - url: appendToUrl(url, gdprParams) - })); - } - if (syncOptions.pixelEnabled && serverResponses.length > 0) { - serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ - type: 'image', - url: appendToUrl(url, gdprParams) - })); + if (serverResponses.length > 0 && serverResponses[0].body.userSync) { + if (syncOptions.iframeEnabled) { + serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ + type: 'iframe', + url: appendToUrl(url, gdprParams) + })); + } + if (syncOptions.pixelEnabled) { + serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ + type: 'image', + url: appendToUrl(url, gdprParams) + })); + } } return syncs; } diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js index a4abf46e90f..09f40895ec9 100644 --- a/test/spec/modules/dspxBidAdapter_spec.js +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -358,4 +358,27 @@ describe('dspxAdapter', function () { expect(userSync[2].type).to.be.equal('image'); }); }); + + describe(`getUserSyncs test usage in passback response`, function () { + let serverResponses; + + beforeEach(function () { + serverResponses = [{ + body: { + reason: 8002, + status: 'error', + msg: 'passback', + } + }]; + }); + + it(`check for zero array when iframeEnabled`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true })).to.be.an('array'); + expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses).length).to.be.equal(0); + }); + it(`check for zero array when iframeEnabled`, function () { + expect(spec.getUserSyncs({ pixelEnabled: true })).to.be.an('array'); + expect(spec.getUserSyncs({ pixelEnabled: true }, serverResponses).length).to.be.equal(0); + }); + }); }); From b39e27b3861189b5d92c9e513112d48dc41d0183 Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Wed, 21 Jul 2021 16:21:30 +0400 Subject: [PATCH 1301/1476] Rads Adapter: Fix userSync problem with passback responses (#7200) Co-authored-by: Alexander --- modules/radsBidAdapter.js | 24 ++++++++++++----------- test/spec/modules/radsBidAdapter_spec.js | 25 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index 2db42802067..5cc88440629 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -119,17 +119,19 @@ export const spec = { } } - if (syncOptions.iframeEnabled) { - serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ - type: 'iframe', - url: appendToUrl(url, gdprParams) - })); - } - if (syncOptions.pixelEnabled && serverResponses.length > 0) { - serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ - type: 'image', - url: appendToUrl(url, gdprParams) - })); + if (serverResponses.length > 0 && serverResponses[0].body.userSync) { + if (syncOptions.iframeEnabled) { + serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ + type: 'iframe', + url: appendToUrl(url, gdprParams) + })); + } + if (syncOptions.pixelEnabled) { + serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ + type: 'image', + url: appendToUrl(url, gdprParams) + })); + } } return syncs; } diff --git a/test/spec/modules/radsBidAdapter_spec.js b/test/spec/modules/radsBidAdapter_spec.js index 019108fce9d..271f7cb1147 100644 --- a/test/spec/modules/radsBidAdapter_spec.js +++ b/test/spec/modules/radsBidAdapter_spec.js @@ -307,4 +307,29 @@ describe('radsAdapter', function () { expect(userSync[2].type).to.be.equal('image'); }); }); + + describe(`getUserSyncs test usage passback response`, function () { + let serverResponses; + + beforeEach(function () { + serverResponses = [{ + body: { + reason: 8002, + status: 'rejected', + msg: 'passback', + bid_id: '115de76437d5ae6', + 'zone': '4773', + } + }]; + }); + + it(`check for zero array when iframeEnabled`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true })).to.be.an('array'); + expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses).length).to.be.equal(0); + }); + it(`check for zero array when iframeEnabled`, function () { + expect(spec.getUserSyncs({ pixelEnabled: true })).to.be.an('array'); + expect(spec.getUserSyncs({ pixelEnabled: true }, serverResponses).length).to.be.equal(0); + }); + }); }); From 1381f34721bf6337ade25c1160a993f130ea8032 Mon Sep 17 00:00:00 2001 From: Igor Tchibirev Date: Wed, 21 Jul 2021 13:33:48 -0400 Subject: [PATCH 1302/1476] Realvu Analytics Adapter: improve flagging of 'display:none' containers (#7182) * Remove _ps in _f=conf request * Replace " * realvuAnalyticsAdapter_spec updated * Update realvuAnalyticsAdapter.js * Update realvuAnalyticsAdapter.js Improve value returned by addUnitById() * Update realvuAnalyticsAdapter.js improve flagging of 'display:none' containers * Update realvuAnalyticsAdapter.js Co-authored-by: Igor Tchibirev --- modules/realvuAnalyticsAdapter.js | 62 ++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 099bfda5e32..5540d136dea 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -306,28 +306,46 @@ export let lib = { if (!restored) { a.target = z.questA(a.div); let target = (a.target !== null) ? a.target : a.div; - a.box.w = Math.max(target.offsetWidth, a.w); - a.box.h = Math.max(target.offsetHeight, a.h); - let q = z.findPosG(target); - let pad = {}; - pad.t = z.padd(target, 'Top'); - pad.l = z.padd(target, 'Left'); - pad.r = z.padd(target, 'Right'); - pad.b = z.padd(target, 'Bottom'); - let ax = q.x + pad.l; - let ay = q.y + pad.t; - a.box.x = ax; - a.box.y = ay; - if (a.box.w > a.w && a.box.w > 1) { - ax += (a.box.w - a.w - pad.l - pad.r) / 2; - } - if (a.box.h > a.h && a.box.h > 1) { - ay += (a.box.h - a.h - pad.t - pad.b) / 2; - } - if ((ax > 0 && ay > 0) && (a.x != ax || a.y != ay)) { - a.x = ax; - a.y = ay; - z.writePos(a); + if (window.getComputedStyle(target).display == 'none') { + let targSibl = target.previousElementSibling; // for 'none' containers on mobile define y as previous sibling y+h + if (targSibl) { + let q = z.findPosG(targSibl); + a.x = q.x; + a.y = q.y + targSibl.offsetHeight; + } else { + target = target.parentNode; + let q = z.findPosG(target); + a.x = q.x; + a.y = q.y; + } + a.box.x = a.x; + a.box.y = a.y; + a.box.w = a.w; + a.box.h = a.h; + } else { + a.box.w = Math.max(target.offsetWidth, a.w); + a.box.h = Math.max(target.offsetHeight, a.h); + let q = z.findPosG(target); + let pad = {}; + pad.t = z.padd(target, 'Top'); + pad.l = z.padd(target, 'Left'); + pad.r = z.padd(target, 'Right'); + pad.b = z.padd(target, 'Bottom'); + let ax = q.x + pad.l; + let ay = q.y + pad.t; + a.box.x = ax; + a.box.y = ay; + if (a.box.w > a.w && a.box.w > 1) { + ax += (a.box.w - a.w - pad.l - pad.r) / 2; + } + if (a.box.h > a.h && a.box.h > 1) { + ay += (a.box.h - a.h - pad.t - pad.b) / 2; + } + if ((ax > 0 && ay > 0) && (a.x != ax || a.y != ay)) { + a.x = ax; + a.y = ay; + z.writePos(a); + } } } let vtr = ((a.box.w * a.box.h) < 242500) ? 49 : 29; // treashfold more then 49% and more then 29% for "oversized" From df23aec379bb1cca27ef5e9fc85d8017a2a8680d Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 21 Jul 2021 22:16:40 +0200 Subject: [PATCH 1303/1476] tappx Bid Adapter: add internal Tappx params and refactor pageUrl (#7181) * tappxBidAdapter :: update adapter version * tappxBidAdapter :: update and add tappx internal params * tappxBidAdapter :: add mktag to overview * tappxBidAdapter :: update way getting pageUrl * tapppxBidAdapter :: update pageUrl * tappxBidAdapter :: remove unused params * tappxBidAdapter :: add bcid and bcrid to requests Co-authored-by: marc_tappx --- modules/tappxBidAdapter.js | 60 ++++++++++++++++++++++++++++---------- modules/tappxBidAdapter.md | 42 ++++++++++++++++---------- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 3c7274d1497..927d60277cd 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10623'; +const TAPPX_BIDDER_VERSION = '0.1.10714'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream']; @@ -225,11 +225,11 @@ function buildOneRequest(validBidRequests, bidderRequest) { hostDomain = hostInfo.domain; const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); + const MKTAG = utils.deepAccess(validBidRequests, 'params.mktag'); const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); const BIDEXTRA = utils.deepAccess(validBidRequests, 'params.ext'); const bannerMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.banner'); const videoMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.video'); - const { refererInfo } = bidderRequest; // let requests = []; let payload = {}; @@ -249,12 +249,12 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.app = app; api[0] = utils.deepAccess(validBidRequests, 'params.api') ? utils.deepAccess(validBidRequests, 'params.api') : [3, 5]; } else { + let bundle = _extractPageUrl(validBidRequests, bidderRequest); let site = {}; - site.name = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; - site.bundle = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; - site.domain = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; - publisher.name = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; - publisher.domain = (bidderRequest && refererInfo) ? utils.parseUrl(refererInfo.referer).hostname : window.location.hostname; + site.name = bundle; + site.domain = bundle; + publisher.name = bundle; + publisher.domain = bundle; tagid = `${site.name}_typeAdBanVid_${getOs()}`; payload.site = site; } @@ -339,7 +339,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { } let bidder = {}; - bidder.tappxkey = TAPPXKEY; bidder.endpoint = ENDPOINT; bidder.host = hostInfo.url; bidder.bidfloor = BIDFLOOR; @@ -368,12 +367,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { geo.country = utils.deepAccess(validBidRequests, 'params.geo.country'); // < Device object - // > Params - let params = {}; - params.host = 'tappx.com'; - params.bidfloor = BIDFLOOR; - // < Params - // > GDPR let user = {}; user.ext = {}; @@ -410,6 +403,15 @@ function buildOneRequest(validBidRequests, bidderRequest) { } // < GDPR + // > Payload Ext + let payloadExt = {}; + payloadExt.bidder = {}; + payloadExt.bidder.tappxkey = TAPPXKEY; + payloadExt.bidder.mktag = MKTAG; + payloadExt.bidder.bcid = utils.deepAccess(validBidRequests, 'params.bcid'); + payloadExt.bidder.bcrid = utils.deepAccess(validBidRequests, 'params.bcrid'); + // < Payload Ext + // > Payload payload.id = validBidRequests.auctionId; payload.test = utils.deepAccess(validBidRequests, 'params.test') ? 1 : 0; @@ -418,9 +420,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.bidder = BIDDER_CODE; payload.imp = [imp]; payload.user = user; + payload.ext = payloadExt; payload.device = device; - payload.params = params; payload.regs = regs; // < Payload @@ -491,4 +493,32 @@ export function _checkParamDataType(key, value, datatype) { return undefined; } +export function _extractPageUrl(validBidRequests, bidderRequest) { + let domainUrl = utils.deepAccess(validBidRequests, 'params.domainUrl'); + + if (typeof domainUrl == 'undefined' || domainUrl == null) { + domainUrl = config.getConfig('pageUrl') || utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl'); + } + + if (typeof domainUrl == 'undefined' || domainUrl == null) { + try { + domainUrl = window.top.document.head.querySelector('link[rel="canonical"][href]').getAttribute('href'); + } catch (error) { + domainUrl = undefined; + } + } + + try { + domainUrl = domainUrl.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img)[0].replace(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?/img, ''); + } catch (error) { + domainUrl = undefined; + } + + if (typeof domainUrl == 'undefined' || domainUrl == null) { + domainUrl = window.location.hostname; + } + + return domainUrl; +} + registerBidder(spec); diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index 3ec4a4a5829..f98b996c52a 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -29,7 +29,12 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 tappxkey: "pub-1234-android-1234", endpoint: "ZZ1234PBJS", bidfloor: 0.005, - test: true // Optional for testing purposes + mktag: "123456", // Optional: tappx mktag + test: true, // Optional: for testing purposes + domainUrl: "www.example.com", // Optional: add domain site + ext: { // Optional: extra params + foo: "bar" + } } } ] @@ -62,21 +67,26 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 tappxkey: "pub-1234-desktop-1234", endpoint: "VZ12TESTCTV", bidfloor: 0.005, - test: true, - video: { // optional - skippable: true, // optional - minduration: 5, // optional - maxduration: 30, // optional - startdelay: 5, // optional - playbackmethod: [1,3], // optional - api: [ 1, 2 ], // optional - protocols: [ 2, 3 ], // optional - battr: [ 13, 14 ], // optional - linearity: 1, // optional - placement: 2, // optional - minbitrate: 10, // optional - maxbitrate: 10 // optional - } + mktag: "123456", // Optional: tappx mktag + test: true, // Optional: for testing purposes + domainUrl: "www.example.com", // Optional: add domain site + video: { // Optional + skippable: true, // Optional + minduration: 5, // Optional + maxduration: 30, // Optional + startdelay: 5, // Optional + playbackmethod: [1,3], // Optional + api: [ 1, 2 ], // Optional + protocols: [ 2, 3 ], // Optional + battr: [ 13, 14 ], // Optional + linearity: 1, // Optional + placement: 2, // Optional + minbitrate: 10, // Optional + maxbitrate: 10 // Optional + }, + ext: { // Optional: extra params + foo: "bar" + } } }] } From b4c2552aa1bc847c37b0902f8befee33399fc984 Mon Sep 17 00:00:00 2001 From: cs83 Date: Thu, 22 Jul 2021 15:49:06 +0300 Subject: [PATCH 1304/1476] Smartico Bid Adapter: Add meta fields to interpreted response object (#7203) * Adding smartico adapter * bug #6486 fix, added maintainer email * bug #6486 fix, modified test parameters * bug #6486 fix, modified test parameters #2 * #6486 applied review related updates & fixes * #6486 applied review related updates & fixes #2 * #6486 applied review related updates & fixes #3 * samrtico adapter bug fix * smartico adapter unit test update after bug fixing * smartico adapter bug fix #2 * smartico adapter bug fix #3 * fix linting errors * update for version 5.0: meta.advertiserDomains is added * update for version 5.0: meta.advertiserDomains is added (unit test) update * Update smarticoBidAdapter_spec.js Co-authored-by: Dmitri Co-authored-by: Chris Huie Co-authored-by: Patrick McCann --- modules/smarticoBidAdapter.js | 6 +++++- test/spec/modules/smarticoBidAdapter_spec.js | 13 +++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/modules/smarticoBidAdapter.js b/modules/smarticoBidAdapter.js index e923ed98e16..2399a12f932 100644 --- a/modules/smarticoBidAdapter.js +++ b/modules/smarticoBidAdapter.js @@ -106,7 +106,11 @@ export const spec = { netRevenue: !!ad.netRevenue, currency: ad.currency, ttl: ad.ttl, - ad: html + ad: html, + meta: { + advertiserDomains: ad.domains, + advertiserName: ad.title + } } bidResponses.push(bidObject); } diff --git a/test/spec/modules/smarticoBidAdapter_spec.js b/test/spec/modules/smarticoBidAdapter_spec.js index 75402704e13..104fa22a851 100644 --- a/test/spec/modules/smarticoBidAdapter_spec.js +++ b/test/spec/modules/smarticoBidAdapter_spec.js @@ -81,7 +81,9 @@ describe('smarticoBidAdapter', function () { ttl: 30, bannerFormatWidth: 300, bannerFormatHeight: 250, - bannerFormatAlias: 'medium_rectangle' + bannerFormatAlias: 'medium_rectangle', + domains: ['www.advertiser.com'], + title: 'Advertiser' }] }; let expectedResponse = [{ @@ -93,7 +95,11 @@ describe('smarticoBidAdapter', function () { currency: 'EUR', netRevenue: false, // gross ttl: 30, - ad: '"'; describe('teadsBidAdapter', () => { const adapter = newBidder(spec); + let cookiesAreEnabledStub, getCookieStub; + + beforeEach(function () { + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); + }); + + afterEach(function () { + cookiesAreEnabledStub.restore(); + getCookieStub.restore(); + }); describe('inherited functions', () => { it('exists and is a function', () => { @@ -102,7 +114,7 @@ describe('teadsBidAdapter', () => { 'timeout': 3000 }; - it('sends bid request to ENDPOINT via POST', function() { + it('should send bid request to ENDPOINT via POST', function() { const request = spec.buildRequests(bidRequests, bidderResquestDefault); expect(request.url).to.equal(ENDPOINT); @@ -274,7 +286,6 @@ describe('teadsBidAdapter', () => { }); it('should send GDPR to endpoint with 22 status', function() { - let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; let bidderRequest = { 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', @@ -322,7 +333,6 @@ describe('teadsBidAdapter', () => { }); it('should send GDPR to endpoint with 0 status when gdprApplies = false (vendorData = undefined)', function() { - let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; let bidderRequest = { 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', @@ -377,7 +387,7 @@ describe('teadsBidAdapter', () => { } } }; - checkMediaTypesSizes(mediaTypesPlayerSize, '32x34') + checkMediaTypesSizes(mediaTypesPlayerSize, '32x34'); }); it('should add schain info to payload if available', function () { @@ -416,7 +426,7 @@ describe('teadsBidAdapter', () => { } } }; - checkMediaTypesSizes(mediaTypesVideoSizes, '12x14') + checkMediaTypesSizes(mediaTypesVideoSizes, '12x14'); }); it('should use good mediaTypes banner sizes', function() { @@ -427,7 +437,7 @@ describe('teadsBidAdapter', () => { } } }; - checkMediaTypesSizes(mediaTypesBannerSize, '46x48') + checkMediaTypesSizes(mediaTypesBannerSize, '46x48'); }); it('should use good mediaTypes for both video and banner sizes', function() { @@ -441,7 +451,135 @@ describe('teadsBidAdapter', () => { } } }; - checkMediaTypesSizes(hybridMediaTypes, ['46x48', '50x34', '45x45']) + checkMediaTypesSizes(hybridMediaTypes, ['46x48', '50x34', '45x45']); + }); + + describe('User IDs', function () { + const baseBidRequest = { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394, + 'pageId': 1234 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee', + 'deviceWidth': 1680 + }; + + describe('FLoC ID', function () { + it('should not add cohortId and cohortVersion params to payload if FLoC ID system is not enabled', function () { + const bidRequest = { + ...baseBidRequest, + userId: {} // no "flocId" property -> assumption that the FLoC ID system is disabled + }; + + const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload).not.to.have.property('cohortId'); + expect(payload).not.to.have.property('cohortVersion'); + }); + + it('should add cohortId param to payload if FLoC ID system is enabled and ID available, but not version', function () { + const bidRequest = { + ...baseBidRequest, + userId: { + flocId: { + id: 'my-floc-id' + } + } + }; + + const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.cohortId).to.equal('my-floc-id'); + expect(payload).not.to.have.property('cohortVersion'); + }); + + it('should add cohortId and cohortVersion params to payload if FLoC ID system is enabled', function () { + const bidRequest = { + ...baseBidRequest, + userId: { + flocId: { + id: 'my-floc-id', + version: 'chrome.1.1' + } + } + }; + + const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.cohortId).to.equal('my-floc-id'); + expect(payload.cohortVersion).to.equal('chrome.1.1'); + }); + }); + + describe('Unified ID v2', function () { + it('should not add unifiedId2 param to payload if uid2 system is not enabled', function () { + const bidRequest = { + ...baseBidRequest, + userId: {} // no "uid2" property -> assumption that the Unified ID v2 system is disabled + }; + + const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload).not.to.have.property('unifiedId2'); + }); + + it('should add unifiedId2 param to payload if uid2 system is enabled', function () { + const bidRequest = { + ...baseBidRequest, + userId: { + uid2: { + id: 'my-unified-id-2' + } + } + }; + + const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.unifiedId2).to.equal('my-unified-id-2'); + }) + }); + + describe('First-party cookie Teads ID', function () { + it('should not add firstPartyCookieTeadsId param to payload if cookies are not enabled', function () { + cookiesAreEnabledStub.returns(false); + + const request = spec.buildRequests([baseBidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload).not.to.have.property('firstPartyCookieTeadsId'); + }); + + it('should not add firstPartyCookieTeadsId param to payload if first-party cookie is not available', function () { + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs('_tfpvi').returns(undefined); + + const request = spec.buildRequests([baseBidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload).not.to.have.property('firstPartyCookieTeadsId'); + }); + + it('should add firstPartyCookieTeadsId param to payload if first-party cookie is available', function () { + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs('_tfpvi').returns('my-teads-id'); + + const request = spec.buildRequests([baseBidRequest], bidderResquestDefault); + const payload = JSON.parse(request.data); + + expect(payload.firstPartyCookieTeadsId).to.equal('my-teads-id'); + }); + }); }); function checkMediaTypesSizes(mediaTypes, expectedSizes) { From 71c1c6722f6c61aedec934d228781e4275a2fbbf Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Mon, 2 Aug 2021 20:07:23 +0200 Subject: [PATCH 1334/1476] Mediasquare bidder: add metrics to onBidWon Event (#7252) * Update mediasquareBidAdapter.js * Update mediasquareBidAdapter.js * Update mediasquareBidAdapter_spec.js * Update mediasquareBidAdapter_spec.js * Update mediasquareBidAdapter.js --- modules/mediasquareBidAdapter.js | 6 +++++- test/spec/modules/mediasquareBidAdapter_spec.js | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index a586157bf20..e442b01a115 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -106,6 +106,9 @@ export const spec = { 'advertiserDomains': value['adomain'] } }; + if ('match' in value) { + bidResponse['mediasquare']['match'] = value['match']; + } if ('native' in value) { bidResponse['native'] = value['native']; bidResponse['mediaType'] = 'native'; @@ -156,12 +159,13 @@ export const spec = { if (bid.hasOwnProperty('mediasquare')) { if (bid['mediasquare'].hasOwnProperty('bidder')) { params.push('bidder=' + bid['mediasquare']['bidder']); } if (bid['mediasquare'].hasOwnProperty('code')) { params.push('code=' + bid['mediasquare']['code']); } + if (bid['mediasquare'].hasOwnProperty('match')) { params.push('match=' + bid['mediasquare']['match']); } }; for (let i = 0; i < paramsToSearchFor.length; i++) { if (bid.hasOwnProperty(paramsToSearchFor[i])) { params.push(paramsToSearchFor[i] + '=' + bid[paramsToSearchFor[i]]); } } if (params.length > 0) { params = '?' + params.join('&'); } - ajax(endpoint + BIDDER_ENDPOINT_WINNING + params, null); + ajax(endpoint + BIDDER_ENDPOINT_WINNING + params, null, undefined, {method: 'GET', withCredentials: true}); return true; } diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 20e5588a99e..f3f09a8ddf8 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -140,7 +140,14 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.meta.advertiserDomains).to.exist; expect(bid.meta.advertiserDomains).to.have.lengthOf(1); }); - + it('Verifies match', function () { + const request = spec.buildRequests(DEFAULT_PARAMS, DEFAULT_OPTIONS); + BID_RESPONSE.body.responses[0].match = true; + const response = spec.interpretResponse(BID_RESPONSE, request); + const bid = response[0]; + expect(bid.mediasquare.match).to.exist; + expect(bid.mediasquare.match).to.equal(true); + }); it('Verifies bidder code', function () { expect(spec.code).to.equal('mediasquare'); }); From 3162aa93ad2e8951ea3a817664715ecaa63d88fe Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Mon, 2 Aug 2021 14:14:44 -0400 Subject: [PATCH 1335/1476] gptPreAuction Module: update to include MCM support (#7242) * Updating gptPreAuction module to include MCM support * Fix lint error * Default getConfig to object * Added unit tests * Fix unit test desc typo Co-authored-by: Michael Moschovas --- modules/gptPreAuction.js | 12 +++++++++++- test/spec/modules/gptPreAuction_spec.js | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js index ee2b5406453..c351a5181a9 100644 --- a/modules/gptPreAuction.js +++ b/modules/gptPreAuction.js @@ -33,11 +33,21 @@ export const appendGptSlots = adUnits => { const context = adUnit.ortb2Imp.ext.data; context.adserver = context.adserver || {}; context.adserver.name = 'gam'; - context.adserver.adslot = slot.getAdUnitPath(); + context.adserver.adslot = sanitizeSlotPath(slot.getAdUnitPath()); } }); }; +const sanitizeSlotPath = (path) => { + const gptConfig = config.getConfig('gptPreAuction') || {}; + + if (gptConfig.mcmEnabled) { + return path.replace(/(^\/\d*),\d*\//, '$1/'); + } + + return path; +} + export const appendPbAdSlot = adUnit => { adUnit.ortb2Imp = adUnit.ortb2Imp || {}; adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; diff --git a/test/spec/modules/gptPreAuction_spec.js b/test/spec/modules/gptPreAuction_spec.js index c4a81c21d5c..3e8dbfe8d92 100644 --- a/test/spec/modules/gptPreAuction_spec.js +++ b/test/spec/modules/gptPreAuction_spec.js @@ -95,6 +95,31 @@ describe('GPT pre-auction module', () => { expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode2' }); }); + it('will trim child id if mcmEnabled is set to true', () => { + config.setConfig({ gptPreAuction: { enabled: true, mcmEnabled: true } }); + window.googletag.pubads().setSlots([ + makeSlot({ code: '/12345,21212/slotCode1', divId: 'div1' }), + makeSlot({ code: '/12345,21212/slotCode2', divId: 'div2' }), + makeSlot({ code: '/12345,21212/slotCode3', divId: 'div3' }) + ]); + const adUnit = { code: '/12345,21212/slotCode2', ortb2Imp: { ext: { data: {} } } }; + appendGptSlots([adUnit]); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: '/12345/slotCode2' }); + }); + + it('will not trim child id if mcmEnabled is not set to true', () => { + window.googletag.pubads().setSlots([ + makeSlot({ code: '/12345,21212/slotCode1', divId: 'div1' }), + makeSlot({ code: '/12345,21212/slotCode2', divId: 'div2' }), + makeSlot({ code: '/12345,21212/slotCode3', divId: 'div3' }) + ]); + const adUnit = { code: '/12345,21212/slotCode2', ortb2Imp: { ext: { data: {} } } }; + appendGptSlots([adUnit]); + expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); + expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: '/12345,21212/slotCode2' }); + }); + it('should use the customGptSlotMatching function if one is given', () => { config.setConfig({ gptPreAuction: { From adb17317696aae924c70a4096b676a587c2a2e73 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 2 Aug 2021 22:24:13 +0100 Subject: [PATCH 1336/1476] AirGrid RTD Submodule: Initial Release (#7108) --- .../gpt/airgridRtdProvider_example.html | 152 ++++++++++++++++++ modules/airgridRtdProvider.js | 138 ++++++++++++++++ modules/airgridRtdProvider.md | 95 +++++++++++ test/spec/modules/airgridRtdProvider_spec.js | 97 +++++++++++ 4 files changed, 482 insertions(+) create mode 100644 integrationExamples/gpt/airgridRtdProvider_example.html create mode 100644 modules/airgridRtdProvider.js create mode 100644 modules/airgridRtdProvider.md create mode 100644 test/spec/modules/airgridRtdProvider_spec.js diff --git a/integrationExamples/gpt/airgridRtdProvider_example.html b/integrationExamples/gpt/airgridRtdProvider_example.html new file mode 100644 index 00000000000..a8fd989f682 --- /dev/null +++ b/integrationExamples/gpt/airgridRtdProvider_example.html @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + +

AirGrid RTD Prebid

+ +
+ +
+ + AirGrid Audiences: +
+ + diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js new file mode 100644 index 00000000000..8d212204da8 --- /dev/null +++ b/modules/airgridRtdProvider.js @@ -0,0 +1,138 @@ +/** + * This module adds the AirGrid provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch real-time audience data from AirGrid + * @module modules/airgridRtdProvider + * @requires module:modules/realTimeData + */ +import {config} from '../src/config.js'; +import {submodule} from '../src/hook.js'; +import {mergeDeep, isPlainObject, deepSetValue, deepAccess} from '../src/utils.js'; +import {getGlobal} from '../src/prebidGlobal.js'; +import {getStorageManager} from '../src/storageManager.js'; + +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'airgrid'; +const AG_TCF_ID = 782; +export const AG_AUDIENCE_IDS_KEY = 'edkt_matched_audience_ids' + +export const storage = getStorageManager(AG_TCF_ID, SUBMODULE_NAME); + +/** + * Attach script tag to DOM + * @param {Object} rtdConfig + * @return {void} + */ +export function attachScriptTagToDOM(rtdConfig) { + var edktInitializor = window.edktInitializor = window.edktInitializor || {}; + if (!edktInitializor.invoked) { + edktInitializor.invoked = true; + edktInitializor.accountId = rtdConfig.params.accountId; + edktInitializor.publisherId = rtdConfig.params.publisherId; + edktInitializor.apiKey = rtdConfig.params.apiKey; + edktInitializor.load = function(e) { + var p = e || 'sdk'; + var n = document.createElement('script'); + n.type = 'text/javascript'; + n.async = true; + n.src = 'https://cdn.edkt.io/' + p + '/edgekit.min.js'; + document.getElementsByTagName('head')[0].appendChild(n); + }; + edktInitializor.load(edktInitializor.accountId); + } +} + +/** + * Fetch audiences from localStorage + * @return {Array} + */ +export function getMatchedAudiencesFromStorage() { + const audiences = storage.getDataFromLocalStorage(AG_AUDIENCE_IDS_KEY); + if (!audiences) return [] + try { + return JSON.parse(audiences); + } catch (e) { + return []; + } +} + +/** + * Mutates the adUnits object + * @param {Object} adUnits + * @param {Array} audiences + * @return {void} + */ +function setAudiencesToAppNexusAdUnits(adUnits, audiences) { + adUnits.forEach((adUnit) => { + adUnit.bids.forEach((bid) => { + if (bid.bidder && bid.bidder === 'appnexus') { + deepSetValue(bid, 'params.keywords.perid', audiences || []); + } + }) + }) +} + +/** + * Pass audience data to configured bidders, using ORTB2 + * @param {Object} rtdConfig + * @param {Array} audiences + * @return {void} + */ +export function setAudiencesUsingBidderOrtb2(rtdConfig, audiences) { + const bidders = deepAccess(rtdConfig, 'params.bidders'); + if (!bidders || bidders.length === 0) return; + const allBiddersConfig = config.getBidderConfig(); + const agOrtb2 = {} + deepSetValue(agOrtb2, 'ortb2.user.ext.data.airgrid', audiences || []); + + bidders.forEach((bidder) => { + let bidderConfig = {}; + if (isPlainObject(allBiddersConfig[bidder])) { + bidderConfig = allBiddersConfig[bidder]; + } + config.setBidderConfig({ + bidders: [bidder], + config: mergeDeep(bidderConfig, agOrtb2) + }); + }); +} + +/** + * Module init + * @param {Object} rtdConfig + * @param {Object} userConsent + * @return {boolean} + */ +function init(rtdConfig, userConsent) { + attachScriptTagToDOM(rtdConfig); + return true; +} + +/** + * Real-time data retrieval from AirGrid + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} rtdConfig + * @param {Object} userConsent + * @return {void} + */ +export function passAudiencesToBidders(bidConfig, onDone, rtdConfig, userConsent) { + const adUnits = bidConfig.adUnits || getGlobal().adUnits; + const audiences = getMatchedAudiencesFromStorage(); + if (audiences.length > 0) { + setAudiencesUsingBidderOrtb2(rtdConfig, audiences); + if (adUnits) { + setAudiencesToAppNexusAdUnits(adUnits, audiences); + } + } + onDone(); +}; + +/** @type {RtdSubmodule} */ +export const airgridSubmodule = { + name: SUBMODULE_NAME, + init: init, + getBidRequestData: passAudiencesToBidders +}; + +submodule(MODULE_NAME, airgridSubmodule); diff --git a/modules/airgridRtdProvider.md b/modules/airgridRtdProvider.md new file mode 100644 index 00000000000..7ee502b4c10 --- /dev/null +++ b/modules/airgridRtdProvider.md @@ -0,0 +1,95 @@ + --- + layout: page_v2 + title: AirGrid RTD SubModule + description: Client-side, cookieless and privacy-first audiences. + page_type: module + module_type: rtd + module_code : example + enable_download : true + sidebarType : 1 + --- + +# AirGrid + +AirGrid is a privacy-first, cookie-less audience platform. Designed to help publishers increase inventory yield, +whilst providing audience signal to buyers in the bid request, without exposing raw user level data to any party. + +This real-time data module provides quality first-party data, contextual data, site-level data and more that is +injected into bid request objects destined for different bidders in order to optimize targeting. + +## Usage + +Compile the Halo RTD module into your Prebid build: + +`gulp build --modules=rtdModule,airgridRtdProvider,appnexusBidAdapter` + +Add the AirGrid RTD provider to your Prebid config. In this example we will configure publisher 1234 to retrieve segments from Audigent. See the "Parameter Descriptions" below for more detailed information of the configuration parameters. + +```js +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: 'airgrid', + waitForIt: true, + params: { + // These are unique values for each account. + apiKey: 'apiKey', + accountId: 'accountId', + publisherId: 'publisherId', + bidders: ['appnexus', 'pubmatic'] + } + } + ] + } + ... +} +``` + +### Parameter Descriptions + +| Name |Type | Description | Notes | +| :------------ | :------------ | :------------ |:------------ | +| name | `String` | RTD sub module name | Always 'airgrid' | +| waitForIt | `Boolean` | Wether to delay auction for module response | Optional. Defaults to false | +| params.apiKey | `Boolean` | Publisher partner specific API key | Required | +| params.accountId | `String` | Publisher partner specific account ID | Required | +| params.publisherId | `String` | Publisher partner specific publisher ID | Required | +| params.bidders | `Array` | Bidders with which to share segment information | Optional | + +_Note: Although the module supports passing segment data to any bidder using the ORTB2 spec, there is no way for this to be currently monetised. Please reach out to support, to discuss using bidders other than Xandr/AppNexus._ + +If you do not have your own `apiKey`, `accountId` & `publisherId` please reach out to [support@airgrid.io](mailto:support@airgrid.io) + +## Testing + +To view an example of the on page setup required: + +```bash +gulp serve-fast --modules=rtdModule,airgridRtdProvider,appnexusBidAdapter +``` + +Then in your browser access: + +``` +http://localhost:9999/integrationExamples/gpt/airgridRtdProvider_example.html +``` + +Run the unit tests, just on the AirGrid RTD module test file: + +```bash +gulp test --file "test/spec/modules/airgridRtdProvider_spec.js" +``` + +## Support + +If you require further assistance or are interested in discussing the module functionality please reach out to: +- [hello@airgrid.io](mailto:hello@airgrid.io) for general questions. +- [support@airgrid.io](mailto:support@airgrid.io) for technical questions. + +You are also able to find more examples and other integration routes on the [AirGrid docs site](docs.airgrid.io). + +Happy Coding! 😊 +The AirGrid Team. diff --git a/test/spec/modules/airgridRtdProvider_spec.js b/test/spec/modules/airgridRtdProvider_spec.js new file mode 100644 index 00000000000..cc10dda4ad1 --- /dev/null +++ b/test/spec/modules/airgridRtdProvider_spec.js @@ -0,0 +1,97 @@ +import {config} from 'src/config.js'; +import {deepAccess} from 'src/utils.js' +import {getAdUnits} from '../../fixtures/fixtures.js'; +import * as agRTD from 'modules/airgridRtdProvider.js'; + +const MATCHED_AUDIENCES = ['travel', 'sport']; +const RTD_CONFIG = { + auctionDelay: 250, + dataProviders: [{ + name: 'airgrid', + waitForIt: true, + params: { + apiKey: 'key123', + accountId: 'sdk', + publisherId: 'pub123', + bidders: ['pubmatic'] + } + }] +}; + +describe('airgrid RTD Submodule', function() { + let getDataFromLocalStorageStub; + + beforeEach(function() { + config.resetConfig(); + getDataFromLocalStorageStub = sinon.stub(agRTD.storage, 'getDataFromLocalStorage'); + }); + + afterEach(function () { + getDataFromLocalStorageStub.restore(); + }); + + describe('Initialise module', function() { + it('should initalise and return true', function () { + expect(agRTD.airgridSubmodule.init(RTD_CONFIG.dataProviders[0])).to.equal(true); + }); + it('should attach script to DOM with correct config', function() { + agRTD.attachScriptTagToDOM(RTD_CONFIG); + expect(window.edktInitializor.invoked).to.be.true; + expect(window.edktInitializor.apiKey).to.equal(RTD_CONFIG.dataProviders[0].params.apiKey); + expect(window.edktInitializor.accountId).to.equal(RTD_CONFIG.dataProviders[0].params.accountId); + expect(window.edktInitializor.publisherId).to.equal(RTD_CONFIG.dataProviders[0].params.publisherId); + }); + }); + + describe('Get matched audiences', function() { + it('gets matched audiences from local storage', function() { + getDataFromLocalStorageStub.withArgs(agRTD.AG_AUDIENCE_IDS_KEY).returns(JSON.stringify(MATCHED_AUDIENCES)); + + const audiences = agRTD.getMatchedAudiencesFromStorage(); + expect(audiences).to.have.members(MATCHED_AUDIENCES); + }); + }); + + describe('Add matched audiences', function() { + it('merges matched audiences on appnexus AdUnits', function() { + const adUnits = getAdUnits(); + getDataFromLocalStorageStub.withArgs(agRTD.AG_AUDIENCE_IDS_KEY).returns(JSON.stringify(MATCHED_AUDIENCES)); + agRTD.passAudiencesToBidders({ adUnits }, () => {}, {}, {}); + + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid; + if (bidder === 'appnexus') { + expect(deepAccess(params, 'keywords.perid')).to.eql(MATCHED_AUDIENCES); + } + }); + }); + }); + it('does not merge audiences on appnexus adunits, since none are matched', function() { + const adUnits = getAdUnits(); + getDataFromLocalStorageStub.withArgs(agRTD.AG_AUDIENCE_IDS_KEY).returns(undefined); + agRTD.passAudiencesToBidders({ adUnits }, () => {}, {}, {}); + + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const { bidder, params } = bid; + if (bidder === 'appnexus') { + expect(deepAccess(params, 'keywords.perid')).to.be.undefined; + } + }); + }); + }); + it('sets bidder specific ORTB2 config', function() { + getDataFromLocalStorageStub.withArgs(agRTD.AG_AUDIENCE_IDS_KEY).returns(JSON.stringify(MATCHED_AUDIENCES)); + const audiences = agRTD.getMatchedAudiencesFromStorage(); + agRTD.setAudiencesUsingBidderOrtb2(RTD_CONFIG.dataProviders[0], audiences); + + const allBiddersConfig = config.getBidderConfig(); + const bidders = RTD_CONFIG.dataProviders[0].params.bidders; + Object.keys(allBiddersConfig).forEach((bidder) => { + if (bidders.indexOf(bidder) === -1) return; + expect(deepAccess(allBiddersConfig[bidder], 'ortb2.user.ext.data.airgrid')).to.eql(MATCHED_AUDIENCES); + }); + }); + }); +}); From ca7ab182558ed4dffb0c016c13b32805820abaf5 Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Tue, 3 Aug 2021 11:03:49 +0300 Subject: [PATCH 1337/1476] Next Millennium Bid Adapter: update to comply with Prebid v5 (#7209) * Start * nextMillenniumBidAdapter for v5 * add test page * undo changes in hello_world * manually kick off tests Co-authored-by: Chris Huie --- modules/nextMillenniumBidAdapter.js | 85 +++++++++++++++++++ .../modules/nextMillenniumBidAdapter_spec.js | 54 ++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 modules/nextMillenniumBidAdapter.js create mode 100644 test/spec/modules/nextMillenniumBidAdapter_spec.js diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js new file mode 100644 index 00000000000..7164a517569 --- /dev/null +++ b/modules/nextMillenniumBidAdapter.js @@ -0,0 +1,85 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'nextMillennium'; +const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; +const TIME_TO_LIVE = 360; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function(bid) { + return !!( + bid.params.placement_id && utils.isStr(bid.params.placement_id) + ); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + const requests = []; + + utils._each(validBidRequests, function(bid) { + const postBody = { + 'ext': { + 'prebid': { + 'storedrequest': { + 'id': utils.getBidIdParameter('placement_id', bid.params) + } + } + } + } + const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies !== 'undefined') { + postBody.gdprApplies = !!gdprConsent.gdprApplies; + } + if (typeof gdprConsent.consentString !== 'undefined') { + postBody.consentString = gdprConsent.consentString; + } + } + + requests.push({ + method: 'POST', + url: ENDPOINT, + data: JSON.stringify(postBody), + options: { + contentType: 'application/json', + withCredentials: true + }, + bidId: bid.bidId + }); + }); + + return requests; + }, + + interpretResponse: function(serverResponse, bidRequest) { + const response = serverResponse.body; + const bidResponses = []; + + utils._each(response.seatbid, (resp) => { + utils._each(resp.bid, (bid) => { + bidResponses.push({ + requestId: bidRequest.bidId, + cpm: bid.price, + width: bid.w, + height: bid.h, + creativeId: bid.adid, + currency: response.cur, + netRevenue: false, + ttl: TIME_TO_LIVE, + meta: { + advertiserDomains: bid.adomain || [] + }, + ad: bid.adm + }); + }); + }); + + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js new file mode 100644 index 00000000000..e9a49682605 --- /dev/null +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -0,0 +1,54 @@ +import { expect } from 'chai'; +import { spec } from 'modules/nextMillenniumBidAdapter.js'; + +describe('nextMillenniumBidAdapterTests', function() { + const bidRequestData = [ + { + bidId: 'bid1234', + bidder: 'nextMillennium', + params: { placement_id: '-1' }, + sizes: [[300, 250]] + } + ]; + + it('validate_generated_params', function() { + const request = spec.buildRequests(bidRequestData); + expect(request[0].bidId).to.equal('bid1234'); + }); + + 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); + + let bid = bids[0]; + + expect(bid.creativeId).to.equal('96846035'); + expect(bid.ad).to.equal('Hello! It\'s a test ad!'); + expect(bid.cpm).to.equal(0.5); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); +}); From 72eacfac1a125ca32279ab31bf1aea92284f93b5 Mon Sep 17 00:00:00 2001 From: Wls-demo <67785512+Wls-demo@users.noreply.github.com> Date: Tue, 3 Aug 2021 12:01:02 +0300 Subject: [PATCH 1338/1476] Boldwin Bid Adapter: add advertiserDomains and getFloor handling (#7254) * new boldwin bid adapter * fix * Restarting ci / circleci * changes Co-authored-by: Aiholkin Co-authored-by: Vladislav Isaiko Co-authored-by: Mykhailo Yaremchuk --- modules/boldwinBidAdapter.js | 152 +++++++++++ modules/boldwinBidAdapter.md | 6 +- test/spec/modules/boldwinBidAdapter_spec.js | 288 ++++++++++++++++++++ 3 files changed, 442 insertions(+), 4 deletions(-) create mode 100644 modules/boldwinBidAdapter.js create mode 100644 test/spec/modules/boldwinBidAdapter_spec.js diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js new file mode 100644 index 00000000000..66b4cbfd77b --- /dev/null +++ b/modules/boldwinBidAdapter.js @@ -0,0 +1,152 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'boldwin'; +const AD_URL = 'https://ssp.videowalldirect.com/pbjs'; +const SYNC_URL = 'https://cs.videowalldirect.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); + case NATIVE: + return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); + default: + return false; + } +} + +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 + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && bid.params.placementId); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + '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++) { + let bid = validBidRequests[i]; + const { mediaTypes } = bid; + const placement = {}; + let sizes; + if (mediaTypes) { + if (mediaTypes[BANNER] && mediaTypes[BANNER].sizes) { + placement.adFormat = BANNER; + sizes = mediaTypes[BANNER].sizes + } else if (mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize) { + placement.adFormat = VIDEO; + sizes = 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 { + placement.adFormat = NATIVE; + placement.native = mediaTypes[NATIVE]; + } + } + placements.push({ + ...placement, + placementId: bid.params.placementId, + bidId: bid.bidId, + sizes: sizes || [], + wPlayer: sizes ? sizes[0] : 0, + hPlayer: sizes ? sizes[1] : 0, + schain: bid.schain || {}, + bidFloor: getBidFloor(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: () => { + return [{ + type: 'image', + url: SYNC_URL + }]; + } +}; + +registerBidder(spec); diff --git a/modules/boldwinBidAdapter.md b/modules/boldwinBidAdapter.md index 4bf272c4de3..5e2a5b139b3 100644 --- a/modules/boldwinBidAdapter.md +++ b/modules/boldwinBidAdapter.md @@ -24,8 +24,7 @@ Module that connects to boldwin demand sources { bidder: 'boldwin', params: { - placementId: 0, - traffic: 'banner' + placementId: 'testBanner', } } ] @@ -43,8 +42,7 @@ Module that connects to boldwin demand sources { bidder: 'boldwin', params: { - placementId: 0, - traffic: 'video' + placementId: 'testVideo', } } ] diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js new file mode 100644 index 00000000000..afb5f935621 --- /dev/null +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -0,0 +1,288 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/boldwinBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; + +describe('BoldwinBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'boldwin', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + params: { + placementId: 'testBanner', + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and placementId 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://ssp.videowalldirect.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', 'secure', '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.secure).to.be.within(0, 1); + 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', 'hPlayer', 'wPlayer', 'schain', 'bidFloor'); + 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'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement.adFormat).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + 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: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + 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('23fhj33i987f'); + 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 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' + }] + }; + 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', + }] + }; + 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 () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', 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('image'); + expect(userSync[0].url).to.be.equal('https://cs.videowalldirect.com'); + }); + }); +}); From 9eed22025ed20e41635e7bb0e064d0bd4dc3f821 Mon Sep 17 00:00:00 2001 From: Bill Newman Date: Tue, 3 Aug 2021 12:39:43 +0300 Subject: [PATCH 1339/1476] Colossus Bid Adapter: add advertiserDomains and video params support (#7245) * 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 Co-authored-by: Vladislav Isaiko Co-authored-by: Aiholkin Co-authored-by: Mykhailo Yaremchuk --- modules/colossussspBidAdapter.js | 176 ++++++++++++++++ modules/colossussspBidAdapter.md | 29 ++- .../modules/colossussspBidAdapter_spec.js | 190 ++++++++++++++++++ 3 files changed, 380 insertions(+), 15 deletions(-) create mode 100644 modules/colossussspBidAdapter.js create mode 100644 test/spec/modules/colossussspBidAdapter_spec.js diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js new file mode 100644 index 00000000000..0f7daf8fda9 --- /dev/null +++ b/modules/colossussspBidAdapter.js @@ -0,0 +1,176 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'colossusssp'; +const G_URL = 'https://colossusssp.com/?c=o&m=multi'; +const G_URL_SYNC = 'https://colossusssp.com/?c=o&m=cookie'; + +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); + case NATIVE: + return Boolean(bid.native); + default: + return false; + } +} + +function getUserId(eids, id, source, uidExt) { + if (id) { + var uid = { id }; + if (uidExt) { + uid.ext = uidExt; + } + eids.push({ + source, + uids: [ uid ] + }); + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + /** + * 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: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placement_id)); + }, + + /** + * 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: (validBidRequests, bidderRequest) => { + const winTop = utils.getWindowTop(); + const location = winTop.location; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language : '', + 'secure': location.protocol === 'https:' ? 1 : 0, + 'host': location.host, + 'page': location.pathname, + 'placements': placements, + }; + + if (bidderRequest) { + if (bidderRequest.uspConsent) { + request.ccpa = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent) { + request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' + request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + } + } + + for (let i = 0; i < validBidRequests.length; i++) { + let bid = validBidRequests[i]; + let traff = bid.params.traffic || BANNER + let placement = { + placementId: bid.params.placement_id, + bidId: bid.bidId, + sizes: bid.mediaTypes[traff].sizes, + traffic: traff, + eids: [], + floor: {} + }; + if (typeof bid.getFloor === 'function') { + let tmpFloor = {}; + for (let size of placement.sizes) { + tmpFloor = bid.getFloor({ + currency: 'USD', + mediaType: traff, + size: size + }); + if (tmpFloor) { + placement.floor[`${size[0]}x${size[1]}`] = tmpFloor.floor; + } + } + } + if (bid.schain) { + placement.schain = bid.schain; + } + if (bid.userId) { + getUserId(placement.eids, bid.userId.britepoolid, 'britepool.com'); + getUserId(placement.eids, bid.userId.idl_env, 'identityLink'); + getUserId(placement.eids, bid.userId.id5id, 'id5-sync.com') + getUserId(placement.eids, bid.userId.tdid, 'adserver.org', { + rtiPartner: 'TDID' + }); + } + if (traff === VIDEO) { + 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; + } + placements.push(placement); + } + return { + method: 'POST', + url: G_URL, + data: request + }; + }, + + /** + * 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: (serverResponse) => { + let response = []; + try { + serverResponse = serverResponse.body; + for (let i = 0; i < serverResponse.length; i++) { + let resItem = serverResponse[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + } catch (e) { + utils.logMessage(e); + }; + return response; + }, + + getUserSyncs: () => { + return [{ + type: 'image', + url: G_URL_SYNC + }]; + } +}; + +registerBidder(spec); diff --git a/modules/colossussspBidAdapter.md b/modules/colossussspBidAdapter.md index d95080546c2..8797c648c95 100644 --- a/modules/colossussspBidAdapter.md +++ b/modules/colossussspBidAdapter.md @@ -13,19 +13,18 @@ Module that connects to Colossus SSP demand sources # Test Parameters ``` var adUnits = [{ - code: 'placementid_0', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]] - } - }, - bids: [{ - bidder: 'colossusssp', - params: { - placement_id: 0, - traffic: 'banner' - } - }] - } - ]; + code: 'placementid_0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'colossusssp', + params: { + placement_id: 0, + traffic: 'banner' + } + }] + ]; ``` diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js new file mode 100644 index 00000000000..150bcb72121 --- /dev/null +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -0,0 +1,190 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/colossussspBidAdapter.js'; + +describe('ColossussspAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'colossusssp', + bidderRequestId: '145e1d6a7837c9', + params: { + placement_id: 0 + }, + placementCode: 'placementid_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '0', + hp: 1, + rid: 'bidrequestid', + // name: 'alladsallthetime', + domain: 'example.com' + } + ] + } + }; + let bidderRequest = { + bidderCode: 'colossus', + auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', + bidderRequestId: 'ffffffffffffff', + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000, + uspConsent: '1YN-', + refererInfo: { + referer: 'http://www.example.com', + reachedTop: true, + }, + bids: [bid] + } + + describe('isBidRequestValid', function () { + it('Should return true when placement_id can be cast to a number', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when placement_id is not a number', function () { + bid.params.placement_id = 'aaa'; + 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://colossusssp.com/?c=o&m=multi'); + }); + it('Should contain ccpa', function() { + expect(serverRequest.data.ccpa).to.be.an('string') + }) + + 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'); + 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'); + let placements = data['placements']; + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'floor'); + expect(placement.schain).to.be.an('object') + expect(placement.placementId).to.be.a('number'); + expect(placement.bidId).to.be.a('string'); + expect(placement.traffic).to.be.a('string'); + expect(placement.sizes).to.be.an('array'); + expect(placement.floor).to.be.an('object'); + } + }); + 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('buildRequests with user ids', function () { + bid.userId = {} + bid.userId.britepoolid = 'britepoolid123'; + bid.userId.idl_env = 'idl_env123'; + bid.userId.tdid = 'tdid123'; + bid.userId.id5id = { uid: 'id5id123' }; + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + let placements = data['placements']; + expect(data).to.be.an('object'); + for (let i = 0; i < placements.length; i++) { + let placement = placements[i]; + expect(placement).to.have.property('eids') + expect(placement.eids).to.be.an('array') + expect(placement.eids.length).to.be.equal(4) + for (let index in placement.eids) { + let v = placement.eids[index]; + expect(v).to.have.all.keys('source', 'uids') + expect(v.source).to.be.oneOf(['britepool.com', 'identityLink', 'adserver.org', 'id5-sync.com']) + expect(v.uids).to.be.an('array'); + expect(v.uids.length).to.be.equal(1) + expect(v.uids[0]).to.have.property('id') + } + } + }); + }); + + describe('interpretResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

Hello ad

', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType', 'meta'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('getUserSyncs', function () { + let userSync = spec.getUserSyncs(); + it('Returns valid URL and type', 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('image'); + expect(userSync[0].url).to.be.equal('https://colossusssp.com/?c=o&m=cookie'); + }); + }); +}); From 369f1d327b83985bcaab8c5e1e7bc5d532e7b4a8 Mon Sep 17 00:00:00 2001 From: pnh-pubx <73683023+pnh-pubx@users.noreply.github.com> Date: Tue, 3 Aug 2021 15:50:15 +0530 Subject: [PATCH 1340/1476] Added gptSlotCode field to the auction logs object. Updated master from the upstream (#7257) Co-authored-by: Phaneendra Hegde --- modules/pubxaiAnalyticsAdapter.js | 1 + test/spec/modules/pubxaiAnalyticsAdapter_spec.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 136b328d381..98b4dec1dca 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -57,6 +57,7 @@ function mapBidResponse(bidResponse, status) { if (typeof bidResponse !== 'undefined') { let bid = { adUnitCode: bidResponse.adUnitCode, + gptSlotCode: utils.getGptSlotInfoForAdUnitCode(bidResponse.adUnitCode).gptSlot || null, auctionId: bidResponse.auctionId, bidderCode: bidResponse.bidder, cpm: bidResponse.cpm, diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 40f7afeeb6a..3d9be082be3 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -521,6 +521,7 @@ describe('pubxai analytics adapter', function() { 'bidderCode': 'appnexus', 'bidId': '248f9a4489835e', 'adUnitCode': '/19968336/header-bid-tag-1', + 'gptSlotCode': utils.getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', 'sizes': '300x250', 'renderStatus': 2, @@ -582,6 +583,7 @@ describe('pubxai analytics adapter', function() { let expectedAfterBidWon = { 'winningBid': { 'adUnitCode': '/19968336/header-bid-tag-1', + 'gptSlotCode': utils.getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', 'bidderCode': 'appnexus', 'bidId': '248f9a4489835e', From b6b35af8462e1f076d71ed563aac0dd4f2e4607b Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Tue, 3 Aug 2021 13:37:46 +0200 Subject: [PATCH 1341/1476] integr8 Bid Adapter: add new bid adapter (#6882) * Added integr8 adapter * Double quote to single quote * Added floor module support * Added floor tests --- modules/integr8BidAdapter.js | 141 ++++++++++++ modules/integr8BidAdapter.md | 67 ++++++ test/spec/modules/integr8BidAdapter_spec.js | 226 ++++++++++++++++++++ 3 files changed, 434 insertions(+) create mode 100644 modules/integr8BidAdapter.js create mode 100644 modules/integr8BidAdapter.md create mode 100644 test/spec/modules/integr8BidAdapter_spec.js diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js new file mode 100644 index 00000000000..e79878e0b3a --- /dev/null +++ b/modules/integr8BidAdapter.js @@ -0,0 +1,141 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'integr8'; +const ENDPOINT_URL = 'https://integr8.central.gjirafa.tech/bid'; +const DIMENSION_SEPARATOR = 'x'; +const SIZE_SEPARATOR = ';'; + +export const spec = { + code: BIDDER_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 && bid.params.propertyId && bid.params.placementId); + }, + /** + * 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) { + let propertyId = ''; + let pageViewGuid = ''; + let storageId = ''; + let bidderRequestId = ''; + let url = ''; + let contents = []; + let data = {}; + + if (bidderRequest) { + bidderRequestId = bidderRequest.bidderRequestId; + + if (bidderRequest.refererInfo) { + url = bidderRequest.refererInfo.referer; + } + } + + let placements = validBidRequests.map(bidRequest => { + if (!propertyId) { propertyId = bidRequest.params.propertyId; } + if (!pageViewGuid) { pageViewGuid = bidRequest.params.pageViewGuid || ''; } + if (!storageId) { storageId = bidRequest.params.storageId || ''; } + if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents; } + if (!Object.keys(data).length && bidRequest.params.data && Object.keys(bidRequest.params.data).length) { data = bidRequest.params.data; } + + return { + sizes: generateSizeParam(bidRequest.sizes), + adUnitId: bidRequest.adUnitCode, + placementId: bidRequest.params.placementId, + bidid: bidRequest.bidId, + count: bidRequest.params.count, + skipTime: utils.deepAccess(bidRequest, 'mediaTypes.video.skipafter', bidRequest.params.skipTime), + floor: getBidFloor(bidRequest) + }; + }); + + let body = { + propertyId: propertyId, + pageViewGuid: pageViewGuid, + storageId: storageId, + url: url, + requestid: bidderRequestId, + placements: placements, + contents: contents, + data: data + } + + return [{ + method: 'POST', + url: ENDPOINT_URL, + data: body + }]; + }, + /** + * 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) { + const responses = serverResponse.body; + const bidResponses = []; + for (var i = 0; i < responses.length; i++) { + const bidResponse = { + requestId: responses[i].BidId, + cpm: responses[i].CPM, + width: responses[i].Width, + height: responses[i].Height, + creativeId: responses[i].CreativeId, + currency: responses[i].Currency, + netRevenue: responses[i].NetRevenue, + ttl: responses[i].TTL, + referrer: responses[i].Referrer, + ad: responses[i].Ad, + vastUrl: responses[i].VastUrl, + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } + }; + bidResponses.push(bidResponse); + } + return bidResponses; + } +} + +/** +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ +function generateSizeParam(sizes) { + return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); +} + +export function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return null; + } + + let floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { + return floor.floor; + } + + return null; +} + +registerBidder(spec); diff --git a/modules/integr8BidAdapter.md b/modules/integr8BidAdapter.md new file mode 100644 index 00000000000..eadab7acdb3 --- /dev/null +++ b/modules/integr8BidAdapter.md @@ -0,0 +1,67 @@ +# Overview +Module Name: Integr8 Bidder Adapter Module + +Type: Bidder Adapter + +Maintainer: arditb@gjirafa.com + +# Description +Integr8 Bidder Adapter for Prebid.js. + +# Test Parameters +```js +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'integr8', + params: { + propertyId: '105109', //Required + placementId: '846835', //Required + data: { //Optional + catalogs: [{ + catalogId: "699229", + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } + } + } + }] + }, + { + code: 'test-div', + mediaTypes: { + video: { + context: 'instream' + } + }, + bids: [{ + bidder: 'integr8', + params: { + propertyId: '105109', //Required + placementId: '846830', //Required + data: { //Optional + catalogs: [{ + catalogId: "699229", + items: ["193", "4", "1"] + }], + inventory: { + category: ["tech"], + query: ["iphone 12"] + } + } + } + }] + } +]; +``` diff --git a/test/spec/modules/integr8BidAdapter_spec.js b/test/spec/modules/integr8BidAdapter_spec.js new file mode 100644 index 00000000000..8c5a4b47903 --- /dev/null +++ b/test/spec/modules/integr8BidAdapter_spec.js @@ -0,0 +1,226 @@ +import { expect } from 'chai'; +import { spec, getBidFloor } from 'modules/integr8BidAdapter'; + +describe('integr8AdapterTest', () => { + describe('bidRequestValidity', () => { + it('bidRequest with propertyId and placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'integr8', + params: { + propertyId: '{propertyId}', + placementId: '{placementId}' + } + })).to.equal(true); + }); + + it('bidRequest without propertyId', () => { + expect(spec.isBidRequestValid({ + bidder: 'integr8', + params: { + placementId: '{placementId}' + } + })).to.equal(false); + }); + + it('bidRequest without placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'integr8', + params: { + propertyId: '{propertyId}', + } + })).to.equal(false); + }); + + it('bidRequest without propertyId or placementId', () => { + expect(spec.isBidRequestValid({ + bidder: 'integr8', + params: {} + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'integr8', + 'params': { + 'propertyId': '{propertyId}', + 'placementId': '{placementId}', + 'data': { + 'catalogs': [{ + 'catalogId': '699229', + 'items': ['1', '2', '3'] + }], + 'inventory': { + 'category': ['category1', 'category2'], + 'query': ['query'] + } + } + }, + 'adUnitCode': 'hb-leaderboard', + 'transactionId': 'b6b889bb-776c-48fd-bc7b-d11a1cf0425e', + 'sizes': [[728, 90]], + 'bidId': '10bdc36fe0b48c8', + 'bidderRequestId': '70deaff71c281d', + 'auctionId': 'f9012acc-b6b7-4748-9098-97252914f9dc' + }]; + + it('bidRequest HTTP method', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.method).to.equal('POST'); + }); + }); + + it('bidRequest url', () => { + const endpointUrl = 'https://integr8.central.gjirafa.tech/bid'; + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.url).to.match(new RegExp(`${endpointUrl}`)); + }); + }); + + it('bidRequest data', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.data).to.exist; + }); + }); + + it('bidRequest sizes', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function (requestItem) { + expect(requestItem.data.placements).to.exist; + expect(requestItem.data.placements.length).to.equal(1); + expect(requestItem.data.placements[0].sizes).to.equal('728x90'); + }); + }); + + it('bidRequest data param', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach((requestItem) => { + expect(requestItem.data.data).to.exist; + expect(requestItem.data.data.catalogs).to.exist; + expect(requestItem.data.data.inventory).to.exist; + expect(requestItem.data.data.catalogs.length).to.equal(1); + expect(requestItem.data.data.catalogs[0].items.length).to.equal(3); + expect(Object.keys(requestItem.data.data.inventory).length).to.equal(2); + expect(requestItem.data.data.inventory.category.length).to.equal(2); + expect(requestItem.data.data.inventory.query.length).to.equal(1); + }); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = { + 'method': 'POST', + 'url': 'https://integr8.central.gjirafa.tech/bid', + 'data': { + 'sizes': '728x90', + 'adUnitId': 'hb-leaderboard', + 'placementId': '{placementId}', + 'propertyId': '{propertyId}', + 'pageViewGuid': '{pageViewGuid}', + 'url': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'requestid': '26ee8fe87940da7', + 'bidid': '2962dbedc4768bf' + } + }; + + const bidResponse = { + body: [{ + 'CPM': 1, + 'Width': 728, + 'Height': 90, + 'Referrer': 'http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + 'Ad': '
Test ad
', + 'CreativeId': '123abc', + 'NetRevenue': false, + 'Currency': 'EUR', + 'TTL': 360, + 'ADomain': ['somedomain.com'] + }], + headers: {} + }; + + it('all keys present', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let keys = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'creativeId', + 'currency', + 'netRevenue', + 'ttl', + 'referrer', + 'ad', + 'vastUrl', + 'mediaType', + 'meta' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function (key) { + expect(keys.indexOf(key) !== -1).to.equal(true); + }); + }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(728); + expect(result[0].height).to.equal(90); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) + }); + + describe('floor pricing', () => { + it('should return null when getFloor is not a function', () => { + const bid = { getFloor: 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when getFloor doesnt return an object', () => { + const bid = { getFloor: () => 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when floor is not a number', () => { + const bid = { + getFloor: () => ({ floor: 'string', currency: 'EUR' }) + }; + + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when currency is not EUR', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'USD' }) + }; + + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return floor value when everything is correct', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'EUR' }) + }; + + const result = getBidFloor(bid); + expect(result).to.equal(5); + }); + }); +}); From 55b86118925a1561fded08fc3e65c37c2990ddd8 Mon Sep 17 00:00:00 2001 From: lasloche <62240785+lasloche@users.noreply.github.com> Date: Tue, 3 Aug 2021 17:15:32 +0300 Subject: [PATCH 1342/1476] Rise Bid Adapter: add optional placementId param (#7246) * add placementId optional param * add testing for placementId param --- modules/riseBidAdapter.js | 4 ++++ modules/riseBidAdapter.md | 2 ++ test/spec/modules/riseBidAdapter_spec.js | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index b03c5c15056..6887805b854 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -239,6 +239,10 @@ function generateParameters(bid, bidderRequest) { bidder_version: BIDDER_VERSION }; + if (params.placementId) { + requestParams.placement_id = params.placementId; + } + if (syncEnabled) { const allowedSyncMethod = getAllowedSyncMethod(filterSettings, bidderCode); if (allowedSyncMethod) { diff --git a/modules/riseBidAdapter.md b/modules/riseBidAdapter.md index 67eeab18226..6251b92e0a9 100644 --- a/modules/riseBidAdapter.md +++ b/modules/riseBidAdapter.md @@ -23,6 +23,7 @@ The adapter supports Video(instream). For the integration, Rise returns content | `org` | required | String | Rise publisher Id provided by your Rise representative | "56f91cd4d3e3660002000033" | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `ifa` | optional | String | The ID for advertisers (also referred to as "IDFA") | "XXX-XXX" +| `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false # Test Parameters @@ -43,6 +44,7 @@ var adUnits = [ org: '56f91cd4d3e3660002000033', // Required floorPrice: 2.00, // Optional ifa: 'XXX-XXX', // Optional + placementId: '12345678', // Optional testMode: false // Optional } }] diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index b6c2f4fc61a..61b307eef21 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -75,6 +75,15 @@ describe('riseAdapter', function () { const bidderRequest = { bidderCode: 'rise', } + const placementId = '12345678'; + + it('sends the placementId as a query param', function () { + bidRequests[0].params.placementId = placementId; + const requests = spec.buildRequests(bidRequests, bidderRequest); + for (const request of requests) { + expect(request.data.placement_id).to.equal(placementId); + } + }); it('sends bid request to ENDPOINT via GET', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); From 51f4f48e2cbda175ec46934fccfa1326c3193f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Ortas=20Lebranc=C3=B3n?= <32935912+mario-orle@users.noreply.github.com> Date: Tue, 3 Aug 2021 16:49:22 +0200 Subject: [PATCH 1343/1476] Vidoomy Bid Adapter: add new bid adapter (#7178) --- modules/vidoomyBidAdapter.js | 200 +++++++++++++++++++ modules/vidoomyBidAdapter.md | 59 ++++++ test/spec/modules/vidoomyBidAdapter_spec.js | 210 ++++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 modules/vidoomyBidAdapter.js create mode 100644 modules/vidoomyBidAdapter.md create mode 100644 test/spec/modules/vidoomyBidAdapter_spec.js diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js new file mode 100644 index 00000000000..b579de8618b --- /dev/null +++ b/modules/vidoomyBidAdapter.js @@ -0,0 +1,200 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; +import * as utils from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; + +const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; +const BIDDER_CODE = 'vidoomy'; +const GVLID = 380; +const isBidRequestValid = bid => { + if (!bid.params) { + utils.logError(BIDDER_CODE + ': bid.params should be non-empty'); + return false; + } + + if (!+bid.params.pid) { + utils.logError(BIDDER_CODE + ': bid.params.pid should be non-empty Number'); + return false; + } + + if (!+bid.params.id) { + utils.logError(BIDDER_CODE + ': bid.params.id should be non-empty Number'); + return false; + } + + if (bid.params && bid.params.mediaTypes && bid.params.mediaTypes.video && bid.params.mediaTypes.video.context === INSTREAM && !bid.params.mediaTypes.video.playerSize) { + utils.logError(BIDDER_CODE + ': bid.params.mediaType.video should have a playerSize property to tell player size when is INSTREAM'); + return false; + } + + return true; +}; + +const isBidResponseValid = bid => { + if (!bid.requestId || !bid.cpm || !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); + default: + return false; + } +} + +const buildRequests = (validBidRequests, bidderRequest) => { + const serverRequests = validBidRequests.map(bid => { + let adType = BANNER; + let w, h; + if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + [w, h] = bid.mediaTypes[BANNER].sizes[0]; + adType = BANNER; + } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + [w, h] = bid.mediaTypes[VIDEO].playerSize; + adType = VIDEO; + } + + let host = ''; + try { + host = bidderRequest.refererInfo.referer.split('#')[0].replace(/^(https\:\/\/|http\:\/\/)|(\/)$/g, '').split('/')[0]; + } catch (eBidRequest) { + try { + host = window.location.href.replace(/^(https\:\/\/|http\:\/\/)|(\/)$/g, '').split('/')[0]; + } catch (eLocationHref) { + host = window.location.href; + } + } + const hostname = host.split(':')[0]; + + const videoContext = utils.deepAccess(bid, 'mediaTypes.video.context'); + + const queryParams = []; + queryParams.push(['id', bid.params.id]); + queryParams.push(['adtype', adType]); + queryParams.push(['w', w]); + queryParams.push(['h', h]); + queryParams.push(['pos', parseInt(bid.params.position) || 1]); + queryParams.push(['ua', navigator.userAgent]); + queryParams.push(['l', navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '']); + queryParams.push(['dt', /Mobi/.test(navigator.userAgent) ? 2 : 1]); + queryParams.push(['pid', bid.params.pid]); + queryParams.push(['requestId', bid.bidId]); + queryParams.push(['d', hostname]); + queryParams.push(['sp', encodeURIComponent(bidderRequest.refererInfo.referer)]); + if (bidderRequest.gdprConsent) { + queryParams.push(['gdpr', bidderRequest.gdprConsent.gdprApplies]); + queryParams.push(['gdprcs', bidderRequest.gdprConsent.consentString]); + } + queryParams.push(['usp', bidderRequest.uspConsent || '']); + queryParams.push(['coppa', !!config.getConfig('coppa')]); + + const rawQueryParams = queryParams.map(qp => qp.join('=')).join('&'); + + const url = `${ENDPOINT}?${rawQueryParams}`; + return { + method: 'GET', + url: url, + data: {videoContext} + } + }); + return serverRequests; +}; + +const render = (bid) => { + bid.ad = bid.vastUrl; + var obj = { + vastTimeout: 5000, + maxAllowedVastTagRedirects: 3, + allowVpaid: true, + autoPlay: true, + preload: true, + mute: true, + } + window.outstreamPlayer(bid, bid.adUnitCode, obj); +} + +const interpretResponse = (serverResponse, bidRequest) => { + try { + let responseBody = serverResponse.body; + if (responseBody.mediaType === 'video') { + responseBody.ad = responseBody.vastUrl; + const videoContext = bidRequest.data.videoContext; + + if (videoContext === OUTSTREAM) { + try { + const renderer = Renderer.install({ + id: bidRequest.bidId, + adunitcode: bidRequest.tagId, + loaded: false, + config: responseBody.mediaType, + url: responseBody.meta.rendererUrl + }); + renderer.setRender(render); + + responseBody.renderer = renderer; + } catch (e) { + responseBody.ad = responseBody.vastUrl; + utils.logError(BIDDER_CODE + ': error while installing renderer to show outstream ad'); + } + } + } + const bid = { + vastUrl: responseBody.vastUrl, + ad: responseBody.ad, + renderer: responseBody.renderer, + mediaType: responseBody.mediaType, + requestId: responseBody.requestId, + cpm: responseBody.cpm, + currency: responseBody.currency, + width: responseBody.width, + height: responseBody.height, + creativeId: responseBody.creativeId, + netRevenue: responseBody.netRevenue, + ttl: responseBody.ttl, + meta: { + mediaType: responseBody.meta.mediaType, + rendererUrl: responseBody.meta.rendererUrl, + advertiserDomains: responseBody.meta.advertiserDomains, + advertiserId: responseBody.meta.advertiserId, + advertiserName: responseBody.meta.advertiserName, + agencyId: responseBody.meta.agencyId, + agencyName: responseBody.meta.agencyName, + brandId: responseBody.meta.brandId, + brandName: responseBody.meta.brandName, + dchain: responseBody.meta.dchain, + networkId: responseBody.meta.networkId, + networkName: responseBody.meta.networkName, + primaryCatId: responseBody.meta.primaryCatId, + secondaryCatIds: responseBody.meta.secondaryCatIds + } + }; + + const bids = []; + + if (isBidResponseValid(bid)) { + bids.push(bid); + } else { + utils.logError(BIDDER_CODE + ': server returns invalid response'); + } + + return bids; + } catch (e) { + utils.logError(BIDDER_CODE + ': error parsing server response to Prebid format'); + return []; + } +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + gvlid: GVLID, +}; + +registerBidder(spec); diff --git a/modules/vidoomyBidAdapter.md b/modules/vidoomyBidAdapter.md new file mode 100644 index 00000000000..11e0ad40dbb --- /dev/null +++ b/modules/vidoomyBidAdapter.md @@ -0,0 +1,59 @@ +# Overview + +**Module Name:** Vidoomy Bid Adapter + +**Module Type:** Bidder Adapter + +**Maintainer:** support@vidoomy.com + +# Description + +Module to connect with Vidoomy, supporting banner and video + +# Test Parameters +For banner +```js +var adUnits = [ + { + code: 'test-ad', + mediaTypes: { + banner: { + sizes: [[300, 250]] // only first size will be accepted + } + }, + bids: [ + { + bidder: 'vidoomy', + params: { + id: '123123', + pid: '123123' + } + } + ] + } +]; +``` + +For video +```js +var adUnits = [ + { + code: 'test-ad', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [300, 250] + } + }, + bids: [ + { + bidder: 'vidoomy', + params: { + id: '123123', + pid: '123123' + } + } + ] + } +]; +``` diff --git a/test/spec/modules/vidoomyBidAdapter_spec.js b/test/spec/modules/vidoomyBidAdapter_spec.js new file mode 100644 index 00000000000..37452914e79 --- /dev/null +++ b/test/spec/modules/vidoomyBidAdapter_spec.js @@ -0,0 +1,210 @@ +import { expect } from 'chai'; +import { spec } from 'modules/vidoomyBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { INSTREAM } from '../../../src/video'; + +const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; + +describe('vidoomyBidAdapter', function() { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid; + beforeEach(() => { + bid = { + 'bidder': 'vidoomy', + 'params': { + pid: '123123', + id: '123123' + }, + 'adUnitCode': 'code', + 'sizes': [[300, 250]] + }; + }); + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when pid is empty', function () { + bid.params.pid = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when id is empty', function () { + bid.params.id = ''; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaType is video with INSTREAM context and lacks playerSize property', function () { + bid.params.mediaTypes = { + video: { + context: INSTREAM + } + } + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'vidoomy', + 'params': { + pid: '123123', + id: '123123' + }, + 'adUnitCode': 'code', + 'mediaTypes': { + 'banner': { + 'context': 'outstream', + 'sizes': [[300, 250], [200, 100]] + } + }, + }, + { + 'bidder': 'vidoomy', + 'params': { + pid: '456456', + id: '456456' + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'playerSize': [400, 225], + } + }, + 'adUnitCode': 'code2', + } + ]; + + let bidderRequest = { + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'http://example.com', + stack: ['http://example.com'] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + + it('sends bid request to our endpoint via GET', function () { + expect(request[0].method).to.equal('GET'); + expect(request[1].method).to.equal('GET'); + }); + + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.include(ENDPOINT); + expect(request[1].url).to.include(ENDPOINT); + }); + + it('only accepts first width and height sizes', function () { + expect(request[0].url).to.include('w=300'); + expect(request[0].url).to.include('h=250'); + expect(request[0].url).to.not.include('w=200'); + expect(request[0].url).to.not.include('h=100'); + expect(request[1].url).to.include('w=400'); + expect(request[1].url).to.include('h=225'); + }); + + it('should send id and pid parameters', function () { + expect(request[0].url).to.include('id=123123'); + expect(request[0].url).to.include('pid=123123'); + expect(request[1].url).to.include('id=456456'); + expect(request[1].url).to.include('pid=456456'); + }); + }); + + describe('interpretResponse', function () { + const serverResponseVideo = { + body: { + 'vastUrl': 'https:\/\/vpaid.vidoomy.com\/demo-ad\/tag.xml', + 'mediaType': 'video', + 'requestId': '123123', + 'cpm': 3.265, + 'currency': 'USD', + 'width': 0, + 'height': 300, + 'creativeId': '123123', + 'dealId': '23cb20aa264b72', + 'netRevenue': true, + 'ttl': 60, + 'meta': { + 'mediaType': 'video', + 'rendererUrl': 'https:\/\/vpaid.vidoomy.com\/outstreamplayer\/bundle.js', + 'advertiserDomains': ['vidoomy.com'], + 'advertiserId': 123, + 'advertiserName': 'Vidoomy', + 'agencyId': null, + 'agencyName': null, + 'brandId': null, + 'brandName': null, + 'dchain': null, + 'networkId': null, + 'networkName': null, + 'primaryCatId': 'IAB3-1', + 'secondaryCatIds': null + } + } + } + + const serverResponseBanner = { + body: { + 'ad': ' + +
+ + + +
+ + + diff --git a/modules/bidViewabilityIO.js b/modules/bidViewabilityIO.js new file mode 100644 index 00000000000..4651e424d00 --- /dev/null +++ b/modules/bidViewabilityIO.js @@ -0,0 +1,91 @@ +import { config } from '../src/config.js'; +import * as events from '../src/events.js'; +import { EVENTS } from '../src/constants.json'; +import * as utils from '../src/utils.js'; + +const MODULE_NAME = 'bidViewabilityIO'; +const CONFIG_ENABLED = 'enabled'; + +// IAB numbers from: https://support.google.com/admanager/answer/4524488?hl=en +const IAB_VIEWABLE_DISPLAY_TIME = 1000; +const IAB_VIEWABLE_DISPLAY_LARGE_PX = 242000; +export const IAB_VIEWABLE_DISPLAY_THRESHOLD = 0.5 +export const IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD = 0.3; + +const CLIENT_SUPPORTS_IO = window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype && + 'intersectionRatio' in window.IntersectionObserverEntry.prototype; + +const supportedMediaTypes = [ + 'banner' +]; + +export let isSupportedMediaType = (bid) => { + return supportedMediaTypes.indexOf(bid.mediaType) > -1; +} + +let logMessage = (message) => { + return utils.logMessage(`${MODULE_NAME}: ${message}`); +} + +// returns options for the iO that detects if the ad is viewable +export let getViewableOptions = (bid) => { + if (bid.mediaType === 'banner') { + return { + root: null, + rootMargin: '0px', + threshold: bid.width * bid.height > IAB_VIEWABLE_DISPLAY_LARGE_PX ? IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD : IAB_VIEWABLE_DISPLAY_THRESHOLD + } + } +} + +// markViewed returns a function what will be executed when an ad satisifes the viewable iO +export let markViewed = (bid, entry, observer) => { + return () => { + observer.unobserve(entry.target); + events.emit(EVENTS.BID_VIEWABLE, bid); + logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} was viewed`); + } +} + +// viewCallbackFactory creates the callback used by the viewable IntersectionObserver. +// When an ad comes into view, it sets a timeout for a function to be executed +// when that ad would be considered viewed per the IAB specs. The bid that was rendered +// is passed into the factory, so it can pass it into markViewed, so that it can be included +// in the BID_VIEWABLE event data. If the ad leaves view before the timer goes off, the setTimeout +// is cancelled, an the bid will not be marked as viewed. There's probably some kind of race-ish +// thing going on between IO and setTimeout but this isn't going to be perfect, it's just going to +// be pretty good. +export let viewCallbackFactory = (bid) => { + return (entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + logMessage(`viewable timer starting for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`); + entry.target.view_tracker = setTimeout(markViewed(bid, entry, observer), IAB_VIEWABLE_DISPLAY_TIME); + } else { + logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} is out of view`); + if (entry.target.view_tracker) { + clearTimeout(entry.target.view_tracker); + logMessage(`viewable timer stopped for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`); + } + } + }); + }; +}; + +export let init = () => { + config.getConfig(MODULE_NAME, conf => { + if (conf[MODULE_NAME][CONFIG_ENABLED] && CLIENT_SUPPORTS_IO) { + // if the module is enabled and the browser supports Intersection Observer, + // then listen to AD_RENDER_SUCCEEDED to setup IO's for supported mediaTypes + events.on(EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => { + if (isSupportedMediaType(bid)) { + let viewable = new IntersectionObserver(viewCallbackFactory(bid), getViewableOptions(bid)); + let element = document.getElementById(bid.adUnitCode); + viewable.observe(element); + } + }); + } + }); +} + +init() diff --git a/modules/bidViewabilityIO.md b/modules/bidViewabilityIO.md new file mode 100644 index 00000000000..ad04cf38681 --- /dev/null +++ b/modules/bidViewabilityIO.md @@ -0,0 +1,41 @@ +# Overview + +Module Name: bidViewabilityIO + +Purpose: Emit a BID_VIEWABLE event when a bid becomes viewable using the browsers IntersectionObserver API + +Maintainer: adam.prime@alum.utoronto.ca + +# Description +- This module will trigger a BID_VIEWABLE event which other modules, adapters or publisher code can use to get a sense of viewability +- You can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewabilityIO')``` +- Viewability, as measured by this module is not perfect, nor should it be expected to be. +- The module does not require any specific ad server, or an adserver at all. + +# Limitations + +- Currently only supports the banner mediaType +- Assumes that the adUnitCode of the ad is also the id attribute of the element that the ad is rendered into. +- Does not make any attempt to ensure that the ad inside that element is itself visible. It assumes that the publisher is operating in good faith. + +# Params +- enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable + +# Example of consuming BID_VIEWABLE event +``` + pbjs.onEvent('bidViewable', function(bid){ + console.log('got bid details in bidViewable event', bid); + }); + +``` + +# Example of using config +``` + pbjs.setConfig({ + bidViewabilityIO: { + enabled: true, + } + }); +``` + +An example implmentation without an ad server can be found in integrationExamples/postbid/bidViewabilityIO_example.html diff --git a/test/spec/modules/bidViewabilityIO_spec.js b/test/spec/modules/bidViewabilityIO_spec.js new file mode 100644 index 00000000000..b59dbc867c1 --- /dev/null +++ b/test/spec/modules/bidViewabilityIO_spec.js @@ -0,0 +1,145 @@ +import * as bidViewabilityIO from 'modules/bidViewabilityIO.js'; +import * as events from 'src/events.js'; +import * as utils from 'src/utils.js'; +import * as sinon from 'sinon'; +import { expect } from 'chai'; +import { EVENTS } from 'src/constants.json'; + +describe('#bidViewabilityIO', function() { + const makeElement = (id) => { + const el = document.createElement('div'); + el.setAttribute('id', id); + return el; + } + const banner_bid = { + adUnitCode: 'banner_id', + mediaType: 'banner', + width: 728, + height: 90 + }; + + const large_banner_bid = { + adUnitCode: 'large_banner_id', + mediaType: 'banner', + width: 970, + height: 250 + }; + + const video_bid = { + mediaType: 'video', + }; + + const native_bid = { + mediaType: 'native', + }; + + it('init to be a function', function() { + expect(bidViewabilityIO.init).to.be.a('function') + }); + + describe('isSupportedMediaType tests', function() { + it('banner to be supported', function() { + expect(bidViewabilityIO.isSupportedMediaType(banner_bid)).to.be.true + }); + + it('video not to be supported', function() { + expect(bidViewabilityIO.isSupportedMediaType(video_bid)).to.be.false + }); + + it('native not to be supported', function() { + expect(bidViewabilityIO.isSupportedMediaType(native_bid)).to.be.false + }); + }) + + describe('getViewableOptions tests', function() { + it('normal banner has expected threshold in options object', function() { + expect(bidViewabilityIO.getViewableOptions(banner_bid).threshold).to.equal(bidViewabilityIO.IAB_VIEWABLE_DISPLAY_THRESHOLD); + }); + + it('large banner has expected threshold in options object', function() { + expect(bidViewabilityIO.getViewableOptions(large_banner_bid).threshold).to.equal(bidViewabilityIO.IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD) + }); + + it('video bid has undefined viewable options', function() { + expect(bidViewabilityIO.getViewableOptions(video_bid)).to.be.undefined + }); + + it('native bid has undefined viewable options', function() { + expect(bidViewabilityIO.getViewableOptions(native_bid)).to.be.undefined + }); + }) + + describe('markViewed tests', function() { + let sandbox; + const mockObserver = { + unobserve: sinon.spy() + }; + const mockEntry = { + target: makeElement('target_id') + }; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + }) + + afterEach(function() { + sandbox.restore() + }) + + it('markViewed returns a function', function() { + expect(bidViewabilityIO.markViewed(banner_bid, mockEntry, mockObserver)).to.be.a('function') + }); + + it('markViewed unobserves', function() { + const emitSpy = sandbox.spy(events, ['emit']); + const func = bidViewabilityIO.markViewed(banner_bid, mockEntry, mockObserver); + func(); + expect(mockObserver.unobserve.calledOnce).to.be.true; + expect(emitSpy.calledOnce).to.be.true; + // expect(emitSpy.firstCall.args).to.be.false; + expect(emitSpy.firstCall.args[0]).to.eq(EVENTS.BID_VIEWABLE); + }); + }) + + describe('viewCallbackFactory tests', function() { + let sandbox; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + }) + + afterEach(function() { + sandbox.restore() + }) + + it('viewCallbackFactory returns a function', function() { + expect(bidViewabilityIO.viewCallbackFactory(banner_bid)).to.be.a('function') + }); + + it('viewCallbackFactory function does stuff', function() { + const logMessageSpy = sandbox.spy(utils, ['logMessage']); + const mockObserver = { + unobserve: sandbox.spy() + }; + const mockEntries = [{ + isIntersecting: true, + target: makeElement('true_id') + }, + { + isIntersecting: false, + target: makeElement('false_id') + }, + { + isIntersecting: false, + target: makeElement('false_id') + }]; + mockEntries[2].target.view_tracker = 8; + + const func = bidViewabilityIO.viewCallbackFactory(banner_bid); + func(mockEntries, mockObserver); + expect(mockEntries[0].target.view_tracker).to.be.a('number'); + expect(mockEntries[1].target.view_tracker).to.be.undefined; + expect(logMessageSpy.lastCall.lastArg).to.eq('bidViewabilityIO: viewable timer stopped for id: false_id code: banner_id'); + }); + }) +}); From e679fe75480a06082606f443ea1954ee657d7aab Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 10 Aug 2021 07:30:00 -0700 Subject: [PATCH 1364/1476] Between Bid Adapter: add sharedid for Prebid 5.0 (#7222) * Add back in sharedid #7221 * fix linting * add tests for sharedid * remove trailing spaces --- modules/betweenBidAdapter.js | 13 +++++- test/spec/modules/betweenBidAdapter_spec.js | 47 +++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index feb6cae437a..5a351def958 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,5 +1,5 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { getAdUnitSizes, parseSizesInput } from '../src/utils.js'; +import { getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'between'; @@ -37,6 +37,8 @@ export const spec = { tz: getTz(), fl: getFl(), rr: getRr(), + shid: getSharedId(i)('id'), + shid3: getSharedId(i)('third'), s: i.params.s, bidid: i.bidId, transactionid: i.transactionId, @@ -147,6 +149,15 @@ export const spec = { } } +function getSharedId(bid) { + const id = deepAccess(bid, 'userId.sharedid.id'); + const third = deepAccess(bid, 'userId.sharedid.third'); + return function(kind) { + if (kind === 'id') return id || ''; + return third || ''; + } +} + function getRr() { try { var td = top.document; diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 62f36182d55..44d0752d4b2 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -222,6 +222,53 @@ describe('betweenBidAdapterTests', function () { expect(req_data.sizes).to.deep.equal(['970x250', '240x400', '728x90']) }); + it('check sharedId with id and third', function() { + const bidRequestData = [{ + bidId: 'bid123', + bidder: 'between', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + s: 1112, + }, + userId: { + sharedid: { + id: '01EXQE7JKNDRDDVATB0S2GX1NT', + third: '01EXQE7JKNDRDDVATB0S2GX1NT' + } + } + }]; + const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; + const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; + expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT'); + }); + + it('check sharedId with only id', function() { + const bidRequestData = [{ + bidId: 'bid123', + bidder: 'between', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + s: 1112, + }, + userId: { + sharedid: { + id: '01EXQE7JKNDRDDVATB0S2GX1NT', + } + } + }]; + const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; + const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; + expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal(''); + }); + it('check adomain', function() { const serverResponse = { body: [{ From 11df18dff4bf10f05ed01da62f214ce13690a2d2 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Tue, 10 Aug 2021 11:20:06 -0700 Subject: [PATCH 1365/1476] PubMatic: if multi-format ad-unit does not have outstreamAU or renderer (for out-stream) then continue w/o video (#7275) * Bug fix to still bid banner and/or native when no outstream renderer is available --- modules/pubmaticBidAdapter.js | 15 +++- test/spec/modules/pubmaticBidAdapter_spec.js | 83 ++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index df22f8713cd..f6e6e67444a 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -975,8 +975,19 @@ export const spec = { !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { - utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); - return false; + // we are here since outstream ad-unit is provided without outstreamAU and renderer + // so it is not a valid video ad-unit + // but it may be valid banner or native ad-unit + // so if mediaType banner or Native is present then we will remove media-type video and return true + + if (bid.mediaTypes.hasOwnProperty(BANNER) || bid.mediaTypes.hasOwnProperty(NATIVE)) { + delete bid.mediaTypes[VIDEO]; + utils.logWarn(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting mediatype Video of bid: `, bid); + return true; + } else { + utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); + return false; + } } } return true; diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index d22acabd4cf..23c5f01e520 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -930,6 +930,89 @@ describe('PubMatic adapter', function () { delete bid.params.video.mimes; // Undefined expect(spec.isBidRequestValid(bid)).to.equal(false); }); + + it('checks on bid.params.outstreamAU & bid.renderer & bid.mediaTypes.video.renderer', function() { + const getThebid = function() { + let bid = utils.deepClone(validOutstreamBidRequest.bids[0]); + bid.params.outstreamAU = 'pubmatic-test'; + bid.renderer = ' '; // we are only checking if this key is set or not + bid.mediaTypes.video.renderer = ' '; // we are only checking if this key is set or not + return bid; + } + + // true: when all are present + // mdiatype: outstream + // bid.params.outstreamAU : Y + // bid.renderer : Y + // bid.mediaTypes.video.renderer : Y + let bid = getThebid(); + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : Y + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : Y + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : Y + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // false: none present; only outstream + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + // true: none present; outstream + Banner + // mdiatype: outstream, banner + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + bid.mediaTypes.banner = {sizes: [ [300, 250], [300, 600] ]}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: none present; outstream + Native + // mdiatype: outstream, native + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + bid.mediaTypes.native = {} + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); }); describe('Request formation', function () { From 6df0219586416e9a1aa2c959addabb0528536ff3 Mon Sep 17 00:00:00 2001 From: IOTiagoFaria <76956619+IOTiagoFaria@users.noreply.github.com> Date: Wed, 11 Aug 2021 15:41:46 +0100 Subject: [PATCH 1366/1476] InteractiveOffers : parameters changed & dynamic endpoint (#7286) * InteractiveOffers BidAdapter: New endpoint * InteractiveOffers - Parameters changed & dynamic endpoint * InteractiveOffers - Fix lint errors * InteractiveOffers - Change the spec file * InteractiveOffers - Fix spec file Co-authored-by: EC2 Default User --- modules/interactiveOffersBidAdapter.js | 68 ++++++++++++------- modules/interactiveOffersBidAdapter.md | 4 +- .../interactiveOffersBidAdapter_spec.js | 12 ++-- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js index 66949be8a52..958c671e4b9 100644 --- a/modules/interactiveOffersBidAdapter.js +++ b/modules/interactiveOffersBidAdapter.js @@ -4,7 +4,7 @@ import {config} from '../src/config.js'; import * as utils from '../src/utils.js'; const BIDDER_CODE = 'interactiveOffers'; -const ENDPOINT = 'https://prebid.ioadx.com/bidRequest/?partnerId=4a3bab187a74ac4862920cca864d6eff195ff5e4'; +const ENDPOINT = 'https://prebid.ioadx.com/bidRequest/?partnerId='; const DEFAULT = { 'OpenRTBBidRequest': {}, @@ -35,8 +35,8 @@ export const spec = { isBidRequestValid: function(bid) { let ret = true; if (bid && bid.params) { - if (!utils.isNumber(bid.params.pubid)) { - utils.logWarn('pubid must be a valid numeric ID'); + if (!bid.params.partnerId) { + utils.logWarn('partnerId must be a valid ID'); ret = false; } if (bid.params.tmax && !utils.isNumber(bid.params.tmax)) { @@ -50,10 +50,11 @@ export const spec = { return ret; }, buildRequests: function(validBidRequests, bidderRequest) { - let payload = parseRequestPrebidjsToOpenRTB(bidderRequest); + let aux = parseRequestPrebidjsToOpenRTB(bidderRequest); + let payload = aux.payload; return { method: 'POST', - url: ENDPOINT, + url: ENDPOINT + aux.partnerId, data: JSON.stringify(payload), bidderRequest: bidderRequest }; @@ -61,7 +62,10 @@ export const spec = { interpretResponse: function(response, request) { let bidResponses = []; - if (response.body && response.body.length) { + if (response.body) { + if (!response.body.length) { + response.body = [response.body]; + } bidResponses = parseResponseOpenRTBToPrebidjs(response.body); } return bidResponses; @@ -69,6 +73,10 @@ export const spec = { }; function parseRequestPrebidjsToOpenRTB(prebidRequest) { + let ret = { + payload: {}, + partnerId: null + }; let pageURL = window.location.href; let domain = window.location.hostname; let secure = (window.location.protocol == 'https:' ? 1 : 0); @@ -105,12 +113,15 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest) { openRTBRequest.imp = []; prebidRequest.bids.forEach(function(bid, impId) { impId++; + if (!ret.partnerId) { + ret.partnerId = bid.params.partnerId; + } let imp = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestImp'])); imp.id = impId; imp.secure = secure; imp.tagid = bid.bidId; - openRTBRequest.site.publisher.id = openRTBRequest.site.publisher.id || bid.params.pubid; + openRTBRequest.site.publisher.id = openRTBRequest.site.publisher.id || 0; openRTBRequest.tmax = openRTBRequest.tmax || bid.params.tmax || 0; Object.keys(bid.mediaTypes).forEach(function(mediaType) { @@ -130,31 +141,36 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest) { }); openRTBRequest.imp.push(imp); }); - return openRTBRequest; + ret.payload = openRTBRequest; + return ret; } function parseResponseOpenRTBToPrebidjs(openRTBResponse) { let prebidResponse = []; openRTBResponse.forEach(function(response) { - response.seatbid.forEach(function(seatbid) { - seatbid.bid.forEach(function(bid) { - let prebid = JSON.parse(JSON.stringify(DEFAULT['PrebidBid'])); - prebid.requestId = bid.ext.tagid; - prebid.ad = bid.adm; - prebid.creativeId = bid.crid; - prebid.cpm = bid.price; - prebid.width = bid.w; - prebid.height = bid.h; - prebid.mediaType = 'banner'; - prebid.meta = { - advertiserDomains: bid.adomain, - advertiserId: bid.adid, - mediaType: 'banner', - primaryCatId: bid.cat[0] || '', - secondaryCatIds: bid.cat + if (response.seatbid && response.seatbid.forEach) { + response.seatbid.forEach(function(seatbid) { + if (seatbid.bid && seatbid.bid.forEach) { + seatbid.bid.forEach(function(bid) { + let prebid = JSON.parse(JSON.stringify(DEFAULT['PrebidBid'])); + prebid.requestId = bid.ext.tagid; + prebid.ad = bid.adm; + prebid.creativeId = bid.crid; + prebid.cpm = bid.price; + prebid.width = bid.w; + prebid.height = bid.h; + prebid.mediaType = 'banner'; + prebid.meta = { + advertiserDomains: bid.adomain, + advertiserId: bid.adid, + mediaType: 'banner', + primaryCatId: bid.cat[0] || '', + secondaryCatIds: bid.cat + } + prebidResponse.push(prebid); + }); } - prebidResponse.push(prebid); }); - }); + } }); return prebidResponse; } diff --git a/modules/interactiveOffersBidAdapter.md b/modules/interactiveOffersBidAdapter.md index 581b2e49a68..b96572fbf94 100644 --- a/modules/interactiveOffersBidAdapter.md +++ b/modules/interactiveOffersBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: dev@interactiveoffers.com # Description -Module that connects to interactiveOffers demand sources. Param pubid is required. +Module that connects to interactiveOffers demand sources. Param partnerId is required. # Test Parameters ``` @@ -24,7 +24,7 @@ Module that connects to interactiveOffers demand sources. Param pubid is require { bidder: "interactiveOffers", params: { - pubid: 10, + partnerId: "abcd1234", tmax: 250 } } diff --git a/test/spec/modules/interactiveOffersBidAdapter_spec.js b/test/spec/modules/interactiveOffersBidAdapter_spec.js index 2ea620dc30c..ff9ca123def 100644 --- a/test/spec/modules/interactiveOffersBidAdapter_spec.js +++ b/test/spec/modules/interactiveOffersBidAdapter_spec.js @@ -3,7 +3,7 @@ import {spec} from 'modules/interactiveOffersBidAdapter.js'; describe('Interactive Offers Prebbid.js Adapter', function() { describe('isBidRequestValid function', function() { - let bid = {bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}; + let bid = {bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}; it('returns true if all the required params are present and properly formatted', function() { expect(spec.isBidRequestValid(bid)).to.be.true; @@ -15,15 +15,15 @@ describe('Interactive Offers Prebbid.js Adapter', function() { }); it('returns false if any if the required params is not properly formatted', function() { - bid.params = {pubid: 'abcd123', tmax: 250}; + bid.params = {partnerid: '100', tmax: 250}; expect(spec.isBidRequestValid(bid)).to.be.false; - bid.params = {pubid: 100, tmax: '+250'}; + bid.params = {partnerId: '100', tmax: '+250'}; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); describe('buildRequests function', function() { - let validBidRequests = [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; - let bidderRequest = {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://www.google.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://www.google.com'], canonicalUrl: null}}; + let validBidRequests = [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; + let bidderRequest = {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://www.google.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://www.google.com'], canonicalUrl: null}}; it('returns a Prebid.js request object with a valid json string at the "data" property', function() { let request = spec.buildRequests(validBidRequests, bidderRequest); @@ -32,7 +32,7 @@ describe('Interactive Offers Prebbid.js Adapter', function() { }); describe('interpretResponse function', function() { let openRTBResponse = {body: [{cur: 'USD', id: '2052afa35febb79baa9893cc3ae8b83b89740df65fe98b1bd358dbae6e912801', seatbid: [{seat: 1493, bid: [{ext: {tagid: '227faa83f86546'}, crid: '24477', adm: '', nurl: '', adid: '1138', adomain: ['url.com'], price: '1.53', w: 300, h: 250, iurl: 'http://url.com', cat: ['IAB13-11'], id: '5507ced7a39c06942d3cb260197112ba712e4180', attr: [], impid: 1, cid: '13280'}]}], 'bidid': '0959b9d58ba71b3db3fa29dce3b117c01fc85de0'}], 'headers': {}}; - let prebidRequest = {method: 'POST', url: 'https://url.com', data: '{"id": "1aad860c-e04b-482b-acac-0da55ed491c8", "site": {"id": "url.com", "name": "url.com", "domain": "url.com", "page": "http://url.com", "ref": "http://url.com", "publisher": {"id": 100, "name": "http://url.com", "domain": "url.com"}, "content": {"language": "pt-PT"}}, "source": {"fd": 0, "tid": "1aad860c-e04b-482b-acac-0da55ed491c8", "pchain": ""}, "device": {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36", "language": "pt-PT"}, "user": {}, "imp": [{"id":1, "secure": 0, "tagid": "227faa83f86546", "banner": {"pos": 0, "w": 300, "h": 250, "format": [{"w": 300, "h": 250}]}}], "tmax": 300}', bidderRequest: {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {pubid: 100, tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://url.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://url.com'], canonicalUrl: null}}}; + let prebidRequest = {method: 'POST', url: 'https://url.com', data: '{"id": "1aad860c-e04b-482b-acac-0da55ed491c8", "site": {"id": "url.com", "name": "url.com", "domain": "url.com", "page": "http://url.com", "ref": "http://url.com", "publisher": {"id": 100, "name": "http://url.com", "domain": "url.com"}, "content": {"language": "pt-PT"}}, "source": {"fd": 0, "tid": "1aad860c-e04b-482b-acac-0da55ed491c8", "pchain": ""}, "device": {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36", "language": "pt-PT"}, "user": {}, "imp": [{"id":1, "secure": 0, "tagid": "227faa83f86546", "banner": {"pos": 0, "w": 300, "h": 250, "format": [{"w": 300, "h": 250}]}}], "tmax": 300}', bidderRequest: {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://url.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://url.com'], canonicalUrl: null}}}; it('returns an array of Prebid.js response objects', function() { let prebidResponses = spec.interpretResponse(openRTBResponse, prebidRequest); From 17aa2646d00e04bed38dae595dca124f9c341fe6 Mon Sep 17 00:00:00 2001 From: MK Platform <88486298+mediakeys-platform@users.noreply.github.com> Date: Wed, 11 Aug 2021 20:27:49 +0200 Subject: [PATCH 1367/1476] Mediakeys: add bidder adapter (#7268) * Mediakeys: add bidder adapter * Removed superfluous argument Co-authored-by: Jean-Paul COSAL --- modules/mediakeysBidAdapter.js | 380 +++++++++++++ modules/mediakeysBidAdapter.md | 31 + test/spec/modules/mediakeysBidAdapter_spec.js | 538 ++++++++++++++++++ 3 files changed, 949 insertions(+) create mode 100644 modules/mediakeysBidAdapter.js create mode 100644 modules/mediakeysBidAdapter.md create mode 100644 test/spec/modules/mediakeysBidAdapter_spec.js diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js new file mode 100644 index 00000000000..539d2f6c9cf --- /dev/null +++ b/modules/mediakeysBidAdapter.js @@ -0,0 +1,380 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import { createEidsArray } from './userId/eids.js'; + +const AUCTION_TYPE = 1; +const BIDDER_CODE = 'mediakeys'; +const ENDPOINT = 'https://prebid.eu-central-1.bidder.mediakeys.io/bids'; +const GVLID = 498; +const SUPPORTED_MEDIA_TYPES = [BANNER]; +const DEFAULT_CURRENCY = 'USD'; +const NET_REVENUE = true; + +/** + * Detects the capability to reach window.top. + * + * @returns {boolean} + */ +function canAccessTopWindow() { + try { + return !!utils.getWindowTop().location.href; + } catch (error) { + return false; + } +} + +/** + * Returns the OpenRtb deviceType id detected from User Agent + * Voluntary limited to phone, tablet, desktop. + * + * @returns {number} + */ +function getDeviceType() { + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { + return 5; + } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { + return 4; + } + return 2; +} + +/** + * Returns the OS name detected from User Agent. + * + * @returns {number} + */ +function getOS() { + 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'; +} + +/** + * Returns floor from priceFloors module or MediaKey default value. + * + * @param {*} bid a Prebid.js bid (request) object + * @param {string} mediaType the mediaType or the wildcard '*' + * @param {string|array} size the size array or the wildcard '*' + * @returns {number|boolean} + */ +function getFloor(bid, mediaType, size = '*') { + if (!utils.isFn(bid.getFloor)) { + return false; + } + + if (SUPPORTED_MEDIA_TYPES.indexOf(mediaType) === -1) { + utils.logWarn(`${BIDDER_CODE}: Unable to detect floor price for unsupported mediaType ${mediaType}. No floor will be used.`); + return false; + } + + const floor = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType, + size + }) + + return (!isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY) ? floor.floor : false +} + +/** + * Returns an openRTB 2.5 object. + * This one will be populated at each step of the buildRequest process. + * + * @returns {object} + */ +function createOrtbTemplate() { + return { + id: '', + at: AUCTION_TYPE, + cur: [DEFAULT_CURRENCY], + imp: [], + site: {}, // computed in buildRequest() + device: { + ip: '', + js: 1, + dnt: utils.getDNT(), + ua: navigator.userAgent, + devicetype: getDeviceType(), + os: getOS(), + h: screen.height, + w: screen.width, + language: navigator.language, + make: navigator.vendor ? navigator.vendor : '' + }, + user: {}, + regs: { + ext: { + gdpr: 0 // not applied by default + } + }, + ext: { + is_secure: 1 + } + }; +} + +/** + * Returns an openRtb 2.5 banner object. + * + * @param {object} bid Prebid bid object from request + * @returns {object} + */ +function createBannerImp(bid) { + let sizes = bid.mediaTypes.banner.sizes; + const params = utils.deepAccess(bid, 'params', {}); + + if (!utils.isArray(sizes) || !sizes.length) { + utils.logWarn(`${BIDDER_CODE}: mediaTypes.banner.size missing for adunit: ${bid.params.adUnit}. Ignoring the banner impression in the adunit.`); + } else { + const banner = {}; + + banner.w = parseInt(sizes[0][0], 10); + banner.h = parseInt(sizes[0][1], 10); + + const format = []; + sizes.forEach(function (size) { + if (size.length && size.length > 1) { + format.push({w: size[0], h: size[1]}); + } + }); + banner.format = format; + + banner.topframe = utils.inIframe() ? 0 : 1; + banner.pos = params.pos || 0; + + return banner; + } +} + +/** + * Create the OpenRTB 2.5 imp object. + * + * @param {*} bid Prebid bid object from request + * @returns + */ +function createImp(bid) { + const imp = { + id: bid.bidId, + tagid: bid.params.adUnit || undefined, + bidfloorcur: DEFAULT_CURRENCY, + secure: 1, + }; + + // Only supports proper mediaTypes definition… + for (let mediaType in bid.mediaTypes) { + // There is no default floor. bidfloor is set only + // if the priceFloors module is activated and returns a valid floor. + const floor = getFloor(bid, mediaType); + if (floor) { + imp.bidfloor = floor; + } + + if (mediaType === BANNER) { + const banner = createBannerImp(bid); + if (banner) { + imp.banner = banner; + } + } + } + + // handle FPD for imp. + const ortb2Imp = utils.deepAccess(bid, 'ortb2Imp.ext.data'); + if (ortb2Imp) { + const fpd = { ...bid.ortb2Imp }; + utils.mergeDeep(imp, fpd); + } + + return imp; +} + +/** + * If array, extract the first IAB category from provided list + * If string just return it + * + * @param {string|Array} cat IAB Category + * @returns {string|null} + */ +function getPrimaryCatFromResponse(cat) { + if (!cat || (utils.isArray(cat) && !cat.length)) { + return; + } + + if (utils.isArray(cat)) { + return cat[0]; + } else if (utils.isStr(cat)) { + return cat; + } +} + +export const spec = { + code: BIDDER_CODE, + + gvlid: GVLID, + + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + + isBidRequestValid: function(bid) { + return !!(bid && !utils.isEmpty(bid)); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + const payload = createOrtbTemplate(); + + // Pass the auctionId as ortb2 id + // See https://github.com/prebid/Prebid.js/issues/6563 + utils.deepSetValue(payload, 'id', bidderRequest.auctionId); + utils.deepSetValue(payload, 'source.tid', bidderRequest.auctionId); + + validBidRequests.forEach(validBid => { + let bid = utils.deepClone(validBid); + + // No additional params atm. + const imp = createImp(bid); + + payload.imp.push(imp); + }); + + if (validBidRequests[0].schain) { + utils.deepSetValue(payload, 'source.ext.schain', validBidRequests[0].schain); + } + + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(payload, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + if (bidderRequest && bidderRequest.uspConsent) { + utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + if (config.getConfig('coppa') === true) { + utils.deepSetValue(payload, 'regs.coppa', 1); + } + + if (utils.deepAccess(validBidRequests[0], 'userId')) { + utils.deepSetValue(payload, 'user.ext.eids', createEidsArray(validBidRequests[0].userId)); + } + + // Assign payload.site from refererinfo + if (bidderRequest.refererInfo) { + if (bidderRequest.refererInfo.reachedTop) { + const sitePage = bidderRequest.refererInfo.referer; + utils.deepSetValue(payload, 'site.page', sitePage); + utils.deepSetValue(payload, 'site.domain', utils.parseUrl(sitePage, { + noDecodeWholeURL: true + }).hostname); + + if (canAccessTopWindow()) { + utils.deepSetValue(payload, 'site.ref', utils.getWindowTop().document.referrer); + } + } + } + + // Handle First Party Data (need publisher fpd setup) + const fpd = config.getConfig('ortb2') || {}; + if (fpd.site) { + utils.mergeDeep(payload, { site: fpd.site }); + } + if (fpd.user) { + utils.mergeDeep(payload, { user: fpd.user }); + } + // Here we can handle device.geo prop + const deviceGeo = utils.deepAccess(fpd, 'device.geo'); + if (deviceGeo) { + utils.mergeDeep(payload.device, { geo: deviceGeo }); + } + + const request = { + method: 'POST', + url: ENDPOINT, + data: payload, + options: { + withCredentials: false + } + } + + return request; + }, + + interpretResponse(serverResponse, bidRequest) { + const bidResponses = []; + + try { + if (serverResponse.body && serverResponse.body.seatbid && utils.isArray(serverResponse.body.seatbid)) { + const currency = serverResponse.body.cur || DEFAULT_CURRENCY; + const referrer = bidRequest.site && bidRequest.site.ref ? bidRequest.site.ref : ''; + + serverResponse.body.seatbid.forEach(bidderSeat => { + if (!utils.isArray(bidderSeat.bid) || !bidderSeat.bid.length) { + return; + } + + bidderSeat.bid.forEach(bid => { + let mediaType; + // Actually only BANNER is supported, but other types will be added soon. + switch (utils.deepAccess(bid, 'ext.prebid.type')) { + case 'V': + mediaType = VIDEO; + break; + case 'N': + mediaType = NATIVE; + break; + default: + mediaType = BANNER; + } + + const meta = { + advertiserDomains: (Array.isArray(bid.adomain) && bid.adomain.length) ? bid.adomain : [], + advertiserName: utils.deepAccess(bid, 'ext.advertiser_name', null), + agencyName: utils.deepAccess(bid, 'ext.agency_name', null), + primaryCatId: getPrimaryCatFromResponse(bid.cat), + mediaType + } + + const newBid = { + requestId: bid.impid, + cpm: (parseFloat(bid.price) || 0), + width: bid.w, + height: bid.h, + creativeId: bid.crid || bid.id, + dealId: bid.dealid || null, + currency, + netRevenue: NET_REVENUE, + ttl: 360, // seconds. https://docs.prebid.org/dev-docs/faq.html#does-prebidjs-cache-bids + referrer, + ad: bid.adm, + mediaType, + burl: bid.burl, + meta: utils.cleanObj(meta) + }; + + bidResponses.push(newBid); + }); + }); + } + } catch (e) { + utils.logError(BIDDER_CODE, e); + } + + return bidResponses; + }, + + onBidWon: function (bid) { + if (!bid.burl) { + return; + } + + const url = bid.burl.replace(/\$\{AUCTION_PRICE\}/, bid.cpm); + + utils.triggerPixel(url); + } +} + +registerBidder(spec) diff --git a/modules/mediakeysBidAdapter.md b/modules/mediakeysBidAdapter.md new file mode 100644 index 00000000000..75e69659c8a --- /dev/null +++ b/modules/mediakeysBidAdapter.md @@ -0,0 +1,31 @@ +# Overview + +``` +Module Name: Mediakeys Bid Adapter +Module Type: Bidder Adapter +Maintainer: prebidjs@mediakeys.com +``` + +# Description + +Connects to Mediakeys demand source to fetch bids. + +# Test Parameters + +## Banner only Ad Unit + +``` +var adUnits = [ +{ + code: 'test', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + } + }, + bids: [{ + bidder: 'mediakeys', + params: {} // no params required. + }] +}, +``` diff --git a/test/spec/modules/mediakeysBidAdapter_spec.js b/test/spec/modules/mediakeysBidAdapter_spec.js new file mode 100644 index 00000000000..040c0abd566 --- /dev/null +++ b/test/spec/modules/mediakeysBidAdapter_spec.js @@ -0,0 +1,538 @@ +import { expect } from 'chai'; +import { spec } from 'modules/mediakeysBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; +import { config } from 'src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; + +describe('mediakeysBidAdapter', function () { + const adapter = newBidder(spec); + let utilsMock; + let sandbox; + + const bid = { + bidder: 'mediakeys', + params: {}, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: '47789656-9e5c-4250-b7e0-2ce4cbe71a55', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '299320f4de980d', + bidderRequestId: '1c1b642f803242', + auctionId: '84212956-c377-40e8-b000-9885a06dc692', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + ortb2Imp: { + ext: { data: { something: 'test' } } + } + }; + + const bidderRequest = { + bidderCode: 'mediakeys', + auctionId: '84212956-c377-40e8-b000-9885a06dc692', + bidderRequestId: '1c1b642f803242', + bids: [ + { + bidder: 'mediakeys', + params: {}, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: '47789656-9e5c-4250-b7e0-2ce4cbe71a55', + sizes: [ + [300, 250], + [300, 600], + ], + bidId: '299320f4de980d', + bidderRequestId: '1c1b642f803242', + auctionId: '84212956-c377-40e8-b000-9885a06dc692', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + ortb2Imp: { + ext: { data: { something: 'test' } } + } + }, + ], + auctionStart: 1620973766319, + timeout: 1000, + refererInfo: { + referer: + 'https://local.url/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: [ + 'https://local.url/integrationExamples/gpt/hello_world.html?pbjs_debug=true', + ], + canonicalUrl: null, + }, + start: 1620973766325, + }; + + beforeEach(function () { + utilsMock = sinon.mock(utils); + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + utilsMock.restore(); + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + it('should returns true when bid is provided even with empty params', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should returns false when bid is falsy or empty', function () { + const emptyBid = {}; + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid(false)).to.equal(false); + expect(spec.isBidRequestValid(emptyBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('should create imp for supported mediaType only', function() { + const bidRequests = [utils.deepClone(bid)]; + const bidderRequestCopy = utils.deepClone(bidderRequest); + + bidRequests[0].mediaTypes.video = { + playerSize: [300, 250], + context: 'outstream' + } + + bidRequests[0].mediaTypes.native = { + type: 'image' + } + + const request = spec.buildRequests(bidRequests, bidderRequestCopy); + const data = request.data; + + expect(data.imp.length).to.equal(1); + expect(data.imp[0].banner).to.exist; + expect(data.imp[0].video).to.not.exist; + expect(data.imp[0].native).to.not.exist; + }); + + it('should get expected properties with default values (no params set)', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + + // openRTB 2.5 + expect(data.at).to.equal(1); + expect(data.cur[0]).to.equal('USD'); // default currency + expect(data.source.tid).to.equal(bidderRequest.auctionId); + + expect(data.imp.length).to.equal(1); + expect(data.imp[0].id).to.equal(bidRequests[0].bidId); + expect(data.imp[0].banner.w).to.equal(300); + expect(data.imp[0].banner.h).to.equal(250); + expect(data.imp[0].banner.format[0].w).to.equal(300); + expect(data.imp[0].banner.format[0].h).to.equal(250); + expect(data.imp[0].banner.format[1].w).to.equal(300); + expect(data.imp[0].banner.format[1].h).to.equal(600); + expect(data.imp[0].banner.topframe).to.equal(0); + expect(data.imp[0].banner.pos).to.equal(0); + + // Ortb2Imp ext + expect(data.imp[0].ext).to.exist; + expect(data.imp[0].ext.data.something).to.equal('test'); + }); + + it('should get expected properties with values from params', function () { + const bidRequests = [utils.deepClone(bid)]; + bidRequests[0].params = { + pos: 2, + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + expect(data.imp[0].banner.pos).to.equal(2); + }); + + it('should get expected properties with schain', function () { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'ssp.test', + sid: '00001', + hp: 1, + }, + ], + }; + const bidRequests = [utils.deepClone(bid)]; + bidRequests[0].schain = schain; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + expect(data.source.ext.schain).to.equal(schain); + }); + + it('should get expected properties with coppa', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + expect(data.regs.coppa).to.equal(1); + + config.getConfig.restore(); + }); + + it('should get expected properties with US privacy', function () { + const consent = 'Y11N'; + const bidRequests = [utils.deepClone(bid)]; + const bidderRequestWithUsPrivcay = utils.deepClone(bidderRequest); + bidderRequestWithUsPrivcay.uspConsent = consent; + const request = spec.buildRequests( + bidRequests, + bidderRequestWithUsPrivcay + ); + const data = request.data; + expect(data.regs.ext.us_privacy).to.equal(consent); + }); + + it('should get expected properties with GDPR', function () { + const consent = { + consentString: 'kjfdniwjnifwenrif3', + gdprApplies: true, + }; + const bidRequests = [utils.deepClone(bid)]; + const bidderRequestWithGDPR = utils.deepClone(bidderRequest); + bidderRequestWithGDPR.gdprConsent = consent; + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + const data = request.data; + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal(consent.consentString); + }); + + describe('PriceFloors module support', function() { + const getFloorTest = (options) => { + switch (options.mediaType) { + case BANNER: + return { floor: 1, currency: 'USD' } + case VIDEO: + return { floor: 5, currency: 'USD' } + case NATIVE: + return { floor: 3, currency: 'USD' } + default: + return false + } + }; + + it('should not set `imp[]bidfloor` property when priceFloors module is not available', function () { + const bidRequests = [bid]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + expect(data.imp[0].banner).to.exist; + expect(data.imp[0].bidfloor).to.not.exist + }); + + it('should not set `imp[]bidfloor` property when priceFloors module returns false', function () { + const bidWithPriceFloors = utils.deepClone(bid); + + bidWithPriceFloors.getFloor = () => { + return false; + }; + + const bidRequests = [bidWithPriceFloors]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + + expect(data.imp[0].banner).to.exist; + expect(data.imp[0].bidfloor).to.not.exist; + }); + + it('should get and set floor by mediatype', function() { + const bidWithPriceFloors = utils.deepClone(bid); + + bidWithPriceFloors.mediaTypes.video = { + playerSize: [600, 480] + }; + + bidWithPriceFloors.getFloor = getFloorTest; + + const bidRequests = [bidWithPriceFloors]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + + expect(data.imp[0].banner).to.exist; + expect(data.imp[0].bidfloor).to.equal(1); + + // expect(data.imp[1].video).to.exist; + // expect(data.imp[1].bidfloor).to.equal(5); + }); + + it('should set properties at payload level from FPD', function() { + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + ortb2: { + site: { + domain: 'domain.example', + cat: ['IAB12'], + ext: { + data: { + category: 'sport', + } + } + }, + user: { + yob: 1985, + gender: 'm' + }, + device: { + geo: { + country: 'FR', + city: 'Marseille' + } + } + } + }; + return utils.deepAccess(config, key); + }); + + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = request.data; + expect(data.site.domain).to.equal('domain.example'); + expect(data.site.cat[0]).to.equal('IAB12'); + expect(data.site.ext.data.category).to.equal('sport'); + expect(data.user.yob).to.equal(1985); + expect(data.user.gender).to.equal('m'); + expect(data.device.geo.country).to.equal('FR'); + expect(data.device.geo.city).to.equal('Marseille'); + }); + }); + + describe('should support userId modules', function() { + const userId = { + pubcid: '01EAJWWNEPN3CYMM5N8M5VXY22', + unsuported: '666' + }; + + it('should send "user.eids" in the request for Prebid.js supported modules only', function() { + const bidCopy = utils.deepClone(bid); + bidCopy.userId = userId; + + const bidderRequestCopy = utils.deepClone(bidderRequest); + bidderRequestCopy.bids[0].userId = userId; + + const bidRequests = [utils.deepClone(bidCopy)]; + const request = spec.buildRequests(bidRequests, bidderRequestCopy); + const data = request.data; + + const expected = [{ + source: 'pubcid.org', + uids: [ + { + atype: 1, + id: '01EAJWWNEPN3CYMM5N8M5VXY22' + } + ] + }]; + expect(data.user.ext).to.exist; + expect(data.user.ext.eids).to.have.lengthOf(1); + expect(data.user.ext.eids).to.deep.equal(expected); + }); + }); + }); + + describe('intrepretResponse', function () { + const rawServerResponse = { + body: { + id: '60839f99-d5f2-3ab3-b6ac-736b4fe9d0ae', + seatbid: [ + { + bid: [ + { + id: '60839f99-d5f2-3ab3-b6ac-736b4fe9d0ae_0_0', + impid: '1', + price: 0.4319, + nurl: 'https://local.url/notif?index=ab-cd-ef&price=${AUCTION_PRICE}', + burl: 'https://local.url/notif?index=ab-cd-ef&price=${AUCTION_PRICE}', + adm: '', + adomain: ['domain.io'], + iurl: 'https://local.url', + cid: 'string-id', + crid: 'string-id', + cat: ['IAB2'], + attr: [], + w: 300, + h: 250, + ext: { + advertiser_name: 'Advertiser', + agency_name: 'mediakeys', + prebid: { + type: 'B' + } + }, + }, + ], + seat: '337', + }, + ], + cur: 'USD', + ext: { protocol: '5.3' }, + } + } + + it('Returns empty array if no bid', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const response01 = spec.interpretResponse({ body: { seatbid: [{ bid: [] }] } }, request); + const response02 = spec.interpretResponse({ body: { seatbid: [] } }, request); + const response03 = spec.interpretResponse({ body: { seatbid: null } }, request); + const response04 = spec.interpretResponse({ body: { seatbid: null } }, request); + const response05 = spec.interpretResponse({ body: {} }, request); + const response06 = spec.interpretResponse({}, request); + + expect(response01.length).to.equal(0); + expect(response02.length).to.equal(0); + expect(response03.length).to.equal(0); + expect(response04.length).to.equal(0); + expect(response05.length).to.equal(0); + expect(response06.length).to.equal(0); + }); + + it('Log an error', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + sinon.stub(utils, 'isArray').throws(); + spec.interpretResponse(rawServerResponse, request); + utilsMock.expects('logError').once(); + utils.isArray.restore(); + }); + + it('Meta Primary category handling', function() { + const rawServerResponseCopy = utils.deepClone(rawServerResponse); + const rawServerResponseCopy2 = utils.deepClone(rawServerResponse); + const rawServerResponseCopy3 = utils.deepClone(rawServerResponse); + const rawServerResponseCopy4 = utils.deepClone(rawServerResponse); + const rawServerResponseCopy5 = utils.deepClone(rawServerResponse); + const rawServerResponseCopy6 = utils.deepClone(rawServerResponse); + rawServerResponseCopy.body.seatbid[0].bid[0].cat = 'IAB12-1'; + rawServerResponseCopy2.body.seatbid[0].bid[0].cat = ['IAB12', 'IAB12-1']; + rawServerResponseCopy3.body.seatbid[0].bid[0].cat = ''; + rawServerResponseCopy4.body.seatbid[0].bid[0].cat = []; + rawServerResponseCopy5.body.seatbid[0].bid[0].cat = 123; + delete rawServerResponseCopy6.body.seatbid[0].bid[0].cat; + + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const response = spec.interpretResponse(rawServerResponseCopy, request); + const response2 = spec.interpretResponse(rawServerResponseCopy2, request); + const response3 = spec.interpretResponse(rawServerResponseCopy3, request); + const response4 = spec.interpretResponse(rawServerResponseCopy4, request); + const response5 = spec.interpretResponse(rawServerResponseCopy5, request); + const response6 = spec.interpretResponse(rawServerResponseCopy6, request); + expect(response[0].meta.primaryCatId).to.equal('IAB12-1'); + expect(response2[0].meta.primaryCatId).to.equal('IAB12'); + expect(response3[0].meta.primaryCatId).to.not.exist; + expect(response4[0].meta.primaryCatId).to.not.exist; + expect(response5[0].meta.primaryCatId).to.not.exist; + expect(response6[0].meta.primaryCatId).to.not.exist; + }); + + it('Build banner response', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const response = spec.interpretResponse(rawServerResponse, request); + + expect(response.length).to.equal(1); + expect(response[0].requestId).to.equal(rawServerResponse.body.seatbid[0].bid[0].impid); + expect(response[0].cpm).to.equal(rawServerResponse.body.seatbid[0].bid[0].price); + expect(response[0].width).to.equal(rawServerResponse.body.seatbid[0].bid[0].w); + expect(response[0].height).to.equal(rawServerResponse.body.seatbid[0].bid[0].h); + expect(response[0].creativeId).to.equal(rawServerResponse.body.seatbid[0].bid[0].crid); + expect(response[0].dealId).to.equal(null); + expect(response[0].currency).to.equal(rawServerResponse.body.cur); + expect(response[0].netRevenue).to.equal(true); + expect(response[0].ttl).to.equal(360); + expect(response[0].referrer).to.equal(''); + expect(response[0].ad).to.equal(rawServerResponse.body.seatbid[0].bid[0].adm); + expect(response[0].mediaType).to.equal('banner'); + expect(response[0].burl).to.equal(rawServerResponse.body.seatbid[0].bid[0].burl); + expect(response[0].meta).to.deep.equal({ + advertiserDomains: rawServerResponse.body.seatbid[0].bid[0].adomain, + advertiserName: 'Advertiser', + agencyName: 'mediakeys', + primaryCatId: 'IAB2', + mediaType: 'banner' + }); + }); + + it('Build video response', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const rawServerResponseVideo = utils.deepClone(rawServerResponse); + rawServerResponseVideo.body.seatbid[0].bid[0].ext.prebid.type = 'V'; + const response = spec.interpretResponse(rawServerResponseVideo, request); + + expect(response.length).to.equal(1); + expect(response[0].mediaType).to.equal('video'); + expect(response[0].meta.mediaType).to.equal('video'); + }); + + it('Build native response', function () { + const bidRequests = [utils.deepClone(bid)]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const rawServerResponseVideo = utils.deepClone(rawServerResponse); + rawServerResponseVideo.body.seatbid[0].bid[0].ext.prebid.type = 'N'; + const response = spec.interpretResponse(rawServerResponseVideo, request); + + expect(response.length).to.equal(1); + expect(response[0].mediaType).to.equal('native'); + expect(response[0].meta.mediaType).to.equal('native'); + }); + }); + + describe('onBidWon', function() { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function() { + utils.triggerPixel.restore(); + }); + + it('Should not trigger pixel if bid does not contain burl', function() { + const result = spec.onBidWon({}); + expect(result).to.be.undefined; + expect(utils.triggerPixel.callCount).to.equal(0); + }) + + it('Should trigger pixel if bid.burl exists', function() { + const result = spec.onBidWon({ + cpm: 4.2, + burl: 'https://example.com/p=${AUCTION_PRICE}&foo=bar' + }); + + expect(utils.triggerPixel.callCount).to.equal(1) + expect(utils.triggerPixel.firstCall.args[0]).to.be.equal( + 'https://example.com/p=4.2&foo=bar' + ); + }) + }) +}); From d0a5fe61b48aaa3ccaa37a3d3c9b2b01cc165204 Mon Sep 17 00:00:00 2001 From: eknis Date: Thu, 12 Aug 2021 05:15:18 +0900 Subject: [PATCH 1368/1476] Intimate Merger Universal Identifier System: add imuid submodule (#7239) * add imuidIdSystem * add test and refactoring imuid module --- integrationExamples/gpt/userId_example.html | 6 + modules/.submodules.json | 3 +- modules/imuIdSystem.js | 151 ++++++++++++++++++ modules/imuIdSystem.md | 35 ++++ modules/userId/eids.js | 4 + modules/userId/userId.md | 6 + test/spec/modules/imuIdSystem_spec.js | 168 ++++++++++++++++++++ 7 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 modules/imuIdSystem.js create mode 100644 modules/imuIdSystem.md create mode 100644 test/spec/modules/imuIdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 5659a208103..dfea06d17d0 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -245,7 +245,13 @@ // To get new token, register https://developer.chrome.com/origintrials/#/trials/active for Federated Learning of Cohorts token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" } + }, + { + "name": "imuid", + "params": { + "cid": 5126 // Set your Intimate Merger Customer ID here for production } + } ], "syncDelay": 5000, "auctionDelay": 1000 diff --git a/modules/.submodules.json b/modules/.submodules.json index 555e8adaefa..1e52b02a358 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -30,7 +30,8 @@ "dmdIdSystem", "akamaiDAPId", "flocIdSystem", - "amxIdSystem" + "amxIdSystem", + "imuIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js new file mode 100644 index 00000000000..da03c63fc8a --- /dev/null +++ b/modules/imuIdSystem.js @@ -0,0 +1,151 @@ +/** + * The {@link module:modules/userId} module is required + * @module modules/imuIdSystem + * + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import { ajax } from '../src/ajax.js' +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); + +export const storageKey = '__im_uid'; +export const cookieKey = '_im_vid'; +export const apiUrl = 'https://audiencedata.im-apps.net/imuid/get'; +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(utils.timestamp()).toUTCString()); +} + +export function removeImDataFromLocalStorage() { + storage.removeDataFromLocalStorage(storageKey); + storage.removeDataFromLocalStorage(`${storageKey}_mt`); +} + +function setImDataInCookie(value) { + storage.setCookie( + cookieKey, + value, + new Date(utils.timestamp() + cookiesMaxAge).toUTCString(), + 'none' + ); +} + +export function getLocalData() { + const mt = storage.getDataFromLocalStorage(`${storageKey}_mt`); + let expired = true; + if (Date.parse(mt) && Date.now() - (new Date(mt)).getTime() < storageMaxAge) { + expired = false; + } + return { + id: storage.getDataFromLocalStorage(storageKey), + vid: storage.getCookie(cookieKey), + expired: expired + }; +} + +export function apiSuccessProcess(jsonResponse) { + if (!jsonResponse) { + return; + } + if (jsonResponse.uid) { + setImDataInLocalStorage(jsonResponse.uid); + if (jsonResponse.vid) { + setImDataInCookie(jsonResponse.vid); + } + } else { + removeImDataFromLocalStorage(); + } +} + +export function getApiCallback(callback) { + return { + success: response => { + let responseObj = {}; + if (response) { + try { + responseObj = JSON.parse(response); + apiSuccessProcess(responseObj); + } catch (error) { + utils.logError('User ID - imuid submodule: ' + error); + } + } + if (callback && responseObj.uid) { + callback(responseObj.uid); + } + }, + error: error => { + utils.logError('User ID - imuid submodule was unable to get data from api: ' + error); + if (callback) { + callback(); + } + } + }; +} + +export function callImuidApi(apiUrl) { + return function (callback) { + ajax(apiUrl, getApiCallback(callback), undefined, {method: 'GET', withCredentials: true}); + }; +} + +export function getApiUrl(cid, url) { + if (url) { + return `${url}?cid=${cid}`; + } + return `${apiUrl}?cid=${cid}`; +} + +/** @type {Submodule} */ +export const imuIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'imuid', + /** + * decode the stored id value for passing to bid requests + * @function + * @returns {{imuid: string} | undefined} + */ + decode(id) { + if (id && typeof id === 'string') { + return {imuid: id}; + } + return undefined; + }, + /** + * @function + * @param {SubmoduleConfig} [config] + * @returns {{id: string} | undefined | {callback:function}}} + */ + getId(config) { + const configParams = (config && config.params) || {}; + if (!configParams || typeof configParams.cid !== 'number') { + utils.logError('User ID - imuid submodule requires a valid cid to be defined'); + return undefined; + } + let apiUrl = getApiUrl(configParams.cid, configParams.url); + const localData = getLocalData(); + if (localData.vid) { + apiUrl += `&vid=${localData.vid}`; + setImDataInCookie(localData.vid); + } + + if (!localData.id) { + return {callback: callImuidApi(apiUrl)} + } + if (localData.expired) { + callImuidApi(apiUrl)(); + } + return {id: localData.id}; + } +}; + +submodule('userId', imuIdSubmodule); diff --git a/modules/imuIdSystem.md b/modules/imuIdSystem.md new file mode 100644 index 00000000000..9b6b1d108df --- /dev/null +++ b/modules/imuIdSystem.md @@ -0,0 +1,35 @@ +## Intimate Merger User ID Submodule + +IM-UID is a universal identifier provided by Intimate Merger. +The integration of [IM-UID](https://intimatemerger.com/r/uid) into Prebid.js consists of this module. + +## Building Prebid with IM-UID Support + +First, make sure to add the Intimate Merger submodule to your Prebid.js package with: + +``` +gulp build --modules=imuIdSystem, userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'imuid', + params: { + cid 5126 // Set your Intimate Merger Customer ID here for production + } + } + }] + } +}); +``` + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | The name of this module. | `"imuid"` | +| params | Required | Object | Details of module params. | | +| params.cid | Required | String | This is the Customer ID value obtained via Intimate Merger. | `5126` | +| params.url | Optional | String | Use this to change the default endpoint URL. | `"https://example.com/some/api"` | diff --git a/modules/userId/eids.js b/modules/userId/eids.js index e11eb3edb68..813dd2cf427 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -221,6 +221,10 @@ const USER_IDS_CONFIG = { amxId: { source: 'amxrtb.com', atype: 1, + }, + 'imuid': { + source: 'intimatemerger.com', + atype: 1 } }; diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 1c7f854f725..bbbe995983a 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -264,6 +264,12 @@ pbjs.setConfig({ name: "_dpes_id", expires: 90 } + }, + { + name: 'imuid', + params: { + cid: 5126 // Set your Intimate Merger Customer ID here for production + } }], syncDelay: 5000 } diff --git a/test/spec/modules/imuIdSystem_spec.js b/test/spec/modules/imuIdSystem_spec.js new file mode 100644 index 00000000000..2934a7c213b --- /dev/null +++ b/test/spec/modules/imuIdSystem_spec.js @@ -0,0 +1,168 @@ +import { + imuIdSubmodule, + storage, + getApiUrl, + apiSuccessProcess, + getLocalData, + callImuidApi, + getApiCallback, + storageKey, + cookieKey, + apiUrl +} from 'modules/imuIdSystem.js'; + +import * as utils from 'src/utils.js'; + +describe('imuId module', function () { + // let setLocalStorageStub; + let getLocalStorageStub; + let getCookieStub; + // let ajaxBuilderStub; + + beforeEach(function (done) { + // setLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + getCookieStub = sinon.stub(storage, 'getCookie'); + // ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockResponse('{}')); + done(); + }); + + afterEach(function () { + getLocalStorageStub.restore(); + getCookieStub.restore(); + // ajaxBuilderStub.restore(); + }); + + const storageTestCasesForEmpty = [ + undefined, + null, + '' + ] + + const configParamTestCase = { + params: { + cid: 5126 + } + } + + describe('getId()', function () { + it('should return the uid when it exists in local storages', function () { + getLocalStorageStub.withArgs(storageKey).returns('testUid'); + const id = imuIdSubmodule.getId(configParamTestCase); + expect(id).to.be.deep.equal({id: 'testUid'}); + }); + + storageTestCasesForEmpty.forEach(testCase => it('should return the callback when it not exists in local storages', function () { + getLocalStorageStub.withArgs(storageKey).returns(testCase); + const id = imuIdSubmodule.getId(configParamTestCase); + expect(id).have.all.keys('callback'); + })); + + it('should return "undefined" when empty param', function () { + const id = imuIdSubmodule.getId(); + expect(id).to.be.deep.equal(undefined); + }); + + it('should return the callback when it not exists in local storages (and has vid)', function () { + getCookieStub.withArgs(cookieKey).returns('test'); + const id = imuIdSubmodule.getId(configParamTestCase); + expect(id).have.all.keys('callback'); + }); + }); + + describe('getApiUrl()', function () { + it('should return default url when cid only', function () { + const url = getApiUrl(5126); + expect(url).to.be.equal(`${apiUrl}?cid=5126`); + }); + + it('should return param url when set url', function () { + const url = getApiUrl(5126, 'testurl'); + expect(url).to.be.equal('testurl?cid=5126'); + }); + }); + + 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'}); + }); + + it('should return the undefined when decode id is not "string"', function () { + const id = imuIdSubmodule.decode(1); + expect(id).to.equal(undefined); + }); + }); + + describe('getLocalData()', function () { + it('always have the same key', function () { + getLocalStorageStub.withArgs(storageKey).returns('testid'); + 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', + vid: 'testvid', + expired: false + }); + }); + + it('should return expired is true', function () { + getLocalStorageStub.withArgs(`${storageKey}_mt`).returns(0); + const localData = getLocalData(); + expect(localData).to.be.deep.equal({ + id: undefined, + vid: undefined, + expired: true + }); + }); + }); + + describe('apiSuccessProcess()', function () { + it('should return the undefined when success response', function () { + const res = apiSuccessProcess({ + uid: 'test', + vid: 'test' + }); + expect(res).to.equal(undefined); + }); + + it('should return the undefined when empty response', function () { + const res = apiSuccessProcess(); + expect(res).to.equal(undefined); + }); + + it('should return the undefined when error response', function () { + const res = apiSuccessProcess({ + error: 'error response' + }); + expect(res).to.equal(undefined); + }); + }); + + describe('callImuidApi()', function () { + it('should return function when set url', function () { + const res = callImuidApi(`${apiUrl}?cid=5126`); + expect(res).to.exist.and.to.be.a('function'); + }); + }); + + describe('getApiCallback()', function () { + it('should return success and error functions', function () { + const res = getApiCallback(); + expect(res.success).to.exist.and.to.be.a('function'); + expect(res.error).to.exist.and.to.be.a('function'); + }); + + it('should return "undefined" success', function () { + const res = getApiCallback(function(uid) { return uid }); + expect(res.success('{"uid": "testid"}')).to.equal(undefined); + expect(res.error()).to.equal(undefined); + }); + + it('should return "undefined" catch error response', function () { + const res = getApiCallback(function(uid) { return uid }); + expect(res.success('error response')).to.equal(undefined); + }); + }); +}); From 217c8f66c5a5c08076a9f75e8b6be2d05a067fcf Mon Sep 17 00:00:00 2001 From: CPMStar Date: Wed, 11 Aug 2021 13:36:04 -0700 Subject: [PATCH 1369/1476] CPMStar Bid Adapter: Add adomain support for Prebid 5.x (#7284) * added cpmstarBidAdapter with meta.advertiserDomains support * fix linting Co-authored-by: Chris Huie --- modules/cpmstarBidAdapter.js | 183 ++++++++++++++++ test/spec/modules/cpmstarBidAdapter_spec.js | 231 ++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100755 modules/cpmstarBidAdapter.js create mode 100755 test/spec/modules/cpmstarBidAdapter_spec.js diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js new file mode 100755 index 00000000000..14c0d43add7 --- /dev/null +++ b/modules/cpmstarBidAdapter.js @@ -0,0 +1,183 @@ + +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'; + +const BIDDER_CODE = 'cpmstar'; + +const ENDPOINT_DEV = 'https://dev.server.cpmstar.com/view.aspx'; +const ENDPOINT_STAGING = 'https://staging.server.cpmstar.com/view.aspx'; +const ENDPOINT_PRODUCTION = 'https://server.cpmstar.com/view.aspx'; + +const DEFAULT_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; + +function fixedEncodeURIComponent(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16); + }); +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + pageID: Math.floor(Math.random() * 10e6), + + getMediaType: function (bidRequest) { + if (bidRequest == null) return BANNER; + return !utils.deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; + }, + + getPlayerSize: function (bidRequest) { + 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]; + return playerSize; + }, + + isBidRequestValid: function (bid) { + return ((typeof bid.params.placementId === 'string') && !!bid.params.placementId.length) || (typeof bid.params.placementId === 'number'); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + var requests = []; + // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. + + for (var i = 0; i < validBidRequests.length; i++) { + var bidRequest = validBidRequests[i]; + 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=' + utils.getBidIdParameter('placementId', bidRequest.params) + + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + + '&requestid=' + bidRequest.bidId + + '&referer=' + encodeURIComponent(referer); + + if (bidRequest.schain && bidRequest.schain.nodes) { + var schain = bidRequest.schain; + var schainString = ''; + schainString += schain.ver + ',' + schain.complete; + for (var i2 = 0; i2 < schain.nodes.length; i2++) { + var node = schain.nodes[i2]; + schainString += '!' + + fixedEncodeURIComponent(node.asi || '') + ',' + + fixedEncodeURIComponent(node.sid || '') + ',' + + fixedEncodeURIComponent(node.hp || '') + ',' + + fixedEncodeURIComponent(node.rid || '') + ',' + + fixedEncodeURIComponent(node.name || '') + ',' + + fixedEncodeURIComponent(node.domain || ''); + } + url += '&schain=' + schainString + } + + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString != null) { + url += '&gdpr_consent=' + bidderRequest.gdprConsent.consentString; + } + if (bidderRequest.gdprConsent.gdprApplies != null) { + url += '&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? 1 : 0); + } + } + + if (bidderRequest.uspConsent != null) { + url += '&us_privacy=' + bidderRequest.uspConsent; + } + + if (config.getConfig('coppa')) { + url += '&tfcd=' + (config.getConfig('coppa') ? 1 : 0); + } + + requests.push({ + method: 'GET', + url: url, + bidRequest: bidRequest, + }); + } + + return requests; + }, + + interpretResponse: function (serverResponse, request) { + var bidRequest = request.bidRequest; + var mediaType = spec.getMediaType(bidRequest); + + var bidResponses = []; + + if (!Array.isArray(serverResponse.body)) { + serverResponse.body = [serverResponse.body]; + } + + for (var i = 0; i < serverResponse.body.length; i++) { + var raw = serverResponse.body[i]; + var rawBid = raw.creatives[0]; + if (!rawBid) { + utils.logWarn('cpmstarBidAdapter: server response failed check'); + return; + } + var cpm = (parseFloat(rawBid.cpm) || 0); + + if (!cpm) { + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + return; + } + + var bidResponse = { + requestId: rawBid.requestid, + cpm: cpm, + width: rawBid.width || 0, + height: rawBid.height || 0, + currency: rawBid.currency ? rawBid.currency : DEFAULT_CURRENCY, + netRevenue: rawBid.netRevenue ? rawBid.netRevenue : true, + ttl: rawBid.ttl ? rawBid.ttl : DEFAULT_TTL, + creativeId: rawBid.creativeid || 0, + meta: { + advertiserDomains: rawBid.adomain ? rawBid.adomain : [] + } + }; + + if (rawBid.hasOwnProperty('dealId')) { + bidResponse.dealId = rawBid.dealId + } + + if (mediaType == BANNER && rawBid.code) { + bidResponse.ad = rawBid.code + (rawBid.px_cr ? "\n" : ''); + } else if (mediaType == VIDEO && rawBid.creativemacros && rawBid.creativemacros.HTML5VID_VASTSTRING) { + var playerSize = spec.getPlayerSize(bidRequest); + if (playerSize != null) { + bidResponse.width = playerSize[0]; + bidResponse.height = playerSize[1]; + } + bidResponse.mediaType = VIDEO; + bidResponse.vastXml = rawBid.creativemacros.HTML5VID_VASTSTRING; + } else { + return utils.logError('bad response', rawBid); + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + if (serverResponses.length == 0 || !serverResponses[0].body) return syncs; + var usersyncs = serverResponses[0].body[0].syncs; + if (!usersyncs || usersyncs.length < 0) return syncs; + for (var i = 0; i < usersyncs.length; i++) { + var us = usersyncs[i]; + if ((us.type === 'image' && syncOptions.pixelEnabled) || (us.type == 'iframe' && syncOptions.iframeEnabled)) { + syncs.push(us); + } + } + return syncs; + } + +}; +registerBidder(spec); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js new file mode 100755 index 00000000000..285fca9690a --- /dev/null +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -0,0 +1,231 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cpmstarBidAdapter.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; + +const valid_bid_requests = [{ + 'bidder': 'cpmstar', + 'params': { + 'placementId': '57' + }, + 'sizes': [[300, 250]], + 'bidId': 'bidId' +}]; + +const bidderRequest = { + refererInfo: { + referer: 'referer', + reachedTop: false, + } +}; + +const serverResponse = { + body: [{ + creatives: [{ + cpm: 1, + width: 0, + height: 0, + currency: 'USD', + netRevenue: true, + ttl: 1, + creativeid: '1234', + requestid: '11123', + code: 'no idea', + media: 'banner', + } + ], + syncs: [{ type: 'image', url: 'https://server.cpmstar.com/pixel.aspx' }] + }] +}; + +describe('Cpmstar Bid Adapter', function () { + describe('isBidRequestValid', function () { + it('should return true since the bid is valid', + function () { + var bid = { params: { placementId: 123456 } }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }) + + it('should return false since the bid is invalid', function () { + var bid = { params: { placementId: '' } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }) + + it('should return a valid player size', function () { + var bid = { + mediaTypes: { + video: { + playerSize: [[960, 540]] + } + } + } + expect(spec.getPlayerSize(bid)[0]).to.equal(960); + expect(spec.getPlayerSize(bid)[1]).to.equal(540); + }) + + it('should return a default player size', function () { + var bid = { + mediaTypes: { + video: { + playerSize: null + } + } + } + expect(spec.getPlayerSize(bid)[0]).to.equal(640); + expect(spec.getPlayerSize(bid)[1]).to.equal(440); + }) + }); + + describe('buildRequests', function () { + it('should produce a valid production request', function () { + var requests = spec.buildRequests(valid_bid_requests, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('https://server.cpmstar.com/view.aspx'); + }); + it('should produce a valid staging request', function () { + var stgReq = deepClone(valid_bid_requests); + stgReq[0].params.endpoint = 'staging'; + var requests = spec.buildRequests(stgReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('https://staging.server.cpmstar.com/view.aspx'); + }); + it('should produce a valid dev request', function () { + var devReq = deepClone(valid_bid_requests); + devReq[0].params.endpoint = 'dev'; + var requests = spec.buildRequests(devReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('https://dev.server.cpmstar.com/view.aspx'); + }); + it('should produce a request with support for GDPR', function () { + var gdpr_bidderRequest = deepClone(bidderRequest); + gdpr_bidderRequest.gdprConsent = { + consentString: 'consentString', + gdprApplies: true + }; + var requests = spec.buildRequests(valid_bid_requests, gdpr_bidderRequest); + expect(requests[0]).to.have.property('url'); + expect(requests[0].url).to.include('gdpr_consent=consentString'); + expect(requests[0].url).to.include('gdpr=1'); + }); + it('should produce a request with support for USP', function () { + var usp_bidderRequest = deepClone(bidderRequest); + usp_bidderRequest.uspConsent = '1YYY'; + var requests = spec.buildRequests(valid_bid_requests, usp_bidderRequest); + expect(requests[0]).to.have.property('url'); + expect(requests[0].url).to.include('us_privacy=1YYY'); + }); + it('should produce a request with support for COPPA', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + var requests = spec.buildRequests(valid_bid_requests, bidderRequest); + config.getConfig.restore(); + expect(requests[0]).to.have.property('url'); + expect(requests[0].url).to.include('tfcd=1'); + }); + }); + + it('should produce a request with support for OpenRTB SupplyChain', function () { + var reqs = deepClone(valid_bid_requests); + reqs[0].schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1 + }, + { + 'asi': 'exchange2.com', + 'sid': 'abcd', + 'hp': 1 + } + ] + }; + var requests = spec.buildRequests(reqs, bidderRequest); + expect(requests[0]).to.have.property('url'); + expect(requests[0].url).to.include('&schain=1.0,1!exchange1.com,1234,1,,,!exchange2.com,abcd,1,,,'); + }); + + describe('interpretResponse', function () { + const request = { + bidRequest: { + mediaType: 'BANNER' + } + }; + + it('should return a valid bidresponse array', function () { + var r = spec.interpretResponse(serverResponse, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return a valid bidresponse array from a non-array-body', function () { + var r = spec.interpretResponse({ body: serverResponse.body[0] }, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return undefined due to an invalid cpm value', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].cpm = 0; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return undefined due to a bad response', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].code = null; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return a valid response with a dealId', function () { + var dealServer = deepClone(serverResponse); + dealServer.body[0].creatives[0].dealId = 'deal'; + expect(spec.interpretResponse(dealServer, request)[0].dealId).to.equal('deal'); + }); + }); + + describe('getUserSyncs', function () { + var sres = [deepClone(serverResponse)]; + + it('should return a valid pixel sync', function () { + var syncs = spec.getUserSyncs({ pixelEnabled: true }, sres); + expect(syncs.length).equal(1); + expect(syncs[0].type).equal('image'); + expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); + }); + + it('should return a valid iframe sync', function () { + sres[0].body[0].syncs[0].type = 'iframe'; + var syncs = spec.getUserSyncs({ iframeEnabled: true }, sres); + expect(syncs.length).equal(1); + expect(syncs[0].type).equal('iframe'); + expect(syncs[0].url).equal('https://server.cpmstar.com/pixel.aspx'); + }); + }); +}); From 3d8ed534dac05153e54d3c07b13700cf6628985b Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Thu, 12 Aug 2021 16:49:35 +0300 Subject: [PATCH 1370/1476] Adf adapter: schain support added (#7292) --- modules/adfBidAdapter.js | 5 +++++ test/spec/modules/adfBidAdapter_spec.js | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 7666e98b374..43dcdcd1604 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -94,6 +94,7 @@ export const spec = { const currency = getConfig('currency.adServerCurrency'); const cur = currency && [ currency ]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); + const schain = setOnAny(validBidRequests, 'schain'); const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; @@ -206,6 +207,10 @@ export const spec = { utils.deepSetValue(request, 'user.ext.eids', eids); } + if (schain) { + utils.deepSetValue(request, 'source.ext.schain', schain); + } + return { method: 'POST', url: 'https://' + adxDomain + '/adx/openrtb', diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 25ad6987153..ef11490529a 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -245,6 +245,27 @@ describe('Adf adapter', function () { assert.deepEqual(request.cur, [ 'EUR' ]); }); + it('should pass supply chain object', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + assert.deepEqual(request.source.ext.schain, { + validation: 'strict', + config: { + ver: '1.0' + } + }); + }); + describe('priceType', function () { it('should send default priceType', function () { let validBidRequests = [{ From 3b59a2b310d47092557a5605d7074bcb07a3885a Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Thu, 12 Aug 2021 15:38:34 +0100 Subject: [PATCH 1371/1476] targeting: allow non-string (eg. numeric) targeting segments (#7160) Documentation[1] shows a numeric example which causes an exception as we try to call .split(','). [1] https://docs.prebid.org/dev-docs/add-rtd-submodule.html#gettargetingdata --- src/targeting.js | 4 ++- test/spec/unit/core/targeting_spec.js | 47 ++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/targeting.js b/src/targeting.js index edf9521c251..4bbed7bb758 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -634,7 +634,9 @@ export function newTargeting(auctionManager) { return Object.keys(aut) .map(function(key) { - return {[key]: utils.isArray(aut[key]) ? aut[key] : aut[key].split(',')}; + if (utils.isStr(aut[key])) aut[key] = aut[key].split(','); + if (!utils.isArray(aut[key])) aut[key] = [ aut[key] ]; + return { [key]: aut[key] }; }); } diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index c82aac1acf9..f83bd2f6635 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { targeting as targetingInstance, filters, getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm } from 'src/targeting.js'; import { config } from 'src/config.js'; -import { getAdUnits, createBidReceived } from 'test/fixtures/fixtures.js'; +import { createBidReceived } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; import { auctionManager } from 'src/auctionManager.js'; import * as utils from 'src/utils.js'; @@ -280,6 +280,51 @@ describe('targeting tests', function () { bidExpiryStub.restore(); }); + describe('when handling different adunit targeting value types', function () { + const adUnitCode = '/123456/header-bid-tag-0'; + const adServerTargeting = {}; + + let getAdUnitsStub; + + before(function() { + getAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnits').callsFake(function() { + return [ + { + 'code': adUnitCode, + [CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]: adServerTargeting + } + ]; + }); + }); + + after(function() { + getAdUnitsStub.restore(); + }); + + afterEach(function() { + delete adServerTargeting.test_type; + }); + + const pairs = [ + ['string', '2.3', '2.3'], + ['number', 2.3, '2.3'], + ['boolean', true, 'true'], + ['string-separated', '2.3,4.5', '2.3, 4.5'], + ['array-of-string', ['2.3', '4.5'], '2.3, 4.5'], + ['array-of-number', [2.3, 4.5], '2.3, 4.5'], + ['array-of-boolean', [true, false], 'true, false'] + ]; + pairs.forEach(([type, value, result]) => { + it(`accepts ${type}`, function() { + adServerTargeting.test_type = value; + + const targeting = targetingInstance.getAllTargeting([adUnitCode]); + + expect(targeting[adUnitCode].test_type).is.equal(result); + }); + }); + }); + describe('when hb_deal is present in bid.adserverTargeting', function () { let bid4; From 7f5a3be54e1525d493647652e5fa15b2fe8f40e1 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 12 Aug 2021 11:23:17 -0700 Subject: [PATCH 1372/1476] Prebid 5.9.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f96581fbdfb..a4f6abd1a4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.9.0-pre", + "version": "5.9.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 707d73f126cbad0bb37a70f877838600845e35fa Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Thu, 12 Aug 2021 11:53:42 -0700 Subject: [PATCH 1373/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a4f6abd1a4f..f8cd2fc3ef3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.9.0", + "version": "5.10.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 32587e593f59449e5aa78925512dba5722bc6dba Mon Sep 17 00:00:00 2001 From: Mikhail Ivanchenko Date: Fri, 13 Aug 2021 22:50:02 +0300 Subject: [PATCH 1374/1476] add an auctionId to request (#7293) --- modules/nextMillenniumBidAdapter.js | 1 + test/spec/modules/nextMillenniumBidAdapter_spec.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 7164a517569..2101bb34e2b 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -21,6 +21,7 @@ export const spec = { utils._each(validBidRequests, function(bid) { const postBody = { + 'id': bid.auctionId, 'ext': { 'prebid': { 'storedrequest': { diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index e9a49682605..15256d6c382 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -5,6 +5,7 @@ describe('nextMillenniumBidAdapterTests', function() { const bidRequestData = [ { bidId: 'bid1234', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', bidder: 'nextMillennium', params: { placement_id: '-1' }, sizes: [[300, 250]] @@ -14,6 +15,7 @@ describe('nextMillenniumBidAdapterTests', function() { it('validate_generated_params', function() { const request = spec.buildRequests(bidRequestData); expect(request[0].bidId).to.equal('bid1234'); + expect(JSON.parse(request[0].data).id).to.equal('b06c5141-fe8f-4cdf-9d7d-54415490a917'); }); it('validate_response_params', function() { From 95e3400cdfbdd1fdc55daaff24883a792aed3076 Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Sat, 14 Aug 2021 07:06:44 -0700 Subject: [PATCH 1375/1476] Conversant Bid Adapter: add getUserSync (#7185) * add getUserSyncs to the conversant adapter * Review Changes --- modules/conversantBidAdapter.js | 46 ++++++++++++++- .../spec/modules/conversantBidAdapter_spec.js | 58 ++++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 5af399365bd..a1ca094273b 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {getStorageManager} from '../src/storageManager.js'; const GVLID = 24; export const storage = getStorageManager(GVLID); @@ -248,6 +248,48 @@ export const spec = { 'secure': 'number', 'mobile': 'number' }, params); + }, + + /** + * Register User Sync. + */ + getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { + let params = {}; + const syncs = []; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + params.gdpr = (gdprConsent.gdprApplies) ? 1 : 0; + params.gdpr_consent = encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + params.us_privacy = encodeURIComponent(uspConsent); + } + + if (responses && responses.ext) { + const pixels = [{urls: responses.ext.fsyncs, type: 'iframe'}, {urls: responses.ext.psyncs, type: 'image'}] + .filter((entry) => { + return entry.urls && + ((entry.type === 'iframe' && syncOptions.iframeEnabled) || + (entry.type === 'image' && syncOptions.pixelEnabled)); + }) + .map((entry) => { + return entry.urls.map((endpoint) => { + let urlInfo = utils.parseUrl(endpoint); + utils.mergeDeep(urlInfo.search, params); + if (Object.keys(urlInfo.search).length === 0) { + delete urlInfo.search; // empty search object causes buildUrl to add a trailing ? to the url + } + return {type: entry.type, url: utils.buildUrl(urlInfo)}; + }) + .reduce((x, y) => x.concat(y), []); + }) + .reduce((x, y) => x.concat(y), []); + syncs.push(...pixels); + } + return syncs; } }; diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 96a7f419341..e871ab3f9c6 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/conversantBidAdapter.js'; import * as utils from 'src/utils.js'; -import { createEidsArray } from 'modules/userId/eids.js'; +import {createEidsArray} from 'modules/userId/eids.js'; describe('Conversant adapter tests', function() { const siteId = '108060'; @@ -661,4 +661,60 @@ describe('Conversant adapter tests', function() { expect(payload.imp[0]).to.have.property('bidfloor', 0); }); }); + + describe('getUserSyncs', function() { + const syncurl_iframe = 'https://sync.dotomi.com:8080/iframe'; + const syncurl_image = 'https://sync.dotomi.com:8080/pixel'; + const cnvrResponse = {ext: {psyncs: [syncurl_image], fsyncs: [syncurl_iframe]}}; + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + afterEach(function() { + sandbox.restore(); + }); + + it('empty params', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined)) + .to.deep.equal([]); + expect(spec.getUserSyncs({ iframeEnabled: true }, {ext: {}}, undefined, undefined)) + .to.deep.equal([]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{ type: 'iframe', url: syncurl_iframe }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{ type: 'image', url: syncurl_image }]); + expect(spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{type: 'iframe', url: syncurl_iframe}, {type: 'image', url: syncurl_image}]); + }); + + it('URL building', function() { + expect(spec.getUserSyncs({pixelEnabled: true}, {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}, undefined, undefined)) + .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234`}]); + expect(spec.getUserSyncs({pixelEnabled: true}, {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}, undefined, '1NYN')) + .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234&us_privacy=1NYN`}]); + }); + + it('GDPR', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: false, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=0&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: undefined}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=` }]); + + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: false, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=0&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: undefined}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=` }]); + }); + + it('US_Privacy', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, undefined, '1NYN')) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?us_privacy=1NYN` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, undefined, '1NYN')) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?us_privacy=1NYN` }]); + }); + }); }); From f360f39e9de2eaae0955bb3283ca8c3e4ae84c64 Mon Sep 17 00:00:00 2001 From: SmartyAdsSSP <41569976+SmartyAdsSSP@users.noreply.github.com> Date: Sat, 14 Aug 2021 17:48:20 +0300 Subject: [PATCH 1376/1476] SmartyAds Bid Adapter: add support for adomain (#6940) * add support adomain * Update smartyadsBidAdapter_spec.js * fix linting error Co-authored-by: eryomindiman Co-authored-by: Patrick McCann Co-authored-by: Chris Huie --- test/spec/modules/smartyadsBidAdapter_spec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 8804050134a..14363329a9e 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -90,19 +90,22 @@ describe('SmartyadsAdapter', function () { creativeId: '2', netRevenue: true, currency: 'USD', - dealId: '1' + dealId: '1', + meta: {advertiserDomains: ['example.com']} }] }; 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'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal('23fhj33i987f'); 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.meta).to.have.property('advertiserDomains') + expect(dataItem.meta.advertiserDomains).to.deep.equal(['example.com']); expect(dataItem.ttl).to.equal(120); expect(dataItem.creativeId).to.equal('2'); expect(dataItem.netRevenue).to.be.true; From 6aa63d6a9f941971bd730e4a21026e6ae4d2a32e Mon Sep 17 00:00:00 2001 From: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Date: Mon, 16 Aug 2021 15:04:15 +0200 Subject: [PATCH 1377/1476] change path url (#7303) --- modules/proxistoreBidAdapter.js | 13 +++++-------- test/spec/modules/proxistoreBidAdapter_spec.js | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js index 2e4c7eb22cb..8b191c70e75 100644 --- a/modules/proxistoreBidAdapter.js +++ b/modules/proxistoreBidAdapter.js @@ -3,6 +3,8 @@ import * as utils from '../src/utils.js'; const BIDDER_CODE = 'proxistore'; const PROXISTORE_VENDOR_ID = 418; +const COOKIE_BASE_URL = 'https://abs.proxistore.com/v3/rtb/prebid/multi'; +const COOKIE_LESS_URL = 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi'; function _createServerRequest(bidRequests, bidderRequest) { var sizeIds = []; @@ -83,14 +85,9 @@ function _createServerRequest(bidRequests, bidderRequest) { }; var endPointUri = payload.gdpr.consentGiven || !payload.gdpr.applies - ? 'https://abs.proxistore.com/'.concat( - payload.language, - '/v3/rtb/prebid/multi' - ) - : 'https://abs.cookieless-proxistore.com/'.concat( - payload.language, - '/v3/rtb/prebid/multi' - ); + ? COOKIE_BASE_URL + : COOKIE_LESS_URL; + return { method: 'POST', url: endPointUri, diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js index 95f29423492..084c533b5b5 100644 --- a/test/spec/modules/proxistoreBidAdapter_spec.js +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -55,9 +55,9 @@ describe('ProxistoreBidAdapter', function () { }); describe('buildRequests', function () { const url = { - cookieBase: 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi', + cookieBase: 'https://abs.proxistore.com/v3/rtb/prebid/multi', cookieLess: - 'https://abs.cookieless-proxistore.com/fr/v3/rtb/prebid/multi', + 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi', }; let request = spec.buildRequests([bid], bidderRequest); From 51eb7d0b348f3a29f0fa106d1691abf071652bc4 Mon Sep 17 00:00:00 2001 From: Jonathan Nadarajah <50102657+jogury@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:42:59 +0200 Subject: [PATCH 1378/1476] ogury Bid Adapter: handle onBidWon event on prebid.js (#7298) * oguryBidAdapter: handle onBidWon event on prebid.js * oguryBidAdapter: remove blank space at end of file * oguryBidAdapter: fix new line at end of file * trigger rebuild on CI * trigger rebuild on CI --- modules/oguryBidAdapter.js | 11 ++++-- test/spec/modules/oguryBidAdapter_spec.js | 41 +++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 44c9b7a509a..2e5342e6f9e 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -3,6 +3,7 @@ import { BANNER } from '../src/mediaTypes.js'; import { getAdUnitSizes, logWarn, isFn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ajax } from '../src/ajax.js' const BIDDER_CODE = 'ogury'; const DEFAULT_TIMEOUT = 1000; @@ -112,7 +113,8 @@ function interpretResponse(openRtbBidResponse) { ext: bid.ext, meta: { advertiserDomains: bid.adomain - } + }, + nurl: bid.nurl }; bidResponse.ad = bid.adm; @@ -135,6 +137,10 @@ function getFloor(bid) { return floorResult.currency === 'USD' ? floorResult.floor : 0; } +function onBidWon(bid) { + if (bid && bid.hasOwnProperty('nurl') && bid.nurl.length > 0) ajax(bid['nurl'], null); +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], @@ -142,7 +148,8 @@ export const spec = { getUserSyncs, buildRequests, interpretResponse, - getFloor + getFloor, + onBidWon } registerBidder(spec); diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 57de2162789..0a57690db27 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -338,7 +338,8 @@ describe('OguryBidAdapter', function () { netRevenue: true, meta: { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain - } + }, + nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, cpm: openRtbBidResponse.body.seatbid[0].bid[1].price, @@ -352,7 +353,8 @@ describe('OguryBidAdapter', function () { netRevenue: true, meta: { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain - } + }, + nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl }] let request = spec.buildRequests(bidRequests, bidderRequest); @@ -370,4 +372,39 @@ describe('OguryBidAdapter', function () { expect(result.length).to.equal(0) }) }); + + describe('onBidWon', function() { + const nurl = 'https://fakewinurl.test'; + let xhr; + let requests; + + beforeEach(function() { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (xhr) => { + requests.push(xhr); + }; + }) + + afterEach(function() { + xhr.restore(); + }) + + it('Should not create nurl request if bid is undefined', function() { + spec.onBidWon(); + expect(requests.length).to.equal(0); + }) + + it('Should not create nurl request if bid does not contains nurl', function() { + spec.onBidWon({}) + expect(requests.length).to.equal(0); + }) + + it('Should create nurl request if bid nurl', function() { + spec.onBidWon({ nurl }) + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal(nurl); + expect(requests[0].method).to.equal('GET') + }) + }) }); From 07ab54d60b4fa94fca3f922382eee508cfe7d629 Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Tue, 17 Aug 2021 03:35:42 -0700 Subject: [PATCH 1379/1476] IX Bid Adapter: First Party Data Support (#7265) * pass fpd data to r object * remove .repeat() func in tests * check if r.site.page exists * use config.getConfig('ortb2') * use JSON.stringify to calculate fpd length * explicitly calculate request size for fpd * check for impressionObjects.length --- modules/ixBidAdapter.js | 68 ++++++++++++- test/spec/modules/ixBidAdapter_spec.js | 129 +++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 2 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 766ece90fce..2d7157bc674 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -26,6 +26,15 @@ const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; // determines which eids we send and the rtiPartner field in ext + +const FIRST_PARTY_DATA = { + SITE: [ + 'id', 'name', 'domain', 'cat', 'sectioncat', 'pagecat', 'page', 'ref', 'search', 'mobile', + 'privacypolicy', 'publisher', 'content', 'keywords', 'ext' + ], + USER: ['id', 'buyeruid', 'yob', 'gender', 'keywords', 'customdata', 'geo', 'data', 'ext'] +}; + const SOURCE_RTI_MAPPING = { 'liveramp.com': 'idl', 'netid.de': 'NETID', @@ -566,6 +575,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } let currentRequestSize = baseRequestSize; + let fpdRequestSize = 0; + let isFpdAdded = false; if (otherIxConfig) { // Append firstPartyData to r.site.page if firstPartyData exists. @@ -579,7 +590,18 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } firstPartyString = firstPartyString.slice(0, -1); - r.site.page += firstPartyString; + fpdRequestSize = encodeURIComponent(firstPartyString).length; + + if (fpdRequestSize < MAX_REQUEST_SIZE) { + if ('page' in r.site) { + r.site.page += firstPartyString; + } else { + r.site.page = firstPartyString; + } + currentRequestSize += fpdRequestSize; + } else { + utils.logError('ix bidder: IX config FPD request size has exceeded maximum request size.'); + } } // Create t in payload if timeout is configured. @@ -617,7 +639,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length; } - if (BANNER in impressionObjects[0]) { + if (impressionObjects.length && BANNER in impressionObjects[0]) { const { id, banner: { topframe } } = impressionObjects[0]; const _bannerImpression = { id, @@ -642,6 +664,47 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.imp.push(...impressionObjects); } + currentRequestSize += currentImpressionSize; + + const fpd = config.getConfig('ortb2') || {}; + + if (!utils.isEmpty(fpd) && !isFpdAdded) { + r.ext.ixdiag.fpd = true; + + const site = { ...(fpd.site || fpd.context) }; + + Object.keys(site).forEach(key => { + if (FIRST_PARTY_DATA.SITE.indexOf(key) === -1) { + delete site[key]; + } + }); + + const user = { ...fpd.user }; + + Object.keys(user).forEach(key => { + if (FIRST_PARTY_DATA.USER.indexOf(key) === -1) { + delete user[key]; + } + }); + + const clonedRObject = utils.deepClone(r); + + clonedRObject.site = utils.mergeDeep({}, clonedRObject.site, site); + clonedRObject.user = utils.mergeDeep({}, clonedRObject.user, user); + + const requestSize = `${baseUrl}${utils.parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length; + + if (requestSize < MAX_REQUEST_SIZE) { + r.site = utils.mergeDeep({}, r.site, site); + r.user = utils.mergeDeep({}, r.user, user); + isFpdAdded = true; + const fpdRequestSize = encodeURIComponent(JSON.stringify({ ...site, ...user })).length; + currentRequestSize += fpdRequestSize; + } else { + utils.logError('ix bidder: FPD request size has exceeded maximum request size.'); + } + } + const isLastAdUnit = adUnitIndex === transactionIds.length - 1; if (wasAdUnitImpressionsTrimmed || isLastAdUnit) { @@ -664,6 +727,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.imp = []; r.ext.ixdiag.msd = 0; r.ext.ixdiag.msi = 0; + isFpdAdded = false; } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 4c24af6c082..53bfc74af7f 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1232,6 +1232,135 @@ describe('IndexexchangeAdapter', function () { }); }); + describe('First party data', function () { + afterEach(function() { + config.setConfig({ + ortb2: {} + }) + }); + + it('should not set ixdiag.fpd value if not defined', function () { + config.setConfig({ + ortb2: {} + }); + + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + const r = JSON.parse(request.data.r); + + expect(r.ext.ixdiag.fpd).to.be.undefined; + }); + + it('should set ixdiag.fpd value if it exists using fpd', function () { + config.setConfig({ + fpd: { + site: { + data: { + pageType: 'article' + } + } + } + }); + + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + const r = JSON.parse(request.data.r); + + expect(r.ext.ixdiag.fpd).to.exist; + }); + + it('should set ixdiag.fpd value if it exists using ortb2', function () { + config.setConfig({ + ortb2: { + site: { + ext: { + data: { + pageType: 'article' + } + } + } + } + }); + + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + const r = JSON.parse(request.data.r); + + expect(r.ext.ixdiag.fpd).to.exist; + }); + + it('should not send information that is not part of openRTB spec v2.5 using fpd', function () { + config.setConfig({ + fpd: { + site: { + keywords: 'power tools, drills', + search: 'drill', + testProperty: 'test_string' + }, + user: { + keywords: ['a'], + testProperty: 'test_string' + } + } + }); + + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + const r = JSON.parse(request.data.r); + + expect(r.site.keywords).to.exist; + expect(r.site.search).to.exist; + expect(r.site.testProperty).to.be.undefined; + expect(r.user.keywords).to.exist; + expect(r.user.testProperty).to.be.undefined; + }); + + it('should not send information that is not part of openRTB spec v2.5 using ortb2', function () { + config.setConfig({ + ortb2: { + site: { + keywords: 'power tools, drills', + search: 'drill', + testProperty: 'test_string' + }, + user: { + keywords: ['a'], + testProperty: 'test_string' + } + } + }); + + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; + const r = JSON.parse(request.data.r); + + expect(r.site.keywords).to.exist; + expect(r.site.search).to.exist; + expect(r.site.testProperty).to.be.undefined; + expect(r.user.keywords).to.exist; + expect(r.user.testProperty).to.be.undefined; + }); + + it('should not add fpd data to r object if it exceeds maximum request', function () { + config.setConfig({ + ortb2: { + site: { + keywords: 'power tools, drills', + search: 'drill', + }, + user: { + keywords: Array(1000).join('#'), + } + } + }); + + const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); + bid.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES; + + const request = spec.buildRequests([bid])[0]; + const r = JSON.parse(request.data.r); + + expect(r.site.ref).to.exist; + expect(r.site.keywords).to.be.undefined; + expect(r.user).to.be.undefined; + }); + }); + describe('buildRequests', function () { let request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const requestUrl = request.url; From 5a9bbb391a3c955bfe265e349fb249048f0069fe Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:46:47 +0530 Subject: [PATCH 1380/1476] BrightMountainMedia: update server request format (#7210) * Update BrightMountainMedia cookie sync URL * Bright Mountain Media: Update bidder code * Bright Mountain Media: Add brightmountainmedia as alias * Bright Mountain Media: Update Bid Endpoint * BrightMountainMedia Bid Adapter: add floors module support * BrightMountainMedia Bid Adapter: support advertiserDomains * BrightMountainMedia Bid Adapter: support userId * BrightMountainMedia: update read userid form userIdAsEids * BrightMountainMedia: refactor request format --- modules/brightMountainMediaBidAdapter.js | 324 ++++++++++------ .../brightMountainMediaBidAdapter_spec.js | 345 ++++++++++-------- 2 files changed, 399 insertions(+), 270 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 51356098b58..602e621632c 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -1,22 +1,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'bmtm'; -const AD_URL = 'https://one.elitebidder.com/api/hb'; +const AD_URL = 'https://one.elitebidder.com/api/hb?sid='; const SYNC_URL = 'https://console.brightmountainmedia.com:8443/cookieSync'; - -const videoExt = [ - 'video/x-ms-wmv', - 'video/x-flv', - 'video/mp4', - 'video/3gpp', - 'application/x-mpegURL', - 'video/quicktime', - 'video/x-msvideo', - 'application/x-shockwave-flash', - 'application/javascript' -]; +const CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, @@ -24,134 +14,124 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && bid.params.placement_id); + if (bid.bidId && bid.bidder && bid.params && bid.params.placement_id) { + return true; + } + if (bid.params.placement_id == 0 && bid.params.test === 1) { + return true; + } + return false; }, buildRequests: (validBidRequests, bidderRequest) => { - let winTop = window; - let location; - try { - location = new URL(bidderRequest.refererInfo.referer) - winTop = window.top; - } catch (e) { - location = winTop.location; - utils.logMessage(e); - }; - let placements = []; - let request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language : '', - 'secure': document.location.protocol === 'https:' ? 1 : 0, - 'host': location.host, - 'page': location.href, - 'prebidVersion': '$prebid.version$', - 'placements': placements + let requestData = []; + let size = [0, 0]; + let oRTBRequest = { + at: 2, + site: buildSite(bidderRequest), + device: buildDevice(), + cur: [CURRENCY], + tmax: 1000, + regs: buildRegs(bidderRequest), + user: {}, + source: {}, }; - if (bidderRequest) { - if (bidderRequest.gdprConsent) { - request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' - request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 - } - } - for (let i = 0; i < validBidRequests.length; i++) { - let bid = validBidRequests[i]; - let placement = { - placementId: bid.params.placement_id, - bidId: bid.bidId, - floor: {}, - userIds: {}, - }; - - if (bid.mediaTypes.hasOwnProperty(BANNER)) { - placement['traffic'] = BANNER; + + validBidRequests.forEach((bid) => { + oRTBRequest['id'] = utils.generateUUID(); + oRTBRequest['imp'] = [ + { + id: '1', + bidfloor: 0, + bidfloorcur: CURRENCY, + secure: document.location.protocol === 'https:' ? 1 : 0, + ext: { + placement_id: bid.params.placement_id, + prebidVersion: '$prebid.version$', + } + }, + ]; + + if (utils.deepAccess(bid, 'mediaTypes.banner')) { if (bid.mediaTypes.banner.sizes) { - placement['sizes'] = bid.mediaTypes.banner.sizes; + size = bid.mediaTypes.banner.sizes[0]; } - } - if (bid.mediaTypes.hasOwnProperty(VIDEO)) { - placement['traffic'] = VIDEO; - if (bid.mediaTypes.video.context) { - placement['context'] = bid.mediaTypes.video.context; + oRTBRequest.imp[0].banner = { + h: size[0], + w: size[1], } + } else { if (bid.mediaTypes.video.playerSize) { - placement['sizes'] = bid.mediaTypes.video.playerSize; - } - if (bid.mediaTypes.video.mimes && Array.isArray(bid.mediaTypes.video.mimes)) { - placement['mimes'] = bid.mediaTypes.video.mimes; - } else { - placement['mimes'] = videoExt; - } - if (bid.mediaTypes.video.skip != undefined) { - placement['skip'] = bid.mediaTypes.video.skip; + size = bid.mediaTypes.video.playerSize[0]; } - if (bid.mediaTypes.video.playbackmethod && Array.isArray(bid.mediaTypes.video.playbackmethod)) { - placement['playbackmethod'] = bid.mediaTypes.video.playbackmethod; + + oRTBRequest.imp[0].video = { + h: size[0], + w: size[1], + mimes: bid.mediaTypes.video.mimes ? bid.mediaTypes.video.mimes : [], + skip: bid.mediaTypes.video.skip ? 1 : 0, + playbackmethod: bid.mediaTypes.video.playbackmethod ? bid.mediaTypes.video.playbackmethod : [], + protocols: bid.mediaTypes.video.protocols ? bid.mediaTypes.video.protocols : [], + api: bid.mediaTypes.video.api ? bid.mediaTypes.video.api : [], + minduration: bid.mediaTypes.video.minduration ? bid.mediaTypes.video.minduration : 1, + maxduration: bid.mediaTypes.video.maxduration ? bid.mediaTypes.video.maxduration : 999, } } - if (typeof bid.getFloor === 'function') { - let floorInfo = {}; + oRTBRequest.imp[0].bidfloor = getFloor(bid, size); + oRTBRequest.user = getUserIdAsEids(bid.userIdAsEids) + oRTBRequest.source = getSchain(bid.schain) + + requestData.push({ + method: 'POST', + url: `${AD_URL}${bid.params.placement_id}`, + data: JSON.stringify(oRTBRequest), + bidRequest: bid, + }) + }); + return requestData; + }, - for (let size of placement['sizes']) { - floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: placement['traffic'], - size: size, - }); + interpretResponse: (serverResponse, { bidRequest }) => { + let bidResponse = []; + let bid; + let response; - if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') { - placement.floor[`${size[0]}x${size[1]}`] = parseFloat(floorInfo.floor); - } - } - } + try { + response = serverResponse.body + bid = response.seatbid[0].bid[0]; + } catch (e) { + response = null; + } - if (bid.schain) { - placement.schain = bid.schain; - } + if (!response || !bid || !bid.adm || !bid.price) { + utils.logWarn(`Bidder ${spec.code} no valid bid`); + return []; + } - if (bid.userIdAsEids) { - placement.userIds = { eids: bid.userIdAsEids }; + let tempResponse = { + requestId: bidRequest.bidId, + cpm: bid.price, + currency: response.cur, + width: bid.w, + height: bid.h, + creativeId: bid.crid, + mediaType: utils.deepAccess(bidRequest, 'mediaTypes.banner') ? BANNER : VIDEO, + ttl: 3000, + netRevenue: true, + meta: { + advertiserDomains: bid.adomain } - placements.push(placement); - } - return { - method: 'POST', - url: AD_URL, - data: request }; - }, - interpretResponse: (serverResponse) => { - let bidResponse = []; - const response = serverResponse.body; - if (response && Array.isArray(response) && response.length > 0) { - for (let i = 0; i < response.length; i++) { - if (response[i].cpm > 0) { - const tempResponse = { - requestId: response[i].requestId, - width: response[i].width, - height: response[i].height, - cpm: response[i].cpm, - mediaType: response[i].mediaType, - creativeId: response[i].creativeId, - currency: response[i].currency, - netRevenue: response[i].netRevenue, - ttl: response[i].ttl, - ad: response[i].ad, - meta: { - advertiserDomains: response[i].adomain && response[i].adomain.length ? response[i].adomain : [], - } - }; - - if (response[i].mediaType && response[i].mediaType === 'video') { - response[i].vastXml = response[i].ad; - } - bidResponse.push(tempResponse); - } - } + if (tempResponse.mediaType === BANNER) { + tempResponse.ad = replaceAuctionPrice(bid.adm, bid.price); + } else { + tempResponse.vastXml = replaceAuctionPrice(bid.adm, bid.price); } + + bidResponse.push(tempResponse); return bidResponse; }, @@ -167,3 +147,107 @@ export const spec = { }; registerBidder(spec); + +function buildSite(bidderRequest) { + let site = { + name: window.location.hostname, + publisher: { + domain: window.location.hostname, + } + }; + + if (bidderRequest && bidderRequest.refererInfo) { + utils.deepSetValue( + site, + 'page', + bidderRequest.refererInfo.referer.href ? bidderRequest.refererInfo.referer.href : '', + ); + utils.deepSetValue( + site, + 'ref', + bidderRequest.refererInfo.referer ? bidderRequest.refererInfo.referer : '', + ); + } + return site; +} + +function buildDevice() { + return { + ua: navigator.userAgent, + w: window.top.screen.width, + h: window.top.screen.height, + js: 1, + language: navigator.language, + dnt: navigator.doNotTrack === 'yes' || navigator.doNotTrack == '1' || + navigator.msDoNotTrack == '1' ? 1 : 0, + } +} + +function buildRegs(bidderRequest) { + let regs = { + coppa: config.getConfig('coppa') == true ? 1 : 0, + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue( + regs, + 'ext.gdpr', + bidderRequest.gdprConsent.gdprApplies ? 1 : 0, + ); + utils.deepSetValue( + regs, + 'ext.gdprConsentString', + bidderRequest.gdprConsent.consentString || 'ALL', + ); + } + + if (bidderRequest && bidderRequest.uspConsent) { + utils.deepSetValue(regs, + 'ext.us_privacy', + bidderRequest.uspConsent); + } + return regs; +} + +function replaceAuctionPrice(str, cpm) { + if (!str) return; + return str.replace(/\$\{AUCTION_PRICE\}/g, cpm); +} + +function getFloor(bid, size) { + if (typeof bid.getFloor === 'function') { + let floorInfo = {}; + floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: size, + }); + + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') { + return parseFloat(floorInfo.floor); + } + } + return 0; +} + +function getUserIdAsEids(userIds) { + if (userIds) { + return { + ext: { + eids: userIds, + } + } + }; + return {}; +} + +function getSchain(schain) { + if (schain) { + return { + ext: { + schain: schain, + } + } + } + return {}; +} diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 114c1f19a6e..5e433abebd8 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -2,8 +2,6 @@ import { expect } from 'chai'; import { spec } from '../../../modules/brightMountainMediaBidAdapter.js'; const BIDDER_CODE = 'bmtm'; -const ENDPOINT_URL = 'https://one.elitebidder.com/api/hb'; -const ENDPOINT_URL_SYNC = 'https://console.brightmountainmedia.com:8443/cookieSync'; const PLACEMENT_ID = 329; describe('brightMountainMediaBidAdapter_spec', function () { @@ -22,6 +20,47 @@ describe('brightMountainMediaBidAdapter_spec', function () { } }, transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + getFloor: function () { + return { + currency: 'USD', + floor: 0.5, + } + }, + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }, + userIdAsEids: [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5-ZHMOaW5vh_TJhKVSaTWmuoTpwqjGGwx5v0WbaSV8yw', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '00000000000000000000000000', + 'atype': 1 + } + ] + } + ] }; let bidVideo = { @@ -56,7 +95,12 @@ describe('brightMountainMediaBidAdapter_spec', function () { refererInfo: { referer: 'http://www.example.com', reachedTop: true, - } + }, + gdprConsent: { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true + }, + bids: [bidBanner], }; describe('isBidRequestValid', function () { @@ -64,119 +108,60 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(spec.isBidRequestValid(bidBanner)).to.be.true; }); it('Should return false when required params are not passed', function () { - bidBanner.params = {} + bidBanner.params = {}; expect(spec.isBidRequestValid(bidBanner)).to.be.false; }); }); - function testServerRequestBody(serverRequest) { + describe('buildRequests', function () { + let request = spec.buildRequests([bidBanner], bidderRequest)[0]; + let data = JSON.parse(request.data); + 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(ENDPOINT_URL); + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + expect(request.method).to.be.a('string'); + expect(request.url).to.be.a('string'); + expect(request.data).to.be.an('string'); }); 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', 'prebidVersion', 'placements'); - 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'); - let placements = data['placements']; - expect(placements).to.be.an('array'); + expect(data).to.have.all.keys('at', 'site', 'device', 'cur', 'tmax', 'regs', 'user', 'source', 'imp', 'id'); + expect(data.at).to.be.a('number'); + expect(data.site).to.be.an('object'); + expect(data.device).to.be.an('object'); + expect(data.cur).to.be.an('array'); + expect(data.tmax).to.be.a('number'); + expect(data.regs).to.be.an('object'); + expect(data.user).to.be.an('object'); + expect(data.source).to.be.an('object'); + expect(data.imp).to.be.an('array'); + expect(data.id).to.be.a('string'); }); - } - describe('buildRequests', function () { - bidderRequest['bids'] = [bidBanner]; - let serverRequest = spec.buildRequests([bidBanner], bidderRequest); - testServerRequestBody(serverRequest); - - it('sends bidfloor param if present', function () { - bidBanner.getFloor = function () { - return { - currency: 'USD', - floor: 0.5, - } - }; - const request = spec.buildRequests([bidBanner], bidderRequest); - expect(request.data.placements[0].floor['300x250']).to.equal(0.5); + it('Sends bidfloor param if present', function () { + expect(data.imp[0].bidfloor).to.equal(0.5); }); - it('sends gdpr info if exists', function () { - const gdprConsent = { - consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', - gdprApplies: true - }; - - bidderRequest['gdprConsent'] = gdprConsent; - const request = spec.buildRequests([bidBanner], bidderRequest); - - expect(request.data.gdpr_require).to.exist.and.to.be.a('number'); - expect(request.data.gdpr_consent).to.exist.and.to.be.a('string'); + it('Sends regs info if exists', function () { + expect(data.regs.ext.gdpr).to.exist.and.to.be.a('number'); + expect(data.regs.ext.gdprConsentString).to.exist.and.to.be.a('string'); + expect(data.regs.ext.us_privacy).to.exist.and.to.be.a('string'); }); - it('sends schain info if exists', function () { - const schain = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1 - } - ] - }; - bidBanner.schain = schain; - const request = spec.buildRequests([bidBanner], bidderRequest); - expect(request.data.placements[0].schain).to.be.an('object'); + it('Sends schain info if exists', function () { + expect(data.source.ext).to.be.an('object'); }); it('sends userId info if exists', function () { - const userIdAsEids = [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': 'ID5-ZHMOaW5vh_TJhKVSaTWmuoTpwqjGGwx5v0WbaSV8yw', - 'atype': 1, - 'ext': { - 'linkType': 2 - } - } - ] - }, - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '00000000000000000000000000', - 'atype': 1 - } - ] - } - ]; - bidBanner.userIdAsEids = userIdAsEids; - const request = spec.buildRequests([bidBanner], bidderRequest); - - expect(request.data.placements[0]).to.have.property('userIds'); - expect(request.data.placements[0].userIds).to.not.equal(null).and.to.not.be.undefined; - expect(request.data.placements[0].userIds.eids.length).to.greaterThan(0); - for (let index in request.data.placements[0].userIds.eids) { - let eid = request.data.placements[0].userIds.eids[index]; + expect(data.user.ext).to.have.property('eids'); + expect(data.user.ext.eids).to.not.equal(null).and.to.not.be.undefined; + expect(data.user.ext.eids.length).to.greaterThan(0); + for (let index in data.user.ext.eids) { + let eid = data.user.ext.eids[index]; expect(eid.source).to.not.equal(null).and.to.not.be.undefined; expect(eid.uids).to.not.equal(null).and.to.not.be.undefined; for (let uidsIndex in eid.uids) { @@ -186,22 +171,97 @@ describe('brightMountainMediaBidAdapter_spec', function () { } }); - bidderRequest['bids'] = [bidVideo]; - serverRequest = spec.buildRequests([bidVideo], bidderRequest); - testServerRequestBody(serverRequest); + it('Returns valid data if array of bids is valid for banner', function () { + expect(data).to.be.an('object'); + expect(data).to.have.property('imp'); + expect(data.imp.length).to.greaterThan(0); + expect(data.imp[0]).to.have.property('banner'); + expect(data.imp[0].banner).to.be.an('object'); + expect(data.imp[0].banner.h).to.exist.and.to.be.a('number'); + expect(data.imp[0].banner.w).to.exist.and.to.be.a('number'); + }); - 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; + it('Returns valid data if array of bids is valid for video', function () { + bidderRequest.bids = [bidVideo]; + let serverRequest = spec.buildRequests([bidVideo], bidderRequest)[0]; + let data = JSON.parse(serverRequest.data); + expect(data).to.be.an('object'); + expect(data).to.have.property('imp'); + expect(data.imp.length).to.greaterThan(0); + expect(data.imp[0]).to.have.property('video'); + expect(data.imp[0].video).to.be.an('object'); + expect(data.imp[0].video.h).to.exist.and.to.be.a('number'); + expect(data.imp[0].video.w).to.exist.and.to.be.a('number'); }); }); - function testServerResponse(serverResponses) { - it('Returns an array of valid server responses if response object is valid', function () { - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; + describe('interpretResponse', function () { + let resObjectBanner = { + 'id': '2763-05f22da29b3ffb6-6959', + 'bidid': 'e5b41111bec9e4a4e94b85d082f8fb08', + 'seatbid': [ + { + 'bid': [ + { + 'id': '9550c3e641761cfbf2a4dd672b50ddb9', + 'impid': '968', + 'price': 0.3, + 'w': 300, + 'h': 250, + 'nurl': 'https://example.com/?w=nr&pf=${AUCTION_PRICE}&type=b&uq=483531c101942cbb270cd088b2eec43f', + 'adomain': [ + 'example.com' + ], + 'cid': '3845_105654', + 'crid': '3845_305654', + 'adm': '

Test Ad

', + 'adid': '17794c46ca26', + 'iurl': 'https://example.com/?t=preview2&k=17794c46ca26' + } + ], + 'seat': '3845' + } + ], + 'cur': 'USD' + }; + + let resObjectVideo = { + 'id': '2763-05f22da29b3ffb6-6959', + 'bidid': 'e5b41111bec9e4a4e94b85d082f8fb08', + 'seatbid': [ + { + 'bid': [ + { + 'id': '9550c3e641761cfbf2a4dd672b50ddb9', + 'impid': '968', + 'price': 0.3, + 'w': 300, + 'h': 250, + 'nurl': 'https://example.com/?w=nr&pf=${AUCTION_PRICE}&type=b&uq=483531c101942cbb270cd088b2eec43f', + 'adomain': [ + 'example.com' + ], + 'cid': '3845_105654', + 'crid': '3845_305654', + 'adm': '', + 'adid': '17794c46ca26', + 'iurl': 'https://example.com/?t=preview2&k=17794c46ca26' + } + ], + 'seat': '3845' + } + ], + 'cur': 'USD' + }; + + it('Returns an array of valid response if response object is valid for banner', function () { + const bidResponse = spec.interpretResponse({ + body: resObjectBanner + }, { bidRequest: bidBanner }); + + expect(bidResponse).to.be.an('array').that.is.not.empty; + for (let i = 0; i < bidResponse.length; i++) { + let dataItem = bidResponse[i]; expect(dataItem.requestId).to.be.a('string'); expect(dataItem.cpm).to.be.a('number'); expect(dataItem.width).to.be.a('number'); @@ -215,49 +275,34 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(dataItem.meta.advertiserDomains[0]).to.be.a('string'); } }); - } - - describe('interpretResponse', function () { - let resObjectBanner = { - body: [{ - requestId: '123', - mediaType: 'banner', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD', - adomain: ['adomain.com'], - }] - }; - let resObjectVideo = { - body: [{ - requestId: '123', - mediaType: 'video', - cpm: 1.5, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD', - adomain: ['adomain.com'], - }] - }; - let serverResponses = spec.interpretResponse(resObjectBanner); - testServerResponse(serverResponses); + it('Returns an array of valid response if response object is valid for video', function () { + const bidResponse = spec.interpretResponse({ + body: resObjectVideo + }, { bidRequest: bidVideo }); - serverResponses = spec.interpretResponse(resObjectVideo); - testServerResponse(serverResponses); + expect(bidResponse).to.be.an('array').that.is.not.empty; + for (let i = 0; i < bidResponse.length; i++) { + let dataItem = bidResponse[i]; + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.vastXml).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + expect(dataItem.meta.advertiserDomains[0]).to.be.a('string'); + } + }); it('Returns an empty array if invalid response is passed', function () { - serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; + const bidResponse = spec.interpretResponse({ + body: '' + }, { bidRequest: bidBanner }); + expect(bidResponse).to.be.an('array').that.is.empty; }); }); @@ -270,7 +315,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.exist; expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.exist; expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe') - expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal(ENDPOINT_URL_SYNC) + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('https://console.brightmountainmedia.com:8443/cookieSync') }); }); }); From 2eb70e14febed98a9261dff5e151f9be106efcd5 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:45:53 +0200 Subject: [PATCH 1381/1476] Malltv Analytics Adapter: add new analytics adapter (#7218) * Added malltv analytics adapter * Removed error endpoint and added vastUrl * Removed ad render failed test * Check if bid.getCpmInNewCurrency is a function * refactor getCpmInEur * Fixed tests failing when run with other tests --- modules/malltvAnalyticsAdapter.js | 188 ++++++ modules/malltvAnalyticsAdapter.md | 25 + .../modules/malltvAnalyticsAdapter_spec.js | 540 ++++++++++++++++++ 3 files changed, 753 insertions(+) create mode 100644 modules/malltvAnalyticsAdapter.js create mode 100644 modules/malltvAnalyticsAdapter.md create mode 100644 test/spec/modules/malltvAnalyticsAdapter_spec.js diff --git a/modules/malltvAnalyticsAdapter.js b/modules/malltvAnalyticsAdapter.js new file mode 100644 index 00000000000..3431681ef2f --- /dev/null +++ b/modules/malltvAnalyticsAdapter.js @@ -0,0 +1,188 @@ +import {ajax} from '../src/ajax.js' +import adapter from '../src/AnalyticsAdapter.js' +import CONSTANTS from '../src/constants.json' +import adapterManager from '../src/adapterManager.js' +import {getGlobal} from '../src/prebidGlobal.js' +import {logInfo, logError, deepClone} from '../src/utils.js' + +const analyticsType = 'endpoint' +export const ANALYTICS_VERSION = '1.0.0' +export const DEFAULT_SERVER = 'https://central.mall.tv/analytics' + +const { + EVENTS: { + AUCTION_END, + BID_TIMEOUT + } +} = CONSTANTS + +export const BIDDER_STATUS = { + BID: 1, + NO_BID: 2, + BID_WON: 3, + TIMEOUT: 4 +} + +export const getCpmInEur = function (bid) { + if (bid.currency !== 'EUR' && typeof bid.getCpmInNewCurrency === 'function') { + return bid.getCpmInNewCurrency('EUR'); + } + + return bid.cpm; +} + +const analyticsOptions = {} + +export const parseBidderCode = function (bid) { + let bidderCode = bid.bidderCode || bid.bidder + return bidderCode.toLowerCase() +} + +export const parseAdUnitCode = function (bidResponse) { + return bidResponse.adUnitCode.toLowerCase() +} + +export const malltvAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, analyticsType}), { + + cachedAuctions: {}, + + initConfig(config) { + /** + * Required option: propertyId + * + * Optional option: server + * @type {boolean} + */ + analyticsOptions.options = deepClone(config.options) + if (typeof config.options.propertyId !== 'string' || config.options.propertyId.length < 1) { + logError('"options.propertyId" is required.') + return false + } + + analyticsOptions.propertyId = config.options.propertyId + analyticsOptions.server = config.options.server || DEFAULT_SERVER + + return true + }, + track({eventType, args}) { + switch (eventType) { + case BID_TIMEOUT: + this.handleBidTimeout(args) + break + case AUCTION_END: + this.handleAuctionEnd(args) + break + } + }, + handleBidTimeout(timeoutBids) { + timeoutBids.forEach((bid) => { + const cachedAuction = this.getCachedAuction(bid.auctionId) + cachedAuction.timeoutBids.push(bid) + }) + }, + handleAuctionEnd(auctionEndArgs) { + const cachedAuction = this.getCachedAuction(auctionEndArgs.auctionId) + const highestCpmBids = getGlobal().getHighestCpmBids() + this.sendEventMessage('end', + this.createBidMessage(auctionEndArgs, highestCpmBids, cachedAuction.timeoutBids) + ) + }, + createBidMessage(auctionEndArgs, winningBids, timeoutBids) { + const {auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids} = auctionEndArgs + const message = this.createCommonMessage(auctionId) + + message.auctionElapsed = (auctionEnd - timestamp) + message.timeout = timeout + + adUnitCodes.forEach((adUnitCode) => { + message.adUnits[adUnitCode] = {} + }) + + // We handled noBids first because when currency conversion is enabled, a bid with a foreign currency + // will be set to NO_BID initially, and then set to BID after the currency rate json file is fully loaded. + // In this situation, the bid exists in both noBids and bids arrays. + noBids.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.NO_BID)) + + // This array may contain some timeout bids (responses come back after auction timeout) + bidsReceived.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.BID)) + + // We handle timeout after bids since it's possible that a bid has a response, but the response comes back + // after auction end. In this case, the bid exists in both bidsReceived and timeoutBids arrays. + timeoutBids.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.TIMEOUT)) + + // mark the winning bids with prebidWon = true + winningBids.forEach(bid => { + const adUnitCode = parseAdUnitCode(bid) + const bidder = parseBidderCode(bid) + message.adUnits[adUnitCode][bidder].prebidWon = true + }) + return message + }, + createCommonMessage(auctionId) { + return { + analyticsVersion: ANALYTICS_VERSION, + auctionId: auctionId, + propertyId: analyticsOptions.propertyId, + referrer: window.location.href, + prebidVersion: '$prebid.version$', + adUnits: {}, + } + }, + addBidResponseToMessage(message, bid, status) { + const adUnitCode = parseAdUnitCode(bid) + message.adUnits[adUnitCode] = message.adUnits[adUnitCode] || {} + const bidder = parseBidderCode(bid) + const bidResponse = this.serializeBidResponse(bid, status) + message.adUnits[adUnitCode][bidder] = bidResponse + }, + serializeBidResponse(bid, status) { + const result = { + prebidWon: (status === BIDDER_STATUS.BID_WON), + isTimeout: (status === BIDDER_STATUS.TIMEOUT), + status: status, + } + if (status === BIDDER_STATUS.BID || status === BIDDER_STATUS.BID_WON) { + Object.assign(result, { + time: bid.timeToRespond, + cpm: bid.cpm, + currency: bid.currency, + originalCpm: bid.originalCpm || bid.cpm, + cpmEur: getCpmInEur(bid), + originalCurrency: bid.originalCurrency || bid.currency, + vastUrl: bid.vastUrl + }) + } + return result + }, + sendEventMessage(endPoint, data) { + logInfo(`AJAX: ${endPoint}: ` + JSON.stringify(data)) + + ajax(`${analyticsOptions.server}/${endPoint}`, null, JSON.stringify(data), { + contentType: 'application/json' + }) + }, + getCachedAuction(auctionId) { + this.cachedAuctions[auctionId] = this.cachedAuctions[auctionId] || { + timeoutBids: [], + } + return this.cachedAuctions[auctionId] + }, + getAnalyticsOptions() { + return analyticsOptions + }, +}) + +// save the base class function +malltvAnalyticsAdapter.originEnableAnalytics = malltvAnalyticsAdapter.enableAnalytics + +// override enableAnalytics so we can get access to the config passed in from the page +malltvAnalyticsAdapter.enableAnalytics = function (config) { + if (this.initConfig(config)) { + malltvAnalyticsAdapter.originEnableAnalytics(config) // call the base class function + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: malltvAnalyticsAdapter, + code: 'malltvAnalytics' +}) diff --git a/modules/malltvAnalyticsAdapter.md b/modules/malltvAnalyticsAdapter.md new file mode 100644 index 00000000000..cc62d939694 --- /dev/null +++ b/modules/malltvAnalyticsAdapter.md @@ -0,0 +1,25 @@ +# Overview + +Module Name: Malltv Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: arditb@gjirafa.com + +# Description + +Analytics adapter for Malltv + +# Parameters + +``` +{ + provider: 'malltvAnalytics', + options: { + 'propertyId': 'YOUR_PROPERTY_ID', // Required + 'server': 'YOUR_ANALYTICS_SERVER' // Optional + } +} +``` + +PS. [Prebid currency module](http://prebid.org/dev-docs/modules/currency.html) is required, please make sure your prebid code contains currency module code. diff --git a/test/spec/modules/malltvAnalyticsAdapter_spec.js b/test/spec/modules/malltvAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..599ac6e4256 --- /dev/null +++ b/test/spec/modules/malltvAnalyticsAdapter_spec.js @@ -0,0 +1,540 @@ +import { + malltvAnalyticsAdapter, parseBidderCode, parseAdUnitCode, + ANALYTICS_VERSION, BIDDER_STATUS, DEFAULT_SERVER +} from 'modules/malltvAnalyticsAdapter.js' +import { expect } from 'chai' +import { getCpmInEur } from '../../../modules/malltvAnalyticsAdapter' +import events from 'src/events' +import constants from 'src/constants.json' + +const auctionId = 'b0b39610-b941-4659-a87c-de9f62d3e13e' +const propertyId = '123456' +const server = 'https://analytics.server.url/v1' + +describe('Malltv Prebid AnalyticsAdapter Testing', function () { + describe('event tracking and message cache manager', function () { + beforeEach(function () { + const configOptions = { propertyId } + + sinon.stub(events, 'getEvents').returns([]) + malltvAnalyticsAdapter.enableAnalytics({ + provider: 'malltvAnalytics', + options: configOptions + }) + }) + + afterEach(function () { + malltvAnalyticsAdapter.disableAnalytics() + events.getEvents.restore() + }) + + describe('#getCpmInEur()', function() { + it('should get bid cpm as currency is EUR', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'malltv', + bidderCode: 'MALLTV', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1' + }, + ] + const result = getCpmInEur(receivedBids[0]) + expect(result).to.equal(0.1) + }) + }) + + describe('#parseBidderCode()', function() { + it('should get lower case bidder code from bidderCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'malltv', + bidderCode: 'MALLTV', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1' + }, + ] + const result = parseBidderCode(receivedBids[0]) + expect(result).to.equal('malltv') + }) + + it('should get lower case bidder code from bidder field value as bidderCode field is missing', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'MALLTV', + bidderCode: '', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1' + }, + ] + const result = parseBidderCode(receivedBids[0]) + expect(result).to.equal('malltv') + }) + }) + + describe('#parseAdUnitCode()', function() { + it('should get lower case adUnit code from adUnitCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'ADUNIT', + bidder: 'malltv', + bidderCode: 'MALLTV', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1' + }, + ] + const result = parseAdUnitCode(receivedBids[0]) + expect(result).to.equal('adunit') + }) + }) + + describe('#getCachedAuction()', function() { + const existing = {timeoutBids: [{}]} + malltvAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing + + it('should get the existing cached object if it exists', function() { + const result = malltvAnalyticsAdapter.getCachedAuction('test_auction_id') + + expect(result).to.equal(existing) + }) + + it('should create a new object and store it in the cache on cache miss', function() { + const result = malltvAnalyticsAdapter.getCachedAuction('no_such_id') + + expect(result).to.deep.include({ + timeoutBids: [], + }) + }) + }) + + describe('when formatting JSON payload sent to backend', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'malltv', + bidderCode: 'malltv', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1', + vastUrl: null + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'gjirafa', + bidderCode: 'gjirafa', + requestId: 'b2c3d4e5', + timeToRespond: 100, + cpm: 0.08, + currency: 'EUR', + originalCpm: 0.08, + originalCurrency: 'EUR', + ad: 'fake ad2', + vastUrl: null + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'malltv', + bidderCode: 'malltv', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'EUR', + originalCpm: 0.09, + originalCurrency: 'EUR', + ad: 'fake ad3', + vastUrl: null + }, + ] + const highestCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'malltv', + bidderCode: 'malltv', + // No requestId + timeToRespond: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + ad: 'fake ad1', + vastUrl: null + } + ] + const noBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'malltv', + bidderCode: 'malltv', + bidId: 'a1b2c3d4', + } + ] + const timeoutBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'gjirafa', + bidderCode: 'gjirafa', + bidId: '00123d4c', + } + ] + const withoutOriginalCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'malltv', + bidderCode: 'malltv', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.29, + currency: 'EUR', + originalCpm: '', + originalCurrency: 'EUR', + ad: 'fake ad3' + }, + ] + const withoutOriginalCurrencyBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'malltv', + bidderCode: 'malltv', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'EUR', + originalCpm: 0.09, + originalCurrency: '', + ad: 'fake ad3' + }, + ] + + function assertHavingRequiredMessageFields(message) { + expect(message).to.include({ + analyticsVersion: ANALYTICS_VERSION, + auctionId: auctionId, + propertyId: propertyId, + prebidVersion: '$prebid.version$', + }) + } + + describe('#createCommonMessage', function() { + it('should correctly serialize some common fields', function() { + const message = malltvAnalyticsAdapter.createCommonMessage(auctionId) + + assertHavingRequiredMessageFields(message) + }) + }) + + describe('#serializeBidResponse', function() { + it('should handle BID properly and serialize bid price related fields', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID) + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + cpmEur: 0.1, + }) + }) + + it('should handle NO_BID properly and set status to noBid', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.NO_BID) + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + }) + }) + + it('should handle BID_WON properly and serialize bid price related fields', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID_WON) + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + cpmEur: 0.1, + }) + }) + + it('should handle TIMEOUT properly and set status to timeout and isTimeout to true', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.TIMEOUT) + + expect(result).to.include({ + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + }) + }) + + it('should handle BID_WON properly and fill originalCpm field with cpm in missing originalCpm case', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(withoutOriginalCpmBids[0], BIDDER_STATUS.BID_WON) + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.29, + currency: 'EUR', + originalCpm: 0.29, + originalCurrency: 'EUR', + cpmEur: 0.29, + }) + }) + + it('should handle BID_WON properly and fill originalCurrency field with currency in missing originalCurrency case', function() { + const result = malltvAnalyticsAdapter.serializeBidResponse(withoutOriginalCurrencyBids[0], BIDDER_STATUS.BID_WON) + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.09, + currency: 'EUR', + originalCpm: 0.09, + originalCurrency: 'EUR', + cpmEur: 0.09, + }) + }) + }) + + describe('#addBidResponseToMessage()', function() { + it('should add a bid response in the output message, grouped by adunit_id and bidder', function() { + const message = { + adUnits: {} + } + malltvAnalyticsAdapter.addBidResponseToMessage(message, noBids[0], BIDDER_STATUS.NO_BID) + + expect(message.adUnits).to.deep.include({ + 'adunit_2': { + 'malltv': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + } + } + }) + }) + }) + + describe('#createBidMessage()', function() { + it('should format auction message sent to the backend', function() { + const args = { + auctionId: auctionId, + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + adUnitCodes: ['adunit_1', 'adunit_2'], + bidsReceived: receivedBids, + noBids: noBids + } + + const result = malltvAnalyticsAdapter.createBidMessage(args, highestCpmBids, timeoutBids) + + assertHavingRequiredMessageFields(result) + expect(result).to.deep.include({ + auctionElapsed: 100, + timeout: 3000, + adUnits: { + 'adunit_1': { + 'malltv': { + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'EUR', + originalCpm: 0.1, + originalCurrency: 'EUR', + cpmEur: 0.1, + vastUrl: null + }, + 'gjirafa': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 100, + cpm: 0.08, + currency: 'EUR', + originalCpm: 0.08, + originalCurrency: 'EUR', + cpmEur: 0.08, + vastUrl: null + } + }, + 'adunit_2': { + // this bid result exists in both bid and noBid arrays and should be treated as bid + 'malltv': { + prebidWon: false, + isTimeout: false, + time: 120, + cpm: 0.09, + currency: 'EUR', + originalCpm: 0.09, + originalCurrency: 'EUR', + cpmEur: 0.09, + status: BIDDER_STATUS.BID, + vastUrl: null + }, + 'gjirafa': { + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + } + } + } + }) + }) + }) + + describe('#handleBidTimeout()', function() { + it('should cached the timeout bid as BID_TIMEOUT event was triggered', function() { + malltvAnalyticsAdapter.cachedAuctions['test_timeout_auction_id'] = { 'timeoutBids': [] } + const args = [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }] + + malltvAnalyticsAdapter.handleBidTimeout(args) + const result = malltvAnalyticsAdapter.getCachedAuction('test_timeout_auction_id') + expect(result).to.deep.include({ + timeoutBids: [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }] + }) + }) + }) + }) + }) + + describe('Malltv Analytics Adapter track handler ', function () { + const configOptions = { propertyId } + + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]) + malltvAnalyticsAdapter.enableAnalytics({ + provider: 'malltvAnalytics', + options: configOptions + }) + }) + + afterEach(function () { + malltvAnalyticsAdapter.disableAnalytics() + events.getEvents.restore() + }) + + it('should call handleBidTimeout as BID_TIMEOUT trigger event', function() { + sinon.spy(malltvAnalyticsAdapter, 'handleBidTimeout') + events.emit(constants.EVENTS.BID_TIMEOUT, {}) + sinon.assert.callCount(malltvAnalyticsAdapter.handleBidTimeout, 1) + malltvAnalyticsAdapter.handleBidTimeout.restore() + }) + + it('should call handleAuctionEnd as AUCTION_END trigger event', function() { + sinon.spy(malltvAnalyticsAdapter, 'handleAuctionEnd') + events.emit(constants.EVENTS.AUCTION_END, {}) + sinon.assert.callCount(malltvAnalyticsAdapter.handleAuctionEnd, 1) + malltvAnalyticsAdapter.handleAuctionEnd.restore() + }) + }) + + describe('enableAnalytics and config parser', function () { + const configOptions = { propertyId, server } + + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]) + malltvAnalyticsAdapter.enableAnalytics({ + provider: 'malltvAnalytics', + options: configOptions + }) + }) + + afterEach(function () { + malltvAnalyticsAdapter.disableAnalytics() + events.getEvents.restore() + }) + + it('should parse config correctly with optional values', function () { + const { options, propertyId, server } = malltvAnalyticsAdapter.getAnalyticsOptions() + + expect(options).to.deep.equal(configOptions) + expect(propertyId).to.equal(configOptions.propertyId) + expect(server).to.equal(configOptions.server) + }) + + it('should not enable Analytics when propertyId is missing', function() { + const configOptions = { + options: { } + } + + const isConfigValid = malltvAnalyticsAdapter.initConfig(configOptions) + expect(isConfigValid).to.equal(false) + }) + + it('should use DEFAULT_SERVER when server is missing', function () { + const configOptions = { + options: { + propertyId + } + } + malltvAnalyticsAdapter.initConfig(configOptions) + expect(malltvAnalyticsAdapter.getAnalyticsOptions().server).to.equal(DEFAULT_SERVER) + }) + }) +}) From 284dd71590eb47fa99f9017fc273dfb0b943fd33 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 17 Aug 2021 07:05:52 -0700 Subject: [PATCH 1382/1476] Dependencies: Fix dependabot dependency path parse (#7285) --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95a53b37173..50296a6ae96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16202,7 +16202,7 @@ "dev": true }, "path-parse": { - "version": "1.0.7", + "version": ">=1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true @@ -17385,7 +17385,7 @@ "dev": true, "requires": { "is-core-module": "2.4.0", - "path-parse": "1.0.7" + "path-parse": ">=1.0.7" } }, "resolve-alpn": { From 2c2cd544c85a8127ae41478987132267aede6bcc Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 17 Aug 2021 17:40:47 +0200 Subject: [PATCH 1383/1476] Add `auctionId`, `PlayerName` to PBS params (#7312) --- modules/adagioBidAdapter.js | 22 +++++++++++++++++----- test/spec/modules/adagioBidAdapter_spec.js | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index e8893b89282..ffa955290be 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -340,6 +340,16 @@ function isNewSession(adagioStorage) { ) } +function setPlayerName(bidRequest) { + const playerName = (internal.isRendererPreferredFromPublisher(bidRequest)) ? 'other' : 'adagio'; + + if (playerName === 'other') { + utils.logWarn(`${LOG_PREFIX} renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.`); + } + + return playerName; +} + export const internal = { enqueue, getPageviewId, @@ -414,11 +424,7 @@ function _buildVideoBidRequest(bidRequest) { }; if (videoParams.context && videoParams.context === OUTSTREAM) { - bidRequest.mediaTypes.video.playerName = (internal.isRendererPreferredFromPublisher(bidRequest)) ? 'other' : 'adagio'; - - if (bidRequest.mediaTypes.video.playerName === 'other') { - utils.logWarn(`${LOG_PREFIX} renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.`); - } + bidRequest.mediaTypes.video.playerName = setPlayerName(bidRequest); } // Only whitelisted OpenRTB options need to be validated. @@ -1071,6 +1077,8 @@ export const spec = { if (isOrtb) { autoFillParams(adagioBid); + adagioBid.params.auctionId = utils.deepAccess(adagioBidderRequest, 'auctionId'); + const globalFeatures = GlobalExchange.getOrSetGlobalFeatures(); adagioBid.params.features = { ...globalFeatures, @@ -1082,6 +1090,10 @@ export const spec = { adagioBid.params.prebidVersion = '$prebid.version$'; adagioBid.params.data = GlobalExchange.getExchangeData(); + if (utils.deepAccess(adagioBid, 'mediaTypes.video.context') === OUTSTREAM) { + adagioBid.params.playerName = setPlayerName(adagioBid); + } + storeRequestInAdagioNS(adagioBid); } diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 7c8e6ec2362..ed3d26c1f4d 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1151,14 +1151,24 @@ describe('Adagio bid adapter', () => { }; const bid01 = new BidRequestBuilder({ 'mediaTypes': { - banner: { sizes: [[300, 250]] } + banner: { sizes: [[300, 250]] }, + video: { + context: 'outstream', + playerSize: [300, 250], + renderer: { + url: 'https://url.tld', + render: () => true + } + } } }).withParams().build(); - const params = spec.transformBidParams({ organizationId: '1000' }, true, adUnit, [{ bidderCode: 'adagio', bids: [bid01] }]); + const params = spec.transformBidParams({ organizationId: '1000' }, true, adUnit, [{ bidderCode: 'adagio', auctionId: bid01.auctionId, bids: [bid01] }]); expect(params.organizationId).to.exist; - expect(params.organizationId).to.exist; + expect(params.auctionId).to.exist; + expect(params.playerName).to.exist; + expect(params.playerName).to.equal('other'); expect(params.features).to.exist; expect(params.features.page_dimensions).to.exist; expect(params.features.adunit_position).to.exist; From c5440334e438397d6e61d8cee901f561f4cc0cc4 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 17 Aug 2021 08:41:17 -0700 Subject: [PATCH 1384/1476] Kinesso ID: add UserId module and fixes conflicts on pr 7077 (#7283) * Kinesso User id module * Kinesso User id module * fix conflicts * fix test * fix error to kinessoId Co-authored-by: skocheri --- modules/kinessoIdSystem.js | 241 +++++++++++++++++++++++++++++++ modules/userId/eids.js | 4 + modules/userId/eids.md | 7 + modules/userId/userId.md | 27 +++- test/spec/modules/eids_spec.js | 14 ++ test/spec/modules/userId_spec.js | 154 ++++++++++++++------ 6 files changed, 400 insertions(+), 47 deletions(-) create mode 100644 modules/kinessoIdSystem.js diff --git a/modules/kinessoIdSystem.js b/modules/kinessoIdSystem.js new file mode 100644 index 00000000000..2cd7f594dd2 --- /dev/null +++ b/modules/kinessoIdSystem.js @@ -0,0 +1,241 @@ +/** + * This module adds KinessoId ID support to the User ID module + * The {@link module:modules/userId} module is required. + * @module modules/KinessoIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; +import {coppaDataHandler, uspDataHandler} from '../src/adapterManager.js'; + +const MODULE_NAME = 'kpuid'; +const ID_SVC = 'https://id.knsso.com/id'; +// These values should NEVER change. If +// they do, we're no longer making ulids! +const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32 +const ENCODING_LEN = ENCODING.length; +const TIME_MAX = Math.pow(2, 48) - 1; +const TIME_LEN = 10; +const RANDOM_LEN = 16; +const id = factory(); + +/** + * the factory to generate unique identifier based on time and current pseudorandom number + * @param {string} the current pseudorandom number generator + * @returns {function(*=): *} + */ +function factory(currPrng) { + if (!currPrng) { + currPrng = detectPrng(); + } + return function ulid(seedTime) { + if (isNaN(seedTime)) { + seedTime = Date.now(); + } + return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng); + }; +} + +/** + * gets a a random charcter from generated pseudorandom number + * @param {string} the generated pseudorandom number + * @returns {string} + */ +function randomChar(prng) { + let rand = Math.floor(prng() * ENCODING_LEN); + if (rand === ENCODING_LEN) { + rand = ENCODING_LEN - 1; + } + return ENCODING.charAt(rand); +} + +/** + * encodes random character + * @param len + * @param prng + * @returns {string} + */ +function encodeRandom(len, prng) { + let str = ''; + for (; len > 0; len--) { + str = randomChar(prng) + str; + } + return str; +} + +/** + * encodes the time based on the length + * @param now + * @param len + * @returns {string} encoded time. + */ +function encodeTime(now, len) { + if (isNaN(now)) { + throw new Error(now + ' must be a number'); + } + + if (Number.isInteger(now) === false) { + throw createError('time must be an integer'); + } + + if (now > TIME_MAX) { + throw createError('cannot encode time greater than ' + TIME_MAX); + } + if (now < 0) { + throw createError('time must be positive'); + } + + if (Number.isInteger(len) === false) { + throw createError('length must be an integer'); + } + if (len < 0) { + throw createError('length must be positive'); + } + + let mod; + let str = ''; + for (; len > 0; len--) { + mod = now % ENCODING_LEN; + str = ENCODING.charAt(mod) + str; + now = (now - mod) / ENCODING_LEN; + } + return str; +} + +/** + * creates and logs the error message + * @function + * @param {string} error message + * @returns {Error} + */ +function createError(message) { + utils.logError(message); + const err = new Error(message); + err.source = 'kinessoId'; + return err; +} + +/** + * detects the pseudorandom number generator and generates the random number + * @function + * @param {string} error message + * @returns {string} a random number + */ +function detectPrng(root) { + if (!root) { + root = typeof window !== 'undefined' ? window : null; + } + const browserCrypto = root && (root.crypto || root.msCrypto); + if (browserCrypto) { + return () => { + const buffer = new Uint8Array(1); + browserCrypto.getRandomValues(buffer); + return buffer[0] / 0xff; + }; + } + return () => Math.random(); +} + +/** + * existing id generation call back + * @param result + * @param callback + * @returns {{success: success, error: error}} + */ +function syncId(storedId) { + return { + success: function (responseBody) { + utils.logInfo('KinessoId: id to be synced: ' + storedId); + }, + error: function () { + utils.logInfo('KinessoId: Sync error for id : ' + storedId); + } + } +} + +/** + * Encode the id + * @param value + * @returns {string|*} + */ +function encodeId(value) { + const result = {}; + const knssoId = (value && typeof value === 'string') ? value : undefined; + if (knssoId) { + result.kpuid = knssoId; + utils.logInfo('KinessoId: Decoded value ' + JSON.stringify(result)); + return result; + } + return knssoId; +} + +/** + * Builds and returns the shared Id URL with attached consent data if applicable + * @param {Object} consentData + * @return {string} + */ +function kinessoSyncUrl(accountId, consentData) { + const usPrivacyString = uspDataHandler.getConsentData(); + let kinessoSyncUrl = `${ID_SVC}?accountid=${accountId}`; + if (usPrivacyString) { + kinessoSyncUrl = `${kinessoSyncUrl}?us_privacy=${usPrivacyString}`; + } + if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return kinessoSyncUrl; + + kinessoSyncUrl = `${kinessoSyncUrl}&gdpr=1&gdpr_consent=${consentData.consentString}`; + return kinessoSyncUrl +} + +/** @type {Submodule} */ +export const kinessoIdSubmodule = { + + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{kpuid:{ id: string}} or undefined if value doesn't exists + */ + decode(value) { + return (value) ? encodeId(value) : undefined; + }, + + /** + * performs action to obtain id and return a value. + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData|undefined} consentData + * @returns {knssoId} + */ + getId(config, consentData) { + const configParams = (config && config.params) || {}; + if (!configParams || typeof configParams.accountid !== 'number') { + utils.logError('User ID - KinessoId submodule requires a valid accountid to be defined'); + return; + } + const coppa = coppaDataHandler.getCoppa(); + if (coppa) { + utils.logInfo('KinessoId: IDs not provided for coppa requests, exiting KinessoId'); + return; + } + const accountId = configParams.accountid; + const knnsoId = id(); + utils.logInfo('KinessoId: generated id ' + knnsoId); + const kinessoIdPayload = {}; + kinessoIdPayload.id = knnsoId; + const payloadString = JSON.stringify(kinessoIdPayload); + ajax(kinessoSyncUrl(accountId, consentData), syncId(knnsoId), payloadString, {method: 'POST', withCredentials: true}); + return {'id': knnsoId}; + } + +}; + +// Register submodule for userId +submodule('userId', kinessoIdSubmodule); diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 813dd2cf427..71267616662 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -222,6 +222,10 @@ const USER_IDS_CONFIG = { source: 'amxrtb.com', atype: 1, }, + 'kpuid': { + source: 'kpuid.com', + atype: 3 + }, 'imuid': { source: 'intimatemerger.com', atype: 1 diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 279a5a0cb49..ab454c54d30 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -195,6 +195,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 3 }] + }, + { + source: 'kpuid.com', + uids: [{ + id: 'some-random-id-value', + atype: 3 + }] } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index bbbe995983a..11bf74e5d87 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -130,12 +130,16 @@ pbjs.setConfig({ token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" } },{ - name: 'quantcastId', - storage: { - type: 'cookie', - expires : 30 - } - }], + name: "kpuid", + params:{ + accountid: 124 // example of account id + }, + storage: { + type: "cookie", + name: "knssoId", + expires: 30 + }, + ], syncDelay: 5000, auctionDelay: 1000 } @@ -264,6 +268,17 @@ pbjs.setConfig({ name: "_dpes_id", expires: 90 } + },{ + name: "kpuid", + params:{ + accountid: 124 // example of account id + }, + storage: { + type: "html5", + name: "knssoId", + expires: 30 + }, + } }, { name: 'imuid', diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index d22225d191e..1277486a154 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -283,6 +283,20 @@ describe('eids array generation for known sub-modules', function() { }] }); }); + it('kpuid', function() { + const userId = { + kpuid: 'Sample_Token' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'kpuid.com', + uids: [{ + id: 'Sample_Token', + atype: 3 + }] + }); + }); it('pubProvidedId', function() { const userId = { pubProvidedId: [{ diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 9b36de6a94b..0190bceca70 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -46,8 +46,9 @@ import {uid2IdSubmodule} from 'modules/uid2IdSystem.js'; import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {flocIdSubmodule} from 'modules/flocIdSystem.js' -import { amxIdSubmodule } from '../../../modules/amxIdSystem.js'; +import {amxIdSubmodule} from '../../../modules/amxIdSystem.js'; import {akamaiDAPIdSubmodule} from 'modules/akamaiDAPIdSystem.js' +import {kinessoIdSubmodule} from 'modules/kinessoIdSystem.js' let assert = require('chai').assert; let expect = require('chai').expect; @@ -507,7 +508,7 @@ describe('User ID', function () { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -515,14 +516,14 @@ describe('User ID', function () { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({userSync: {}}); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -533,7 +534,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -550,7 +551,7 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); @@ -571,8 +572,8 @@ describe('User ID', function () { expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 22 configurations should result in 22 submodules add', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + it('config with 23 configurations should result in 23 submodules add', function () { + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -634,14 +635,17 @@ describe('User ID', function () { }, { name: 'amxId', storage: {name: 'amxId', type: 'html5'} + }, { + name: 'kpuid', + storage: {name: 'kpuid', type: 'cookie'} }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 22 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 23 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ @@ -657,7 +661,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -672,7 +676,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, flocIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ userSync: { @@ -926,7 +930,7 @@ describe('User ID', function () { }); it('test hook from pubcommonid html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('pubcid', 'testpubcid'); localStorage.setItem('pubcid_exp', new Date(Date.now() + 100000).toUTCString()); @@ -969,7 +973,7 @@ describe('User ID', function () { }); it('test hook from UnifiedId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); @@ -1026,7 +1030,7 @@ describe('User ID', function () { }); it('test hook from identityLink html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('idl_env', 'AiGNC8Z5ONyZKSpIPf'); localStorage.setItem('idl_env_exp', ''); @@ -1120,7 +1124,7 @@ describe('User ID', function () { }); it('test hook from liveIntentId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier'})); localStorage.setItem('_li_pbid_exp', ''); @@ -1144,6 +1148,56 @@ describe('User ID', function () { }, {adUnits}); }); + it('test hook from Kinesso cookies', function (done) { + // simulate existing browser local storage values + coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([kinessoIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['kpuid', 'kpuid', 'cookie'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.kpuid'); + expect(bid.userId.kpuid).to.equal('KINESSO_ID'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'kpuid.com', + uids: [{id: 'KINESSO_ID', atype: 3}] + }); + }); + }); + coreStorage.setCookie('kpuid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook from Kinesso html5', function (done) { + // simulate existing browser local storage values + localStorage.setItem('kpuid', 'KINESSO_ID'); + localStorage.setItem('kpuid_exp', ''); + + setSubmoduleRegistry([kinessoIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['kpuid', 'kpuid', 'html5'])); + + requestBidsHook(function () { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.kpuid'); + expect(bid.userId.kpuid).to.equal('KINESSO_ID'); + expect(bid.userIdAsEids[0]).to.deep.equal({ + source: 'kpuid.com', + uids: [{id: 'KINESSO_ID', atype: 3}] + }); + }); + }); + localStorage.removeItem('kpuid'); + localStorage.removeItem('kpuid_exp', ''); + done(); + }, {adUnits}); + }); + it('test hook from liveIntentId cookie', function (done) { coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); @@ -1380,7 +1434,7 @@ describe('User ID', function () { }); it('test hook from liveIntentId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('_li_pbid', JSON.stringify({'unifiedId': 'random-ls-identifier', 'segments': ['123']})); localStorage.setItem('_li_pbid_exp', ''); @@ -1435,7 +1489,7 @@ describe('User ID', function () { }); it('test hook from britepoolid cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([britepoolIdSubmodule]); @@ -1459,7 +1513,7 @@ describe('User ID', function () { }); it('test hook from dmdId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('dmdId', 'testdmdId', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([dmdIdSubmodule]); @@ -1483,7 +1537,7 @@ describe('User ID', function () { }); it('test hook from netId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([netIdSubmodule]); @@ -1507,7 +1561,7 @@ describe('User ID', function () { }); it('test hook from intentIqId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('intentIqId', 'abcdefghijk', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([intentIqIdSubmodule]); @@ -1531,7 +1585,7 @@ describe('User ID', function () { }); it('test hook from haloId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('haloId', JSON.stringify({'haloId': 'random-ls-identifier'})); localStorage.setItem('haloId_exp', ''); @@ -1557,7 +1611,7 @@ describe('User ID', function () { }); it('test hook from merkleId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('merkleId', JSON.stringify({'pam_id': {'id': 'testmerkleId', 'keyID': 1}}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([merkleIdSubmodule]); @@ -1581,7 +1635,7 @@ describe('User ID', function () { }); it('test hook from zeotapIdPlus cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('IDP', btoa(JSON.stringify('abcdefghijk')), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([zeotapIdPlusSubmodule]); @@ -1605,7 +1659,7 @@ describe('User ID', function () { }); it('test hook from mwOpenLinkId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('mwol', JSON.stringify({eid: 'XX-YY-ZZ-123'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([mwOpenLinkIdSubModule]); @@ -1625,7 +1679,7 @@ describe('User ID', function () { }); it('test hook from admixerId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('admixerId', 'testadmixerId'); localStorage.setItem('admixerId_exp', ''); @@ -1672,7 +1726,7 @@ describe('User ID', function () { }); it('test hook from deepintentId cookies', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([deepintentDpesSubmodule]); @@ -1696,7 +1750,7 @@ describe('User ID', function () { }); it('test hook from deepintentId html5', function (done) { - // simulate existing browser local storage values + // simulate existing browser local storage values localStorage.setItem('deepintentId', 'testdeepintentId'); localStorage.setItem('deepintentId_exp', ''); @@ -1718,7 +1772,7 @@ describe('User ID', function () { }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, amxId, dmdId and mwOpenLinkId have data to pass', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId, intentIqId, zeotapIdPlus, netId, haloId, Criteo, UID 2.0, admixerId, amxId, dmdId, kpuid and mwOpenLinkId have data to pass', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1734,12 +1788,13 @@ describe('User ID', function () { coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); // amxId only supports localStorage localStorage.setItem('amxId', 'test_amxid_id'); localStorage.setItem('amxId_exp', (new Date(Date.now() + 5000)).toUTCString()); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1757,12 +1812,13 @@ describe('User ID', function () { ['uid2', 'uid2id', 'cookie'], ['admixerId', 'admixerId', 'cookie'], ['amxId', 'amxId', 'html5'], - ['deepintentId', 'deepintentId', 'cookie'])); + ['deepintentId', 'deepintentId', 'cookie'], + ['kpuid', 'kpuid', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid + // verify that the PubCommonId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.pubcid'); expect(bid.userId.pubcid).to.equal('testpubcid'); // also check that UnifiedId id data was copied to bid @@ -1812,7 +1868,10 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.deepintentId'); expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(bid.userIdAsEids.length).to.equal(16); + expect(bid).to.have.deep.nested.property('userId.kpuid'); + expect(bid.userId.kpuid).to.equal('KINESSO_ID'); + + expect(bid.userIdAsEids.length).to.equal(17); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1830,13 +1889,14 @@ describe('User ID', function () { coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('deepintentId', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('kpuid', EXPIRED_COOKIE_DATE); localStorage.removeItem('amxId'); localStorage.removeItem('amxId_exp'); done(); }, {adUnits}); }); - it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, dmdId, intentIqId, zeotapIdPlus, criteo, netId, haloId, UID 2.0, admixerId and mwOpenLinkId have their modules added before and after init', function (done) { + it('test hook when pubCommonId, unifiedId, id5Id, britepoolId, dmdId, intentIqId, zeotapIdPlus, criteo, netId, haloId, UID 2.0, admixerId, kpuid and mwOpenLinkId have their modules added before and after init', function (done) { coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); coreStorage.setCookie('id5id', JSON.stringify({'universal_uid': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); @@ -1852,6 +1912,7 @@ describe('User ID', function () { coreStorage.setCookie('uid2id', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('admixerId', 'testadmixerId', (new Date(Date.now() + 5000).toUTCString())); coreStorage.setCookie('deepintentId', 'testdeepintentId', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1876,6 +1937,7 @@ describe('User ID', function () { attachIdSystem(uid2IdSubmodule); attachIdSystem(admixerIdSubmodule); attachIdSystem(deepintentDpesSubmodule); + attachIdSystem(kinessoIdSubmodule); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'], @@ -1892,12 +1954,13 @@ describe('User ID', function () { ['tapadId', 'tapad_id', 'cookie'], ['uid2', 'uid2id', 'cookie'], ['admixerId', 'admixerId', 'cookie'], - ['deepintentId', 'deepintentId', 'cookie'])); + ['deepintentId', 'deepintentId', 'cookie'], + ['kpuid', 'kpuid', 'cookie'])); requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid + // verify that the PubCommonId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.pubcid'); expect(bid.userId.pubcid).to.equal('testpubcid'); // also check that UnifiedId id data was copied to bid @@ -1946,8 +2009,10 @@ describe('User ID', function () { // also check that deepintentId was copied to bid expect(bid).to.have.deep.nested.property('userId.deepintentId'); expect(bid.userId.deepintentId).to.equal('testdeepintentId'); + expect(bid).to.have.deep.nested.property('userId.kpuid'); + expect(bid.userId.kpuid).to.equal('KINESSO_ID'); - expect(bid.userIdAsEids.length).to.equal(15); + expect(bid.userIdAsEids.length).to.equal(16); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1965,6 +2030,7 @@ describe('User ID', function () { coreStorage.setCookie('uid2id', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('admixerId', '', EXPIRED_COOKIE_DATE); coreStorage.setCookie('deepintentId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('kpuid', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -2014,8 +2080,9 @@ describe('User ID', function () { coreStorage.setCookie('__uid2_advertising_token', 'Sample_AD_Token', (new Date(Date.now() + 5000).toUTCString())); localStorage.setItem('amxId', 'test_amxid_id'); localStorage.setItem('amxId_exp', new Date(Date.now() + 5000).toUTCString()) + coreStorage.setCookie('kpuid', 'KINESSO_ID', (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule]); + setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, akamaiDAPIdSubmodule, amxIdSubmodule, kinessoIdSubmodule]); init(config); config.setConfig({ @@ -2051,6 +2118,8 @@ describe('User ID', function () { name: 'deepintentId', storage: {name: 'deepintentId', type: 'cookie'} }, { name: 'amxId', storage: {name: 'amxId', type: 'html5'} + }, { + name: 'kpuid', storage: {name: 'kpuid', type: 'cookie'} }] } }); @@ -2072,7 +2141,7 @@ describe('User ID', function () { requestBidsHook(function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { - // check PubCommonId id data was copied to bid + // check PubCommonId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.pubcid'); expect(bid.userId.pubcid).to.equal('testpubcid'); // check UnifiedId id data was copied to bid @@ -2120,7 +2189,9 @@ describe('User ID', function () { expect(bid).to.have.deep.nested.property('userId.deepintentId'); expect(bid.userId.deepintentId).to.equal('testdeepintentId'); - expect(bid.userIdAsEids.length).to.equal(14); + expect(bid).to.have.deep.nested.property('userId.kpuid'); + expect(bid.userId.kpuid).to.equal('KINESSO_ID'); + expect(bid.userIdAsEids.length).to.equal(15); }); }); coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -2139,6 +2210,7 @@ describe('User ID', function () { coreStorage.setCookie('mwol', '', EXPIRED_COOKIE_DATE); localStorage.removeItem('amxId'); localStorage.removeItem('amxId_exp'); + coreStorage.setCookie('kpuid', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -2296,7 +2368,7 @@ describe('User ID', function () { }; const sharedBeforeFunction = function () { - // clear cookies + // clear cookies expStr = (new Date(Date.now() + 25000).toUTCString()); coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); coreStorage.setCookie(`${mockIdCookieName}_last`, '', EXPIRED_COOKIE_DATE); From bce339649c91541b751aaf74492e0b72c02982ce Mon Sep 17 00:00:00 2001 From: evanmsmrtb Date: Tue, 17 Aug 2021 11:42:26 -0500 Subject: [PATCH 1385/1476] Resetdigital Bid Adapter: update default endpoint domain (#7230) * Add alias, update valid opts * Update bidder tests * Initial ResetDigital adapter * Update resetdigital checks * Use prebid utils for domain info * Address lint and adomain items * Update default endpoint --- modules/resetdigitalBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/resetdigitalBidAdapter.js b/modules/resetdigitalBidAdapter.js index adfd8a9a9af..7591c3391a0 100644 --- a/modules/resetdigitalBidAdapter.js +++ b/modules/resetdigitalBidAdapter.js @@ -56,7 +56,7 @@ export const spec = { } let params = validBidRequests[0].params - let url = params.endpoint ? params.endpoint : '//hb.vhsrv.com' + let url = params.endpoint ? params.endpoint : '//ads.resetsrv.com' return { method: 'POST', url: url, From a4207df7be087fa3f5281f2b5234ac5e2ad3efd5 Mon Sep 17 00:00:00 2001 From: Etarget <40423120+etargetse@users.noreply.github.com> Date: Tue, 17 Aug 2021 19:01:13 +0200 Subject: [PATCH 1386/1476] etarget Bid Adapter: update support for using priceFloor module (#7305) * new feature getMetaData * metaData unit test * advertiserDomains * advertiserDomains * added ortb2 * getMetaData feature moved into bidderRequest object * getMetaData featured moved into bidderRequest object * getMetaData feature moved into bidderRequest object * implemented priceFloor * priceFloor test values * added priceFloor * deepClone import * priceFloor test * priceFloor test * floorPrice test * priceFloor test * priceFloor test * priceFloor test * priceFloor test * priceFloor test * priceFloor update --- modules/etargetBidAdapter.js | 20 +++++++++++++++++++- test/spec/modules/etargetBidAdapter_spec.js | 11 +++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index a7e79b2fd9b..51f2759d256 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -29,6 +29,7 @@ export const spec = { var request = []; var bids = JSON.parse(JSON.stringify(validBidRequests)); var lastCountry = 'sk'; + var floors = []; for (i = 0, l = bids.length; i < l; i++) { bid = bids[i]; if (countryMap[bid.params.country]) { @@ -37,6 +38,7 @@ export const spec = { reqParams = bid.params; reqParams.transactionId = bid.transactionId; request.push(formRequestUrl(reqParams)); + floors[i] = getBidFloor(bid); } request.unshift('https://' + lastCountry + '.search.etargetnet.com/hb/?hbget=1'); @@ -52,6 +54,9 @@ export const spec = { request.push('gdpr_consent=' + gdprObject.gdpr_consent); } bidderRequest.metaData = getMetaData(); + if (floors.length > 0) { + bidderRequest.floors = floors; + } } return { @@ -139,7 +144,6 @@ export const spec = { bidObject.gdpr = bidRequest.gdpr.gdpr; bidObject.gdpr_consent = bidRequest.gdpr.gdpr_consent; } - if (bid.adomain) { utils.deepSetValue(bidObject, 'meta.advertiserDomains', Array.isArray(bid.adomain) ? bid.adomain : [bid.adomain]); } @@ -160,4 +164,18 @@ export const spec = { } } }; +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return null; + } + let floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor)) { + return floor.floor; + } + return null; +} registerBidder(spec); diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index e2310aee1fb..55394dcdeea 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -1,6 +1,7 @@ import {assert, expect} from 'chai'; import {spec} from 'modules/etargetBidAdapter.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { deepClone } from 'src/utils.js'; describe('etarget adapter', function () { let serverResponse, bidRequest, bidResponses; @@ -41,6 +42,16 @@ describe('etarget adapter', function () { assert.equal(request.method, 'POST'); }); + it('should attach floor param when either bid param or getFloor function exists', function () { + // let getFloorResponse = { currency: 'EUR', floor: 5 }; + let request = null; + let bidRequest = deepClone(bids[0]); + + // floor param has to be NULL + request = spec.buildRequests([bidRequest]); + assert.equal(typeof request.floors, 'undefined'); + }); + it('should correctly form bid items', function () { let bidList = bids; let request = spec.buildRequests(bidList); From 718e1641a931eff4c204b156ca48340f1a9af28a Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 17 Aug 2021 23:22:39 +0600 Subject: [PATCH 1387/1476] ZetaSspBidAdapter: provide gdpr and sspa values in bidRequest (#7311) Co-authored-by: Surovenko Alexey --- modules/zetaSspBidAdapter.js | 23 ++++++++++----------- test/spec/modules/zetaSspBidAdapter_spec.js | 10 +++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 76ceea0fdd1..4d5966e03e9 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -73,19 +73,18 @@ export const spec = { if (params.test) { payload.test = params.test; } - if (request.gdprConsent) { - payload.regs = { - ext: { - gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0 - } - }; - if (request.gdprConsent.gdprApplies && request.gdprConsent.consentString) { - payload.user.ext = { - ...payload.user.ext, - consent: request.gdprConsent.consentString - } - } + + // Attaching GDPR Consent Params + if (bidderRequest && bidderRequest.gdprConsent) { + utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(payload, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // CCPA + if (bidderRequest && bidderRequest.uspConsent) { + utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + provideEids(request, payload); return { method: 'POST', diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 9f25a489dab..00421f0ac20 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -39,6 +39,7 @@ describe('Zeta Ssp Bid Adapter', function () { gdprApplies: 1, consentString: 'consentString' }, + uspConsent: 'someCCPAString', params: { placement: 111, user: { @@ -169,6 +170,15 @@ describe('Zeta Ssp Bid Adapter', function () { expect(sync4.url).to.include('&us_privacy='); }); + it('Test provide gdpr and ccpa values in payload', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + + expect(payload.user.ext.consent).to.eql('consentString'); + expect(payload.regs.ext.gdpr).to.eql(1); + expect(payload.regs.ext.us_privacy).to.eql('someCCPAString'); + }); + it('Test do not override user object', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); From d43427d7997e8ca2d10483da2f6afaf8538ff901 Mon Sep 17 00:00:00 2001 From: Lisa Benmore Date: Tue, 17 Aug 2021 11:16:41 -0700 Subject: [PATCH 1388/1476] Gumgum: fix how we send TTD global placement ID (#7310) * Gumgum: ADJS-1064 Fix how we send TTD global placement ID * updated unit tests * undo unnecessary changes --- modules/gumgumBidAdapter.js | 3 +-- test/spec/modules/gumgumBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 9b4ec41049c..f00254fd4be 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -346,8 +346,7 @@ function buildRequests (validBidRequests, bidderRequest) { sizes, url: BID_ENDPOINT, method: 'GET', - gpid: gpid, - data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId)) + data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId), { gpid }) }) }); return bids; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 75a9c5c975a..8016d3fb678 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -192,8 +192,8 @@ describe('gumgumAdapter', function () { it('should set the global placement id (gpid)', function () { const req = { ...bidRequests[0], ortb2Imp: { ext: { data: { adserver: { name: 'test', adslot: 123456 } } } } } const bidRequest = spec.buildRequests([req])[0]; - expect(bidRequest).to.have.property('gpid'); - expect(bidRequest.gpid).to.equal(123456); + expect(bidRequest.data).to.have.property('gpid'); + expect(bidRequest.data.gpid).to.equal(123456); }); it('should set the bid floor if getFloor module is not present but static bid floor is defined', function () { From da239c6afcfe5eed4990e328c4d9ed09b40d9e8c Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Tue, 17 Aug 2021 19:23:59 +0100 Subject: [PATCH 1389/1476] Adloox (video) adserver module (#6309) --- integrationExamples/gpt/adloox.html | 8 +- modules/adlooxAdServerVideo.js | 223 ++++++++++++ modules/adlooxAdServerVideo.md | 92 +++++ modules/adlooxAnalyticsAdapter.js | 5 + modules/adlooxAnalyticsAdapter.md | 20 +- test/spec/modules/adlooxAdServerVideo_spec.js | 339 ++++++++++++++++++ .../modules/adlooxAnalyticsAdapter_spec.js | 24 ++ 7 files changed, 703 insertions(+), 8 deletions(-) create mode 100644 modules/adlooxAdServerVideo.js create mode 100644 modules/adlooxAdServerVideo.md create mode 100644 test/spec/modules/adlooxAdServerVideo_spec.js diff --git a/integrationExamples/gpt/adloox.html b/integrationExamples/gpt/adloox.html index 33f8b9be6a2..2a772bb7ce2 100644 --- a/integrationExamples/gpt/adloox.html +++ b/integrationExamples/gpt/adloox.html @@ -112,6 +112,7 @@ var videoBids = bids[videoAdUnit.code]; if (videoBids) { +// DEMO NOTES: your environment likely will use the commented section //// var videoUrl = videoBids.bids[0].vastUrl; // var videoUrl = pbjs.adServers.dfp.buildVideoUrl({ // adUnit: videoAdUnit, @@ -124,7 +125,12 @@ // output: 'vast' // } // }); - invokeVideoPlayer(videoUrl); +////////////////////////////////////////////////////////////////////////// + var ret = pbjs.adServers.adloox.buildVideoUrl({ + adUnit: videoAdUnit, + url: videoUrl + }, invokeVideoPlayer); + if (!ret) console.log('Error building Adloox video URL'); } } diff --git a/modules/adlooxAdServerVideo.js b/modules/adlooxAdServerVideo.js new file mode 100644 index 00000000000..2a61e59b714 --- /dev/null +++ b/modules/adlooxAdServerVideo.js @@ -0,0 +1,223 @@ +/** + * This module adds [Adloox]{@link https://www.adloox.com/} Ad Server support for Video to Prebid + * @module modules/adlooxAdServerVideo + * @requires module:modules/adlooxAnalyticsAdapter + */ + +/* eslint prebid/validate-imports: "off" */ + +import { registerVideoSupport } from '../src/adServerManager.js'; +import { command as analyticsCommand, COMMAND } from './adlooxAnalyticsAdapter.js'; +import { ajax } from '../src/ajax.js'; +import { EVENTS } from '../src/constants.json'; +import { targeting } from '../src/targeting.js'; +import * as utils from '../src/utils.js'; + +const MODULE = 'adlooxAdserverVideo'; + +const URL_VAST = 'https://j.adlooxtracking.com/ads/vast/tag.php'; + +export function buildVideoUrl(options, callback) { + utils.logInfo(MODULE, 'buildVideoUrl', options); + + if (!utils.isFn(callback)) { + utils.logError(MODULE, 'invalid callback'); + return false; + } + if (!utils.isPlainObject(options)) { + utils.logError(MODULE, 'missing options'); + return false; + } + if (!(options.url_vast === undefined || utils.isStr(options.url_vast))) { + utils.logError(MODULE, 'invalid url_vast options value'); + return false; + } + if (!(utils.isPlainObject(options.adUnit) || utils.isPlainObject(options.bid))) { + utils.logError(MODULE, "requires either 'adUnit' or 'bid' options value"); + return false; + } + if (!utils.isStr(options.url)) { + utils.logError(MODULE, 'invalid url options value'); + return false; + } + if (!(options.wrap === undefined || utils.isBoolean(options.wrap))) { + utils.logError(MODULE, 'invalid wrap options value'); + return false; + } + if (!(options.blob === undefined || utils.isBoolean(options.blob))) { + utils.logError(MODULE, 'invalid blob options value'); + return false; + } + + // same logic used in modules/dfpAdServerVideo.js + options.bid = options.bid || targeting.getWinningBids(options.adUnit.code)[0]; + + utils.deepSetValue(options.bid, 'ext.adloox.video.adserver', true); + + if (options.wrap !== false) { + VASTWrapper(options, callback); + } else { + track(options, callback); + } + + return true; +} + +registerVideoSupport('adloox', { + buildVideoUrl: buildVideoUrl +}); + +function track(options, callback) { + callback(options.url); + + const bid = utils.deepClone(options.bid); + bid.ext.adloox.video.adserver = false; + + analyticsCommand(COMMAND.TRACK, { + eventType: EVENTS.BID_WON, + args: bid + }); +} + +function VASTWrapper(options, callback) { + const chain = []; + + function process(result) { + function getAd(xml) { + if (!xml || xml.documentElement.tagName != 'VAST') { + utils.logError(MODULE, 'not a VAST tag, using non-wrapped tracking'); + return; + } + + const ads = xml.querySelectorAll('Ad'); + if (!ads.length) { + utils.logError(MODULE, 'no VAST ads, using non-wrapped tracking'); + return; + } + + // get first Ad (VAST may be an Ad Pod so sort on sequence and pick lowest sequence number) + const ad = Array.prototype.slice.call(ads).sort(function(a, b) { + return parseInt(a.getAttribute('sequence'), 10) - parseInt(b.getAttribute('sequence'), 10); + }).shift(); + + return ad; + } + + function getWrapper(ad) { + return ad.querySelector('VASTAdTagURI'); + } + + function durationToSeconds(duration) { + return Date.parse('1970-01-01 ' + duration + 'Z') / 1000; + } + + function blobify() { + if (!(chain.length > 0 && options.blob !== false)) return; + + const urls = []; + + function toBlob(r) { + const text = new XMLSerializer().serializeToString(r.xml); + const url = URL.createObjectURL(new Blob([text], { type: r.type })); + urls.push(url); + return url; + } + + let n = chain.length - 1; // do not process the linear + while (n-- > 0) { + const ad = getAd(chain[n].xml); + const wrapper = getWrapper(ad); + wrapper.textContent = toBlob(chain[n + 1]); + } + + options.url = toBlob(chain[0]); + + const epoch = utils.timestamp() - new Date().getTimezoneOffset() * 60 * 1000; + const expires0 = options.bid.ttl * 1000 - (epoch - options.bid.responseTimestamp); + const expires = Math.max(30 * 1000, expires0); + setTimeout(function() { urls.forEach(u => URL.revokeObjectURL(u)) }, expires); + } + + if (!result) { + blobify(); + return track(options, callback); + } + + const ad = getAd(result.xml); + if (!ad) { + blobify(); + return track(options, callback); + } + + chain.push(result); + + const wrapper = getWrapper(ad); + if (wrapper) { + if (chain.length > 5) { + utils.logWarn(MODULE, `got wrapped tag at depth ${chain.length}, not continuing`); + blobify(); + return track(options, callback); + } + return fetch(wrapper.textContent.trim()); + } + + blobify(); + + const version = chain[0].xml.documentElement.getAttribute('version'); + + const vpaid = ad.querySelector("MediaFiles > MediaFile[apiFramework='VPAID'][type='application/javascript']"); + + const duration = durationToSeconds(ad.querySelector('Duration').textContent.trim()); + + let skip; + const skipd = ad.querySelector('Linear').getAttribute('skipoffset'); + if (skipd) skip = durationToSeconds(skipd.trim()); + + const args = [ + [ 'client', '%%client%%' ], + [ 'platform_id', '%%platformid%%' ], + [ 'scriptname', 'adl_%%clientid%%' ], + [ 'tag_id', '%%tagid%%' ], + [ 'fwtype', 4 ], + [ 'vast', options.url ], + [ 'id11', 'video' ], + [ 'id12', '$ADLOOX_WEBSITE' ], + [ 'id18', (!skip || skip >= duration) ? 'fd' : 'od' ], + [ 'id19', 'na' ], + [ 'id20', 'na' ] + ]; + if (version && version != 3) args.push([ 'version', version ]); + if (vpaid) args.push([ 'vpaid', 1 ]); + if (duration != 15) args.push([ 'duration', duration ]); + if (skip) args.push([ 'skip', skip ]); + + utils.logInfo(MODULE, `processed VAST tag chain of depth ${chain.depth}, running callback`); + + analyticsCommand(COMMAND.URL, { + url: (options.url_vast || URL_VAST) + '?', + args: args, + bid: options.bid, + ids: true + }, callback); + } + + function fetch(url, withoutcredentials) { + utils.logInfo(MODULE, `fetching VAST ${url}`); + + ajax(url, { + success: function(responseText, q) { + process({ type: q.getResponseHeader('content-type'), xml: q.responseXML }); + }, + error: function(statusText, q) { + if (!withoutcredentials) { + utils.logWarn(MODULE, `unable to download (${statusText}), suspected CORS withCredentials problem, retrying without`); + return fetch(url, true); + } + utils.logError(MODULE, `failed to fetch (${statusText}), using non-wrapped tracking`); + process(); + } + }, undefined, { withCredentials: !withoutcredentials }); + } + + fetch(options.url); +} diff --git a/modules/adlooxAdServerVideo.md b/modules/adlooxAdServerVideo.md new file mode 100644 index 00000000000..db8e3cfb295 --- /dev/null +++ b/modules/adlooxAdServerVideo.md @@ -0,0 +1,92 @@ +# Overview + + Module Name: Adloox Ad Server Video + Module Type: Ad Server Video + Maintainer: contact@adloox.com + +# Description + +Ad Server Video for adloox.com. Contact contact@adloox.com for information. + +This module pairs with the [Adloox Analytics Adapter](./adlooxAnalyticsAdapter.md) to provide video tracking and measurement. + +Prebid.js does not support [`bidWon` events for video](https://github.com/prebid/prebid.github.io/issues/1320) without the [Instream Video Ads Tracking](https://docs.prebid.org/dev-docs/modules/instreamTracking.html) module that has the limitations: + + * works only with instream video + * viewability metrics are *not* [MRC accredited](http://mediaratingcouncil.org/) + +This module has two modes of operation configurable with the `wrap` parameter below: + + * **`true` [default]:** + * provides MRC accredited viewability measurement of your [IAB](https://www.iab.com/) [VPAID](https://iabtechlab.com/standards/video-player-ad-interface-definition-vpaid/) and [OM SDK](https://iabtechlab.com/standards/open-measurement-sdk/) enabled inventory + * VAST tracking is collected by Adloox + * wraps the winning bid VAST URL with the Adloox VAST/VPAID wrapper (`https://j.adlooxtracking.com/ads/vast/tag.php?...`) + * **`false`:** + * sends a `bidWon` event *only* to the Adloox Analytics Adapter + * inventory is measured + * viewability metrics are *not* MRC accredited. + * VAST tracking is *not* collected by Adloox + +**N.B.** this module is compatible for use alongside the Instream Video Ads Tracking module though not required in order to function + +## Example + +To view an example of an Adloox integration look at the example provided in the [Adloox Analytics Adapter documentation](./adlooxAnalyticsAdapter.md#example). + +# Integration + +To use this, you *must* also integrate the [Adloox Analytics Adapter](./adlooxAnalyticsAdapter.md) (and optionally the Instream Video Ads Tracking module) as shown below: + + function sendAdserverRequest(bids, timedOut, auctionId) { + if (pbjs.initAdserverSet) return; + pbjs.initAdserverSet = true; + + // handle display tags as usual + googletag.cmd.push(function() { + pbjs.setTargetingForGPTAsync && pbjs.setTargetingForGPTAsync(adUnits); + googletag.pubads().refresh(); + }); + + // handle the bids on the video adUnit + var videoBids = bids[videoAdUnit.code]; + if (videoBids) { + var videoUrl = pbjs.adServers.dfp.buildVideoUrl({ + adUnit: videoAdUnit, + params: { + iu: '/19968336/prebid_cache_video_adunit', + cust_params: { + section: 'blog', + anotherKey: 'anotherValue' + }, + output: 'vast' + } + }); + pbjs.adServers.adloox.buildVideoUrl({ + adUnit: videoAdUnit, + url: videoUrl + }, invokeVideoPlayer); + } + } + +The helper function takes the form: + + pbjs.adServers.adloox.buildVideoUrl(options, callback) + +Where: + + * **`options`:** configuration object: + * **`adUnit`:** ad unit that is being filled + * **`bid` [optional]:** if you override the hardcoded `pbjs.adServers.dfp.buildVideoUrl(...)` logic that picks the first bid you *must* pass in the `bid` object you select + * **`url`:** VAST tag URL, typically the value returned by `pbjs.adServers.dfp.buildVideoUrl(...)` + * **`wrap`:** + * **`true` [default]:** VAST tag is be converted to an Adloox VAST wrapped tag + * **`false`:** VAST tag URL is returned as is + * **`blob`:** + * **`true` [default]:** [Blob URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) is returned so that the VAST tag is only fetched once + * only has an affect when `wrap` is set to `true` + * **`false`:** VAST tag may be fetched twice depending on your Ad Server and video player configuration + * use when the ad is served into a cross-origin (non-friendly) IFRAME + * use if during QAing you discover your video player does not supports Blob URLs; widely supported (including JW Player) so contact your player vendor to resolve this where possible for the best user and device experience + * **`callback`:** function you use to pass the VAST tag URL to your video player + +**N.B.** call `pbjs.adServers.adloox.buildVideoUrl(...)` as close as possible to starting the ad to reduce impression discrepancies diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index 3e92ae34004..cfd01b4434a 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -211,6 +211,11 @@ analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) { } analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) { + if (utils.deepAccess(bid, 'ext.adloox.video.adserver')) { + utils.logMessage(MODULE, `measuring '${bid.mediaType}' ad unit code '${bid.adUnitCode}' via Ad Server module`); + return; + } + const sl = analyticsAdapter.context.toselector(bid); let el; try { diff --git a/modules/adlooxAnalyticsAdapter.md b/modules/adlooxAnalyticsAdapter.md index 0ca67f937f6..d4d53a81b3c 100644 --- a/modules/adlooxAnalyticsAdapter.md +++ b/modules/adlooxAnalyticsAdapter.md @@ -18,17 +18,23 @@ The adapter adds an HTML ` + + + + + + + + + + +

IM RTD Prebid

+ +
+ +
+ +Intimate Merger Universal Identifier: +
+ +Intimate Merger Real-Time Data: +
+ + diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js new file mode 100644 index 00000000000..db2c51ccf51 --- /dev/null +++ b/modules/imRtdProvider.js @@ -0,0 +1,197 @@ +/** + * The {@link module:modules/realTimeData} module is required + * The module will fetch real-time data from Intimate Merger + * @module modules/imRtdProvider + * @requires module:modules/realTimeData + */ +import {ajax} from '../src/ajax.js'; +import {config} from '../src/config.js'; +import {getGlobal} from '../src/prebidGlobal.js' +import {getStorageManager} from '../src/storageManager.js'; +import { + deepSetValue, + deepAccess, + timestamp, + mergeDeep, + logError, + logInfo, + isFn +} from '../src/utils.js' +import {submodule} from '../src/hook.js'; + +export const imUidLocalName = '__im_uid'; +export const imVidCookieName = '_im_vid'; +export const imRtdLocalName = '__im_sids'; +export const storage = getStorageManager(); +const submoduleName = 'im'; +const segmentsMaxAge = 3600000; // 1 hour (30 * 60 * 1000) +const uidMaxAge = 1800000; // 30 minites (30 * 60 * 1000) +const vidMaxAge = 97200000000; // 37 months ((365 * 3 + 30) * 24 * 60 * 60 * 1000) + +function setImDataInCookie(value) { + storage.setCookie( + imVidCookieName, + value, + new Date(timestamp() + vidMaxAge).toUTCString(), + 'none' + ); +} + +export function getCustomBidderFunction(config, bidder) { + const overwriteFn = deepAccess(config, `params.overwrites.${bidder}`) + + if (overwriteFn && isFn(overwriteFn)) { + return overwriteFn + } else { + return null + } +} + +/** + * Add real-time data. + * @param {Object} bidConfig + * @param {Object} moduleConfig + * @param {Object} data + */ +export function setRealTimeData(bidConfig, moduleConfig, data) { + const adUnits = bidConfig.adUnits || getGlobal().adUnits; + const utils = {deepSetValue, deepAccess, logInfo, logError, mergeDeep}; + + if (data.im_segments) { + const ortb2 = config.getConfig('ortb2') || {}; + deepSetValue(ortb2, 'user.ext.data.im_segments', data.im_segments); + config.setConfig({ortb2: ortb2}); + + 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); + }); + } + } + + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + const overwriteFunction = getCustomBidderFunction(moduleConfig, bid.bidder); + if (overwriteFunction) { + overwriteFunction(bid, data, utils, config); + } + }) + }); +} + +/** + * Real-time data retrieval from Intimate Merger + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} moduleConfig + */ +export function getRealTimeData(reqBidsConfigObj, onDone, moduleConfig) { + const cid = deepAccess(moduleConfig, 'params.cid'); + if (!cid) { + logError('imRtdProvider requires a valid cid to be defined'); + onDone(); + return; + } + const sids = storage.getDataFromLocalStorage(imRtdLocalName); + const parsedSids = sids ? sids.split(',') : []; + const mt = storage.getDataFromLocalStorage(`${imRtdLocalName}_mt`); + const localVid = storage.getCookie(imVidCookieName); + let apiUrl = `https://sync6.im-apps.net/${cid}/rtd`; + let expired = true; + let alreadyDone = false; + + if (localVid) { + apiUrl += `?vid=${localVid}`; + setImDataInCookie(localVid); + } + + if (Date.parse(mt) && Date.now() - (new Date(mt)).getTime() < segmentsMaxAge) { + expired = false; + } + + if (sids !== null) { + setRealTimeData(reqBidsConfigObj, moduleConfig, {im_segments: parsedSids}); + onDone(); + alreadyDone = true; + } + + if (expired) { + ajax( + apiUrl, + getApiCallback(reqBidsConfigObj, alreadyDone ? undefined : onDone, moduleConfig), + undefined, + {method: 'GET', withCredentials: true} + ); + } +} + +/** + * Api callback from Intimate Merger + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} moduleConfig + */ +export function getApiCallback(reqBidsConfigObj, onDone, moduleConfig) { + return { + success: function (response, req) { + let parsedResponse = {}; + if (req.status === 200) { + try { + parsedResponse = JSON.parse(response); + } catch (e) { + logError('unable to get Intimate Merger segment data'); + } + + if (parsedResponse.uid) { + const imuid = storage.getDataFromLocalStorage(imUidLocalName); + const imuidMt = storage.getDataFromLocalStorage(`${imUidLocalName}_mt`); + const imuidExpired = Date.parse(imuidMt) && Date.now() - (new Date(imuidMt)).getTime() < uidMaxAge; + if (!imuid || imuidExpired) { + storage.setDataInLocalStorage(imUidLocalName, parsedResponse.uid); + storage.setDataInLocalStorage(`${imUidLocalName}_mt`, new Date(timestamp()).toUTCString()); + } + } + + if (parsedResponse.vid) { + setImDataInCookie(parsedResponse.vid); + } + + if (parsedResponse.segments) { + setRealTimeData(reqBidsConfigObj, moduleConfig, {im_segments: parsedResponse.segments}); + storage.setDataInLocalStorage(imRtdLocalName, parsedResponse.segments); + storage.setDataInLocalStorage(`${imRtdLocalName}_mt`, new Date(timestamp()).toUTCString()); + } + } + if (onDone) { + onDone(); + } + }, + error: function () { + if (onDone) { + onDone(); + } + logError('unable to get Intimate Merger segment data'); + } + } +} + +/** + * Module init + * @param {Object} provider + * @param {Object} userConsent + * @return {boolean} + */ +function init(provider, userConsent) { + return true; +} + +/** @type {RtdSubmodule} */ +export const imRtdSubmodule = { + name: submoduleName, + getBidRequestData: getRealTimeData, + init: init +}; + +submodule('realTimeData', imRtdSubmodule); diff --git a/modules/imRtdProvider.md b/modules/imRtdProvider.md new file mode 100644 index 00000000000..7ece2b996b4 --- /dev/null +++ b/modules/imRtdProvider.md @@ -0,0 +1,41 @@ +## Intimate Merger Real-time Data Submodule + +provided by Intimate Merger. + +## Building Prebid with Real-time Data Support + +First, make sure to add the Intimate Merger submodule to your Prebid.js package with: + +`gulp build --modules=rtdModule,imRtdProvider` + +The following configuration parameters are available: + +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 5000, + dataProviders: [ + { + name: "im", + waitForIt: true, + params: { + cid: 5126, // Set your Intimate Merger Customer ID here for production + setGptKeyValues: true + } + } + ] + } + ... +} +``` + +### Parameter Descriptions for the im Configuration Section + +| Param under dataProviders | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | The name of this module. | `"im"` | +| waitForIt | Optional | Boolean | Required to ensure that the auction is delayed until prefetch is complete. Defaults to false but recommended to true | `true` | +| 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` | diff --git a/test/spec/modules/imRtdProvider_spec.js b/test/spec/modules/imRtdProvider_spec.js new file mode 100644 index 00000000000..58410dc0e38 --- /dev/null +++ b/test/spec/modules/imRtdProvider_spec.js @@ -0,0 +1,151 @@ +import { + imRtdSubmodule, + storage, + getCustomBidderFunction, + setRealTimeData, + getRealTimeData, + getApiCallback, + imUidLocalName, + imVidCookieName, + imRtdLocalName +} from 'modules/imRtdProvider.js' +import { timestamp } from '../../../src/utils.js' + +describe('imRtdProvider', function () { + let getLocalStorageStub; + let getCookieStub; + + const testReqBidsConfigObj = { + adUnits: [ + { + bids: ['test1', 'test2'] + } + ] + }; + const onDone = function() { return true }; + const moduleConfig = { + params: { + cid: 5126, + setGptKeyValues: true + } + } + + beforeEach(function (done) { + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + getCookieStub = sinon.stub(storage, 'getCookie'); + done(); + }); + + afterEach(function () { + getLocalStorageStub.restore(); + getCookieStub.restore(); + }); + + describe('imRtdSubmodule', function () { + it('should initalise and return true', function () { + expect(imRtdSubmodule.init()).to.equal(true) + }) + }) + + describe('getCustomBidderFunction', function () { + it('should return config function', function () { + const config = { + params: { + overwrites: { + testBidder: function() { + return 'testString'; + } + } + } + }; + const bidder = 'testBidder' + expect(getCustomBidderFunction(config, bidder)).to.exist.and.to.be.a('function'); + expect(getCustomBidderFunction(config, bidder)()).to.equal('testString'); + }) + it('should return null when overwrites falsy', function () { + const config = { + params: { + overwrites: { + testBidder: null + } + } + }; + const bidder = 'testBidder' + expect(getCustomBidderFunction(config, bidder)).to.equal(null); + }) + }) + + describe('processBidderFunction', function () { + + }) + + describe('setRealTimeData', function () { + it('should return true when empty params', function () { + expect(setRealTimeData({adUnits: []}, {params: {}}, {im_segments: []})).to.equal(undefined) + }); + it('should return true when overwrites and bid params', function () { + const config = { + params: { + overwrites: { + testBidder: function() { return true } + } + } + }; + expect(setRealTimeData(testReqBidsConfigObj, config, {im_segments: []})).to.equal(undefined) + }); + }) + + describe('getRealTimeData', function () { + it('should initalise and return when empty params', function () { + expect(getRealTimeData({}, function() {}, {})).to.equal(undefined) + }); + + it('should initalise and return with config', function () { + expect(getRealTimeData(testReqBidsConfigObj, onDone, moduleConfig)).to.equal(undefined) + }); + + it('should return the uid when sids(rtd) not expired', function () { + getLocalStorageStub.withArgs(imUidLocalName).returns('testUid'); + getLocalStorageStub.withArgs(imRtdLocalName).returns('testSids'); + getCookieStub.withArgs(imVidCookieName).returns('testUid'); + getLocalStorageStub.withArgs(`${imRtdLocalName}_mt`).returns(new Date(timestamp()).toUTCString()); + expect(getRealTimeData(testReqBidsConfigObj, onDone, moduleConfig)).to.equal(undefined) + }); + + it('should return the uid when it exists uid, sids(rtd), vid in storages and sids(rtd) expired', function () { + getLocalStorageStub.withArgs(imUidLocalName).returns('testUid'); + getLocalStorageStub.withArgs(imRtdLocalName).returns('testSids'); + getCookieStub.withArgs(imVidCookieName).returns('testUid'); + getLocalStorageStub.withArgs(`${imRtdLocalName}_mt`).returns(0); + expect(getRealTimeData(testReqBidsConfigObj, onDone, moduleConfig)).to.equal(undefined) + }); + + it('should return the uid when uid not expired', function () { + getLocalStorageStub.withArgs(imUidLocalName).returns('testUid'); + getLocalStorageStub.withArgs(imRtdLocalName).returns('testSids'); + getCookieStub.withArgs(imVidCookieName).returns('testUid'); + getLocalStorageStub.withArgs(`${imUidLocalName}_mt`).returns(new Date(timestamp()).toUTCString()); + expect(getRealTimeData(testReqBidsConfigObj, onDone, moduleConfig)).to.equal(undefined) + }); + }) + + describe('getApiCallback', function () { + it('should return success and error functions', function () { + const res = getApiCallback(testReqBidsConfigObj, false, moduleConfig); + expect(res.success).to.exist.and.to.be.a('function'); + expect(res.error).to.exist.and.to.be.a('function'); + }); + + it('should return "undefined" success', function () { + const res = getApiCallback(testReqBidsConfigObj, false, moduleConfig); + const successResponse = '{"uid": "testid", "segments": "testsegment", "vid": "testvid"}'; + expect(res.success(successResponse, {status: 200})).to.equal(undefined); + expect(res.error()).to.equal(undefined); + }); + + it('should return "undefined" catch error response', function () { + const res = getApiCallback(testReqBidsConfigObj, false, moduleConfig); + expect(res.success('error response', {status: 400})).to.equal(undefined); + }); + }) +}) From 51caaef5c8a70c2c98e1c0a6163cea3b1175ca0f Mon Sep 17 00:00:00 2001 From: Manasi Date: Wed, 1 Sep 2021 01:59:00 +0530 Subject: [PATCH 1429/1476] Pubmatic Bid Adapter: add support for JW Player (#7291) * changes to support jw player in pubmatic adapter * changed incorrect variable name in function * code optimisation changes * unmix quotes for linting Co-authored-by: Manasi Co-authored-by: Chris Huie --- modules/pubmaticBidAdapter.js | 21 +++++++ test/spec/modules/pubmaticBidAdapter_spec.js | 63 ++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index f6e6e67444a..7edeb5589d1 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -605,6 +605,26 @@ function _addDealCustomTargetings(imp, bid) { } } +function _addJWPlayerSegmentData(imp, bid) { + var jwSegData = (bid.rtd && bid.rtd.jwplayer && bid.rtd.jwplayer.targeting) || undefined; + var jwPlayerData = ''; + const jwMark = 'jw-'; + + if (jwSegData === undefined || jwSegData === '' || !jwSegData.hasOwnProperty('segments')) return; + + var maxLength = jwSegData.segments.length; + + jwPlayerData += jwMark + 'id=' + jwSegData.content.id; // add the content id first + + for (var i = 0; i < maxLength; i++) { + jwPlayerData += '|' + jwMark + jwSegData.segments[i] + '=1'; + } + const ext = imp.ext; + (ext && ext.key_val === undefined) + ? ext.key_val = jwPlayerData + : ext.key_val += '|' + jwPlayerData; +} + function _createImpressionObject(bid, conf) { var impObj = {}; var bannerObj; @@ -627,6 +647,7 @@ function _createImpressionObject(bid, conf) { _addPMPDealsInImpression(impObj, bid); _addDealCustomTargetings(impObj, bid); + _addJWPlayerSegmentData(impObj, bid); if (bid.hasOwnProperty('mediaTypes')) { for (mediaTypes in bid.mediaTypes) { switch (mediaTypes) { diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 23c5f01e520..df3516579de 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1565,6 +1565,69 @@ describe('PubMatic adapter', function () { expect(data2.regs).to.equal(undefined);// USP/CCPAs }); + it('Request params check with JW player params', function() { + let bidRequests = [ + { + bidder: 'pubmatic', + params: { + publisherId: '301', + adSlot: '/15671365/DMDemo@300x250:0', + dctr: 'key1=val1|key2=val2,val3' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', + rtd: { + jwplayer: { + targeting: { + content: { id: 'jw_d9J2zcaA' }, + segments: ['80011026', '80011035'] + } + } + } + }]; + let key_val_output = 'key1=val1|key2=val2,val3|jw-id=jw_d9J2zcaA|jw-80011026=1|jw-80011035=1' + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.imp[0].ext).to.exist.and.to.be.an('object'); + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(key_val_output); + + // jw player data not available. Only dctr sent. + delete bidRequests[0].rtd; + request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + + expect(data.imp[0].ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); + + // jw player data is available, but dctr is not present + bidRequests[0].rtd = { + jwplayer: { + targeting: { + content: { id: 'jw_d9J2zcaA' }, + segments: ['80011026', '80011035'] + } + } + }; + + delete bidRequests[0].params.dctr; + key_val_output = 'jw-id=jw_d9J2zcaA|jw-80011026=1|jw-80011035=1'; + request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + + expect(data.imp[0].ext).to.exist.and.to.be.an('object'); + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(key_val_output); + }); + describe('FPD', function() { let newRequest; From c4feb7000098f7d3579e4dca0f8743885d4b0fc9 Mon Sep 17 00:00:00 2001 From: IQZoneAdx <88879712+IQZoneAdx@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:15:53 +0300 Subject: [PATCH 1430/1476] add IQZone adapter (#7309) LGTM --- modules/iqzoneBidAdapter.js | 176 ++++++++++ modules/iqzoneBidAdapter.md | 80 +++++ test/spec/modules/iqzoneBidAdapter_spec.js | 371 +++++++++++++++++++++ 3 files changed, 627 insertions(+) create mode 100644 modules/iqzoneBidAdapter.js create mode 100644 modules/iqzoneBidAdapter.md create mode 100644 test/spec/modules/iqzoneBidAdapter_spec.js diff --git a/modules/iqzoneBidAdapter.js b/modules/iqzoneBidAdapter.js new file mode 100644 index 00000000000..77bdcc0188d --- /dev/null +++ b/modules/iqzoneBidAdapter.js @@ -0,0 +1,176 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'iqzone'; +const AD_URL = 'https://smartssp-us-east.iqzone.com/pbjs'; + +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 = { + placementId, + bidId, + schain, + bidfloor + }; + + 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 (!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 + } +} + +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 = {}) => { + 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) { + utils.logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + utils.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; + } +}; + +registerBidder(spec); diff --git a/modules/iqzoneBidAdapter.md b/modules/iqzoneBidAdapter.md new file mode 100644 index 00000000000..75e82d59b3e --- /dev/null +++ b/modules/iqzoneBidAdapter.md @@ -0,0 +1,80 @@ +# Overview + +``` +Module Name: IQZone Bidder Adapter +Module Type: IQZone Bidder Adapter +Maintainer: no-reply@vsn.si +``` + +# Description + +Connects to IQZone exchange for bids. + +IQZone 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: 'iqzone', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'iqzone', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'iqzone', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/iqzoneBidAdapter_spec.js b/test/spec/modules/iqzoneBidAdapter_spec.js new file mode 100644 index 00000000000..3c7da783728 --- /dev/null +++ b/test/spec/modules/iqzoneBidAdapter_spec.js @@ -0,0 +1,371 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/iqzoneBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'iqzone' + +describe('IQZoneBidAdapter', 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://smartssp-us-east.iqzone.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); + + 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; + }); + }); +}); From e485e9ea7e36e7efb26accd83d71a92e183e26b8 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:18:00 +0200 Subject: [PATCH 1431/1476] smartx Bid Adapter: bugfix outstream options for default outstream renderer configuration (#7372) * 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 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 47 ++++++++++++----- modules/smartxBidAdapter.md | 8 +-- test/spec/modules/smartxBidAdapter_spec.js | 61 ++++++++++++++++++---- 3 files changed, 89 insertions(+), 27 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index c29708c8420..44e70082d9d 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -329,25 +329,20 @@ export const spec = { } function createOutstreamConfig(bid) { - const confMinAdWidth = utils.getBidIdParameter('minAdWidth', bid.renderer.config.outstream_options) || 290; - const confMaxAdWidth = utils.getBidIdParameter('maxAdWidth', bid.renderer.config.outstream_options) || 900; - const confStartOpen = utils.getBidIdParameter('startOpen', bid.renderer.config.outstream_options); - const confEndingScreen = utils.getBidIdParameter('endingScreen', bid.renderer.config.outstream_options); - const confTitle = utils.getBidIdParameter('title', bid.renderer.config.outstream_options); - const confSkipOffset = utils.getBidIdParameter('skipOffset', bid.renderer.config.outstream_options); - const confDesiredBitrate = utils.getBidIdParameter('desiredBitrate', bid.renderer.config.outstream_options); - const elementId = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options) || bid.adUnitCode; + let confMinAdWidth = utils.getBidIdParameter('minAdWidth', bid.renderer.config.outstream_options) || 290; + let confMaxAdWidth = utils.getBidIdParameter('maxAdWidth', bid.renderer.config.outstream_options) || 900; + let confStartOpen = utils.getBidIdParameter('startOpen', bid.renderer.config.outstream_options) + let confEndingScreen = utils.getBidIdParameter('endingScreen', bid.renderer.config.outstream_options) + let confTitle = utils.getBidIdParameter('title', bid.renderer.config.outstream_options); + let confSkipOffset = utils.getBidIdParameter('skipOffset', bid.renderer.config.outstream_options); + let confDesiredBitrate = utils.getBidIdParameter('desiredBitrate', bid.renderer.config.outstream_options); + let elementId = utils.getBidIdParameter('slot', bid.renderer.config.outstream_options) || bid.adUnitCode; utils.logMessage('[SMARTX][renderer] Handle SmartX outstream renderer'); var smartPlayObj = { minAdWidth: confMinAdWidth, maxAdWidth: confMaxAdWidth, - title: confTitle, - skipOffset: confSkipOffset, - startOpen: confStartOpen, - endingScreen: confEndingScreen, - desiredBitrate: confDesiredBitrate, onStartCallback: function (m, n) { try { window.sc_smartIntxtStart(n); @@ -365,13 +360,37 @@ function createOutstreamConfig(bid) { }, }; + if (confStartOpen == 'true') { + smartPlayObj.startOpen = true; + } else if (confStartOpen == 'false') { + smartPlayObj.startOpen = false; + } + + if (confEndingScreen == 'true') { + smartPlayObj.endingScreen = true; + } else if (confEndingScreen == 'false') { + smartPlayObj.endingScreen = false; + } + + if (confTitle) { + smartPlayObj.title = confTitle; + } + + if (confSkipOffset) { + smartPlayObj.skipOffset = confSkipOffset; + } + + if (confDesiredBitrate) { + smartPlayObj.desiredBitrate = confDesiredBitrate; + } + smartPlayObj.adResponse = bid.vastContent; const divID = '[id="' + elementId + '"]'; try { // eslint-disable-next-line - let _outstreamPlayer = new OutstreamPlayer(divID, smartPlayObj); + let _outstreamPlayer = new OutstreamPlayer(divID, smartPlayObj); } catch (e) { utils.logError('[SMARTX][renderer] Error caught: ' + e); } diff --git a/modules/smartxBidAdapter.md b/modules/smartxBidAdapter.md index 223e51763b9..853f06d6baf 100644 --- a/modules/smartxBidAdapter.md +++ b/modules/smartxBidAdapter.md @@ -38,8 +38,8 @@ This adapter requires setup and approval from the smartclip team. maxAdWidth: 900, title: '', skipOffset: 0, - startOpen: true, - endingScreen: true, + startOpen: 'true', + endingScreen: 'true', desiredBitrate: 800, }, } @@ -73,8 +73,8 @@ This adapter requires setup and approval from the smartclip team. maxAdWidth: 900, title: '', skipOffset: 0, - startOpen: true, - endingScreen: true, + startOpen: 'true', + endingScreen: 'true', desiredBitrate: 800, }, user: { diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 89c03034ba4..4e560c87df3 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -495,7 +495,7 @@ describe('The smartx adapter', function () { }; }); - it('should attempt to insert the script', function () { + it('should attempt to insert the script without outstream config options set', function () { var scriptTag; sinon.stub(window.document, 'getElementById').returns({ appendChild: sinon.stub().callsFake(function (script) { @@ -506,8 +506,51 @@ describe('The smartx adapter', function () { responses[0].renderer.render(responses[0]); - // expect(scriptTag.getAttribute('type')).to.equal('text/javascript'); - // expect(scriptTag.getAttribute('src')).to.equal('https://dco.smartclip.net/?plc=7777778'); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); + + window.document.getElementById.restore(); + }); + + it('should attempt to insert the script with outstream config options set', function () { + var scriptTag; + sinon.stub(window.document, 'getElementById').returns({ + appendChild: sinon.stub().callsFake(function (script) { + scriptTag = script + }) + }); + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + + bidderRequestObj.bidRequest.bids[0].params.outstream_options.startOpen = 'true'; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.endingScreen = 'true'; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.title = 'abc'; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.skipOffset = 2; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.desiredBitrate = 123; + + responses[0].renderer.render(responses[0]); + + bidderRequestObj.bidRequest.bids[0].params.outstream_options.startOpen = 'false'; + bidderRequestObj.bidRequest.bids[0].params.outstream_options.endingScreen = 'false'; + + responses[0].renderer.render(responses[0]); + + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); + + window.document.getElementById.restore(); + }); + + it('should attempt to insert the script without defined slot', function () { + var scriptTag; + sinon.stub(window.document, 'getElementById').returns({ + appendChild: sinon.stub().callsFake(function (script) { + scriptTag = script + }) + }); + var responses = spec.interpretResponse(serverResponse, bidderRequestObj); + + delete bidderRequestObj.bidRequest.bids[0].params.outstream_options.slot; + + responses[0].renderer.render(responses[0]); + expect(responses[0].renderer.url).to.equal('https://dco.smartclip.net/?plc=7777778'); window.document.getElementById.restore(); @@ -540,7 +583,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 3.21); }); - it('obtain floor from params', function() { + it('obtain floor from params', function () { bid.getFloor = () => { return { currency: 'EUR', @@ -553,7 +596,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 0.64); }); - it('check currency USD', function() { + it('check currency USD', function () { bid.getFloor = () => { return { currency: 'USD', @@ -567,7 +610,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 1.23); }); - it('check defaut currency EUR', function() { + it('check defaut currency EUR', function () { delete bid.params.bidfloorcur; bid.getFloor = () => { @@ -582,7 +625,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 4.56); }); - it('bad floor value', function() { + it('bad floor value', function () { bid.getFloor = () => { return { currency: 'EUR', @@ -594,7 +637,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 0); }); - it('empty floor object', function() { + it('empty floor object', function () { bid.getFloor = () => { return {}; }; @@ -603,7 +646,7 @@ describe('The smartx adapter', function () { expect(payload.data.imp[0]).to.have.property('bidfloor', 0); }); - it('undefined floor result', function() { + it('undefined floor result', function () { bid.getFloor = () => {}; const payload = spec.buildRequests([bid], bidRequestObj)[0]; From fffa3e32ad8e6ef7d1086c2367d8583774738a01 Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Wed, 1 Sep 2021 16:35:01 +0100 Subject: [PATCH 1432/1476] Adloox real time data module (#6310) --- integrationExamples/gpt/adloox.html | 27 +- modules/adlooxAnalyticsAdapter.js | 11 +- modules/adlooxAnalyticsAdapter.md | 17 +- modules/adlooxRtdProvider.js | 390 ++++++++++++++++++ modules/adlooxRtdProvider.md | 105 +++++ .../modules/adlooxAnalyticsAdapter_spec.js | 2 +- test/spec/modules/adlooxRtdProvider_spec.js | 313 ++++++++++++++ 7 files changed, 848 insertions(+), 17 deletions(-) create mode 100644 modules/adlooxRtdProvider.js create mode 100644 modules/adlooxRtdProvider.md create mode 100644 test/spec/modules/adlooxRtdProvider_spec.js diff --git a/integrationExamples/gpt/adloox.html b/integrationExamples/gpt/adloox.html index 2a772bb7ce2..e8920cf2ee1 100644 --- a/integrationExamples/gpt/adloox.html +++ b/integrationExamples/gpt/adloox.html @@ -64,9 +64,11 @@ playerSize: [ 640, 480 ] } }, - fpd: { - context: { - pbAdSlot: '/19968336/prebid_cache_video_adunit' + ortb2Imp: { + ext: { + data: { + pbadslot: '/19968336/prebid_cache_video_adunit' + } } }, bids: [ @@ -106,7 +108,8 @@ pbjs.initAdserverSet = true; googletag.cmd.push(function() { - pbjs.setTargetingForGPTAsync && pbjs.setTargetingForGPTAsync(adUnits); + const adUnitCodes = adUnits.map(adUnit => adUnit.code); + pbjs.setTargetingForGPTAsync(adUnitCodes); googletag.pubads().refresh(); }); @@ -135,12 +138,24 @@ } // optionally wrap with googletag to have gpt-pre-auction - // automatically populate Prebid Ad Slot (pbAdSlot) + // automatically populate Prebid Ad Slot (pbadslot) // https://docs.prebid.org/dev-docs/modules/gpt-pre-auction.html - // alternatively remove wrapping and set AdUnit.fpd.context.pbAdSlot + // alternatively remove wrapping and set AdUnit.ortb2Imp.ext.data.pbadslot googletag.cmd.push(function() { pbjs.que.push(function() { pbjs.setConfig({ + realTimeData: { + auctionDelay: AUCTION_DELAY, + dataProviders: [ + { + name: 'adloox', + params: { // optional, defaults shown + thresholds: [ 50, 60, 70, 80, 90 ], + slotinpath: false + } + } + ] + }, instreamTracking: { enabled: true }, diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index cfd01b4434a..17787f816df 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -43,14 +43,15 @@ MACRO['targetelt'] = function(b, c) { MACRO['creatype'] = function(b, c) { return b.mediaType == 'video' ? ADLOOX_MEDIATYPE.VIDEO : ADLOOX_MEDIATYPE.DISPLAY; }; -MACRO['pbAdSlot'] = function(b, c) { +MACRO['pbadslot'] = function(b, c) { const adUnit = find(auctionManager.getAdUnits(), a => b.adUnitCode === a.code); - return utils.deepAccess(adUnit, 'fpd.context.pbAdSlot') || utils.getGptSlotInfoForAdUnitCode(b.adUnitCode).gptSlot || b.adUnitCode; + return utils.deepAccess(adUnit, 'ortb2Imp.ext.data.pbadslot') || utils.getGptSlotInfoForAdUnitCode(b.adUnitCode).gptSlot || b.adUnitCode; }; +MACRO['pbAdSlot'] = MACRO['pbadslot']; // legacy const PARAMS_DEFAULT = { 'id1': function(b) { return b.adUnitCode }, - 'id2': '%%pbAdSlot%%', + 'id2': '%%pbadslot%%', 'id3': function(b) { return b.bidder }, 'id4': function(b) { return b.adId }, 'id5': function(b) { return b.dealId }, @@ -251,6 +252,7 @@ export default analyticsAdapter; const COMMAND_QUEUE = {}; export const COMMAND = { CONFIG: 'config', + TOSELECTOR: 'toselector', URL: 'url', TRACK: 'track' }; @@ -278,6 +280,9 @@ function commandProcess(cid) { }; callback(response); break; + case COMMAND.TOSELECTOR: + callback(analyticsAdapter.context.toselector(data.bid)); + break; case COMMAND.URL: if (data.ids) data.args = data.args.concat(analyticsAdapter.context.params.filter(p => /^id([1-9]|10)$/.test(p[0]))); // not >10 callback(analyticsAdapter.url(data.url, data.args, data.bid)); diff --git a/modules/adlooxAnalyticsAdapter.md b/modules/adlooxAnalyticsAdapter.md index d4d53a81b3c..c4618a2e3aa 100644 --- a/modules/adlooxAnalyticsAdapter.md +++ b/modules/adlooxAnalyticsAdapter.md @@ -2,11 +2,11 @@ Module Name: Adloox Analytics Adapter Module Type: Analytics Adapter - Maintainer: technique@adloox.com + Maintainer: contact@adloox.com # Description -Analytics adapter for adloox.com. Contact adops@adloox.com for information. +Analytics adapter for adloox.com. Contact contact@adloox.com for information. This module can be used to track: @@ -34,7 +34,7 @@ When tracking video you have two options: To view an [example of an Adloox integration](../integrationExamples/gpt/adloox.html): - gulp serve --nolint --notest --modules=gptPreAuction,categoryTranslation,dfpAdServerVideo,instreamTracking,rubiconBidAdapter,spotxBidAdapter,adlooxAnalyticsAdapter,adlooxAdServerVideo + gulp serve --nolint --notest --modules=gptPreAuction,categoryTranslation,dfpAdServerVideo,rtdModule,instreamTracking,rubiconBidAdapter,spotxBidAdapter,adlooxAnalyticsAdapter,adlooxAdServerVideo,adlooxRtdProvider **N.B.** `categoryTranslation` is required by `dfpAdServerVideo` that otherwise causes a JavaScript console warning @@ -44,6 +44,8 @@ Now point your browser at: http://localhost:9999/integrationExamples/gpt/adloox. The example is published publically at: https://storage.googleapis.com/adloox-ads-js-test/prebid.html?pbjs_debug=true +**N.B.** this will show a [CORS error](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors) for the request `https://p.adlooxtracking.com/q?...` that is safe to ignore on the public example page; it is related to the [RTD integration](./adlooxRtdProvider.md) which requires pre-registration of your sites + It is recommended you use [Google Chrome's 'Local Overrides' located in the Developer Tools panel](https://www.trysmudford.com/blog/chrome-local-overrides/) to explore the example without the inconvience of having to run your own web server. #### Pre-built `prebid.js` @@ -61,7 +63,7 @@ You should be able to use this during the QA process of your own internal testin The main Prebid.js documentation is a bit opaque on this but you can use the following to test only Adloox's modules: gulp lint - gulp test-coverage --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo}_spec.js' + gulp test-coverage --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo,RtdProvider}_spec.js' gulp view-coverage # Integration @@ -103,8 +105,8 @@ For example, you have a number of reporting breakdown slots available in the for platformid: 0, tagid: 0, params: { - id1: function(b) { return b.adUnitCode }, - id2: '%%pbAdSlot%%', + id1: function(b) { return b.adUnitCode }, // do not change when using the Adloox RTD Provider + id2: '%%pbadslot%%', // do not change when using the Adloox RTD Provider id3: function(b) { return b.bidder }, id4: function(b) { return b.adId }, id5: function(b) { return b.dealId }, @@ -123,7 +125,8 @@ For example, you have a number of reporting breakdown slots available in the for The following macros are available - * `%%pbAdSlot%%`: [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html) returns [`AdUnit.code`](https://docs.prebid.org/features/pbAdSlot.html) if set otherwise returns [`AdUnit.code`](https://docs.prebid.org/dev-docs/adunit-reference.html#adunit) + * `%%pbadslot%%`: [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html) returns [`AdUnit.code`](https://docs.prebid.org/features/pbAdSlot.html) if set otherwise returns [`AdUnit.code`](https://docs.prebid.org/dev-docs/adunit-reference.html#adunit) + * it is recommended you read the [Prebid Ad Slot section in the Adloox RTD Provider documentation](./adlooxRtdProvider.md#prebid-ad-slot) ### Functions diff --git a/modules/adlooxRtdProvider.js b/modules/adlooxRtdProvider.js new file mode 100644 index 00000000000..3e93047cccc --- /dev/null +++ b/modules/adlooxRtdProvider.js @@ -0,0 +1,390 @@ +/** + * This module adds the Adloox provider to the real time data module + * This module adds the [Adloox]{@link https://www.adloox.com/} provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch segments from Adloox's server + * @module modules/adlooxRtdProvider + * @requires module:modules/realTimeData + * @requires module:modules/adlooxAnalyticsAdapter + */ + +/* eslint standard/no-callback-literal: "off" */ +/* eslint prebid/validate-imports: "off" */ + +import { command as analyticsCommand, COMMAND } from './adlooxAnalyticsAdapter.js'; +import { config as _config } from '../src/config.js'; +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import * as utils from '../src/utils.js'; +import includes from 'core-js-pure/features/array/includes.js'; + +const MODULE_NAME = 'adloox'; +const MODULE = `${MODULE_NAME}RtdProvider`; + +const API_ORIGIN = 'https://p.adlooxtracking.com'; +const SEGMENT_HISTORIC = { 'a': 'aud', 'd': 'dis', 'v': 'vid' }; +const SEGMENT_HISTORIC_VALUES = Object.keys(SEGMENT_HISTORIC).map(k => SEGMENT_HISTORIC[k]); + +const ADSERVER_TARGETING_PREFIX = 'adl_'; + +const CREATIVE_WIDTH_MIN = 20; +const CREATIVE_HEIGHT_MIN = 20; +const CREATIVE_AREA_MIN = CREATIVE_WIDTH_MIN * CREATIVE_HEIGHT_MIN; +// try to avoid using IntersectionObserver as it has unbounded (observed multi-second) latency +let intersectionObserver = window == top ? false : undefined; +const intersectionObserverElements = []; +// .map/.findIndex are safe here as they are only used for intersectionObserver +function atf(adUnit, cb) { + analyticsCommand(COMMAND.TOSELECTOR, { bid: { adUnitCode: adUnit.code } }, function(selector) { + const element = document.querySelector(selector); + if (!element) return cb(null); + + if (window.getComputedStyle(element)['display'] == 'none') return cb(NaN); + + try { + // short circuit for cross-origin + if (intersectionObserver) throw false; + + // Google's advice is to collapse slots on no fill but + // we have to cater to clients that grow slots on fill + const rect = (function(rect) { + const sizes = utils.getAdUnitSizes(adUnit); + + if (sizes.length == 0) return false; + // interstitial (0x0, 1x1) + if (sizes.length == 1 && (sizes[0][0] * sizes[0][1]) <= 1) return true; + // try to catch premium slots (coord=0,0) as they will likely bounce into view + if (rect.top <= -window.pageYOffset && rect.left <= -window.pageXOffset && rect.top == rect.bottom) return true; + + // pick the smallest creative size as many publishers will just leave the element unbounded in the vertical + let width = Infinity; + let height = Infinity; + for (let i = 0; i < sizes.length; i++) { + const area = sizes[i][0] * sizes[i][1]; + if (area < CREATIVE_AREA_MIN) continue; + if (area < (width * height)) { + width = sizes[i][0]; + height = sizes[i][1]; + } + } + // we also scale the smallest size to the size of the slot as publishers resize units depending on viewport + const scale = Math.min(1, (rect.right - rect.left) / width); + + return { + left: rect.left, + right: rect.left + Math.max(CREATIVE_WIDTH_MIN, scale * width), + top: rect.top, + bottom: rect.top + Math.max(CREATIVE_HEIGHT_MIN, scale * height) + }; + })(element.getBoundingClientRect()); + + if (rect === false) return cb(NaN); + if (rect === true) return cb(1); + + const W = rect.right - rect.left; + const H = rect.bottom - rect.top; + + if (W * H < CREATIVE_AREA_MIN) return cb(NaN); + + let el; + let win = window; + while (1) { + // https://stackoverflow.com/a/8876069 + const vw = Math.max(win.document.documentElement.clientWidth || 0, win.innerWidth || 0); + const vh = Math.max(win.document.documentElement.clientHeight || 0, win.innerHeight || 0); + + // cut to viewport + rect.left = Math.min(Math.max(rect.left, 0), vw); + rect.right = Math.min(Math.max(rect.right, 0), vw); + rect.top = Math.min(Math.max(rect.top, 0), vh); + rect.bottom = Math.min(Math.max(rect.bottom, 0), vh); + + if (win == top) return cb(((rect.right - rect.left) * (rect.bottom - rect.top)) / (W * H)); + el = win.frameElement; + if (!el) throw false; // cross-origin + win = win.parent; + + // transpose to frame element + const frameElementRect = el.getBoundingClientRect(); + rect.left += frameElementRect.left; + rect.right = Math.min(rect.right + frameElementRect.left, frameElementRect.right); + rect.top += frameElementRect.top; + rect.bottom = Math.min(rect.bottom + frameElementRect.top, frameElementRect.bottom); + } + } catch (_) { + if (intersectionObserver === undefined) { + try { + intersectionObserver = new IntersectionObserver(function(entries) { + entries.forEach(entry => { + const ratio = entry.intersectionRect.width * entry.intersectionRect.height < CREATIVE_AREA_MIN ? NaN : entry.intersectionRatio; + const idx = intersectionObserverElements.findIndex(x => x.element == entry.target); + intersectionObserverElements[idx].cb.forEach(cb => cb(ratio)); + intersectionObserverElements.splice(idx, 1); + intersectionObserver.unobserve(entry.target); + }); + }); + } catch (_) { + intersectionObserver = false; + } + } + if (!intersectionObserver) return cb(null); + const idx = intersectionObserverElements.findIndex(x => x.element == element); + if (idx == -1) { + intersectionObserverElements.push({ element, cb: [ cb ] }); + intersectionObserver.observe(element); + } else { + intersectionObserverElements[idx].cb.push(cb); + } + } + }); +} + +function init(config, userConsent) { + utils.logInfo(MODULE, 'init', config, userConsent); + + if (!utils.isPlainObject(config)) { + utils.logError(MODULE, 'missing config'); + return false; + } + if (config.params === undefined) config.params = {}; + if (!(utils.isPlainObject(config.params))) { + utils.logError(MODULE, 'invalid params'); + return false; + } + if (!(config.params.api_origin === undefined || utils.isStr(config.params.api_origin))) { + utils.logError(MODULE, 'invalid api_origin params value'); + return false; + } + if (!(config.params.imps === undefined || (utils.isInteger(config.params.imps) && config.params.imps > 0))) { + utils.logError(MODULE, 'invalid imps params value'); + return false; + } + if (!(config.params.freqcap_ip === undefined || (utils.isInteger(config.params.freqcap_ip) && config.params.freqcap_ip >= 0))) { + utils.logError(MODULE, 'invalid freqcap_ip params value'); + return false; + } + if (!(config.params.freqcap_ipua === undefined || (utils.isInteger(config.params.freqcap_ipua) && config.params.freqcap_ipua >= 0))) { + utils.logError(MODULE, 'invalid freqcap_ipua params value'); + return false; + } + if (!(config.params.thresholds === undefined || (utils.isArray(config.params.thresholds) && config.params.thresholds.every(x => utils.isInteger(x) && x > 0 && x <= 100)))) { + utils.logError(MODULE, 'invalid thresholds params value'); + return false; + } + if (!(config.params.slotinpath === undefined || utils.isBoolean(config.params.slotinpath))) { + utils.logError(MODULE, 'invalid slotinpath params value'); + return false; + } + // legacy/deprecated configuration code path + if (!(config.params.params === undefined || (utils.isPlainObject(config.params.params) && utils.isInteger(config.params.params.clientid) && utils.isInteger(config.params.params.tagid) && utils.isInteger(config.params.params.platformid)))) { + utils.logError(MODULE, 'invalid subsection params block'); + return false; + } + + config.params.thresholds = config.params.thresholds || [ 50, 60, 70, 80, 90 ]; + + function analyticsConfigCallback(data) { + config = utils.mergeDeep(config.params, data); + } + if (config.params.params) { + utils.logWarn(MODULE, `legacy/deprecated configuration (please migrate to ${MODULE_NAME}AnalyticsAdapter)`); + analyticsConfigCallback(config.params.params); + } else { + analyticsCommand(COMMAND.CONFIG, null, analyticsConfigCallback); + } + + return true; +} + +function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { + // gptPreAuction runs *after* RTD so pbadslot may not be populated... (╯°□°)╯ ┻━┻ + const adUnits = (reqBidsConfigObj.adUnits || getGlobal().adUnits).map(adUnit => { + let path = utils.deepAccess(adUnit, 'ortb2Imp.ext.data.pbadslot'); + if (!path) path = utils.getGptSlotInfoForAdUnitCode(adUnit.code).gptSlot; + return { + path: path, + unit: adUnit + }; + }).filter(adUnit => !!adUnit.path); + + let response = {}; + function setSegments() { + function val(v, k) { + if (!((SEGMENT_HISTORIC[k] || k == 'atf') && v >= 0)) return v; + return config.params.thresholds.filter(t => t <= v); + } + + const ortb2 = _config.getConfig('ortb2') || {}; + const dataSite = _config.getConfig('ortb2.site.ext.data') || {}; + const dataUser = _config.getConfig('ortb2.user.ext.data') || {}; + + utils._each(response, (v0, k0) => { + if (k0 == '_') return; + const k = SEGMENT_HISTORIC[k0] || k0; + const v = val(v0, k0); + utils.deepSetValue(k == k0 ? dataUser : dataSite, `${MODULE_NAME}_rtd.${k}`, v); + }); + utils.deepSetValue(dataSite, `${MODULE_NAME}_rtd.ok`, true); + + utils.deepSetValue(ortb2, 'site.ext.data', dataSite); + utils.deepSetValue(ortb2, 'user.ext.data', dataUser); + _config.setConfig({ ortb2 }); + + adUnits.forEach((adUnit, i) => { + utils._each(response['_'][i], (v0, k0) => { + const k = SEGMENT_HISTORIC[k0] || k0; + const v = val(v0, k0); + utils.deepSetValue(adUnit.unit, `ortb2Imp.ext.data.${MODULE_NAME}_rtd.${k}`, v); + }); + }); + }; + + // utils.mergeDeep does not handle merging deep arrays... (╯°□°)╯ ┻━┻ + function mergeDeep(target, ...sources) { + function emptyValue(v) { + if (utils.isPlainObject(v)) { + return {}; + } else if (utils.isArray(v)) { + return []; + } else { + return undefined; + } + } + + if (!sources.length) return target; + const source = sources.shift(); + + if (utils.isPlainObject(target) && utils.isPlainObject(source)) { + Object.keys(source).forEach(key => { + if (!(key in target)) target[key] = emptyValue(source[key]); + target[key] = target[key] !== undefined ? mergeDeep(target[key], source[key]) : source[key]; + }); + } else if (utils.isArray(target) && utils.isArray(source)) { + source.forEach((v, i) => { + if (!(i in target)) target[i] = emptyValue(v); + target[i] = target[i] !== undefined ? mergeDeep(target[i], v) : v; + }); + } else { + target = source; + } + + return mergeDeep(target, ...sources); + } + + let semaphore = 1; + function semaphoreInc(inc) { + if (semaphore == 0) return; + semaphore += inc; + if (semaphore == 0) { + setSegments() + callback(); + } + } + + const args = [ + [ 'v', `pbjs-${getGlobal().version}` ], + [ 'c', config.params.clientid ], + [ 'p', config.params.platformid ], + [ 't', config.params.tagid ], + [ 'imp', config.params.imps ], + [ 'fc_ip', config.params.freqcap_ip ], + [ 'fc_ipua', config.params.freqcap_ipua ], + [ 'pn', document.location.pathname ] + ]; + + if (!adUnits.length) { + utils.logWarn(MODULE, 'no suitable adUnits (missing pbadslot?)'); + } + const atfQueue = []; + adUnits.map((adUnit, i) => { + const ref = [ adUnit.path ]; + if (!config.params.slotinpath) ref.push(adUnit.unit.code); + args.push(['s', ref.join('\t')]); + + semaphoreInc(1); + atfQueue.push(function() { + atf(adUnit.unit, function(x) { + let viewable = document.visibilityState === undefined || document.visibilityState == 'visible'; + try { viewable = viewable && top.document.hasFocus() } catch (_) {} + utils.logInfo(MODULE, `atf code=${adUnit.unit.code} has area=${x}, viewable=${viewable}`); + const atfList = []; atfList[i] = { atf: parseInt(x * 100) }; + response = mergeDeep(response, { _: atfList }); + semaphoreInc(-1); + }); + }); + }); + function atfCb() { + atfQueue.forEach(x => x()); + } + if (document.readyState == 'loading') { + document.addEventListener('DOMContentLoaded', atfCb, false); + } else { + atfCb(); + } + + analyticsCommand(COMMAND.URL, { + url: (config.params.api_origin || API_ORIGIN) + '/q?', + args: args + }, function(url) { + ajax(url, { + success: function(responseText, q) { + try { + if (q.getResponseHeader('content-type') == 'application/json') { + response = mergeDeep(response, JSON.parse(responseText)); + } else { + throw false; + } + } catch (_) { + utils.logError(MODULE, 'unexpected response'); + } + semaphoreInc(-1); + }, + error: function(statusText, q) { + utils.logError(MODULE, 'request failed'); + semaphoreInc(-1); + } + }); + }); +} + +function getTargetingData(adUnitArray, config, userConsent) { + function targetingNormalise(v) { + if (utils.isArray(v) && v.length == 0) return undefined; + if (utils.isBoolean(v)) v = ~~v; + if (!v) return undefined; // empty string and zero + return v; + } + + const dataSite = _config.getConfig(`ortb2.site.ext.data.${MODULE_NAME}_rtd`) || {}; + if (!dataSite.ok) return {}; + + const dataUser = _config.getConfig(`ortb2.user.ext.data.${MODULE_NAME}_rtd`) || {}; + return getGlobal().adUnits.filter(adUnit => includes(adUnitArray, adUnit.code)).reduce((a, adUnit) => { + a[adUnit.code] = {}; + + utils._each(dataSite, (v0, k) => { + if (includes(SEGMENT_HISTORIC_VALUES, k)) return; // ignore site average viewability + const v = targetingNormalise(v0); + if (v) a[adUnit.code][ADSERVER_TARGETING_PREFIX + k] = v; + }); + + const adUnitSegments = utils.deepAccess(adUnit, `ortb2Imp.ext.data.${MODULE_NAME}_rtd`, {}); + utils._each(Object.assign({}, dataUser, adUnitSegments), (v0, k) => { + const v = targetingNormalise(v0); + if (v) a[adUnit.code][ADSERVER_TARGETING_PREFIX + k] = v; + }); + + return a; + }, {}); +} + +export const subModuleObj = { + name: MODULE_NAME, + init, + getBidRequestData, + getTargetingData, + atf // used by adlooxRtdProvider_spec.js +}; + +submodule('realTimeData', subModuleObj); diff --git a/modules/adlooxRtdProvider.md b/modules/adlooxRtdProvider.md new file mode 100644 index 00000000000..6c75fbc2d8b --- /dev/null +++ b/modules/adlooxRtdProvider.md @@ -0,0 +1,105 @@ +# Overview + + Module Name: Adloox RTD Provider + Module Type: RTD Provider + Maintainer: contact@adloox.com + +# Description + +RTD provider for adloox.com. Contact contact@adloox.com for information. + +This provider fetches segments and populates the [First Party Data](https://docs.prebid.org/features/firstPartyData.html) attributes, some examples of the segments as described by the Adloox 'Google Publisher Tag Targeting Guidelines' and where they are placed are: + + * Page segments are placed into `ortb2.site.ext.data.adloox_rtd`: + * **`ok`:** boolean (use to capture if our module successfully ran) + * Device segments are placed into `ortb2.user.ext.data.adloox_rtd`: + * **`ivt`:** boolean + * **`ua_old`:** boolean + * **`ip`:** list of strings describing classification of IP (eg. `rfc-special`, `iab-dc`, ...) + * AdUnit segments are placed into `AdUnit.ortb2Imp.ext.data.adloox_rtd`: + * **`{dis,vid,aud}`:** an list of integers describing the likelihood the AdUnit will be visible + * **`atf`:** an list of integers describing the percentage of pixels visible at auction + * measured only once at pre-auction + * usable when the publisher uses the strategy of collapsing ad slots on no-fill + * using the reverse strategy, growing ad slots on fill, invalidates the measurement the position of all content (including the slots) changes post-auction + * works best when your page loads your ad slots have their actual size rendered (ie. not zero height) + * uses the smallest ad unit (above a threshold area of 20x20) supplied by the [publisher to Prebid.js](https://docs.prebid.org/dev-docs/examples/basic-example.html) and measures viewability as if that size to be used + * when used in cross-origin (unfriendly) IFRAME environments the ad slot is directly measured as is (ignoring publisher provided sizes) due to limitations in using [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) + +**N.B.** this provider does not offer or utilise any user orientated data + +These segments are also sent to your ad server but are translated using the following rules: + + * prepended the segment name with `adl_` + * segments are filtered out when their value is either: + * empty string ("") + * zero (`0`) + * boolean `false` + * empty list/array + +For example: + + * `ortb2.site.ext.data.adloox_rtd.ok` is translated to `adl_ok` + * `ortb2.user.ext.data.adloox_rtd.ivt` is translated to `adl_ivt` + * `AdUnit.ortb2Imp.ext.data.adloox_rtd.dis` is translated to `adl_dis` + +## Example + +To view an example of an Adloox integration look at the example provided in the [Adloox Analytics Adapter documentation](./adlooxAnalyticsAdapter.md#example). + +# Integration + +To use this, you *must* also integrate the [Adloox Analytics Adapter](./adlooxAnalyticsAdapter.md) as shown below: + + pbjs.setConfig({ + ... + + realTimeData: { + auctionDelay: 100, // see below for guidance + dataProviders: [ + { + name: 'adloox', + params: { // optional, defaults shown + thresholds: [ 50, 60, 70, 80, 90 ], + slotinpath: false + } + } + ] + }, + + ... + }); + pbjs.enableAnalytics({ + provider: 'adloox', + options: { + client: 'adlooxtest', + clientid: 127, + platformid: 0, + tagid: 0 + } + }); + +You may optionally pass a subsection `params` in the `params` block to the Adloox RTD Provider, these will be passed through to the segment handler as is and as described by the integration guidelines. + +**N.B.** If you pass `params` to the Adloox Analytics Adapter, `id1` (`AdUnit.code`) and `id2` (`%%pbadslot%%`) *must* describe a stable identifier otherwise no usable segments will be served and so they *must not* be changed; if `id1` for your inventory could contain a non-stable random number please consult with us before continuing + +Though our segment technology is fast (less than 10ms) the time it takes for the users device to connect to our service and fetch the segments may not be. For this reason we recommend setting `auctionDelay` no lower than 100ms and if possible you should explore using user-agent sourced information such as [NetworkInformation.{rtt,downlink,...}](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation) to dynamically tune this for each user. + +## Prebid Ad Slot + +To create reliable segments, a stable description for slots on your inventory needs to be supplied which is typically solved by using the [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html). + +You may use one of two ways to do achieve this: + + * for display inventory [using GPT](https://developers.google.com/publisher-tag/guides/get-started) you may configure Prebid.js to automatically use the [full ad unit path](https://developers.google.com/publisher-tag/reference#googletag.Slot_getAdUnitPath) + 1. include the [`gptPreAuction` module](https://docs.prebid.org/dev-docs/modules/gpt-pre-auction.html) + 1. wrap both `pbjs.setConfig({...})` and `pbjs.enableAnalytics({...})` with `googletag.cmd.push(function() { ... })` + * set `pbadslot` in the [first party data](https://docs.prebid.org/dev-docs/adunit-reference.html#first-party-data) variable `AdUnit.ortb2Imp.ext.data.pbadslot` for all your ad units + +## Timeouts + +It is strongly recommended you increase any [failsafe timeout](https://docs.prebid.org/dev-docs/faq.html#when-starting-out-what-should-my-timeouts-be) you use by at least the value you supply to `auctionDelay` above. + +Adloox recommends you use the following (based on [examples provided on the Prebid.js website](https://docs.prebid.org/dev-docs/examples/basic-example.html)) + + FAILSAFE_TIMEOUT = AUCTION_DELAY + (3 * PREBID_TIMEOUT) diff --git a/test/spec/modules/adlooxAnalyticsAdapter_spec.js b/test/spec/modules/adlooxAnalyticsAdapter_spec.js index 0264922f45e..01ab3afcf2f 100644 --- a/test/spec/modules/adlooxAnalyticsAdapter_spec.js +++ b/test/spec/modules/adlooxAnalyticsAdapter_spec.js @@ -35,7 +35,7 @@ describe('Adloox Analytics Adapter', function () { tagid: 0, params: { dummy1: '%%client%%', - dummy2: '%%pbAdSlot%%', + dummy2: '%%pbadslot%%', dummy3: function(bid) { throw new Error(esplode) } } }; diff --git a/test/spec/modules/adlooxRtdProvider_spec.js b/test/spec/modules/adlooxRtdProvider_spec.js new file mode 100644 index 00000000000..c0438c45451 --- /dev/null +++ b/test/spec/modules/adlooxRtdProvider_spec.js @@ -0,0 +1,313 @@ +import adapterManager from 'src/adapterManager.js'; +import analyticsAdapter from 'modules/adlooxAnalyticsAdapter.js'; +import { config as _config } from 'src/config.js'; +import { expect } from 'chai'; +import events from 'src/events.js'; +import * as prebidGlobal from 'src/prebidGlobal.js'; +import { subModuleObj as rtdProvider } from 'modules/adlooxRtdProvider.js'; +import * as utils from 'src/utils.js'; + +const analyticsAdapterName = 'adloox'; + +describe('Adloox RTD Provider', function () { + let sandbox; + + const adUnit = { + code: 'ad-slot-1', + ortb2Imp: { + ext: { + data: { + pbadslot: '/123456/home/ad-slot-1' + } + } + }, + mediaTypes: { + banner: { + sizes: [ [300, 250] ] + } + }, + bids: [ + { + bidder: 'dummy' + } + ] + }; + + const analyticsOptions = { + js: 'https://j.adlooxtracking.com/ads/js/tfav_adl_%%clientid%%.js', + client: 'adlooxtest', + clientid: 127, + platformid: 0, + tagid: 0 + }; + + const config = {}; + + adapterManager.registerAnalyticsAdapter({ + code: analyticsAdapterName, + adapter: analyticsAdapter + }); + + before(function () { + sandbox = sinon.sandbox.create(); + sandbox.stub(events, 'getEvents').returns([]); + }); + + after(function () { + sandbox.restore(); + sandbox = undefined; + }); + + describe('invalid config', function () { + it('should require config', function (done) { + const ret = rtdProvider.init(); + + expect(ret).is.false; + + done(); + }); + + it('should reject non-object config.params', function (done) { + const ret = rtdProvider.init({ params: null }); + + expect(ret).is.false; + + done(); + }); + + it('should reject non-string config.params.api_origin', function (done) { + const ret = rtdProvider.init({ params: { api_origin: null } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject less than one config.params.imps', function (done) { + const ret = rtdProvider.init({ params: { imps: 0 } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject negative config.params.freqcap_ip', function (done) { + const ret = rtdProvider.init({ params: { freqcap_ip: -1 } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject negative integer config.params.freqcap_ipua', function (done) { + const ret = rtdProvider.init({ params: { freqcap_ipua: -1 } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject non-array of integers with value greater than zero config.params.thresholds', function (done) { + const ret = rtdProvider.init({ params: { thresholds: [ 70, null ] } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject non-boolean config.params.slotinpath', function (done) { + const ret = rtdProvider.init({ params: { slotinpath: null } }); + + expect(ret).is.false; + + done(); + }); + + it('should reject invalid config.params.params (legacy/deprecated)', function (done) { + const ret = rtdProvider.init({ params: { params: { clientid: 0, tagid: 0 } } }); + + expect(ret).is.false; + + done(); + }); + }); + + describe('process segments', function () { + before(function () { + adapterManager.enableAnalytics({ + provider: analyticsAdapterName, + options: analyticsOptions + }); + expect(analyticsAdapter.context).is.not.null; + }); + + after(function () { + analyticsAdapter.disableAnalytics(); + expect(analyticsAdapter.context).is.null; + }); + + let server = null; + let __config = null, CONFIG = null; + let getConfigStub, setConfigStub; + beforeEach(function () { + server = sinon.createFakeServer(); + __config = {}; + CONFIG = utils.deepClone(config); + getConfigStub = sinon.stub(_config, 'getConfig').callsFake(function (path) { + return utils.deepAccess(__config, path); + }); + setConfigStub = sinon.stub(_config, 'setConfig').callsFake(function (obj) { + utils.mergeDeep(__config, obj); + }); + }); + afterEach(function () { + setConfigStub.restore(); + getConfigStub.restore(); + getConfigStub = setConfigStub = undefined; + CONFIG = null; + __config = null; + server.restore(); + server = null; + }); + + it('should fetch segments', function (done) { + const adUnitWithSegments = utils.deepClone(adUnit); + const getGlobalStub = sinon.stub(prebidGlobal, 'getGlobal').returns({ + adUnits: [ adUnitWithSegments ] + }); + + const ret = rtdProvider.init(CONFIG); + expect(ret).is.true; + + const callback = function () { + expect(__config.ortb2.site.ext.data.adloox_rtd.ok).is.true; + expect(__config.ortb2.site.ext.data.adloox_rtd.nope).is.undefined; + expect(__config.ortb2.user.ext.data.adloox_rtd.unused).is.false; + expect(__config.ortb2.user.ext.data.adloox_rtd.nope).is.undefined; + expect(adUnitWithSegments.ortb2Imp.ext.data.adloox_rtd.dis.length).is.equal(3); + expect(adUnitWithSegments.ortb2Imp.ext.data.adloox_rtd.nope).is.undefined; + + getGlobalStub.restore(); + + done(); + }; + rtdProvider.getBidRequestData({}, callback, CONFIG, null); + + const request = server.requests[0]; + const response = { unused: false, _: [ { d: 77 } ] }; + request.respond(200, { 'content-type': 'application/json' }, JSON.stringify(response)); + }); + + it('should set ad server targeting', function (done) { + utils.deepSetValue(__config, 'ortb2.site.ext.data.adloox_rtd.ok', true); + + const adUnitWithSegments = utils.deepClone(adUnit); + utils.deepSetValue(adUnitWithSegments, 'ortb2Imp.ext.data.adloox_rtd.dis', [ 50, 60 ]); + const getGlobalStub = sinon.stub(prebidGlobal, 'getGlobal').returns({ + adUnits: [ adUnitWithSegments ] + }); + + const targetingData = rtdProvider.getTargetingData([ adUnitWithSegments.code ], CONFIG); + expect(Object.keys(targetingData).length).is.equal(1); + expect(Object.keys(targetingData[adUnit.code]).length).is.equal(2); + expect(targetingData[adUnit.code].adl_ok).is.equal(1); + expect(targetingData[adUnit.code].adl_dis.length).is.equal(2); + + getGlobalStub.restore(); + + done(); + }); + }); + + describe('measure atf', function () { + const adUnitCopy = utils.deepClone(adUnit); + + const ratio = 0.38; + const [ [width, height] ] = utils.getAdUnitSizes(adUnitCopy); + + before(function () { + adapterManager.enableAnalytics({ + provider: analyticsAdapterName, + options: analyticsOptions + }); + expect(analyticsAdapter.context).is.not.null; + }); + + after(function () { + analyticsAdapter.disableAnalytics(); + expect(analyticsAdapter.context).is.null; + }); + + it(`should return ${ratio} for same-origin`, function (done) { + const el = document.createElement('div'); + el.setAttribute('id', adUnitCopy.code); + + const offset = height * ratio; + const elStub = sinon.stub(el, 'getBoundingClientRect').returns({ + top: 0 - (height - offset), + bottom: height - offset, + left: 0, + right: width + }); + + const querySelectorStub = sinon.stub(document, 'querySelector'); + querySelectorStub.withArgs(`#${adUnitCopy.code}`).returns(el); + + rtdProvider.atf(adUnitCopy, function(x) { + expect(x).is.equal(ratio); + + querySelectorStub.restore(); + elStub.restore(); + + done(); + }); + }); + + ('IntersectionObserver' in window ? it : it.skip)(`should return ${ratio} for cross-origin`, function (done) { + const frameElementStub = sinon.stub(window, 'frameElement').value(null); + + const el = document.createElement('div'); + el.setAttribute('id', adUnitCopy.code); + + const elStub = sinon.stub(el, 'getBoundingClientRect').returns({ + top: 0, + bottom: height, + left: 0, + right: width + }); + + const querySelectorStub = sinon.stub(document, 'querySelector'); + querySelectorStub.withArgs(`#${adUnitCopy.code}`).returns(el); + + let intersectionObserverStubFn = null; + const intersectionObserverStub = sinon.stub(window, 'IntersectionObserver').callsFake((fn) => { + intersectionObserverStubFn = fn; + return { + observe: (element) => { + expect(element).is.equal(el); + + intersectionObserverStubFn([{ + target: element, + intersectionRect: { width, height }, + intersectionRatio: ratio + }]); + }, + unobserve: (element) => { + expect(element).is.equal(el); + } + } + }); + + rtdProvider.atf(adUnitCopy, function(x) { + expect(x).is.equal(ratio); + + intersectionObserverStub.restore(); + querySelectorStub.restore(); + elStub.restore(); + frameElementStub.restore(); + + done(); + }); + }); + }); +}); From 343bae2334ea938049a58b874a19910e685710ff Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 1 Sep 2021 13:45:44 -0400 Subject: [PATCH 1433/1476] Prebid 5.12.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35c7c304230..308a529a671 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.12.0-pre", + "version": "5.12.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 68c2a4d83d145d35c04c1cb549b55e79bcbef933 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Wed, 1 Sep 2021 14:28:04 -0400 Subject: [PATCH 1434/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 308a529a671..e9ac6d496dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.12.0", + "version": "5.13.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 253600d5839adb8e13a5d1bfbfec12f09ee4df0b Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 1 Sep 2021 17:49:25 -0700 Subject: [PATCH 1435/1476] Finteza Analytics Adapter: bugfix for flaky test (Issue #7348) (#7356) * Testing if Another Adapter is the Issue * researching error * add time to test latency * add this * fix undefined * move length check after other checks * fix linting * move other length test * update other length check to test --- test/spec/modules/fintezaAnalyticsAdapter_spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index 4c76f79f518..54d1a7e7976 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -119,11 +119,11 @@ describe('finteza analytics adapter', function () { // Emit the events with the "real" arguments events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); - expect(server.requests.length).to.equal(2); - expect(server.requests[0].method).to.equal('GET'); expect(server.requests[0].withCredentials).to.equal(true); + expect(server.requests.length).to.equal(2); + let url = parseUrl(server.requests[0].url); expect(url.protocol).to.equal('https'); @@ -173,11 +173,11 @@ describe('finteza analytics adapter', function () { // Emit the events with the "real" arguments events.emit(constants.EVENTS.BID_WON, bidWon); - expect(server.requests.length).to.equal(1); - expect(server.requests[0].method).to.equal('GET'); expect(server.requests[0].withCredentials).to.equal(true); + expect(server.requests.length).to.equal(1); + const url = parseUrl(server.requests[0].url); expect(url.protocol).to.equal('https'); @@ -212,11 +212,11 @@ describe('finteza analytics adapter', function () { // Emit the events with the "real" arguments events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); - expect(server.requests.length).to.equal(1); - expect(server.requests[0].method).to.equal('GET'); expect(server.requests[0].withCredentials).to.equal(true); + expect(server.requests.length).to.equal(1); + const url = parseUrl(server.requests[0].url); expect(url.protocol).to.equal('https'); From b3c091613ec29967f2102f0ca8c4e4c0cbf18c49 Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 2 Sep 2021 08:25:54 -0400 Subject: [PATCH 1436/1476] IX Adapter: buildRequests refactor (#7364) * buildRequests refactor * remove use of Array.includes Co-authored-by: Love Sharma Co-authored-by: Kajan Umakanthan --- modules/ixBidAdapter.js | 180 ++++++++++++++++--------- test/spec/modules/ixBidAdapter_spec.js | 29 +--- 2 files changed, 116 insertions(+), 93 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 2d7157bc674..ab9c995aa7c 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -4,6 +4,7 @@ import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -108,8 +109,8 @@ function bidToVideoImp(bid) { const imp = bidToImp(bid); const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video'); const videoParamRef = utils.deepAccess(bid, 'params.video'); - - if (!checkVideoParams(bid, videoAdUnitRef, videoParamRef)) { + const videoParamErrors = checkVideoParams(videoAdUnitRef, videoParamRef); + if (videoParamErrors.length) { return {}; } @@ -305,7 +306,7 @@ function isValidSize(size) { * @return {boolean} True if the size object is an element of the size array, and false * otherwise. */ -function includesSize(sizeArray = [], size) { +function includesSize(sizeArray = [], size = []) { if (isValidSize(sizeArray)) { return sizeArray[0] === size[0] && sizeArray[1] === size[1]; } @@ -319,13 +320,12 @@ function includesSize(sizeArray = [], size) { /** * Checks if all required video params are present - * @param {object} bid Bid Object * @param {object} mediaTypeVideoRef Ad unit level mediaTypes object * @param {object} paramsVideoRef IX bidder params level video object - * @returns bool Are the required video params available + * @returns {string[]} Are the required video params available */ -function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { - let reqParamsPresent = true; +function checkVideoParams(mediaTypeVideoRef, paramsVideoRef) { + const errorList = []; if (!mediaTypeVideoRef) { utils.logWarn('IX Bid Adapter: mediaTypes.video is the preferred location for video params in ad unit'); @@ -336,23 +336,21 @@ function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { const propInVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty(property); if (!propInMediaType && !propInVideoRef) { - utils.logError(`IX Bid Adapter: ${property} is not included in either the adunit or params level`); - reqParamsPresent = false; + errorList.push(`IX Bid Adapter: ${property} is not included in either the adunit or params level`); } } - // early return - if (!reqParamsPresent) { - return false; - } - // check protocols/protocol const protocolMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocol'); const protocolsMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocols'); const protocolVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocol'); const protocolsVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocols'); - return protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef; + if (!(protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef)) { + errorList.push('IX Bid Adapter: protocol/protcols is not included in either the adunit or params level'); + } + + return errorList; } /** @@ -825,6 +823,66 @@ function removeFromSizes(bannerSizeList, bannerSize) { } } +/** + * Creates IX Video impressions based on validBidRequests + * @param {object} validBidRequest valid request provided by prebid + * @param {object} videoImps reference to created video impressions + */ +function createVideoImps(validBidRequest, videoImps) { + const imp = bidToVideoImp(validBidRequest); + if (Object.keys(imp).length != 0) { + videoImps[validBidRequest.transactionId] = {}; + videoImps[validBidRequest.transactionId].ixImps = []; + videoImps[validBidRequest.transactionId].ixImps.push(imp); + } +} + +/** + * Creates IX banner impressions based on validBidRequests + * @param {object} validBidRequest valid request provided by prebid + * @param {object} missingBannerSizes reference to missing banner config sizes + * @param {object} bannerImps reference to created banner impressions + */ +function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { + const DEFAULT_IX_CONFIG = { + detectMissingSizes: true, + }; + + const ixConfig = { ...DEFAULT_IX_CONFIG, ...config.getConfig('ix') }; + + let imp = bidToBannerImp(validBidRequest); + + const bannerSizeDefined = includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), utils.deepAccess(validBidRequest, 'params.size')); + + // Create IX imps from params.size + if (bannerSizeDefined) { + if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { + bannerImps[validBidRequest.transactionId] = {}; + } + if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { + bannerImps[validBidRequest.transactionId].ixImps = [] + } + bannerImps[validBidRequest.transactionId].ixImps.push(imp); + } + + if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { + updateMissingSizes(validBidRequest, missingBannerSizes, imp); + } +} + +/** + * Determines IX configuration type based on IX params + * @param {object} valid IX configured param + * @returns {string} + */ +function detectParamsType(validBidRequest) { + if (utils.deepAccess(validBidRequest, 'params.video') && utils.deepAccess(validBidRequest, 'mediaTypes.video')) { + return VIDEO; + } + + return BANNER; +} + /** * Updates the Object to track missing banner sizes. * @@ -933,9 +991,15 @@ export const spec = { return false; } } - // For multi format unit - if (!mediaTypeBannerSizes && (mediaTypeVideoRef || paramsVideoRef)) { - return checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef); + + if (mediaTypeVideoRef && paramsVideoRef) { + const errorList = checkVideoParams(mediaTypeVideoRef, paramsVideoRef); + if (errorList.length) { + errorList.forEach((err) => { + utils.logError(err); + }); + return false; + } } return true; }, @@ -948,58 +1012,43 @@ export const spec = { * @return {object} Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - let reqs = []; - let bannerImps = {}; - let videoImps = {}; - let validBidRequest = null; - - // To capture the missing sizes i.e not configured for ix - let missingBannerSizes = {}; + const reqs = []; // Stores banner + video requests + const bannerImps = {}; // Stores created banner impressions + const videoImps = {}; // Stores created video impressions + const multiFormatAdUnits = {}; // Stores references identified multi-format adUnits + const missingBannerSizes = {}; // To capture the missing sizes i.e not configured for ix + + // Step 1: Create impresssions from IX params + validBidRequests.forEach((validBidRequest) => { + const adUnitMediaTypes = Object.keys(utils.deepAccess(validBidRequest, 'mediaTypes', {})) + + switch (detectParamsType(validBidRequest)) { + case BANNER: + createBannerImps(validBidRequest, missingBannerSizes, bannerImps); + break; + case VIDEO: + createVideoImps(validBidRequest, videoImps) + break; + } - const DEFAULT_IX_CONFIG = { - detectMissingSizes: true, - }; + if (includes(adUnitMediaTypes, BANNER) && includes(adUnitMediaTypes, VIDEO)) { + multiFormatAdUnits[validBidRequest.transactionId] = validBidRequest; + } + }); - const ixConfig = { ...DEFAULT_IX_CONFIG, ...config.getConfig('ix') }; - - for (let i = 0; i < validBidRequests.length; i++) { - validBidRequest = validBidRequests[i]; - const videoAdUnitRef = utils.deepAccess(validBidRequest, 'mediaTypes.video'); - const videoParamRef = utils.deepAccess(validBidRequest, 'params.video'); - - // identify video ad unit - if (validBidRequest.mediaType === VIDEO || videoAdUnitRef || videoParamRef) { - if (!videoImps.hasOwnProperty(validBidRequest.transactionId)) { - const imp = bidToVideoImp(validBidRequest); - if (Object.keys(imp).length != 0) { - videoImps[validBidRequest.transactionId] = {}; - videoImps[validBidRequest.transactionId].ixImps = []; - videoImps[validBidRequest.transactionId].ixImps.push(imp); - } - } + // Step 2: Create impressions for multi-format adunits missing configurations + Object.keys(multiFormatAdUnits).forEach((transactionId) => { + const validBidRequest = multiFormatAdUnits[transactionId]; + if (!bannerImps[transactionId]) { + createBannerImps(validBidRequest, missingBannerSizes, bannerImps); } - if (validBidRequest.mediaType === BANNER || - (utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes')) || - (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { - let imp = bidToBannerImp(validBidRequest); - // Create IX imps from params.size - if (utils.deepAccess(validBidRequest, 'params.size')) { - if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { - bannerImps[validBidRequest.transactionId] = {}; - } - if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { - bannerImps[validBidRequest.transactionId].ixImps = [] - } - bannerImps[validBidRequest.transactionId].ixImps.push(imp); - } - if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { - updateMissingSizes(validBidRequest, missingBannerSizes, imp); - } + if (!videoImps[transactionId]) { + createVideoImps(validBidRequest, videoImps) } - } + }); - // Finding the missing banner sizes, and making impressions for them + // Step 3: Update banner impressions with missing sizes for (var transactionId in missingBannerSizes) { if (missingBannerSizes.hasOwnProperty(transactionId)) { let missingSizes = missingBannerSizes[transactionId].missingSizes; @@ -1014,13 +1063,14 @@ export const spec = { let origImp = missingBannerSizes[transactionId].impression; for (let i = 0; i < missingSizes.length; i++) { - let newImp = createMissingBannerImp(validBidRequest, origImp, missingSizes[i]); + let newImp = createMissingBannerImp(validBidRequests[0], origImp, missingSizes[i]); bannerImps[transactionId].missingImps.push(newImp); bannerImps[transactionId].missingCount++; } } } + // Step 4: Build banner & video requests if (Object.keys(bannerImps).length > 0) { reqs.push(...buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 53bfc74af7f..85236571383 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1372,12 +1372,6 @@ describe('IndexexchangeAdapter', function () { const requestWithoutSchain = spec.buildRequests(bidWithoutSchain, DEFAULT_OPTION)[0]; const queryWithoutSchain = requestWithoutSchain.data; - const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID); - delete bidWithoutMediaType[0].mediaTypes; - bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]]; - const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_OPTION)[0]; - const queryWithoutMediaType = requestWithoutMediaType.data; - it('request should be made to IX endpoint with GET method', function () { expect(requestMethod).to.equal('GET'); expect(requestUrl).to.equal(IX_SECURE_ENDPOINT); @@ -1598,27 +1592,6 @@ describe('IndexexchangeAdapter', function () { }); }); - it('payload without mediaType should have correct format and value', function () { - const payload = JSON.parse(queryWithoutMediaType.r); - - expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); - expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); - expect(payload.site.ref).to.equal(document.referrer); - expect(payload.ext.source).to.equal('prebid'); - expect(payload.imp).to.be.an('array'); - expect(payload.imp).to.have.lengthOf(1); - }); - - it('impression without mediaType should have correct format and value', function () { - const impression = JSON.parse(queryWithoutMediaType.r).imp[0]; - - expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner.format).to.be.length(1); - expect(impression.banner.format[0].w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impression.banner.format[0].h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.be.oneOf([0, 1]); - }); - it('impression should have sid if id is configured as number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 50; @@ -2005,7 +1978,7 @@ describe('IndexexchangeAdapter', function () { it('should handle unexpected context', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); - bid.mediaTypes.video.context = 'VaccineJanssen'; + bid.mediaTypes.video.context = 'not-valid'; const request = spec.buildRequests([bid])[0]; const impression = JSON.parse(request.data.r).imp[0]; expect(impression.video.placement).to.be.undefined; From 28202c990a8add61438cdd2784165f18e004b115 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Thu, 2 Sep 2021 15:25:03 +0200 Subject: [PATCH 1437/1476] Onetag Bid Adapter: extend mediaType support (#7363) * add support for all mediaType fields * fix test unit Co-authored-by: francesco --- modules/onetagBidAdapter.js | 9 +++------ test/spec/modules/onetagBidAdapter_spec.js | 7 ++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index aab68176d87..7a0b78ceeff 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -7,6 +7,7 @@ import find from 'core-js-pure/features/array/find.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'; const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; @@ -239,15 +240,10 @@ function requestsToBids(bidRequests) { // Pass parameters // Context: instream - outstream - adpod videoObj['context'] = bidRequest.mediaTypes.video.context; - // MIME Video Types - videoObj['mimes'] = bidRequest.mediaTypes.video.mimes; // Sizes videoObj['playerSize'] = parseVideoSize(bidRequest); // Other params - videoObj['protocols'] = bidRequest.mediaTypes.video.protocols; - videoObj['maxDuration'] = bidRequest.mediaTypes.video.maxduration; - videoObj['api'] = bidRequest.mediaTypes.video.api; - videoObj['playbackmethod'] = bidRequest.mediaTypes.video.playbackmethod || []; + videoObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.video); videoObj['type'] = VIDEO; return videoObj; }); @@ -256,6 +252,7 @@ function requestsToBids(bidRequests) { setGeneralInfo.call(bannerObj, bidRequest); bannerObj['sizes'] = parseSizes(bidRequest); bannerObj['type'] = BANNER; + bannerObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.banner); return bannerObj; }); return videoBidRequests.concat(bannerBidRequests); diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 9fb73f7774b..e873597ca15 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -191,12 +191,8 @@ describe('onetag', function () { 'pubId', 'transactionId', 'context', - 'mimes', 'playerSize', - 'protocols', - 'maxDuration', - 'api', - 'playbackmethod', + 'mediaTypeInfo', 'type' ); } else if (isValid(BANNER, bid)) { @@ -207,6 +203,7 @@ describe('onetag', function () { 'bidderRequestId', 'pubId', 'transactionId', + 'mediaTypeInfo', 'sizes', 'type' ); From 697abe06a4b63bc64635688a69beda084d2dd7e6 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 2 Sep 2021 19:09:55 +0200 Subject: [PATCH 1438/1476] Impactify Bid Adapter: add userid schain support (#7377) * Update for Prebid 5.X * Update to Prebid 5.X * Add support for UserID and Schain Modules. * Remove ESL-lint for no console * Add the UserID in test --- modules/impactifyBidAdapter.js | 17 +++++++++++++++-- test/spec/modules/impactifyBidAdapter_spec.js | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 8809d853dd9..5b0b1dff048 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -2,6 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import {ajax} from '../src/ajax.js'; +import { createEidsArray } from './userId/eids.js'; const BIDDER_CODE = 'impactify'; const BIDDER_ALIAS = ['imp']; @@ -32,7 +33,8 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => { id: bidderRequest.auctionId, validBidRequests, cur: [DEFAULT_CURRENCY], - imp: [] + imp: [], + source: {tid: bidderRequest.auctionId} }; // Force impactify debugging parameter @@ -40,6 +42,17 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => { request.test = Number(window.localStorage.getItem('_im_db_bidder')); } + // Set Schain in request + let schain = utils.deepAccess(validBidRequests, '0.schain'); + if (schain) request.source.ext = { schain: schain }; + + // Set eids + let bidUserId = utils.deepAccess(validBidRequests, '0.userId'); + let eids = createEidsArray(bidUserId); + if (eids.length) { + utils.deepSetValue(request, 'user.ext.eids', eids); + } + // Set device/user/site if (!request.device) request.device = {}; if (!request.site) request.site = {}; @@ -106,7 +119,7 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: ['video'], + supportedMediaTypes: ['video', 'banner'], aliases: BIDDER_ALIAS, /** diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js index 8bc02d9afce..8bb2d089ad8 100644 --- a/test/spec/modules/impactifyBidAdapter_spec.js +++ b/test/spec/modules/impactifyBidAdapter_spec.js @@ -135,7 +135,21 @@ describe('ImpactifyAdapter', function () { bidId: '123456789', bidderRequestId: '987654321', auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', - transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' + transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002', + userId: { + pubcid: '87a0327b-851c-4bb3-a925-0c7be94548f5' + }, + userIdAsEids: [ + { + source: 'pubcid.org', + uids: [ + { + id: '87a0327b-851c-4bb3-a925-0c7be94548f5', + atype: 1 + } + ] + } + ] } ]; let videoBidderRequest = { From 9945c02fa5063864e57ab8d702e13863889ce372 Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Fri, 3 Sep 2021 22:08:31 +0200 Subject: [PATCH 1439/1476] VIS.X Bid Adapter: migrate from GET to POSTs & send additional userIDs as an EIDS object (#7328) * VIS.X: migrate from GET to POSTs & send additional userIDs * VIS.X: fix tests --- modules/visxBidAdapter.js | 344 +++++++++---------- test/spec/modules/visxBidAdapter_spec.js | 405 +++++++++++++++-------- 2 files changed, 414 insertions(+), 335 deletions(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 535beb8af3c..2677b970dfd 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -3,19 +3,21 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { INSTREAM as VIDEO_INSTREAM } from '../src/video.js'; -const { parseSizesInput, getKeys, logError, deepAccess } = utils; +const { parseSizesInput, logError, deepAccess } = utils; const BIDDER_CODE = 'visx'; const GVLID = 154; const BASE_URL = 'https://t.visx.net'; -const ENDPOINT_URL = BASE_URL + '/hb'; +const DEBUG_URL = 'https://t-stage.visx.net'; +const ENDPOINT_PATH = '/hb_post'; const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; -const ADAPTER_SYNC_URL = BASE_URL + '/push_sync'; -const TRACK_TIMEOUT_URL = BASE_URL + '/track/bid_timeout'; +const ADAPTER_SYNC_PATH = '/push_sync'; +const TRACK_TIMEOUT_PATH = '/track/bid_timeout'; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', noBid: 'Array of bid objects is empty', + noImpId: 'Bid from response has no impid parameter - ', noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', emptyUids: 'Uids should not be empty', emptySeatbid: 'Seatbid array from response has an empty item', @@ -28,7 +30,6 @@ const LOG_ERROR_MESS = { videoMissing: 'Bid request videoType property is missing - ' }; const currencyWhiteList = ['EUR', 'USD', 'GBP', 'PLN']; -const RE_EMPTY_OR_ONLY_COMMAS = /^,*$/; export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -47,89 +48,53 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const auids = []; const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; const bids = validBidRequests || []; const currency = config.getConfig(`currency.bidderCurrencyDefault.${BIDDER_CODE}`) || config.getConfig('currency.adServerCurrency') || DEFAULT_CUR; + let reqId; let payloadSchain; let payloadUserId; - const videoTypes = _initVideoTypes(bids); + let payloadUserEids; + let timeout; if (currencyWhiteList.indexOf(currency) === -1) { logError(LOG_ERROR_MESS.notAllowedCurrency + currency); return; } + const imp = []; + bids.forEach(bid => { reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode, schain, userId} = bid; - auids.push(uid); + + const impObj = buildImpObject(bid); + if (impObj) { + imp.push(impObj); + bidsMap[bid.bidId] = bid; + } + + const { params: { uid }, schain, userId, userIdAsEids } = bid; + if (!payloadSchain && schain) { payloadSchain = schain; } - if (!payloadUserId && userId) { - payloadUserId = userId; + if (!payloadUserEids && userIdAsEids) { + payloadUserEids = userIdAsEids; } - const sizesId = parseSizesInput(bid.sizes); - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); + if (!payloadUserId && userId) { + payloadUserId = userId; } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); + auids.push(uid); }); - const payload = { - pt: 'net', - auids: auids.join(','), - sizes: getKeys(sizeMap).join(','), - r: reqId, - cur: currency, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$', - ...videoTypes - }; - - if (payloadSchain) { - payload.schain = JSON.stringify(payloadSchain); - } - - if (payloadUserId) { - if (payloadUserId.tdid) { - payload.tdid = payloadUserId.tdid; - } - if (payloadUserId.id5id && payloadUserId.id5id.uid) { - payload.id5 = payloadUserId.id5id.uid; - } - if (payloadUserId.digitrustid && payloadUserId.digitrustid.data && payloadUserId.digitrustid.data.id) { - payload.dtid = payloadUserId.digitrustid.data.id; - } - } + const payload = {}; if (bidderRequest) { + timeout = bidderRequest.timeout; if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { payload.u = bidderRequest.refererInfo.referer; } @@ -143,19 +108,50 @@ export const spec = { } } + const bidderTimeout = Number(config.getConfig('bidderTimeout')) || timeout; + const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + const source = { + ext: { + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$', + ...(payloadSchain && { schain: payloadSchain }) + } + }; + const user = { + ext: { + ...(payloadUserEids && { eids: payloadUserEids }), + ...(payload.gdpr_consent && { consent: payload.gdpr_consent }) + } + }; + const regs = ('gdpr_applies' in payload) && { + ext: { + gdpr: payload.gdpr_applies + } + }; + + const request = { + id: reqId, + imp, + tmax, + cur: [currency], + source, + site: { page: payload.u }, + ...(Object.keys(user.ext).length && { user }), + ...(regs && { regs }) + }; + return { - method: 'GET', - url: ENDPOINT_URL, - data: payload, - bidsMap: bidsMap, + method: 'POST', + url: buildUrl(ENDPOINT_PATH) + '?auids=' + encodeURIComponent(auids.join(',')), + data: request, + bidsMap }; }, interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; - const bidsWithoutSizeMatching = []; const bidsMap = bidRequest.bidsMap; - const currency = bidRequest.data.cur; + const currency = bidRequest.data.cur[0]; let errorMessage; @@ -166,10 +162,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, currency, bidResponses, bidsWithoutSizeMatching); - }); - bidsWithoutSizeMatching.forEach(serverBid => { - _addBidResponse(serverBid, bidsMap, currency, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, currency, bidResponses); }); } if (errorMessage) logError(errorMessage); @@ -188,7 +181,7 @@ export const spec = { } return [{ type: 'image', - url: ADAPTER_SYNC_URL + (query.length ? '?' + query.join('&') : '') + url: buildUrl(ADAPTER_SYNC_PATH) + (query.length ? '?' + query.join('&') : '') }]; } }, @@ -206,10 +199,61 @@ export const spec = { }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data - utils.triggerPixel(TRACK_TIMEOUT_URL + '?data=' + JSON.stringify(timeoutData)); + utils.triggerPixel(buildUrl(TRACK_TIMEOUT_PATH) + '?data=' + JSON.stringify(timeoutData)); } }; +function buildUrl(path) { + return (config.getConfig('devMode') ? DEBUG_URL : BASE_URL) + path; +} + +function makeBanner(bannerParams) { + const bannerSizes = bannerParams && bannerParams.sizes; + if (bannerSizes) { + const sizes = utils.parseSizesInput(bannerSizes); + if (sizes.length) { + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + return { format }; + } + } +} + +function makeVideo(videoParams = {}) { + const video = Object.keys(videoParams).filter((param) => param !== 'context' && param !== 'playerSize') + .reduce((result, param) => { + result[param] = videoParams[param]; + return result; + }, { w: utils.deepAccess(videoParams, 'playerSize.0.0'), h: utils.deepAccess(videoParams, 'playerSize.0.1') }); + + if (video.w && video.h && video.mimes) { + return video; + } +} + +function buildImpObject(bid) { + const { params: { uid }, bidId, mediaTypes, sizes } = bid; + const video = mediaTypes && _isVideoBid(bid) && _isValidVideoBid(bid) && makeVideo(mediaTypes.video); + const banner = makeBanner((mediaTypes && mediaTypes.banner) || (!video && { sizes })); + const impObject = { + id: bidId, + ...(banner && { banner }), + ...(video && { video }), + ext: { + bidder: { uid: Number(uid) }, + } + }; + + if (impObject.ext.bidder.uid && (impObject.banner || impObject.video)) { + return impObject; + } +} + function _getBidFromResponse(respItem) { if (!respItem) { logError(LOG_ERROR_MESS.emptySeatbid); @@ -221,69 +265,47 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithoutSizeMatching) { +function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); + if (!serverBid.impid) errorMessage = LOG_ERROR_MESS.noImpId + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { const reqCurrency = currency || DEFAULT_CUR; - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { + const bid = bidsMap[serverBid.impid]; + if (bid) { if (serverBid.cur && serverBid.cur !== reqCurrency) { errorMessage = LOG_ERROR_MESS.currencyMismatch + reqCurrency + ' - ' + serverBid.cur; } else { - const sizeId = bidsWithoutSizeMatching ? `${serverBid.w}x${serverBid.h}` : Object.keys(awaitingBids)[0]; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - const bidResponse = { - requestId: bid.bidId, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, - currency: reqCurrency, - netRevenue: true, - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid, - meta: { - advertiserDomains: serverBid.advertiserDomains ? serverBid.advertiserDomains : [], - mediaType: serverBid.mediaType - }, - }; - - if (serverBid.ext && serverBid.ext.prebid) { - bidResponse.ext = serverBid.ext.prebid; - } - - if (!_isVideoInstreamBid(bid)) { - bidResponse.ad = serverBid.adm; - } else { - bidResponse.vastXml = serverBid.adm; - bidResponse.mediaType = 'video'; - } - - bidResponses.push(bidResponse); - - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!getKeys(parent).length) { - delete bidsMap[uid]; - } - } - }); - } + const bidResponse = { + requestId: bid.bidId, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, + currency: reqCurrency, + netRevenue: true, + ttl: TIME_TO_LIVE, + dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.advertiserDomains ? serverBid.advertiserDomains : [], + mediaType: serverBid.mediaType + }, + }; + + if (serverBid.ext && serverBid.ext.prebid) { + bidResponse.ext = serverBid.ext.prebid; + } + + if (!_isVideoInstreamBid(bid)) { + bidResponse.ad = serverBid.adm; } else { - bidsWithoutSizeMatching && bidsWithoutSizeMatching.push(serverBid); + bidResponse.vastXml = serverBid.adm; + bidResponse.mediaType = 'video'; } + + bidResponses.push(bidResponse); } } else { errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; @@ -336,74 +358,4 @@ function _isValidVideoBid(bid, logErrors = false) { return result; } -function _initVideoTypes(bids) { - const result = {}; - let _playerSize = []; - let _protocols = []; - let _api = []; - let _mimes = []; - let _minduration = []; - let _maxduration = []; - let _skip = []; - if (bids && bids.length) { - bids.forEach(function (bid) { - const mediaTypes = deepAccess(bid, 'mediaTypes.video', {}); - let bidPlayerSize = ''; - let bidProtocols = ''; - let bidApi = ''; - let bidMimes = ''; - let bidMinduration = null; - let bidMaxduration = null; - let bidSkip = null; - if (_isVideoBid(bid) && _isValidVideoBid(bid)) { - bidPlayerSize = parseSizesInput(deepAccess(mediaTypes, 'playerSize', [])).join('|'); - bidProtocols = deepAccess(mediaTypes, 'protocols', []).join('|'); - bidApi = deepAccess(mediaTypes, 'api', []).join('|'); - bidMimes = deepAccess(mediaTypes, 'mimes', []).join('|'); - bidMinduration = deepAccess(mediaTypes, 'minduration', null); - bidMaxduration = deepAccess(mediaTypes, 'maxduration', null); - bidSkip = deepAccess(mediaTypes, 'skip', null); - } - _playerSize.push(bidPlayerSize); - _protocols.push(bidProtocols); - _api.push(bidApi); - _mimes.push(bidMimes); - _minduration.push(bidMinduration); - _maxduration.push(bidMaxduration); - _skip.push(bidSkip); - }); - } - _playerSize = _playerSize.join(','); - _protocols = _protocols.join(','); - _api = _api.join(','); - _mimes = _mimes.join(','); - _minduration = _minduration.join(','); - _maxduration = _maxduration.join(','); - _skip = _skip.join(','); - - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_playerSize)) { - result.playerSize = _playerSize; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_protocols)) { - result.protocols = _protocols; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_api)) { - result.api = _api; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_mimes)) { - result.mimes = _mimes; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_minduration)) { - result.minduration = _minduration; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_maxduration)) { - result.maxduration = _maxduration; - } - if (!RE_EMPTY_OR_ONLY_COMMAS.test(_skip)) { - result.skip = _skip; - } - - return result; -} - registerBidder(spec); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 4a8207768ab..74ab959b5f2 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -44,7 +44,7 @@ describe('VisxAdapter', function () { videoBid.mediaTypes = { video: { context: 'instream', - playerSize: [400, 300] + playerSize: [[400, 300]] } }; expect(spec.isBidRequestValid(videoBid)).to.equal(false); @@ -55,7 +55,7 @@ describe('VisxAdapter', function () { videoBid.mediaTypes = { video: { context: 'instream', - playerSize: [400, 300], + playerSize: [[400, 300]], mimes: ['video/mp4'], protocols: [3, 6] } @@ -65,7 +65,16 @@ describe('VisxAdapter', function () { }); describe('buildRequests', function () { + function parseRequest(url) { + const res = {}; + (url.split('?')[1] || '').split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } const bidderRequest = { + timeout: 3000, refererInfo: { referer: 'https://example.com' } @@ -78,12 +87,11 @@ describe('VisxAdapter', function () { {asi: 'exchange1.com', sid: '1234!abcd', hp: 1, name: 'publisher, Inc.', domain: 'publisher.com'} ] }; - const schainString = JSON.stringify(schainObject); let bidRequests = [ { 'bidder': 'visx', 'params': { - 'uid': '903535' + 'uid': 903535 }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -94,7 +102,7 @@ describe('VisxAdapter', function () { { 'bidder': 'visx', 'params': { - 'uid': '903535' + 'uid': 903535 }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90], [300, 250]], @@ -105,7 +113,7 @@ describe('VisxAdapter', function () { { 'bidder': 'visx', 'params': { - 'uid': '903536' + 'uid': 903536 }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -116,13 +124,13 @@ describe('VisxAdapter', function () { { 'bidder': 'visx', 'params': { - 'uid': '903537' + 'uid': 903537 }, 'adUnitCode': 'adunit-code-video-3', 'mediaTypes': { 'video': { 'context': 'instream', - 'playerSize': [400, 300], + 'playerSize': [[400, 300]], 'mimes': ['video/mp4', 'video/mpeg'], 'protocols': [3, 6], 'minduration': 5, @@ -135,83 +143,91 @@ describe('VisxAdapter', function () { } ]; + const expectedFullImps = [{ + 'id': '30b31c1838de1e', + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, + 'ext': {'bidder': {'uid': 903535}} + }, + { + 'id': '3150ccb55da321', + 'banner': {'format': [{'w': 728, 'h': 90}, {'w': 300, 'h': 250}]}, + 'ext': {'bidder': {'uid': 903535}} + }, + { + 'id': '42dbe3a7168a6a', + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, + 'ext': {'bidder': {'uid': 903536}} + }, + { + 'id': '39a4e3a7168a6a', + 'video': { + 'w': 400, + 'h': 300, + 'mimes': ['video/mp4', 'video/mpeg'], + 'protocols': [3, 6], + 'minduration': 5, + 'maxduration': 30 + }, + 'ext': {'bidder': {'uid': 903537}} + }]; + it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); + const firstBid = bidRequests[0]; + const bids = [firstBid]; + const request = spec.buildRequests(bids, bidderRequest); + const payload = parseRequest(request.url); + expect(request.url).to.be.an('string'); expect(payload).to.have.property('auids', '903535'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); - }); - it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': [expectedFullImps[0]], + 'tmax': 3000, + 'cur': ['EUR'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer} + }); }); - it('pt parameter must be "net" if params.priceType === "gross"', function () { - bidRequests[1].params.priceType = 'gross'; + it('should attach valid params to the tag with multiformat request', function () { const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; + const payload = parseRequest(request.url); expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); - delete bidRequests[1].params.priceType; - }); - it('pt parameter must be "net" if params.priceType === "net"', function () { - bidRequests[1].params.priceType = 'net'; - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); - delete bidRequests[1].params.priceType; - }); - it('pt parameter must be "net" if params.priceType === "undefined"', function () { - bidRequests[1].params.priceType = 'undefined'; - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); - delete bidRequests[1].params.priceType; + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['EUR'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer} + }); }); it('should add currency from currency.bidderCurrencyDefault', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'currency.bidderCurrencyDefault.visx' ? 'GBP' : 'USD'); const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; + const payload = parseRequest(request.url); expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'GBP'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['GBP'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer} + }); + getConfigStub.restore(); }); @@ -219,39 +235,73 @@ describe('VisxAdapter', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'currency.bidderCurrencyDefault.visx' ? '' : 'USD'); const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; + const payload = parseRequest(request.url); expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536,903537'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'USD'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['USD'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer} + }); + getConfigStub.restore(); }); it('if gdprConsent is present payload must have gdpr params', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + const request = spec.buildRequests(bidRequests, Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest)); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['EUR'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer}, + 'user': {'ext': {'consent': 'AAA'}}, + 'regs': {'ext': {'gdpr': 1}} + }); }); it('if gdprApplies is false gdpr_applies must be 0', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 0); + const request = spec.buildRequests(bidRequests, Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest)); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['EUR'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer}, + 'user': {'ext': {'consent': 'AAA'}}, + 'regs': {'ext': {'gdpr': 0}} + }); }); it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + const request = spec.buildRequests(bidRequests, Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest)); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['EUR'], + 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}}, + 'site': {'page': referrer}, + 'user': {'ext': {'consent': 'AAA'}}, + 'regs': {'ext': {'gdpr': 1}} + }); }); it('if schain is present payload must have schain param', function () { @@ -261,49 +311,110 @@ describe('VisxAdapter', function () { bidRequests[2] ]; const request = spec.buildRequests(schainBidRequests, bidderRequest); - const payload = request.data; + const payload = parseRequest(request.url); expect(payload).to.be.an('object'); - expect(payload).to.have.property('schain', schainString); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'EUR'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps.slice(0, -1), + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$', + 'schain': schainObject + } + }, + 'site': {'page': referrer}, + }); }); it('if userId is available payload must have appropriate params', function () { - const schainBidRequests = [ + const eids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { + source: 'adserver.org', + uids: [{ + id: 'some-random-id-value', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + } + ]; + const userIdBidRequests = [ Object.assign({userId: { tdid: '111', id5id: { uid: '222' }, digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} - }}, bidRequests[0]), + }, + userIdAsEids: eids}, bidRequests[0]), bidRequests[1], bidRequests[2] ]; - const request = spec.buildRequests(schainBidRequests, bidderRequest); - const payload = request.data; - expect(payload).to.be.an('object'); - expect(payload).to.have.property('tdid', '111'); - expect(payload).to.have.property('id5', '222'); - expect(payload).to.have.property('dtid', 'DTID'); + const request = spec.buildRequests(userIdBidRequests, bidderRequest); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps.slice(0, -1), + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$' + } + }, + 'site': {'page': referrer}, + 'user': {'ext': {'eids': eids}} + }); }); it('should pass grouped video bid\'s params in payload', function () { const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = request.data; - expect(payload).to.have.property('protocols', ',,,3|6'); - expect(payload).to.have.property('mimes', ',,,video/mp4|video/mpeg'); - expect(payload).to.have.property('playerSize', ',,,400x300'); - expect(payload).to.have.property('minduration', ',,,5'); - expect(payload).to.have.property('maxduration', ',,,30'); - expect(payload).to.not.have.property('skip'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': expectedFullImps, + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$' + } + }, + 'site': {'page': referrer} + }); }); }); describe('buildRequests (multiple media types w/ unsupported video+outstream)', function () { + function parseRequest(url) { + const res = {}; + (url.split('?')[1] || '').split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } const bidderRequest = { + timeout: 3000, refererInfo: { referer: 'https://example.com' } @@ -320,33 +431,49 @@ describe('VisxAdapter', function () { 'mediaTypes': { 'video': { 'context': 'outstream', - 'playerSize': [400, 300] + 'playerSize': [[400, 300]] } }, 'bidId': '39aff3a7169a6a', - 'bidderRequestId': '22edffe2733bf6', + 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a476', } ]; it('should send requst for banner bid', function () { const request = spec.buildRequests([bidRequests[0]], bidderRequest); - const payload = request.data; + const payload = parseRequest(request.url); expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903538'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.not.have.property('playerSize'); + + const postData = request.data; + expect(postData).to.be.an('object'); + expect(postData).to.deep.equal({ + 'id': '22edbae2733bf6', + 'imp': [{ + 'id': '39aff3a7169a6a', + 'banner': {'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}]}, + 'ext': {'bidder': {'uid': 903538}} + }], + 'tmax': 3000, + 'cur': ['EUR'], + 'source': { + 'ext': { + 'wrapperType': 'Prebid_js', + 'wrapperVersion': '$prebid.version$' + } + }, + 'site': {'page': referrer} + }); }); }); describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'impid': '300bfeb0d71a5b', 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '4dff80cc4ee346', 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'impid': '5703af74d0472a', 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0, 'impid': '300bfeb0d7190gf', 'auid': 903537, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, @@ -362,7 +489,7 @@ describe('VisxAdapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', + 'bidId': '300bfeb0d71a5b', 'bidderRequestId': '5f2009617a7c0a', 'auctionId': '1cbd2feafe5e8b', } @@ -370,7 +497,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests); const expectedResponse = [ { - 'requestId': '659423fff799cb', + 'requestId': '300bfeb0d71a5b', 'cpm': 1.15, 'creativeId': 903535, 'dealId': undefined, @@ -492,7 +619,7 @@ describe('VisxAdapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', + 'bidId': '300bfeb0d71a5b', 'bidderRequestId': '5f2009617a7c0a', 'auctionId': '1cbd2feafe5e8b', } @@ -501,7 +628,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests); const expectedResponse = [ { - 'requestId': '659423fff799cb', + 'requestId': '300bfeb0d71a5b', 'cpm': 1.15, 'creativeId': 903535, 'dealId': undefined, @@ -568,11 +695,11 @@ describe('VisxAdapter', function () { it('complicated case', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'impid': '2164be6358b9', 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner', 'advertiserDomains': ['some_domain.com']}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '4e111f1b66e4', 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'impid': '26d6f897b516', 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'impid': '326bde7fbf69', 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '1751cd90161', 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -721,8 +848,8 @@ describe('VisxAdapter', function () { it('dublicate uids and sizes in one slot', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'impid': '5126e301f4be', 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '57b2ebe70e16', 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR', 'mediaType': 'banner'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -801,7 +928,7 @@ describe('VisxAdapter', function () { it('handles video bid', function () { const fullResponse = [ - {'bid': [{'price': 0.5, 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR', 'mediaType': 'video'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '2164be6358b9', 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR', 'mediaType': 'video'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -813,7 +940,7 @@ describe('VisxAdapter', function () { 'mediaTypes': { 'video': { 'context': 'instream', - 'playerSize': [400, 300], + 'playerSize': [[400, 300]], 'mimes': ['video/mp4'], 'protocols': [3, 6] } @@ -850,7 +977,7 @@ describe('VisxAdapter', function () { it('handles multiformat bid response with outstream+banner as banner', function () { const fullResponse = [ - {'bid': [{'price': 0.5, 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR', 'mediaType': 'video'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'impid': '2164be6358b9', 'adm': '', 'auid': 903537, 'w': 400, 'h': 300, 'cur': 'EUR', 'mediaType': 'video'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -862,7 +989,7 @@ describe('VisxAdapter', function () { 'mediaTypes': { 'video': { 'context': 'outstream', - 'playerSize': [400, 300], + 'playerSize': [[400, 300]], 'mimes': ['video/mp4'], 'protocols': [3, 6] } @@ -908,7 +1035,7 @@ describe('VisxAdapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', + 'bidId': '300bfeb0d71a5b', 'bidderRequestId': '5f2009617a7c0a', 'auctionId': '1cbd2feafe5e8b', } @@ -918,7 +1045,7 @@ describe('VisxAdapter', function () { const winUrl = 'https://t.visx.net/track/win/53245341'; const expectedResponse = [ { - 'requestId': '659423fff799cb', + 'requestId': '300bfeb0d71a5b', 'cpm': 1.15, 'creativeId': 903535, 'dealId': undefined, From b029e51b219f36ce444fe702abadab5fd6248f13 Mon Sep 17 00:00:00 2001 From: Noam Tzuberi Date: Mon, 6 Sep 2021 13:26:09 +0300 Subject: [PATCH 1440/1476] Rise Bid Adapter: improve isBidRequestValid and size detection along with other updates (#7362) * 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 * improvments * fixes & extra improcments * fix bug * revert packege-lock.json * rollback getsizes changes * fix * bump Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur --- modules/riseBidAdapter.js | 102 ++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 6887805b854..492653afb95 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -5,7 +5,7 @@ import {config} from '../src/config.js'; const SUPPORTED_AD_TYPES = [VIDEO]; const BIDDER_CODE = 'rise'; -const BIDDER_VERSION = '4.0.1'; +const ADAPTER_VERSION = '5.0.0'; const TTL = 360; const CURRENCY = 'USD'; const SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; @@ -20,10 +20,21 @@ const SUPPORTED_SYNC_METHODS = { export const spec = { code: BIDDER_CODE, - version: BIDDER_VERSION, + gvlid: 1043, + version: ADAPTER_VERSION, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function(bidRequest) { - return !!(bidRequest.params.org); + if (!bidRequest.params) { + utils.logWarn('no params have been set to Rise adapter'); + return false; + } + + if (!bidRequest.params.org) { + utils.logWarn('org is a mandatory param for Rise adapter'); + return false; + } + + return true; }, buildRequests: function (bidRequests, bidderRequest) { if (bidRequests.length === 0) { @@ -101,7 +112,7 @@ function getFloor(bid) { mediaType: VIDEO, size: '*' }); - return floorResult.currency === CURRENCY ? floorResult.floor : 0; + return floorResult.currency === CURRENCY && floorResult.floor ? floorResult.floor : 0; } /** @@ -207,6 +218,27 @@ function getEndpoint(testMode) { : SELLER_ENDPOINT + MODES.PRODUCTION; } +/** + * get device type + * @param uad {ua} + * @returns {string} + */ +function getDeviceType(ua) { + if (/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i + .test(ua.toLowerCase())) { + return '5'; + } + if (/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i + .test(ua.toLowerCase())) { + return '4'; + } + if (/smart[-_\s]?tv|hbbtv|appletv|googletv|hdmi|netcast|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b/i + .test(ua.toLowerCase())) { + return '3'; + } + return '1'; +} + /** * Generate query parameters for the request * @param bid {bid} @@ -214,14 +246,23 @@ function getEndpoint(testMode) { * @returns {Object} */ function generateParameters(bid, bidderRequest) { + const {params} = bid; const timeout = config.getConfig('bidderTimeout'); - const { syncEnabled, filterSettings } = config.getConfig('userSync') || {}; - const [ width, height ] = getSizes(bid); - const { params } = bid; - const { bidderCode } = bidderRequest; + const {syncEnabled, filterSettings} = config.getConfig('userSync') || {}; + const [width, height] = getSizes(bid); + const {bidderCode} = bidderRequest; const domain = window.location.hostname; + // fix floor price in case of NAN + if (isNaN(params.floorPrice)) { + params.floorPrice = 0; + } + const requestParams = { + wrapper_type: 'prebidjs', + wrapper_vendor: '$$PREBID_GLOBAL$$', + wrapper_version: '$prebid.version$', + adapter_version: ADAPTER_VERSION, auction_start: utils.timestamp(), ad_unit_code: utils.getBidIdParameter('adUnitCode', bid), tmax: timeout, @@ -236,9 +277,52 @@ function generateParameters(bid, bidderRequest) { session_id: utils.getBidIdParameter('auctionId', bid), publisher_name: domain, site_domain: domain, - bidder_version: BIDDER_VERSION + dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, + device_type: getDeviceType(navigator.userAgent) }; + const userIdsParam = utils.getBidIdParameter('userId', bid); + if (userIdsParam) { + requestParams.userIds = JSON.stringify(userIdsParam); + } + + const ortb2Metadata = config.getConfig('ortb2') || {}; + if (ortb2Metadata.site) { + requestParams.site_metadata = JSON.stringify(ortb2Metadata.site); + } + if (ortb2Metadata.user) { + requestParams.user_metadata = JSON.stringify(ortb2Metadata.user); + } + + const playbackMethod = utils.deepAccess(bid, 'mediaTypes.video.playbackmethod'); + if (playbackMethod) { + requestParams.playback_method = playbackMethod; + } + const placement = utils.deepAccess(bid, 'mediaTypes.video.placement'); + if (placement) { + requestParams.placement = placement; + } + const pos = utils.deepAccess(bid, 'mediaTypes.video.pos'); + if (pos) { + requestParams.pos = pos; + } + const minduration = utils.deepAccess(bid, 'mediaTypes.video.minduration'); + if (minduration) { + requestParams.min_duration = minduration; + } + const maxduration = utils.deepAccess(bid, 'mediaTypes.video.maxduration'); + if (maxduration) { + requestParams.max_duration = maxduration; + } + const skip = utils.deepAccess(bid, 'mediaTypes.video.skip'); + if (skip) { + requestParams.skip = skip; + } + const linearity = utils.deepAccess(bid, 'mediaTypes.video.linearity'); + if (linearity) { + requestParams.linearity = linearity; + } + if (params.placementId) { requestParams.placement_id = params.placementId; } From 126e16529b4acb98b56a4d07984cfd7864fe36d3 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 7 Sep 2021 15:04:17 +0300 Subject: [PATCH 1441/1476] Adkernel Bid Adapter: unibots alias (#7387) --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 5cfb44f7127..a6bf56f8cad 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -70,7 +70,8 @@ export const spec = { {code: 'converge', gvlid: 248}, {code: 'adomega'}, {code: 'denakop'}, - {code: 'rtbanalytica'} + {code: 'rtbanalytica'}, + {code: 'unibots'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From b4daf00252e7f08d0402334497bbe55a0d198d6d Mon Sep 17 00:00:00 2001 From: SmartyAdsSSP <41569976+SmartyAdsSSP@users.noreply.github.com> Date: Tue, 7 Sep 2021 17:28:04 +0300 Subject: [PATCH 1442/1476] change smartyads ad unit parameters (#7380) --- modules/smartyadsBidAdapter.js | 33 +++++++++++++------ modules/smartyadsBidAdapter.md | 12 +++++-- test/spec/modules/smartyadsBidAdapter_spec.js | 25 +++++++++----- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js index 610617155ed..9c38cb89492 100644 --- a/modules/smartyadsBidAdapter.js +++ b/modules/smartyadsBidAdapter.js @@ -4,8 +4,8 @@ import { config } from '../src/config.js'; import * as utils from '../src/utils.js'; const BIDDER_CODE = 'smartyads'; -const AD_URL = 'https://ssp-nj.webtradehub.com/?c=o&m=multi'; -const URL_SYNC = 'https://ssp-nj.webtradehub.com/?c=o&m=cookie'; +const AD_URL = 'https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js'; +const URL_SYNC = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a'; function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -29,7 +29,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(bid.params.placementId)); + return Boolean(bid.bidId && bid.params && !isNaN(bid.params.sourceid) && !isNaN(bid.params.accountid) && bid.params.host == 'prebid'); }, buildRequests: (validBidRequests = [], bidderRequest) => { @@ -68,10 +68,11 @@ export const spec = { let bid = validBidRequests[i]; let traff = bid.params.traffic || BANNER placements.push({ - placementId: bid.params.placementId, + placementId: bid.params.sourceid, bidId: bid.bidId, sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [], - traffic: traff + traffic: traff, + publisherId: bid.params.accountid }); if (bid.schain) { placements.schain = bid.schain; @@ -96,11 +97,23 @@ export const spec = { return response; }, - getUserSyncs: (syncOptions, serverResponses) => { - return [{ - type: 'image', - url: URL_SYNC - }]; + getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '') => { + let syncs = []; + let { gdprApplies, consentString = '' } = gdprConsent; + + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=iframe&us_privacy=${uspConsent}` + }); + } else { + syncs.push({ + type: 'image', + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=image&us_privacy=${uspConsent}` + }); + } + + return syncs } }; diff --git a/modules/smartyadsBidAdapter.md b/modules/smartyadsBidAdapter.md index 4fe4d51a2e6..f078d905e62 100644 --- a/modules/smartyadsBidAdapter.md +++ b/modules/smartyadsBidAdapter.md @@ -23,7 +23,9 @@ Module that connects to SmartyAds' demand sources { bidder: 'smartyads', params: { - placementId: 0, + host: 'prebid', + sourceid: '0', + accountid: '0', traffic: 'native' } } @@ -41,7 +43,9 @@ Module that connects to SmartyAds' demand sources { bidder: 'smartyads', params: { - placementId: 0, + host: 'prebid', + sourceid: '0', + accountid: '0', traffic: 'banner' } } @@ -60,7 +64,9 @@ Module that connects to SmartyAds' demand sources { bidder: 'smartyads', params: { - placementId: 0, + host: 'prebid', + sourceid: '0', + accountid: '0', traffic: 'video' } } diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 14363329a9e..3474753c838 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -7,17 +7,19 @@ describe('SmartyadsAdapter', function () { bidId: '23fhj33i987f', bidder: 'smartyads', params: { - placementId: 0, + host: 'prebid', + sourceid: '0', + accountid: '0', traffic: 'banner' } }; describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and placementId parameters present', function () { + it('Should return true if there are bidId, params and sourceid 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; + delete bid.params.sourceid; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); @@ -34,7 +36,7 @@ describe('SmartyadsAdapter', function () { expect(serverRequest.method).to.equal('POST'); }); it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://ssp-nj.webtradehub.com/?c=o&m=multi'); + expect(serverRequest.url).to.equal('https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js'); }); it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; @@ -48,8 +50,8 @@ describe('SmartyadsAdapter', function () { expect(data.host).to.be.a('string'); expect(data.page).to.be.a('string'); let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes'); - expect(placement.placementId).to.equal(0); + expect(placement).to.have.keys('placementId', 'bidId', 'traffic', 'sizes', 'publisherId'); + expect(placement.placementId).to.equal('0'); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal('banner'); }); @@ -241,13 +243,18 @@ describe('SmartyadsAdapter', function () { }); }); describe('getUserSyncs', function () { - let userSync = spec.getUserSyncs(); + const syncUrl = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a&gdpr=0&gdpr_consent=&type=iframe&us_privacy='; + const syncOptions = { + iframeEnabled: true + }; + let userSync = spec.getUserSyncs(syncOptions); it('Returns valid URL and type', 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('image'); - expect(userSync[0].url).to.be.equal('https://ssp-nj.webtradehub.com/?c=o&m=cookie'); + expect(userSync).to.deep.equal([ + { type: 'iframe', url: syncUrl } + ]); }); }); }); From 5b87fdcbb094e30fdc7a4e8a777674e530753127 Mon Sep 17 00:00:00 2001 From: PWyrembak Date: Tue, 7 Sep 2021 18:34:03 +0300 Subject: [PATCH 1443/1476] TrustX Bid Adapter: convert all id-like request fields to a string (#7386) --- modules/trustxBidAdapter.js | 8 ++-- test/spec/modules/trustxBidAdapter_spec.js | 45 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 141ed79cfca..d99dbf0cd87 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -95,10 +95,10 @@ export const spec = { } } let impObj = { - id: bidId, + id: bidId && bidId.toString(), tagid: uid.toString(), ext: { - divid: adUnitCode + divid: adUnitCode && adUnitCode.toString() } }; @@ -132,7 +132,7 @@ export const spec = { }); const source = { - tid: auctionId, + tid: auctionId && auctionId.toString(), ext: { wrapper: 'Prebid_js', wrapper_version: '$prebid.version$' @@ -147,7 +147,7 @@ export const spec = { const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; let request = { - id: bidderRequestId, + id: bidderRequestId && bidderRequestId.toString(), site: { page: referer }, diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index a32943eef80..faaae620ad2 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -519,6 +519,51 @@ describe('TrustXAdapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); + it('all id like request fields must be a string', function () { + const bidderRequestWithNumId = Object.assign({}, bidderRequest, { bidderRequestId: 123123, auctionId: 345345543 }); + + let bidRequestWithNumId = { + 'bidder': 'trustx', + 'params': { + 'uid': 43, + }, + 'adUnitCode': 111111, + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'bidId': 23423423, + 'bidderRequestId': 123123, + 'auctionId': 345345543, + }; + + const request = spec.buildRequests([bidRequestWithNumId], bidderRequestWithNumId); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': '123123', + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': '345345543', + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': '23423423', + 'tagid': '43', + 'ext': {'divid': '111111'}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); describe('floorModule', function () { const floorTestData = { From 1ba68883a4aa0b6cc6a59e75d1ba07027589e4a0 Mon Sep 17 00:00:00 2001 From: Eddy Pechuzal <46331062+epechuzal@users.noreply.github.com> Date: Tue, 7 Sep 2021 13:57:53 -0700 Subject: [PATCH 1444/1476] Sharethrough adapter: connect to OpenRTB endpoint (#7290) --- modules/sharethroughBidAdapter.js | 427 ++++---- modules/sharethroughBidAdapter.md | 68 +- .../modules/sharethroughBidAdapter_spec.js | 980 ++++++++---------- 3 files changed, 704 insertions(+), 771 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 967d8194607..24ab2530014 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,123 +1,190 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { createEidsArray } from './userId/eids.js'; -const VERSION = '3.4.1'; +const VERSION = '4.0.0'; const BIDDER_CODE = 'sharethrough'; -const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; -const DEFAULT_SIZE = [1, 1]; +const SUPPLY_ID = 'WYu2BXv1'; + +const STR_ENDPOINT = `https://btlr.sharethrough.com/universal/v1?supply_id=${SUPPLY_ID}`; // this allows stubbing of utility function that is used internally by the sharethrough adapter export const sharethroughInternal = { - b64EncodeUnicode, - handleIframe, - isLockedInFrame, - getProtocol + getProtocol, }; export const sharethroughAdapterSpec = { code: BIDDER_CODE, + supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: bid => !!bid.params.pkey && bid.bidder === BIDDER_CODE, buildRequests: (bidRequests, bidderRequest) => { - return bidRequests.map(bidRequest => { - let query = { - placement_key: bidRequest.params.pkey, - bidId: bidRequest.bidId, - consent_required: false, - instant_play_capable: canAutoPlayHTML5Video(), - hbSource: 'prebid', - hbVersion: '$prebid.version$', - strVersion: VERSION, - }; - - const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); - if (gpid) { - query.gpid = gpid; - } - - Object.assign(query, handleUniversalIds(bidRequest)); - - const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; - query.secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1); - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) { - query.consent_string = bidderRequest.gdprConsent.consentString; - } - - if (bidderRequest && bidderRequest.gdprConsent) { - query.consent_required = !!bidderRequest.gdprConsent.gdprApplies; - } - - if (bidderRequest && bidderRequest.uspConsent) { - query.us_privacy = bidderRequest.uspConsent + const timeout = config.getConfig('bidderTimeout'); + + const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; + const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1); + + const req = { + id: utils.generateUUID(), + at: 1, + cur: ['USD'], + tmax: timeout, + site: { + domain: window.location.hostname, + page: window.location.href, + ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null, + }, + user: { + ext: { + eids: userIdAsEids(bidRequests[0]), + }, + }, + device: { + ua: navigator.userAgent, + language: navigator.language, + js: 1, + dnt: navigator.doNotTrack === '1' ? 1 : 0, + h: window.screen.height, + w: window.screen.width, + }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {}, + }, + source: { + ext: { + version: '$prebid.version$', + str: VERSION, + schain: bidRequests[0].schain, + }, + }, + bcat: bidRequests[0].params.bcat || [], + badv: bidRequests[0].params.badv || [], + test: 0, + }; + + if (bidderRequest.gdprConsent) { + const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true; + req.regs.ext.gdpr = gdprApplies ? 1 : 0; + if (gdprApplies) { + req.user.ext.consent = bidderRequest.gdprConsent.consentString; } + } - if (config.getConfig('coppa') === true) { - query.coppa = true - } + if (bidderRequest.uspConsent) { + req.regs.ext.us_privacy = bidderRequest.uspConsent; + } - if (bidRequest.schain) { - query.schain = JSON.stringify(bidRequest.schain); - } + const imps = bidRequests.map(bidReq => { + const impression = {}; - const floor = getFloor(bidRequest); - if (floor) { - query.bidfloor = floor; + const gpid = utils.deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + impression.ext = { gpid: gpid }; } - if (bidRequest.params.badv) { - query.badv = bidRequest.params.badv; + // if request is for video, we only support instream + if (bidReq.mediaTypes && bidReq.mediaTypes.video && bidReq.mediaTypes.video.context === 'outstream') { + // return null so we can easily remove this imp from the array of imps that we send to adserver + return null; } - if (bidRequest.params.bcat) { - query.bcat = bidRequest.params.bcat; + if (bidReq.mediaTypes && bidReq.mediaTypes.video) { + const videoRequest = bidReq.mediaTypes.video; + + // default playerSize, only change this if we know width and height are properly defined in the request + let [w, h] = [640, 360]; + if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[1]) { + [w, h] = videoRequest.playerSize; + } + + impression.video = { + pos: nullish(videoRequest.pos, 0), + topframe: utils.inIframe() ? 0 : 1, + skip: nullish(videoRequest.skip, 0), + linearity: nullish(videoRequest.linearity, 1), + minduration: nullish(videoRequest.minduration, 5), + maxduration: nullish(videoRequest.maxduration, 60), + playbackmethod: videoRequest.playbackmethod || [2], + api: getVideoApi(videoRequest), + mimes: videoRequest.mimes || ['video/mp4'], + protocols: getVideoProtocols(videoRequest), + w, + h, + startdelay: nullish(videoRequest.startdelay, 0), + skipmin: nullish(videoRequest.skipmin, 0), + skipafter: nullish(videoRequest.skipafter, 0), + }; + + if (videoRequest.placement) impression.video.placement = videoRequest.placement; + if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery; + if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype; + if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad; + } else { + impression.banner = { + pos: utils.deepAccess(bidReq, 'mediaTypes.banner.pos', 0), + topframe: utils.inIframe() ? 0 : 1, + format: bidReq.sizes.map(size => ({ w: +size[0], h: +size[1] })), + }; } - // Data that does not need to go to the server, - // but we need as part of interpretResponse() - const strData = { - skipIframeBusting: bidRequest.params.iframe, - iframeSize: bidRequest.params.iframeSize, - sizes: bidRequest.sizes - }; - return { - method: 'POST', - url: STR_ENDPOINT, - data: query, - strData: strData + id: bidReq.bidId, + tagid: String(bidReq.params.pkey), + secure: secure ? 1 : 0, + bidfloor: getBidRequestFloor(bidReq), + ...impression, }; - }) + }).filter(imp => !!imp); + + return { + method: 'POST', + url: STR_ENDPOINT, + data: { + ...req, + imp: imps, + }, + bidRequests, + bidderRequest, + }; }, interpretResponse: ({ body }, req) => { - if (!body || !body.creatives || !body.creatives.length) { + if (!body || !body.seatbid || body.seatbid.length === 0 || !body.seatbid[0].bid || body.seatbid[0].bid.length === 0) { return []; } - const creative = body.creatives[0]; - let size = DEFAULT_SIZE; - if (req.strData.iframeSize || req.strData.sizes.length) { - size = req.strData.iframeSize - ? req.strData.iframeSize - : getLargestSize(req.strData.sizes); - } + return body.seatbid[0].bid.map(bid => { + const request = matchRequest(bid.impid, req); + + const response = { + requestId: bid.impid, + width: +bid.w, + height: +bid.h, + cpm: +bid.price, + creativeId: bid.crid, + dealId: bid.dealid || null, + mediaType: request.mediaTypes && request.mediaTypes.video ? VIDEO : BANNER, + currency: body.cur || 'USD', + netRevenue: true, + ttl: 360, + ad: bid.adm, + nurl: bid.nurl, + meta: { + advertiserDomains: bid.adomain || [], + }, + }; - return [{ - requestId: req.data.bidId, - width: size[0], - height: size[1], - cpm: creative.cpm, - creativeId: creative.creative.creative_key, - dealId: creative.creative.deal_id, - currency: 'USD', - netRevenue: true, - ttl: 360, - meta: { advertiserDomains: creative.creative && creative.creative.adomain ? creative.creative.adomain : [] }, - ad: generateAd(body, req) - }]; + if (response.mediaType === VIDEO) { + response.ttl = 3600; + response.vastXml = bid.adm; + } + + return response; + }); }, getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { @@ -138,175 +205,77 @@ export const sharethroughAdapterSpec = { }, // Empty implementation for prebid core to be able to find it - onTimeout: (data) => {}, + onTimeout: (data) => { + }, // Empty implementation for prebid core to be able to find it - onBidWon: (bid) => {}, + onBidWon: (bid) => { + }, // Empty implementation for prebid core to be able to find it - onSetTargeting: (bid) => {} + onSetTargeting: (bid) => { + }, }; -function handleUniversalIds(bidRequest) { - if (!bidRequest.userId) return {}; - - const universalIds = {}; - - const ttd = utils.deepAccess(bidRequest, 'userId.tdid'); - if (ttd) universalIds.ttduid = ttd; - - const pubc = utils.deepAccess(bidRequest, 'userId.pubcid') || utils.deepAccess(bidRequest, 'crumbs.pubcid'); - if (pubc) universalIds.pubcid = pubc; - - const idl = utils.deepAccess(bidRequest, 'userId.idl_env'); - if (idl) universalIds.idluid = idl; - - const id5 = utils.deepAccess(bidRequest, 'userId.id5id.uid'); - if (id5) { - universalIds.id5uid = { id: id5 }; - const id5link = utils.deepAccess(bidRequest, 'userId.id5id.ext.linkType'); - if (id5link) universalIds.id5uid.linkType = id5link; - } - - const lipb = utils.deepAccess(bidRequest, 'userId.lipb.lipbid'); - if (lipb) universalIds.liuid = lipb; - - return universalIds; -} - -function getLargestSize(sizes) { - function area(size) { - return size[0] * size[1]; +function getVideoApi({ api }) { + let defaultValue = [2]; + if (api && Array.isArray(api) && api.length > 0) { + return api; + } else { + return defaultValue; } - - return sizes.reduce((prev, current) => { - if (area(current) > area(prev)) { - return current - } else { - return prev - } - }); } -function generateAd(body, req) { - const strRespId = `str_response_${req.data.bidId}`; - - let adMarkup = ` -
-
- - `; - - if (req.strData.skipIframeBusting) { - // Don't break out of iframe - adMarkup = adMarkup + ``; +function getVideoProtocols({ protocols }) { + let defaultValue = [2, 3, 5, 6, 7, 8]; + if (protocols && Array.isArray(protocols) && protocols.length > 0) { + return protocols; } else { - // Add logic to the markup that detects whether or not in top level document is accessible - // this logic will deploy sfp.js and/or iframe buster script(s) as appropriate - adMarkup = adMarkup + ` - - `; + return defaultValue; } - - return adMarkup; } -function handleIframe () { - // only load iframe buster JS if we can access the top level document - // if we are 'locked in' to this frame then no point trying to bust out: we may as well render in the frame instead - var iframeBusterLoaded = false; - if (!window.lockedInFrame) { - var sfpIframeBusterJs = document.createElement('script'); - sfpIframeBusterJs.src = 'https://native.sharethrough.com/assets/sfp-set-targeting.js'; - sfpIframeBusterJs.type = 'text/javascript'; - try { - window.document.getElementsByTagName('body')[0].appendChild(sfpIframeBusterJs); - iframeBusterLoaded = true; - } catch (e) { - utils.logError('Trouble writing frame buster script, error details:', e); - } - } - - var clientJsLoaded = (!iframeBusterLoaded) ? !!(window.STR && window.STR.Tag) : !!(window.top.STR && window.top.STR.Tag); - if (!clientJsLoaded) { - var sfpJs = document.createElement('script'); - sfpJs.src = 'https://native.sharethrough.com/assets/sfp.js'; - sfpJs.type = 'text/javascript'; - - // only add sfp js to window.top if iframe busting successfully loaded; otherwise, add to iframe - try { - if (iframeBusterLoaded) { - window.top.document.getElementsByTagName('body')[0].appendChild(sfpJs); - } else { - window.document.getElementsByTagName('body')[0].appendChild(sfpJs); - } - } catch (e) { - utils.logError('Trouble writing sfp script, error details:', e); +function getBidRequestFloor(bid) { + let floor = null; + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner', + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })), + }); + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { + floor = parseFloat(floorInfo.floor); } } + return floor !== null ? floor : bid.params.floor; } -// determines if we are capable of busting out of the iframe we are in -// if we catch a DOMException when trying to access top-level document, it means we're stuck in the frame we're in -function isLockedInFrame () { - window.lockedInFrame = false; - try { - window.lockedInFrame = !window.top.document; - } catch (e) { - window.lockedInFrame = (e instanceof DOMException); +function userIdAsEids(bidRequest) { + const eids = createEidsArray(utils.deepAccess(bidRequest, 'userId')) || []; + + const flocData = utils.deepAccess(bidRequest, 'userId.flocId'); + const isFlocIdValid = flocData && flocData.id && flocData.version; + if (isFlocIdValid) { + eids.push({ + source: 'chrome.com', + uids: [{ id: flocData.id, atype: 1, ext: { ver: flocData.version } }], + }); } -} -// See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem -function b64EncodeUnicode(str) { - return btoa( - encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, - function toSolidBytes(match, p1) { - return String.fromCharCode('0x' + p1); - })); + return eids; } -function canAutoPlayHTML5Video() { - const userAgent = navigator.userAgent; - if (!userAgent) return false; - - const isAndroid = /Android/i.test(userAgent); - const isiOS = /iPhone|iPad|iPod/i.test(userAgent); - const chromeVersion = parseInt((/Chrome\/([0-9]+)/.exec(userAgent) || [0, 0])[1]); - const chromeiOSVersion = parseInt((/CriOS\/([0-9]+)/.exec(userAgent) || [0, 0])[1]); - const safariVersion = parseInt((/Version\/([0-9]+)/.exec(userAgent) || [0, 0])[1]); - - if ( - (isAndroid && chromeVersion >= 53) || - (isiOS && (safariVersion >= 10 || chromeiOSVersion >= 53)) || - !(isAndroid || isiOS) - ) { - return true; - } else { - return false; - } +function getProtocol() { + return window.location.protocol; } -function getProtocol() { - return document.location.protocol; +function matchRequest(id, request) { + return request.bidRequests.find(bid => bid.bidId === id); } -function getFloor(bid) { - if (utils.isFn(bid.getFloor)) { - const floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: 'banner', - size: bid.sizes.map(size => ({ w: size[0], h: size[1] })) - }); - if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') { - return parseFloat(floorInfo.floor); - } - } - return null; +// stub for ?? operator +function nullish(input, def) { + return input === null || input === undefined ? def : input; } registerBidder(sharethroughAdapterSpec); diff --git a/modules/sharethroughBidAdapter.md b/modules/sharethroughBidAdapter.md index 396b8164577..218ef353d4e 100644 --- a/modules/sharethroughBidAdapter.md +++ b/modules/sharethroughBidAdapter.md @@ -23,22 +23,74 @@ Module that connects to Sharethrough's demand sources // REQUIRED - The placement key pkey: 'LuB3vxGGFrBZJa6tifXW4xgK', - // OPTIONAL - Render Sharethrough creative in an iframe, defaults to false - iframe: true, - - // OPTIONAL - If iframeSize is provided, we'll use this size for the iframe - // otherwise we'll grab the largest size from the sizes array - // This is ignored if iframe: false - iframeSize: [250, 250], - // OPTIONAL - Blocked Advertiser Domains badv: ['domain1.com', 'domain2.com'], // OPTIONAL - Blocked Categories (IAB codes) bcat: ['IAB1-1', 'IAB1-2'], + + // OPTIONAL - default bid floor, if not specified in bid request (USD) + floor: 0.1, } } ] } ]; ``` + +# Sample Instream Video Ad Unit: For Publishers +``` +var adVideoAdUnits = [ +{ + code: 'test-div-video', + mediaTypes: { + video: { + // CANNOT be 'outstream' + context: 'instream', + placement: 1, + delivery: 1, + companiontype: 'companion type', + companionad: 'companion ad', + // default values shown below this point + pos: 0, + skip: 0, + linearity: 1, + minduration: 5, + maxduration: 60, + playbackmethod: [2], + api: [2], + mimes: ['video/mp4'], + protocols: [2, 3, 5, 6, 7, 8], + playerSize: [640, 360], + startdelay: 0, + skipmin: 0, + skipafter: 0, + }, + }, + bids: [{ + bidder: 'sharethrough', + params: { + pkey: 'pkey1' + } + }] +}] +``` + +# Sample Banner Ad Unit: For Publishers +``` +var adUnits = [ +{ + code: 'test-div-video', + mediaTypes: { + banner: { + pos: 0, // default + }, + }, + bids: [{ + bidder: 'sharethrough', + params: { + pkey: 'pkey1' + } + }] +}] +``` diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 3e406e1af44..122c6cdbb39 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,247 +1,38 @@ import { expect } from 'chai'; import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as utils from '../../../src/utils.js'; import { config } from 'src/config'; +import * as utils from 'src/utils'; const spec = newBidder(sharethroughAdapterSpec).getSpec(); -const bidRequests = [ - { - bidder: 'sharethrough', - bidId: 'bidId1', - sizes: [[600, 300]], - placementCode: 'foo', - params: { - pkey: 'aaaa1111' - }, - ortb2Imp: { - ext: { - data: { - pbadslot: 'adslot-id-1' - } - } - }, - userId: { - tdid: 'fake-tdid', - pubcid: 'fake-pubcid', - idl_env: 'fake-identity-link', - id5id: { - uid: 'fake-id5id', - ext: { - linkType: 2 - } - }, - lipb: { - lipbid: 'fake-lipbid' - } - }, - crumbs: { - pubcid: 'fake-pubcid-in-crumbs-obj' - } - }, - { - bidder: 'sharethrough', - bidId: 'bidId2', - sizes: [[700, 400]], - placementCode: 'bar', - params: { - pkey: 'bbbb2222', - iframe: true - } - }, - { - bidder: 'sharethrough', - bidId: 'bidId3', - sizes: [[700, 400]], - placementCode: 'coconut', - params: { - pkey: 'cccc3333', - iframe: true, - iframeSize: [500, 500] - } - }, - { - bidder: 'sharethrough', - bidId: 'bidId4', - sizes: [[700, 400]], - placementCode: 'bar', - params: { - pkey: 'dddd4444', - badv: ['domain1.com', 'domain2.com'] - } - }, - { - bidder: 'sharethrough', - bidId: 'bidId5', - sizes: [[700, 400]], - placementCode: 'bar', - params: { - pkey: 'eeee5555', - bcat: ['IAB1-1', 'IAB1-2'] - } - }, -]; - -const prebidRequests = [ - { - method: 'POST', - url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', - data: { - bidId: 'bidId', - placement_key: 'pKey' - }, - strData: { - skipIframeBusting: false, - sizes: [] - } - }, - { - method: 'POST', - url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', - data: { - bidId: 'bidId', - placement_key: 'pKey' - }, - strData: { - skipIframeBusting: true, - sizes: [[300, 250], [300, 300], [250, 250], [600, 50]] - } - }, - { - method: 'POST', - url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', - data: { - bidId: 'bidId', - placement_key: 'pKey' - }, - strData: { - skipIframeBusting: true, - iframeSize: [500, 500], - sizes: [[300, 250], [300, 300], [250, 250], [600, 50]] - } - }, - { - method: 'POST', - url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', - data: { - bidId: 'bidId', - placement_key: 'pKey' - }, - strData: { - skipIframeBusting: false, - sizes: [[0, 0]] - } - }, - { - method: 'POST', - url: 'https://btlr.sharethrough.com/WYu2BXv1/v1', - data: { - bidId: 'bidId', - placement_key: 'pKey' - }, - strData: { - skipIframeBusting: false, - sizes: [[300, 250], [300, 300], [250, 250], [600, 50]] - } - } -]; - -const bidderResponse = { - body: { - 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994', - 'bidId': 'bidId1', - 'version': 1, - 'creatives': [{ - 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051', - 'cpm': 12.34, - 'creative': { - 'deal_id': 'aDealId', - 'creative_key': 'aCreativeId', - 'title': '✓ à la mode' - } - }], - 'stxUserId': '' - }, - header: { get: (header) => header } -}; - -const setUserAgent = (uaString) => { - window.navigator['__defineGetter__']('userAgent', function() { - return uaString; - }); -}; - -describe('sharethrough internal spec', function() { - let windowStub, windowTopStub; - let stubbedReturn = [{ - appendChild: () => undefined - }] - beforeEach(function() { - windowStub = sinon.stub(window.document, 'getElementsByTagName'); - windowTopStub = sinon.stub(window.top.document, 'getElementsByTagName'); - windowStub.withArgs('body').returns(stubbedReturn); - windowTopStub.withArgs('body').returns(stubbedReturn); - }); - - afterEach(function() { - windowStub.restore(); - windowTopStub.restore(); - window.STR = undefined; - window.top.STR = undefined; - }); - - describe('we cannot access top level document', function() { - beforeEach(function() { - window.lockedInFrame = true; - }); - - afterEach(function() { - window.lockedInFrame = false; - }); - it('appends sfp.js to the safeframe', function() { - sharethroughInternal.handleIframe(); - expect(windowStub.calledOnce).to.be.true; - }); +describe('sharethrough adapter spec', function() { + let protocolStub; + let inIframeStub; - it('does not append anything if sfp.js is already loaded in the safeframe', function() { - window.STR = { Tag: true }; - sharethroughInternal.handleIframe(); - expect(windowStub.notCalled).to.be.true; - expect(windowTopStub.notCalled).to.be.true; - }); + beforeEach(() => { + protocolStub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https'); + inIframeStub = sinon.stub(utils, 'inIframe').returns(false); }); - describe('we are able to bust out of the iframe', function() { - it('appends sfp.js to window.top', function() { - sharethroughInternal.handleIframe(); - expect(windowStub.calledOnce).to.be.true; - expect(windowTopStub.calledOnce).to.be.true; - }); - - it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function() { - window.top.STR = { Tag: true }; - sharethroughInternal.handleIframe(); - expect(windowStub.calledOnce).to.be.true; - expect(windowTopStub.notCalled).to.be.true; - }); + afterEach(() => { + protocolStub.restore(); + inIframeStub.restore(); }); -}); -describe('sharethrough adapter spec', function() { - describe('.code', function() { + describe('code', function() { it('should return a bidder code of sharethrough', function() { expect(spec.code).to.eql('sharethrough'); }); }); - describe('.isBidRequestValid', function() { + describe('isBidRequestValid', function() { it('should return false if req has no pkey', function() { const invalidBidRequest = { bidder: 'sharethrough', params: { - notPKey: 'abc123' - } + notPKey: 'abc123', + }, }; expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); @@ -250,383 +41,504 @@ describe('sharethrough adapter spec', function() { const invalidBidRequest = { bidder: 'notSharethrough', params: { - notPKey: 'abc123' + pkey: 'abc123', } }; expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); it('should return true if req is correct', function() { - expect(spec.isBidRequestValid(bidRequests[0])).to.eq(true); - expect(spec.isBidRequestValid(bidRequests[1])).to.eq(true); + const validBidRequest = { + bidder: 'sharethrough', + params: { + pkey: 'abc123', + }, + }; + expect(spec.isBidRequestValid(validBidRequest)).to.eq(true); }); }); - describe('.buildRequests', function() { - it('should return an array of requests', function() { - const builtBidRequests = spec.buildRequests(bidRequests); + describe('open rtb', () => { + let bidRequests, bidderRequest; + + beforeEach(() => { + config.setConfig({ + bidderTimeout: 242, + coppa: true, + }); - expect(builtBidRequests[0].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1'); - expect(builtBidRequests[1].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1'); - expect(builtBidRequests[0].method).to.eq('POST'); + bidRequests = [ + { + bidder: 'sharethrough', + bidId: 'bidId1', + sizes: [[300, 250], [300, 600]], + params: { + pkey: 'aaaa1111', + bcat: ['cat1', 'cat2'], + badv: ['adv1', 'adv2'], + }, + mediaTypes: { + banner: { + pos: 1, + }, + }, + ortb2Imp: { + ext: { + data: { + pbadslot: 'universal-id', + }, + }, + }, + userId: { + tdid: 'fake-tdid', + pubcid: 'fake-pubcid', + idl_env: 'fake-identity-link', + id5id: { + uid: 'fake-id5id', + ext: { + linkType: 2, + }, + }, + lipb: { + lipbid: 'fake-lipbid', + }, + criteoId: 'fake-criteo', + britepoolid: 'fake-britepool', + intentIqId: 'fake-intentiq', + lotamePanoramaId: 'fake-lotame', + parrableId: { + eid: 'fake-parrable', + }, + netId: 'fake-netid', + sharedid: { + id: 'fake-sharedid', + }, + flocId: { + id: 'fake-flocid', + version: '42', + }, + }, + crumbs: { + pubcid: 'fake-pubcid-in-crumbs-obj', + }, + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1, + }, + ], + }, + getFloor: () => ({ currency: 'USD', floor: 42 }), + }, + { + bidder: 'sharethrough', + bidId: 'bidId2', + sizes: [[600, 300]], + params: { + pkey: 'bbbb2222', + }, + mediaTypes: { + video: { + pos: 3, + skip: 1, + linearity: 0, + minduration: 10, + maxduration: 30, + playbackmethod: [1], + api: [3], + mimes: ['video/3gpp'], + protocols: [2, 3], + playerSize: [640, 480], + startdelay: 42, + skipmin: 10, + skipafter: 20, + placement: 1, + delivery: 1, + companiontype: 'companion type', + companionad: 'companion ad', + context: 'instream', + }, + }, + getFloor: () => ({ currency: 'USD', floor: 42 }), + }, + ]; + + bidderRequest = { + refererInfo: { + referer: 'https://referer.com', + }, + }; }); - it('should set the instant_play_capable parameter correctly based on browser userAgent string', function() { - setUserAgent('Android Chrome/60'); - let builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + describe('buildRequests', function() { + describe('top level object', () => { + it('should build openRTB request', () => { + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + + expect(builtRequest.method).to.equal('POST'); + expect(builtRequest.url).not.to.be.undefined; + expect(builtRequest.options).to.be.undefined; + expect(builtRequest.bidderRequest).to.deep.equal(bidderRequest); + + const openRtbReq = builtRequest.data; + expect(openRtbReq.id).not.to.be.undefined; + expect(openRtbReq.cur).to.deep.equal(['USD']); + expect(openRtbReq.tmax).to.equal(242); + + expect(openRtbReq.site.domain).not.to.be.undefined; + expect(openRtbReq.site.page).not.to.be.undefined; + expect(openRtbReq.site.ref).to.equal('https://referer.com'); + + const expectedEids = { + 'liveramp.com': { id: 'fake-identity-link' }, + 'id5-sync.com': { id: 'fake-id5id' }, + 'pubcid.org': { id: 'fake-pubcid' }, + 'adserver.org': { id: 'fake-tdid' }, + 'criteo.com': { id: 'fake-criteo' }, + 'britepool.com': { id: 'fake-britepool' }, + 'liveintent.com': { id: 'fake-lipbid' }, + 'intentiq.com': { id: 'fake-intentiq' }, + 'crwdcntrl.net': { id: 'fake-lotame' }, + 'parrable.com': { id: 'fake-parrable' }, + 'netid.de': { id: 'fake-netid' }, + 'chrome.com': { id: 'fake-flocid' }, + }; + expect(openRtbReq.user.ext.eids).to.be.an('array').that.have.length(Object.keys(expectedEids).length); + for (const eid of openRtbReq.user.ext.eids) { + expect(Object.keys(expectedEids)).to.include(eid.source); + expect(eid.uids[0].id).to.equal(expectedEids[eid.source].id); + expect(eid.uids[0].atype).to.be.ok; + } - setUserAgent('iPhone Version/11'); - builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + expect(openRtbReq.device.ua).to.equal(navigator.userAgent); + expect(openRtbReq.regs.coppa).to.equal(1); - setUserAgent('iPhone CriOS/60'); - builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + expect(openRtbReq.source.ext.version).not.to.be.undefined; + expect(openRtbReq.source.ext.str).not.to.be.undefined; + expect(openRtbReq.source.ext.schain).to.deep.equal(bidRequests[0].schain); - setUserAgent('Android Chrome/50'); - builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.false; + expect(openRtbReq.bcat).to.deep.equal(bidRequests[0].params.bcat); + expect(openRtbReq.badv).to.deep.equal(bidRequests[0].params.badv); - setUserAgent('Android Chrome'); - builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.false; + expect(openRtbReq.imp).to.have.length(2); - setUserAgent(undefined); - builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0].data.instant_play_capable).to.be.false; - }); + expect(openRtbReq.imp[0].id).to.equal('bidId1'); + expect(openRtbReq.imp[0].tagid).to.equal('aaaa1111'); + expect(openRtbReq.imp[0].secure).to.equal(1); + expect(openRtbReq.imp[0].bidfloor).to.equal(42); - it('should set the secure parameter to false when the protocol is http', function() { - const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('http:'); - const bidRequest = spec.buildRequests(bidRequests, null)[0]; - expect(bidRequest.data.secure).to.be.false; - stub.restore(); - }); + expect(openRtbReq.imp[1].id).to.equal('bidId2'); + expect(openRtbReq.imp[1].tagid).to.equal('bbbb2222'); + expect(openRtbReq.imp[1].secure).to.equal(1); + expect(openRtbReq.imp[1].bidfloor).to.equal(42); + }); - it('should set the secure parameter to true when the protocol is https', function() { - const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https:'); - const bidRequest = spec.buildRequests(bidRequests, null)[0]; - expect(bidRequest.data.secure).to.be.true; - stub.restore(); - }); + it('should have empty eid array if no id is provided', () => { + const openRtbReq = spec.buildRequests([bidRequests[1]], bidderRequest).data; - it('should set the secure parameter to true when the protocol is neither http or https', function() { - const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('about:'); - const bidRequest = spec.buildRequests(bidRequests, null)[0]; - expect(bidRequest.data.secure).to.be.true; - stub.restore(); - }); + expect(openRtbReq.user.ext.eids).to.deep.equal([]); + }); + }); - it('should add ccpa parameter if uspConsent is present', function() { - const uspConsent = '1YNN'; - const bidderRequest = { uspConsent: uspConsent }; - const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(bidRequest.data.us_privacy).to.eq(uspConsent); - }); + describe('regulation', () => { + describe('gdpr', () => { + it('should populate request accordingly when gdpr applies', () => { + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'consent', + }; - it('should add consent parameters if gdprConsent is present', function() { - const gdprConsent = { consentString: 'consent_string123', gdprApplies: true }; - const bidderRequest = { gdprConsent: gdprConsent }; - const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(bidRequest.data.consent_required).to.eq(true); - expect(bidRequest.data.consent_string).to.eq('consent_string123'); - }); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should handle gdprConsent is present but values are undefined case', function() { - const gdprConsent = { consent_string: undefined, gdprApplies: undefined }; - const bidderRequest = { gdprConsent: gdprConsent }; - const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(bidRequest.data).to.not.include.any.keys('consent_string'); - }); + expect(openRtbReq.regs.ext.gdpr).to.equal(1); + expect(openRtbReq.user.ext.consent).to.equal('consent'); + }); - it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.ttduid).to.eq('fake-tdid'); - }); + it('should populate request accordingly when gdpr explicitly does not apply', () => { + bidderRequest.gdprConsent = { + gdprApplies: false, + }; - it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + - ' userId object of the bidrequest', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); - }); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + - ' crumbs object of the bidrequest', function() { - const bidData = utils.deepClone(bidRequests); - delete bidData[0].userId.pubcid; + expect(openRtbReq.regs.ext.gdpr).to.equal(0); + expect(openRtbReq.user.ext.consent).to.be.undefined; + }); + }); - const bidRequest = spec.buildRequests(bidData)[0]; - expect(bidRequest.data.pubcid).to.eq('fake-pubcid-in-crumbs-obj'); - }); + describe('US privacy', () => { + it('should populate request accordingly when us privacy applies', () => { + bidderRequest.uspConsent = 'consent'; - it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' + - ' crumbs object of the bidrequest', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - delete bidRequest.userId; - expect(bidRequest.data.pubcid).to.eq('fake-pubcid'); - }); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should add the idluid parameter if a bid request contains a value for Identity Link from Live Ramp', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.idluid).to.eq('fake-identity-link'); - }); + expect(openRtbReq.regs.ext.us_privacy).to.equal('consent'); + }); + }); - it('should add the id5uid parameter if a bid request contains a value for ID5', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.id5uid.id).to.eq('fake-id5id'); - expect(bidRequest.data.id5uid.linkType).to.eq(2); - }); + describe('coppa', () => { + it('should populate request accordingly when coppa does not apply', () => { + config.setConfig({ coppa: false }); - it('should add the liuid parameter if a bid request contains a value for LiveIntent ID', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.liuid).to.eq('fake-lipbid'); - }); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should add Sharethrough specific parameters', function() { - const builtBidRequests = spec.buildRequests(bidRequests); - expect(builtBidRequests[0]).to.deep.include({ - strData: { - skipIframeBusting: undefined, - iframeSize: undefined, - sizes: [[600, 300]] - } + expect(openRtbReq.regs.coppa).to.equal(0); + }); + }); }); - }); - - it('should add a supply chain parameter if schain is present', function() { - // shallow copy of the first bidRequest obj, so we don't mutate - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest['schain'] = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'directseller.com', - sid: '00001', - rid: 'BidRequest1', - hp: 1 - } - ] - }; - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data.schain).to.eq(JSON.stringify(bidRequest.schain)); - }); - - describe('gpid', () => { - it('should include the gpid param if pbadslot is found in ortb2Imp in the bid request', () => { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data.gpid).to.eq('adslot-id-1') - }); + describe('universal id', () => { + it('should include gpid when universal id is provided', () => { + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should not include the gpid param if pbadslot is not found in ortb2Imp in the bid request', () => { - const bidRequest = spec.buildRequests(bidRequests)[1]; - expect(bidRequest.data).to.not.include.any.keys('gpid'); + expect(openRtbReq.imp[0].ext.gpid).to.equal('universal-id'); + expect(openRtbReq.imp[1].ext).to.be.undefined; + }); }); - }); - - it('should add badv if provided', () => { - const builtBidRequest = spec.buildRequests([bidRequests[3]])[0]; - - expect(builtBidRequest.data.badv).to.have.members(['domain1.com', 'domain2.com']) - }); - - it('should add bcat if provided', () => { - const builtBidRequest = spec.buildRequests([bidRequests[4]])[0]; - expect(builtBidRequest.data.bcat).to.have.members(['IAB1-1', 'IAB1-2']) - }); + describe('secure flag', () => { + it('should be positive when protocol is https', () => { + protocolStub.returns('https'); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should not add a supply chain parameter if schain is missing', function() { - const bidRequest = spec.buildRequests(bidRequests)[0]; - expect(bidRequest.data).to.not.include.any.keys('schain'); - }); + expect(openRtbReq.imp[0].secure).to.equal(1); + expect(openRtbReq.imp[1].secure).to.equal(1); + }); - it('should include the bidfloor parameter if it is present in the bid request', function() { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest['getFloor'] = () => ({ currency: 'USD', floor: 0.5 }); - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data.bidfloor).to.eq(0.5); - }); + it('should be negative when protocol is http', () => { + protocolStub.returns('http'); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should not include the bidfloor parameter if it is missing in the bid request', function() { - const bidRequest = Object.assign({}, bidRequests[0]); - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data).to.not.include.any.keys('bidfloor'); - }); + expect(openRtbReq.imp[0].secure).to.equal(0); + expect(openRtbReq.imp[1].secure).to.equal(0); + }); - describe('coppa', function() { - it('should add coppa to request if enabled', function() { - config.setConfig({coppa: true}); - const bidRequest = Object.assign({}, bidRequests[0]); - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data.coppa).to.eq(true); - }); + it('should be positive when protocol is neither http nor https', () => { + protocolStub.returns('about'); + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + const openRtbReq = builtRequest.data; - it('should not add coppa to request if disabled', function() { - config.setConfig({coppa: false}); - const bidRequest = Object.assign({}, bidRequests[0]); - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data.coppa).to.be.undefined; + expect(openRtbReq.imp[0].secure).to.equal(1); + expect(openRtbReq.imp[1].secure).to.equal(1); + }); }); - it('should not add coppa to request if unknown value', function() { - config.setConfig({coppa: 'something'}); - const bidRequest = Object.assign({}, bidRequests[0]); - const builtBidRequest = spec.buildRequests([bidRequest])[0]; - expect(builtBidRequest.data.coppa).to.be.undefined; - }); - }); - }); + describe('banner imp', () => { + it('should generate open rtb banner imp', () => { + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); - describe('.interpretResponse', function() { - it('returns a correctly parsed out response', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.deep.include( - { - width: 1, - height: 1, - cpm: 12.34, - creativeId: 'aCreativeId', - dealId: 'aDealId', - currency: 'USD', - netRevenue: true, - ttl: 360, - meta: { advertiserDomains: [] } + const bannerImp = builtRequest.data.imp[0].banner; + expect(bannerImp.pos).to.equal(1); + expect(bannerImp.topframe).to.equal(1); + expect(bannerImp.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); }); - }); - it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[1])[0]).to.include( - { - width: 300, - height: 300, - cpm: 12.34, - creativeId: 'aCreativeId', - dealId: 'aDealId', - currency: 'USD', - netRevenue: true, - ttl: 360 - }); - }); + it('should default to pos 0 if not provided', () => { + delete bidRequests[0].mediaTypes; + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[2])[0]).to.include( - { - width: 500, - height: 500, - cpm: 12.34, - creativeId: 'aCreativeId', - dealId: 'aDealId', - currency: 'USD', - netRevenue: true, - ttl: 360 + const bannerImp = builtRequest.data.imp[0].banner; + expect(bannerImp.pos).to.equal(0); }); - }); + }); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[3])[0]).to.include( - { - width: 0, - height: 0, - cpm: 12.34, - creativeId: 'aCreativeId', - dealId: 'aDealId', - currency: 'USD', - netRevenue: true, - ttl: 360 + describe('video imp', () => { + it('should generate open rtb video imp', () => { + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + + const videoImp = builtRequest.data.imp[1].video; + expect(videoImp.pos).to.equal(3); + expect(videoImp.topframe).to.equal(1); + expect(videoImp.skip).to.equal(1); + expect(videoImp.linearity).to.equal(0); + expect(videoImp.minduration).to.equal(10); + expect(videoImp.maxduration).to.equal(30); + expect(videoImp.playbackmethod).to.deep.equal([1]); + expect(videoImp.api).to.deep.equal([3]); + expect(videoImp.mimes).to.deep.equal(['video/3gpp']); + expect(videoImp.protocols).to.deep.equal([2, 3]); + expect(videoImp.w).to.equal(640); + expect(videoImp.h).to.equal(480); + expect(videoImp.startdelay).to.equal(42); + expect(videoImp.skipmin).to.equal(10); + expect(videoImp.skipafter).to.equal(20); + expect(videoImp.placement).to.equal(1); + expect(videoImp.delivery).to.equal(1); + expect(videoImp.companiontype).to.equal('companion type'); + expect(videoImp.companionad).to.equal('companion ad'); }); - }); - it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function() { - expect(spec.interpretResponse(bidderResponse, prebidRequests[4])[0]).to.include( - { - width: 300, - height: 300, - cpm: 12.34, - creativeId: 'aCreativeId', - dealId: 'aDealId', - currency: 'USD', - netRevenue: true, - ttl: 360 + it('should set defaults if no value provided', () => { + delete bidRequests[1].mediaTypes.video.pos; + delete bidRequests[1].mediaTypes.video.skip; + delete bidRequests[1].mediaTypes.video.linearity; + delete bidRequests[1].mediaTypes.video.minduration; + delete bidRequests[1].mediaTypes.video.maxduration; + delete bidRequests[1].mediaTypes.video.playbackmethod; + delete bidRequests[1].mediaTypes.video.api; + delete bidRequests[1].mediaTypes.video.mimes; + delete bidRequests[1].mediaTypes.video.protocols; + delete bidRequests[1].mediaTypes.video.playerSize; + delete bidRequests[1].mediaTypes.video.startdelay; + delete bidRequests[1].mediaTypes.video.skipmin; + delete bidRequests[1].mediaTypes.video.skipafter; + delete bidRequests[1].mediaTypes.video.placement; + delete bidRequests[1].mediaTypes.video.delivery; + delete bidRequests[1].mediaTypes.video.companiontype; + delete bidRequests[1].mediaTypes.video.companionad; + + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); + + const videoImp = builtRequest.data.imp[1].video; + expect(videoImp.pos).to.equal(0); + expect(videoImp.skip).to.equal(0); + expect(videoImp.linearity).to.equal(1); + expect(videoImp.minduration).to.equal(5); + expect(videoImp.maxduration).to.equal(60); + expect(videoImp.playbackmethod).to.deep.equal([2]); + expect(videoImp.api).to.deep.equal([2]); + expect(videoImp.mimes).to.deep.equal(['video/mp4']); + expect(videoImp.protocols).to.deep.equal([2, 3, 5, 6, 7, 8]); + expect(videoImp.w).to.equal(640); + expect(videoImp.h).to.equal(360); + expect(videoImp.startdelay).to.equal(0); + expect(videoImp.skipmin).to.equal(0); + expect(videoImp.skipafter).to.equal(0); + expect(videoImp.placement).to.be.undefined; + expect(videoImp.delivery).to.be.undefined; + expect(videoImp.companiontype).to.be.undefined; + expect(videoImp.companionad).to.be.undefined; }); - }); - it('returns a blank array if there are no creatives', function() { - const bidResponse = { body: { creatives: [] } }; - expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; - }); + it('should not return a video impression if context is outstream', () => { + bidRequests[1].mediaTypes.video.context = 'outstream'; + const builtRequest = spec.buildRequests(bidRequests, bidderRequest); - it('returns a blank array if body object is empty', function() { - const bidResponse = { body: {} }; - expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; - }); - - it('returns a blank array if body is null', function() { - const bidResponse = { body: null }; - expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty; + const videoImp = builtRequest.data.imp[1]; + expect(videoImp).to.be.undefined; + }); + }); }); - it('correctly generates ad markup when skipIframeBusting is false', function() { - const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[0])[0].ad; - let resp = null; + describe('interpretResponse', function() { + let request; + let response; + + beforeEach(() => { + request = spec.buildRequests(bidRequests, bidderRequest); + response = { + body: { + seatbid: [{ + bid: [{ + id: '123', + impid: 'bidId1', + w: 300, + h: 250, + price: 42, + crid: 'creative', + dealid: 'deal', + adomain: ['domain.com'], + adm: 'markup', + }, { + id: '456', + impid: 'bidId2', + w: 640, + h: 480, + price: 42, + adm: 'vastTag', + }], + }], + }, + }; + }); - expect(() => btoa(JSON.stringify(bidderResponse))).to.throw(); - expect(() => resp = sharethroughInternal.b64EncodeUnicode(JSON.stringify(bidderResponse))).not.to.throw(); - expect(adMarkup).to.match( - /data-str-native-key="pKey" data-stx-response-name="str_response_bidId"/); - expect(!!adMarkup.indexOf(resp)).to.eql(true); + describe('banner', () => { + it('should return a banner bid', () => { + const resp = spec.interpretResponse(response, request); + + const bannerBid = resp[0]; + expect(bannerBid.requestId).to.equal('bidId1'); + expect(bannerBid.width).to.equal(300); + expect(bannerBid.height).to.equal(250); + expect(bannerBid.cpm).to.equal(42); + expect(bannerBid.creativeId).to.equal('creative'); + expect(bannerBid.dealId).to.equal('deal'); + expect(bannerBid.mediaType).to.equal('banner'); + expect(bannerBid.currency).to.equal('USD'); + expect(bannerBid.netRevenue).to.equal(true); + expect(bannerBid.ttl).to.equal(360); + expect(bannerBid.ad).to.equal('markup'); + expect(bannerBid.meta.advertiserDomains).to.deep.equal(['domain.com']); + expect(bannerBid.vastXml).to.be.undefined; + }); + }); - // insert functionality to autodetect whether or not in safeframe, and handle JS insertion - expect(adMarkup).to.match(/isLockedInFrame/); - expect(adMarkup).to.match(/handleIframe/); + describe('video', () => { + it('should return a video bid', () => { + const resp = spec.interpretResponse(response, request); + + const bannerBid = resp[1]; + expect(bannerBid.requestId).to.equal('bidId2'); + expect(bannerBid.width).to.equal(640); + expect(bannerBid.height).to.equal(480); + expect(bannerBid.cpm).to.equal(42); + expect(bannerBid.creativeId).to.be.undefined; + expect(bannerBid.dealId).to.be.null; + expect(bannerBid.mediaType).to.equal('video'); + expect(bannerBid.currency).to.equal('USD'); + expect(bannerBid.netRevenue).to.equal(true); + expect(bannerBid.ttl).to.equal(3600); + expect(bannerBid.ad).to.equal('vastTag'); + expect(bannerBid.meta.advertiserDomains).to.deep.equal([]); + expect(bannerBid.vastXml).to.equal('vastTag'); + }); + }); }); - it('correctly generates ad markup when skipIframeBusting is true', function() { - const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[1])[0].ad; - let resp = null; - - expect(() => btoa(JSON.stringify(bidderResponse))).to.throw(); - expect(() => resp = sharethroughInternal.b64EncodeUnicode(JSON.stringify(bidderResponse))).not.to.throw(); - expect(adMarkup).to.match( - /data-str-native-key="pKey" data-stx-response-name="str_response_bidId"/); - expect(!!adMarkup.indexOf(resp)).to.eql(true); - expect(adMarkup).to.match( - / + + + + + + + + +

Ad Serverless Test Page

+ +
+
+
+ + From 1cd74ba202f2aeeb66db61bd4510b40cfc812f03 Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Wed, 8 Sep 2021 12:52:02 -0700 Subject: [PATCH 1457/1476] Publink Id System (Conversant): add new user id module (#7322) --- modules/.submodules.json | 3 +- modules/publinkIdSystem.js | 123 +++++++++++++++++++ modules/publinkIdSystem.md | 28 +++++ modules/userId/eids.js | 4 + test/spec/modules/publinkIdSystem_spec.js | 136 ++++++++++++++++++++++ 5 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 modules/publinkIdSystem.js create mode 100644 modules/publinkIdSystem.md create mode 100644 test/spec/modules/publinkIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index f08db109bb6..e4de1819a16 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -32,7 +32,8 @@ "flocIdSystem", "amxIdSystem", "naveggId", - "imuIdSystem" + "imuIdSystem", + "publinkIdSystem" ], "adpod": [ "freeWheelAdserverVideo", diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js new file mode 100644 index 00000000000..5e549732097 --- /dev/null +++ b/modules/publinkIdSystem.js @@ -0,0 +1,123 @@ +/** + * This module adds the PublinkId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/publinkIdSystem + * @requires module:modules/userId + */ + +import {submodule} from '../src/hook.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {ajax} from '../src/ajax.js'; +import * as utils from '../src/utils.js'; +import {uspDataHandler} from '../src/adapterManager.js'; + +const MODULE_NAME = 'publinkId'; +const GVLID = 24; +const PUBLINK_COOKIE = '_publink'; +const PUBLINK_S2S_COOKIE = '_publink_srv'; + +export const storage = getStorageManager(GVLID); + +function publinkIdUrl(params, consentData) { + let url = utils.parseUrl('https://proc.ad.cpe.dotomi.com/cvx/client/sync/publink'); + url.search = { + deh: params.e, + mpn: 'Prebid.js', + mpv: '$prebid.version$', + }; + if (consentData) { + url.search.gdpr = (consentData.gdprApplies) ? 1 : 0; + url.search.gdpr_consent = consentData.consentString; + } + + const usPrivacyString = uspDataHandler.getConsentData(); + if (usPrivacyString && typeof usPrivacyString === 'string') { + url.search.us_privacy = usPrivacyString; + } + + return utils.buildUrl(url); +} + +function makeCallback(config = {}, consentData) { + return function(prebidCallback) { + const options = {method: 'GET', withCredentials: true}; + let handleResponse = function(responseText, xhr) { + if (xhr.status === 200) { + let response = JSON.parse(responseText); + if (response) { + prebidCallback(response.publink); + } + } + }; + if (config.params && config.params.e) { + ajax(publinkIdUrl(config.params, consentData), handleResponse, undefined, options); + } + }; +} + +function getlocalValue() { + let result; + function getData(key) { + let value; + if (storage.hasLocalStorage()) { + value = storage.getDataFromLocalStorage(key); + } + if (!value) { + value = storage.getCookie(key); + } + + if (typeof value === 'string') { + try { + const obj = JSON.parse(value); + if (obj && obj.exp && obj.exp > Date.now()) { + return obj.publink; + } + } catch (e) { + utils.logError(e); + } + } + } + result = getData(PUBLINK_S2S_COOKIE); + if (!result) { + result = getData(PUBLINK_COOKIE); + } + return result; +} + +/** @type {Submodule} */ +export const publinkIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + gvlid: GVLID, + + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} id encrypted userid + * @returns {{publinkId: string} | undefined} + */ + decode(publinkId) { + return {publink: publinkId}; + }, + + /** + * performs action to obtain id + * Use a publink cookie first if it is present, otherwise use prebids copy, if neither are available callout to get a new id + * @function + * @param {SubmoduleConfig} [config] Config object with params and storage properties + * @returns {IdResponse} + */ + getId: function(config, consentData, storedId) { + const localValue = getlocalValue(); + if (localValue) { + return {id: localValue}; + } + if (!storedId) { + return {callback: makeCallback(config, consentData)}; + } + } +}; +submodule('userId', publinkIdSubmodule); diff --git a/modules/publinkIdSystem.md b/modules/publinkIdSystem.md new file mode 100644 index 00000000000..669828322a5 --- /dev/null +++ b/modules/publinkIdSystem.md @@ -0,0 +1,28 @@ +## Publink User ID Submodule + +Publink user id module + +## Configuration Descriptions for the `userId` Configuration Section + +| Param Name | Required | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Yes | String | module identifier | `"publinkId"` | +| params.e | Yes | String | hashed email address | `"e80b5017098950fc58aad83c8c14978e"` | + +### Example configuration for Publink +``` +pbjs.setConfig({ + userSync: { + userIds: [{ + name: "publinkId", + storage: { + name: "pbjs_publink", + type: "html5" + }, + params: { + e: "e80b5017098950fc58aad83c8c14978e", // example hashed email (md5) + } + }], + } + }); +``` diff --git a/modules/userId/eids.js b/modules/userId/eids.js index f6707f211d2..0a8e94883b4 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -228,6 +228,10 @@ const USER_IDS_CONFIG = { source: 'amxrtb.com', atype: 1, }, + 'publinkId': { + source: 'epsilon.com', + atype: 3 + }, 'kpuid': { source: 'kpuid.com', atype: 3 diff --git a/test/spec/modules/publinkIdSystem_spec.js b/test/spec/modules/publinkIdSystem_spec.js new file mode 100644 index 00000000000..326285709f3 --- /dev/null +++ b/test/spec/modules/publinkIdSystem_spec.js @@ -0,0 +1,136 @@ +import {publinkIdSubmodule} from 'modules/publinkIdSystem.js'; +import {getStorageManager} from '../../../src/storageManager'; +import {server} from 'test/mocks/xhr.js'; +import sinon from 'sinon'; +import {uspDataHandler} from '../../../src/adapterManager'; + +export const storage = getStorageManager(24); +const TEST_COOKIE_VALUE = 'cookievalue'; +describe('PublinkIdSystem', () => { + describe('decode', () => { + it('decode', () => { + const result = publinkIdSubmodule.decode(TEST_COOKIE_VALUE); + expect(result).deep.equals({publink: TEST_COOKIE_VALUE}); + }); + }); + + describe('Fetch Local Cookies', () => { + const PUBLINK_COOKIE = '_publink'; + const PUBLINK_SRV_COOKIE = '_publink_srv'; + const EXP = Date.now() + 60 * 60 * 24 * 7 * 1000; + const COOKIE_VALUE = {publink: 'publinkCookieValue', exp: EXP}; + const LOCAL_VALUE = {publink: 'publinkLocalStorageValue', exp: EXP}; + const COOKIE_EXPIRATION = (new Date(Date.now() + 60 * 60 * 24 * 1000)).toUTCString(); + const DELETE_COOKIE = 'Thu, 01 Jan 1970 00:00:01 GMT'; + it('publink srv cookie', () => { + storage.setCookie(PUBLINK_SRV_COOKIE, JSON.stringify(COOKIE_VALUE), COOKIE_EXPIRATION); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(COOKIE_VALUE.publink); + storage.setCookie(PUBLINK_SRV_COOKIE, '', DELETE_COOKIE); + }); + it('publink srv local storage', () => { + storage.setDataInLocalStorage(PUBLINK_SRV_COOKIE, JSON.stringify(LOCAL_VALUE)); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(LOCAL_VALUE.publink); + storage.removeDataFromLocalStorage(PUBLINK_SRV_COOKIE); + }); + it('publink cookie', () => { + storage.setCookie(PUBLINK_COOKIE, JSON.stringify(COOKIE_VALUE), COOKIE_EXPIRATION); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(COOKIE_VALUE.publink); + storage.setCookie(PUBLINK_COOKIE, '', DELETE_COOKIE); + }); + it('publink local storage', () => { + storage.setDataInLocalStorage(PUBLINK_COOKIE, JSON.stringify(LOCAL_VALUE)); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(LOCAL_VALUE.publink); + storage.removeDataFromLocalStorage(PUBLINK_COOKIE); + }); + it('ignore expired cookie', () => { + storage.setDataInLocalStorage(PUBLINK_COOKIE, JSON.stringify({publink: 'value', exp: Date.now() - 60 * 60 * 24 * 1000})); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.be.undefined; + storage.removeDataFromLocalStorage(PUBLINK_COOKIE); + }); + it('priority goes to publink_srv cookie', () => { + storage.setCookie(PUBLINK_SRV_COOKIE, JSON.stringify(COOKIE_VALUE), COOKIE_EXPIRATION); + storage.setDataInLocalStorage(PUBLINK_COOKIE, JSON.stringify(LOCAL_VALUE)); + const result = publinkIdSubmodule.getId(); + expect(result.id).to.equal(COOKIE_VALUE.publink); + storage.setCookie(PUBLINK_SRV_COOKIE, '', DELETE_COOKIE); + storage.removeDataFromLocalStorage(PUBLINK_COOKIE); + }); + }); + + describe('getId', () => { + const serverResponse = {publink: 'ec0xHT3yfAOnykP64Qf0ORSi7LjNT1wju04ZSCsoPBekOJdBwK-0Zl_lXKDNnzhauC4iszBc-PvA1Be6IMlh1QocA'}; + it('no config', () => { + const result = publinkIdSubmodule.getId(); + expect(result).to.exist; + expect(result.callback).to.be.a('function'); + }); + it('Use local copy', () => { + const result = publinkIdSubmodule.getId({}, undefined, TEST_COOKIE_VALUE); + expect(result).to.be.undefined; + }); + + describe('callout for id', () => { + let callbackSpy = sinon.spy(); + + beforeEach(() => { + callbackSpy.resetHistory(); + }); + + it('Fetch with consent data', () => { + const config = {storage: {type: 'cookie'}, params: {e: 'hashedemailvalue'}}; + const consentData = {gdprApplies: 1, consentString: 'myconsentstring'}; + let submoduleCallback = publinkIdSubmodule.getId(config, consentData).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + expect(request.url).to.equal('https://proc.ad.cpe.dotomi.com/cvx/client/sync/publink?deh=hashedemailvalue&mpn=Prebid.js&mpv=$prebid.version$&gdpr=1&gdpr_consent=myconsentstring'); + + request.respond(200, {}, JSON.stringify(serverResponse)); + expect(callbackSpy.calledOnce).to.be.true; + expect(callbackSpy.lastCall.lastArg).to.equal(serverResponse.publink); + }); + + it('server doesnt respond', () => { + const config = {storage: {type: 'cookie'}, params: {e: 'hashedemailvalue'}}; + let submoduleCallback = publinkIdSubmodule.getId(config).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + expect(request.url).to.equal('https://proc.ad.cpe.dotomi.com/cvx/client/sync/publink?deh=hashedemailvalue&mpn=Prebid.js&mpv=$prebid.version$'); + + request.respond(204, {}, JSON.stringify(serverResponse)); + expect(callbackSpy.calledOnce).to.be.false; + }); + }); + + describe('usPrivacy', () => { + let callbackSpy = sinon.spy(); + const oldPrivacy = uspDataHandler.getConsentData(); + before(() => { + uspDataHandler.setConsentData('1YNN'); + }); + after(() => { + uspDataHandler.setConsentData(oldPrivacy); + callbackSpy.resetHistory(); + }); + + it('Fetch with usprivacy data', () => { + const config = {storage: {type: 'cookie'}, params: {e: 'hashedemailvalue'}}; + let submoduleCallback = publinkIdSubmodule.getId(config).callback; + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + expect(request.url).to.equal('https://proc.ad.cpe.dotomi.com/cvx/client/sync/publink?deh=hashedemailvalue&mpn=Prebid.js&mpv=$prebid.version$&us_privacy=1YNN'); + + request.respond(200, {}, JSON.stringify(serverResponse)); + expect(callbackSpy.calledOnce).to.be.true; + expect(callbackSpy.lastCall.lastArg).to.equal(serverResponse.publink); + }); + }); + }); +}); From e8b344dac65a3600b7d4ff058c9701a830c90334 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 8 Sep 2021 13:04:32 -0700 Subject: [PATCH 1458/1476] If the bidReq has gam adslot use it (#7374) --- modules/priceFloors.js | 8 ++- test/spec/integration/faker/googletag.js | 5 ++ test/spec/modules/priceFloors_spec.js | 79 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 3555fedbf3a..5f7a39e0784 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -64,13 +64,19 @@ function getHostNameFromReferer(referer) { return referrerHostname; } +// First look into bidRequest! +function getGptSlotFromBidRequest(bidRequest) { + const isGam = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.adserver.name') === 'gam'; + return isGam && bidRequest.ortb2Imp.ext.data.adserver.adslot; +} + /** * @summary floor field types with their matching functions to resolve the actual matched value */ export let fieldMatchingFunctions = { 'size': (bidRequest, bidResponse) => utils.parseGPTSingleSizeArray(bidResponse.size) || '*', 'mediaType': (bidRequest, bidResponse) => bidResponse.mediaType || 'banner', - 'gptSlot': (bidRequest, bidResponse) => utils.getGptSlotInfoForAdUnitCode(bidRequest.adUnitCode).gptSlot, + 'gptSlot': (bidRequest, bidResponse) => getGptSlotFromBidRequest(bidRequest) || utils.getGptSlotInfoForAdUnitCode(bidRequest.adUnitCode).gptSlot, 'domain': (bidRequest, bidResponse) => referrerHostname || getHostNameFromReferer(getRefererInfo().referer), 'adUnitCode': (bidRequest, bidResponse) => bidRequest.adUnitCode } diff --git a/test/spec/integration/faker/googletag.js b/test/spec/integration/faker/googletag.js index 9d91bf315d9..1d1a7512153 100644 --- a/test/spec/integration/faker/googletag.js +++ b/test/spec/integration/faker/googletag.js @@ -91,4 +91,9 @@ export function disable() { window.googletag = undefined; } +export function reset() { + disable(); + enable(); +} + enable(); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 548d2789d3e..b3105dafc39 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -15,6 +15,7 @@ import { allowedFields } from 'modules/priceFloors.js'; import events from 'src/events.js'; +import * as mockGpt from '../integration/faker/googletag.js'; describe('the price floors module', function () { let logErrorSpy; @@ -398,6 +399,84 @@ describe('the price floors module', function () { matchingFloor: 5.0 }); }); + describe('with gpt enabled', function () { + let gptFloorData; + beforeEach(function () { + gptFloorData = { + currency: 'USD', + schema: { + fields: ['gptSlot'] + }, + values: { + '/12345/sports/soccer': 1.1, + '/12345/sports/basketball': 2.2, + '/12345/news/politics': 3.3, + '/12345/news/weather': 4.4, + '*': 5.5, + }, + default: 0.5 + }; + // reset it so no lingering stuff from other test specs + mockGpt.reset(); + mockGpt.makeSlot({ + code: '/12345/sports/soccer', + divId: 'test_div_1' + }); + mockGpt.makeSlot({ + code: '/12345/sports/basketball', + divId: 'test_div_2' + }); + }); + afterEach(function () { + // reset it so no lingering stuff from other test specs + mockGpt.reset(); + }); + it('picks the right rule when looking for gptSlot', function () { + expect(getFirstMatchingFloor(gptFloorData, basicBidRequest)).to.deep.equal({ + floorMin: 0, + floorRuleValue: 1.1, + matchingFloor: 1.1, + matchingData: '/12345/sports/soccer', + matchingRule: '/12345/sports/soccer' + }); + + let newBidRequest = { ...basicBidRequest, adUnitCode: 'test_div_2' } + expect(getFirstMatchingFloor(gptFloorData, newBidRequest)).to.deep.equal({ + floorMin: 0, + floorRuleValue: 2.2, + matchingFloor: 2.2, + matchingData: '/12345/sports/basketball', + matchingRule: '/12345/sports/basketball' + }); + }); + it('picks the gptSlot from the bidRequest and does not call the slotMatching', function () { + const newBidRequest1 = { ...basicBidRequest }; + utils.deepSetValue(newBidRequest1, 'ortb2Imp.ext.data.adserver', { + name: 'gam', + adslot: '/12345/news/politics' + }) + expect(getFirstMatchingFloor(gptFloorData, newBidRequest1)).to.deep.equal({ + floorMin: 0, + floorRuleValue: 3.3, + matchingFloor: 3.3, + matchingData: '/12345/news/politics', + matchingRule: '/12345/news/politics' + }); + + const newBidRequest2 = { ...basicBidRequest, adUnitCode: 'test_div_2' }; + utils.deepSetValue(newBidRequest2, 'ortb2Imp.ext.data.adserver', { + name: 'gam', + adslot: '/12345/news/weather' + }) + expect(getFirstMatchingFloor(gptFloorData, newBidRequest2)).to.deep.equal({ + floorMin: 0, + floorRuleValue: 4.4, + matchingFloor: 4.4, + matchingData: '/12345/news/weather', + matchingRule: '/12345/news/weather' + }); + }); + }); }); describe('pre-auction tests', function () { let exposedAdUnits; From 5e2dcbd6a046c1247dc0ffe4fec0910f7a7b8637 Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Thu, 9 Sep 2021 08:07:49 -0700 Subject: [PATCH 1459/1476] IX Bid Adapter: Adding support for IX Outstream Renderer (#7390) * add ix renderer support * add unit tests * lint fix --- modules/ixBidAdapter.js | 62 +++++++++++++++++++++++--- test/spec/modules/ixBidAdapter_spec.js | 40 +++++++++++------ 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index ab9c995aa7c..8f821e87911 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -5,6 +5,7 @@ import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; import includes from 'core-js-pure/features/array/includes.js'; +import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -24,7 +25,7 @@ const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; - +const RENDERER_URL = 'https://js-sec.indexww.com/htv/video-player.js'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; // determines which eids we send and the rtiPartner field in ext @@ -267,6 +268,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.width = bidRequest.video.w; bid.height = bidRequest.video.h; bid.mediaType = VIDEO; + bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else { bid.ad = rawBid.adm; @@ -385,17 +387,22 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { } /** - * Finds the impression with the associated id. + * Get bid request object with the associated id. * * @param {*} id Id of the impression. * @param {array} impressions List of impressions sent in the request. * @return {object} The impression with the associated id. */ -function getBidRequest(id, impressions) { +function getBidRequest(id, impressions, validBidRequests) { if (!id) { return; } - return find(impressions, imp => imp.id === id); + const bidRequest = { + ...find(validBidRequests, bid => bid.bidId === id), + ...find(impressions, imp => imp.id === id) + } + + return bidRequest; } /** @@ -718,7 +725,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { requests.push({ method: 'GET', url: baseUrl, - data: clonedPayload + data: clonedPayload, + validBidRequests }); currentRequestSize = baseRequestSize; @@ -930,6 +938,43 @@ function createMissingBannerImp(bid, imp, newSize) { return newImp; } +/** + * Initialize Outstream Renderer + * @param {Object} bid + */ +function outstreamRenderer (bid) { + bid.renderer.push(() => { + var config = { + width: bid.width, + height: bid.height, + timeout: 3000 + }; + + window.IXOutstreamPlayer(bid.vastUrl, bid.adUnitCode, config); + }); +} + +/** + * Create Outstream Renderer + * @param {string} id + * @returns {Renderer} + */ +function createRenderer (id) { + const renderer = Renderer.install({ + id: id, + url: RENDERER_URL, + loaded: false + }); + + try { + renderer.setRender(outstreamRenderer); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + export const spec = { code: BIDDER_CODE, @@ -1108,8 +1153,13 @@ export const spec = { let requestBid = JSON.parse(bidderRequest.data.r); for (let j = 0; j < innerBids.length; j++) { - const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp); + const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp, bidderRequest.validBidRequests); bid = parseBid(innerBids[j], responseBody.cur, bidRequest); + + if (!utils.deepAccess(bid, 'mediaTypes.video.renderer') && utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + bid.mediaTypes.video.renderer = createRenderer(innerBids[j].bidId); + } + bids.push(bid); } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 85236571383..6eff922536f 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1233,7 +1233,7 @@ describe('IndexexchangeAdapter', function () { }); describe('First party data', function () { - afterEach(function() { + afterEach(function () { config.setConfig({ ortb2: {} }) @@ -2129,7 +2129,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2153,7 +2153,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2180,7 +2180,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); }); it('should set Japanese price correctly', function () { @@ -2206,7 +2206,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2235,7 +2235,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0].dealId).to.equal(expectedParse[0].dealId); }); @@ -2262,7 +2262,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2290,7 +2290,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0].dealId).to.deep.equal(expectedParse[0].dealId); }); @@ -2301,6 +2301,17 @@ describe('IndexexchangeAdapter', function () { cpm: 1.1, creativeId: '12346', mediaType: 'video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [ + 400, + 100 + ] + ] + } + }, width: 640, height: 480, currency: 'USD', @@ -2315,7 +2326,10 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { + data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: ONE_VIDEO + }); + expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2357,16 +2371,16 @@ describe('IndexexchangeAdapter', function () { const VIDEO_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_VIDEO_BID_RESPONSE); VIDEO_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 200; BANNER_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 100; - const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); - const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); + const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(bannerResult[0].ttl).to.equal(100); expect(videoResult[0].ttl).to.equal(200); }); it('should default bid[].ttl if seat[].bid[].exp is not in the resposne', function () { - const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); - const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); + const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(bannerResult[0].ttl).to.equal(300); expect(videoResult[0].ttl).to.equal(3600); From 5e0e47391d7d989dd9493b15aefd06b2f3754195 Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Thu, 9 Sep 2021 11:38:13 -0400 Subject: [PATCH 1460/1476] A publisher requested that we remove the bid.ad value for outstream since we provide the vastUrl (#7394) --- modules/sonobiBidAdapter.js | 1 + test/spec/modules/sonobiBidAdapter_spec.js | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 2edcdca1a51..01966f3d6b1 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -230,6 +230,7 @@ export const spec = { delete bids.width; delete bids.height; } else if (mediaType === 'outstream' && bidRequest) { + delete bids.ad; // Some pubs expect bids.ad to be a vast xml structure, we have a vatUrl so lets delete this. bids.mediaType = 'video'; bids.vastUrl = createCreative(bidResponse.sbi_dc, bid.sbi_aid); bids.renderer = newRenderer(bidRequest.adUnitCode, bids, deepAccess( diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 8eee973794a..05ba3f0897b 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -752,6 +752,7 @@ describe('SonobiBidAdapter', function () { expect(resp.width).to.equal(prebidResponse[i].width); expect(resp.height).to.equal(prebidResponse[i].height); expect(resp.renderer).to.be.ok; + expect(resp.ad).to.equal(undefined); } else if (resp.mediaType === 'video') { expect(resp.vastUrl.indexOf('vast.xml')).to.be.greaterThan(0); expect(resp.ad).to.be.undefined; From 545d9031c108c5ae4a924707e216498769ea1ade Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 9 Sep 2021 18:08:29 +0200 Subject: [PATCH 1461/1476] BLIINK Bid Adapter : Add new adapter (#7299) * feat(adapter): Add bliink bid adapter * feat(tests): Add tests unit file * refactor: code optimisation and fix cookie sync * fix(bliinkAdapter): get meta value Co-authored-by: Jonathan Co-authored-by: samuel.kerboeuf --- modules/bliinkBidAdapter.js | 309 ++++++++++++ modules/bliinkBidAdapter.md | 71 +++ test/spec/modules/bliinkBidAdapter_spec.js | 559 +++++++++++++++++++++ 3 files changed, 939 insertions(+) create mode 100644 modules/bliinkBidAdapter.js create mode 100644 modules/bliinkBidAdapter.md create mode 100644 test/spec/modules/bliinkBidAdapter_spec.js diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js new file mode 100644 index 00000000000..2f4cb9beac6 --- /dev/null +++ b/modules/bliinkBidAdapter.js @@ -0,0 +1,309 @@ +// eslint-disable-next-line prebid/validate-imports +// eslint-disable-next-line prebid/validate-imports +import {registerBidder} from 'src/adapters/bidderFactory.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 META_KEYWORDS = 'keywords' +export const META_DESCRIPTION = 'description' + +const VIDEO = 'video' +const NATIVE = 'native' +const BANNER = 'banner' + +const supportedMediaTypes = [BANNER, VIDEO, NATIVE] +const aliasBidderCode = ['bk'] + +export function getMetaList(name) { + if (!name || name.length === 0) return [] + + return [ + { + key: 'name', + value: name, + }, + { + key: 'name*', + value: name, + }, + { + key: 'itemprop*', + value: name, + }, + { + key: 'property', + value: `'og:${name}'`, + }, + { + key: 'property', + value: `'twitter:${name}'`, + }, + { + key: 'property', + value: `'article:${name}'`, + }, + ] +} + +export function getOneMetaValue(query) { + const metaEl = document.querySelector(query) + + if (metaEl && metaEl.content) { + return metaEl.content + } + + return null +} + +export function getMetaValue(name) { + const metaList = getMetaList(name) + for (let i = 0; i < metaList.length; i++) { + const meta = metaList[i]; + const metaValue = getOneMetaValue(`meta[${meta.key}=${meta.value}]`); + if (metaValue) { + return metaValue + } + } + return '' +} + +export function getKeywords() { + const metaKeywords = getMetaValue(META_KEYWORDS) + if (metaKeywords) { + const keywords = [ + ...metaKeywords.split(','), + ] + + if (keywords && keywords.length > 0) { + 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() + const xml = parser.parseFromString(content, 'text/xml') + + if (xml && + xml.getElementsByTagName('VAST')[0] && + xml.getElementsByTagName('VAST')[0].tagName === 'VAST') { + return xml + } + + return null +} + +/** + * @param bidRequest + * @param bliinkCreative + * @return {{cpm, netRevenue: boolean, ad: string, requestId, width: number, currency: string, mediaType: string, vastXml, ttl: number, height: number}|null} + */ +export const buildBid = (bidRequest, bliinkCreative) => { + if (!bidRequest && !bliinkCreative) return null + + const body = { + requestId: bidRequest.bidId, + cpm: bliinkCreative.price, + creativeId: bliinkCreative.creativeId, + currency: 'EUR', + netRevenue: false, + width: 1, + height: 1, + 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'] + + return Object.assign(body, { + currency: bliinkCreative.currency, + width: 1, + height: 1, + mediaType: VIDEO, + ad: '', + vastXml: bliinkCreative.content, + }) +} + +/** + * @description Verify the the AdUnits.bids, respond with true (valid) or false (invalid). + * + * @param bid + * @return boolean + */ +export const isBidRequestValid = (bid) => { + return !(!bid || !bid.params || !bid.params.placement || !bid.params.tagId) +} + +/** + * @description Takes an array of valid bid requests, all of which are guaranteed to have passed the isBidRequestValid() test. + * + * @param _[] + * @param bidderRequest + * @return {{ method: string, url: string } | null} + */ +export const buildRequests = (_, bidderRequest) => { + if (!bidderRequest) return null + + let data = { + pageUrl: bidderRequest.refererInfo.referer, + pageDescription: getMetaValue(META_DESCRIPTION), + keywords: getKeywords().join(','), + pageTitle: document.title, + } + + 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, + refererInfo: bidderRequest.refererInfo, + } + + if (bidderRequest.gdprConsent) { + data = Object.assign(data, { + gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies, + gdpr_consent: bidderRequest.gdprConsent.consentString + }) + } + + 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, + } + } + + return null +} + +/** + * @description Parse the response (from buildRequests) and generate one or more bid objects. + * + * @param serverResponse + * @param request + * @return + */ +const interpretResponse = (serverResponse, request) => { + if ((serverResponse && serverResponse.mode === 'no-ad') && (!request.params)) { + return [] + } + + const body = serverResponse.body + const serverBody = request.params + + const xml = parseXML(body) + + if (xml) { + 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] + + const 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); + } + + return [] +} + +/** + * @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 + * @param syncOptions + * @param serverResponses + * @param gdprConsent + * @return {[{type: string, url: string}]|*[]} + */ +const getUserSyncs = (syncOptions, serverResponses, gdprConsent) => { + let syncs = [] + + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + 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}`, + }, + { + type: 'image', + url: `https://secure.adnxs.com/getuid?${appnexusCallbackURL}`, + }, + ] + } + } + + return syncs; +} + +/** + * @type {{interpretResponse: interpretResponse, code: string, aliases: string[], getUserSyncs: getUserSyncs, buildRequests: buildRequests, onTimeout: onTimeout, onSetTargeting: onSetTargeting, isBidRequestValid: isBidRequestValid, onBidWon: onBidWon}} + */ +export const spec = { + code: BIDDER_CODE, + aliases: aliasBidderCode, + supportedMediaTypes: supportedMediaTypes, + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, +} + +registerBidder(spec) diff --git a/modules/bliinkBidAdapter.md b/modules/bliinkBidAdapter.md new file mode 100644 index 00000000000..ae0d4275396 --- /dev/null +++ b/modules/bliinkBidAdapter.md @@ -0,0 +1,71 @@ +# Overview + +``` +Module Name: BLIINK Bidder Adapter +Module Type: Bidder Adapter +Maintainer: samuel@bliink.io | jonathan@bliink.io +gdpr_supported: true +tcf2_supported: true +media_types: banner, native, video +``` + +# Description + +Module that connects to BLIINK demand sources to fetch bids. + +# Test Parameters + +## Sample Banner Ad Unit + +```js +const adUnits = [ + { + code: '/19968336/test', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'bliink', + params: { + placement: 'banner', + tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' + } + } + ] + } +] +``` + +## Sample Instream Video Ad Unit + +```js +const adUnits = [ + { + code: '/19968336/prebid_cache_video_adunit', + sizes: [[640,480]], + mediaType: 'video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + } + }, + bids: [ + { + bidder: 'bliink', + params: { + tagId: '41', + placement: 'video', + } + } + ] + } +] +``` diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js new file mode 100644 index 00000000000..4fbd0978552 --- /dev/null +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -0,0 +1,559 @@ +import { expect } from 'chai' +import { spec, buildBid, BLIINK_ENDPOINT_ENGINE, parseXML, getMetaList } from 'modules/bliinkBidAdapter.js' + +/** + * @description Mockup bidRequest + * @return {{ + * bidderWinsCount: number, + * adUnitCode: string, + * bidder: string, + * src: string, + * bidRequestsCount: number, + * params: {tagId: string, placement: string}, + * bidId: string, + * transactionId: string, + * auctionId: string, + * bidderRequestId: string, + * bidderRequestsCount: number, + * mediaTypes: {banner: {sizes: number[][]}}, + * sizes: number[][], + * crumbs: {pubcid: string}, + * ortb2Imp: {ext: {data: {pbadslot: string}}}}} + */ +const getConfigBid = () => { + return { + adUnitCode: '/19968336/test', + auctionId: '6752b51c-dcd4-4001-85dc-885ab5c504cf', + bidId: '2def0c5b2a7f6e', + bidRequestsCount: 1, + bidder: 'bliink', + bidderRequestId: '1592eb20088b18', + bidderRequestsCount: 1, + bidderWinsCount: 0, + crumbs: { + pubcid: '55ffadc5-051f-428d-8ecc-dc585e0bde0d' + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + ortb2Imp: { + ext: { + data: { + pbadslot: '/19968336/test' + } + } + }, + params: { + placement: 'banner', + tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' + }, + sizes: [ + [300, 250] + ], + src: 'client', + transactionId: 'cc6678c4-9746-4082-b9e2-d8065d078ebf' + } +} + +/** + * @description Mockup response from engine.bliink.io/xxxx + * @return { + * { + * viewability_percent_in_view: number, + * viewability_duration: number, + * ad_id: number, + * adm: string, + * id: number, + * category: number, + * type: number + * } +* } + */ +const getConfigCreative = () => { + return { + ad_id: 5648, + adm: '', + price: 1, + currency: 'EUR', + category: 1, + id: 2825, + type: 1, + viewability_duration: 1, + viewability_percent_in_view: 30, + } +} + +const getConfigCreativeVideo = () => { + return { + ad_id: 5648, + price: 1, + currency: 'EUR', + category: 1, + creativeId: 2825, + content: '' + } +} + +/** + * @description Mockup BuildRequest function + * @return {{bidderRequestId: string, bidderCode: string, bids: {bidderWinsCount: number, adUnitCode: string, bidder: string, src: string, bidRequestsCount: number, params: {tagId: string, placement: string}, bidId: string, transactionId: string, auctionId: string, bidderRequestId: string, bidderRequestsCount: number, mediaTypes: {banner: {sizes: number[][]}}, sizes: number[][], crumbs: {pubcid: string}, ortb2Imp: {ext: {data: {pbadslot: string}}}}[], refererInfo: {referer: string, canonicalUrl: null, isAmp: boolean, reachedTop: boolean, numIframes: number}}} + */ +const getConfigBuildRequest = () => { + return { + bidderRequestId: '164ddfd207e94d', + bidderCode: 'bliink', + bids: [getConfigBid()], + params: { + bids: [getConfigBid()], + }, + refererInfo: { + canonicalUrl: null, + isAmp: false, + numIframes: 0, + reachedTop: true, + referer: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', + }, + } +} + +/** + * @description Mockup response from API + * @param noAd + * @return {{mode: string, message: string}|{headers: {}, body: {mode: string, creative: {viewability_percent_in_view: number, viewability_duration: number, ad_id: number, adm: string, id: number, category: number, type: number}, token: string}}} + */ +const getConfigInterpretResponse = (noAd = false) => { + if (noAd) { + return { + message: 'invalid tag', + mode: 'no-ad' + } + } + + return { + body: { + creative: getConfigCreative(), + mode: 'ad', + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgxNzA4MzEsImlhdCI6MTYyNzU2NjAzMSwiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6IjM1YmU1NDNjLTNkZTQtNGQ1Yy04N2NjLWIzYzEyOGZiYzU0MCIsIm5ldHdvcmtJZCI6MjEsInNpdGVJZCI6NTksInRhZ0lkIjo1OSwiY29va2llSWQiOiJjNGU4MWVhOS1jMjhmLTQwZDItODY1ZC1hNjQzZjE1OTcyZjUiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwiaXAiOiI3OC4xMjIuNzUuNzIiLCJ0aW1lIjoxNjI3NTY2MDMxLCJsb2NhdGlvbiI6eyJsYXRpdHVkZSI6NDguOTczOSwibG9uZ2l0dWRlIjozLjMxMTMsInJlZ2lvbiI6IkhERiIsImNvdW50cnkiOiJGUiIsImNpdHkiOiJTYXVsY2hlcnkiLCJ6aXBDb2RlIjoiMDIzMTAiLCJkZXBhcnRtZW50IjoiMDIifSwiY2l0eSI6IlNhdWxjaGVyeSIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEyNCBTYWZhcmkvNTM3LjM2In0sImdkcHIiOnsiaGFzQ29uc2VudCI6dHJ1ZX0sIndpbiI6ZmFsc2UsImFkSWQiOjU2NDgsImFkdmVydGlzZXJJZCI6MSwiY2FtcGFpZ25JZCI6MSwiY3JlYXRpdmVJZCI6MjgyNSwiZXJyb3IiOmZhbHNlfX0.-UefQH4G0k-RJGemBYffs-KL7EEwma2Wuwgk2xnpij8' + }, + headers: {}, + } +} + +/** + * @description Mockup response from API for RTB creative + * @param noAd + * @return {{body: string} | {mode: string, message: string}} + */ +const getConfigInterpretResponseRTB = (noAd = false) => { + if (noAd) { + return { + message: 'invalid tag', + mode: 'no-ad' + } + } + + return { + body: '' + } +} + +/** + * + * + * + * @description Below start tests for utils fonctions + * + * + * + */ + +const testsGetMetaList = [ + { + title: 'Should return empty array if there are no parameters', + args: { + fn: getMetaList() + }, + want: [] + }, + { + title: 'Should return list of metas with name associated', + args: { + fn: getMetaList('test'), + }, + want: [ + { + key: 'name', + value: 'test', + }, + { + key: 'name*', + value: 'test', + }, + { + key: 'itemprop*', + value: 'test', + }, + { + key: 'property', + value: `'og:${'test'}'`, + }, + { + key: 'property', + value: `'twitter:${'test'}'`, + }, + { + key: 'property', + value: `'article:${'test'}'`, + }, + ] + } +] + +describe('BLIINK Adapter getMetaList', function() { + for (const test of testsGetMetaList) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) + +/** + * @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) + }) + } +}) + +/** + * + * + * + * @description End tests for utils fonctions + * + * + * + */ + +/** + * @description Array of tests used in describe function below + * @type {[{args: {fn}, want: boolean, title: string}, {args: {fn}, want: boolean, title: string}, {args: {fn}, want: boolean, title: string}]} + */ +const testsIsBidRequestValid = [ + { + title: 'isBidRequestValid format not valid', + args: { + fn: spec.isBidRequestValid({}) + }, + want: false, + }, + { + title: 'isBidRequestValid does not receive any bid', + args: { + fn: spec.isBidRequestValid() + }, + want: false, + }, + { + title: 'isBidRequestValid Receive a valid bid', + args: { + fn: spec.isBidRequestValid(getConfigBid()) + }, + want: true, + } +] + +describe('BLIINK Adapter isBidRequestValid', function() { + for (const test of testsIsBidRequestValid) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) + +const testsInterpretResponse = [ + { + title: 'Should construct bid for video instream', + args: { + fn: spec.interpretResponse(getConfigInterpretResponseRTB(false), getConfigBuildRequest()) + }, + want: { + ad: '', + cpm: 0, + currency: 'EUR', + height: 1, + width: 1, + creativeId: 0, + mediaType: 'video', + netRevenue: false, + requestId: '2def0c5b2a7f6e', + ttl: 3600, + vastXml: getConfigInterpretResponseRTB().body, + } + }, + { + title: 'ServerResponse with message: invalid tag, return empty array', + args: { + fn: spec.interpretResponse(getConfigInterpretResponse(true), getConfigBuildRequest()) + }, + want: [] + }, +] + +describe('BLIINK Adapter interpretResponse', function() { + for (const test of testsInterpretResponse) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) + +/** + * @description Array of tests used in describe function below + * @type {[ + * {args: + * {fn: { + * cpm: number, + * netRevenue: boolean, + * ad, requestId, + * meta: {mediaType}, + * width: number, + * currency: string, + * ttl: number, + * creativeId: number, + * height: number + * } + * }, want, title: string}]} + */ +const testsBuildBid = [ + { + title: 'Should return null if no bid passed in parameters', + args: { + fn: buildBid() + }, + want: null + }, + { + title: 'Input data must respect the output model', + args: { + fn: buildBid({ id: 1, test: '123' }, { id: 2, test: '345' }, false, false) + }, + want: null + }, + { + title: 'input data respect the output model for video', + args: { + fn: buildBid(getConfigBid(), getConfigCreativeVideo()) + }, + want: { + requestId: getConfigBid().bidId, + cpm: 1, + currency: 'EUR', + mediaType: 'video', + width: 1, + height: 1, + creativeId: getConfigCreativeVideo().creativeId, + netRevenue: false, + vastXml: getConfigCreativeVideo().content, + ad: getConfigCreative().adm, + ttl: 3600, + } + } +] + +describe('BLIINK Adapter buildBid', function() { + for (const test of testsBuildBid) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) + +/** + * @description Array of tests used in describe function below + * @type {[{args: {fn}, want, title: string}]} + */ +const testsBuildRequests = [ + { + title: 'Should not build request, no bidder request exist', + args: { + fn: spec.buildRequests() + }, + want: null + }, + { + title: 'Should build request if bidderRequest exist', + args: { + fn: spec.buildRequests([], getConfigBuildRequest()) + }, + want: { + method: 'GET', + url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest().bids[0].params.tagId}`, + params: { + bidderRequestId: getConfigBuildRequest().bidderRequestId, + bidderCode: getConfigBuildRequest().bidderCode, + bids: getConfigBuildRequest().bids, + refererInfo: getConfigBuildRequest().refererInfo + }, + data: { + height: 250, + width: 300, + keywords: '', + pageDescription: '', + pageTitle: '', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', + } + } + }, + { + title: 'Should build request width GDPR configuration', + args: { + fn: spec.buildRequests([], Object.assign(getConfigBuildRequest(), { + gdprConsent: { + gdprApplies: true, + consentString: 'XXXX' + }, + })) + }, + want: { + method: 'GET', + url: `${BLIINK_ENDPOINT_ENGINE}/${getConfigBuildRequest().bids[0].params.tagId}`, + params: { + bidderRequestId: getConfigBuildRequest().bidderRequestId, + bidderCode: getConfigBuildRequest().bidderCode, + bids: getConfigBuildRequest().bids, + refererInfo: getConfigBuildRequest().refererInfo + }, + data: { + gdpr: true, + gdpr_consent: 'XXXX', + pageDescription: '', + pageTitle: '', + keywords: '', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', + height: 250, + width: 300, + } + } + } +] + +describe('BLIINK Adapter buildRequests', function() { + for (const test of testsBuildRequests) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) + +const getSyncOptions = (pixelEnabled = true, iframeEnabled = 'false') => { + return { + pixelEnabled, + iframeEnabled + } +} + +const getServerResponses = () => { + return [ + { + body: '', + } + ] +} + +const getGdprConsent = () => { + return { + gdprApplies: 1, + consentString: 'XXX' + } +} + +const testsGetUserSyncs = [ + { + title: 'Should not have gdprConsent exist', + args: { + fn: spec.getUserSyncs(getSyncOptions(), getServerResponses(), getGdprConsent()) + }, + want: [ + { + 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' + }, + { + 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', + }, + { + type: 'image', + url: 'https://ups.analytics.yahoo.com/ups/58499/occ?gdpr=1&gdpr_consent=XXX', + }, + { + type: 'image', + url: 'https://secure.adnxs.com/getuid?https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dazerion%26uid%3D%24UID', + }, + ] + }, + { + title: 'Should not have gdpr consent', + args: { + fn: spec.getUserSyncs(getSyncOptions(), getServerResponses()) + }, + want: [] + } +] + +describe('BLIINK Adapter getUserSyncs', function() { + for (const test of testsGetUserSyncs) { + it(test.title, () => { + const res = test.args.fn + expect(res).to.eql(test.want) + }) + } +}) From 5ac1dbff44d7c557e5cedb459e0c84a3741e7b93 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Thu, 9 Sep 2021 19:01:16 +0200 Subject: [PATCH 1462/1476] PBjs Core (Targeting): bugfix for issue #7323 adding extra spaces (#7337) --- src/targeting.js | 8 ++++---- test/spec/unit/core/targeting_spec.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/targeting.js b/src/targeting.js index 4bbed7bb758..96692376d82 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -337,7 +337,7 @@ export function newTargeting(auctionManager) { * "div-gpt-ad-1460505748561-0": [{"hb_bidder": ["appnexusAst"]}] * }, * { - * "div-gpt-ad-1460505748561-0": [{"hb_bidder_appnexusAs": ["appnexusAst"]}] + * "div-gpt-ad-1460505748561-0": [{"hb_bidder_appnexusAs": ["appnexusAst", "other"]}] * } * ] * ``` @@ -346,7 +346,7 @@ export function newTargeting(auctionManager) { * { * "div-gpt-ad-1460505748561-0": { * "hb_bidder": "appnexusAst", - * "hb_bidder_appnexusAs": "appnexusAst" + * "hb_bidder_appnexusAs": "appnexusAst,other" * } * } * ``` @@ -360,7 +360,7 @@ export function newTargeting(auctionManager) { [Object.keys(targeting)[0]]: targeting[Object.keys(targeting)[0]] .map(target => { return { - [Object.keys(target)[0]]: target[Object.keys(target)[0]].join(', ') + [Object.keys(target)[0]]: target[Object.keys(target)[0]].join(',') }; }).reduce((p, c) => Object.assign(c, p), {}) }; @@ -634,7 +634,7 @@ export function newTargeting(auctionManager) { return Object.keys(aut) .map(function(key) { - if (utils.isStr(aut[key])) aut[key] = aut[key].split(','); + if (utils.isStr(aut[key])) aut[key] = aut[key].split(',').map(s => s.trim()); if (!utils.isArray(aut[key])) aut[key] = [ aut[key] ]; return { [key]: aut[key] }; }); diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index f83bd2f6635..1064d7c0f7d 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -309,10 +309,10 @@ describe('targeting tests', function () { ['string', '2.3', '2.3'], ['number', 2.3, '2.3'], ['boolean', true, 'true'], - ['string-separated', '2.3,4.5', '2.3, 4.5'], - ['array-of-string', ['2.3', '4.5'], '2.3, 4.5'], - ['array-of-number', [2.3, 4.5], '2.3, 4.5'], - ['array-of-boolean', [true, false], 'true, false'] + ['string-separated', '2.3, 4.5', '2.3,4.5'], + ['array-of-string', ['2.3', '4.5'], '2.3,4.5'], + ['array-of-number', [2.3, 4.5], '2.3,4.5'], + ['array-of-boolean', [true, false], 'true,false'] ]; pairs.forEach(([type, value, result]) => { it(`accepts ${type}`, function() { From 71cb998afc4491f7fc7bea5e09b7bcc1cf7ac49f Mon Sep 17 00:00:00 2001 From: anastasya123 <89073753+anastasya123@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:03:14 +0300 Subject: [PATCH 1463/1476] Between Bid Adapter: add ids (#7316) * between adapter: add ids * between-adapter: update ids --- modules/betweenBidAdapter.js | 19 ++--- test/spec/modules/betweenBidAdapter_spec.js | 95 +++++++++++---------- 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 5a351def958..7ac1e4edf15 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,5 +1,5 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; +import { getAdUnitSizes, parseSizesInput } from '../src/utils.js'; import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'between'; @@ -21,7 +21,7 @@ export const spec = { /** * Make a server request from the list of BidRequests. * - * @param {validBidRequests[]} - an array of bids + * @param {validBidRequest?pbjs_debug=trues[]} - an array of bids * @return ServerRequest Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { @@ -29,21 +29,21 @@ export const spec = { const gdprConsent = bidderRequest && bidderRequest.gdprConsent; const refInfo = getRefererInfo(); - validBidRequests.forEach(i => { + validBidRequests.forEach((i) => { let params = { + eids: getUsersIds(i), sizes: parseSizesInput(getAdUnitSizes(i)), jst: 'hb', ord: Math.random() * 10000000000000000, tz: getTz(), fl: getFl(), rr: getRr(), - shid: getSharedId(i)('id'), - shid3: getSharedId(i)('third'), s: i.params.s, bidid: i.bidId, transactionid: i.transactionId, auctionid: i.auctionId }; + if (i.params.itu !== undefined) { params.itu = i.params.itu; } @@ -149,13 +149,8 @@ export const spec = { } } -function getSharedId(bid) { - const id = deepAccess(bid, 'userId.sharedid.id'); - const third = deepAccess(bid, 'userId.sharedid.third'); - return function(kind) { - if (kind === 'id') return id || ''; - return third || ''; - } +function getUsersIds({ userIdAsEids }) { + return (userIdAsEids && userIdAsEids.length !== 0) ? userIdAsEids : []; } function getRr() { diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 44d0752d4b2..7f8e69669a8 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -77,6 +77,54 @@ describe('betweenBidAdapterTests', function () { expect(req_data.subid).to.equal(1138); }); + + it('validate eids parameter', function() { + const USER_ID_DATA = [ + { + source: 'admixer.net', + uids: [ + { id: '5706411dc1c54268ac2ed668b27f92a3', atype: 3 } + ] + } + ]; + + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: { + w: 240, + h: 400, + s: 1112, + }, + sizes: [[240, 400]], + userIdAsEids: USER_ID_DATA, + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = JSON.parse(request.data)[0].data; + + expect(req_data.eids).to.have.deep.members(USER_ID_DATA); + }); + + it('validate eids parameter, if userIdAsEids = undefined', function() { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: { + w: 240, + h: 400, + s: 1112, + }, + sizes: [[240, 400]], + userIdAsEids: undefined + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = JSON.parse(request.data)[0].data; + + expect(req_data.eids).to.have.deep.members([]); + }); + it('validate click3rd param', function() { let bidRequestData = [{ bidId: 'bid1234', @@ -222,53 +270,6 @@ describe('betweenBidAdapterTests', function () { expect(req_data.sizes).to.deep.equal(['970x250', '240x400', '728x90']) }); - it('check sharedId with id and third', function() { - const bidRequestData = [{ - bidId: 'bid123', - bidder: 'between', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - s: 1112, - }, - userId: { - sharedid: { - id: '01EXQE7JKNDRDDVATB0S2GX1NT', - third: '01EXQE7JKNDRDDVATB0S2GX1NT' - } - } - }]; - const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; - const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; - expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT'); - }); - - it('check sharedId with only id', function() { - const bidRequestData = [{ - bidId: 'bid123', - bidder: 'between', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - s: 1112, - }, - userId: { - sharedid: { - id: '01EXQE7JKNDRDDVATB0S2GX1NT', - } - } - }]; - const shid = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid; - const shid3 = JSON.parse(spec.buildRequests(bidRequestData).data)[0].data.shid3; - expect(shid).to.equal('01EXQE7JKNDRDDVATB0S2GX1NT') && expect(shid3).to.equal(''); - }); - it('check adomain', function() { const serverResponse = { body: [{ From 300db10dbc0fc79b6fff0c54ebbbbe55600cfbde Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 9 Sep 2021 11:32:31 -0700 Subject: [PATCH 1464/1476] "Prebid 5.13 Release" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9ac6d496dd..2a151a4b8b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.13.0-pre", + "version": "5.13.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bbb73bb00c6c1439536dceed1d635ab22f56ebbc Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 9 Sep 2021 11:59:27 -0700 Subject: [PATCH 1465/1476] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a151a4b8b3..769dc0bbbf9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "5.13.0", + "version": "5.14.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 6b1d704e002be80488e6a1e0fc3fe71377c69600 Mon Sep 17 00:00:00 2001 From: Dejan Grbavcic Date: Fri, 10 Sep 2021 00:40:13 +0200 Subject: [PATCH 1466/1476] TargetVideo Bid Adapter: add new adapter (#7336) * TargetVideo bid adapter * TargetVideo bid adapter * TargetVideo bid adapter --- modules/appnexusBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 72cf0baa3bd..9882e71fe4f 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -78,6 +78,7 @@ export const spec = { { code: 'districtm', gvlid: 144 }, { code: 'adasta' }, { code: 'beintoo', gvlid: 618 }, + { code: 'targetVideo' }, ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 2b921539c0dd58fdc0743083266e2ab352fe7bde Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:50:31 -0700 Subject: [PATCH 1467/1476] Merkle endpoint configurable (#7400) Co-authored-by: skocheri --- integrationExamples/gpt/userId_example.html | 1 + modules/merkleIdSystem.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index dfea06d17d0..653dd9c59f3 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -154,6 +154,7 @@ { "name": "merkleId", "params": { + "endpoint": "https://test_endpoint/", "vendor": "sdfg", "sv_cid": "dfg", "sv_pubid": "xcv", diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index 4ab29ec6f68..f4fc4356fbb 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -11,7 +11,6 @@ import {submodule} from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; const MODULE_NAME = 'merkleId'; -const ID_URL = 'https://id2.sv.rkdms.com/identity/'; const DEFAULT_REFRESH = 7 * 3600; const SESSION_COOKIE_NAME = '_svsid'; @@ -42,7 +41,7 @@ function setSession(storage, response) { function constructUrl(configParams) { const session = getSession(configParams); - let url = ID_URL + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; + let url = configParams.endpoint + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; if (session) { url = `${url}&sv_session=${session}`; } @@ -126,6 +125,11 @@ export const merkleIdSubmodule = { utils.logError('User ID - merkleId submodule requires a valid sv_pubid string to be defined'); return; } + + if (typeof configParams.endpoint !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid endpoint string to be defined'); + return; + } if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { utils.logError('User ID - merkleId submodule does not currently handle consent strings'); return; From 35b5dca0a9c0e8bd05d1e5b9a5cb6b1541ae6935 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 10 Sep 2021 14:38:17 -0400 Subject: [PATCH 1468/1476] Revert "Merkle endpoint configurable (#7400)" (#7401) This reverts commit 2b921539c0dd58fdc0743083266e2ab352fe7bde. --- integrationExamples/gpt/userId_example.html | 1 - modules/merkleIdSystem.js | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 653dd9c59f3..dfea06d17d0 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -154,7 +154,6 @@ { "name": "merkleId", "params": { - "endpoint": "https://test_endpoint/", "vendor": "sdfg", "sv_cid": "dfg", "sv_pubid": "xcv", diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index f4fc4356fbb..4ab29ec6f68 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -11,6 +11,7 @@ import {submodule} from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; const MODULE_NAME = 'merkleId'; +const ID_URL = 'https://id2.sv.rkdms.com/identity/'; const DEFAULT_REFRESH = 7 * 3600; const SESSION_COOKIE_NAME = '_svsid'; @@ -41,7 +42,7 @@ function setSession(storage, response) { function constructUrl(configParams) { const session = getSession(configParams); - let url = configParams.endpoint + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; + let url = ID_URL + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; if (session) { url = `${url}&sv_session=${session}`; } @@ -125,11 +126,6 @@ export const merkleIdSubmodule = { utils.logError('User ID - merkleId submodule requires a valid sv_pubid string to be defined'); return; } - - if (typeof configParams.endpoint !== 'string') { - utils.logError('User ID - merkleId submodule requires a valid endpoint string to be defined'); - return; - } if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { utils.logError('User ID - merkleId submodule does not currently handle consent strings'); return; From 46fe440f82d7c0b09d4698292ed376768c3f1cb4 Mon Sep 17 00:00:00 2001 From: Luigi Sayson <48766825+luigi-sayson@users.noreply.github.com> Date: Mon, 13 Sep 2021 17:51:04 -0700 Subject: [PATCH 1469/1476] Timeout RTD module: initial release (#7395) * Add Prebid timeout RTD module * increase test coverage * Add header to doc * Lint fixes * Add unknown connection speed to doc * Fix doc, add unit test --- modules/timeoutRtdProvider.js | 173 ++++++++++ modules/timeoutRtdProvider.md | 151 +++++++++ test/spec/modules/timeoutRtdProvider_spec.js | 339 +++++++++++++++++++ 3 files changed, 663 insertions(+) create mode 100644 modules/timeoutRtdProvider.js create mode 100644 modules/timeoutRtdProvider.md create mode 100644 test/spec/modules/timeoutRtdProvider_spec.js diff --git a/modules/timeoutRtdProvider.js b/modules/timeoutRtdProvider.js new file mode 100644 index 00000000000..020a7d6f7f0 --- /dev/null +++ b/modules/timeoutRtdProvider.js @@ -0,0 +1,173 @@ + +import { submodule } from '../src/hook.js'; +import * as ajax from '../src/ajax.js'; +import * as utils from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +const SUBMODULE_NAME = 'timeout'; + +// this allows the stubbing of functions during testing +export const timeoutRtdFunctions = { + getDeviceType, + getConnectionSpeed, + checkVideo, + calculateTimeoutModifier, + handleTimeoutIncrement +}; + +function getDeviceType() { + const userAgent = window.navigator.userAgent.toLowerCase(); + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(userAgent))) { + return 5; // tablet + } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(userAgent))) { + return 4; // mobile + } + return 2; // personal computer +} + +function checkVideo(adUnits) { + return adUnits.some((adUnit) => { + return adUnit.mediaTypes && adUnit.mediaTypes.video; + }); +} + +function getConnectionSpeed() { + const connection = window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection || {} + const connectionType = connection.type || connection.effectiveType; + + switch (connectionType) { + case 'slow-2g': + case '2g': + return 'slow'; + + case '3g': + return 'medium'; + + case 'bluetooth': + case 'cellular': + case 'ethernet': + case 'wifi': + case 'wimax': + case '4g': + return 'fast'; + } + + return 'unknown'; +} +/** + * Calculate the time to be added to the timeout + * @param {Array} adUnits + * @param {Object} rules + * @return {int} + */ +function calculateTimeoutModifier(adUnits, rules) { + utils.logInfo('Timeout rules', rules); + let timeoutModifier = 0; + let toAdd = 0; + + if (rules.includesVideo) { + const hasVideo = timeoutRtdFunctions.checkVideo(adUnits); + toAdd = rules.includesVideo[hasVideo] || 0; + utils.logInfo(`Adding ${toAdd} to timeout for includesVideo ${hasVideo}`) + timeoutModifier += toAdd; + } + + if (rules.numAdUnits) { + const numAdUnits = adUnits.length; + if (rules.numAdUnits[numAdUnits]) { + timeoutModifier += rules.numAdUnits[numAdUnits]; + } else { + for (const [rangeStr, timeoutVal] of Object.entries(rules.numAdUnits)) { + const [lowerBound, upperBound] = rangeStr.split('-'); + if (parseInt(lowerBound) <= numAdUnits && numAdUnits <= parseInt(upperBound)) { + utils.logInfo(`Adding ${timeoutVal} to timeout for numAdUnits ${numAdUnits}`) + timeoutModifier += timeoutVal; + break; + } + } + } + } + + if (rules.deviceType) { + const deviceType = timeoutRtdFunctions.getDeviceType(); + toAdd = rules.deviceType[deviceType] || 0; + utils.logInfo(`Adding ${toAdd} to timeout for deviceType ${deviceType}`) + timeoutModifier += toAdd; + } + + if (rules.connectionSpeed) { + const connectionSpeed = timeoutRtdFunctions.getConnectionSpeed(); + toAdd = rules.connectionSpeed[connectionSpeed] || 0; + utils.logInfo(`Adding ${toAdd} to timeout for connectionSpeed ${connectionSpeed}`) + timeoutModifier += toAdd; + } + + utils.logInfo('timeout Modifier calculated', timeoutModifier); + return timeoutModifier; +} + +/** + * + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} config + * @param {Object} userConsent + */ +function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { + utils.logInfo('Timeout rtd config', config); + const timeoutUrl = utils.deepAccess(config, 'params.endpoint.url'); + if (timeoutUrl) { + utils.logInfo('Timeout url', timeoutUrl); + ajax.ajaxBuilder()(timeoutUrl, { + success: function(response) { + try { + const rules = JSON.parse(response); + timeoutRtdFunctions.handleTimeoutIncrement(reqBidsConfigObj, rules); + } catch (e) { + utils.logError('Error parsing json response from timeout provider.') + } + callback(); + }, + error: function(errorStatus) { + utils.logError('Timeout request error!', errorStatus); + callback(); + } + }); + } else if (utils.deepAccess(config, 'params.rules')) { + timeoutRtdFunctions.handleTimeoutIncrement(reqBidsConfigObj, utils.deepAccess(config, 'params.rules')); + callback(); + } else { + utils.logInfo('No timeout endpoint or timeout rules found. Exiting timeout rtd module'); + callback(); + } +} + +/** + * Gets the timeout modifier, adds it to the bidder timeout, and sets it to reqBidsConfigObj + * @param {Object} reqBidsConfigObj + * @param {Object} rules + */ +function handleTimeoutIncrement(reqBidsConfigObj, rules) { + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + const timeoutModifier = timeoutRtdFunctions.calculateTimeoutModifier(adUnits, rules); + const bidderTimeout = getGlobal().getConfig('bidderTimeout'); + reqBidsConfigObj.timeout = bidderTimeout + timeoutModifier; +} + +/** @type {RtdSubmodule} */ +export const timeoutSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: SUBMODULE_NAME, + init: () => true, + getBidRequestData, +}; + +function registerSubModule() { + submodule('realTimeData', timeoutSubmodule); +} + +registerSubModule(); diff --git a/modules/timeoutRtdProvider.md b/modules/timeoutRtdProvider.md new file mode 100644 index 00000000000..49d1e1fc70a --- /dev/null +++ b/modules/timeoutRtdProvider.md @@ -0,0 +1,151 @@ + --- + layout: page_v2 + title: Timeout Rtd Module + description: Module for managing timeouts in real time + page_type: module + module_type: rtd + module_code : example + enable_download : true + sidebarType : 1 + --- + +## Overview +The timeout RTD module enables publishers to set rules that determine the timeout based on +certain features. It supports rule sets dynamically retrieved from a timeout provider as well as rules +set directly via configuration. +Build the timeout RTD module into the Prebid.js package with: +``` +gulp build --modules=timeoutRtdProvider,rtdModule... +``` + +## Configuration +The module is configured in the realTimeData.dataProviders object. The module will override +`bidderTimeout` in the pbjs config. + +### Timeout Data Provider interface +The timeout RTD module provides an interface of dynamically fetching timeout rules from +a data provider just before the auction begins. The endpoint url is set in the config just as in +the example below, and the timeout data will be used when making bid requests. + +``` +pbjs.setConfig({ + ... + "realTimeData": { + "dataProviders": [{ + "name": 'timeout', + "params": { + "endpoint": { + "url": "http://{cdn-link}.json" + } + } + } + ]}, + + // This value below will be modified by the timeout RTD module if it successfully + // fetches the timeout data. + "bidderTimeout": 1500, + ... +}); +``` + +Sample Endpoint Response: +``` +{ + "rules": { + "includesVideo": { + "true": 200, + "false": 50 + }, + "numAdUnits" : { + "1-5": 100, + "6-10": 200, + "11-15": 300 + }, + "deviceType": { + "2": 50, + "4": 100, + "5": 200 + }, + "connectionSpeed": { + "slow": 200, + "medium": 100, + "fast": 50, + "unknown": 10 + }, +} +``` + +### Rule Handling: +The rules retrieved from the endpoint will be used to add time to the `bidderTimeout` based on certain features such as +the user's deviceType, connection speed, etc. These rules can also be configured statically on page via a `rules` object. +Note that the timeout Module will ignore the static rules if an endpoint url is provided. The timeout rules follow the +format: +``` +{ + '': { + '': + } +} +``` +See bottom of page for examples. + +Currently supported features: + +|Name |Description | Keys | Example +| :------------ | :------------ | :------------ |:------------ | +| includesVideo | Adds time to the timeout based on whether there is a video ad unit in the auction or not | 'true'/'false'| { "true": 200, "false": 50 } | +| numAdUnits | Adds time based on the number of ad units. Ranges in the format `'lowerbound-upperbound` are accepted. This range is inclusive | numbers or number ranges | {"1": 50, "2-5": 100, "6-10": 200} | +| deviceType | Adds time based on device type| 2, 4, or 5| {"2": 50, "4": 100} | +| connectionSpeed | Adds time based on connection speed. `connectionSpeed` defaults to 'unknown' if connection speed cannot be determined | slow, medium, fast, or unknown | { "slow": 200} | + +If there are multiple rules set, all of them would be used and any that apply will be added to the base timeout. For example, if the rules object contains: +``` +{ + "includesVideo": { + "true": 200, + "false": 50 + }, + "numAdUnits" : { + "1-3": 100, + "4-5": 200 + } +} +``` +and there are 3 ad units in the auction, all of which are banner, then the timeout to be added will be 150 milliseconds (50 for `includesVideo[false]` + 100 for `numAdUnits['1-3']`). + +Full example: +``` +pbjs.setConfig({ + ... + "realTimeData": { + "dataProviders": [{ + "name": 'timeout', + "params": { + "rules": { + "includesVideo": { + "true": 200, + "false": 50 + }, + "numAdUnits" : { + "1-5": 100, + "6-10": 200, + "11-15": 300 + }, + "deviceType": { + "2": 50, + "4": 100, + "5": 200 + }, + "connectionSpeed": { + "slow": 200, + "medium": 100, + "fast": 50, + "unknown": 10 + }, + } + } + ]} + ... + // The timeout RTD module will add time to `bidderTimeout` based on the rules set above. + "bidderTimeout": 1500, +``` diff --git a/test/spec/modules/timeoutRtdProvider_spec.js b/test/spec/modules/timeoutRtdProvider_spec.js new file mode 100644 index 00000000000..88415a99b5e --- /dev/null +++ b/test/spec/modules/timeoutRtdProvider_spec.js @@ -0,0 +1,339 @@ + +import { timeoutRtdFunctions, timeoutSubmodule } from '../../../modules/timeoutRtdProvider' +import { expect } from 'chai'; +import * as ajax from 'src/ajax.js'; +import * as prebidGlobal from 'src/prebidGlobal.js'; + +const DEFAULT_USER_AGENT = window.navigator.userAgent; +const DEFAULT_CONNECTION = window.navigator.connection; + +const PC_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246'; +const MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1'; +const TABLET_USER_AGENT = 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'; + +function resetUserAgent() { + window.navigator.__defineGetter__('userAgent', () => DEFAULT_USER_AGENT); +}; + +function setUserAgent(userAgent) { + window.navigator.__defineGetter__('userAgent', () => userAgent); +} + +function resetConnection() { + window.navigator.__defineGetter__('connection', () => DEFAULT_CONNECTION); +} +function setConnectionType(connectionType) { + window.navigator.__defineGetter__('connection', () => { return {'type': connectionType} }); +} + +describe('getDeviceType', () => { + afterEach(() => { + resetUserAgent(); + }); + + [ + // deviceType, userAgent, deviceTypeNum + ['pc', PC_USER_AGENT, 2], + ['mobile', MOBILE_USER_AGENT, 4], + ['tablet', TABLET_USER_AGENT, 5], + ].forEach(function(args) { + const [deviceType, userAgent, deviceTypeNum] = args; + it(`should be able to recognize ${deviceType} devices`, () => { + setUserAgent(userAgent); + const res = timeoutRtdFunctions.getDeviceType(); + expect(res).to.equal(deviceTypeNum) + }) + }) +}); + +describe('getConnectionSpeed', () => { + afterEach(() => { + resetConnection(); + }); + [ + // connectionType, connectionSpeed + ['slow-2g', 'slow'], + ['2g', 'slow'], + ['3g', 'medium'], + ['bluetooth', 'fast'], + ['cellular', 'fast'], + ['ethernet', 'fast'], + ['wifi', 'fast'], + ['wimax', 'fast'], + ['4g', 'fast'], + ['not known', 'unknown'], + [undefined, 'unknown'], + ].forEach(function(args) { + const [connectionType, connectionSpeed] = args; + it(`should be able to categorize connection speed when the connection type is ${connectionType}`, () => { + setConnectionType(connectionType); + const res = timeoutRtdFunctions.getConnectionSpeed(); + expect(res).to.equal(connectionSpeed) + }) + }) +}); + +describe('Timeout modifier calculations', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should be able to detect video ad units', () => { + let adUnits = [] + let res = timeoutRtdFunctions.checkVideo(adUnits); + expect(res).to.be.false; + + adUnits = [{ + mediaTypes: { + video: [] + } + }]; + res = timeoutRtdFunctions.checkVideo(adUnits); + expect(res).to.be.true; + + adUnits = [{ + mediaTypes: { + banner: [] + } + }]; + res = timeoutRtdFunctions.checkVideo(adUnits); + expect(res).to.be.false; + }); + + it('should calculate the timeout modifier for video', () => { + sandbox.stub(timeoutRtdFunctions, 'checkVideo').returns(true); + const rules = { + includesVideo: { + 'true': 200, + 'false': 50 + } + } + const res = timeoutRtdFunctions.calculateTimeoutModifier([], rules); + expect(res).to.equal(200) + }); + + it('should calculate the timeout modifier for connectionSpeed', () => { + sandbox.stub(timeoutRtdFunctions, 'getConnectionSpeed').returns('slow'); + const rules = { + connectionSpeed: { + 'slow': 200, + 'medium': 100, + 'fast': 50 + } + } + const res = timeoutRtdFunctions.calculateTimeoutModifier([], rules); + expect(res).to.equal(200); + }); + + it('should calculate the timeout modifier for deviceType', () => { + sandbox.stub(timeoutRtdFunctions, 'getDeviceType').returns(4); + const rules = { + deviceType: { + '2': 50, + '4': 100, + '5': 200 + }, + } + const res = timeoutRtdFunctions.calculateTimeoutModifier([], rules); + expect(res).to.equal(100) + }); + + it('should calculate the timeout modifier for ranged numAdunits', () => { + const rules = { + numAdUnits: { + '1-5': 100, + '6-10': 200, + '11-15': 300, + } + } + const adUnits = [1, 2, 3, 4, 5, 6]; + const res = timeoutRtdFunctions.calculateTimeoutModifier(adUnits, rules); + expect(res).to.equal(200) + }); + + it('should calculate the timeout modifier for exact numAdunits', () => { + const rules = { + numAdUnits: { + '1': 100, + '2': 200, + '3': 300, + '4-5': 400, + } + } + const adUnits = [1, 2]; + const res = timeoutRtdFunctions.calculateTimeoutModifier(adUnits, rules); + expect(res).to.equal(200); + }); + + it('should add up all the modifiers when all the rules are present', () => { + sandbox.stub(timeoutRtdFunctions, 'getConnectionSpeed').returns('slow'); + sandbox.stub(timeoutRtdFunctions, 'getDeviceType').returns(4); + const rules = { + connectionSpeed: { + 'slow': 200, + 'medium': 100, + 'fast': 50 + }, + deviceType: { + '2': 50, + '4': 100, + '5': 200 + }, + includesVideo: { + 'true': 200, + 'false': 50 + }, + numAdUnits: { + '1': 100, + '2': 200, + '3': 300, + '4-5': 400, + } + } + const res = timeoutRtdFunctions.calculateTimeoutModifier([{ + mediaTypes: { + video: [] + } + }], rules); + expect(res).to.equal(600); + }); +}); + +describe('Timeout RTD submodule', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should init successfully', () => { + expect(timeoutSubmodule.init()).to.equal(true); + }); + + it('should make a request to the endpoint url if it is provided, and handle the response', () => { + const response = '{"deviceType":{ "2": 50, "4": 100, "5": 200 }}' + const ajaxStub = sandbox.stub().callsFake(function (url, callbackObj) { + callbackObj.success(response); + }); + sandbox.stub(ajax, 'ajaxBuilder').callsFake(function () { return ajaxStub }); + + const reqBidsConfigObj = {} + const expectedLink = 'https://somelink.json' + const config = { + 'name': 'timeout', + 'params': { + 'endpoint': { + url: expectedLink + } + } + } + const handleTimeoutIncrementStub = sandbox.stub(timeoutRtdFunctions, 'handleTimeoutIncrement'); + timeoutSubmodule.getBidRequestData(reqBidsConfigObj, function() {}, config) + + expect(ajaxStub.calledWith(expectedLink)).to.be.true; + expect(handleTimeoutIncrementStub.calledWith(reqBidsConfigObj, JSON.parse(response))).to.be.true; + }); + + it('should make a request to the endpoint url and ignore the rules object if the endpoint is provided', () => { + const ajaxStub = sandbox.stub().callsFake((url, callbackObj) => {}); + sandbox.stub(ajax, 'ajaxBuilder').callsFake(() => ajaxStub); + const expectedLink = 'https://somelink.json' + const config = { + 'name': 'timeout', + 'params': { + 'endpoint': { + url: expectedLink + }, + 'rules': { + 'includesVideo': { + 'true': 200, + }, + } + } + } + timeoutSubmodule.getBidRequestData({}, function() {}, config); + expect(ajaxStub.calledWith(expectedLink)).to.be.true; + }); + + it('should use the rules object if there is no endpoint url', () => { + const config = { + 'name': 'timeout', + 'params': { + 'rules': { + 'includesVideo': { + 'true': 200, + }, + } + } + } + const handleTimeoutIncrementStub = sandbox.stub(timeoutRtdFunctions, 'handleTimeoutIncrement'); + const reqBidsConfigObj = {}; + timeoutSubmodule.getBidRequestData(reqBidsConfigObj, function() {}, config); + expect(handleTimeoutIncrementStub.calledWith(reqBidsConfigObj, config.params.rules)).to.be.true; + }); + + it('should exit quietly if no relevant timeout config is found', () => { + const callback = sandbox.stub() + const ajaxStub = sandbox.stub().callsFake((url, callbackObj) => {}); + sandbox.stub(ajax, 'ajaxBuilder').callsFake(function() { return ajaxStub }); + const handleTimeoutIncrementStub = sandbox.stub(timeoutRtdFunctions, 'handleTimeoutIncrement'); + + timeoutSubmodule.getBidRequestData({}, callback, {}); + + expect(handleTimeoutIncrementStub.called).to.be.false; + expect(callback.called).to.be.true; + expect(ajaxStub.called).to.be.false; + }); + + it('should be able to increment the timeout with the calculated timeout modifier', () => { + const baseTimeout = 100; + const getConfigStub = sandbox.stub().returns(baseTimeout); + sandbox.stub(prebidGlobal, 'getGlobal').callsFake(() => { + return { + getConfig: getConfigStub + } + }); + + const reqBidsConfigObj = {adUnits: [1, 2, 3]} + const addedTimeout = 400; + const rules = { + numAdUnits: { + '3-5': addedTimeout, + } + } + + timeoutRtdFunctions.handleTimeoutIncrement(reqBidsConfigObj, rules) + expect(reqBidsConfigObj.timeout).to.be.equal(baseTimeout + addedTimeout); + }); + + it('should be able to increment the timeout with the calculated timeout modifier when there are multiple matching rules', () => { + const baseTimeout = 100; + const getConfigStub = sandbox.stub().returns(baseTimeout); + sandbox.stub(prebidGlobal, 'getGlobal').callsFake(() => { + return { + getConfig: getConfigStub + } + }); + + const reqBidsConfigObj = {adUnits: [1, 2, 3]} + const addedTimeout = 400; + const rules = { + numAdUnits: { + '3-5': addedTimeout / 2, + }, + includesVideo: { + 'false': addedTimeout / 2, + } + } + timeoutRtdFunctions.handleTimeoutIncrement(reqBidsConfigObj, rules) + expect(reqBidsConfigObj.timeout).to.be.equal(baseTimeout + addedTimeout); + }); +}); From 848ae9cc9cb40bc69379e9a1269024e5d14d51cd Mon Sep 17 00:00:00 2001 From: Tachfine Date: Tue, 14 Sep 2021 12:43:07 +0200 Subject: [PATCH 1470/1476] CriteoIdSystem returns a callback to initiate user sync (#7371) --- modules/criteoIdSystem.js | 44 ++++++++------ test/spec/modules/criteoIdSystem_spec.js | 75 +++++++++++++----------- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index ac26d34d529..6a028a6cc25 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -6,8 +6,8 @@ */ import * as utils from '../src/utils.js' -import * as ajax from '../src/ajax.js' -import { getRefererInfo } from '../src/refererDetection.js' +import { ajax } from '../src/ajax.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -65,7 +65,7 @@ function buildCriteoUsersyncUrl(topUrl, domain, bundle, areCookiesWriteable, isL return url; } -function callCriteoUserSync(parsedCriteoData, gdprString) { +function callCriteoUserSync(parsedCriteoData, gdprString, callback) { const cw = storage.cookiesAreEnabled(); const lsw = storage.localStorageIsEnabled(); const topUrl = extractProtocolHost(getRefererInfo().referer); @@ -82,26 +82,32 @@ function callCriteoUserSync(parsedCriteoData, gdprString) { gdprString ); - ajax.ajaxBuilder()( - url, - response => { + const callbacks = { + success: response => { const jsonResponse = JSON.parse(response); - if (jsonResponse.bidId) { - saveOnAllStorages(bididStorageKey, jsonResponse.bidId); - } else { - deleteFromAllStorages(bididStorageKey); - } - if (jsonResponse.acwsUrl) { const urlsToCall = typeof jsonResponse.acwsUrl === 'string' ? [jsonResponse.acwsUrl] : jsonResponse.acwsUrl; urlsToCall.forEach(url => utils.triggerPixel(url)); } else if (jsonResponse.bundle) { saveOnAllStorages(bundleStorageKey, jsonResponse.bundle); } + + if (jsonResponse.bidId) { + saveOnAllStorages(bididStorageKey, jsonResponse.bidId); + const criteoId = { criteoId: jsonResponse.bidId }; + callback(criteoId); + } else { + deleteFromAllStorages(bididStorageKey); + callback(); + } }, - undefined, - { method: 'GET', contentType: 'application/json', withCredentials: true } - ); + error: error => { + utils.logError(`criteoIdSystem: unable to sync user id`, error); + callback(); + } + }; + + ajax(url, callbacks, undefined, { method: 'GET', contentType: 'application/json', withCredentials: true }); } /** @type {Submodule} */ @@ -132,9 +138,13 @@ export const criteoIdSubmodule = { const gdprConsentString = hasGdprData ? consentData.consentString : undefined; let localData = getCriteoDataFromAllStorages(); - callCriteoUserSync(localData, gdprConsentString); - return { id: localData.bidId ? { criteoId: localData.bidId } : undefined } + const result = (callback) => callCriteoUserSync(localData, gdprConsentString, callback); + + return { + id: localData.bidId ? { criteoId: localData.bidId } : undefined, + callback: result + } } }; diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index 65e5aaf741d..828b8401af1 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -1,15 +1,9 @@ import { criteoIdSubmodule, storage } from 'modules/criteoIdSystem.js'; import * as utils from 'src/utils.js'; -import * as ajaxLib from 'src/ajax.js'; +import {server} from '../../mocks/xhr'; const pastDateString = new Date(0).toString() -function mockResponse(responseText, fakeResponse = (url, callback) => callback(responseText)) { - return function() { - return fakeResponse; - } -} - describe('CriteoId module', function () { const cookiesMaxAge = 13 * 30 * 24 * 60 * 60 * 1000; @@ -22,7 +16,6 @@ describe('CriteoId module', function () { let removeFromLocalStorageStub; let timeStampStub; let parseUrlStub; - let ajaxBuilderStub; let triggerPixelStub; beforeEach(function (done) { @@ -32,7 +25,6 @@ describe('CriteoId module', function () { setLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); removeFromLocalStorageStub = sinon.stub(storage, 'removeDataFromLocalStorage'); timeStampStub = sinon.stub(utils, 'timestamp').returns(nowTimestamp); - ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockResponse('{}')); parseUrlStub = sinon.stub(utils, 'parseUrl').returns({protocol: 'https', hostname: 'testdev.com'}) triggerPixelStub = sinon.stub(utils, 'triggerPixel'); done(); @@ -45,7 +37,6 @@ describe('CriteoId module', function () { setLocalStorageStub.restore(); removeFromLocalStorageStub.restore(); timeStampStub.restore(); - ajaxBuilderStub.restore(); triggerPixelStub.restore(); parseUrlStub.restore(); }); @@ -61,8 +52,9 @@ describe('CriteoId module', function () { getCookieStub.withArgs('cto_bidid').returns(testCase.cookie); getLocalStorageStub.withArgs('cto_bidid').returns(testCase.localStorage); - const id = criteoIdSubmodule.getId(); - expect(id).to.be.deep.equal({id: testCase.expected ? { criteoId: testCase.expected } : undefined}); + const result = criteoIdSubmodule.getId(); + expect(result.id).to.be.deep.equal(testCase.expected ? { criteoId: testCase.expected } : undefined); + expect(result.callback).to.be.a('function'); })) it('decode() should return the bidId when it exists in local storages', function () { @@ -74,16 +66,21 @@ describe('CriteoId module', function () { getCookieStub.withArgs('cto_bundle').returns('bundle'); window.criteo_pubtag = {} - const emptyObj = '{}'; - let ajaxStub = sinon.stub().callsFake((url, callback) => callback(emptyObj)); - ajaxBuilderStub.callsFake(mockResponse(undefined, ajaxStub)) + let callBackSpy = sinon.spy(); + let result = criteoIdSubmodule.getId(); + result.callback(callBackSpy); - criteoIdSubmodule.getId(); const expectedUrl = `https://gum.criteo.com/sid/json?origin=prebid&topUrl=https%3A%2F%2Ftestdev.com%2F&domain=testdev.com&bundle=bundle&cw=1&pbt=1&lsw=1`; - expect(ajaxStub.calledWith(expectedUrl)).to.be.true; + let request = server.requests[0]; + expect(request.url).to.be.eq(expectedUrl); - window.criteo_pubtag = undefined; + request.respond( + 200, + {'Content-Type': 'application/json'}, + JSON.stringify({}) + ); + expect(callBackSpy.calledOnce).to.be.true; }); const responses = [ @@ -101,16 +98,19 @@ describe('CriteoId module', function () { responses.forEach(response => describe('test user sync response behavior', function () { const expirationTs = new Date(nowTimestamp + cookiesMaxAge).toString(); - beforeEach(function (done) { - const fakeResponse = (url, callback) => { - callback(JSON.stringify(response)); - setTimeout(done, 0); - } - ajaxBuilderStub.callsFake(mockResponse(undefined, fakeResponse)); - criteoIdSubmodule.getId(); - }) - it('should save bidId if it exists', function () { + const result = criteoIdSubmodule.getId(); + result.callback((id) => { + expect(id).to.be.deep.equal(response.bidId ? { criteoId: response.bidId } : undefined); + }); + + let request = server.requests[0]; + request.respond( + 200, + {'Content-Type': 'application/json'}, + JSON.stringify(response) + ); + if (response.acwsUrl) { expect(triggerPixelStub.called).to.be.true; expect(setCookieStub.calledWith('cto_bundle')).to.be.false; @@ -140,16 +140,23 @@ describe('CriteoId module', function () { ]; gdprConsentTestCases.forEach(testCase => it('should call user sync url with the gdprConsent', function () { - const emptyObj = '{}'; - let ajaxStub = sinon.stub().callsFake((url, callback) => callback(emptyObj)); - ajaxBuilderStub.callsFake(mockResponse(undefined, ajaxStub)) - - criteoIdSubmodule.getId(undefined, testCase.consentData); + let callBackSpy = sinon.spy(); + let result = criteoIdSubmodule.getId(undefined, testCase.consentData); + result.callback(callBackSpy); + let request = server.requests[0]; if (testCase.expected) { - expect(ajaxStub.calledWithMatch(`gdprString=${testCase.expected}`)).to.be.true; + expect(request.url).to.have.string(`gdprString=${testCase.expected}`); } else { - expect(ajaxStub.calledWithMatch('gdprString')).not.to.be.true; + expect(request.url).to.not.have.string('gdprString'); } + + request.respond( + 200, + {'Content-Type': 'application/json'}, + JSON.stringify({}) + ); + + expect(callBackSpy.calledOnce).to.be.true; })); }); From 0f7d0ceb8d9dd401308808a6c1cf8497e7c635ce Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Tue, 14 Sep 2021 17:25:36 +0100 Subject: [PATCH 1471/1476] Added sizeId 562 (300x431) (#7408) --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 354289a1823..3fca68521bb 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -110,7 +110,8 @@ var sizeMap = { 548: '500x1000', 550: '980x480', 552: '300x200', - 558: '640x640' + 558: '640x640', + 562: '300x431' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From 27a90675ef76f2179c1b47ebb3159ba9dbee7674 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 14 Sep 2021 12:40:48 -0400 Subject: [PATCH 1472/1476] Update .submodules.json (#7406) --- modules/.submodules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/.submodules.json b/modules/.submodules.json index e4de1819a16..3a3b94ea469 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -50,7 +50,8 @@ "optimeraRtdProvider", "permutiveRtdProvider", "reconciliationRtdProvider", - "sirdataRtdProvider" + "sirdataRtdProvider", + "timeoutRtdProvider" ], "fpdModule": [ "enrichmentFpdModule", From d9bc98e77863da18a842c7e7b16ec3207bbae9f0 Mon Sep 17 00:00:00 2001 From: John Salis Date: Tue, 14 Sep 2021 14:36:25 -0400 Subject: [PATCH 1473/1476] add custom error messages for beachfront bid validation (#7412) Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index d633359eb8e..3531fa45d1b 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -6,7 +6,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; -const ADAPTER_VERSION = '1.17'; +const ADAPTER_VERSION = '1.18'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; const CURRENCY = 'USD'; @@ -32,7 +32,27 @@ export const spec = { supportedMediaTypes: [ VIDEO, BANNER ], isBidRequestValid(bid) { - return !!(isVideoBidValid(bid) || isBannerBidValid(bid)); + if (isVideoBid(bid)) { + if (!getVideoBidParam(bid, 'appId')) { + utils.logWarn('Beachfront: appId param is required for video bids.'); + return false; + } + if (!getVideoBidParam(bid, 'bidfloor')) { + utils.logWarn('Beachfront: bidfloor param is required for video bids.'); + return false; + } + } + if (isBannerBid(bid)) { + if (!getBannerBidParam(bid, 'appId')) { + utils.logWarn('Beachfront: appId param is required for banner bids.'); + return false; + } + if (!getBannerBidParam(bid, 'bidfloor')) { + utils.logWarn('Beachfront: bidfloor param is required for banner bids.'); + return false; + } + } + return true; }, buildRequests(bids, bidderRequest) { From c064ea9ed4c00a31333d780c5ad4dd0d351ef363 Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Tue, 14 Sep 2021 19:53:20 +0100 Subject: [PATCH 1474/1476] Add new sizes (#7414) Dimensions: 320x431 Size ID: 564 Dimensions: 320x300 Size ID: 566 Dimensions: 300x150 Size ID: 568 Dimensions: 300x125 Size ID: 570 Dimensions: 250x350 Size ID: 572 Dimensions: 620x891 Size ID: 574 Dimensions: 610x877 Size ID: 576 Dimensions: 980x552 Size ID: 578 Dimensions: 505x656 Size ID: 580 --- modules/rubiconBidAdapter.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 3fca68521bb..d502d7efb25 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -111,7 +111,16 @@ var sizeMap = { 550: '980x480', 552: '300x200', 558: '640x640', - 562: '300x431' + 562: '300x431', + 564: '320x431', + 566: '320x300', + 568: '300x150', + 570: '300x125', + 572: '250x350', + 574: '620x891', + 576: '610x877', + 578: '980x552', + 580: '505x656' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From 648b08d1c415a8e6ae49eea0ee861e53aa34c4e8 Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Tue, 14 Sep 2021 16:26:01 -0400 Subject: [PATCH 1475/1476] PulsePoint Bid Adapter: support for additional user id providers (#7389) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * ET-9372: PulsePoint Adapter - support for additional user id providers * Fix for haloId --- modules/pulsepointBidAdapter.js | 12 ++++++--- .../spec/modules/pulsepointBidAdapter_spec.js | 26 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index adea33fc3b9..5f8f096926b 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -421,12 +421,18 @@ function user(bidRequest, bidderRequest) { if (bidRequest) { if (bidRequest.userId) { ext.eids = []; - addExternalUserId(ext.eids, bidRequest.userId.pubcid, 'pubcommon'); + addExternalUserId(ext.eids, bidRequest.userId.pubcid, 'pubcid.org'); addExternalUserId(ext.eids, bidRequest.userId.britepoolid, 'britepool.com'); - addExternalUserId(ext.eids, bidRequest.userId.criteoId, 'criteo'); - addExternalUserId(ext.eids, bidRequest.userId.idl_env, 'identityLink'); + addExternalUserId(ext.eids, bidRequest.userId.criteoId, 'criteo.com'); + addExternalUserId(ext.eids, bidRequest.userId.idl_env, 'liveramp.com'); addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.id5id.uid'), 'id5-sync.com', utils.deepAccess(bidRequest, 'userId.id5id.ext')); addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.parrableId.eid'), 'parrable.com'); + addExternalUserId(ext.eids, bidRequest.userId.fabrickId, 'neustar.biz'); + addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.haloId.haloId'), 'audigent.com'); + addExternalUserId(ext.eids, bidRequest.userId.merkleId, 'merkleinc.com'); + addExternalUserId(ext.eids, bidRequest.userId.lotamePanoramaId, 'crwdcntrl.net'); + addExternalUserId(ext.eids, bidRequest.userId.connectid, 'verizonmedia.com'); + addExternalUserId(ext.eids, utils.deepAccess(bidRequest, 'userId.uid2.id'), 'uidapi.com'); // liveintent if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { addExternalUserId(ext.eids, bidRequest.userId.lipb.lipbid, 'liveintent.com'); diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 6630cb0907c..92f7aa0b70d 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -637,7 +637,7 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.user.ext).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.have.lengthOf(2); - expect(ortbRequest.user.ext.eids[0].source).to.equal('pubcommon'); + expect(ortbRequest.user.ext.eids[0].source).to.equal('pubcid.org'); expect(ortbRequest.user.ext.eids[0].uids).to.have.lengthOf(1); expect(ortbRequest.user.ext.eids[0].uids[0].id).to.equal('userid_pubcid'); expect(ortbRequest.user.ext.eids[1].source).to.equal('adserver.org'); @@ -659,6 +659,16 @@ describe('PulsePoint Adapter Tests', function () { parrableId: { eid: 'parrable_id234' }, lipb: { lipbid: 'liveintent_id123' + }, + haloId: { + haloId: 'halo_user1' + }, + lotamePanoramaId: 'lotame_user2', + merkleId: 'merkle_user3', + fabrickId: 'fabrick_user4', + connectid: 'connect_user5', + uid2: { + id: 'uid2_user6' } }; const userVerify = function(obj, source, id) { @@ -677,13 +687,19 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.user).to.not.be.undefined; expect(ortbRequest.user.ext).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.not.be.undefined; - expect(ortbRequest.user.ext.eids).to.have.lengthOf(6); + expect(ortbRequest.user.ext.eids).to.have.lengthOf(12); userVerify(ortbRequest.user.ext.eids[0], 'britepool.com', 'britepool_id123'); - userVerify(ortbRequest.user.ext.eids[1], 'criteo', 'criteo_id234'); - userVerify(ortbRequest.user.ext.eids[2], 'identityLink', 'idl_id123'); + userVerify(ortbRequest.user.ext.eids[1], 'criteo.com', 'criteo_id234'); + userVerify(ortbRequest.user.ext.eids[2], 'liveramp.com', 'idl_id123'); userVerify(ortbRequest.user.ext.eids[3], 'id5-sync.com', 'id5id_234'); userVerify(ortbRequest.user.ext.eids[4], 'parrable.com', 'parrable_id234'); - userVerify(ortbRequest.user.ext.eids[5], 'liveintent.com', 'liveintent_id123'); + userVerify(ortbRequest.user.ext.eids[5], 'neustar.biz', 'fabrick_user4'); + userVerify(ortbRequest.user.ext.eids[6], 'audigent.com', 'halo_user1'); + userVerify(ortbRequest.user.ext.eids[7], 'merkleinc.com', 'merkle_user3'); + userVerify(ortbRequest.user.ext.eids[8], 'crwdcntrl.net', 'lotame_user2'); + userVerify(ortbRequest.user.ext.eids[9], 'verizonmedia.com', 'connect_user5'); + userVerify(ortbRequest.user.ext.eids[10], 'uidapi.com', 'uid2_user6'); + userVerify(ortbRequest.user.ext.eids[11], 'liveintent.com', 'liveintent_id123'); }); it('Verify multiple adsizes', function () { const bidRequests = deepClone(slotConfigs); From be25401d8214158a79f2aa6dec1768924a853930 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Wed, 15 Sep 2021 16:07:58 +0200 Subject: [PATCH 1476/1476] smartx Bid Adapter: fix empty title not configurable (#7417) * 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 Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 44e70082d9d..73e036cadb0 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -372,7 +372,7 @@ function createOutstreamConfig(bid) { smartPlayObj.endingScreen = false; } - if (confTitle) { + if (confTitle || (typeof bid.renderer.config.outstream_options.title == 'string' && bid.renderer.config.outstream_options.title == '')) { smartPlayObj.title = confTitle; }